summaryrefslogtreecommitdiff
path: root/src/js
diff options
context:
space:
mode:
Diffstat (limited to 'src/js')
-rw-r--r--src/js/wasm.js-post.js318
1 files changed, 206 insertions, 112 deletions
diff --git a/src/js/wasm.js-post.js b/src/js/wasm.js-post.js
index 75a91d4d4..9851fc777 100644
--- a/src/js/wasm.js-post.js
+++ b/src/js/wasm.js-post.js
@@ -16,17 +16,25 @@
function integrateWasmJS(Module) {
// wasm.js has several methods for creating the compiled code module here:
- // * 'wasm-s-parser': load s-expression code from a .wast and create wasm
- // * 'asm2wasm': load asm.js code and translate to wasm
- // * 'just-asm': no wasm, just load the asm.js code and use that (good for testing)
+ // * 'native-wasm' : use native WebAssembly support in the browser
+ // * 'interpret-s-expr': load s-expression code from a .wast and interpret
+ // * 'interpret-binary': load binary wasm and interpret
+ // * 'interpret-asm2wasm': load asm.js code, translate to wasm, and interpret
+ // * 'asmjs': no wasm, just load the asm.js code and use that (good for testing)
// The method can be set at compile time (BINARYEN_METHOD), or runtime by setting Module['wasmJSMethod'].
- var method = Module['wasmJSMethod'] || 'wasm-s-parser';
- assert(method == 'asm2wasm' || method == 'wasm-s-parser' || method == 'just-asm');
+ // The method can be a comma-separated list, in which case, we will try the
+ // options one by one. Some of them can fail gracefully, and then we can try
+ // the next.
- if (method == 'just-asm') {
- eval(Module['read'](Module['asmjsCodeFile']));
- return;
- }
+ // inputs
+
+ var method = Module['wasmJSMethod'] || {{{ wasmJSMethod }}} || 'native-wasm,interpret-s-expr'; // by default, try native and then .wast
+
+ var wasmTextFile = Module['wasmTextFile'] || {{{ wasmTextFile }}};
+ var wasmBinaryFile = Module['wasmBinaryFile'] || {{{ wasmBinaryFile }}};
+ var asmjsCodeFile = Module['asmjsCodeFile'] || {{{ asmjsCodeFile }}};
+
+ // utilities
var asm2wasmImports = { // special asm2wasm imports
"f64-rem": function(x, y) {
@@ -40,48 +48,76 @@ function integrateWasmJS(Module) {
},
};
- function flatten(obj) {
- var ret = {};
- for (var x in obj) {
- for (var y in obj[x]) {
- if (ret[y]) Module['printErr']('warning: flatten dupe: ' + y);
- if (typeof obj[x][y] === 'function') {
- ret[y] = obj[x][y];
- }
- }
+ var info = {
+ 'global': null,
+ 'env': null,
+ 'asm2wasm': asm2wasmImports,
+ 'parent': Module // Module inside wasm-js.cpp refers to wasm-js.cpp; this allows access to the outside program.
+ };
+
+ var exports = null;
+
+ function lookupImport(mod, base) {
+ var lookup = info;
+ if (mod.indexOf('.') < 0) {
+ lookup = (lookup || {})[mod];
+ } else {
+ var parts = mod.split('.');
+ lookup = (lookup || {})[parts[0]];
+ lookup = (lookup || {})[parts[1]];
}
- return ret;
+ if (base) {
+ lookup = (lookup || {})[base];
+ }
+ if (lookup === undefined) {
+ abort('bad lookupImport to (' + mod + ').' + base);
+ }
+ return lookup;
}
function mergeMemory(newBuffer) {
// The wasm instance creates its memory. But static init code might have written to
- // buffer already, and we must copy it over in a proper merge.
+ // buffer already, including the mem init file, and we must copy it over in a proper merge.
// TODO: avoid this copy, by avoiding such static init writes
// TODO: in shorter term, just copy up to the last static init write
var oldBuffer = Module['buffer'];
- assert(newBuffer.byteLength >= oldBuffer.byteLength, 'we might fail if we allocated more than TOTAL_MEMORY');
- // the wasm module does write out the memory initialization, in range STATIC_BASE..STATIC_BUMP, so avoid that
- (new Int8Array(newBuffer).subarray(0, STATIC_BASE)).set(new Int8Array(oldBuffer).subarray(0, STATIC_BASE));
- (new Int8Array(newBuffer).subarray(STATIC_BASE + STATIC_BUMP)).set(new Int8Array(oldBuffer).subarray(STATIC_BASE + STATIC_BUMP));
+ if (newBuffer.byteLength < oldBuffer.byteLength) {
+ Module['printErr']('the new buffer in mergeMemory is smaller than the previous one. in native wasm, we should grow memory here');
+ }
+ var oldView = new Int8Array(oldBuffer);
+ var newView = new Int8Array(newBuffer);
+ if ({{{ WASM_BACKEND }}}) {
+ // memory segments arrived in the wast, do not trample them
+ oldView.set(newView.subarray(STATIC_BASE, STATIC_BASE + STATIC_BUMP), STATIC_BASE);
+ }
+ newView.set(oldView);
updateGlobalBuffer(newBuffer);
updateGlobalBufferViews();
Module['reallocBuffer'] = function(size) {
var old = Module['buffer'];
- wasmJS['asmExports']['__growWasmMemory'](size); // tiny wasm method that just does grow_memory
+ exports['__growWasmMemory'](size); // tiny wasm method that just does grow_memory
return Module['buffer'] !== old ? Module['buffer'] : null; // if it was reallocated, it changed
};
}
+ var WasmTypes = {
+ none: 0,
+ i32: 1,
+ i64: 2,
+ f32: 3,
+ f64: 4
+ };
+
// wasm lacks globals, so asm2wasm maps them into locations in memory. that information cannot
// be present in the wasm output of asm2wasm, so we store it in a side file. If we load asm2wasm
// output, either generated ahead of time or on the client, we need to apply those mapped
// globals after loading the module.
- function applyMappedGlobals() {
- var mappedGlobals = JSON.parse(Module['read'](Module['wasmCodeFile'] + '.mappedGlobals'));
+ function applyMappedGlobals(globalsFileBase) {
+ var mappedGlobals = JSON.parse(Module['read'](globalsFileBase + '.mappedGlobals'));
for (var name in mappedGlobals) {
var global = mappedGlobals[name];
if (!global.import) continue; // non-imports are initialized to zero in the typed array anyhow, so nothing to do here
- var value = wasmJS['lookupImport'](global.module, global.base);
+ var value = lookupImport(global.module, global.base);
var address = global.address;
switch (global.type) {
case WasmTypes.i32: Module['HEAP32'][address >> 2] = value; break;
@@ -92,113 +128,171 @@ function integrateWasmJS(Module) {
}
}
- if (typeof WASM === 'object' || typeof wasmEval === 'function') {
+ function fixImports(imports) {
+ if (!{{{ WASM_BACKEND }}}) return imports;
+ var ret = {};
+ for (var i in imports) {
+ var fixed = i;
+ if (fixed[0] == '_') fixed = fixed.substr(1);
+ ret[fixed] = imports[i];
+ }
+ return ret;
+ }
+
+ function getBinary() {
+ var binary;
+ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
+ binary = Module['wasmBinary'];
+ assert(binary, "on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)");
+ binary = new Uint8Array(binary);
+ } else {
+ binary = Module['readBinary'](wasmBinaryFile);
+ }
+ return binary;
+ }
+
+ // do-method functions
+
+ function doJustAsm() {
+ if (typeof Module['asm'] !== 'function') {
+ // you can load the .asm.js file before this, to avoid this sync xhr and eval
+ eval(Module['read'](asmjsCodeFile));
+ }
+ if (typeof Module['asm'] !== 'function') {
+ // evalling the asm.js file should have set this
+ Module['printErr']('asm evalling did not set the module properly');
+ return false;
+ }
+ return true;
+ }
+
+ function doNativeWasm() {
+ if (typeof Wasm !== 'object') {
+ Module['printErr']('no native wasm support detected');
+ return false;
+ }
+
// Provide an "asm.js function" for the application, called to "link" the asm.js module. We instantiate
// the wasm module at that time, and it receives imports and provides exports and so forth, the app
// doesn't need to care that it is wasm and not asm.
Module['asm'] = function(global, env, providedBuffer) {
- // Load the wasm module
- var binary = Module['readBinary'](Module['wasmCodeFile']);
- // Create an instance of the module using native support in the JS engine.
- var importObj = {
- "global.Math": global.Math,
- "env": env,
- "asm2wasm": asm2wasmImports
+ global = fixImports(global);
+ env = fixImports(env);
+
+ // Load the wasm module and create an instance of using native support in the JS engine.
+ info['global'] = {
+ 'NaN': NaN,
+ 'Infinity': Infinity
};
+ info['global.Math'] = global.Math;
+ info['env'] = env;
var instance;
- if (typeof WASM === 'object') {
- instance = WASM.instantiateModule(binary, flatten(importObj));
- } else if (typeof wasmEval === 'function') {
- instance = wasmEval(binary.buffer, importObj);
- } else {
- throw 'how to wasm?';
- }
- mergeMemory(instance.memory);
+ instance = Wasm.instantiateModule(getBinary(), info);
+ exports = instance.exports;
+ mergeMemory(exports.memory);
- applyMappedGlobals();
+ applyMappedGlobals(wasmBinaryFile);
- return instance;
+ return exports;
};
- return;
+ Module["usingWasm"] = true;
+
+ return true;
}
- var WasmTypes = {
- none: 0,
- i32: 1,
- i64: 2,
- f32: 3,
- f64: 4
- };
+ function doWasmPolyfill(method) {
+ if (typeof WasmJS !== 'function') {
+ Module['printErr']('WasmJS not detected - polyfill not bundled?');
+ return false;
+ }
- // Use wasm.js to polyfill and execute code in a wasm interpreter.
- var wasmJS = WasmJS({});
+ // Use wasm.js to polyfill and execute code in a wasm interpreter.
+ var wasmJS = WasmJS({});
- // XXX don't be confused. Module here is in the outside program. wasmJS is the inner wasm-js.cpp.
- wasmJS['outside'] = Module; // Inside wasm-js.cpp, Module['outside'] reaches the outside module.
+ // XXX don't be confused. Module here is in the outside program. wasmJS is the inner wasm-js.cpp.
+ wasmJS['outside'] = Module; // Inside wasm-js.cpp, Module['outside'] reaches the outside module.
- // Information for the instance of the module.
- var info = wasmJS['info'] = {
- global: null,
- env: null,
- asm2wasm: asm2wasmImports,
- parent: Module // Module inside wasm-js.cpp refers to wasm-js.cpp; this allows access to the outside program.
- };
+ // Information for the instance of the module.
+ wasmJS['info'] = info;
- wasmJS['lookupImport'] = function(mod, base) {
- var lookup = info;
- if (mod.indexOf('.') < 0) {
- lookup = (lookup || {})[mod];
- } else {
- var parts = mod.split('.');
- lookup = (lookup || {})[parts[0]];
- lookup = (lookup || {})[parts[1]];
- }
- lookup = (lookup || {})[base];
- if (lookup === undefined) {
- abort('bad lookupImport to (' + mod + ').' + base);
- }
- return lookup;
- }
+ wasmJS['lookupImport'] = lookupImport;
+
+ // The asm.js function, called to "link" the asm.js module. At that time, we are provided imports
+ // and respond with exports, and so forth.
+ Module['asm'] = function(global, env, providedBuffer) {
+ global = fixImports(global);
+ env = fixImports(env);
- // The asm.js function, called to "link" the asm.js module. At that time, we are provided imports
- // and respond with exports, and so forth.
- Module['asm'] = function(global, env, providedBuffer) {
- assert(providedBuffer === Module['buffer']); // we should not even need to pass it as a 3rd arg for wasm, but that's the asm.js way.
+ assert(providedBuffer === Module['buffer']); // we should not even need to pass it as a 3rd arg for wasm, but that's the asm.js way.
- info.global = global;
- info.env = env;
+ info.global = global;
+ info.env = env;
- Module['reallocBuffer'] = function(size) {
- var old = Module['buffer'];
- wasmJS['asmExports']['__growWasmMemory'](size); // tiny wasm method that just does grow_memory
- return Module['buffer'] !== old ? Module['buffer'] : null; // if it was reallocated, it changed
- };
+ wasmJS['providedTotalMemory'] = Module['buffer'].byteLength;
- // Prepare to generate wasm, using either asm2wasm or wasm-s-parser
- var code = Module['read'](method == 'asm2wasm' ? Module['asmjsCodeFile'] : Module['wasmCodeFile']);
- var temp = wasmJS['_malloc'](code.length + 1);
- wasmJS['writeAsciiToMemory'](code, temp);
- if (method == 'asm2wasm') {
- wasmJS['_load_asm2wasm'](temp);
- } else {
- wasmJS['_load_s_expr2wasm'](temp);
- }
- wasmJS['_free'](temp);
+ // Prepare to generate wasm, using either asm2wasm or s-exprs
+ var code;
+ if (method === 'interpret-binary') {
+ code = getBinary();
+ } else {
+ code = Module['read'](method == 'interpret-asm2wasm' ? asmjsCodeFile : wasmTextFile);
+ }
+ var temp;
+ if (method == 'interpret-asm2wasm') {
+ temp = wasmJS['_malloc'](code.length + 1);
+ wasmJS['writeAsciiToMemory'](code, temp);
+ wasmJS['_load_asm2wasm'](temp);
+ } else if (method === 'interpret-s-expr') {
+ temp = wasmJS['_malloc'](code.length + 1);
+ wasmJS['writeAsciiToMemory'](code, temp);
+ wasmJS['_load_s_expr2wasm'](temp);
+ } else if (method === 'interpret-binary') {
+ temp = wasmJS['_malloc'](code.length);
+ wasmJS['HEAPU8'].set(code, temp);
+ wasmJS['_load_binary2wasm'](temp, code.length);
+ } else {
+ throw 'what? ' + method;
+ }
+ wasmJS['_free'](temp);
- wasmJS['providedTotalMemory'] = Module['buffer'].byteLength;
+ wasmJS['_instantiate'](temp);
- wasmJS['_instantiate'](temp);
+ if (Module['newBuffer']) {
+ mergeMemory(Module['newBuffer']);
+ Module['newBuffer'] = null;
+ }
- if (Module['newBuffer']) {
- mergeMemory(Module['newBuffer']);
- Module['newBuffer'] = null;
- }
+ if (method == 'interpret-s-expr') {
+ applyMappedGlobals(wasmTextFile);
+ } else if (method == 'interpret-binary') {
+ applyMappedGlobals(wasmBinaryFile);
+ }
- if (method == 'wasm-s-parser') {
- applyMappedGlobals();
- }
+ exports = wasmJS['asmExports'];
- return wasmJS['asmExports'];
- };
+ return exports;
+ };
+
+ return true;
+ }
+
+ // use the right method
+
+ var methods = method.split(',');
+ for (var i = 0; i < methods.length; i++) {
+ var curr = methods[i];
+ //Module['printErr']('using wasm/js method: ' + curr);
+ if (curr === 'native-wasm') {
+ if (doNativeWasm()) return;
+ } else if (curr === 'asmjs') {
+ if (doJustAsm()) return;
+ } else if (curr === 'interpret-asm2wasm' || curr === 'interpret-s-expr' || curr === 'interpret-binary') {
+ if (doWasmPolyfill(curr)) return;
+ } else {
+ throw 'bad method: ' + curr;
+ }
+ }
+ throw 'no wasm method succeeded';
}
+