diff options
Diffstat (limited to 'src/js')
-rw-r--r-- | src/js/wasm.js-post.js | 318 |
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'; } + |