From C# To Javascript and Back Again, Via ClearScript

Note: This is the second of two articles on integrating C# and scripting languages. You can read the second part (for Ruby) here.

I recently created an engine (I know, I know) to create text-based Interactive Fiction games in C#. My requirement was simple: create a core engine with some core functionality (rooms, commands, and objects) in .NET, and allow end-users to write their games in Javascript. In the process, I picked up a little-known gem called ClearScript as the vehicle of interaction.

Let’s start with my requirements: I should be able to write core functionality classes in C#, instantiate and extend them in ClearScript, and return them back to C# for consumption by the game engine. I also need strong typing of objects (despite the dynamic nature of scripting languages), and the ability to create delegates or functions inside my scripting language (and return those to C#, too).

You can view the source code on GitHub. This post summarizes my implementation, and the challenges I ran though.

Why ClearScript?

ClearScript is far from the most popular Javascript library for C#. Why did I choose it? Because it had a simple syntax, and more importantly, it allowed me to return strongly-typed objects to C# (including Action instances) where Javascript.NET, Jurassic, could not.

JavascriptRunner Class

I created a core “JavascriptRunner” class which encapsulates all the Javascript-centric invocation. It contains a simple constructor, and a method public T Execute(string script, IDictionary parameters).

The first parameter (the script) is the easiest part. The second parameter contains any variables from C# that I want to expose to Javascript (this makes up my “API” which users write against).

One issue I ran into is that ClearScript doesn’t support the widely-supported console.log method. To get around this, I simply registered the Console class to it:

engine.AddHostType("Console", typeof(Console));

Here’s how that looks, as of writing:


public JavascriptRunner()
{
this.engine = new V8ScriptEngine();
var assembly = System.Reflection.Assembly.Load("MeltDown.Core");

foreach (var type in assembly.GetTypes())
{
    engine.AddHostType(type.Name, type);
}

// Used for anonymous functions
engine.AddHostType("Action", typeof(Action));
// JS doesn't support console.log (JS/V8/Chrome style)
engine.AddHostType("Console", typeof(Console));

}

public T Execute(string script, IDictionary<string, object> parameters)
{

foreach (var kvp in parameters)
{
    // TODO: these are engine-scope. It doesn't reset.
    engine.AddHostObject(kvp.Key, kvp.Value);
}

var toReturn = engine.Evaluate(script);

if (toReturn is T)
{
    return (T)toReturn;
}
else
{

throw new ArgumentException("Expected " + typeof(T).FullName + " but got " + toReturn.GetType().FullName);
}
}

Notice I can’t pass in parameters per script execution (unlike the Ruby version). That’s okay too; there’s room to improve.

Converting ClearScript Arrays to C# Lists

The second major hurdle is that ClearScript returns an Array instance instead of an enumeration if I use typical Javascript list syntax, like this: var x = ['a', 17, 2.0 / 3].

To get around this, I created a ScriptHelper class. This class contains two methods: IsArray and ToList. In both methods, I rely on V8-specific implementation, and since the object input is dynamic, I can just call Javascript methods and get back what I want. Here’s how that looks (Javascript only):


public static List ToList(dynamic source)
{
for (int i = 0; i < source.length; i++)
{
object next = source[i];
found.Add(GetAsType(next));
}

return found;

}

public static bool IsArray(dynamic source) {
return source.constructor.name == "Array";
}

This allows me to write “Javascript-like” code without worrying about types.

Returning Delegates/Functions

The last hurdle was related to functions; given a delegate or function type (like Action), how can I return an object which I can type to a delegate type?

The answer was two parts. First, I need to register the Action class with ClearScript, like so:

engine.AddHostType("Action", typeof(Action));

The second was to wrap any functions into an Action constructor:


new Action(function() {
this.Description = "A steaming, hand-burning potato";
})

Conclusion

Given my requirements, I achieved them, and have code that allows me to write very “Javascript-like” code. That’s great; I can leverage C# to take care of some of the leaky abstractions, and everything works.

I suggest you download and play with the full source code from GitHub. You can look at the ScriptRunner.Tests project to see working Javascript code coming back to C#.

About Ashiq Alibhai, PMP

Ashiq has been coding C# since 2005. A desktop, web, and RIA application developer, he's touched ASP.NET MVC, ActiveRecord, Silverlight, NUnit, and all kinds of exciting .NET technologies. He started C# City in order to accelerate his .NET learning.
This entry was posted in Libraries, Wndows Forms and tagged , . Bookmark the permalink.

One Response to From C# To Javascript and Back Again, Via ClearScript

  1. satyendra pandey says:

    Another blog with code example to make c# to javascript and vice versa call. Begins from scratch . http://pandeysk.wordpress.com/2014/05/23/clearscript-step-by-step/

Leave a Reply

Your email address will not be published. Required fields are marked *