feat: use instantiateStreaming

This commit is contained in:
Sven SAULEAU 2018-03-08 17:54:06 +01:00
parent 71e46c8e58
commit faff92c52b
No known key found for this signature in database
GPG Key ID: F5464AC83B687AD1
10 changed files with 365 additions and 62 deletions

1
examples/sven/src/a.js Normal file
View File

@ -0,0 +1 @@
export const two = 2;

3
examples/sven/src/b.js Normal file
View File

@ -0,0 +1,3 @@
export function logFoo() {
console.log("foo");
}

View File

@ -0,0 +1,22 @@
// Before transformation:
//
// (module
// (import "./b" "logFoo" (func $a))
// (import "./a" "two" (global i32))
// (func (export "getTwo") (result i32)
// (get_global 0)
// )
// (func (export "logFoo")
// (call $a)
// )
// )
//
// ----
//
// After transformation:
//
import("./test.wasm").then(({getTwo, logFoo}) => {
console.log("getTwo", getTwo());
console.log(logFoo());
})

View File

@ -4,9 +4,144 @@
*/
"use strict";
const { RawSource } = require("webpack-sources");
const { edit, add } = require("@webassemblyjs/wasm-edit");
const { decode } = require("@webassemblyjs/wasm-parser");
// const { print } = require("@webassemblyjs/wast-printer");
const t = require("@webassemblyjs/ast");
// FIXME(sven): remove this once we're ready to merge
function debug(...msg) {
if (false) console.log(...msg);
}
function compose(...fns) {
return fns.reverse().reduce((prevFn, nextFn) => {
return value => nextFn(prevFn(value));
}, value => value);
}
/**
* Export the start function and removes the start instruction
*/
function rewriteStartFunc(bin) {
debug("rewriteStartFunc");
let startAtFuncIndex;
bin = edit(bin, {
Start(path) {
startAtFuncIndex = path.node.index;
path.remove();
}
});
// No start func, abort here
if (startAtFuncIndex === undefined) {
return bin;
}
debug("found start at func index", startAtFuncIndex.value);
bin = add(bin, [t.moduleExport("start", "Func", startAtFuncIndex)]);
return bin;
}
/**
* Replaces global imports by func imports
* (which will return the globals at runtime)
*
* Also needs to update the calls instructions `get_global` and `set_global`
* which become function calls.
*/
function rewriteGlobalImports(bin) {
debug("rewriteGlobalImports");
const ast = decode(bin, {
ignoreCodeSection: true,
ignoreDataSection: true
});
const funcType = t.typeInstructionFunc([], ["i32"]);
// get next func index
let nextFuncindex = 0;
t.traverse(ast, {
Func() {
nextFuncindex++;
},
ModuleImport({ node }) {
if (node.descr.type === "Func") {
nextFuncindex++;
}
}
});
const funcTypeIndex = t.indexLiteral(nextFuncindex);
bin = add(bin, [funcType]);
let importedGlobalIndex = 0;
const mapGlobalAndFuncIndex = {};
bin = edit(bin, {
ModuleImport(path) {
const { node } = path;
// is importing a global
if (node.descr.type === "GlobalType") {
node.name = "_global_get_" + node.name;
node.descr = t.funcImportDescr(
funcTypeIndex,
funcType.functype.params,
funcType.functype.results
);
mapGlobalAndFuncIndex[importedGlobalIndex] = funcTypeIndex;
importedGlobalIndex++;
}
}
});
// Update accessers
bin = edit(bin, {
Instr(path) {
const [firstArg] = path.node.args;
const funcIndex = mapGlobalAndFuncIndex[firstArg.value];
debug(`rename get_global ${firstArg.value} to call ${funcIndex.value}`);
const newNode = t.callInstruction(funcIndex);
path.replaceWith(newNode);
}
});
return bin;
}
const transform = compose(
rewriteGlobalImports,
rewriteStartFunc
);
class WebAssemblyGenerator {
generate(module) {
return module.originalSource();
const bin = module.originalSource().source();
debug("__________________________________________________________");
const newBin = transform(bin);
debug("__________________________________________________________");
// console.log(print(decode(newBin)))
return new RawSource(newBin);
}
}

View File

@ -37,7 +37,12 @@ class WebAssemblyParser extends Tapable {
},
ModuleImport({ node }) {
const dep = new WebAssemblyImportDependency(node.module, node.name);
const dep = new WebAssemblyImportDependency(
node.module,
node.name,
node.descr
);
state.module.addDependency(dep);
}
});

View File

@ -6,9 +6,10 @@
const ModuleDependency = require("./ModuleDependency");
class WebAssemblyImportDependency extends ModuleDependency {
constructor(request, name) {
constructor(request, name, description) {
super(request);
this.name = name;
this.description = description;
}
getReference() {

View File

@ -40,50 +40,11 @@ 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";',
"",
"// Instantiate WebAssembly module",
"var instance = new WebAssembly.Instance(__webpack_require__.w[module.i], {" +
generateImports(),
"});",
"var instance = __webpack_require__.w[module.i]",
"",
"// export exports from WebAssembly module",
// TODO rewrite this to getters depending on exports to support circular dependencies

View File

@ -5,6 +5,7 @@
"use strict";
const Template = require("../Template");
const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
class FetchCompileWasmMainTemplatePlugin {
apply(mainTemplate) {
@ -26,6 +27,86 @@ class FetchCompileWasmMainTemplatePlugin {
(source, chunk, hash) => {
const webassemblyModuleFilename =
mainTemplate.outputOptions.webassemblyModuleFilename;
/**
* Get all wasm modules
*/
function getAllWasmModules() {
const wasmModules = chunk.getAllAsyncChunks();
const array = [];
for (const chunk of wasmModules) {
for (const m of chunk.modulesIterable) {
if (m.type.startsWith("webassembly")) {
array.push(m);
}
}
}
return array;
}
function generateImportObject(module) {
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,
description: dep.description
});
}
}
const importsCode = [];
for (const pair of depsByRequest) {
const properties = [];
for (const data of pair[1]) {
let params = "";
let result = "void 0";
if (data.description.type === "FuncImportDescr") {
params = data.description.params.map(
(param, k) => "p" + k + param.valtype
);
result = `__webpack_require__(${JSON.stringify(
data.module.id
)})[${JSON.stringify(data.usedName)}](${params})`;
}
if (data.description.type === "GlobalType") {
data.exportName = "_global_get_" + data.exportName;
result = `__webpack_require__(${JSON.stringify(
data.module.id
)})[${JSON.stringify(data.usedName)}]`;
}
properties.push(
`\n\t\t${JSON.stringify(data.exportName)}: function(${params}) {
return ${result};
}`
);
}
importsCode.push(
`\n\t${JSON.stringify(pair[0])}: {${properties.join(",")}\n\t}`
);
}
return (
JSON.stringify(module.id) + ": {" + importsCode.join(",") + "\n}"
);
}
const wasmModules = getAllWasmModules();
const importObjects = wasmModules.map(generateImportObject);
const chunkModuleMaps = chunk.getChunkModuleMaps(m =>
m.type.startsWith("webassembly")
);
@ -61,6 +142,10 @@ class FetchCompileWasmMainTemplatePlugin {
"",
"// Fetch + compile chunk loading for webassembly",
"",
"var importObjects = {",
Template.indent([importObjects]),
"}",
"",
`var wasmModules = ${JSON.stringify(
chunkModuleMaps.id
)}[chunkId] || [];`,
@ -70,27 +155,25 @@ class FetchCompileWasmMainTemplatePlugin {
"var installedWasmModuleData = installedWasmModules[wasmModuleId];",
"",
'// a Promise means "currently loading" or "already loaded".',
"promises.push(installedWasmModuleData ||",
Template.indent([
`(installedWasmModules[wasmModuleId] = fetch(${
mainTemplate.requireFn
}.p + ${wasmModuleSrcPath}).then(function(response) {`,
`var importObject = importObjects[wasmModuleId]`,
`var req = fetch(${mainTemplate.requireFn}.p + ${
wasmModuleSrcPath
})`,
"if(WebAssembly.instantiateStreaming) {",
Template.indent([
"if(WebAssembly.compileStreaming) {",
Template.indent([
"return WebAssembly.compileStreaming(response);"
]),
"} else {",
Template.indent([
"return response.arrayBuffer().then(function(bytes) { return WebAssembly.compile(bytes); });"
]),
"}"
"promises.push(WebAssembly.instantiateStreaming(req, importObject)",
`.then(function(res) { ${
mainTemplate.requireFn
}.w[wasmModuleId] = installedWasmModules[wasmModuleId] = res.instance; }))`
]),
`}).then(function(module) { ${
mainTemplate.requireFn
}.w[wasmModuleId] = module; }))`
]),
");"
"} else {",
Template.indent([
// FIXME(sven): ensrue this still works / change it
"promises.push(response.arrayBuffer().then(function(bytes) { installedWasmModules[wasmModuleId] = WebAssembly.compile(bytes); }));"
]),
"}"
])
]),
"});"
]);
@ -104,7 +187,7 @@ class FetchCompileWasmMainTemplatePlugin {
return Template.asString([
source,
"",
"// object with all compiled WebAssembly.Modules",
"// object with all WebAssembly.instance",
`${mainTemplate.requireFn}.w = {};`
]);
}

View File

@ -6,6 +6,7 @@
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "^1.0.0",
"@webassemblyjs/wasm-edit": "^1.1.2-y.0",
"@webassemblyjs/wasm-parser": "^1.0.0",
"acorn": "^5.0.0",
"acorn-dynamic-import": "^3.0.0",

View File

@ -24,6 +24,56 @@
webassembly-floating-point-hex-parser "0.1.2"
webassemblyjs "1.0.0"
"@webassemblyjs/ast@1.1.2-y.0":
version "1.1.2-y.0"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.1.2-y.0.tgz#138d5ba9f554542b0222da1b6a969bbfab525f1c"
dependencies:
"@webassemblyjs/wast-parser" "1.1.2-y.0"
webassembly-floating-point-hex-parser "0.1.2"
webassemblyjs "1.1.2-y.0"
"@webassemblyjs/helper-buffer@1.1.2-y.0":
version "1.1.2-y.0"
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.1.2-y.0.tgz#25e8b7d133be147fe70f522263491da2b36f5259"
"@webassemblyjs/helper-leb128@1.1.2-y.0":
version "1.1.2-y.0"
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-leb128/-/helper-leb128-1.1.2-y.0.tgz#ba158e90f936a5caadafb83dfd362600046cd6a9"
dependencies:
leb "^0.3.0"
"@webassemblyjs/helper-wasm-bytecode@1.1.2-y.0":
version "1.1.2-y.0"
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.1.2-y.0.tgz#d50c40200fc5ab6a4ab0c080f9ff4c815c2f0302"
"@webassemblyjs/helper-wasm-section@1.1.2-y.0":
version "1.1.2-y.0"
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.1.2-y.0.tgz#28acfd9c1f1aeb3864031f15b4398f9c9ffac8dc"
dependencies:
"@webassemblyjs/ast" "1.1.2-y.0"
"@webassemblyjs/helper-buffer" "1.1.2-y.0"
"@webassemblyjs/helper-wasm-bytecode" "1.1.2-y.0"
"@webassemblyjs/wasm-gen" "1.1.2-y.0"
"@webassemblyjs/wasm-edit@^1.1.2-y.0":
version "1.1.2-y.0"
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.1.2-y.0.tgz#9145814a40b4e1ef023a6b644dab8832d7c62531"
dependencies:
"@webassemblyjs/ast" "1.1.2-y.0"
"@webassemblyjs/helper-buffer" "1.1.2-y.0"
"@webassemblyjs/helper-wasm-bytecode" "1.1.2-y.0"
"@webassemblyjs/helper-wasm-section" "1.1.2-y.0"
"@webassemblyjs/wasm-gen" "1.1.2-y.0"
"@webassemblyjs/wasm-parser" "1.1.2-y.0"
"@webassemblyjs/wasm-gen@1.1.2-y.0":
version "1.1.2-y.0"
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.1.2-y.0.tgz#38f103f25eff059b0a2c436e7e345b0bd216fff5"
dependencies:
"@webassemblyjs/ast" "1.1.2-y.0"
"@webassemblyjs/helper-leb128" "1.1.2-y.0"
"@webassemblyjs/helper-wasm-bytecode" "1.1.2-y.0"
"@webassemblyjs/wasm-parser@1.0.0", "@webassemblyjs/wasm-parser@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.0.0.tgz#79e3e45f907a57c7a8bb74e746ee7bab7b77041a"
@ -32,6 +82,16 @@
"@webassemblyjs/wasm-parser" "1.0.0"
webassemblyjs "1.0.0"
"@webassemblyjs/wasm-parser@1.1.2-y.0":
version "1.1.2-y.0"
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.1.2-y.0.tgz#9c90f9e7ce1f54b922b8b76de5355f13e0d8c6f2"
dependencies:
"@webassemblyjs/ast" "1.1.2-y.0"
"@webassemblyjs/helper-leb128" "1.1.2-y.0"
"@webassemblyjs/helper-wasm-bytecode" "1.1.2-y.0"
"@webassemblyjs/wasm-parser" "1.1.2-y.0"
webassemblyjs "1.1.2-y.0"
"@webassemblyjs/wast-parser@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.0.0.tgz#d874e2dcb1726d31868518be7807f8f2ff358425"
@ -42,6 +102,23 @@
webassembly-floating-point-hex-parser "0.1.2"
webassemblyjs "1.0.0"
"@webassemblyjs/wast-parser@1.1.2-y.0":
version "1.1.2-y.0"
resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.1.2-y.0.tgz#f9cd5abb9f5f06687b49f8e6582ed5b4c233b099"
dependencies:
"@babel/code-frame" "^7.0.0-beta.36"
"@webassemblyjs/ast" "1.1.2-y.0"
long "^3.2.0"
webassembly-floating-point-hex-parser "0.1.2"
webassemblyjs "1.1.2-y.0"
"@webassemblyjs/wast-printer@^1.1.2-y.0":
version "1.1.2-y.0"
resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.1.2-y.0.tgz#9212f81e9e8b25a7d35f51d97f43b64c1e7eb655"
dependencies:
"@webassemblyjs/wast-parser" "1.1.2-y.0"
long "^3.2.0"
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@ -2469,6 +2546,10 @@ lcov-parse@0.0.10, lcov-parse@0.x:
version "0.0.10"
resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-0.0.10.tgz#1b0b8ff9ac9c7889250582b70b71315d9da6d9a3"
leb@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/leb/-/leb-0.3.0.tgz#32bee9fad168328d6aea8522d833f4180eed1da3"
less-loader@^4.0.3:
version "4.0.5"
resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-4.0.5.tgz#ae155a7406cac6acd293d785587fcff0f478c4dd"
@ -4692,6 +4773,16 @@ webassemblyjs@1.0.0:
long "^3.2.0"
webassembly-floating-point-hex-parser "0.1.2"
webassemblyjs@1.1.2-y.0:
version "1.1.2-y.0"
resolved "https://registry.yarnpkg.com/webassemblyjs/-/webassemblyjs-1.1.2-y.0.tgz#22ee9599176f59d9ef2a0793c0c274402fe23047"
dependencies:
"@webassemblyjs/ast" "1.1.2-y.0"
"@webassemblyjs/wasm-parser" "1.1.2-y.0"
"@webassemblyjs/wast-parser" "1.1.2-y.0"
long "^3.2.0"
webassembly-floating-point-hex-parser "0.1.2"
webpack-dev-middleware@^1.9.0:
version "1.12.2"
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz#f8fc1120ce3b4fc5680ceecb43d777966b21105e"