Skip to content

Commit f2910fc

Browse files
authored
Merge pull request webpack#7173 from webpack/wasm/decoding
add test cases for decoding wasm
2 parents 9bc9c75 + e90b766 commit f2910fc

14 files changed

+223
-97
lines changed

lib/wasm/WasmMainTemplatePlugin.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ function generateImportObject(module) {
3939
const exportName = dep.name;
4040
const usedName = dep.module && dep.module.isUsed(exportName);
4141

42+
if (dep.module === null) {
43+
// Dependency was not found, an error will be thrown later
44+
continue;
45+
}
46+
4247
if (usedName !== false) {
4348
array.push({
4449
exportName,
@@ -145,7 +150,7 @@ class WasmMainTemplatePlugin {
145150
"var installedWasmModules = {};",
146151
"",
147152
"var wasmImportObjects = {",
148-
Template.indent([importObjects]),
153+
Template.indent(importObjects),
149154
"};"
150155
]);
151156
}

lib/wasm/WebAssemblyGenerator.js

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ function compose(...fns) {
1919

2020
// Utility functions
2121
const isGlobalImport = moduleImport => moduleImport.descr.type === "GlobalType";
22+
const isFuncImport = moduleImport =>
23+
moduleImport.descr.type === "FuncImportDescr";
2224
const initFuncId = t.identifier("__webpack_init__");
2325

2426
// TODO replace with @callback
@@ -50,8 +52,8 @@ function getStartFuncIndex(ast) {
5052
let startAtFuncIndex;
5153

5254
t.traverse(ast, {
53-
Start(path) {
54-
startAtFuncIndex = path.node.index;
55+
Start({ node }) {
56+
startAtFuncIndex = node.index;
5557
}
5658
});
5759

@@ -78,23 +80,57 @@ function getImportedGlobals(ast) {
7880
return importedGlobals;
7981
}
8082

83+
function getCountImportedFunc(ast) {
84+
let count = 0;
85+
86+
t.traverse(ast, {
87+
ModuleImport({ node }) {
88+
if (isFuncImport(node) === true) {
89+
count++;
90+
}
91+
}
92+
});
93+
94+
return count;
95+
}
96+
8197
/**
82-
* Get next func index
83-
*
84-
* Funcs are referenced by their index in the type section, we just return the
85-
* next index.
98+
* Get next type index
8699
*
87100
* @param {Object} ast - Module's AST
88101
* @returns {t.IndexLiteral} - index
89102
*/
90-
function getNextFuncIndex(ast) {
103+
function getNextTypeIndex(ast) {
91104
const typeSectionMetadata = t.getSectionMetadata(ast, "type");
92105

93106
if (typeof typeSectionMetadata === "undefined") {
94107
return t.indexLiteral(0);
95108
}
96109

97-
return t.indexLiteral(typeSectionMetadata.vectorOfSize);
110+
return t.indexLiteral(typeSectionMetadata.vectorOfSize.value);
111+
}
112+
113+
/**
114+
* Get next func index
115+
*
116+
* The Func section metadata provide informations for implemented funcs
117+
* in order to have the correct index we shift the index by number of external
118+
* functions.
119+
*
120+
* @param {Object} ast - Module's AST
121+
* @param {Number} countImportedFunc - number of imported funcs
122+
* @returns {t.IndexLiteral} - index
123+
*/
124+
function getNextFuncIndex(ast, countImportedFunc) {
125+
const funcSectionMetadata = t.getSectionMetadata(ast, "func");
126+
127+
if (typeof funcSectionMetadata === "undefined") {
128+
return t.indexLiteral(0 + countImportedFunc);
129+
}
130+
131+
const vectorOfSize = funcSectionMetadata.vectorOfSize.value;
132+
133+
return t.indexLiteral(vectorOfSize + countImportedFunc);
98134
}
99135

100136
/**
@@ -145,13 +181,15 @@ const rewriteImportedGlobals = state => bin => {
145181
* @param {t.ModuleImport[]} state.importedGlobals list of imported globals
146182
* @param {*} state.funcSectionMetadata ??
147183
* @param {t.IndexLiteral} state.nextFuncIndex index of the next function
184+
* @param {t.IndexLiteral} state.nextTypeIndex index of the next type
148185
* @returns {ArrayBufferTransform} transform
149186
*/
150187
const addInitFunction = ({
151188
startAtFuncIndex,
152189
importedGlobals,
153190
funcSectionMetadata,
154-
nextFuncIndex
191+
nextFuncIndex,
192+
nextTypeIndex
155193
}) => bin => {
156194
const funcParams = importedGlobals.map(importedGlobal => {
157195
// used for debugging
@@ -176,14 +214,19 @@ const addInitFunction = ({
176214

177215
const funcResults = [];
178216

217+
// Code section
179218
const func = t.func(initFuncId, funcParams, funcResults, funcBody);
180219

220+
// Type section
181221
const functype = t.typeInstructionFunc(
182222
func.signature.params,
183223
func.signature.result
184224
);
185-
const funcindex = t.indexInFuncSection(nextFuncIndex);
186225

226+
// Func section
227+
const funcindex = t.indexInFuncSection(nextTypeIndex);
228+
229+
// Export section
187230
const moduleExport = t.moduleExport(initFuncId.value, "Func", nextFuncIndex);
188231

189232
return add(bin, [func, moduleExport, funcindex, functype]);
@@ -193,14 +236,19 @@ class WebAssemblyGenerator extends Generator {
193236
generate(module) {
194237
const bin = module.originalSource().source();
195238

239+
// FIXME(sven): this module is parsed twice, we could preserve the AST
240+
// from wasm/WebAssemblyParser.js
196241
const ast = decode(bin, {
197-
ignoreDataSection: true
242+
ignoreDataSection: true,
243+
ignoreCodeSection: true
198244
});
199245

200246
const importedGlobals = getImportedGlobals(ast);
201247
const funcSectionMetadata = t.getSectionMetadata(ast, "func");
248+
const countImportedFunc = getCountImportedFunc(ast);
202249
const startAtFuncIndex = getStartFuncIndex(ast);
203-
const nextFuncIndex = getNextFuncIndex(ast);
250+
const nextFuncIndex = getNextFuncIndex(ast, countImportedFunc);
251+
const nextTypeIndex = getNextTypeIndex(ast);
204252

205253
const transform = compose(
206254
removeStartFunc({}),
@@ -211,7 +259,8 @@ class WebAssemblyGenerator extends Generator {
211259
importedGlobals,
212260
funcSectionMetadata,
213261
startAtFuncIndex,
214-
nextFuncIndex
262+
nextFuncIndex,
263+
nextTypeIndex
215264
})
216265
);
217266

lib/wasm/WebAssemblyJavascriptGenerator.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ function generateInitParams(module) {
1818
const exportName = dep.name;
1919
const usedName = dep.module && dep.module.isUsed(exportName);
2020

21+
if (dep.module === null) {
22+
// Dependency was not found, an error will be thrown later
23+
continue;
24+
}
25+
2126
if (usedName !== false) {
2227
list.push(
2328
`__webpack_require__(${JSON.stringify(

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
"description": "Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.",
66
"license": "MIT",
77
"dependencies": {
8-
"@webassemblyjs/ast": "1.2.7",
9-
"@webassemblyjs/wasm-edit": "1.2.7",
10-
"@webassemblyjs/wasm-parser": "1.2.7",
8+
"@webassemblyjs/ast": "1.3.0",
9+
"@webassemblyjs/wasm-edit": "1.3.0",
10+
"@webassemblyjs/wasm-parser": "1.3.0",
1111
"acorn": "^5.0.0",
1212
"acorn-dynamic-import": "^3.0.0",
1313
"ajv": "^6.1.0",

test/cases/wasm/decoding/index.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
it("should support wasm compiled from c++", function() {
2+
return import("./memory3.wasm").then(function(wasm) {
3+
expect(wasm._Z3getv()).toBe(0);
4+
wasm._Z3seti(42);
5+
expect(wasm._Z3getv()).toBe(42);
6+
});
7+
});
8+
9+
it("should raw memory export without data", function() {
10+
return import("./memory2.wasm").then(function(wasm) {
11+
expect(wasm.memory).toBeInstanceOf(WebAssembly.Memory);
12+
expect(wasm.memory.buffer).toBeInstanceOf(ArrayBuffer);
13+
expect(wasm.memory.buffer.byteLength).toBe(1 << 16);
14+
});
15+
});
35 Bytes
Binary file not shown.
143 Bytes
Binary file not shown.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
var supportsWebAssembly = require("../../../helpers/supportsWebAssembly");
2+
3+
module.exports = function(config) {
4+
return supportsWebAssembly();
5+
};

test/cases/wasm/table/index.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// the message is inconsistency between some nodejs versions
2+
const UNKNOWN_FUNCTION_TABLE = /invalid index into function table|invalid function/;
3+
4+
it("should support tables", function() {
5+
return import("./wasm-table.wasm").then(function(wasm) {
6+
expect(wasm.callByIndex(0)).toEqual(42);
7+
expect(wasm.callByIndex(1)).toEqual(13);
8+
expect(() => wasm.callByIndex(2)).toThrow(UNKNOWN_FUNCTION_TABLE);
9+
});
10+
});
11+
12+
it("should support exported tables", function() {
13+
return import("./wasm-table-export.wasm").then(function(wasm) {
14+
expect(wasm.table).toBeInstanceOf(WebAssembly.Table);
15+
expect(wasm.table.length).toBe(2);
16+
const e0 = wasm.table.get(0);
17+
const e1 = wasm.table.get(1);
18+
expect(e0).toBeInstanceOf(Function);
19+
expect(e1).toBeInstanceOf(Function);
20+
expect(e0()).toEqual(42);
21+
expect(e1()).toEqual(13);
22+
});
23+
});
24+
25+
it("should support imported tables", function() {
26+
return import("./wasm-table-imported.wasm").then(function(wasm) {
27+
expect(wasm.callByIndex(0)).toEqual(42);
28+
expect(wasm.callByIndex(1)).toEqual(13);
29+
expect(() => wasm.callByIndex(2)).toThrow(UNKNOWN_FUNCTION_TABLE);
30+
});
31+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
var supportsWebAssembly = require("../../../helpers/supportsWebAssembly");
2+
3+
module.exports = function(config) {
4+
return supportsWebAssembly();
5+
};

0 commit comments

Comments
 (0)