Skip to content

Commit 6ac5fa7

Browse files
authored
wasm2js: Mangle import names for JS (#2267)
This fixes names that would be invalid in JS, like a.b. Turns out the Go compiler emits wasm with such imports. Also add some docs on how to use wasm2js. Fixes #2263
1 parent 772891f commit 6ac5fa7

7 files changed

Lines changed: 114 additions & 10 deletions

File tree

README.md

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -280,13 +280,33 @@ as a translation of
280280
)
281281
```
282282

283-
You can also tell wasm2js to optimize, using the normal optimization flags
284-
wasm-opt and other tools receive (such as `-Os`). For optimal code size,
285-
you should both optimize and run a JavaScript minifier afterwards.
283+
wasm2js's output is in ES6 module format - basically, it converts a wasm
284+
module into an ES6 module (to run on older browsers and Node.js versions
285+
you can use Babel etc. to convert it to ES5). Let's look at a full example
286+
of calling that hello world wast; first, create the main JS file:
286287

287-
Things to keep in mind with wasm2js's output:
288+
```javascript
289+
// main.mjs
290+
import { add } from "./hello_world.mjs";
291+
console.log('the sum of 1 and 2 is:', add(1, 2));
292+
```
293+
294+
The run this (note that you need a new enough Node.js with ES6 module
295+
support):
296+
297+
```shell
298+
$ bin/wasm2js test/hello_world.wast -o hello_world.mjs
299+
$ node --experimental-modules main.mjs
300+
the sum of 1 and 2 is: 3
301+
```
302+
303+
Things keep to in mind with wasm2js's output:
288304

289-
* It is not possible to match WebAssemblty semantics 100% precisely with fast
305+
* You should run wasm2js with optimizations for release builds, using `-O`
306+
or another optimization level. That will optimize along the entire pipeline
307+
(wasm and JS). It won't do everything a JS minifer would, though, like
308+
minify whitespace, so you should still run a normal JS minifer afterwards.
309+
* It is not possible to match WebAssembly semantics 100% precisely with fast
290310
JavaScript code. For example, every load and store may trap, and to make
291311
JavaScript do the same we'd need to add checks everywhere, which would be
292312
large and slow. Instead, wasm2js assumes loads and stores do not trap, that

scripts/test/mod.ule.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
export function ba_se() {
3+
console.log('"mod.ule"."ba.se"');
4+
}

scripts/test/node-esm-loader.mjs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@ export function resolve(specifier, parentModuleURL = baseURL, defaultResolve) {
1616
format: 'builtin'
1717
};
1818
}
19-
// Resolve the 'spectest' and 'env' modules to our custom implementations of
20-
// various builtins.
21-
if (specifier == 'spectest' || specifier == 'env') {
19+
// Resolve special modules used in our test suite.
20+
if (specifier == 'spectest' || specifier == 'env' || specifier == 'mod.ule') {
2221
const resolved = new URL('./scripts/test/' + specifier + '.js', parentModuleURL);
2322
return {
2423
url: resolved.href,

src/wasm2js.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2087,7 +2087,8 @@ void Wasm2JSGlue::emitPreES6() {
20872087
}
20882088
baseModuleMap[base] = module;
20892089

2090-
out << "import { " << base.str << " } from '" << module.str << "';\n";
2090+
out << "import { " << asmangle(base.str) << " } from '" << module.str
2091+
<< "';\n";
20912092
};
20922093

20932094
ImportInfo imports(wasm);
@@ -2185,7 +2186,7 @@ void Wasm2JSGlue::emitPostES6() {
21852186
if (ABI::wasm2js::isScratchMemoryHelper(import->base)) {
21862187
return;
21872188
}
2188-
out << "," << import->base.str;
2189+
out << "," << asmangle(import->base.str);
21892190
});
21902191
out << "},mem" << moduleName.str << ");\n";
21912192

test/wasm2js/dot_import.2asm.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { ba_se } from 'mod.ule';
2+
3+
function asmFunc(global, env, buffer) {
4+
var HEAP8 = new global.Int8Array(buffer);
5+
var HEAP16 = new global.Int16Array(buffer);
6+
var HEAP32 = new global.Int32Array(buffer);
7+
var HEAPU8 = new global.Uint8Array(buffer);
8+
var HEAPU16 = new global.Uint16Array(buffer);
9+
var HEAPU32 = new global.Uint32Array(buffer);
10+
var HEAPF32 = new global.Float32Array(buffer);
11+
var HEAPF64 = new global.Float64Array(buffer);
12+
var Math_imul = global.Math.imul;
13+
var Math_fround = global.Math.fround;
14+
var Math_abs = global.Math.abs;
15+
var Math_clz32 = global.Math.clz32;
16+
var Math_min = global.Math.min;
17+
var Math_max = global.Math.max;
18+
var Math_floor = global.Math.floor;
19+
var Math_ceil = global.Math.ceil;
20+
var Math_sqrt = global.Math.sqrt;
21+
var abort = env.abort;
22+
var nan = global.NaN;
23+
var infinity = global.Infinity;
24+
var base = env.ba_se;
25+
function $0() {
26+
base();
27+
}
28+
29+
var FUNCTION_TABLE = [];
30+
return {
31+
"exported": $0
32+
};
33+
}
34+
35+
var memasmFunc = new ArrayBuffer(65536);
36+
var retasmFunc = asmFunc({Math,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,NaN,Infinity}, {abort:function() { throw new Error('abort'); },ba_se},memasmFunc);
37+
export var exported = retasmFunc.exported;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { ba_se } from 'mod.ule';
2+
3+
function asmFunc(global, env, buffer) {
4+
var HEAP8 = new global.Int8Array(buffer);
5+
var HEAP16 = new global.Int16Array(buffer);
6+
var HEAP32 = new global.Int32Array(buffer);
7+
var HEAPU8 = new global.Uint8Array(buffer);
8+
var HEAPU16 = new global.Uint16Array(buffer);
9+
var HEAPU32 = new global.Uint32Array(buffer);
10+
var HEAPF32 = new global.Float32Array(buffer);
11+
var HEAPF64 = new global.Float64Array(buffer);
12+
var Math_imul = global.Math.imul;
13+
var Math_fround = global.Math.fround;
14+
var Math_abs = global.Math.abs;
15+
var Math_clz32 = global.Math.clz32;
16+
var Math_min = global.Math.min;
17+
var Math_max = global.Math.max;
18+
var Math_floor = global.Math.floor;
19+
var Math_ceil = global.Math.ceil;
20+
var Math_sqrt = global.Math.sqrt;
21+
var abort = env.abort;
22+
var nan = global.NaN;
23+
var infinity = global.Infinity;
24+
var base = env.ba_se;
25+
function $0() {
26+
base();
27+
}
28+
29+
var FUNCTION_TABLE = [];
30+
return {
31+
"exported": $0
32+
};
33+
}
34+
35+
var memasmFunc = new ArrayBuffer(65536);
36+
var retasmFunc = asmFunc({Math,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,NaN,Infinity}, {abort:function() { throw new Error('abort'); },ba_se},memasmFunc);
37+
export var exported = retasmFunc.exported;

test/wasm2js/dot_import.wast

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(module
2+
(import "mod.ule" "ba.se" (func $base))
3+
(func "exported"
4+
(call $base)
5+
)
6+
)

0 commit comments

Comments
 (0)