Skip to content

Commit 1e4d2b7

Browse files
committed
handle reexported wasm globals in JS wrapper code
1 parent 9d1a3f7 commit 1e4d2b7

9 files changed

+218
-61
lines changed

declarations.d.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ declare module "@webassemblyjs/ast" {
3636
ModuleImport?: (p: NodePath<ModuleImport>) => void;
3737
ModuleExport?: (p: NodePath<ModuleExport>) => void;
3838
Start?: (p: NodePath<Start>) => void;
39+
Global?: (p: NodePath<Global>) => void;
3940
}
4041
);
4142
export class NodePath<T> {
@@ -60,16 +61,29 @@ declare module "@webassemblyjs/ast" {
6061
}
6162
export class ModuleExport extends Node {
6263
name: string;
64+
descr: {
65+
type: string;
66+
exportType: string;
67+
id?: Identifier;
68+
};
6369
}
6470
export class ModuleExportDescr extends Node {}
6571
export class IndexLiteral extends Node {}
66-
export class NumberLiteral extends Node {}
72+
export class NumberLiteral extends Node {
73+
value: number;
74+
raw: string;
75+
}
6776
export class FloatLiteral extends Node {}
68-
export class Global extends Node {}
77+
export class Global extends Node {
78+
init: Instruction[];
79+
}
6980
export class FuncParam extends Node {
7081
valtype: string;
7182
}
72-
export class Instruction extends Node {}
83+
export class Instruction extends Node {
84+
id: string;
85+
args: NumberLiteral[];
86+
}
7387
export class CallInstruction extends Instruction {}
7488
export class ObjectInstruction extends Instruction {}
7589
export class Func extends Node {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Tobias Koppers @sokra
4+
*/
5+
"use strict";
6+
7+
const DependencyReference = require("./DependencyReference");
8+
const ModuleDependency = require("./ModuleDependency");
9+
10+
class WebAssemblyExportImportedDependency extends ModuleDependency {
11+
constructor(exportName, request, name) {
12+
super(request);
13+
/** @type {string} */
14+
this.exportName = exportName;
15+
/** @type {string} */
16+
this.name = name;
17+
}
18+
19+
getReference() {
20+
if (!this.module) return null;
21+
return new DependencyReference(this.module, [this.name], false);
22+
}
23+
24+
get type() {
25+
return "wasm export import";
26+
}
27+
}
28+
29+
module.exports = WebAssemblyExportImportedDependency;

lib/wasm/WebAssemblyGenerator.js

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ const { editWithAST, addWithAST } = require("@webassemblyjs/wasm-edit");
1414
const { decode } = require("@webassemblyjs/wasm-parser");
1515
const t = require("@webassemblyjs/ast");
1616

17+
const WebAssemblyExportImportedDependency = require("../dependencies/WebAssemblyExportImportedDependency");
18+
1719
/** @typedef {import("../Module")} Module */
1820
/** @typedef {import("./WebAssemblyUtils").UsedWasmDependency} UsedWasmDependency */
1921

@@ -225,7 +227,7 @@ const rewriteImportedGlobals = state => bin => {
225227

226228
node.init = [
227229
// Poisong globals, they are meant to be rewritten
228-
t.objectInstruction("const", valtype, [t.numberLiteralFromRaw(666)])
230+
t.objectInstruction("const", valtype, [t.numberLiteralFromRaw(0)])
229231
];
230232

231233
additionalInitCode.push(
@@ -253,18 +255,24 @@ const rewriteImportedGlobals = state => bin => {
253255
* Rewrite the export names
254256
* @param {Object} state state
255257
* @param {Object} state.ast Module's ast
256-
* @param {Object} state.module Module
258+
* @param {Module} state.module Module
259+
* @param {Set<string>} state.invalidExports Module
257260
* @returns {ArrayBufferTransform} transform
258261
*/
259-
const rewriteExportNames = ({ ast, module }) => bin => {
262+
const rewriteExportNames = ({ ast, module, invalidExports }) => bin => {
260263
return editWithAST(ast, bin, {
261264
ModuleExport(path) {
265+
const isInvalid = invalidExports.has(path.node.name);
266+
if (isInvalid) {
267+
path.remove();
268+
return;
269+
}
262270
const usedName = module.isUsed(path.node.name);
263-
if (usedName) {
264-
path.node.name = usedName;
265-
} else {
271+
if (!usedName) {
266272
path.remove();
273+
return;
267274
}
275+
path.node.name = usedName;
268276
}
269277
});
270278
};
@@ -403,14 +411,20 @@ class WebAssemblyGenerator extends Generator {
403411
const nextTypeIndex = getNextTypeIndex(ast);
404412

405413
const usedDependencyMap = getUsedDependencyMap(module);
414+
const invalidExports = new Set(
415+
module.dependencies
416+
.filter(d => d instanceof WebAssemblyExportImportedDependency)
417+
.map(d => d.exportName)
418+
);
406419

407420
/** @type {t.Instruction[]} */
408421
const additionalInitCode = [];
409422

410423
const transform = compose(
411424
rewriteExportNames({
412425
ast,
413-
module
426+
module,
427+
invalidExports
414428
}),
415429

416430
removeStartFunc({ ast }),

lib/wasm/WebAssemblyJavascriptGenerator.js

Lines changed: 92 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -8,59 +8,96 @@ const Generator = require("../Generator");
88
const Template = require("../Template");
99
const { RawSource } = require("webpack-sources");
1010
const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
11-
12-
function generateInitParams(module) {
13-
const list = [];
14-
15-
for (const dep of module.dependencies) {
16-
if (dep instanceof WebAssemblyImportDependency) {
17-
if (dep.description.type === "GlobalType") {
18-
const exportName = dep.name;
19-
const usedName = dep.module && dep.module.isUsed(exportName);
20-
21-
if (dep.module === null) {
22-
// Dependency was not found, an error will be thrown later
23-
continue;
24-
}
25-
26-
if (usedName !== false) {
27-
list.push(
28-
`__webpack_require__(${JSON.stringify(
29-
dep.module.id
30-
)})[${JSON.stringify(usedName)}]`
31-
);
32-
}
33-
}
34-
}
35-
}
36-
37-
return list;
38-
}
11+
const WebAssemblyExportImportedDependency = require("../dependencies/WebAssemblyExportImportedDependency");
3912

4013
class WebAssemblyJavascriptGenerator extends Generator {
4114
generate(module, dependencyTemplates, runtimeTemplate) {
4215
const initIdentifer = Array.isArray(module.usedExports)
4316
? Template.numberToIdentifer(module.usedExports.length)
4417
: "__webpack_init__";
4518

46-
const generateImports = () => {
47-
const modules = new Map();
48-
for (const dep of module.dependencies) {
49-
if (dep.module) modules.set(dep.module, dep.userRequest);
50-
}
51-
return Template.asString(
52-
Array.from(modules, ([m, r]) => {
53-
return `${runtimeTemplate.moduleRaw({
54-
module: m,
55-
request: r
56-
})};`;
57-
})
58-
);
59-
};
19+
let needExportsCopy = false;
20+
const importedModules = new Map();
21+
const initParams = [];
22+
let index = 0;
23+
for (const dep of module.dependencies) {
24+
if (dep.module) {
25+
let importData = importedModules.get(dep.module);
26+
if (importData === undefined) {
27+
importedModules.set(
28+
dep.module,
29+
(importData = {
30+
importVar: `m${index}`,
31+
index,
32+
request: dep.userRequest,
33+
names: new Set(),
34+
reexports: []
35+
})
36+
);
37+
index++;
38+
}
39+
if (dep instanceof WebAssemblyImportDependency) {
40+
importData.names.add(dep.name);
41+
if (dep.description.type === "GlobalType") {
42+
const exportName = dep.name;
43+
const usedName = dep.module && dep.module.isUsed(exportName);
6044

61-
// FIXME(sven): assert that the exports exists in the modules
62-
// otherwise it will default to i32 0
63-
const initParams = generateInitParams(module).join(",");
45+
if (dep.module) {
46+
if (usedName) {
47+
initParams.push(
48+
runtimeTemplate.exportFromImport({
49+
module: dep.module,
50+
request: dep.request,
51+
importVar: importData.importVar,
52+
originModule: module,
53+
exportName: dep.name,
54+
asiSafe: true,
55+
isCall: false,
56+
callContext: null
57+
})
58+
);
59+
}
60+
}
61+
}
62+
}
63+
if (dep instanceof WebAssemblyExportImportedDependency) {
64+
importData.names.add(dep.name);
65+
const usedName = module.isUsed(dep.exportName);
66+
if (usedName) {
67+
const defineStatement = Template.asString([
68+
`${module.exportsArgument}[${JSON.stringify(
69+
usedName
70+
)}] = ${runtimeTemplate.exportFromImport({
71+
module: dep.module,
72+
request: dep.request,
73+
importVar: importData.importVar,
74+
originModule: module,
75+
exportName: dep.name,
76+
asiSafe: true,
77+
isCall: false,
78+
callContext: null
79+
})};`
80+
]);
81+
importData.reexports.push(defineStatement);
82+
needExportsCopy = true;
83+
}
84+
}
85+
}
86+
}
87+
const importsCode = Template.asString(
88+
Array.from(
89+
importedModules,
90+
([module, { importVar, request, reexports }]) => {
91+
const importStatement = runtimeTemplate.importStatement({
92+
module,
93+
request,
94+
importVar,
95+
originModule: module
96+
});
97+
return importStatement + reexports.join("\n");
98+
}
99+
)
100+
);
64101

65102
// create source
66103
const source = new RawSource(
@@ -69,18 +106,24 @@ class WebAssemblyJavascriptGenerator extends Generator {
69106
"// Instantiate WebAssembly module",
70107
"var wasmExports = __webpack_require__.w[module.i];",
71108

109+
!Array.isArray(module.usedExports)
110+
? `__webpack_require__.r(${module.exportsArgument});`
111+
: "",
112+
72113
// this must be before import for circular dependencies
73114
"// export exports from WebAssembly module",
74-
Array.isArray(module.usedExports)
115+
Array.isArray(module.usedExports) && !needExportsCopy
75116
? `${module.moduleArgument}.exports = wasmExports;`
76117
: "for(var name in wasmExports) " +
77118
`if(name != ${JSON.stringify(initIdentifer)}) ` +
78119
`${module.exportsArgument}[name] = wasmExports[name];`,
79120
"// exec imports from WebAssembly module (for esm order)",
80-
generateImports(),
81-
121+
importsCode,
122+
"",
82123
"// exec wasm module",
83-
`wasmExports[${JSON.stringify(initIdentifer)}](${initParams})`
124+
`wasmExports[${JSON.stringify(initIdentifer)}](${initParams.join(
125+
", "
126+
)})`
84127
].join("\n")
85128
);
86129
return source;

lib/wasm/WebAssemblyModulesPlugin.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const WebAssemblyParser = require("./WebAssemblyParser");
99
const WebAssemblyGenerator = require("./WebAssemblyGenerator");
1010
const WebAssemblyJavascriptGenerator = require("./WebAssemblyJavascriptGenerator");
1111
const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
12+
const WebAssemblyExportImportedDependency = require("../dependencies/WebAssemblyExportImportedDependency");
1213

1314
class WebAssemblyModulesPlugin {
1415
apply(compiler) {
@@ -20,6 +21,11 @@ class WebAssemblyModulesPlugin {
2021
normalModuleFactory
2122
);
2223

24+
compilation.dependencyFactories.set(
25+
WebAssemblyExportImportedDependency,
26+
normalModuleFactory
27+
);
28+
2329
normalModuleFactory.hooks.createParser
2430
.for("webassembly/experimental")
2531
.tap("WebAssemblyModulesPlugin", () => {

0 commit comments

Comments
 (0)