Use .NET objects within your JavaScript code

It might be a natural thing to use .NET objects within your JavaScript code.  When you provide additional types to your execution environment it is not only an expansion of possibilities,  but the customer has got the option to communicate with your host application. Providing types is the first stept of having an automation scenario and it is a good place to start of thinking how automatable your application should be.

The first step is very easy: just add the type to the engine instance:

using (var engine = new V8ScriptEngine())
{
   engine.AddHostType("City", typeof(City));

   engine.Execute(...);
}

After you’ve done that, within the script you may create it:

var berlin = new City(‘Berlin’);

You may use the type as any other object in JavaScript. That is quite easy.  But there some other interessting things. When the created object is the return value of a function you may use this instance within your hosting environment.

 

const string script = @"function CreateConfiguration() {" +
   "   var config = new Configuration();" +
   "   config.KeepRecentFiles = true;" +
   "   config.NumberOfRecentFiles = 10;" +
   "   return config;" +
   "};"; 

   using (var engine = new V8ScriptEngine())
   {
      engine.AddHostType("Configuration", typeof(Configuration));

      engine.Execute(script);

      Configuration config = engine.Script.CreateConfiguration("");

      // ... use of the configuration object ...

   }

This simple code avoids having a configuration dialog. Often it is necessary to have a dialog, but sometime it is not. Or you want to hide some of the configuration options. Anyway! You may use your application specific types within your JavaScript code.

But how about using clr types. Naturally you may use it in the same way. You can add any type with the AddHostType method to your engine. But when you want to use a whole namespace it might be not a good idea to add each of these types. For that you may use a HostTypeCollection. This collection includes the filenames of the assemblies, not the names of the namespaces you want to use. So keep in mind that all namespace are available when you’ve added the assembly filename.

const string script = @"var sb = new clr.System.Text.StringBuilder();" + 
                       "sb.AppendFormat('Today is {0}', DateTime.Now);" +
                       "Console.WriteLine(sb.ToString());";

using (var engine = new V8ScriptEngine())
{
   var namespaces = new HostTypeCollection("mscorlib", "System", "System.Core");

   engine.AddHostObject("clr", namespaces);
   engine.AddHostType("Console", typeof(Console));
   engine.AddHostType("DateTime", typeof(DateTime));
                
   engine.Execute(script);
}

There a two things to say. The first thing is that it might be a litte bit confusing to use the method AddHostObject for providing types. You can understand it as a container object that provides types. When you want to create an object directly like

var sb = new StringBuilder();

it will not work. Just only when you write

var sb = new clr.System.Text.StringBuilder();

it works.

The second things about HostTypeCollection is that it provides not only types As you’ve seen you may use it for the creation of instances of clr classes. But you can also access to other members of these namespace like static classes. The following code is a little bit difference from the one above:

 

const string script = @"var sb = new clr.System.Text.StringBuilder();" + 
                       "sb.AppendFormat('Today is {0}', DateTime.Now);" +
                       "clr.System.Console.WriteLine(sb.ToString());";

using (var engine = new V8ScriptEngine())
{
   var namespaces = new HostTypeCollection("mscorlib", "System", "System.Core");

   engine.AddHostObject("clr", namespaces);
   engine.AddHostType("DateTime", typeof(DateTime));
                
   engine.Execute(script);
}

The difference is that I don’t add the Console with the method AddHostType to the engine and access the Console via the clr object I added with the AddHostObject method. It looks quite the same but when you expect of have a large number of types the use of HostTypeCollection is a good shortcut.

Understanding V8Runtime

The V8Runtime is the primary scope of all execution – even you don’t see it everytime. See the simplest way of execute a script:

using (var engine = new V8ScriptEngine())
{
    var result = engine.Evaluate(script);
    // use the result ... 
}

In the background ClearScript creates a V8Runtime when you create a V8ScriptEngine on your own. Therefore the code above is equal to the following

using (var runtime = new V8Runtime())
{
   using (var engine = runtime.CreateScriptEngine())
   {
      var result = engine.Evaluate(script);
      // use the result ...
   }
}

The first difference is the cost of object creation. The V8Runtime is expensive so you’ll should see how to reuse it. The second difference makes the scope of code you execute. All code you execute by the V8ScriptEngine methodes Evaluate, Execute or ExecuteCommand is valid only within the instance of V8ScriptEngine.  The only chance to maintain code from one instance of the ScriptEngine to another is to compile it. See the following code.

using (var runtime = new V8Runtime())
{
   V8Script compiledscript = null;
   
   using (var engine = runtime.CreateScriptEngine())
   {
      var script = "function SayHello(msg) {Console.WriteLine(msg);}";

      compiledscript = engine.Compile(script);
   }

   using (var engine = runtime.CreateScriptEngine())
   {
      engine.AddHostType("Console", typeof(Console));
      engine.Evaluate(compiledscript);
      engine.Execute("SayHello('Das ist eine Meldung');");
   }

   if (compiledscript != null)
      compiledscript.Dispose();
}

The first instance of the ScriptEngine compiles the script and the second use it. That works ’cause the compiled script is bound to the runtime and not to the engine. This small difference is important and you may use this to optimize the usage of ClearScript.

There is one more important thing in the code above. Compiling the code means compiling in fact, not execute. You may use any objects, types or whatever within your code to compile and the compilation will not raise an exception even you haven’t add these to the script engine. This will be relevant not before you’ll use the compiled code with the Evaluate method. The second instance knows how to handle with the term Console, because it is added before (Line 14).

It is to assume that it is faster when you use compiled code within more than one instance of your ScriptEngine.

 

 

Why I like ClearScript

When I spoted ClearScript the first time in autumn 2013 I was looking for an adaption of the JavaScript engine V8 for .NET applications. I was learning about Node.js and was impressed by the simplicity and the possibilities. I thought it might be a good idea to use JavaScript in my favorite programming environment too. I was a bit surprised that there were some projects who made it. There were hosted on codeplex with a different state of complexity and completion. ClearScript looked promisingly because of continuous releases and an active community. So I decided to dive deeper into this.

After all there are some reasons why I like ClearScript:

  • It is very simple and therefore it is very easy to bring it on start. I don’t need to learn a lot of API calls, structures or processes. Reference it is nearly the only thing you have to do.
  • It is powerful. Because you may add all .NET objects and types you like you are able to do nearly everything. (I say “nearly” here because of beeing carefully and because I surely haven’t tried everything yet.)

And the main reason:

  • It allows me to think another way of concept an application. When I programm an application all customer needs are transferd into applications features. Even agile management helps me for better organization I still have to have to take every wish and put it into my application. With ClearScript – or better with JavaScript…V8…ClearScript – I may do this in another way. My focus may not realize every feature. Instead of this my focus is how to help the user to realize what they want. Instead of putting everything in code I have to make available an object model and a good way of communication between host and JavaScript so the end user is able to realize the details of their needs.

That’s still a lot of work of course but I don’t have handle with every custom validation rule or customizable behavior. That’s why I like ClearScript. It helps me to leave all the stuff of feature details when I invest more time in architecture and thinking about the line between my application and the user.