From db0707096b03d88e2f00c9dd53c6e9c39f6147ca Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Sat, 27 Jul 2019 09:51:20 -0700 Subject: [PATCH 1/5] wasm2js: Mangle import names properly, so illegal basenames are legalized. fixes #2263. Adds a complete example of usage in the readme as well --- README.md | 30 +++++++++++++++++++---- src/wasm2js.h | 2 +- test/wasm2js/dot_import.2asm.js | 37 +++++++++++++++++++++++++++++ test/wasm2js/dot_import.2asm.js.opt | 37 +++++++++++++++++++++++++++++ test/wasm2js/dot_import.wast | 6 +++++ 5 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 test/wasm2js/dot_import.2asm.js create mode 100644 test/wasm2js/dot_import.2asm.js.opt create mode 100644 test/wasm2js/dot_import.wast diff --git a/README.md b/README.md index fb0226aee60..3e77e611de8 100644 --- a/README.md +++ b/README.md @@ -280,13 +280,33 @@ as a translation of ) ``` -You can also tell wasm2js to optimize, using the normal optimization flags -wasm-opt and other tools receive (such as `-Os`). For optimal code size, -you should both optimize and run a JavaScript minifier afterwards. +wasm2js's output is in ES6 module format - basically, it converts a wasm +module into an ES6 module (to run on older browsers and Node.js versions +you can use Babel etc. to convert it to ES5). Let's look at a full example +of calling that hello world wast; first, create the main JS file: -Things to keep in mind with wasm2js's output: +```javascript +// main.mjs +import { add } from "./hello_world.mjs"; +console.log('the sum of 1 and 2 is:', add(1, 2)); +``` + +The run this (note that you need a new enough Node.js with ES6 module +support): + +```shell +$ bin/wasm2js test/hello_world.wast -o hello_world.mjs +$ node --experimental-modules main.mjs +the sum of 1 and 2 is: 3 +``` + +Things keep to in mind with wasm2js's output: - * It is not possible to match WebAssemblty semantics 100% precisely with fast + * You should run wasm2js with optimizations for release builds, using `-O` + or another optimization level. That will optimize along the entire pipeline + (wasm and JS). It won't do everything a JS minifer would, though, like + minify whitespace, so you should still run a normal JS minifer afterwards. + * It is not possible to match WebAssembly semantics 100% precisely with fast JavaScript code. For example, every load and store may trap, and to make JavaScript do the same we'd need to add checks everywhere, which would be large and slow. Instead, wasm2js assumes loads and stores do not trap, that diff --git a/src/wasm2js.h b/src/wasm2js.h index b1548c0cae1..6f5fa2ee230 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2087,7 +2087,7 @@ void Wasm2JSGlue::emitPreES6() { } baseModuleMap[base] = module; - out << "import { " << base.str << " } from '" << module.str << "';\n"; + out << "import { " << asmangle(base.str) << " } from '" << module.str << "';\n"; }; ImportInfo imports(wasm); diff --git a/test/wasm2js/dot_import.2asm.js b/test/wasm2js/dot_import.2asm.js new file mode 100644 index 00000000000..22375c1e93c --- /dev/null +++ b/test/wasm2js/dot_import.2asm.js @@ -0,0 +1,37 @@ +import { ba_se } from 'mod.ule'; + +function asmFunc(global, env, buffer) { + var HEAP8 = new global.Int8Array(buffer); + var HEAP16 = new global.Int16Array(buffer); + var HEAP32 = new global.Int32Array(buffer); + var HEAPU8 = new global.Uint8Array(buffer); + var HEAPU16 = new global.Uint16Array(buffer); + var HEAPU32 = new global.Uint32Array(buffer); + var HEAPF32 = new global.Float32Array(buffer); + var HEAPF64 = new global.Float64Array(buffer); + var Math_imul = global.Math.imul; + var Math_fround = global.Math.fround; + var Math_abs = global.Math.abs; + var Math_clz32 = global.Math.clz32; + var Math_min = global.Math.min; + var Math_max = global.Math.max; + var Math_floor = global.Math.floor; + var Math_ceil = global.Math.ceil; + var Math_sqrt = global.Math.sqrt; + var abort = env.abort; + var nan = global.NaN; + var infinity = global.Infinity; + var base = env.ba_se; + function $0() { + base(); + } + + var FUNCTION_TABLE = []; + return { + "exported": $0 + }; +} + +var memasmFunc = new ArrayBuffer(65536); +var retasmFunc = asmFunc({Math,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,NaN,Infinity}, {abort:function() { throw new Error('abort'); },ba.se},memasmFunc); +export var exported = retasmFunc.exported; diff --git a/test/wasm2js/dot_import.2asm.js.opt b/test/wasm2js/dot_import.2asm.js.opt new file mode 100644 index 00000000000..22375c1e93c --- /dev/null +++ b/test/wasm2js/dot_import.2asm.js.opt @@ -0,0 +1,37 @@ +import { ba_se } from 'mod.ule'; + +function asmFunc(global, env, buffer) { + var HEAP8 = new global.Int8Array(buffer); + var HEAP16 = new global.Int16Array(buffer); + var HEAP32 = new global.Int32Array(buffer); + var HEAPU8 = new global.Uint8Array(buffer); + var HEAPU16 = new global.Uint16Array(buffer); + var HEAPU32 = new global.Uint32Array(buffer); + var HEAPF32 = new global.Float32Array(buffer); + var HEAPF64 = new global.Float64Array(buffer); + var Math_imul = global.Math.imul; + var Math_fround = global.Math.fround; + var Math_abs = global.Math.abs; + var Math_clz32 = global.Math.clz32; + var Math_min = global.Math.min; + var Math_max = global.Math.max; + var Math_floor = global.Math.floor; + var Math_ceil = global.Math.ceil; + var Math_sqrt = global.Math.sqrt; + var abort = env.abort; + var nan = global.NaN; + var infinity = global.Infinity; + var base = env.ba_se; + function $0() { + base(); + } + + var FUNCTION_TABLE = []; + return { + "exported": $0 + }; +} + +var memasmFunc = new ArrayBuffer(65536); +var retasmFunc = asmFunc({Math,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,NaN,Infinity}, {abort:function() { throw new Error('abort'); },ba.se},memasmFunc); +export var exported = retasmFunc.exported; diff --git a/test/wasm2js/dot_import.wast b/test/wasm2js/dot_import.wast new file mode 100644 index 00000000000..46c843fdedb --- /dev/null +++ b/test/wasm2js/dot_import.wast @@ -0,0 +1,6 @@ +(module + (import "mod.ule" "ba.se" (func $base)) + (func "exported" + (call $base) + ) +) From 6aea0d2a3c5fd0df5ddbc1f0b83a636a57d4d110 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Sat, 27 Jul 2019 09:52:01 -0700 Subject: [PATCH 2/5] style --- src/wasm2js.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wasm2js.h b/src/wasm2js.h index 6f5fa2ee230..4a93fcc66d8 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2087,7 +2087,8 @@ void Wasm2JSGlue::emitPreES6() { } baseModuleMap[base] = module; - out << "import { " << asmangle(base.str) << " } from '" << module.str << "';\n"; + out << "import { " << asmangle(base.str) << " } from '" << module.str + << "';\n"; }; ImportInfo imports(wasm); From 2549c98565d2d78ed728017c64b189d8c1dcbcbb Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Sat, 27 Jul 2019 10:27:15 -0700 Subject: [PATCH 3/5] more --- src/wasm2js.h | 2 +- test/wasm2js/dot_import.2asm.js | 2 +- test/wasm2js/dot_import.2asm.js.opt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wasm2js.h b/src/wasm2js.h index 4a93fcc66d8..e032ac26dab 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2186,7 +2186,7 @@ void Wasm2JSGlue::emitPostES6() { if (ABI::wasm2js::isScratchMemoryHelper(import->base)) { return; } - out << "," << import->base.str; + out << "," << asmangle(import->base.str); }); out << "},mem" << moduleName.str << ");\n"; diff --git a/test/wasm2js/dot_import.2asm.js b/test/wasm2js/dot_import.2asm.js index 22375c1e93c..87b9971ea27 100644 --- a/test/wasm2js/dot_import.2asm.js +++ b/test/wasm2js/dot_import.2asm.js @@ -33,5 +33,5 @@ function asmFunc(global, env, buffer) { } var memasmFunc = new ArrayBuffer(65536); -var retasmFunc = asmFunc({Math,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,NaN,Infinity}, {abort:function() { throw new Error('abort'); },ba.se},memasmFunc); +var retasmFunc = asmFunc({Math,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,NaN,Infinity}, {abort:function() { throw new Error('abort'); },ba_se},memasmFunc); export var exported = retasmFunc.exported; diff --git a/test/wasm2js/dot_import.2asm.js.opt b/test/wasm2js/dot_import.2asm.js.opt index 22375c1e93c..87b9971ea27 100644 --- a/test/wasm2js/dot_import.2asm.js.opt +++ b/test/wasm2js/dot_import.2asm.js.opt @@ -33,5 +33,5 @@ function asmFunc(global, env, buffer) { } var memasmFunc = new ArrayBuffer(65536); -var retasmFunc = asmFunc({Math,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,NaN,Infinity}, {abort:function() { throw new Error('abort'); },ba.se},memasmFunc); +var retasmFunc = asmFunc({Math,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,NaN,Infinity}, {abort:function() { throw new Error('abort'); },ba_se},memasmFunc); export var exported = retasmFunc.exported; From aa00054d3f9f7d1b478a70bd2fe513bc0a3de9d0 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Sat, 27 Jul 2019 10:56:14 -0700 Subject: [PATCH 4/5] mod.ule --- scripts/test/mod.ule.js | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 scripts/test/mod.ule.js diff --git a/scripts/test/mod.ule.js b/scripts/test/mod.ule.js new file mode 100644 index 00000000000..785cce78af6 --- /dev/null +++ b/scripts/test/mod.ule.js @@ -0,0 +1,4 @@ + +export function ba_se() { + console.log('"mod.ule"."ba.se"'); +} From 0805b8f3733b3f8b1088020b3010d1bbb29132f4 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Sat, 27 Jul 2019 12:14:08 -0700 Subject: [PATCH 5/5] fix --- scripts/test/node-esm-loader.mjs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/test/node-esm-loader.mjs b/scripts/test/node-esm-loader.mjs index 8cd72282299..5d41033fb80 100644 --- a/scripts/test/node-esm-loader.mjs +++ b/scripts/test/node-esm-loader.mjs @@ -16,9 +16,8 @@ export function resolve(specifier, parentModuleURL = baseURL, defaultResolve) { format: 'builtin' }; } - // Resolve the 'spectest' and 'env' modules to our custom implementations of - // various builtins. - if (specifier == 'spectest' || specifier == 'env') { + // Resolve special modules used in our test suite. + if (specifier == 'spectest' || specifier == 'env' || specifier == 'mod.ule') { const resolved = new URL('./scripts/test/' + specifier + '.js', parentModuleURL); return { url: resolved.href,