This project is read-only.

How cross platform is it? How to use V8ManagedObject properly? How to access array items?

Nov 27, 2013 at 1:54 AM
Hi James,

It sounds like this project works in Mono.
Has it been tested on Mac and Linux?
Could it work on iOS or Android via Xamarin?
If not supported today is it possible and planned for the future?

I'm currently using a different V8 wrapper for .NET that is written in C++ CLI, which is not supported in Mono so finding this project was a pleasant surprise.

Thanks,
Ian Reed
Nov 27, 2013 at 3:15 AM
While I'm asking questions:
I noticed that .NET 4.0 Client Profile is not supported due to a dependency on System.Web.
Does System.Web play an important part or would it be possible to remove this dependency so it would work in .NET 4.0 Client Profile?
Nov 27, 2013 at 8:47 PM
Hi Ian,

I created the project with Mono in mind, but I do not have an official port to Mono, nor to any other OS as of yet. That said, look at this:
https://v8dotnet.codeplex.com/workitem/1273
You could try posting there for help instead.
Yes - this is planned, as the whole point of the Mono support is cross-platform focus. :)

The reference to System.Web was simply to support that fact that the working path is different under IIS than when running as a desktop application. When you compile a web app, the location of the libraries end up in the 'bin' path. You can see the line with "HttpContext.Current.Server.MapPath("~/bin");" in the "Assembly Resolver()" method for V8Engine. If you don't plan on using the web, then you don't need it. :) Perhaps I'll look into dynamically loading the Assembly if available, and fail silently.

Thanks,
James
Nov 28, 2013 at 5:49 PM
Hi James,

Thanks for humoring my newbie questions. :)

I am in a scenario where I don't need the web and didn't want to change my project from .NET 4 client profile to full .NET 4 if I didn't have to.
Sounds like I can figure out how to build the source and remove the line you mentioned.
Though if you are up for loading the library dynamically that would be nice, but I understand you have a lot more to consider than just my project.

For the little I've played with V8.NET I have to say it looks pretty awesome so far.

I'm used to noesis.javascript.net so it's a little lower level than what I want 95% of the time.
So I'm wrapping it in a little class to make it easier for me to use.

One question I have is this:
In noesis I could call JavascriptContext.SetParameter("say", (Action<object>)MySayFunction);
Then in javascript call say like: say("Hello");

In V8.NET I tried this:
v8.GlobalObject.SetProperty("say", (Action<string>)Console.WriteLine, null, true, ScriptMemberSecurity.Locked);
But when I call say in script I get this error:
V8.Net.V8CompilerErrorException: TypeError: Property 'say' of object [object Object] is not a function

I know you've got the capability to handle a delegate as I saw you setting the entire Console type as an object and I was able to call Console.WriteLine using that.
What am I doing wrong here?

I was also very happy to see the nice javascript stack traces the exceptions output.
Do you have plans to support sourcemaps?
I ask because I'm currently using Typescript and then hand the compiled JS to my game engine which hands it to noesis.javascript.net.
I haven't yet implemented something to convert the line and column numbers noesis gives me back to the typescript line and columns using the sourcemap.

I'm actually not terribly familiar with sourcemaps, so maybe that's not really a V8.Net thing but something I should be handling at a higher level anyway.
If I did I guess I'd parse the javascript stack trace from the exception message and then do the rest of the source map handling.

Sorry for the random questions in this post. I can start creating a new discussion for each question if you prefer.

Thanks again for your help and for V8.NET!
And have a Happy Thanksgiving!
Ian Reed
Nov 28, 2013 at 10:31 PM
Edited Nov 28, 2013 at 10:33 PM
v8.GlobalObject.SetProperty("say", (Action<string>)Console.WriteLine, null, true, ScriptMemberSecurity.Locked); 
That line will only pass the delegate object for binding (the object itself). It will not bind the method as a callable function (though you could try calling the "Invoke()" method on it ;) ). There is no binding for methods directly (yet). You have to bind an object that contains methods.

That said, what you want to do is create a function template, then get a reference to a native function to call; something like this:
v8.GlobalObject.SetProperty("say", v8.CreateFunctionTemplate().GetFunctionObject(MySayFunction), V8PropertyAttributes.???);
So with this line you would be setting "say" in the global context to a HANDLE (it all boils down to handles) to a function object (the managed V8Function object returned, which can implicitly cast to a handle type). You can optionally set access security on it as well.

The goal of V8.NET is to be a direct interface (as much possible) to the V8 engine. I don't think the V8 engine supports source maps. That said, if I have time, I can look into providing a class (if one doesn't already exist) to help load and parse the maps. This would most likely be a separate utility class within the assembly. Rest assured, I have been thinking about it. I also use TypeScript for my open source DreamSpace Web-Framework project, and the studio shell (which uses V8.NET) loads compiled TypeScript files - so yes, I can see this coming down the road. ;)
Nov 29, 2013 at 7:42 PM
Thanks! The function template stuff worked out great and my say and other methods are working as I'd hoped.

A few more questions:
I made a simple Person class, used SetProperty() to pass it to V8, used GetProperty to get it back, and interacted with it in javascript.
All seems well with that.
Then I set the Person class to inherit from V8ManagedObject and when calling SetProperty I get the following exception:
System.InvalidCastException: Specified cast is not valid.
at V8.Net.InternalHandle.SetProperty(String name, Object obj, String className, Nullable1 recursive, Nullable1 memberSecurity)
at V8.Net.ObjectHandle.SetProperty(String name, Object obj, String className, Nullable1 recursive, Nullable1 memberSecurity)
at V8DotNetTest.MyV8.<>c__DisplayClass7.<Set>b__6() in C:\Data\Dev\Mine\New\2010\V8DotNetTest\V8DotNetTest\MyV8.cs:line 51
at V8.Net.V8NetProxy.WithV8ContextScope(NativeV8EngineProxy* engine, Action action)
at V8.Net.V8Engine.set_WithContextScope(Action value)
at V8DotNetTest.MyV8.Set(String name, Object value) in C:\Data\Dev\Mine\New\2010\V8DotNetTest\V8DotNetTest\MyV8.cs:line 39
at V8DotNetTest.Program.RunMainCode() in C:\Data\Dev\Mine\New\2010\V8DotNetTest\V8DotNetTest\Program.cs:line 68
at V8DotNetTest.Program.Main(String[] args) in C:\Data\Dev\Mine\New\2010\V8DotNetTest\V8DotNetTest\Program.cs:line 20

Do I misunderstand how to use V8ManagedObject?
My goal is to duplicate the setup I had when using noesis.javascript.net.
Essentially I had a JSObject class that had JavascriptGetMember and JavascriptSetMember methods that noesis would call whenever a normal property was not found on my object.
Internally the JSObject kept a Dictionary<string, object> to store and return values.
So my specifically typed objects inherited from this JSObject so they could provide C# properties but also support javascript setting extra properties on them.
Off hand V8ManagedObject seems pretty similar to my JSObject, except with tons more methods that can be overriden for extra flexibility.

Even if you can easily tell me how I'm misusing V8ManagedObject I have one other question:
My JSObject had the Serializable attribute so I could use a BinaryFormatter to serialize my game state for game saves.
My understanding is that each class in the hierarchy must have the Serializable attribute for this to work, which would mean both V8ManagedObject and V8NativeObject would need it.

Could it easily be added to both classes or should I go down the road of implementing my own object that has it's own interceptors?
And if I should implement my own can you direct me to the best place to start looking at how to do that?
Off hand I think I'd look at your implementation of V8ManagedObject as a guide.

Thanks much!
Ian Reed
Nov 29, 2013 at 10:42 PM
Hi,

There are bugs in the current release - try using the latest source or release binaries from the development branch. I have already resolved a lot of issues. I will soon be making an official release.

V8NativeObject doesn't store any data related the the JavaScript environment. That object is a basic wrapper for native-only objects. The V8ManagedObject is ONLY useful as an object created from 'ObjectTemplate' instances (i.e. "var t = {V8Engine}.CreateObjectTemplate(); var obj = t.CreateObject(); / where t is 'V8ManagedObject', or an object derived from it/"). Native objects are faster, so if speed is needed, stick with V8NativeObject, and keep all your values in the JS environment. If you want to access the values on the managed side (i.e. from the dictionary object) to prevent having to call into the native side every time (i.e. a managed side focused app), then V8ManagedObject may be more useful (since all values are stored on the managed side). V8ManagedObject has many virtual methods that are hooks for get/set operations that can be handy as well. Tip: Returning default values, such as "InternalHandle.Empty", from these methods will tell V8 to handle it as normal, and will cause the native V8 engine to set the value on the native object instead (a fallback within V8 itself).

Hope that helps! :) Feel free to post actual code if you need more help on it.
Dec 4, 2013 at 9:09 PM
Hi, thanks I got the latest development release a few days ago and got it building.
I also got it building for client profile by commenting out the System.Web stuff.

Sorry for my late response, I was hoping to get my next big questions sorted out before responding.
I've spent some time trying to get V8.NET working in my existing game engine but have run into some problems.
They are not easy to isolate because it's a reasonably large code base.

So for now I have a smaller question.
I'm having trouble getting the NamedProperty methods of an object inheriting from V8ManagedObject to be called.

Here is a simple test app to demonstrate my problem:
using System;
using V8.Net;

namespace V8ManagedObjectTest
{
class Program
{
    static void Main(string[] args)
    {
        var v8 = new V8Engine();
        v8.WithContextScope = () =>
            {
                var template = v8.CreateObjectTemplate();
                template.RegisterNamedPropertyInterceptors();
                Console.WriteLine(template.NamedPropertyInterceptorsRegistered);
                var obj = template.CreateObject<V8ObjectWrapper>();
                v8.GlobalObject.SetProperty("test", obj, V8PropertyAttributes.None);
                v8.Execute(@"
test.prop = 'Hello';
var temp = test.prop;
");
            };
        Console.WriteLine("Finished");
        Console.ReadKey();
    }
} // end class
public class V8ObjectWrapper : V8ManagedObject
{
    public override InternalHandle GetProperty(string name)
    {
        Console.WriteLine("GetProperty called on " + name);
        return base.GetProperty(name);
    }
    public override bool SetProperty(string name, InternalHandle value, V8PropertyAttributes attributes = V8PropertyAttributes.None)
    {
        Console.WriteLine("SetProperty called on " + name);
        return base.SetProperty(name, value, attributes);
    }
} // end V8ObjectWrapper class
}

The output I get is:
True
Finished

I expect to see some lines about the GetProperty and SetProperty methods being called.

Can you see what I'm doing wrong?
Dec 4, 2013 at 11:05 PM
And another question.
In the summary of the V8ManagedObject class it says:
By default, this object is used for the global environment.

Is this referring to the {V8Engine}.GlobalObject?
If so, how can I set the GlobalObject to be a native V8 object to get speed benefits when doing things like:
for (var i=0; i < list.length; i++)

Thanks! :)
Ian Reed
Dec 5, 2013 at 6:10 AM
I can't help unless you use the current dev release binaries or source. "WithContextScope" doesn't exist anymore, and you may be experiencing bugs that were fixed.

Dec 5, 2013 at 6:11 AM
Sorry, that hasn't been updated yet - the global object is now a native object. :)

Dec 5, 2013 at 6:49 AM
Ah, sorry, I thought I was using the development branch.

I did basically this:
git clone https://git01.codeplex.com/v8dotnet
Then this to get the development branch:
git checkout -b development origin/development

But I am not very familiar with git so this could easily be wrong.
I'll look into it more.

Also good to hear about the global object being native. :)
Dec 5, 2013 at 6:56 AM
Ah, I guess I was still on master. Sorry about that. I'll retest and get back to you.
Thanks for your patience! :)
Dec 5, 2013 at 7:23 AM
I get the same output when using the development branch.
Here is the slightly altered code.

using System;
using V8.Net;

namespace V8ManagedObjectTest
{
class Program
{
    static void Main(string[] args)
    {
        var v8 = new V8Engine();
        var template = v8.CreateObjectTemplate();
        template.RegisterNamedPropertyInterceptors();
        Console.WriteLine(template.NamedPropertyInterceptorsRegistered);
        var obj = template.CreateObject<V8ObjectWrapper>();
        v8.GlobalObject.SetProperty("test", obj, V8PropertyAttributes.None);
        v8.Execute(@"
test.prop = 'Hello';
var temp = test.prop;
");
        Console.WriteLine("Finished");
        Console.ReadKey();
    }
} // end class
public class V8ObjectWrapper : V8ManagedObject
{
    public override InternalHandle GetProperty(string name)
    {
        Console.WriteLine("GetProperty called on " + name);
        return base.GetProperty(name);
    }
    public override bool SetProperty(string name, InternalHandle value, V8PropertyAttributes attributes = V8PropertyAttributes.None)
    {
        Console.WriteLine("SetProperty called on " + name);
        return base.SetProperty(name, value, attributes);
    }
} // end V8ObjectWrapper class
}
Dec 5, 2013 at 5:09 PM
Don't do this...
template.RegisterNamedPropertyInterceptors();
That is done automatically, and calling it again does nothing. You should only call that if you disabled it when calling "v8.CreateObjectTemplate();" (see the optional parameter).

In regards to the callbacks, your issue is you are override the wrong methods. ;) Those methods are called on the managed side to get/set native properties (that is the main purpose), but because V8ManagedObject is focused on the managed side, those methods are overridden to apply to the managed side property lists FIRST, then fall back to the native side of no managed side property is found. You should never need to override those unless you want to hook into that behaviour. The names of the methods you want to override can be found in the "IV8ManagedObject" interface (i.e. "NamedPropertyGetter", "NamedPropertySetter", etc.).

Hope that helps. ;)
Dec 6, 2013 at 3:14 AM
It definitely helped! :) My integration of V8.NET into my existing app is working quite nicely now too.

Here's one more question:
If I have a Handle I can check if it's an array with IsArray and see it's length with ArrayLength.
How do I get at the Handles of the items in the array?
Dec 6, 2013 at 3:35 AM
Use "GetProperty(#)" - an array is just an object, and the index is simply numerical properties (i.e. var a=['T'], t = a["0"] is the same as a[0]).
Dec 6, 2013 at 3:50 AM
GetProperty does not exist on a Handle.
I do see this property:
public V8NativeObject Object { get; }
But it is null for a handle that corresponds to a javascript array.
How do I convert it into somethign that does have the GetProperty method?
Dec 6, 2013 at 4:00 AM
Edited Dec 6, 2013 at 4:01 AM
Handle is an object that just calls into InternalHandle. ObjectHandle is also a handle, that derives from Handle, and also calls into InternalHandle. In either case, you case cast to "InternalHandle" and call the method there, or create an ObjectHandle instead.
Handle arrayHandle= ???;
InternalHandle h = arrayHandle;
h.GetProperty(...);
...or more directly...
ObjectHandle arrayHandle = ???;
arrayHandle.GetProperty(...);
InternalHandle is a struct for fast stack based usage, and since structs don't support inheritance, I place ALL the core functionality in it. End users can use Handle and ObjectHandle, to wrap InternalHandle struct values so it can be tracked by the GC. "Handle" is a basic handle object that may not reference an object. You can implicitly case to ObjectHandle at any time, but there is a cast check to confirm the source handle of the cast is actually an object.

Hope that helps! :)
Dec 6, 2013 at 4:07 AM
Just to add, "GetProperty(#)" exists on these types: ObjectHandle, InternalHandle, and V8NativeObject.
Dec 6, 2013 at 4:15 AM
Thanks very much!
That makes it much clearer for me.
I now seem problem free with V8.NET completely replacing my previous noesis.javascript.net code.

I appreciate all the help, and especially you writing V8.NET in the first place.
I'm sure I'll have more questions down the road though. :)

All the best!
Ian Reed
Dec 6, 2013 at 4:27 AM
Edited Dec 6, 2013 at 4:27 AM
Sounds great, glad to help anytime! Always feel free to post actual code if you ever need help. 8)