Merge pull request #6160 from webpack/wasm/imports

add support for imports in wasm modules
This commit is contained in:
Tobias Koppers 2017-12-22 08:20:58 +01:00 committed by GitHub
commit c6c447b42c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 119 additions and 3 deletions

View File

@ -0,0 +1,5 @@
var supportsWebAssembly = require("../../test/helpers/supportsWebAssembly");
module.exports = function(config) {
return supportsWebAssembly();
};

View File

@ -5,12 +5,15 @@
"use strict";
const WebAssemblyParser = require("./WebAssemblyParser");
const WebAssemblyImportDependency = require("./dependencies/WebAssemblyImportDependency");
class WebAssemblyModulesPlugin {
apply(compiler) {
compiler.hooks.compilation.tap("WebAssemblyModulesPlugin", (compilation, {
normalModuleFactory
}) => {
compilation.dependencyFactories.set(WebAssemblyImportDependency, normalModuleFactory);
normalModuleFactory.hooks.createParser.for("webassembly/experimental").tap("WebAssemblyModulesPlugin", () => {
return new WebAssemblyParser();
});

View File

@ -8,6 +8,7 @@
// Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
const Tapable = require("tapable").Tapable;
const WebAssemblyImportDependency = require("./dependencies/WebAssemblyImportDependency");
class WebAssemblyParser extends Tapable {
constructor(options) {
@ -29,10 +30,13 @@ class WebAssemblyParser extends Tapable {
if(typeof WebAssembly !== "undefined") {
WebAssembly.compile(source).then(module => {
state.module.buildMeta.providedExports = WebAssembly.Module.exports(module).map(exp => exp.name);
for(const imp of WebAssembly.Module.imports(module)) {
const dep = new WebAssemblyImportDependency(imp.module, imp.name, imp.kind);
state.module.addDependency(dep);
}
}).then(() => callback(null, state), err => callback(err));
} else {
state.module.buildMeta.providedExports = false;
callback(null, state);
throw new Error("Can't compile WebAssembly modules without WebAssembly support in current node.js version (Update to latest node.js version)");
}
}
}

View File

@ -0,0 +1,27 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const ModuleDependency = require("./ModuleDependency");
class WebAssemblyImportDependency extends ModuleDependency {
constructor(request, name) {
super(request);
this.name = name;
}
getReference() {
if(!this.module) return null;
return {
module: this.module,
importedNames: [this.name]
};
}
get type() {
return "wasm import";
}
}
module.exports = WebAssemblyImportDependency;

View File

@ -5,6 +5,7 @@
"use strict";
const RawSource = require("webpack-sources").RawSource;
const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
class WasmModuleTemplatePlugin {
apply(moduleTemplate) {
@ -30,11 +31,40 @@ class WasmModuleTemplatePlugin {
return `${module.moduleArgument}.exports = instance.exports;`;
}
};
const generateImports = () => {
const depsByRequest = new Map();
for(const dep of module.dependencies) {
if(dep instanceof WebAssemblyImportDependency) {
const request = dep.request;
let array = depsByRequest.get(request);
if(!array) {
depsByRequest.set(request, array = []);
}
const exportName = dep.name;
const usedName = dep.module && dep.module.isUsed(exportName);
array.push({
exportName,
usedName,
module: dep.module
});
}
}
const importsCode = [];
for(const pair of depsByRequest) {
const properties = [];
for(const data of pair[1]) {
properties.push(`\n\t\t${JSON.stringify(data.exportName)}: __webpack_require__(${JSON.stringify(data.module.id)})[${JSON.stringify(data.usedName)}]`);
}
importsCode.push(`\n\t${JSON.stringify(pair[0])}: {${properties.join(",")}\n\t}`);
}
return importsCode.join(",");
};
const source = new RawSource([
"\"use strict\";",
"",
"// Instanciate WebAssembly module",
"var instance = new WebAssembly.Instance(__webpack_require__.w[module.i], {});",
"var instance = new WebAssembly.Instance(__webpack_require__.w[module.i], {" + generateImports(),
"});",
"",
"// export exports from WebAssmbly module",
// TODO rewrite this to getters depending on exports to support circular dependencies

View File

@ -11,6 +11,11 @@ describe("Examples", () => {
const examples = require("../examples/examples.js");
examples.forEach((examplePath) => {
const filterPath = path.join(examplePath, "test.filter.js");
if(fs.existsSync(filterPath) && !require(filterPath)()) {
describe.skip(path.relative(basePath, examplePath), () => it("filtered"));
return;
}
it("should compile " + path.relative(basePath, examplePath), function(done) {
this.timeout(20000);
let options = {};

View File

@ -0,0 +1,6 @@
it("should allow to run a WebAssembly module with imports", function() {
return import("./wasm.wasm").then(function(wasm) {
const result = wasm.addNumber(20);
result.should.be.eql(42);
});
});

View File

@ -0,0 +1,5 @@
var supportsWebAssembly = require("../../../helpers/supportsWebAssembly");
module.exports = function(config) {
return supportsWebAssembly();
};

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,5 @@
it("should allow to run a WebAssembly module importing JS circular", function() {
return import("./module").then(function(mod) {
mod.result.should.be.eql(42);
});
});

View File

@ -0,0 +1,7 @@
import { addNumber } from "./wasm.wasm";
export var result = addNumber(22);
export function getNumber() {
return 20;
}

View File

@ -0,0 +1,5 @@
var supportsWebAssembly = require("../../../helpers/supportsWebAssembly");
module.exports = function(config) {
return supportsWebAssembly();
};

Binary file not shown.

View File

@ -0,0 +1,6 @@
it("should allow to run a WebAssembly module with imports", function() {
return import("./wasm.wasm?1").then(function(wasm) {
const result = wasm.addNumber(3);
result.should.be.eql(11);
});
});

View File

@ -0,0 +1,3 @@
export function getNumber() {
return 8;
}

View File

@ -0,0 +1,5 @@
var supportsWebAssembly = require("../../../helpers/supportsWebAssembly");
module.exports = function(config) {
return supportsWebAssembly();
};

Binary file not shown.