Skip to content

JSNI: JavaScript Native Interface

kswoll edited this page Feb 12, 2014 · 10 revisions

Occasionally you may have need to have stricter control of some of the cross-compilation process. In particular, there are various Javascript constructs that are not expressible in C#. Using a helper class called Jsni (located in System.Runtime.WootzJs) it is possible to generate Javascript code that would otherwise be impossible using just normal C# syntax. These helpers are grouped into the following kinds:

  • Syntax Helper: An expression type has no analog in C#. Examples include delete, for..in, .apply(), .call(), arguments, regex literals, etc.
  • Javascript Overrides: The expression type exists in Javascript, but the syntax has been co-opted to represent the vagaries of the expression type in terms of C#. For example, new usually calls the generated $ctor constructor on the prototype A Javascript override will allow you to use the same syntax but have it expressed as though you were writing Javascript. For the case of new, that means you can generate a naked new Foo(...) expression for any expression type representing Foo.
  • Global Functions: Javascript allows global functions; C# does not. Therefore, some global functions such as parseInt are accessible here. While these native Javascript implementations of global functions are available to you, they are generally surfaced elsewhere in the BCL. For example, parseInt is how int.Parse is implemented. For obvious portability reasons, you should use the BCL version whenever possilble, as it makes it more likely your C# code can be used elsewhere.

####Syntax Helpers####

  • Jsni.regex(pattern[, suffix]): This allows you to create a Javascript regular expression literal. The result of invoking this method will be the javascript /pattern (or /pattern/suffix).
  • Jsni.reference(stringliteral): Used to reference any arbitary Javascript identifier. The specified identifier is compiled directly into Javascript as an identifier. The specified string MUST be a C# string-literal as the reference must be determined at compile-time. If the expression is not a string literal, the build will fail.
  • Jsni.instanceof(obj, type): The C# is operator is implemented entirely using the internal C# type system. If you want to find out in Javascript terms whether an object is an instance of a particular type, then you can use this helper. The C# expression Jsni.instanceof(obj, type) will generate the Javascript obj instanceof type.

####Javascript Overrides####

  • Jsni.new(expression): Normally when you new a class in C#, it will go through several hops, assuming it to be a C# class with a constructor. Using Jsni.new you can simply generate Javascript that will apply the new operator to any 'ol expression. The result of Jsni.new(Jsni.reference("Date")) would be new Date().

  • Lambdas: The syntax x => x.Foo == "bar" is compiled into a call to a special global function called $delegate. This is very similar to EcmaScript5's bind function (for example, it captures this such that invoking the delegate has a this that a C# programmer would expect, rather than null or window object which would happen otherwise). However, it also loads into the function the requisite data and methods to qualify as a C# delegate. (in addition to this, it qualifies as the specific delegate type it represents when interrogated with is and .GetType().) However, if for whatever reason you want to emulate the Javascript syntax of var func = function(x, y) { return x + y; }, you can use the multitude of function and procedure overloads (for Func<...> and Action<...> respectively) in Jsni to render such a function. For example:

    The C# syntax:

      var func = Jsni.function((x, y) => x + y);
    

    Will generate into:

      var func = function(x, y) { return x + y; };
    
  • Jsni.object(anonymoustypeexpression): Normally, when you have C# syntax like new { Foo = "bar" }, this anonymous type expression invokes the C# $ctor for the generated anonymous type implied by the expression. This means the initializer invokes setter methods since the anonymous type is composed of properties. This is exactly what you want when operating within C#. But if you need to express a normal Javascript object literal (the syntactical equivalent to anonymous types) you'll need to use this helper. For example, Jsni.object(new { Foo = "bar" }) will generate to { Foo: "bar" }. Similar to Jsni.reference, you must pass an anonymous type expression to this method. Failure to do so will fail the build.

####Global Functions####

  • Jsni.parseInt(integerString): Standin for the parseInt() global Javascript function.
  • Jsni.parseFloat(floatString): Standin for the parseFloat() global Javascript function.
  • Jsni.isNaN(number): Standin for the isNan() global Javascript function.
Clone this wiki locally