Wednesday, November 13, 2013

Scripting a C# application with JavaScript and Jurassic

I am back working on JavaScript runtimes and .NET. For the pleasure and future possible projects I tried the JavaScript runtime Jurassic.

Regarding Jurassic, the pros:
  1. Written in C#
    • Allow a good interoperability with code and data between the .NET world and the JavaScript world. see "Exposing a .NET class to JavaScript".
    • Can be compiled for .NET 3.5 and 4.x
  2. Open source
  3. JavaScript 5 compatible
  4. Compile the JavaScript into MSIL via Reclection.Emit/DynamicMethod
    1. Therefore provide better performance than classical interpreter
    2. Compile on Sliverlight and Windows phone 7 .NET framework
    3. May be compiled on MacOS or Linux with Mono (I think).
The cons:
  1. Compile the JavaScript into MSIL via Reclection.Emit/DynamicMethod
    1. Cannot be compiled on iOS with Mono/MonoTouch
    2. I am not sure if it can be compiled on Windows 8 phone and Windows 8 RT
That said if your target platform is Windows, it is a great run-time to build scriptable application.
I wanted to create ultra simple Api in C# that can be called from the JavaScript world. My Api is just one method called __createItem(string itemName, string itemGuid).

I also want my favorite JavaScript libraries to be by default available.
The file Main.js contains the JavaScript.

Here is all the C# code I need to do that.

static void Main(string[] args)
{
    var engine = new Jurassic.ScriptEngine();

    // Export the print method to the JavaScript world
    engine.SetGlobalFunction("print", new Action<object>((o) => {
        Console.WriteLine(o == null ? "null" : o.ToString());
    }));

    // Export my own api to the JavaScript world
    engine.SetGlobalFunction("__createItem", new Func<string, string, bool>((itemName, itemGuid) => {
        Console.WriteLine(string.Format("Creating item {0}, {1}", itemName, itemGuid));
        return true; 
    }));
            
    var jsSource = new StringBuilder(1000);

    // My personal JavaScript libraries
    var a = Assembly.GetExecutingAssembly();
    jsSource.Append(DS.Resources.GetTextResource("dictionary.js"   , a)).AppendLine();
    jsSource.Append(DS.Resources.GetTextResource("list.js"         , a)).AppendLine();
    jsSource.Append(DS.Resources.GetTextResource("stack.js"        , a)).AppendLine();
    jsSource.Append(DS.Resources.GetTextResource("string.js"       , a)).AppendLine();
    jsSource.Append(DS.Resources.GetTextResource("stringbuilder.js", a)).AppendLine();
    jsSource.Append(DS.Resources.GetTextResource("sys.js"          , a)).AppendLine();

    // Create a nice JavaScript wrapper for my C# Api exposed
    jsSource.Append(DynamicSugar.DS.Resources.GetTextResource("MyApi.js", a)).AppendLine();

    jsSource.Append(DynamicSugar.DS.Resources.GetTextResource("Main.js",  a)).AppendLine();
    jsSource.Append("main();"); // Call the main function

    engine.SetGlobalValue("ExitCode", 0);

    try
    {
        engine.Execute(jsSource.ToString());
    }
    catch(Jurassic.JavaScriptException ex)
    {
        Console.WriteLine("Error:{0}, Line:{1}\r\n{2}".format(ex.Message, ex.LineNumber, ex.Source));
    }
    Console.WriteLine(engine.GetGlobalValue<int>("ExitCode"));
    Console.ReadKey();
}
What is great with this kind of architecture is that I can wrap the C# functions exposed to the JavaScript world in a nice singleton wrapper, which give a complete a JavaScript feel.
/*
 * Wrapper the C# api with nice JavaScript singelton object
 */
var MyApi = (function () {

    var _myApi = {};
    _myApi.createItem = function (item)  { return __createItem(item.Name, item.Guid); }
    return _myApi;
 })();

And to finish here is the Main.js. In my protoype this file is embed in a real case this file would be loaded from the disk.
function main() {

    var item = {
        Name : "foo",
        Guid : "{0321ADD2-07FE-4A93-9868-89D8DF054333}"
    };
   
    print("Item {Name}, {Guid}".format(item));
    MyApi.createItem(item);
}
You can find the source code at jura1.zip.

In a next post I will show how to create a method in my Singleton MyApi, that accept a callback function.

No comments:

Post a Comment