Difference between revisions of "JavaScript"

From PowerUI
Jump to: navigation, search
(Can I run x - How compliant is the JavaScript in PowerUI?)
 
(7 intermediate revisions by 3 users not shown)
Line 1: Line 1:
PowerUI currently has three engines for runtime code - Nitro (script type='text/nitro'), Nitrassic (script type='text/javascript-x') and WebAssembly. Nitro is a custom language which more resembles UnityScript and the current version of Nitrassic is mostly compliant with ECMAScript 5. Nitro, the older engine, is now defunct and will be removed in the near future so you should either code using C# or use type='text/javascript-x' in your script tags.
+
PowerUI presently runs Javascript via Jint and an implementation of WebAssembly. It previously included a series of custom Javascript engines which were fast but ultimately fell short when it came to standards compliance.
  
 
== The JavaScript challenge - A short history ==
 
== The JavaScript challenge - A short history ==
  
'''Most gaming platforms, including iOS, explicitly don't allow runtime compliation.''' JavaScript was designed to be runtime compiled so these restrictions cause an instant major problem. It's not like it's a particularly small issue either - all but two of the Unity platforms (Standalone and Android) don't allow runtime compilation. The only route to get JavaScript working on those platforms is to compile the JavaScript 'ahead of time' in the editor. However, that is ''extremely'' difficult for a language as dynamic as JavaScript is. We initially went the same route as Unity - create a JavaScript-like language which can be easily compiled in the editor. That's Nitro.
+
'''Most gaming platforms, including iOS, explicitly don't allow runtime compliation.''' JavaScript was designed to be runtime compiled so these restrictions cause an instant major problem. It's not like it's a particularly small issue either - all but two of the Unity platforms (Standalone and Android) don't allow runtime compilation. The only route to get JavaScript working on those platforms is to compile the JavaScript 'ahead of time' in the editor. However, that is ''extremely'' difficult for a language as dynamic as JavaScript is. We initially went the same route as Unity - create a JavaScript-like language which can be easily compiled in the editor. That was Nitro.
  
 
If you've ever used UnityScript (or Nitro itself) you will notice very quickly that you can't simply run a library like jQuery, or indeed virtually any actual JavaScript from the web ''(it's still very unfortunate that Unity still calls UnityScript 'JavaScript' and uses the .js extension)''. So over the years as PowerUI's support for the web in general has increased, we naturally needed to make a new JavaScript engine - one which supported ahead of time compilation and as much of the JavaScript specification as possible. That's Nitrassic.
 
If you've ever used UnityScript (or Nitro itself) you will notice very quickly that you can't simply run a library like jQuery, or indeed virtually any actual JavaScript from the web ''(it's still very unfortunate that Unity still calls UnityScript 'JavaScript' and uses the .js extension)''. So over the years as PowerUI's support for the web in general has increased, we naturally needed to make a new JavaScript engine - one which supported ahead of time compilation and as much of the JavaScript specification as possible. That's Nitrassic.
  
 
Nitrassic was built by taking the [https://github.com/paulbartrum/jurassic Jurassic engine] and merging it with Nitro (thus the name) - adding a type tracking system to make it suitable for editor compilation. This ultimately had the effect of making Jurassic massively faster, but at the expense of months of high complexity compiler work. We're in new territory here - Nitrassic is the first ahead-of-time JavaScript compiler which could have big implications for things like Node.js in the wider web world; particularly as, when combined with IL2CPP, the result is JavaScript running at near native speeds.
 
Nitrassic was built by taking the [https://github.com/paulbartrum/jurassic Jurassic engine] and merging it with Nitro (thus the name) - adding a type tracking system to make it suitable for editor compilation. This ultimately had the effect of making Jurassic massively faster, but at the expense of months of high complexity compiler work. We're in new territory here - Nitrassic is the first ahead-of-time JavaScript compiler which could have big implications for things like Node.js in the wider web world; particularly as, when combined with IL2CPP, the result is JavaScript running at near native speeds.
 +
 +
Since then however, running Javascript has become a little easier so presently PowerUI runs Jint for full standards compliance.
  
 
== Can I run x - How compliant is the JavaScript in PowerUI? ==
 
== Can I run x - How compliant is the JavaScript in PowerUI? ==
  
The best option? Just try it and see what happens! At the moment, this depends on:
+
The best option? Just try it and see what happens! At the moment, this depends on the Web API's that you intend to use. PowerUI currently has broad support for a variety of web APIs. The API coverage is enough such that jQuery and basic React works for example.
* Further testing is required.
 
* If you 'collapse' a variable - this happens when you set it to things of more than one type and the type tracker is forced to stop tracking it. Not all dynamic resolve paths are complete so your success here will vary. In general though collapsed variables are an indication of incorrect code so the engine will at least tell you about them.
 
 
 
<syntaxhighlight lang="javascript">
 
 
 
var a=14;
 
 
 
function Hello(){
 
    a="My String";
 
}
 
 
 
// A is both a string and a number. It 'collapses' to Object.
 
 
 
</syntaxhighlight>
 
 
 
Note that collapsing function arguments is perfectly fine:
 
 
 
<syntaxhighlight lang="javascript">
 
 
 
function Adder(a,b){
 
    return a+b;
 
}
 
 
 
Adder(1,2);
 
Adder("hello ","world!");
 
 
 
</syntaxhighlight>
 
 
 
* If your JavaScript uses the 'delete' keyword (jQuery does) then tracking the types becomes undefined, but it will still try anyway (jQuery fails because of the below point).
 
* How much of the core Object interface 'x' uses. Primarily that's prototype. Whilst prototypes are supported, anObject.prototype (which is part of that interface) currently isn't. You can access these instance prototypes via a constructor function instead:
 
 
 
<syntaxhighlight lang="javascript">
 
 
 
function Point(x,y){
 
    // 'this' is equivalent of Point.prototype
 
    this.x=x;
 
    this.y=y;
 
}
 
 
 
var p = new Point(114,20);
 
 
 
</syntaxhighlight>
 
 
 
We're working on the above points and any help with that would be greatly appreciated!
 
  
 
== Interaction with the rest of your code ==
 
== Interaction with the rest of your code ==
  
Nitrassic like C# is a .NET language - that means they can call each other with no overhead when crossing the boundary. In short, you get the best of all worlds because it acts like JavaScript with all of the types available to C# being also available to the JavaScript engine.
+
Running Javascript is a little useless if you can't make it interact with your game, and vice versa. Here's how to get that going.
  
 
=== Calling C#/Unity methods from JS ===
 
=== Calling C#/Unity methods from JS ===
  
Nitrassic has no particular issues calling C# methods from your project - it "just works" unless you explicitly block it from doing so:
+
You'll need the classes you want to use to be inside a namespace. From there, you can then import the namespace like this:
  
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
  
// You can use the Unity API too:
+
// You can use the Unity API directly too:
var go = new GameObject();
+
var UnityEngine = importNamespace("UnityEngine");
 +
 
 +
var go = new UnityEngine.GameObject();
  
 
go.name = "MadeWithLoveByRealJavaScript";
 
go.name = "MadeWithLoveByRealJavaScript";
 
// Or if you have some static C# class just call it like you would from C#:
 
MyCSharpClass.HelloFromJavaScript();
 
  
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==== Passing Functions ====
+
=== Calling JS from C# ===
 
 
This is an awesome aspect of Nitrassic and it's highly recommended you make use of it! If you pass a JavaScript function in the place of a delegate (or relatives, like Action) then it will automagically adopt that delegates type. Like this - here's the C#:
 
 
 
<syntaxhighlight lang="csharp">
 
 
 
public static class MyCallbacks{
 
 
 
    public static void RunLater(Action<string> onDone){
 
        // ..Do something..
 
        onDone("Hello from C#!");
 
    }
 
  
}
+
To call JS functions from C#, use document.Run:
 
 
</syntaxhighlight>
 
 
 
And here's the JavaScript:
 
  
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
+
var document = UI.document;
// Usually you'd use this for event based stuff.
+
document.Run("FunctionName", Arguments);
MyCallbacks.RunLater(function(a){
 
  // The type tracker knows 'a' is a string:
 
  console.log(a);
 
});
 
 
 
 
</syntaxhighlight>
 
</syntaxhighlight>
 
 
==== Passing Ambiguous Functions ====
 
 
If your C# function is overloaded (there's multiple versions of it) then it might be ambiguous. addEventListener is a perfect example of when this happens:
 
 
<syntaxhighlight lang="javascript">
 
 
// Add a mousedown event listener:
 
myElement.addEventListener("mousedown",function(e){
 
  // The type tracker is magic enough to figure out that
 
  // 'e' is a MouseEvent - the following has no runtime overhead:
 
  console.log(e.clientX);
 
});
 
 
// Add a keydown event listener:
 
myElement.addEventListener("keydown",function(e){
 
  // The type tracker is magic enough to figure out that
 
  // 'e' is a *KeyboardEvent*.
 
});
 
 
</syntaxhighlight>
 
 
Whenever one of your functions is ambiguous, it should define a special disambiguation method. The one for addEventListener uses that first argument to figure out the most appropriate one is (e.g. when it spots 'mousedown' it knows it's a MouseEvent). Here's how to define a disambiguation method:
 
 
<syntaxhighlight lang="csharp">
 
 
public static class MyCallbacks{
 
   
 
    // These two methods are ambiguous so we'll define a disambiguation method:
 
    [JSProperties(Disambiguation="RunLaterDisambig")]
 
    public static void RunLater(string type, Action<string> onDone){
 
        // ..Do something..
 
        onDone("Hello from C#!");
 
    }
 
 
    public static void RunLater(string type, Action<int> onDone){
 
        // ..Do something..
 
        onDone(140);
 
    }
 
 
    public MethodInfo RunLaterDisambig(MethodGroup group,FunctionCallExpression fce){
 
       
 
        // This runs when compiling each call like MyCallbacks.RunLater("something",function(x){ });
 
        // 'group' is the set of 2 methods we need to select from.
 
        // 'fce' is the function call expression in the source which requires disambiguation.
 
       
 
        // Assuming that we can identify which one to use based on that textual first arg, we can do this:
 
        string typeValue = fce.Arg(0) as string;
 
       
 
        if(typeValue == null || typeValue == "string"){
 
            // It's not a constant string. Can't disambiguate (fallback on a default):
 
            return group.Match(new Type[]{typeof(string), typeof(Action<string>)});
 
        }
 
       
 
        // Otherwise use the int version:
 
        return group.Match(new Type[]{typeof(string), typeof(Action<int>)});
 
    }
 
}
 
 
</syntaxhighlight>
 
 
=== Calling JS from C# ===
 
 
You'll want to get the ScriptEngine and use the CallGlobalFunction method. Work in progress!
 

Latest revision as of 00:18, 6 September 2018

PowerUI presently runs Javascript via Jint and an implementation of WebAssembly. It previously included a series of custom Javascript engines which were fast but ultimately fell short when it came to standards compliance.

The JavaScript challenge - A short history

Most gaming platforms, including iOS, explicitly don't allow runtime compliation. JavaScript was designed to be runtime compiled so these restrictions cause an instant major problem. It's not like it's a particularly small issue either - all but two of the Unity platforms (Standalone and Android) don't allow runtime compilation. The only route to get JavaScript working on those platforms is to compile the JavaScript 'ahead of time' in the editor. However, that is extremely difficult for a language as dynamic as JavaScript is. We initially went the same route as Unity - create a JavaScript-like language which can be easily compiled in the editor. That was Nitro.

If you've ever used UnityScript (or Nitro itself) you will notice very quickly that you can't simply run a library like jQuery, or indeed virtually any actual JavaScript from the web (it's still very unfortunate that Unity still calls UnityScript 'JavaScript' and uses the .js extension). So over the years as PowerUI's support for the web in general has increased, we naturally needed to make a new JavaScript engine - one which supported ahead of time compilation and as much of the JavaScript specification as possible. That's Nitrassic.

Nitrassic was built by taking the Jurassic engine and merging it with Nitro (thus the name) - adding a type tracking system to make it suitable for editor compilation. This ultimately had the effect of making Jurassic massively faster, but at the expense of months of high complexity compiler work. We're in new territory here - Nitrassic is the first ahead-of-time JavaScript compiler which could have big implications for things like Node.js in the wider web world; particularly as, when combined with IL2CPP, the result is JavaScript running at near native speeds.

Since then however, running Javascript has become a little easier so presently PowerUI runs Jint for full standards compliance.

Can I run x - How compliant is the JavaScript in PowerUI?

The best option? Just try it and see what happens! At the moment, this depends on the Web API's that you intend to use. PowerUI currently has broad support for a variety of web APIs. The API coverage is enough such that jQuery and basic React works for example.

Interaction with the rest of your code

Running Javascript is a little useless if you can't make it interact with your game, and vice versa. Here's how to get that going.

Calling C#/Unity methods from JS

You'll need the classes you want to use to be inside a namespace. From there, you can then import the namespace like this:

// You can use the Unity API directly too:
var UnityEngine = importNamespace("UnityEngine");

var go = new UnityEngine.GameObject();

go.name = "MadeWithLoveByRealJavaScript";

Calling JS from C#

To call JS functions from C#, use document.Run:

var document = UI.document;
document.Run("FunctionName", Arguments);