Calling an object function that uses an object as an argument

Jan 17, 2015 at 2:10 PM
Edited Jan 18, 2015 at 3:22 AM
I want to change my proof of concept Typescript service implementation (ts impl, host imp) to something to be more V8 alike and faster. Currently I'm using the following process:
v8Host = new LanguageServiceShimHost(logger);
v8Engine.RegisterType<LanguageServiceShimHost>(null, recursive: true);
v8Engine.GlobalObject.SetProperty("host", v8Host);

//compile Typescript.js 
Handle serviceScriptHandle = v8Engine.Compile("Typescript.js content");
//compile main script to get the language service object "ls"
Handle mainScriptHandle = v8Engine.Compile("var ls = ts.createLanguageService(host, ts.createDocumentRegistry())");
//compile the "compile script" to get ".js" from ".ts" files and return the    //result to "updateCompilerResult" host function as callback.
Handle compileScriptHandle = v8Engine.Compile("var emitResult = ls.getEmitOutput(host.fileName);
host.updateCompilerResult(JSON.stringify({result: emitResult}));"
Then I execute the scripts first:
v8Engine.Execute (serviceScriptHandle, "V8.TS");
v8Engine.Execute (mainScriptHandle, "V8.TS.Main");
And whenever I need to compile .ts to .js I execute:
v8Engine.Execute (compileScriptHandle, "V8.TS.Main");
//on the managed host callback function
void updateCompilerResult(string ){
    CompilerResult = JsonConvert.DeserializeObject<CompilerResult>(result);
}
It seems odd to stringify and then deserialize the output in the managed host.updateCompilerResult() function.
I hope you can help me to estimate my possibilities here to get the most performant solution:

Option 1:
Adding the attribute ScriptObject to the CompilerResult class which reflects the return value.
//on the managed side
[ScriptObject("LanguageServiceShimHost", security: ScriptMemberSecurity.Permanent)]
class CompilerResult () {}

void updateCompilerResult(CompilerResult result){
    tempCompilerResult = result;
}
//calling it in Javascript 
Handle compileScriptHandle = v8Engine.Compile("var emitResult = ls.getEmitOutput(host.fileName);
host.updateCompilerResult( emitResult);"
v8Engine.Execute (compileScriptHandle, "V8.TS.Main");
Option 2:
I would prefer to use a solution where I can access the language service itself but I do not know how to access the the "ls "object to hand over a variable (String or V8NativeObject/V8ManagedObject) as parameters to the script.
class V8TSService{
    public CompilerResult Compile (String filename){
        //Making the script global available (the ls object)
        // executes var ls = ts.createLanguageService(host, ts.createDocumentRegistry());
        v8Engine.Execute (mainScriptHandle, "V8.TS");
        // getting access to the object
        var handle = v8Engine.GlobalObject.GetProperty("ls");
        
        //obstacle I don't have a representation of the typescript service implementation.
        //or can I use an interface here?
        var res  = (Casting some object)v8Engine.DynamicGlobalObject.ls;
        CompilerResult result = res.getEmitOutput(fileName");
    }
}
I hope you have time to help me out with some clues or a sample code that I can use.
Jan 18, 2015 at 3:21 AM
Edited Jan 18, 2015 at 9:49 AM
I have now investigate option 2, currently I want to implement the getcomplitionDetail function which gives code completion on a specific position.

On the Javascript (Typescript) side I have the following object.
    export interface CompletionInfo {
        isMemberCompletion: boolean;
        entries: CompletionEntry[];
    }

    export interface CompletionEntry {
        name: string;
        kind: string;           
        kindModifiers: string;   
    }
resulting example object when I access the function
ls.getCompletionsAtPosition(host.fileName, host.position, host.isMemberCompletion)
{  
   "isMemberCompletion":true,
   "entries":[  
      {  
         "name":"start",
         "kind":"method",
         "kindModifiers":"public"
      },
      {  
         "name":"drive",
         "kind":"method",
         "kindModifiers":"public"
      },
      {  
         "name":"getPosition",
         "kind":"method",
         "kindModifiers":"public"
      }
   ]
}
Then on the managed side I created the following classes and function to reflect the Json structure.
    public class CompletionEntry : V8NativeObject
    {
        public string name { get; set; }
        public string kind { get; set; }            
        public string kindModifiers { get; set; }   
    }

    public class CompletionInfo : V8NativeObject
    {
        public bool isMemberCompletion { get; set; }
        public CompletionEntry[] entries;
    }


        public static CompletionInfo RunMemberCompletionScript()
        {
                //create parameter
                Handle filename = v8Engine.CreateValue(v8Host.fileName);
                Handle position = v8Engine.CreateValue(v8Host.position);
                Handle isMemberCompletion = v8Engine.CreateValue(v8Host.isMemberCompletion);

                var ls = v8Engine.GlobalObject.GetProperty("ls");
                var resultHandle = ls.Call("getCompletionsAtPosition",ls, filename, position, isMemberCompletion);
                CompletionInfo completion = v8Engine.GetObject<CompletionInfo>(resultHandle);
                
                //examine result
                var test0 = resultHandle.GetProperty("isMemberCompletion");
                var test1 = resultHandle.GetProperty("entries");
                CompletionEntry test3 = v8Engine.GetObject<CompletionEntry>(test1.GetProperty(1));
    
               return complition;

        }
With this example I have now the problem that the values of the completion object are null.
CompletionInfo completion = v8Engine.GetObject<CompletionInfo>(resultHandle);
When I examine test0 it shows true for the isMemberCompletion property but on the completion object it shows null.
When I examine test1 it shows that isArray is true and the length is 3 but on the completion object it is null.
test3 all values are null.

Hopefully that you have some suggestion to solve this issue.
Coordinator
Jan 18, 2015 at 4:27 PM
It doesn't work that way - you can't create an object in JS and pull out a new managed side object like that. You would have to first create the managed side object, and pass in the native object handle to associate with it. Optionally you can create a managed side function somewhere that returns new objects for the native side to use, then set properties as needed with the js (or let the managed side set priorities as well). When I get home I'll try to post a better example. Also, if you can zip and send me you project, I'll make it work for you. ;) One other thing to note, there are binary files on the dev branch that you can download that are up to date with lots of fixes.
Jan 18, 2015 at 7:53 PM
Hi James thanks for noticing,

The source code is part of the Typescript-addin for Monodevelop create by Matt Ward so the source is public available. If you like you can checkout the branch investigate-performance-options of my fork git@github.com:chrisber/typescript-addin.git the Important file is V8TypescriptProcessor.cs;



offtopic:
I'm currently helping to make V8.Net available on Linux/OSX. Im using it now to make the Typescript-addin Cross-platform which currently uses Javascript.Net. We did discuss this in the mono discussion here. (Note you haven't responded jet)
I followed your code changes I have noticed that you changed something like how the dll's get loaded. Have you changed also something on the native side or V8 behavior itself? Maybe I'm missing something? I have checked the history on this page did you maybe only pushed the Binaries but not the source?.
Hopefully it is not to much to ask, but could you also set an upstream to your Github repository of v8dotnet? Because I forked from It and I would get notifications then when something changes. If you don't like github you can just use it as Mirror without maintaining it. As soon as v8dotnet runs on OSX and when I have a build process that runs on OSX/Linux/Win identically (currently only osx/linux with clang + gyp). I will send you an pull request here and we could discuss it, but I would rather prefer to send you an pull request on github because it has online inline code review available which would make discussion thinks simpler.
Coordinator
Jan 19, 2015 at 7:06 AM
Ok - I just wanted to make sure I had the same source as you where using for your latest test in the post above.

Putting the source on GitHub was an idea before, but I went with the CodePlex to get a fuller website experience (with versioned releases). Also, BTW, you can discuss pull requests on CodePlex as well. Anyway, sure, I'll look into doing this.

If you look down the history, You'll see the binaries committed. The recent commits are simple updates, so the binaries from the latest commit in dev are the latest.
Coordinator
Jan 19, 2015 at 9:41 PM
Ok, I've put the latest release on GitHub. ;)
Coordinator
Jan 19, 2015 at 10:59 PM
Sorry, after careful review, it sees your code should work. This line:
CompletionInfo completion = v8Engine.GetObject<CompletionInfo>(resultHandle);
should create a new managed object automatically, and associated its native object handle as given. Perhaps check this again with the new release to make sure it was't a bug that's already fixed. Also, make sure the name case is correct between JS and the managed side.

Please explain how your project is setup in detail so I can build and test myself locally.
Jan 21, 2015 at 12:24 AM
Hi James,

thanks for your interest in this project. I zipped my current test project from windows this will be shorter then explaining how to set it up from scratch. Note that I'm not a Windows native so the test process is a bit messy, there could be better ways to do it.

Setup process:
  • Install Monodevelop from Xamarin
  • The zip file contains your v8dotnet project, I added the TypescriptBinding project to it.
  • In visual studio I changed the settings under debugging (if you have a different install dir for xamarin)
    • start external program C:\Program Files (x86)\Xamarin Studio\bin\XamarinStudio.exe
    • working directory C:\Program Files (x86)\Xamarin Studio\bin\
  • start Monodevelop (XamarinStudio) and under tools -> addin-manager -> select install from file, and at the bottom select the MonoDevelop.Typescript_0.3.mpack and ignore install errors :(
    This creates the needed folders under C:\Users\[USERNAME]]\AppData\Local\XamarinStudio-5.0\LocalInstall\Addins (Note if you later forget to place the addin.xml then the addin gets disabled and you have to install or enable it again).
Debugging the addin
  • After building the project, I have to place the TypeScriptBinding.addin.xml from typescript-addinlib/V8Engine/win.x86_x64 into the bin/Debug directory (The file tells MD the dependencies of the addin )
  • Then I copied the files from your release folder .Net4/x86 into /bin/Debug. I renamed V8_Net_Proxy_x86.dll to V8_Net_Proxy.dll
    • Why? I needed to comment out the Resolver event call from V8Engine.cs because Monodevelop doesn't fired it first, just later after start-up and den it uses the wrong path. So I ended up using the native behavior.
  • The last step is to copy all to C:\Users[USERNAME]]\AppData\Local\XamarinStudio-5.0\LocalInstall\Addins into the corresponding addin directory.
  • Start the debugger in VisualStudio. It should start Monodevelop,.Then check under under tools -> addin-manager -> Languagebinding if the addin is loaded.
  • Then in Monodeveol load the project "testme" when it gets loaded the Typescript-addin get's hooked in because it contains an .ts file.
  • Edit the ts file on line 16 alert(greeter.greet()); to alert(greeter); when you then add a dot alert(greeter.); it will try to complete the statement and this is the part we want to debug. V8TypescriptProcessor:102
Jan 23, 2015 at 7:54 PM
Edited Jan 23, 2015 at 8:00 PM
Hi James, I think this is a better example to test it, could you have a look at this. No need to setup Monodevelop.
I created a gist here.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using V8.Net;

namespace TestReturnValue
{


    public class CompletionEntry : V8NativeObject
    {
        public string name { get; set; }
        public string kind { get; set; }
        public string kindModifiers { get; set; }
    }

    public class CompletionInfo : V8NativeObject
    {
        public bool isMemberCompletion { get; set; }
        public CompletionEntry[] entries;
    }

    class Program
    {
        static void Main(string[] args)
        {
                V8Engine v8Engine = new V8Engine();

                Handle result = v8Engine.Execute(
                    @"var TypescriptService = (function () {
        function TypescriptService() {
        }
        TypescriptService.prototype.getCompletionsAtPosition = function (a, b, c) {
            var entries = [
                {
                    'name': 'start',
                    'kind': 'method',
                    'kindModifiers': 'public'
                },
                {
                    'name': 'drive',
                    'kind': 'method',
                    'kindModifiers': 'public'
                },
                {
                    'name': 'getPosition',
                    'kind': 'method',
                    'kindModifiers': 'public'
                }
            ];
            var result = {
                'isMemberCompletion': true,
                'entries': entries
            };
            return result;
        };
        return TypescriptService;
    })();
    var ls = new TypescriptService();"
                );


                //create parameter
                Handle filename = v8Engine.CreateValue("a");
                Handle position = v8Engine.CreateValue("b");
                Handle isMemberCompletion = v8Engine.CreateValue("c");

                var ls = v8Engine.GlobalObject.GetProperty("ls");
                var resultHandle = ls.Call("getCompletionsAtPosition",ls, filename, position, isMemberCompletion);
                CompletionInfo completion = v8Engine.GetObject<CompletionInfo>(resultHandle);
                
                //examine result
                var test0 = resultHandle.GetProperty("isMemberCompletion");
                var test1 = resultHandle.GetProperty("entries");
                CompletionEntry test3 = v8Engine.GetObject<CompletionEntry>(test1.GetProperty(1));

        }
    }
}
Coordinator
Jan 26, 2015 at 10:27 PM
Edited Jan 27, 2015 at 1:50 PM
Ah, I can see already what your issues are here. ;) V8.NET does not automatically bind your method and properties in the way you have it here. If you inherit from V8NativeObject, you are expected to call the inherited methods to read the properties from JavaScript for your own properties when that are accessed - you must code that yourself. It's been awhile, so I'm going to take this code and try to make it work another way. ;) Be back in a bit ...
Coordinator
Jan 26, 2015 at 11:14 PM
Edited Jan 26, 2015 at 11:15 PM
Another note: don't expect a value for result when the last JS line is var ls = (var will cause "undefined" in JS as a result).
Coordinator
Jan 26, 2015 at 11:50 PM
Edited Jan 26, 2015 at 11:50 PM
Try this: https://gist.github.com/rjamesnw/5ee5a0a2a769b321e1d0

Please bare in mind that V8.NET is a low level interface to the V8 engine. There is no automatic serializing of JSON style object layouts to CLR objects. You have to map this yourself. That said, however, to make it a bit easier, I did implement binding of CLR objects to the JS world (for the most part), and you have to work with things that context. Take another look at the console example app, and watch what it prints to the screen. Template types are also accepted, though treated in special ways.
Marked as answer by jamesnw on 1/27/2015 at 6:50 AM
Jan 27, 2015 at 5:44 PM
Thanks for the extensive example