This project is read-only.

Adding custom handling of the function calls from JavaScript to managed C# code

Jan 20, 2014 at 6:22 PM
Edited Jan 20, 2014 at 6:39 PM
Hi,

I am writing a scripting system for a virtual world, which allows developers to expose C# objects to the script. However, to allow implementing security I need to be able to check parameters of the method which is called and supply some of them myself.

Please let me explain with an example. Let say there is the following C# class:
public class Example 
{
    public void MyMethod(IObject obj, IUser user, int param1, float param2);
}
It is exposed to JavaScript as following:
V8Engine engine = new V8Engine();
Example example = new Example();
engine.GlobalObject.SetProperty("example", example, null, true,
                                V8PropertyAttributes.Locked);
What I would like to achieve is that when the method is called as following:
example.MyMethod(42, 3.14);
it should invoke a custom handler, which can supply the values for obj and user parameters based on the V8Engine used to execute the given script and some external data. This is how such a custom handler could roughly look like:
public object JSCallHandler(V8Engine engine, object[] args, MethodInfo targetMethod,
                            object targetObject) 
{
    List<object> newArgs = new List<object>();
    if (targetMethod.GetParameters()[1].ParameterType.Equals(typeof(IUser)))
        newArgs.Add(userDict[engine]);
    if (targetMethod.GetParameters()[0].ParameterType.Equals(typeof(IObject)))
        newArgs.Add(objectDict[engine]);
    newArgs.AddRange(args);
    return targetMethod.Invoke(targetObject, newArgs.ToArray());
}

private IDictionary<V8Engine, IUser> userDict;
private IDictionary<V8Engine, IObject> objectDict;
Is this easily possible or must I rewrite the internals of V8 or V8.NET for this?
Jan 21, 2014 at 5:54 AM
Hi,

I think there are two ways: 1. Don't use the binder and manually expose the class to the script world, or 2. Use the binder and implement IV8NativeObject, and then implement the Initialize() method to overwrite a bound method (the associated JS property) with your own, storing the existing value to call it later as well.

Let me know if you need more details.
Jan 21, 2014 at 7:31 PM
Scripting component has a generic interface which allows to register an arbitrary object. Threfore, binder is required as the class of this object is not known in advance. Additionally, an interface to the component should be simple to use and asking developers to derive from some class they don't know and have to implement the Initialize method is hardly simple. Essentially, devs should know nothing about V8.NET and should be able just to pass an arbitrary object with methods, which may have IUser and IObject parameters. The rest must be encapsulated within the scripting component.

I wonder, what if I create a wrapper class derived from IV8NativeObject for each such registered object, which will intercept all method calls, figure out if additional arguments are needed and call respective methods in the original object? Would that work?
Jan 21, 2014 at 9:23 PM
Edited Jan 21, 2014 at 9:25 PM
Actually, implementing "IV8NativeObject" (interface, not class) IS easy (there's only two methods); but, in retrospect to what you're looking for, it's probably not a solution anyhow.

I think perhaps I was misunderstanding your request, as it sounds to me you are looking to intercept ALL function calls on an unknown object, but V8 only allows intercepting property accesses only (which is what V8ManagedObject is for). You cannot intercept function calls, unless you replace the property on the object itself. What you need is for every single bound method on a class to somehow look for and call a "catch all" handler, which doesn't exist (yet).

The only way I see this working at the moment is if you handle this script side. All you have to do is pass the bound objects to a "Hook()" CLR method, which will replace all function properties on the object with CLR method callbacks. The callbacks would pass along the call requests (hooking), and fill in any missing arguments. :)

I'll look into the possibility of adding support for this for the next release.
Jan 22, 2014 at 12:46 PM
Edited Jan 22, 2014 at 12:46 PM
jamesnw wrote:
Actually, implementing "IV8NativeObject" (interface, not class) IS easy (there's only two methods); but, in retrospect to what you're looking for, it's probably not a solution anyhow.
It still requires devs to learn what V8.NET is, related classes and types, which I would like to avoid.
The only way I see this working at the moment is if you handle this script side. All you have to do is pass the bound objects to a "Hook()" CLR method, which will replace all function properties on the object with CLR method callbacks. The callbacks would pass along the call requests (hooking), and fill in any missing arguments. :)
This is an excellent idea. And probably it won't introduce much of an overhead. Thanks.