I have an AssemblyScript function that returns any string
it is given, as well as the corresponding code to import and run it in NodeJS:
AssemblyScript:
export function parse(x: string): string {
return x;
}
NodeJS:
import { readFileSync } from 'fs';
export default async function compile(raw) {
return WebAssembly.instantiate(
readFileSync('./src/.assembly/engine.wasm') // The WASM file containing compiled AssemblyScript
).then(mod => {
const { parse } = mod.instance.exports;
return parse(raw);
});
}
compile('test').then(res => {
console.log(res);
});
However, when this code is run, it returns an error about the imports argument not being present:
Terminal:
node:internal/process/promises:288
triggerUncaughtException(err, true /* fromPromise */);
^
[TypeError: WebAssembly.instantiate(): Imports argument must be present and must be an object]
Node.js v18.15.0
error Command failed with exit code 1.
Strangely, the code runs just fine if it uses i32
instead of string
:
AssemblyScript:
export function parse(x: i32): i32 {
return x;
}
NodeJS:
import { readFileSync } from 'fs';
export default async function compile(raw) {
return WebAssembly.instantiate(
readFileSync('./src/.assembly/engine.wasm') // The WASM file containing compiled AssemblyScript
).then(mod => {
const { parse } = mod.instance.exports;
return parse(raw);
});
}
compile(44).then(res => {
console.log(res);
});
Terminal:
44
How can I fix the code to work with strings too?
Your punctual error comes from the fact that, when strings are involved, AssemblyScript does a lot more to accommodate them, as they are not in the WebAssembly spec, which makes i32s much easier to work with.
You can see a glimpse of this if you use the strings
command to.
i32:
$ strings release.wasm
parse
memory
sourceMappingURL
./release.wasm.map
string:
$ strings release.wasm
abort
__new
__pin
__unpin
__collect
__rtti_base
memory
parse
A|q!
...many more lines later...
A}q6
A|qA
sourceMappingURL
./release.wasm.map
So, what WebAssembly.intantiate
complains about is firstly, the lack of the imports
object, and secondly, the lack of the abort
function that it needs.
Luckily, for a solution, you don't need to worry about any of that, the AssemblyScript loader does things automatically for you. You can use it to instantiate the module, instead of the WebAssembly.instantiate
function:
import { readFileSync } from 'fs';
import loader from "@assemblyscript/loader";
export default function compile(raw) {
let wasmModule = loader.instantiateSync(readFileSync('./build/release.wasm'), {});
const { __newString, __getString } = wasmModule.exports;
return __getString(wasmModule.exports.parse(__newString(raw)));
}
console.log(compile('test'));
Apart from that, the way to communicate with strings from javascript is sadly still to use the __newString
and __getString
that do the work of translating WebAssembly addresses for you, as WebAssembly does not have the concept of stings.
And, for a bit of good news:
$ node index.js
test
Your code now works as intended.