2017-10-30 20:56:57 +08:00
|
|
|
/*
|
|
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
|
|
Author Tobias Koppers @sokra
|
|
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
|
2017-12-07 16:42:33 +08:00
|
|
|
const Template = require("../Template");
|
2018-03-09 00:54:06 +08:00
|
|
|
const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
|
2017-12-07 16:42:33 +08:00
|
|
|
|
2017-10-30 20:56:57 +08:00
|
|
|
class FetchCompileWasmMainTemplatePlugin {
|
|
|
|
apply(mainTemplate) {
|
2018-02-25 09:00:20 +08:00
|
|
|
mainTemplate.hooks.localVars.tap(
|
|
|
|
"FetchCompileWasmMainTemplatePlugin",
|
|
|
|
(source, chunk) => {
|
|
|
|
if (!chunk.hasModuleInGraph(m => m.type.startsWith("webassembly")))
|
|
|
|
return source;
|
|
|
|
return Template.asString([
|
|
|
|
source,
|
|
|
|
"",
|
|
|
|
"// object to store loaded and loading wasm modules",
|
|
|
|
"var installedWasmModules = {};"
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
mainTemplate.hooks.requireEnsure.tap(
|
|
|
|
"FetchCompileWasmMainTemplatePlugin",
|
|
|
|
(source, chunk, hash) => {
|
|
|
|
const webassemblyModuleFilename =
|
|
|
|
mainTemplate.outputOptions.webassemblyModuleFilename;
|
2018-03-09 00:54:06 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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") {
|
2018-03-09 19:04:27 +08:00
|
|
|
data.exportName = "_global_get_" + data.exportName;
|
2018-03-09 00:54:06 +08:00
|
|
|
|
|
|
|
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}`
|
|
|
|
);
|
|
|
|
}
|
2018-03-09 19:04:27 +08:00
|
|
|
|
|
|
|
// Add interoptable
|
|
|
|
importsCode.push(`\n\t"webpack": { "interoptable": interoptable }\n\t`);
|
|
|
|
|
2018-03-09 00:54:06 +08:00
|
|
|
return (
|
|
|
|
JSON.stringify(module.id) + ": {" + importsCode.join(",") + "\n}"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const wasmModules = getAllWasmModules();
|
|
|
|
const importObjects = wasmModules.map(generateImportObject);
|
|
|
|
|
2018-02-25 09:00:20 +08:00
|
|
|
const chunkModuleMaps = chunk.getChunkModuleMaps(m =>
|
|
|
|
m.type.startsWith("webassembly")
|
|
|
|
);
|
|
|
|
if (Object.keys(chunkModuleMaps.id).length === 0) return source;
|
|
|
|
const wasmModuleSrcPath = mainTemplate.getAssetPath(
|
|
|
|
JSON.stringify(webassemblyModuleFilename),
|
|
|
|
{
|
|
|
|
hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
|
|
|
|
hashWithLength: length =>
|
|
|
|
`" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`,
|
|
|
|
module: {
|
|
|
|
id: '" + wasmModuleId + "',
|
|
|
|
hash: `" + ${JSON.stringify(
|
|
|
|
chunkModuleMaps.hash
|
|
|
|
)}[wasmModuleId] + "`,
|
|
|
|
hashWithLength(length) {
|
|
|
|
const shortChunkHashMap = Object.create(null);
|
|
|
|
for (const wasmModuleId of Object.keys(chunkModuleMaps.hash)) {
|
|
|
|
if (typeof chunkModuleMaps.hash[wasmModuleId] === "string")
|
|
|
|
shortChunkHashMap[wasmModuleId] = chunkModuleMaps.hash[
|
|
|
|
wasmModuleId
|
|
|
|
].substr(0, length);
|
|
|
|
}
|
|
|
|
return `" + ${JSON.stringify(
|
|
|
|
shortChunkHashMap
|
|
|
|
)}[wasmModuleId] + "`;
|
|
|
|
}
|
2018-01-22 20:52:43 +08:00
|
|
|
}
|
2017-10-30 20:56:57 +08:00
|
|
|
}
|
2018-02-25 09:00:20 +08:00
|
|
|
);
|
|
|
|
return Template.asString([
|
|
|
|
source,
|
|
|
|
"",
|
|
|
|
"// Fetch + compile chunk loading for webassembly",
|
|
|
|
"",
|
2018-03-09 19:04:27 +08:00
|
|
|
"var interoptable = new WebAssembly.Table({",
|
|
|
|
Template.indent(["element: 'anyfunc',", "initial: 0"]),
|
|
|
|
"})",
|
|
|
|
"",
|
|
|
|
"interoptable.set(0, () => console.log('called'))",
|
|
|
|
"",
|
2018-03-09 00:54:06 +08:00
|
|
|
"var importObjects = {",
|
|
|
|
Template.indent([importObjects]),
|
|
|
|
"}",
|
|
|
|
"",
|
2018-02-25 09:00:20 +08:00
|
|
|
`var wasmModules = ${JSON.stringify(
|
|
|
|
chunkModuleMaps.id
|
|
|
|
)}[chunkId] || [];`,
|
2017-10-30 20:56:57 +08:00
|
|
|
"",
|
2018-02-25 09:00:20 +08:00
|
|
|
"wasmModules.forEach(function(wasmModuleId) {",
|
2017-12-07 16:42:33 +08:00
|
|
|
Template.indent([
|
2018-02-25 09:00:20 +08:00
|
|
|
"var installedWasmModuleData = installedWasmModules[wasmModuleId];",
|
|
|
|
"",
|
|
|
|
'// a Promise means "currently loading" or "already loaded".',
|
2017-12-07 16:42:33 +08:00
|
|
|
Template.indent([
|
2018-03-09 00:54:06 +08:00
|
|
|
`var importObject = importObjects[wasmModuleId]`,
|
|
|
|
`var req = fetch(${mainTemplate.requireFn}.p + ${
|
|
|
|
wasmModuleSrcPath
|
|
|
|
})`,
|
|
|
|
"if(WebAssembly.instantiateStreaming) {",
|
|
|
|
Template.indent([
|
|
|
|
"promises.push(WebAssembly.instantiateStreaming(req, importObject)",
|
|
|
|
`.then(function(res) { ${
|
|
|
|
mainTemplate.requireFn
|
|
|
|
}.w[wasmModuleId] = installedWasmModules[wasmModuleId] = res.instance; }))`
|
|
|
|
]),
|
|
|
|
"} else {",
|
2017-12-07 16:42:33 +08:00
|
|
|
Template.indent([
|
2018-03-09 00:54:06 +08:00
|
|
|
// FIXME(sven): ensrue this still works / change it
|
|
|
|
"promises.push(response.arrayBuffer().then(function(bytes) { installedWasmModules[wasmModuleId] = WebAssembly.compile(bytes); }));"
|
2017-11-03 16:44:43 +08:00
|
|
|
]),
|
2018-03-09 00:54:06 +08:00
|
|
|
"}"
|
|
|
|
])
|
2017-10-30 20:56:57 +08:00
|
|
|
]),
|
2018-02-25 09:00:20 +08:00
|
|
|
"});"
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
mainTemplate.hooks.requireExtensions.tap(
|
|
|
|
"FetchCompileWasmMainTemplatePlugin",
|
|
|
|
(source, chunk) => {
|
|
|
|
if (!chunk.hasModuleInGraph(m => m.type.startsWith("webassembly")))
|
|
|
|
return source;
|
|
|
|
return Template.asString([
|
|
|
|
source,
|
|
|
|
"",
|
2018-03-09 00:54:06 +08:00
|
|
|
"// object with all WebAssembly.instance",
|
2018-02-25 09:00:20 +08:00
|
|
|
`${mainTemplate.requireFn}.w = {};`
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
);
|
2017-12-14 04:35:39 +08:00
|
|
|
mainTemplate.hooks.hash.tap("FetchCompileWasmMainTemplatePlugin", hash => {
|
2017-11-12 01:48:29 +08:00
|
|
|
hash.update("FetchCompileWasmMainTemplatePlugin");
|
|
|
|
hash.update("1");
|
|
|
|
hash.update(`${mainTemplate.outputOptions.webassemblyModuleFilename}`);
|
2017-10-30 20:56:57 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
module.exports = FetchCompileWasmMainTemplatePlugin;
|