The Jurassic JavaScript run time is not as fast, but because written in C# provides better integration with the .NET world, if you really need it.
But both run-times do not let you access JavaScript objects and arrays in the C# world using the dynamic syntax available in JavaScript.
Here is a sample from the Noesis codeplex web site
JavascriptContext context = new JavascriptContext(); context.SetParameter("console", new SystemConsole()); context.SetParameter("message", "Hello World !"); context.SetParameter("number", 1); string script = @" var i; for (i = 0; i < 5; i++) console.Print(message + ' (' + i + ')'); number += i; "; context.Run(script); Console.WriteLine("number: " + context.GetParameter("number"));
Using the dynamic feature of C# 4.0 and my library DynamicJavaScriptRunTimes.net, You can write the same code this way
dynamic jsContext = new DynamicJavascriptContext( new JavascriptContext() ); jsContext.message = "Hello World !"; jsContext.number = 1; string script = @" var i = 0; for (i = 0; i < 5; i++) console.log(message + ' (' + i + ')'); number += i; "; jsContext.Run(script); Console.WriteLine("number: " + jsContext.number);
JavaScript array are translated into .NET array and JavaScript object are translated into .NET Dictonary<string, object>. The method jsContext.Array() is a helper to create an array in a JavaScript like syntax, inspired by DynamicSugar.Net.
array:
How to create an array in C#, modify it in JavaScript and then read it back in C#.
dynamic jsContext = new DynamicJavascriptContext( new JavascriptContext() ); jsContext.a = new object [] { 1, 2, 3 }; // Regular Syntax jsContext.a = jsContext.Array( 1, 2, 3 ); // My Syntax string script = @" a.push(4); "; jsContext.Run(script); Assert.AreEqual(4, jsContext.a.Length); for(var i=0; i < jsContext.a.Length; i++) Assert.AreEqual(i+1, jsContext.a[i]);
Objects:
As you can see nested objects and arrays are supported. The [ ] syntax to access a property is also supported.
dynamic jsContext = new DynamicJavascriptContext( new JavascriptContext() ); string script = @" Configuration = { Server : 'TOTO', Database : 'Rene', Debug : true, MaxUser : 3, Users : [ { UserName:'rdescartes' ,FirstName:'rene' ,LastName:'descartes' }, { UserName:'bpascal' ,FirstName:'blaise' ,LastName:'pascal' }, { UserName:'cmontesquieu' ,FirstName:'charles' ,LastName:'montesquieu' } ] } "; jsContext.Run(script); Assert.AreEqual("TOTO" , jsContext.Configuration.Server); Assert.AreEqual("Rene" , jsContext.Configuration.Database); Assert.AreEqual(true , jsContext.Configuration.Debug); Assert.AreEqual(3 , jsContext.Configuration.MaxUser); Assert.AreEqual(3 , jsContext.Configuration.Users.Length); Assert.AreEqual("rdescartes", jsContext.Configuration.Users[0].UserName); Assert.AreEqual("TOTO" , jsContext["Configuration"]["Server"]); Assert.AreEqual("Rene" , jsContext["Configuration"]["Database"]); Assert.AreEqual(true , jsContext["Configuration"]["Debug"]); Assert.AreEqual(3 , jsContext["Configuration"]["MaxUser"]); Assert.AreEqual(3 , jsContext["Configuration"]["Users"].Length); Assert.AreEqual("rdescartes", jsContext["Configuration"]["Users"][0].UserName);
More Objects
We can also create objects from C#, pass them to the JavaScript run-time and then access the objects again. The method jsContext.Object() is a helper method inspired by DynamicSugar.Net to create object in a JavaScript like syntax or pass a POCO.
Note: Objects and arrays are not passed by reference. The data is copied from one world to the other.
dynamic jsContext = new DynamicJavascriptContext( new JavascriptContext() ); jsContext.i = jsContext.Object( new { a = jsContext.Object( new { LastName="Torres", Age=46 } ), b = jsContext.Object( new Person("Ferry", 47) ), } ); string script = @" var p1 = { Name : i.a.LastName, Age : i.a.Age } var p2 = { Name : i.b.LastName, Age : i.b.Age } "; jsContext.Run(script); Assert.AreEqual("Torres", jsContext.p1.Name); Assert.AreEqual("Torres", jsContext.p1["Name"]); Assert.AreEqual("Torres", jsContext["p1"]["Name"]); Assert.AreEqual(46, jsContext.p1.Age); Assert.AreEqual(46, jsContext.p1["Age"]); Assert.AreEqual(46, jsContext["p1"]["Age"]); Assert.AreEqual("Ferry", jsContext.p2.Name); Assert.AreEqual("Ferry", jsContext.p2["Name"]); Assert.AreEqual("Ferry", jsContext["p2"]["Name"]); Assert.AreEqual(47, jsContext.p2.Age); Assert.AreEqual(47, jsContext.p2["Age"]); Assert.AreEqual(47, jsContext["p2"]["Age"]);
More Samples
Assert.AreEqual( "Fred", jsContext.Run(@" function Person(firstName){ this.FirstName = firstName; } (new Person('Fred'))" ).FirstName ); //////////////////////////////////////////// DateTime refDate = new DateTime(1964, 12, 11, 01, 02, 03); string script = @" var O2 = { F2: function(pInt,pDouble,pString,pBool,pDate) { return ''+this.Internal+'-'+pInt+'-'+pDouble+'-'+pString+'-'+pBool+'-'+formatDateUS(pDate); }, Internal:1 } "; jsContext.Load("format", Assembly.GetExecutingAssembly()); jsContext.Run(script); var expectedF2 = "1-1-123.456-hello-true-12/11/1964 1:2:3"; var f2Result = jsContext.Call("O2.F2", 1, 123.456, "hello", true, refDate); Assert.AreEqual(expectedF2, f2Result);
The DynamicJavascriptContext class
/// <summary> /// Run the script and return the last value evaluated. Executing a declaration function /// or a global object literal, will load the function or object in the JavaScript context. /// </summary> /// <param name="script"></param> /// <returns> /// </returns> public object Run(string script); /// <summary> /// Load a JavaScript text file or text ressource in the JavaScript context /// </summary> /// <param name="name">The name without the .js extension</param> /// <param name="assembly">The assembly is loading a ressource</param> public void Load(string name, Assembly assembly = null); /// <summary> /// Execute a javascript global function or method /// </summary> /// <param name="functionName">the function name</param> /// <param name="parameters">The parameters</param> /// <returns></returns> public object Call(string functionName, params object[] parameters); /// <summary> /// Helper function to make date compatible with the JavaScript /// run time. Jurassic date are not .net datetime and need a convertion /// </summary> /// <param name="o"></param> /// <returns></returns> public object Date(DateTime d){); /// <summary> /// Helper function to make array compatible with the JavaScript run time. /// </summary> /// <param name="array"></param> /// <returns></returns> public object array(params object [] array); /// <summary> /// Helper function to make a JavaScript object compatible with the JavaScript run-time /// </summary> /// <param name="netObject"></param> /// <returns></returns> public object Object(object netObject);
Conclusion
DynamicJavaScriptRunTimes.net
- Add a nice syntatic sugar layer to execute JavaScript code from C#
- Support 2 JavaScript run-times: Noesis and Jurassic
- Part of the source code is a CoffeeScript v 1.1.1 compiler and run-time.
- Available on github
public class BaseJavaScriptContextImplementation:
ReplyDelete>> var a = new StringBuilder(1024);
Why did you decide that the parameters will be no longer than 1024 bytes?
>> return this.Run("{0}();".format(functionName));
Why not use: functionName + "();"? It much faster.
>> var parameterType = parameters[i].GetType().FullName.ToLower();
>> if (parameterType == "system.string")
Why not use: parameters[i] as string? It much faster too.
>> a.AppendFormat("'{0}'", parameters[i]);
You use this for string parameter, but string can contains ' character, you need to replace it to \'
String can be null - '' no need for null.
And so on...