diff options
Diffstat (limited to 'src/js')
-rw-r--r-- | src/js/post.js | 177 |
1 files changed, 147 insertions, 30 deletions
diff --git a/src/js/post.js b/src/js/post.js index 6406674ba..7f7ca9c1e 100644 --- a/src/js/post.js +++ b/src/js/post.js @@ -1,38 +1,121 @@ -(function() { - var wasmJS = WasmJS({}); // do not use the normal Module in the current scope +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) + // 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'); - // XXX don't be confused. Module here is in the outside program. wasmJS is the inner wasm-js.cpp. + if (method == 'just-asm') { + eval(Module['read'](Module['asmjsCodeFile'])); + return; + } + + var asm2wasmImports = { // special asm2wasm imports + "f64-rem": function(x, y) { + return x % y; + }, + "f64-to-int": function(x) { + return x | 0; + }, + "debugger": function() { + debugger; + }, + }; + + 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); + ret[y] = obj[x][y]; + } + } + return ret; + } + + // 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')); + 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 address = global.address; + switch (global.type) { + case WasmTypes.i32: Module['HEAP32'][address >> 2] = value; break; + case WasmTypes.f32: Module['HEAPF32'][address >> 2] = value; break; + case WasmTypes.f64: Module['HEAPF64'][address >> 3] = value; break; + default: abort(); + } + } + } + + if (typeof WASM === 'object') { + // 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 instance = WASM.instantiateModule(binary, flatten({ // XXX for now, flatten the imports + "global.Math": global.Math, + "env": env, + "asm2wasm": asm2wasmImports + })); - // Generate a module instance of the asm.js converted into wasm. - var code; - if (typeof read === 'function') { - // spidermonkey or v8 shells - code = read(Module['asmjsCodeFile']); - } else if (typeof process === 'object' && typeof require === 'function') { - // node.js - code = require('fs')['readFileSync'](Module['asmjsCodeFile']).toString(); - } else { - throw 'TODO: loading in other platforms'; + // The wasm instance creates its memory. But static init code might have written to + // buffer already, and we must copy it over. + // 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']; + var newBuffer = instance.memory; + 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)); + updateGlobalBuffer(newBuffer); + updateGlobalBufferViews(); + 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 + }; + + applyMappedGlobals(); + + return instance; + }; + + return; } - var temp = wasmJS._malloc(code.length + 1); - wasmJS.writeAsciiToMemory(code, temp); - wasmJS._load_asm(temp); - wasmJS._free(temp); + var WasmTypes = { + none: 0, + i32: 1, + i64: 2, + f32: 3, + f64: 4 + }; + + // Use wasm.js to polyfill and execute code in a wasm interpreter. + var wasmJS = WasmJS({}); - // Generate memory XXX TODO get the right size - var theBuffer = Module['buffer'] = new ArrayBuffer(Module['providedTotalMemory'] || 64*1024*1024); + // 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: { // special asm2wasm imports - "f64-rem": function(x, y) { - return x % y; - }, - }, + asm2wasm: asm2wasmImports, parent: Module // Module inside wasm-js.cpp refers to wasm-js.cpp; this allows access to the outside program. }; @@ -52,14 +135,48 @@ return lookup; } - // The asm.js function, called to "link" the asm.js module. - Module['asm'] = function(global, env, buffer) { - assert(buffer === theBuffer); // we should not even need to pass it as a 3rd arg for wasm, but that's the asm.js way. - // write the provided data to a location the wasm instance can get at it. + // 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. + info.global = global; info.env = env; - wasmJS['_load_mapped_globals'](); // now that we have global and env, we can ready the provided imported globals, copying them to their mapped locations. + + // wasm code would create its own buffer, at this time. But static init code might have + // written to the buffer already, and we must copy it over. We could just avoid + // this copy in wasm.js polyfilling, but to be as close as possible to real wasm, + // we do what wasm would do. + // 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']; + var newBuffer = new ArrayBuffer(oldBuffer.byteLength); + (new Int8Array(newBuffer)).set(new Int8Array(oldBuffer)); + updateGlobalBuffer(newBuffer); + updateGlobalBufferViews(); + wasmJS['providedTotalMemory'] = Module['buffer'].byteLength; + + 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 + }; + + // 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); + applyMappedGlobals(); + } + wasmJS['_free'](temp); + + wasmJS['_instantiate'](temp); + return wasmJS['asmExports']; }; -})(); +} |