Skip to content

Commit 3ee016f

Browse files
committed
add support for imports in wasm modules
1 parent 7b7b2cd commit 3ee016f

16 files changed

+109
-3
lines changed

lib/WebAssemblyModulesPlugin.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
"use strict";
66

77
const WebAssemblyParser = require("./WebAssemblyParser");
8+
const WebAssemblyImportDependency = require("./dependencies/WebAssemblyImportDependency");
89

910
class WebAssemblyModulesPlugin {
1011
apply(compiler) {
1112
compiler.hooks.compilation.tap("WebAssemblyModulesPlugin", (compilation, {
1213
normalModuleFactory
1314
}) => {
15+
compilation.dependencyFactories.set(WebAssemblyImportDependency, normalModuleFactory);
16+
1417
normalModuleFactory.hooks.createParser.for("webassembly/experimental").tap("WebAssemblyModulesPlugin", () => {
1518
return new WebAssemblyParser();
1619
});

lib/WebAssemblyParser.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
99

1010
const Tapable = require("tapable").Tapable;
11+
const WebAssemblyImportDependency = require("./dependencies/WebAssemblyImportDependency");
1112

1213
class WebAssemblyParser extends Tapable {
1314
constructor(options) {
@@ -29,10 +30,13 @@ class WebAssemblyParser extends Tapable {
2930
if(typeof WebAssembly !== "undefined") {
3031
WebAssembly.compile(source).then(module => {
3132
state.module.buildMeta.providedExports = WebAssembly.Module.exports(module).map(exp => exp.name);
33+
for(const imp of WebAssembly.Module.imports(module)) {
34+
const dep = new WebAssemblyImportDependency(imp.module, imp.name, imp.kind);
35+
state.module.addDependency(dep);
36+
}
3237
}).then(() => callback(null, state), err => callback(err));
3338
} else {
34-
state.module.buildMeta.providedExports = false;
35-
callback(null, state);
39+
throw new Error("Can't compile WebAssembly modules without WebAssembly support in current node.js version (Update to latest node.js version)");
3640
}
3741
}
3842
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Tobias Koppers @sokra
4+
*/
5+
"use strict";
6+
const ModuleDependency = require("./ModuleDependency");
7+
8+
class WebAssemblyImportDependency extends ModuleDependency {
9+
constructor(request, name) {
10+
super(request);
11+
this.name = name;
12+
}
13+
14+
getReference() {
15+
if(!this.module) return null;
16+
return {
17+
module: this.module,
18+
importedNames: [this.name]
19+
};
20+
}
21+
22+
get type() {
23+
return "wasm import";
24+
}
25+
}
26+
27+
module.exports = WebAssemblyImportDependency;

lib/wasm/WasmModuleTemplatePlugin.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"use strict";
66

77
const RawSource = require("webpack-sources").RawSource;
8+
const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
89

910
class WasmModuleTemplatePlugin {
1011
apply(moduleTemplate) {
@@ -30,11 +31,40 @@ class WasmModuleTemplatePlugin {
3031
return `${module.moduleArgument}.exports = instance.exports;`;
3132
}
3233
};
34+
const generateImports = () => {
35+
const depsByRequest = new Map();
36+
for(const dep of module.dependencies) {
37+
if(dep instanceof WebAssemblyImportDependency) {
38+
const request = dep.request;
39+
let array = depsByRequest.get(request);
40+
if(!array) {
41+
depsByRequest.set(request, array = []);
42+
}
43+
const exportName = dep.name;
44+
const usedName = dep.module && dep.module.isUsed(exportName);
45+
array.push({
46+
exportName,
47+
usedName,
48+
module: dep.module
49+
});
50+
}
51+
}
52+
const importsCode = [];
53+
for(const pair of depsByRequest) {
54+
const properties = [];
55+
for(const data of pair[1]) {
56+
properties.push(`\n\t\t${JSON.stringify(data.exportName)}: __webpack_require__(${JSON.stringify(data.module.id)})[${JSON.stringify(data.usedName)}]`);
57+
}
58+
importsCode.push(`\n\t${JSON.stringify(pair[0])}: {${properties.join(",")}\n\t}`);
59+
}
60+
return importsCode.join(",");
61+
}
3362
const source = new RawSource([
3463
"\"use strict\";",
3564
"",
3665
"// Instanciate WebAssembly module",
37-
"var instance = new WebAssembly.Instance(__webpack_require__.w[module.i], {});",
66+
"var instance = new WebAssembly.Instance(__webpack_require__.w[module.i], {" + generateImports(),
67+
"});",
3868
"",
3969
"// export exports from WebAssmbly module",
4070
// TODO rewrite this to getters depending on exports to support circular dependencies
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
it("should allow to run a WebAssembly module with imports", function() {
2+
return import("./wasm.wasm").then(function(wasm) {
3+
const result = wasm.addNumber(20);
4+
result.should.be.eql(42);
5+
});
6+
});
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+
};
78 Bytes
Binary file not shown.
42 Bytes
Binary file not shown.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
it("should allow to run a WebAssembly module importing JS circular", function() {
2+
return import("./module").then(function(mod) {
3+
mod.result.should.be.eql(42);
4+
});
5+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { addNumber } from "./wasm.wasm";
2+
3+
export var result = addNumber(22);
4+
5+
export function getNumber() {
6+
return 20;
7+
}
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+
};
74 Bytes
Binary file not shown.

test/cases/wasm/imports/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
it("should allow to run a WebAssembly module with imports", function() {
2+
return import("./wasm.wasm?1").then(function(wasm) {
3+
const result = wasm.addNumber(3);
4+
result.should.be.eql(11);
5+
});
6+
});

test/cases/wasm/imports/module.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function getNumber() {
2+
return 8;
3+
}
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/imports/wasm.wasm

74 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)