Skip to content

Commit e82b849

Browse files
authored
Merge pull request webpack#7467 from webpack/bugfix/reexport-wasm-global
handle reexported wasm globals
2 parents 11fa1d5 + 9a6d9c7 commit e82b849

11 files changed

+272
-80
lines changed

declarations.d.ts

Lines changed: 21 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,33 @@ 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 GlobalType extends Node {
78+
valtype: string;
79+
}
80+
export class Global extends Node {
81+
init: Instruction[];
82+
globalType: GlobalType;
83+
}
6984
export class FuncParam extends Node {
7085
valtype: string;
7186
}
72-
export class Instruction extends Node {}
87+
export class Instruction extends Node {
88+
id: string;
89+
args: NumberLiteral[];
90+
}
7391
export class CallInstruction extends Instruction {}
7492
export class ObjectInstruction extends Instruction {}
7593
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: 43 additions & 27 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

@@ -164,6 +166,27 @@ function getNextFuncIndex(ast, countImportedFunc) {
164166
return t.indexLiteral(vectorOfSize + countImportedFunc);
165167
}
166168

169+
/**
170+
* Create a init instruction for a global
171+
* @param {t.GlobalType} globalType the global type
172+
* @returns {t.Instruction} init expression
173+
*/
174+
const createDefaultInitForGlobal = globalType => {
175+
if (globalType.valtype[0] === "i") {
176+
// create NumberLiteral global initializer
177+
return t.objectInstruction("const", globalType.valtype, [
178+
t.numberLiteralFromRaw(66)
179+
]);
180+
} else if (globalType.valtype[0] === "f") {
181+
// create FloatLiteral global initializer
182+
return t.objectInstruction("const", globalType.valtype, [
183+
t.floatLiteral(66, false, false, "66")
184+
]);
185+
} else {
186+
throw new Error("unknown type: " + globalType.valtype);
187+
}
188+
};
189+
167190
/**
168191
* Rewrite the import globals:
169192
* - removes the ModuleImport instruction
@@ -188,21 +211,7 @@ const rewriteImportedGlobals = state => bin => {
188211

189212
globalType.mutability = "var";
190213

191-
let init;
192-
193-
if (globalType.valtype[0] === "i") {
194-
// create NumberLiteral global initializer
195-
init = t.objectInstruction("const", globalType.valtype, [
196-
t.numberLiteralFromRaw(0)
197-
]);
198-
} else if (globalType.valtype[0] === "f") {
199-
// create FloatLiteral global initializer
200-
init = t.objectInstruction("const", globalType.valtype, [
201-
t.floatLiteral(0, false, false, "0")
202-
]);
203-
} else {
204-
throw new Error("unknown type: " + globalType.valtype);
205-
}
214+
const init = createDefaultInitForGlobal(globalType);
206215

207216
newGlobals.push(t.global(globalType, [init]));
208217

@@ -221,12 +230,7 @@ const rewriteImportedGlobals = state => bin => {
221230

222231
const initialGlobalidx = init.args[0];
223232

224-
const valtype = node.globalType.valtype;
225-
226-
node.init = [
227-
// Poisong globals, they are meant to be rewritten
228-
t.objectInstruction("const", valtype, [t.numberLiteralFromRaw(666)])
229-
];
233+
node.init = [createDefaultInitForGlobal(node.globalType)];
230234

231235
additionalInitCode.push(
232236
/**
@@ -253,18 +257,24 @@ const rewriteImportedGlobals = state => bin => {
253257
* Rewrite the export names
254258
* @param {Object} state state
255259
* @param {Object} state.ast Module's ast
256-
* @param {Object} state.module Module
260+
* @param {Module} state.module Module
261+
* @param {Set<string>} state.externalExports Module
257262
* @returns {ArrayBufferTransform} transform
258263
*/
259-
const rewriteExportNames = ({ ast, module }) => bin => {
264+
const rewriteExportNames = ({ ast, module, externalExports }) => bin => {
260265
return editWithAST(ast, bin, {
261266
ModuleExport(path) {
267+
const isExternal = externalExports.has(path.node.name);
268+
if (isExternal) {
269+
path.remove();
270+
return;
271+
}
262272
const usedName = module.isUsed(path.node.name);
263-
if (usedName) {
264-
path.node.name = usedName;
265-
} else {
273+
if (!usedName) {
266274
path.remove();
275+
return;
267276
}
277+
path.node.name = usedName;
268278
}
269279
});
270280
};
@@ -403,14 +413,20 @@ class WebAssemblyGenerator extends Generator {
403413
const nextTypeIndex = getNextTypeIndex(ast);
404414

405415
const usedDependencyMap = getUsedDependencyMap(module);
416+
const externalExports = new Set(
417+
module.dependencies
418+
.filter(d => d instanceof WebAssemblyExportImportedDependency)
419+
.map(d => d.exportName)
420+
);
406421

407422
/** @type {t.Instruction[]} */
408423
const additionalInitCode = [];
409424

410425
const transform = compose(
411426
rewriteExportNames({
412427
ast,
413-
module
428+
module,
429+
externalExports
414430
}),
415431

416432
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)