v8dotnet
v8dotnet copied to clipboard
Instantiation of classes which inherit from V8NativeObject in JS not working
The following sample throws a V8ExecutionErrorException
in TypeBinder::_BindTypeMembers
, because handle.Object
in line 1561 of V8Engine_Binding.cs returns null:
[ScriptObject("Person", ScriptMemberSecurity.NoAcccess)]
public class Person : V8NativeObject
{
[ScriptMember("name", ScriptMemberSecurity.ReadWrite)]
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
[ScriptMember("toString", ScriptMemberSecurity.ReadWrite)]
public override string ToString()
{
return Name;
}
}
Engine.RegisterType<Person>();
Engine.GlobalObject.SetProperty(typeof(Person));
Engine.Execute
(@"
function test() {
var p = new Person('Bruce');
}
test();
", "V8.NET", true, 0);
As always, thank you. :-)
Another sample does not throw an exception, but does not work as well:
Engine.RegisterType<Person>();
Engine.GlobalObject.SetProperty(typeof(Person));
Person p2 = Engine.CreateObject<Person>();
p2.Name = "Jackie";
Engine.GlobalObject.SetProperty("p2", p2, null, null, ScriptMemberSecurity.Locked);
Engine.Execute
(@"
function hello() {
Browser.println(p2.name); // Output: undefined
if (p2.name == undefined) // True
Browser.println('undefined'); // Output: undefined
}
hello();
", "V8.NET", true, 0);
Inheriting from V8NativeObject and registering it as a type to be used is not supported (yet). Just remove V8NativeObject and use the inject attribute (or better yet, it's faster to just include the engine in the constructor like this: Person(string name, V8Engine engine)
).
Let me show you what is happening currently (and why what you are doing is not supported yet):
-
You call
new Person('Bruce')
to create a new JS object. -
The hooks call into the CLR side to create a new managed object binder for that type.
-
Line 1560 of
V8Engine_Binding.cs
tries to: a) callActivator.CreateInstance(BoundType, convertedArguments)
to create a new instance, and b) CallsEngine.CreateBinding()
to get a binder (anObjectBinder
instance - just CLR bridging hooks) for the new instance. -
Engine.CreateBinding()
(online 2258) checks if the object is a supported handle object (which it is, since you are inheriting from a handle-based object). That tells the system to use the HANDLE value, and NOT the object itself.if (objType == null || obj is IHandleBased) return CreateValue(obj, recursive, memberSecurity);
-
That results in a call to this on line 930-931 of
V8Engine.cs
:if (value == null) return CreateNullValue(); else if (value is IHandleBased) return ((IHandleBased)value).InternalHandle; // (already a V8.NET value!) else ...
-
This scenario is not supported by the binder, which then dies on this line:
handle = Engine.CreateBinding(Activator.CreateInstance(BoundType, convertedArguments), null, _Recursive, _DefaultMemberSecurity, false); handle.Object.Initialize(true, args); <<<<<<< ERROR, Object was never set yet in the binding system because of the handle detection on step 4.
Handles are treated like remote values (which they are), and inheriting from it assumes you want to work with the value on the V8 side, not the object on the managed side.