internal void _Initialize(V8Engine v8EngineProxy, bool registerPropertyInterceptors = true)
 {
     _Initialize(v8EngineProxy,
                 (NativeObjectTemplateProxy *)V8NetProxy.CreateObjectTemplateProxy(v8EngineProxy._NativeV8EngineProxy), // (create a corresponding native object)
                 registerPropertyInterceptors
                 );
 }
        internal void _Initialize(V8Engine v8EngineProxy, NativeFunctionTemplateProxy *nativeFunctionTemplateProxy)
        {
            if (v8EngineProxy == null)
            {
                throw new ArgumentNullException("v8EngineProxy");
            }

            if (nativeFunctionTemplateProxy == null)
            {
                throw new ArgumentNullException("nativeFunctionTemplateProxy");
            }

            _Engine = v8EngineProxy;

            _NativeFunctionTemplateProxy = nativeFunctionTemplateProxy;

            InstanceTemplate        = new ObjectTemplate();
            InstanceTemplate.Parent = this;
            InstanceTemplate._Initialize(_Engine, V8NetProxy.GetFunctionInstanceTemplateProxy(_NativeFunctionTemplateProxy), false);

            PrototypeTemplate        = new ObjectTemplate();
            PrototypeTemplate.Parent = this;
            PrototypeTemplate._Initialize(_Engine, V8NetProxy.GetFunctionPrototypeTemplateProxy(_NativeFunctionTemplateProxy), false);

            OnInitialized();
        }
Exemple #3
0
        /// <summary>
        /// Creates a new CLR object which will be tracked by a new V8 native object.
        /// </summary>
        /// <param name="initialize">If true (default) then then 'IV8NativeObject.Initialize()' is called on the created wrapper before returning.</param>
        /// <typeparam name="T">A custom 'V8NativeObject' type, or just use 'V8NativeObject' as a default.</typeparam>
        public T CreateObject <T>(bool initialize = true)
            where T : V8NativeObject, new()
        {
            // ... create the new managed JavaScript object and store it (to get the "ID")...
            var obj = _CreateManagedObject <T>(null, null);

            try
            {
                // ... create a new native object and associated it with the new managed object ID ...
                obj._Handle._Set(V8NetProxy.CreateObject(_NativeV8EngineProxy, obj.ID));

                /* The V8 object will have an associated internal field set to the index of the created managed object above for quick lookup.  This index is used
                 * to locate the associated managed object when a call-back occurs. The lookup is a fast O(1) operation using the custom 'IndexedObjectList' manager.
                 */
            }
            catch (Exception ex)
            {
                // ... something went wrong, so remove the new managed object ...
                _RemoveObjectWeakReference(obj.ID);
                throw ex;
            }

            if (initialize)
            {
                obj.Initialize(false, null);
            }

            return((T)obj);
        }
Exemple #4
0
        ///// <summary>
        ///// Called when this handle has no more managed references, and the native garbage collector wants to collect this instance.
        ///// <para>Note: Handle disposal process only completes when there are no more managed references to the associated managed object as well.</para>
        ///// </summary>
        //??internal bool _OnNativeGCRequested()
        //{
        //    // ... the native V8 engine is requesting garbage collection ...
        //    var dispose = _IsDisposeReady;
        //    if (dispose && _ManagedObjectInfo != null)
        //        lock (_Engine._Objects)
        //        {
        //            _Engine._Objects.Remove(_ManagedObjectInfo._ID);
        //        }
        //    return dispose;
        //}

        ///// <summary>
        ///// If this is true, then the native handle was made weak, and this handle instance is pending disposal.
        ///// </summary>
        //??internal bool _NativeHandleIsWeak;

        //??internal void _MakeWeakNativeHandle()
        //{
        //    if (!_NativeHandleIsWeak)
        //    {
        //        V8NetProxy.MakeWeakHandle(_HandleProxy);
        //        _NativeHandleIsWeak = true;
        //    }
        //}

        //??internal void _MakeStrongNativeHandle()
        //{
        //    if (_NativeHandleIsWeak)
        //    {
        //        V8NetProxy.MakeStrongHandle(_HandleProxy);
        //        _NativeHandleIsWeak = false;
        //    }
        //}

        /// <summary>
        /// Completes the disposal of the native handle.
        /// <para>Note: A disposed native handle is simply cached for reuse, and always points back to the same managed handle.</para>
        /// </summary>
        internal void _ForceDisposal()
        {
            if (!_IsWeakManagedObject)
            {
                throw new InvalidOperationException("A managed object is still associated with this handle");
            }

            lock (this)
            {
                if (!IsDisposed)
                {
                    //??if (_NativeHandleIsWeak)
                    //    _MakeStrongNativeHandle(); // (prevent any GC callback just in case)

                    V8NetProxy.DisposeHandleProxy(_HandleProxy);
                    // (note: '_HandleProxy' is NOT set to null here because the connection has to be maintained for cache purposes)

                    GC.RemoveMemoryPressure((sizeof(HandleProxy) + sizeof(ValueProxy)));
                }

                if (_ManagedObjectInfo != null)
                {
                    _ManagedObjectInfo.Dispose();
                    _ManagedObjectInfo = null;
                    ManagedObjectID    = -1;
                }
            }
        }
Exemple #5
0
 /// <summary>
 /// Calls the native V8 proxy library to create an error string for use within the V8 JavaScript environment.
 /// <para>Note: The error flag exists in the associated proxy object only.  If the handle is passed along to another operation, only the string message will get passed.</para>
 /// </summary>
 public InternalHandle CreateError(string message, JSValueType errorType)
 {
     if (errorType >= 0)
     {
         throw new InvalidOperationException("Invalid error type.");
     }
     return(V8NetProxy.CreateError(_NativeV8EngineProxy, message, errorType));
 }
Exemple #6
0
 /// <summary>
 /// Creates a new native V8 object only.
 /// </summary>
 /// <param name="objectID">You can associate arbitrary NEGATIVE numbers with objects to use for tracking purposes.  The numbers have to be less than or
 /// equal to -2. Values greater or equal to 0 are used for internal tracking of V8NativeObject instances. -1 is a default value that is set automatically
 /// when new objects are created (which simply means "no ID is set").</param>
 public InternalHandle CreateObject(Int32 objectID = -2)
 {
     if (objectID > -2)
     {
         throw new InvalidOperationException("Object IDs must be <= -2.");
     }
     return(V8NetProxy.CreateObject(_NativeV8EngineProxy, objectID));
 }
Exemple #7
0
        //~Context() { V8NetProxy.DeleteContext(_NativeContext); _NativeContext = null; }

        public void Dispose()
        {
            if (_NativeContext != null)
            {
                V8NetProxy.DeleteContext(_NativeContext);
            }
            _NativeContext = null;
        }
Exemple #8
0
        NativeFunctionCallback _ProxyCallback; // (need to keep a reference)

        /// <summary>
        ///     Registers an invoke handler on the underlying native ObjectTemplate instance, which allows objects to be called like
        ///     a function.
        ///     <para>A proxy delegate is used and stored locally to prevent it from being reclaimed by the
        ///     GC. If you call this method again, the old proxy delegate will be replaced with a new one and registered again on
        ///     the native side. </para>
        /// </summary>
        /// <param name="callback"> A callback that gets invoked when the object is used like a function. </param>
        public void SetCallAsFunctionHandler(JSFunction callback)
        {
            _ProxyCallback = (managedObjectID, isConstructCall, _this, args, argCount) =>
            {
                return(FunctionTemplate._CallBack(managedObjectID, isConstructCall, _this, args, argCount, callback));
            };
            V8NetProxy.SetCallAsFunctionHandler(_NativeObjectTemplateProxy, _ProxyCallback);
        }
        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        /// Registers an invoke handler on the underlying native ObjectTemplate instance, which allows the object to be called like a method.
        /// </summary>
        /// <param name="callback">A callback that gets invoked </param>
        public void RegisterInvokeHandler(JSFunction callback)
        {
            V8NetProxy.RegisterInvokeHandler(_NativeObjectTemplateProxy, (managedObjectID, isConstructCall, _this, args, argCount)
                                             =>
            {
                return(FunctionTemplate._CallBack(managedObjectID, isConstructCall, _this, args, argCount, callback));
            });
            _Engine._StoreAccessor <JSFunction>(_NativeObjectTemplateProxy->ObjectID, "$__InvokeHandler", callback);
        }
        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        /// Calls the V8 'Set()' function on the underlying native function template to set properties that will exist on all function objects created from this template.
        /// </summary>
        public void SetProperty(string name, InternalHandle value, V8PropertyAttributes attributes = V8PropertyAttributes.Undefined)
        {
            if (name.IsNullOrWhiteSpace())
            {
                throw new ArgumentNullException("name (cannot be null, empty, or only whitespace)");
            }

            V8NetProxy.SetFunctionTemplateProperty(_NativeFunctionTemplateProxy, name, value, attributes);
        }
Exemple #11
0
        /// <summary>
        /// Registers an invoke handler on the underlying native ObjectTemplate instance, which allows the object to be called
        /// like a function.
        /// </summary>
        /// <param name="callback">A callback that gets invoked when the object is used like a function.</param>
        public void SetCallAsFunctionHandler(JSFunction callback)
        {
            ManagedJSFunctionCallback proxyCallback = (managedObjectID, isConstructCall, _this, args, argCount) =>
            {
                return(FunctionTemplate._CallBack(managedObjectID, isConstructCall, _this, args, argCount, callback));
            };

            V8NetProxy.SetCallAsFunctionHandler(_NativeObjectTemplateProxy, proxyCallback);
            _Engine._StoreAccessor(_NativeObjectTemplateProxy->ObjectID, "$__InvokeHandler", proxyCallback);
        }
Exemple #12
0
        /// <summary>
        /// Calls the native V8 proxy library to create a JavaScript array for use within the V8 JavaScript environment.
        /// </summary>
        public InternalHandle CreateArray(params InternalHandle[] items)
        {
            HandleProxy **nativeArrayMem = items.Length > 0 ? Utilities.MakeHandleProxyArray(items) : null;

            InternalHandle handle = V8NetProxy.CreateArray(_NativeV8EngineProxy, nativeArrayMem, items.Length);

            Utilities.FreeNativeMemory((IntPtr)nativeArrayMem);

            return(handle);
        }
Exemple #13
0
        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        ///     Calls the V8 'SetAccessor()' function on the underlying native object to create a property that is controlled by
        ///     "getter" and "setter" callbacks.
        ///     <para>WARNING: If you try to set managed accessors on a native-ONLY object (as in, this handle does not yet have a
        ///     managed-side object associated with it) then
        ///     <see cref="V8Engine.CreateObject(InternalHandle, bool)"/> will be called to create a wrapper object so the
        ///     accessor delegates will not get garbage collected, causing errors. You can optionally take control of this yourself
        ///     and call one of the 'CreateObject()' methods on <see cref="V8Engine"/>.</para>
        /// </summary>
        /// <param name="name"> The property name. </param>
        /// <param name="getter">
        ///     The property getter delegate that returns a value when the property is accessed within JavaScript.
        /// </param>
        /// <param name="setter">
        ///     The property setter delegate that sets AND returns a value when the property is accessed within JavaScript.
        /// </param>
        /// <param name="attributes"> (Optional) The attributes to assign to the property. </param>
        /// <param name="access"> (Optional) The access security on the property. </param>
        /// <seealso cref="M:V8.Net.IV8Object.SetAccessor(string,GetterAccessor,SetterAccessor,V8PropertyAttributes,V8AccessControl)"/>
        public virtual void SetAccessor(string name, GetterAccessor getter, SetterAccessor setter,
                                        V8PropertyAttributes attributes = V8PropertyAttributes.None, V8AccessControl access = V8AccessControl.Default)
        {
            attributes = _CreateAccessorProxies(Engine, name, getter, setter, attributes, access, ref _Getter, ref _Setter);

            Getter = getter;
            Setter = setter;

            V8NetProxy.SetObjectAccessor(this, ID, name, _Getter, _Setter, access, attributes);
        }
        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        /// Returns the specified V8Function object type associated with this function template.
        /// There can only ever be one native V8 function object per native V8 function template in a single native V8 JavaScript context;
        /// however, V8.NET (the managed side) does allow multiple function types per template. In this case, a single call triggers all derived types at once.
        /// The first callback to return a value terminates the cycle and any following callbacks are ignored.
        /// <para>WARNING: The returned function object will be garbage collected if you don't store the reference anywhere. If this happens, then calling
        /// the function object in JavaScript will return "undefined".</para>
        /// </summary>
        /// <typeparam name="T">A type that implements IV8Function, or derives from V8Function.</typeparam>
        /// <param name="callback">When a new instance of type 'T' is created, it's 'Callback' property will overwritten by this value (replacing anything that may be set when it was created).
        /// It is expect to provide a callback method when using the default 'V8Function' object, but if you have a custom derivation you can set this to 'null'.</param>
        public T GetFunctionObject <T>(JSFunction callback = null) where T : V8Function, new()
        {
            if (_Engine == null)
            {
                throw new InvalidOperationException("You must create object templates by calling one of the 'V8Engine.CreateFunctionTemplate()' overloads.");
            }

            if (_NativeFunctionTemplateProxy == null)
            {
                throw new InvalidOperationException("This managed function template is not initialized.");
            }

            int        funcID;
            V8Function func;

            lock (_FunctionsByType)
            {
                if (_FunctionsByType.TryGetValue(typeof(T), out funcID))
                {
                    func = _Engine._GetExistingObject(funcID) as V8Function;
                    if (func != null)
                    {
                        return((T)func);
                    }
                }
            }

            // ... get the v8 "Function" object ...

            InternalHandle hNativeFunc = V8NetProxy.GetFunction(_NativeFunctionTemplateProxy);

            // ... create a managed wrapper for the V8 "Function" object (note: functions inherit the native V8 "Object" type) ...

            func = _Engine._GetObject <T>(this, hNativeFunc, true, false); // (note: this will "connect" the native object [hNativeFunc] to a new managed V8Function wrapper, and set the prototype!)

            if (callback != null)
            {
                func.Callback = callback;
            }

            // ... get the function's prototype object, wrap it, and give it to the new function object ...
            // (note: this is a special case, because the function object auto generates the prototype object natively using an existing object template)

            func._Prototype.Set(V8NetProxy.GetObjectPrototype(func._Handle));

            lock (_FunctionsByType)
            {
                _FunctionsByType[typeof(T)] = func.ID; // (this exists to index functions by type)
            }

            func.Initialize(false, null);

            return((T)func);
        }
Exemple #15
0
        public override void Dispose() // (note: This can cause issues if removed while the native object exists [because of the callbacks].)
        {
            if (_NativeObjectTemplateProxy != null && CanDispose)
            {
                _Engine._ClearAccessors(_NativeObjectTemplateProxy->ObjectID);

                V8NetProxy.DeleteObjectTemplateProxy(_NativeObjectTemplateProxy); // (delete the corresponding native object as well; WARNING: This is done on the GC thread!)

                _NativeObjectTemplateProxy = null;
            }
        }
Exemple #16
0
        /// <summary>
        /// Calls the V8 'SetAccessor()' function on the underlying native 'v8::ObjectTenplate' instance to create a property that is controlled by "getter" and "setter" callbacks.
        /// <para>Note: This is template related, which means all objects created from this template will be affected by these special properties.</para>
        /// </summary>
        public void SetAccessor(string name,
                                GetterAccessor getter, SetterAccessor setter,
                                V8PropertyAttributes attributes = V8PropertyAttributes.None, V8AccessControl access = V8AccessControl.Default)
        {
            attributes = V8NativeObject._CreateAccessorProxies(Engine, name, getter, setter, attributes, access, ref _Getter, ref _Setter);

            Getter = getter;
            Setter = setter;

            V8NetProxy.SetObjectTemplateAccessor(_NativeObjectTemplateProxy, -1, name, _Getter, _Setter, access, attributes);
        }
        /// <summary>
        /// Unregisters handlers that intercept access to properties on ALL objects created by this template.  See <see cref="RegisterNamedPropertyInterceptors()"/> and <see cref="RegisterIndexedPropertyInterceptors()"/>.
        /// </summary>
        public void UnregisterPropertyInterceptors()
        {
            if (NamedPropertyInterceptorsRegistered)
            {
                V8NetProxy.UnregisterNamedPropertyHandlers(_NativeObjectTemplateProxy);

                V8NetProxy.UnregisterIndexedPropertyHandlers(_NativeObjectTemplateProxy);

                NamedPropertyInterceptorsRegistered = false;
            }
        }
        internal void _Initialize(V8Engine v8EngineProxy, string className)
        {
            ClassName = className;

            _Initialize(v8EngineProxy,
                        (NativeFunctionTemplateProxy *)V8NetProxy.CreateFunctionTemplateProxy(
                            v8EngineProxy._NativeV8EngineProxy,
                            ClassName,
                            _SetDelegate <NativeFunctionCallback>(_CallBack)) // (create a corresponding native object)
                        );
        }
        public void Dispose() // TODO: !!! This will cause issues if removed while the native object exists. !!!
        {
            if (_NativeObjectTemplateProxy != null)
            {
                _Engine._ClearAccessors(_NativeObjectTemplateProxy->ObjectID);

                V8NetProxy.DeleteObjectTemplateProxy(_NativeObjectTemplateProxy); // (delete the corresponding native object as well; WARNING: This is done on the GC thread!)
                _NativeObjectTemplateProxy = null;
            }

            ((IFinalizable)this).CanFinalize = true;
        }
Exemple #20
0
        /// <summary>
        /// Compiles JavaScript on the V8 engine and returns the result.
        /// Since V8 JIT-compiles script every time, repeated tasks can take advantage of re-executing pre-compiled scripts for a speed boost.
        /// </summary>
        /// <param name="script">The script to run.</param>
        /// <param name="sourceName">A string that identifies the source of the script (handy for debug purposes).</param>
        /// <param name="throwExceptionOnError">If true, and the return value represents an error, an exception is thrown (default is 'false').</param>
        /// <returns>A handle to the compiled script.</returns>
        public Handle Compile(string script, string sourceName = "V8.NET", bool throwExceptionOnError = false)
        {
            Handle result = V8NetProxy.V8Compile(_NativeV8EngineProxy, script, sourceName);

            // (note: speed is not an issue when executing whole scripts, so the result is returned in a handle object instead of a value [safer])

            if (throwExceptionOnError)
            {
                result.ThrowOnError();
            }

            return(result);
        }
        public override void Dispose()
        {
            if (_NativeFunctionTemplateProxy != null && CanDispose)
            {
                V8NetProxy.DeleteFunctionTemplateProxy(_NativeFunctionTemplateProxy); // (delete the corresponding native object as well; WARNING: This is done on the GC thread!)
                _NativeFunctionTemplateProxy = null;

                PrototypeTemplate.Parent = null;
                InstanceTemplate.Parent  = null;
                PrototypeTemplate        = null;
                InstanceTemplate         = null;
            }
        }
        /// <summary>
        /// Registers handlers that intercept access to properties on ALL objects created by this template.  The native V8 engine only supports this on 'ObjectTemplate's.
        /// </summary>
        public void RegisterIndexedPropertyInterceptors()
        {
            if (!IndexedPropertyInterceptorsRegistered)
            {
                V8NetProxy.RegisterIndexedPropertyHandlers(_NativeObjectTemplateProxy,
                                                           _SetDelegate <ManagedIndexedPropertyGetter>(_IndexedPropertyGetter),
                                                           _SetDelegate <ManagedIndexedPropertySetter>(_IndexedPropertySetter),
                                                           _SetDelegate <ManagedIndexedPropertyQuery>(_IndexedPropertyQuery),
                                                           _SetDelegate <ManagedIndexedPropertyDeleter>(_IndexedPropertyDeleter),
                                                           _SetDelegate <ManagedIndexedPropertyEnumerator>(_IndexedPropertyEnumerator));

                IndexedPropertyInterceptorsRegistered = true;
            }
        }
Exemple #23
0
 protected override bool _Finalize(bool finalizer) // (note: This can cause issues if removed while the native object exists [because of the callbacks].)
 {
     if (_NativeObjectTemplateProxy != null)
     {
         if (V8NetProxy.DeleteObjectTemplateProxy(_NativeObjectTemplateProxy)) // (delete the corresponding native object as well; WARNING: This may be done on the GC thread!)
         {
             _NativeObjectTemplateProxy = null;
         }
         else
         {
             return(false); // (bounced, a script might be in progress; try again later)
         }
     }
     return(true);
 }
Exemple #24
0
        public void Dispose()
        {
            if (_NativeFunctionTemplateProxy != null)
            {
                V8NetProxy.DeleteFunctionTemplateProxy(_NativeFunctionTemplateProxy); // (delete the corresponding native object as well; WARNING: This is done on the GC thread!)
                _NativeFunctionTemplateProxy = null;

                PrototypeTemplate.Parent = null;
                InstanceTemplate.Parent  = null;
                PrototypeTemplate        = null;
                InstanceTemplate         = null;
            }

            ((IFinalizable)this).CanFinalize = true;
        }
        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        /// Calls the V8 'Set()' function on the underlying native object template to set properties that will exist on all objects created from this template.
        /// </summary>
        public void SetProperty(string name, InternalHandle value, V8PropertyAttributes attributes = V8PropertyAttributes.None)
        {
            try
            {
                if (name.IsNullOrWhiteSpace())
                {
                    throw new ArgumentNullException("name (cannot be null, empty, or only whitespace)");
                }

                V8NetProxy.SetObjectTemplateProperty(_NativeObjectTemplateProxy, name, value, attributes);
            }
            finally
            {
                value._DisposeIfFirst();
            }
        }
Exemple #26
0
        /// <summary>
        /// Executes JavaScript on the V8 engine and returns the result.
        /// </summary>
        /// <param name="script">The script to run.</param>
        /// <param name="sourceName">A string that identifies the source of the script (handy for debug purposes).</param>
        /// <param name="throwExceptionOnError">If true, and the return value represents an error, an exception is thrown (default is 'false').</param>
        public Handle Execute(Handle script, bool throwExceptionOnError = false)
        {
            if (script.ValueType != JSValueType.Script)
            {
                throw new InvalidOperationException("The handle must represent pre-compiled JavaScript.");
            }

            Handle result = V8NetProxy.V8ExecuteCompiledScript(_NativeV8EngineProxy, script);

            // (note: speed is not an issue when executing whole scripts, so the result is returned in a handle object instead of a value [safer])

            if (throwExceptionOnError)
            {
                result.ThrowOnError();
            }

            return(result);
        }
Exemple #27
0
        public void Dispose()
        {
            if (_NativeV8EngineProxy != null)
            {
                _TerminateWorker(); // (will return only when it has successfully terminated)

                // ... clear all handles of object IDs for disposal ...

                HandleProxy *hProxy;

                for (var i = 0; i < _HandleProxies.Length; i++)
                {
                    hProxy = _HandleProxies[i];
                    if (hProxy != null && !hProxy->IsDisposed)
                    {
                        hProxy->_ObjectID = -2; // (note: this must be <= -2, otherwise the ID auto updates -1 to -2 to flag the ID as already processed)
                    }
                }

                // ... allow all objects to be finalized by the GC ...

                ObservableWeakReference <V8NativeObject> weakRef;

                for (var i = 0; i < _Objects.Count; i++)
                {
                    if ((weakRef = _Objects[i]) != null && weakRef.Object != null)
                    {
                        weakRef.Object._ID      = null;
                        weakRef.Object.Template = null;
                        weakRef.Object._Handle  = ObjectHandle.Empty;
                    }
                }

                // ... destroy the native engine ...

                if (_NativeV8EngineProxy != null)
                {
                    _Engines[_NativeV8EngineProxy->ID] = null; // (notifies any lingering handles that this engine is now gone)
                    V8NetProxy.DestroyV8EngineProxy(_NativeV8EngineProxy);
                    _NativeV8EngineProxy = null;
                }
            }
        }
Exemple #28
0
        /// <summary>
        /// Calls the native V8 proxy library to create the value instance for use within the V8 JavaScript environment.
        /// <para>This overload provides a *quick way* to construct an array of strings.
        /// One big memory block is created to marshal the given strings at one time, which is many times faster than having to create an array of individual native strings.</para>
        /// </summary>
        public InternalHandle CreateValue(IEnumerable <string> items)
        {
            if (items == null)
            {
                return(V8NetProxy.CreateArray(_NativeV8EngineProxy, null, 0));
            }

            var itemsEnum  = items.GetEnumerator();
            int strBufSize = 0; // (size needed for the string chars portion of the memory block)
            int itemsCount = 0;

            while (itemsEnum.MoveNext())
            {
                // get length of all strings together
                strBufSize += itemsEnum.Current.Length + 1; // (+1 for null char)
                itemsCount++;
            }

            itemsEnum.Reset();

            int    strPtrBufSize     = Marshal.SizeOf(typeof(IntPtr)) * itemsCount; // start buffer size with size needed for all string pointers.
            char **oneBigStringBlock = (char **)Utilities.AllocNativeMemory(strPtrBufSize + Marshal.SystemDefaultCharSize * strBufSize);
            char **ptrWritePtr       = oneBigStringBlock;
            char * strWritePtr       = (char *)(((byte *)oneBigStringBlock) + strPtrBufSize);
            int    itemLength;

            while (itemsEnum.MoveNext())
            {
                itemLength = itemsEnum.Current.Length;
                Marshal.Copy(itemsEnum.Current.ToCharArray(), 0, (IntPtr)strWritePtr, itemLength);
                Marshal.WriteInt16((IntPtr)(strWritePtr + itemLength), 0);
                Marshal.WriteIntPtr((IntPtr)ptrWritePtr++, (IntPtr)strWritePtr);
                strWritePtr += itemLength + 1;
            }

            InternalHandle handle = V8NetProxy.CreateStringArray(_NativeV8EngineProxy, oneBigStringBlock, itemsCount);

            Utilities.FreeNativeMemory((IntPtr)oneBigStringBlock);

            return(handle);
        }
        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        /// Calls the underlying native function to create and return a new instance, which will be wrapped in the specified managed object type.
        /// </summary>
        /// <typeparam name="T">A managed object type to wrap the new native object handle.</typeparam>
        /// <param name="args">Arguments to pass to the function to construct the new native instance.</param>
        /// <returns>A new instance of 'T'.</returns>
        public V8ManagedObject CreateInstance <T>(params InternalHandle[] args) // TODO: Parameter passing needs testing.
            where T : V8ManagedObject, new()
        {
            HandleProxy **_args = null;

            if (args.Length > 0)
            {
                _args = (HandleProxy **)Utilities.AllocPointerArray(args.Length);
                for (var i = 0; i < args.Length; i++)
                {
                    _args[i] = args[i];
                }
            }

            // (note: the special case here is that the native function object will use its own template to create instances)

            T obj = _Engine._CreateManagedObject <T>(this, null);

            obj.Template = InstanceTemplate;

            try
            {
                obj._Handle.Set(V8NetProxy.CreateInstanceFromFunctionTemplate(_NativeFunctionTemplateProxy, obj.ID, args.Length, _args));
                // (note: setting '_NativeObject' also updates it's '_ManagedObject' field if necessary.

                obj.Initialize(true, args);
            }
            catch (Exception ex)
            {
                // ... something went wrong, so remove the new managed object ...
                _Engine._RemoveObjectWeakReference(obj.ID);
                throw ex;
            }
            finally
            {
                Utilities.FreeNativeMemory((IntPtr)_args);
            }

            return(obj);
        }
        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        /// Calls the underlying native function to create a new native object and return its handle.
        /// Use this method if you only need the native object and not a managed wrapper.
        /// </summary>
        /// <param name="args">Arguments to pass to the function to construct the new native instance.</param>
        /// <returns>A handle to the new object.</returns>
        public InternalHandle CreateNativeInstance(params InternalHandle[] args) // TODO: Parameter passing needs testing.
        {
            HandleProxy **_args = null;

            if (args.Length > 0)
            {
                _args = (HandleProxy **)Utilities.AllocPointerArray(args.Length);
                for (var i = 0; i < args.Length; i++)
                {
                    _args[i] = args[i];
                }
            }

            try
            {
                return((InternalHandle)V8NetProxy.CreateInstanceFromFunctionTemplate(_NativeFunctionTemplateProxy, -1, args.Length, _args));
            }
            finally
            {
                Utilities.FreeNativeMemory((IntPtr)_args);
            }
        }