summaryrefslogtreecommitdiffstats
path: root/node_modules/webpack/lib/wasm/WasmMainTemplatePlugin.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/webpack/lib/wasm/WasmMainTemplatePlugin.js')
-rw-r--r--node_modules/webpack/lib/wasm/WasmMainTemplatePlugin.js341
1 files changed, 341 insertions, 0 deletions
diff --git a/node_modules/webpack/lib/wasm/WasmMainTemplatePlugin.js b/node_modules/webpack/lib/wasm/WasmMainTemplatePlugin.js
new file mode 100644
index 0000000..494c8ec
--- /dev/null
+++ b/node_modules/webpack/lib/wasm/WasmMainTemplatePlugin.js
@@ -0,0 +1,341 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+*/
+"use strict";
+
+const Template = require("../Template");
+const WebAssemblyUtils = require("./WebAssemblyUtils");
+
+/** @typedef {import("../Module")} Module */
+/** @typedef {import("../MainTemplate")} MainTemplate */
+
+// Get all wasm modules
+const getAllWasmModules = chunk => {
+ 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;
+};
+
+/**
+ * generates the import object function for a module
+ * @param {Module} module the module
+ * @param {boolean} mangle mangle imports
+ * @returns {string} source code
+ */
+const generateImportObject = (module, mangle) => {
+ const waitForInstances = new Map();
+ const properties = [];
+ const usedWasmDependencies = WebAssemblyUtils.getUsedDependencies(
+ module,
+ mangle
+ );
+ for (const usedDep of usedWasmDependencies) {
+ const dep = usedDep.dependency;
+ const importedModule = dep.module;
+ const exportName = dep.name;
+ const usedName = importedModule && importedModule.isUsed(exportName);
+ const description = dep.description;
+ const direct = dep.onlyDirectImport;
+
+ const module = usedDep.module;
+ const name = usedDep.name;
+
+ if (direct) {
+ const instanceVar = `m${waitForInstances.size}`;
+ waitForInstances.set(instanceVar, importedModule.id);
+ properties.push({
+ module,
+ name,
+ value: `${instanceVar}[${JSON.stringify(usedName)}]`
+ });
+ } else {
+ const params = description.signature.params.map(
+ (param, k) => "p" + k + param.valtype
+ );
+
+ const mod = `installedModules[${JSON.stringify(importedModule.id)}]`;
+ const func = `${mod}.exports[${JSON.stringify(usedName)}]`;
+
+ properties.push({
+ module,
+ name,
+ value: Template.asString([
+ (importedModule.type.startsWith("webassembly")
+ ? `${mod} ? ${func} : `
+ : "") + `function(${params}) {`,
+ Template.indent([`return ${func}(${params});`]),
+ "}"
+ ])
+ });
+ }
+ }
+
+ let importObject;
+ if (mangle) {
+ importObject = [
+ "return {",
+ Template.indent([
+ properties.map(p => `${JSON.stringify(p.name)}: ${p.value}`).join(",\n")
+ ]),
+ "};"
+ ];
+ } else {
+ const propertiesByModule = new Map();
+ for (const p of properties) {
+ let list = propertiesByModule.get(p.module);
+ if (list === undefined) {
+ propertiesByModule.set(p.module, (list = []));
+ }
+ list.push(p);
+ }
+ importObject = [
+ "return {",
+ Template.indent([
+ Array.from(propertiesByModule, ([module, list]) => {
+ return Template.asString([
+ `${JSON.stringify(module)}: {`,
+ Template.indent([
+ list.map(p => `${JSON.stringify(p.name)}: ${p.value}`).join(",\n")
+ ]),
+ "}"
+ ]);
+ }).join(",\n")
+ ]),
+ "};"
+ ];
+ }
+
+ if (waitForInstances.size === 1) {
+ const moduleId = Array.from(waitForInstances.values())[0];
+ const promise = `installedWasmModules[${JSON.stringify(moduleId)}]`;
+ const variable = Array.from(waitForInstances.keys())[0];
+ return Template.asString([
+ `${JSON.stringify(module.id)}: function() {`,
+ Template.indent([
+ `return promiseResolve().then(function() { return ${promise}; }).then(function(${variable}) {`,
+ Template.indent(importObject),
+ "});"
+ ]),
+ "},"
+ ]);
+ } else if (waitForInstances.size > 0) {
+ const promises = Array.from(
+ waitForInstances.values(),
+ id => `installedWasmModules[${JSON.stringify(id)}]`
+ ).join(", ");
+ const variables = Array.from(
+ waitForInstances.keys(),
+ (name, i) => `${name} = array[${i}]`
+ ).join(", ");
+ return Template.asString([
+ `${JSON.stringify(module.id)}: function() {`,
+ Template.indent([
+ `return promiseResolve().then(function() { return Promise.all([${promises}]); }).then(function(array) {`,
+ Template.indent([`var ${variables};`, ...importObject]),
+ "});"
+ ]),
+ "},"
+ ]);
+ } else {
+ return Template.asString([
+ `${JSON.stringify(module.id)}: function() {`,
+ Template.indent(importObject),
+ "},"
+ ]);
+ }
+};
+
+class WasmMainTemplatePlugin {
+ constructor({ generateLoadBinaryCode, supportsStreaming, mangleImports }) {
+ this.generateLoadBinaryCode = generateLoadBinaryCode;
+ this.supportsStreaming = supportsStreaming;
+ this.mangleImports = mangleImports;
+ }
+
+ /**
+ * @param {MainTemplate} mainTemplate main template
+ * @returns {void}
+ */
+ apply(mainTemplate) {
+ mainTemplate.hooks.localVars.tap(
+ "WasmMainTemplatePlugin",
+ (source, chunk) => {
+ const wasmModules = getAllWasmModules(chunk);
+ if (wasmModules.length === 0) return source;
+ const importObjects = wasmModules.map(module => {
+ return generateImportObject(module, this.mangleImports);
+ });
+ return Template.asString([
+ source,
+ "",
+ "// object to store loaded and loading wasm modules",
+ "var installedWasmModules = {};",
+ "",
+ // This function is used to delay reading the installed wasm module promises
+ // by a microtask. Sorting them doesn't help because there are egdecases where
+ // sorting is not possible (modules splitted into different chunks).
+ // So we not even trying and solve this by a microtask delay.
+ "function promiseResolve() { return Promise.resolve(); }",
+ "",
+ "var wasmImportObjects = {",
+ Template.indent(importObjects),
+ "};"
+ ]);
+ }
+ );
+ mainTemplate.hooks.requireEnsure.tap(
+ "WasmMainTemplatePlugin",
+ (source, chunk, hash) => {
+ const webassemblyModuleFilename =
+ mainTemplate.outputOptions.webassemblyModuleFilename;
+
+ 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] + "`;
+ }
+ }
+ }
+ );
+ const createImportObject = content =>
+ this.mangleImports
+ ? `{ ${JSON.stringify(
+ WebAssemblyUtils.MANGLED_MODULE
+ )}: ${content} }`
+ : content;
+ return Template.asString([
+ source,
+ "",
+ "// Fetch + compile chunk loading for webassembly",
+ "",
+ `var wasmModules = ${JSON.stringify(
+ chunkModuleMaps.id
+ )}[chunkId] || [];`,
+ "",
+ "wasmModules.forEach(function(wasmModuleId) {",
+ Template.indent([
+ "var installedWasmModuleData = installedWasmModules[wasmModuleId];",
+ "",
+ '// a Promise means "currently loading" or "already loaded".',
+ "if(installedWasmModuleData)",
+ Template.indent(["promises.push(installedWasmModuleData);"]),
+ "else {",
+ Template.indent([
+ `var importObject = wasmImportObjects[wasmModuleId]();`,
+ `var req = ${this.generateLoadBinaryCode(wasmModuleSrcPath)};`,
+ "var promise;",
+ this.supportsStreaming
+ ? Template.asString([
+ "if(importObject instanceof Promise && typeof WebAssembly.compileStreaming === 'function') {",
+ Template.indent([
+ "promise = Promise.all([WebAssembly.compileStreaming(req), importObject]).then(function(items) {",
+ Template.indent([
+ `return WebAssembly.instantiate(items[0], ${createImportObject(
+ "items[1]"
+ )});`
+ ]),
+ "});"
+ ]),
+ "} else if(typeof WebAssembly.instantiateStreaming === 'function') {",
+ Template.indent([
+ `promise = WebAssembly.instantiateStreaming(req, ${createImportObject(
+ "importObject"
+ )});`
+ ])
+ ])
+ : Template.asString([
+ "if(importObject instanceof Promise) {",
+ Template.indent([
+ "var bytesPromise = req.then(function(x) { return x.arrayBuffer(); });",
+ "promise = Promise.all([",
+ Template.indent([
+ "bytesPromise.then(function(bytes) { return WebAssembly.compile(bytes); }),",
+ "importObject"
+ ]),
+ "]).then(function(items) {",
+ Template.indent([
+ `return WebAssembly.instantiate(items[0], ${createImportObject(
+ "items[1]"
+ )});`
+ ]),
+ "});"
+ ])
+ ]),
+ "} else {",
+ Template.indent([
+ "var bytesPromise = req.then(function(x) { return x.arrayBuffer(); });",
+ "promise = bytesPromise.then(function(bytes) {",
+ Template.indent([
+ `return WebAssembly.instantiate(bytes, ${createImportObject(
+ "importObject"
+ )});`
+ ]),
+ "});"
+ ]),
+ "}",
+ "promises.push(installedWasmModules[wasmModuleId] = promise.then(function(res) {",
+ Template.indent([
+ `return ${mainTemplate.requireFn}.w[wasmModuleId] = (res.instance || res).exports;`
+ ]),
+ "}));"
+ ]),
+ "}"
+ ]),
+ "});"
+ ]);
+ }
+ );
+ mainTemplate.hooks.requireExtensions.tap(
+ "WasmMainTemplatePlugin",
+ (source, chunk) => {
+ if (!chunk.hasModuleInGraph(m => m.type.startsWith("webassembly"))) {
+ return source;
+ }
+ return Template.asString([
+ source,
+ "",
+ "// object with all WebAssembly.instance exports",
+ `${mainTemplate.requireFn}.w = {};`
+ ]);
+ }
+ );
+ mainTemplate.hooks.hash.tap("WasmMainTemplatePlugin", hash => {
+ hash.update("WasmMainTemplatePlugin");
+ hash.update("2");
+ });
+ }
+}
+
+module.exports = WasmMainTemplatePlugin;