This project is read-only.

Is it possible to execute methods on managed object

Feb 6, 2015 at 6:27 AM
Is it possible to call methods on managed objects similar to:

    class MainClass
    {
        static V8Engine v8Engine;
        public static void Main (string[] args)
        {
            var script = @"console.log('this would freek me out');";
            var console = new Testconsole ();

            v8Engine = new V8Engine();
            v8Engine.GlobalObject.SetProperty("console", console, recursive: true);
            v8Engine.Execute (script);

            Console.ReadKey ();
        }
    }
    public class Testconsole
    {
        public void log(string msg)
        {
            Console.WriteLine (msg);
        }
    }
Thank you!
Feb 6, 2015 at 8:45 AM
Edited Feb 6, 2015 at 8:51 AM
I think you can do something like this
 
    public class log : V8Function
    {
        public override ObjectHandle Initialize(bool isConstructCall, params InternalHandle[] args)
        {
            Callback = logConstructWrapper;
 
            return base.Initialize(isConstructCall, args);
        }
 
        public InternalHandle logConstructWrapper(V8Engine engine, bool isConstructCall, InternalHandle _this, params InternalHandle[] args)
        {
              Console.WriteLine (args[0]);
        }
    }
 
V8Engine v8Engine = new V8Engine();
var funcTemplate = v8Engine.CreateFunctionTemplate(typeof(log).Name);
var logFunc = funcTemplate.GetFunctionObject<log>();
v8Engine.DynamicGlobalObject.setTimeout = logFunc;
then you are able to write just log every where in js.
(I haven't tested it just altered the example.)

Or just a hint look at this example it creates a TypeScriptCompilerEnvironment class which could be your console class. It has a log function which has an attribute ScriptMember.
Then look at the line and change monodevelop to console. Then you can write in js console.log('"');
Feb 6, 2015 at 6:31 PM
Edited Feb 6, 2015 at 6:33 PM
chrisber01 is correct. As well, if you want to call a CLR method, and have the parameters auto convert where possible, you can also register (expose) the CLR type to JavaScript; otherwise, you need to write a function that will expect JavaScript parameters for the call (and you convert your self). There are examples of many things in the console test project, and here is one such example:
    public InternalHandle TestJSFunction1(V8Engine engine, bool isConstructCall, InternalHandle _this, params InternalHandle[] args)
{
    // ... there can be two different returns based on the call mode! ...
    // (tip: if a new object is created and returned instead (such as V8ManagedObject or an object derived from it), then that object will be the new object (instead of "_this"))
    if (isConstructCall)
    {
        var obj = engine.GetObject(_this);
        obj.AsDynamic.x = args[0];
        ((dynamic)obj).y = 0; // (native objects in this case will always be V8NativeObject dynamic objects)
        obj.SetProperty(0, engine.CreateValue(100));
        obj.SetProperty("1", engine.CreateValue(100.2));
        obj.SetProperty("2", engine.CreateValue("300"));
        obj.SetProperty(4, engine.CreateValue(new DateTime(2013, 1, 2, 3, 4, 5, DateTimeKind.Utc)));
        return _this;
    }
    else return args.Length > 0 ? args[0] : InternalHandle.Empty;
}

    var funcTemplate1 = Engine.CreateFunctionTemplate("_" + GetType().Name + "_"); // (this is in class "V8DotNetTester")
    _MyFunc = funcTemplate1.GetFunctionObject(TestJSFunction1);
If all you want to do is expose a console, I also had an example in the test console project:
     Console.WriteLine(Environment.NewLine + "Creating a global 'Console' object ..."); // line 117 of program.cs
     _JSServer.GlobalObject.SetProperty(typeof(Console), V8PropertyAttributes.Locked, null, true, ScriptMemberSecurity.Locked);
Run the console project included with V8.NET and play with it to learn more. If you have further questions, don't hesitate to ask. ;)
Feb 6, 2015 at 6:52 PM
Edited Feb 7, 2015 at 1:44 AM
There's actually not much benefit from inheriting from V8Function. You can do this instead:
public class log
{
    public InternalHandle logConstructWrapper(V8Engine engine, bool isConstructCall, InternalHandle _this, params InternalHandle[] args)
    {
          Console.WriteLine (args[0]);
    }
}

V8Engine v8Engine = new V8Engine();
var funcTemplate = v8Engine.CreateFunctionTemplate(typeof(log).Name);
var logFunc = funcTemplate.GetFunctionObject(new log().logConstructWrapper); // (you must maintain a reference to the return function in 'logFunc' or the GC will destroy it, and any calls from JS will fail)
v8Engine.DynamicGlobalObject.log = logFunc;
FYI: "isConstructCall" in "{V8NativeObject}.Initialize" only applies to objects created from object templates, and is always false for function objects.
Feb 6, 2015 at 6:55 PM
Edited Feb 6, 2015 at 6:55 PM
@chrisber01: Callback = logConstructWrapper; can also be placed in the constructor, as it doesn't access anything in the engine.

i.e.:
public class log : V8Function
{
    public log()
    {
        Callback = logConstructWrapper;
    }

    public InternalHandle logConstructWrapper(V8Engine engine, bool isConstructCall, InternalHandle _this, params InternalHandle[] args)
    {
          Console.WriteLine (args[0]);
    }
}
Feb 6, 2015 at 11:55 PM
First off, thank you both for taking the time...
My preferred choice would be the "TypeScriptCompilerEnvironment" sample as it would let me group methods into classes. However, this failed with the exception:
"Error: Objects cannot be constructed from this function."
My code:
class MainClass
{
    public static void Main(string[] args)
    {
        var script = @"myconsole.log('this would freek me out');";

        using (var engine = new V8Engine())
        {
            engine.RegisterType<MyConsole>(null, recursive: true);
            engine.GlobalObject.SetProperty("myconsole", new MyConsole());

            try
            {
                var scriptHandle = engine.Compile(script, "Monodevelop.TypeScript.Compiler", true);
                engine.Execute(scriptHandle, "V8.NET Unit Tester", true);

            }
            catch (Exception ex)
            {
                var s = ex.Message;
            }
        }
    }
}
public class MyConsole
{
    [ScriptMember(inScriptName: "log", security: ScriptMemberSecurity.Permanent)]
    public static void log(string message)
    {
        Console.WriteLine(String.Format("{0} : {1} : {2}:", DateTime.Now, "Log", message));
    }
}
BTW, I was only able to catch the exception when running it in .NET. With the mono build (OSx), the exception was never caught.

James sample of using a function template without inheriting from V8Function retulted in ReferenceError: log is not defined:
class MainClass
{
    public static void Main(string[] args)
    {
        using (var engine = new V8Engine())
        {
            var funcTemplate = engine.CreateFunctionTemplate(typeof(log).Name);
            var logFunc = funcTemplate.GetFunctionObject(new log().logConstructWrapper); 
            engine.DynamicGlobalObject.setTimeout = logFunc;

            try
            {
                var scriptHandle = engine.Compile(@"log('this would freek me out');", "Monodevelop.TypeScript.Compiler", true);
                engine.Execute(scriptHandle, "V8.NET Unit Tester", true);
            }
            catch (Exception ex)
            {
                var s = ex.Message;
            }
        }
    }
}
public class log
{
    public InternalHandle logConstructWrapper(V8Engine engine, bool isConstructCall, InternalHandle _this, params InternalHandle[] args)
    {
        Console.WriteLine((string)args[0]);
        return null;
    }
}
... And inheriting from V8Funciton gave the same exception:
class MainClass
{
    public static void Main(string[] args)
    {
        using (var engine = new V8Engine())
        {
            var funcTemplate = engine.CreateFunctionTemplate(typeof(log).Name);
            var logFunc = funcTemplate.GetFunctionObject<log>();
            engine.DynamicGlobalObject.setTimeout = logFunc;

            try
            {
                var scriptHandle = engine.Compile(@"log('this would freek me out');", "Monodevelop.TypeScript.Compiler", true);
                engine.Execute(scriptHandle, "V8.NET Unit Tester", true);
            }
            catch (Exception ex)
            {
                var s = ex.Message;
            }
        }
    }
}
public class log : V8Function
{
    public override ObjectHandle Initialize(bool isConstructCall, params InternalHandle[] args)
    {
        Callback = logConstructWrapper;

        return base.Initialize(isConstructCall, args);
    }

    public InternalHandle logConstructWrapper(V8Engine engine, bool isConstructCall, InternalHandle _this, params InternalHandle[] args)
    {
        Console.WriteLine((string)args[0]);
        return null;
    }
}
Thanks
Mikael
Feb 7, 2015 at 1:19 AM
In your first example code, the log method is static, not an instance. You cannot set a property as an instance, then call a static method on it.

In your second example, you are setting the global property as "setTimeout", and not "log" (thus, "log" is not found).

In your third example, its the same as the second.
Feb 7, 2015 at 1:43 AM
Edited Feb 7, 2015 at 1:44 AM
BTW: I was just copying the original code from Chris and modifying it. He had 'setTimeout', but I forgot to change it to 'log' when I posted the previous reply, so my bad. ;) (it's updated now)
Feb 7, 2015 at 4:50 AM
Sorry about that guys, but I was playing around to see if I could get it to work with static functions. But I get the same exception either way (static or no static).
Feb 10, 2015 at 10:44 PM
The problem was that the target framework was 4.5. Setting it to 4.0 (as with the sample console app) resolved the problem.
Feb 11, 2015 at 6:11 PM
I changed the console app to 4.5, and it still works (on Windows 7 anyhow).