source: frontend/node_modules/webpack/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 12.5 KB
RevLine 
[9af201e]1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3*/
4
5"use strict";
6
7const RuntimeGlobals = require("../RuntimeGlobals");
8const RuntimeModule = require("../RuntimeModule");
9const Template = require("../Template");
10const { compareModulesByIdentifier } = require("../util/comparators");
11const WebAssemblyUtils = require("./WebAssemblyUtils");
12
13/** @typedef {import("@webassemblyjs/ast").Signature} Signature */
14/** @typedef {import("../Chunk")} Chunk */
15/** @typedef {import("../ChunkGraph")} ChunkGraph */
16/** @typedef {import("../ChunkGraph").ModuleId} ModuleId */
17/** @typedef {import("../Compilation")} Compilation */
18/** @typedef {import("../Module")} Module */
19/** @typedef {import("../Module").ReadOnlyRuntimeRequirements} ReadOnlyRuntimeRequirements */
20/** @typedef {import("../ModuleGraph")} ModuleGraph */
21/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
22
23// TODO webpack 6 remove the whole folder
24
25// Get all wasm modules
26/**
27 * @param {ModuleGraph} moduleGraph the module graph
28 * @param {ChunkGraph} chunkGraph the chunk graph
29 * @param {Chunk} chunk the chunk
30 * @returns {Module[]} all wasm modules
31 */
32const getAllWasmModules = (moduleGraph, chunkGraph, chunk) => {
33 const wasmModules = chunk.getAllAsyncChunks();
34 /** @type {Module[]} */
35 const array = [];
36 for (const chunk of wasmModules) {
37 for (const m of chunkGraph.getOrderedChunkModulesIterable(
38 chunk,
39 compareModulesByIdentifier
40 )) {
41 if (m.type.startsWith("webassembly")) {
42 array.push(m);
43 }
44 }
45 }
46
47 return array;
48};
49
50/** @typedef {string[]} Declarations */
51
52/**
53 * generates the import object function for a module
54 * @param {ChunkGraph} chunkGraph the chunk graph
55 * @param {Module} module the module
56 * @param {boolean | undefined} mangle mangle imports
57 * @param {Declarations} declarations array where declarations are pushed to
58 * @param {RuntimeSpec} runtime the runtime
59 * @returns {string} source code
60 */
61const generateImportObject = (
62 chunkGraph,
63 module,
64 mangle,
65 declarations,
66 runtime
67) => {
68 const moduleGraph = chunkGraph.moduleGraph;
69 /** @type {Map<string, ModuleId>} */
70 const waitForInstances = new Map();
71 /** @type {{ module: string, name: string, value: string }[]} */
72 const properties = [];
73 const usedWasmDependencies = WebAssemblyUtils.getUsedDependencies(
74 moduleGraph,
75 module,
76 mangle
77 );
78 for (const usedDep of usedWasmDependencies) {
79 const dep = usedDep.dependency;
80 const importedModule = moduleGraph.getModule(dep);
81 const exportName = dep.name;
82 const usedName =
83 importedModule &&
84 moduleGraph
85 .getExportsInfo(importedModule)
86 .getUsedName(exportName, runtime);
87 const description = dep.description;
88 const direct = dep.onlyDirectImport;
89
90 const module = usedDep.module;
91 const name = usedDep.name;
92
93 if (direct) {
94 const instanceVar = `m${waitForInstances.size}`;
95 waitForInstances.set(
96 instanceVar,
97 /** @type {ModuleId} */
98 (chunkGraph.getModuleId(/** @type {Module} */ (importedModule)))
99 );
100 properties.push({
101 module,
102 name,
103 value: `${instanceVar}[${JSON.stringify(usedName)}]`
104 });
105 } else {
106 const params =
107 /** @type {Signature} */
108 (description.signature).params.map(
109 (param, k) => `p${k}${param.valtype}`
110 );
111
112 const mod = `${RuntimeGlobals.moduleCache}[${JSON.stringify(
113 chunkGraph.getModuleId(/** @type {Module} */ (importedModule))
114 )}]`;
115 const modExports = `${mod}.exports`;
116
117 const cache = `wasmImportedFuncCache${declarations.length}`;
118 declarations.push(`var ${cache};`);
119
120 const modCode =
121 /** @type {Module} */
122 (importedModule).type.startsWith("webassembly")
123 ? `${mod} ? ${modExports}[${JSON.stringify(usedName)}] : `
124 : "";
125
126 properties.push({
127 module,
128 name,
129 value: Template.asString([
130 `${modCode}function(${params}) {`,
131 Template.indent([
132 `if(${cache} === undefined) ${cache} = ${modExports};`,
133 `return ${cache}[${JSON.stringify(usedName)}](${params});`
134 ]),
135 "}"
136 ])
137 });
138 }
139 }
140
141 /** @type {string[]} */
142 let importObject;
143 if (mangle) {
144 importObject = [
145 "return {",
146 Template.indent([
147 properties
148 .map((p) => `${JSON.stringify(p.name)}: ${p.value}`)
149 .join(",\n")
150 ]),
151 "};"
152 ];
153 } else {
154 /** @type {Map<string, { name: string, value: string }[]>} */
155 const propertiesByModule = new Map();
156 for (const p of properties) {
157 let list = propertiesByModule.get(p.module);
158 if (list === undefined) {
159 propertiesByModule.set(p.module, (list = []));
160 }
161 list.push(p);
162 }
163 importObject = [
164 "return {",
165 Template.indent([
166 Array.from(propertiesByModule, ([module, list]) =>
167 Template.asString([
168 `${JSON.stringify(module)}: {`,
169 Template.indent([
170 list
171 .map((p) => `${JSON.stringify(p.name)}: ${p.value}`)
172 .join(",\n")
173 ]),
174 "}"
175 ])
176 ).join(",\n")
177 ]),
178 "};"
179 ];
180 }
181
182 const moduleIdStringified = JSON.stringify(chunkGraph.getModuleId(module));
183 if (waitForInstances.size === 1) {
184 const moduleId = [...waitForInstances.values()][0];
185 const promise = `installedWasmModules[${JSON.stringify(moduleId)}]`;
186 const variable = [...waitForInstances.keys()][0];
187 return Template.asString([
188 `${moduleIdStringified}: function() {`,
189 Template.indent([
190 `return promiseResolve().then(function() { return ${promise}; }).then(function(${variable}) {`,
191 Template.indent(importObject),
192 "});"
193 ]),
194 "},"
195 ]);
196 } else if (waitForInstances.size > 0) {
197 const promises = Array.from(
198 waitForInstances.values(),
199 (id) => `installedWasmModules[${JSON.stringify(id)}]`
200 ).join(", ");
201 const variables = Array.from(
202 waitForInstances.keys(),
203 (name, i) => `${name} = array[${i}]`
204 ).join(", ");
205 return Template.asString([
206 `${moduleIdStringified}: function() {`,
207 Template.indent([
208 `return promiseResolve().then(function() { return Promise.all([${promises}]); }).then(function(array) {`,
209 Template.indent([`var ${variables};`, ...importObject]),
210 "});"
211 ]),
212 "},"
213 ]);
214 }
215 return Template.asString([
216 `${moduleIdStringified}: function() {`,
217 Template.indent(importObject),
218 "},"
219 ]);
220};
221
222/**
223 * @typedef {object} WasmChunkLoadingRuntimeModuleOptions
224 * @property {(path: string) => string} generateLoadBinaryCode
225 * @property {boolean=} supportsStreaming
226 * @property {boolean=} mangleImports
227 * @property {ReadOnlyRuntimeRequirements} runtimeRequirements
228 */
229
230class WasmChunkLoadingRuntimeModule extends RuntimeModule {
231 /**
232 * @param {WasmChunkLoadingRuntimeModuleOptions} options options
233 */
234 constructor({
235 generateLoadBinaryCode,
236 supportsStreaming,
237 mangleImports,
238 runtimeRequirements
239 }) {
240 super("wasm chunk loading", RuntimeModule.STAGE_ATTACH);
241 this.generateLoadBinaryCode = generateLoadBinaryCode;
242 this.supportsStreaming = supportsStreaming;
243 this.mangleImports = mangleImports;
244 this._runtimeRequirements = runtimeRequirements;
245 }
246
247 /**
248 * Generates runtime code for this runtime module.
249 * @returns {string | null} runtime code
250 */
251 generate() {
252 const fn = RuntimeGlobals.ensureChunkHandlers;
253 const withHmr = this._runtimeRequirements.has(
254 RuntimeGlobals.hmrDownloadUpdateHandlers
255 );
256 const compilation = /** @type {Compilation} */ (this.compilation);
257 const { moduleGraph, outputOptions } = compilation;
258 const chunkGraph = /** @type {ChunkGraph} */ (this.chunkGraph);
259 const chunk = /** @type {Chunk} */ (this.chunk);
260 const wasmModules = getAllWasmModules(moduleGraph, chunkGraph, chunk);
261 const { mangleImports } = this;
262 /** @type {Declarations} */
263 const declarations = [];
264 const importObjects = wasmModules.map((module) =>
265 generateImportObject(
266 chunkGraph,
267 module,
268 mangleImports,
269 declarations,
270 chunk.runtime
271 )
272 );
273 const chunkModuleIdMap = chunkGraph.getChunkModuleIdMap(chunk, (m) =>
274 m.type.startsWith("webassembly")
275 );
276 /**
277 * @param {string} content content
278 * @returns {string} created import object
279 */
280 const createImportObject = (content) =>
281 mangleImports
282 ? `{ ${JSON.stringify(WebAssemblyUtils.MANGLED_MODULE)}: ${content} }`
283 : content;
284 const wasmModuleSrcPath = compilation.getPath(
285 JSON.stringify(outputOptions.webassemblyModuleFilename),
286 {
287 hash: `" + ${RuntimeGlobals.getFullHash}() + "`,
288 hashWithLength: (length) =>
289 `" + ${RuntimeGlobals.getFullHash}}().slice(0, ${length}) + "`,
290 module: {
291 id: '" + wasmModuleId + "',
292 hash: `" + ${JSON.stringify(
293 chunkGraph.getChunkModuleRenderedHashMap(chunk, (m) =>
294 m.type.startsWith("webassembly")
295 )
296 )}[chunkId][wasmModuleId] + "`,
297 hashWithLength(length) {
298 return `" + ${JSON.stringify(
299 chunkGraph.getChunkModuleRenderedHashMap(
300 chunk,
301 (m) => m.type.startsWith("webassembly"),
302 length
303 )
304 )}[chunkId][wasmModuleId] + "`;
305 }
306 },
307 runtime: chunk.runtime
308 }
309 );
310
311 const stateExpression = withHmr
312 ? `${RuntimeGlobals.hmrRuntimeStatePrefix}_wasm`
313 : undefined;
314
315 return Template.asString([
316 "// object to store loaded and loading wasm modules",
317 `var installedWasmModules = ${
318 stateExpression ? `${stateExpression} = ${stateExpression} || ` : ""
319 }{};`,
320 "",
321 // This function is used to delay reading the installed wasm module promises
322 // by a microtask. Sorting them doesn't help because there are edge cases where
323 // sorting is not possible (modules splitted into different chunks).
324 // So we not even trying and solve this by a microtask delay.
325 "function promiseResolve() { return Promise.resolve(); }",
326 "",
327 Template.asString(declarations),
328 "var wasmImportObjects = {",
329 Template.indent(importObjects),
330 "};",
331 "",
332 `var wasmModuleMap = ${JSON.stringify(
333 chunkModuleIdMap,
334 undefined,
335 "\t"
336 )};`,
337 "",
338 "// object with all WebAssembly.instance exports",
339 `${RuntimeGlobals.wasmInstances} = {};`,
340 "",
341 "// Fetch + compile chunk loading for webassembly",
342 `${fn}.wasm = function(chunkId, promises) {`,
343 Template.indent([
344 "",
345 "var wasmModules = wasmModuleMap[chunkId] || [];",
346 "",
347 "wasmModules.forEach(function(wasmModuleId, idx) {",
348 Template.indent([
349 "var installedWasmModuleData = installedWasmModules[wasmModuleId];",
350 "",
351 '// a Promise means "currently loading" or "already loaded".',
352 "if(installedWasmModuleData)",
353 Template.indent(["promises.push(installedWasmModuleData);"]),
354 "else {",
355 Template.indent([
356 "var importObject = wasmImportObjects[wasmModuleId]();",
357 `var req = ${this.generateLoadBinaryCode(wasmModuleSrcPath)};`,
358 "var promise;",
359 this.supportsStreaming
360 ? Template.asString([
361 "if(importObject && typeof importObject.then === 'function' && typeof WebAssembly.compileStreaming === 'function') {",
362 Template.indent([
363 "promise = Promise.all([WebAssembly.compileStreaming(req), importObject]).then(function(items) {",
364 Template.indent([
365 `return WebAssembly.instantiate(items[0], ${createImportObject(
366 "items[1]"
367 )});`
368 ]),
369 "});"
370 ]),
371 "} else if(typeof WebAssembly.instantiateStreaming === 'function') {",
372 Template.indent([
373 `promise = WebAssembly.instantiateStreaming(req, ${createImportObject(
374 "importObject"
375 )});`
376 ])
377 ])
378 : Template.asString([
379 "if(importObject && typeof importObject.then === 'function') {",
380 Template.indent([
381 "var bytesPromise = req.then(function(x) { return x.arrayBuffer(); });",
382 "promise = Promise.all([",
383 Template.indent([
384 "bytesPromise.then(function(bytes) { return WebAssembly.compile(bytes); }),",
385 "importObject"
386 ]),
387 "]).then(function(items) {",
388 Template.indent([
389 `return WebAssembly.instantiate(items[0], ${createImportObject(
390 "items[1]"
391 )});`
392 ]),
393 "});"
394 ])
395 ]),
396 "} else {",
397 Template.indent([
398 "var bytesPromise = req.then(function(x) { return x.arrayBuffer(); });",
399 "promise = bytesPromise.then(function(bytes) {",
400 Template.indent([
401 `return WebAssembly.instantiate(bytes, ${createImportObject(
402 "importObject"
403 )});`
404 ]),
405 "});"
406 ]),
407 "}",
408 "promises.push(installedWasmModules[wasmModuleId] = promise.then(function(res) {",
409 Template.indent([
410 `return ${RuntimeGlobals.wasmInstances}[wasmModuleId] = (res.instance || res).exports;`
411 ]),
412 "}));"
413 ]),
414 "}"
415 ]),
416 "});"
417 ]),
418 "};"
419 ]);
420 }
421}
422
423module.exports = WasmChunkLoadingRuntimeModule;
Note: See TracBrowser for help on using the repository browser.