Interop

Of course one important cornerstone of WebAssembly development is inter-operating with with JavaScript based APIs.

Elements provides strongly-typed support for working with the Browser APIs and the Document Object Model (DOM) via the RemObjects.Elements.WebAssenbly.Browser class, but oftentimes you will also want to interact with your own JavaScript code, hosted in separate .js files or in your core .html.

This inter-op works both ways:

Accessing WebAssembly Types from JavaScript

Accessing your Elements classes from JavaScript is easy.

On the WebAssembly side, simply make sure your class is marked with the Export Aspect.

type
  [Export]
  Program = public class
  public
    method HelloWorld();
[Export]
public class Program 
{
    public void HelloWorld() 
@Export
public class Program {
    public void HelloWorld() 
@Export
public class Program 
{
    public void HelloWorld() 
<Export>
Public Class Program

  Public Sub HelloWorld

In JavaScript, you can than instantiate an instance of the class imply by using its name followed by parenthesis, and then call any member on it.

You can see this i action with the Program class in the default template:

var program = result.Program();
program.HelloWorld();

If your constructor or your method take parameters, you can of course pass these within the parenthesis. Make sure to only use parameter or return types that are compatible with JavaScript, of course.

Calling JavaScript functions from WebAssembly

The easiest way to access JavaScript from your Elements code is to use the global Eval method. This method takes a string that can be any arbitrary JavaScript code, but for the purpose of making inter-op calls, it can be a function call to a function insiode yur JavaScript:

var x := Eval('GimmeSomeObject()');
var x = Eval("GimmeSomeObject()");
let x = Eval("GimmeSomeObject()")
var x = Eval("GimmeSomeObject();");
var x = Eval("GimmeSomeObject()");
dim x = Eval("GimmeSomeObject()");

This single line of elements code could call a function declared, for example, like this:

function DoSomething(someParam)
{
   ...
   return 5;
}

Calling JavaScript Object APIs from WebAssembly

You can also obtain references to JavaScript object instances from your Elements code. For example, an Eval call as shown above might return such object, as do many of the existing Browser, NodeJS and DOM APIs exposed by Island RTL.

By default, such objects are typed as Dynamic, which means that – just as in JavaScript itself – the compiler has no intrinsic knowledge of what methods or properties might be available on the object. The compiler will let you make calls to any arbitrary member, and the calls will be dispatched dynamically at runtime – failing at runtime if thye cannt be completed (again, just as they would in JavaScript itself).

var x := Eval('GimmeSomeObject()');
x.LetsMakeACall();
var x = Eval("GimmeSomeObject()");
x.LetsMakeACall();
let x = Eval("GimmeSomeObject()")
x.LetsMakeACall()
var x = Eval("GimmeSomeObject();");
x.LetsMakeACall();
var x = Eval("GimmeSomeObject()")
x.LetsMakeACall()
dim x = Eval("GimmeSomeObject()")
x.LetsMakeACall()

This code obtains a Javascript object by calling the GimmeSomeOject function defined in JavaScript. The variable x will be typed as Dynamic, letting us call any method we want.

Creating Strongly-Typed Interfaces

If you know the exact API of a JavaScript object, you can create a strongly typed interface that describes the available members, on the Elements side. You do this by adding the DynamicInterface(GetType(EcmaScriptObject)) Aspect to the interface:

type
  [DynamicInterface(GetType(EcmaScriptObject))
  ISomeObject = public interface
    method LetsMakeACall;
  end;
[DynamicInterface(GetType(EcmaScriptObject))
public interface ISomeObject
{
    void LetsMakeACall();
}
@DynamicInterface(GetType(EcmaScriptObject)
public interface ISomeObject {
    void LetsMakeACall()
}
@DynamicInterface(GetType(EcmaScriptObject)
public interface ISomeObject 
{
    void LetsMakeACall();
}
<DynamicInterface(GetType(EcmaScriptObject)>
Public Interface ISomeObject
    Sub LetsMakeACall()
End Interface

Once implemented, simply cast your Dynamic object reference to an the interface, and you can now make strongly-typed calls to the object that will; be checked by the compiler (and you will get code completion, as well):

var x := Eval('GimmeSomeObject()') as ISomeObject;
x.LetsMakeACall();
var x = (ISomeObject)Eval("GimmeSomeObject()");
x.LetsMakeACall();
let x = Eval("GimmeSomeObject()") as! ISomeObject
x.LetsMakeACall()
var x = Eval("GimmeSomeObject();") as ISomeObject;
x.LetsMakeACall();
dim x = CType(Eval("GimmeSomeObject()"), ISomeObject)
x.LetsMakeACall()

Now, x is strongly-typed to be a ISomeObject, and the compiler will enforce that you only call known members. And you will get code completion, as well – for example CC after x. would show you LetsMakeACall as valid option.

Predefined Interfaces

Island RTL already contains pre-defined dynamic interfaces for dozens of common JavaScript objects used by the DOM, in the RemObjects.Elements.WebAssembly.DOM namespace.

See Also