Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upIs it possible to share complicated JavaScript data structure directly with WebAssembly? #18
Comments
This comment has been minimized.
This comment has been minimized.
Pauan
commented
Oct 17, 2018
•
|
Yes, with the reference-types and host-bindings proposals, it is possible to pass a JS object reference directly to wasm (and also pass a JS object from wasm back to JS). However, those proposals aren't fully standardized and implemented yet. In the meantime, there is a workaround that you can use right now:
This technique is used in wasm-bindgen and stdweb, and it works with all JS objects (it also works with JS primitive types as well, like strings). Here is a concrete example:
(module
(import "env" "makeNewArray" (func $makeNewArray (result i32)))
(import "env" "push" (func $push (param i32) (param i32)))
(import "env" "length" (func $length (param i32) (result i32)))
(import "env" "free" (func $free (param i32)))
(import "env" "logInt" (func $logInt (param i32)))
(import "env" "logRef" (func $logRef (param i32)))
(func (export "main")
(local $array i32)
(set_local $array (call $makeNewArray))
(call $push (get_local $array) (i32.const 1))
(call $push (get_local $array) (i32.const 1))
(call $push (get_local $array) (i32.const 2))
(call $push (get_local $array) (i32.const 3))
(call $push (get_local $array) (i32.const 5))
(call $logInt (call $length (get_local $array)))
(call $logRef (get_local $array))
(call $free (get_local $array)))
)
// This is a unique integer which is used for the id of the JS objects
var jsRefId = 0;
// This is a dictionary which maps from id to JS object
var jsRefs = {};
// This creates a new id, puts the object into the dictionary, then returns the new id
function mallocJsRef(obj) {
var id = jsRefId;
// Because we always increment the id, it's guaranteed to be unique
// (until it reaches 4294967295)
++jsRefId;
jsRefs[id] = obj;
return id;
}
// This looks up the JS object based upon its id
function lookupJsRef(id) {
return jsRefs[id];
}
// This cleans up the memory for the JS object (by allowing it to be garbage collected)
function freeJsRef(id) {
delete jsRefs[id];
}
var imports = {
env: {
makeNewArray: function () {
return mallocJsRef([]);
},
push: function (id, value) {
lookupJsRef(id).push(value);
},
length: function (id) {
return lookupJsRef(id).length;
},
logInt: function (value) {
console.log(value);
},
logRef: function (id) {
console.log(lookupJsRef(id));
},
free: function (id) {
freeJsRef(id);
}
}
};
WebAssembly.instantiateStreaming(fetch('../out/main.wasm'), imports).then(function (result) {
result.instance.exports.main();
}).catch(function (e) {
console.error(e);
});The wasm code basically does the equivalent of this JS code: var array = [];
array.push(1);
array.push(1);
array.push(2);
array.push(3);
array.push(5);
console.log(array.length);
console.log(array);You can run the
So the only boilerplate is that you have to create small shims (such as Also, the performance should be quite good: calls between JS and wasm are very fast (thanks to recent browser optimizations), and it's just passing an integer back and forth between JS and wasm (no copying of data!) So the only performance cost is creating a new id (which is very fast), inserting the object into the dictionary, looking up the id in the dictionary, and deleting the id from the dictionary. This should generally be fast enough. But if you need more performance, there are various optimizations you can try: rather than using a hash table for the dictionary, you can instead use an array (the indexes of the array are the same as the id). Or you could even create your own custom heap implementation in JS. |
Becavalier commentedOct 17, 2018
•
edited
I want to know is it possible to share complicated JavaScript data structure like "Object" or "Array" directly with WebAssembly context?
As I know, there will be a lot of overhead if we want to share a JavaScript array with WebAssembly, first we need to encode this array data and then pass it to WebAssembly context through WebAssembly.Memory. Second, the WebAssembly module(C/C++ side) also need to fetch those data again in the WebAssembly linear memory through the pointer of this array.
So I want to know is there a way to share this array entity which already generated and exist in V8 (or any other JavaScript engine) without re-constructing it in WebAssembly.Memory again?