Difference between revisions of "Generating WebAssembly with LDC"
m (→Test in HTML page) |
|||
Line 63: | Line 63: | ||
response received | response received | ||
instantiated | instantiated | ||
+ | r = 39.5 | ||
+ | </pre> | ||
+ | |||
+ | == Calling external functions == | ||
+ | |||
+ | The minimal example above only calls in one direction, from JavaScript to WebAssembly. Here's how to call external functions in D: | ||
+ | |||
+ | <tt>wasm.d</tt>: | ||
+ | |||
+ | <syntaxhighlight lang="D"> | ||
+ | extern(C): // disable D mangling | ||
+ | |||
+ | void callback(double a, double b, double c); | ||
+ | |||
+ | double add(double a, double b) | ||
+ | { | ||
+ | const c = a + b; | ||
+ | callback(a, b, c); | ||
+ | return c; | ||
+ | } | ||
+ | |||
+ | void _start() {} | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Add <tt>-L-allow-undefined</tt> as linker flag to the LDC command line, otherwise LLD refuses to link due to undefined <tt>callback()</tt>. | ||
+ | |||
+ | Implement the <tt>callback()</tt> function in JavaScript and specify it in <tt>importObject.env</tt>: | ||
+ | |||
+ | <syntaxhighlight lang="JavaScript"> | ||
+ | const callback = (a, b, c) => { | ||
+ | console.log(`callback from D: ${a} + ${b} = ${c}`); | ||
+ | }; | ||
+ | |||
+ | // ... | ||
+ | |||
+ | const importObject = { | ||
+ | env: { callback } | ||
+ | }; | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | The log should now show: | ||
+ | |||
+ | <pre> | ||
+ | request sent | ||
+ | response received | ||
+ | instantiated | ||
+ | callback from D: 42 + -2.5 = 39.5 | ||
r = 39.5 | r = 39.5 | ||
</pre> | </pre> | ||
[[Category:LDC]] | [[Category:LDC]] |
Revision as of 17:07, 15 July 2018
Starting with v1.11, LDC supports compiling and linking directly to WebAssembly. This page shows how to get started.
Building WebAssembly
Let's generate a .wasm file for this D code (wasm.d):
extern(C): // disable D mangling
double add(double a, double b) { return a + b; }
// seems to be the required entry point
void _start() {}
Build wasm.wasm:
ldc2 -mtriple=wasm32-unknown-unknown-wasm -betterC -link-internally wasm.d
In case LDC errors out (e.g., with unsupported -link-internally), try an official prebuilt release package.
Test in HTML page
Let's test it with a little HTML page, loading and invoking the WebAssembly via JavaScript. Generate an .html file in the same directory as the .wasm file, with the following contents:
<html>
<head>
<script>
const request = new XMLHttpRequest();
request.open('GET', 'wasm.wasm');
request.responseType = 'arraybuffer';
request.onload = () => {
console.log('response received');
const bytes = request.response;
const importObject = {};
WebAssembly.instantiate(bytes, importObject).then(result => {
console.log('instantiated');
const { exports } = result.instance;
// finally, call the add() function implemented in D:
const r = exports.add(42, -2.5);
console.log('r = ' + r);
});
};
request.send();
console.log('request sent');
</script>
</head>
<body>
Test page
</body>
</html>
Note that fetch() doesn't work for files in the local filesystem (file://), but XMLHttpRequest does in Firefox (not in Chrome though IIRC).
Open the HTML page; the JavaScript console should show:
request sent response received instantiated r = 39.5
Calling external functions
The minimal example above only calls in one direction, from JavaScript to WebAssembly. Here's how to call external functions in D:
wasm.d:
extern(C): // disable D mangling
void callback(double a, double b, double c);
double add(double a, double b)
{
const c = a + b;
callback(a, b, c);
return c;
}
void _start() {}
Add -L-allow-undefined as linker flag to the LDC command line, otherwise LLD refuses to link due to undefined callback().
Implement the callback() function in JavaScript and specify it in importObject.env:
const callback = (a, b, c) => {
console.log(`callback from D: ${a} + ${b} = ${c}`);
};
// ...
const importObject = {
env: { callback }
};
The log should now show:
request sent response received instantiated callback from D: 42 + -2.5 = 39.5 r = 39.5