/// <summary> /// Wraps a given object handle with a managed object, and optionally associates it with a template instance. /// <para>Note: Any other managed object associated with the given handle will cause an error. /// You should check '{Handle}.HasManagedObject', or use the "GetObject()" methods to make sure a managed object doesn't already exist.</para> /// <para>This was method exists to support the following cases: 1. The V8 context auto-generates the global object, and /// 2. V8 function objects are not generated from templates, but still need a managed wrapper.</para> /// <para>Note: </para> /// </summary> /// <typeparam name="T">The wrapper type to create (such as V8ManagedObject).</typeparam> /// <param name="v8Object">A handle to a native V8 object.</param> /// <param name="initialize">If true (default) then then 'IV8NativeObject.Initialize()' is called on the created object before returning.</param> internal T _CreateObject <T>(ITemplate template, InternalHandle v8Object, bool initialize = true, bool connectNativeObject = true) where T : V8NativeObject, new() { try { if (!v8Object.IsObjectType) { throw new InvalidOperationException("An object handle type is required (such as a JavaScript object or function handle)."); } // ... create the new managed JavaScript object, store it (to get the "ID"), and connect it to the native V8 object ... var obj = _CreateManagedObject <T>(template, v8Object.PassOn(), connectNativeObject); if (initialize) { obj.Initialize(false, null); } return(obj); } finally { v8Object._DisposeIfFirst(); } }
// -------------------------------------------------------------------------------------------------------------------- /// <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) { try { if (name.IsNullOrWhiteSpace()) { throw new ArgumentNullException("name (cannot be null, empty, or only whitespace)"); } V8NetProxy.SetFunctionTemplateProperty(_NativeFunctionTemplateProxy, name, value, attributes); } finally { value._DisposeIfFirst(); } }
// -------------------------------------------------------------------------------------------------------------------- /// <summary> /// Same as "GetObject()", but used internally for getting objects that are associated with templates (such as getting function prototype objects). /// </summary> internal T _GetObject <T>(ITemplate template, InternalHandle handle, bool createIfNotFound = true, bool initializeOnCreate = true, bool connectNativeObject = true) where T : V8NativeObject, new() { T obj = null; try { if (handle.IsEmpty) { return(null); } if (handle.Engine != this) { throw new InvalidOperationException("The specified handle was not generated from this V8Engine instance."); } var weakRef = _GetObjectWeakReference(handle.ObjectID); // (if out of bounds or invalid, this will simply return null) if (weakRef != null) { obj = weakRef.Reset() as T; if (obj != null && !typeof(T).IsAssignableFrom(obj.GetType())) { throw new InvalidCastException("The existing object of type '" + obj.GetType().Name + "' cannot be converted to type '" + typeof(T).Name + "'."); } } if (obj == null && createIfNotFound) { handle.ObjectID = -1; // (managed object doesn't exist [perhaps GC'd], so reset the ID) obj = _CreateObject <T>(template, handle.PassOn(), initializeOnCreate, connectNativeObject); } } finally { handle._DisposeIfFirst(); } return(obj); }
// -------------------------------------------------------------------------------------------------------------------- /// <summary> /// Creates an uninitialized managed object ONLY (does not attempt to associate it with a JavaScript object, regardless of the supplied handle). /// <para>Warning: The managed wrapper is not yet initialized. When returning the new managed object to the user, make sure to call /// '_ObjectInfo.Initialize()' first. Note however that new objects should only be initialized AFTER setup is completed so the users /// (developers) can write initialization code on completed objects (see source as example for 'FunctionTemplate.GetFunctionObject()').</para> /// </summary> /// <typeparam name="T">The wrapper type to create (such as V8ManagedObject).</typeparam> /// <param name="template">The managed template reference that owns the native object, if applicable.</param> /// <param name="handle">The handle to the native V8 object.</param> /// <param name="connectNativeObject">If true (the default), then a native function is called to associate the native V8 object with the new managed object. /// Set this to false if native V8 objects will be associated manually for special cases. This parameter is ignored if no handle is given (hNObj == null).</param> internal T _CreateManagedObject <T>(ITemplate template, InternalHandle handle, bool connectNativeObject = true) where T : V8NativeObject, new() { T newObject; try { if (typeof(V8ManagedObject).IsAssignableFrom(typeof(T)) && template == null) { throw new InvalidOperationException("You've attempted to create the type '" + typeof(T).Name + "' which implements IV8ManagedObject without a template (ObjectTemplate). The native V8 engine only supports interceptor hooks for objects generated from object templates. At the very least, you can derive from 'V8NativeObject' and use the 'SetAccessor()' method."); } if (!handle.IsUndefined) { if (!handle.IsObjectType) { throw new InvalidOperationException("The specified handle does not represent an native V8 object."); } else if (connectNativeObject && handle.HasObject) { throw new InvalidOperationException("Cannot create a managed object for this handle when one already exists. Existing objects will not be returned by 'Create???' methods to prevent initializing more than once."); } } newObject = new T(); newObject._Engine = this; newObject.Template = template; newObject.Handle = handle; using (_ObjectsLocker.WriteLock()) // (need a lock because of the worker thread) { newObject.ID = _Objects.Add(new ObservableWeakReference <V8NativeObject>(newObject)); } if (!handle.IsUndefined) { if (connectNativeObject) { try { void *templateProxy = (template is ObjectTemplate) ? (void *)((ObjectTemplate)template)._NativeObjectTemplateProxy : (template is FunctionTemplate) ? (void *)((FunctionTemplate)template)._NativeFunctionTemplateProxy : null; V8NetProxy.ConnectObject(handle, newObject.ID, templateProxy); /* 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(newObject.ID); handle.ObjectID = -1; // (existing ID no longer valid) throw ex; } } } } finally { handle._DisposeIfFirst(); } return(newObject); }