Interop
Of course one important cornerstone of WebAssembly development is inter-operating with JavaScript based APIs.
Elements provides strongly-typed support for working with the Browser APIs and the Document Object Model (DOM) via the Browser
class, but oftentimes you will also want to interact with your own JavaScript code, hosted in a 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 then instantiate an instance of the class simply by calling a method matching its name, on the module
, using its name followed by parenthesis. You then can call any public member on it.
You can see this in action with the Program
class in the default template:
var program = module.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.
Calling JavaScript Functions from WebAssembly
The easiest way to access JavaScript from your Elements code is to use the WebAssembly.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 inside your JavaScript.
Essentially it works the same as the eval
function provided by JavaScript itself.
var x := WebAssembly.Eval('DoSomething(10)');
var x = WebAssembly.Eval("DoSomething(10)");
let x = WebAssembly.Eval("DoSomething(10)")
var x = WebAssembly.Eval("DoSomething(10);");
var x = WebAssembly.Eval("DoSomething(10)");
Dim x = WebAssembly.Eval("DoSomething(10)")
This single line of Elements code could call a function that is declared, for example, like this:
function DoSomething(someParam)
{
...
return 5;
}
Declaring Strongly-Typed Method Stubs
The languages extern
/external
/native
/Declare
syntax can be used to declare strongly typed global function stubs that can be called, letting the compiler generate the necessary calls to WebAssembly.Eval()
under the hood:
method DoSomething(someParam: Integer): Integer; external;
public extern int DoSomething(int someParam);
public __external func int DoSomething(int someParam);
public native int DoSomething(int someParam);
Declare Function DoSomething(someParam As Int) As Int
The DoSomething
method can now be called directly anywhere from Elements WebAssembly code, with strongly-typed parameters and result. Under the hood, the compiler will emit the proper call back to JavaScript.
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 an 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 they cannot 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 GimmeSomeObject
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 Browser's Document Object Mode; (DOM). these are declared in the RemObjects.Elements.WebAssembly.DOM namespace.
See Also
DynamicInterface
AspectEcmaScriptObject
TypeWebAssembly
ClassEval
FunctionBrowser
Class- RemObjects.Elements.WebAssembly.DOM namespace