source: frontend/node_modules/webpack/lib/wasm-sync/WebAssemblyGenerator.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: 14.8 KB
RevLine 
[9af201e]1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const t = require("@webassemblyjs/ast");
9const { moduleContextFromModuleAST } = require("@webassemblyjs/ast");
10const { addWithAST, editWithAST } = require("@webassemblyjs/wasm-edit");
11const { decode } = require("@webassemblyjs/wasm-parser");
12const { RawSource } = require("webpack-sources");
13const Generator = require("../Generator");
14const { WEBASSEMBLY_TYPES } = require("../ModuleSourceTypeConstants");
15const WebAssemblyExportImportedDependency = require("../dependencies/WebAssemblyExportImportedDependency");
16const WebAssemblyUtils = require("./WebAssemblyUtils");
17
18/** @typedef {import("webpack-sources").Source} Source */
19/** @typedef {import("../Generator").GenerateContext} GenerateContext */
20/** @typedef {import("../Generator").UpdateHashContext} UpdateHashContext */
21/** @typedef {import("../Module")} Module */
22/** @typedef {import("../Module").SourceType} SourceType */
23/** @typedef {import("../Module").SourceTypes} SourceTypes */
24/** @typedef {import("../ModuleGraph")} ModuleGraph */
25/** @typedef {import("../NormalModule")} NormalModule */
26/** @typedef {import("../util/Hash")} Hash */
27/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
28/** @typedef {import("./WebAssemblyUtils").UsedWasmDependency} UsedWasmDependency */
29/** @typedef {import("@webassemblyjs/ast").Instruction} Instruction */
30/** @typedef {import("@webassemblyjs/ast").ModuleImport} ModuleImport */
31/** @typedef {import("@webassemblyjs/ast").ModuleExport} ModuleExport */
32/** @typedef {import("@webassemblyjs/ast").Global} Global */
33/** @typedef {import("@webassemblyjs/ast").AST} AST */
34/** @typedef {import("@webassemblyjs/ast").GlobalType} GlobalType */
35/**
36 * Defines the node path type used by this module.
37 * @template T
38 * @typedef {import("@webassemblyjs/ast").NodePath<T>} NodePath
39 */
40
41/**
42 * Defines the array buffer transform type used by this module.
43 * @typedef {(buf: ArrayBuffer) => ArrayBuffer} ArrayBufferTransform
44 */
45
46/**
47 * Returns composed transform.
48 * @template T
49 * @param {((prev: ArrayBuffer) => ArrayBuffer)[]} fns transforms
50 * @returns {(buf: ArrayBuffer) => ArrayBuffer} composed transform
51 */
52const compose = (...fns) =>
53 fns.reduce(
54 (prevFn, nextFn) => (value) => nextFn(prevFn(value)),
55 (value) => value
56 );
57
58/**
59 * Removes start func.
60 * @param {object} state state
61 * @param {AST} state.ast Module's ast
62 * @returns {ArrayBufferTransform} transform
63 */
64const removeStartFunc = (state) => (bin) =>
65 editWithAST(state.ast, bin, {
66 Start(path) {
67 path.remove();
68 }
69 });
70
71/**
72 * Get imported globals
73 * @param {AST} ast Module's AST
74 * @returns {t.ModuleImport[]} - nodes
75 */
76const getImportedGlobals = (ast) => {
77 /** @type {t.ModuleImport[]} */
78 const importedGlobals = [];
79
80 t.traverse(ast, {
81 ModuleImport({ node }) {
82 if (t.isGlobalType(node.descr)) {
83 importedGlobals.push(node);
84 }
85 }
86 });
87
88 return importedGlobals;
89};
90
91/**
92 * Get the count for imported func
93 * @param {AST} ast Module's AST
94 * @returns {number} - count
95 */
96const getCountImportedFunc = (ast) => {
97 let count = 0;
98
99 t.traverse(ast, {
100 ModuleImport({ node }) {
101 if (t.isFuncImportDescr(node.descr)) {
102 count++;
103 }
104 }
105 });
106
107 return count;
108};
109
110/**
111 * Get next type index
112 * @param {AST} ast Module's AST
113 * @returns {t.Index} - index
114 */
115const getNextTypeIndex = (ast) => {
116 const typeSectionMetadata = t.getSectionMetadata(ast, "type");
117
118 if (typeSectionMetadata === undefined) {
119 return t.indexLiteral(0);
120 }
121
122 return t.indexLiteral(typeSectionMetadata.vectorOfSize.value);
123};
124
125/**
126 * Get next func index
127 * The Func section metadata provide information for implemented funcs
128 * in order to have the correct index we shift the index by number of external
129 * functions.
130 * @param {AST} ast Module's AST
131 * @param {number} countImportedFunc number of imported funcs
132 * @returns {t.Index} - index
133 */
134const getNextFuncIndex = (ast, countImportedFunc) => {
135 const funcSectionMetadata = t.getSectionMetadata(ast, "func");
136
137 if (funcSectionMetadata === undefined) {
138 return t.indexLiteral(0 + countImportedFunc);
139 }
140
141 const vectorOfSize = funcSectionMetadata.vectorOfSize.value;
142
143 return t.indexLiteral(vectorOfSize + countImportedFunc);
144};
145
146/**
147 * Creates an init instruction for a global type
148 * @param {t.GlobalType} globalType the global type
149 * @returns {t.Instruction} init expression
150 */
151const createDefaultInitForGlobal = (globalType) => {
152 if (globalType.valtype[0] === "i") {
153 // create NumberLiteral global initializer
154 return t.objectInstruction("const", globalType.valtype, [
155 t.numberLiteralFromRaw(66)
156 ]);
157 } else if (globalType.valtype[0] === "f") {
158 // create FloatLiteral global initializer
159 return t.objectInstruction("const", globalType.valtype, [
160 t.floatLiteral(66, false, false, "66")
161 ]);
162 }
163 throw new Error(`unknown type: ${globalType.valtype}`);
164};
165
166/**
167 * Rewrite the import globals:
168 * - removes the ModuleImport instruction
169 * - injects at the same offset a mutable global of the same type
170 *
171 * Since the imported globals are before the other global declarations, our
172 * indices will be preserved.
173 *
174 * Note that globals will become mutable.
175 * @param {object} state transformation state
176 * @param {AST} state.ast Module's ast
177 * @param {t.Instruction[]} state.additionalInitCode list of addition instructions for the init function
178 * @returns {ArrayBufferTransform} transform
179 */
180const rewriteImportedGlobals = (state) => (bin) => {
181 const additionalInitCode = state.additionalInitCode;
182 /** @type {t.Global[]} */
183 const newGlobals = [];
184
185 bin = editWithAST(state.ast, bin, {
186 ModuleImport(path) {
187 if (t.isGlobalType(path.node.descr)) {
188 const globalType =
189 /** @type {GlobalType} */
190 (path.node.descr);
191
192 globalType.mutability = "var";
193
194 const init = [
195 createDefaultInitForGlobal(globalType),
196 t.instruction("end")
197 ];
198
199 newGlobals.push(t.global(globalType, init));
200
201 path.remove();
202 }
203 },
204
205 // in order to preserve non-imported global's order we need to re-inject
206 // those as well
207 /**
208 * Processes the provided path.
209 * @param {NodePath<Global>} path path
210 */
211 Global(path) {
212 const { node } = path;
213 const [init] = node.init;
214
215 if (init.id === "get_global") {
216 node.globalType.mutability = "var";
217
218 const initialGlobalIdx = init.args[0];
219
220 node.init = [
221 createDefaultInitForGlobal(node.globalType),
222 t.instruction("end")
223 ];
224
225 additionalInitCode.push(
226 /**
227 * get_global in global initializer only works for imported globals.
228 * They have the same indices as the init params, so use the
229 * same index.
230 */
231 t.instruction("get_local", [initialGlobalIdx]),
232 t.instruction("set_global", [t.indexLiteral(newGlobals.length)])
233 );
234 }
235
236 newGlobals.push(node);
237
238 path.remove();
239 }
240 });
241
242 // Add global declaration instructions
243 return addWithAST(state.ast, bin, newGlobals);
244};
245
246/**
247 * Rewrite the export names
248 * @param {object} state state
249 * @param {AST} state.ast Module's ast
250 * @param {Module} state.module Module
251 * @param {ModuleGraph} state.moduleGraph module graph
252 * @param {Set<string>} state.externalExports Module
253 * @param {RuntimeSpec} state.runtime runtime
254 * @returns {ArrayBufferTransform} transform
255 */
256const rewriteExportNames =
257 ({ ast, moduleGraph, module, externalExports, runtime }) =>
258 (bin) =>
259 editWithAST(ast, bin, {
260 /**
261 * Processes the provided path.
262 * @param {NodePath<ModuleExport>} path path
263 */
264 ModuleExport(path) {
265 const isExternal = externalExports.has(path.node.name);
266 if (isExternal) {
267 path.remove();
268 return;
269 }
270 const usedName = moduleGraph
271 .getExportsInfo(module)
272 .getUsedName(path.node.name, runtime);
273 if (!usedName) {
274 path.remove();
275 return;
276 }
277 path.node.name = /** @type {string} */ (usedName);
278 }
279 });
280
281/** @typedef {Map<string, UsedWasmDependency>} Mapping */
282
283/**
284 * Mangle import names and modules
285 * @param {object} state state
286 * @param {AST} state.ast Module's ast
287 * @param {Mapping} state.usedDependencyMap mappings to mangle names
288 * @returns {ArrayBufferTransform} transform
289 */
290const rewriteImports =
291 ({ ast, usedDependencyMap }) =>
292 (bin) =>
293 editWithAST(ast, bin, {
294 /**
295 * Processes the provided path.
296 * @param {NodePath<ModuleImport>} path path
297 */
298 ModuleImport(path) {
299 const result = usedDependencyMap.get(
300 `${path.node.module}:${path.node.name}`
301 );
302
303 if (result !== undefined) {
304 path.node.module = result.module;
305 path.node.name = result.name;
306 }
307 }
308 });
309
310/**
311 * Add an init function.
312 *
313 * The init function fills the globals given input arguments.
314 * @param {object} state transformation state
315 * @param {AST} state.ast Module's ast
316 * @param {t.Identifier} state.initFuncId identifier of the init function
317 * @param {t.Index} state.startAtFuncOffset index of the start function
318 * @param {t.ModuleImport[]} state.importedGlobals list of imported globals
319 * @param {t.Instruction[]} state.additionalInitCode list of addition instructions for the init function
320 * @param {t.Index} state.nextFuncIndex index of the next function
321 * @param {t.Index} state.nextTypeIndex index of the next type
322 * @returns {ArrayBufferTransform} transform
323 */
324const addInitFunction =
325 ({
326 ast,
327 initFuncId,
328 startAtFuncOffset,
329 importedGlobals,
330 additionalInitCode,
331 nextFuncIndex,
332 nextTypeIndex
333 }) =>
334 (bin) => {
335 const funcParams = importedGlobals.map((importedGlobal) => {
336 // used for debugging
337 const id = t.identifier(
338 `${importedGlobal.module}.${importedGlobal.name}`
339 );
340
341 return t.funcParam(
342 /** @type {string} */ (importedGlobal.descr.valtype),
343 id
344 );
345 });
346
347 /** @type {Instruction[]} */
348 const funcBody = [];
349 for (const [index, _importedGlobal] of importedGlobals.entries()) {
350 const args = [t.indexLiteral(index)];
351 const body = [
352 t.instruction("get_local", args),
353 t.instruction("set_global", args)
354 ];
355
356 funcBody.push(...body);
357 }
358
359 if (typeof startAtFuncOffset === "number") {
360 funcBody.push(
361 t.callInstruction(t.numberLiteralFromRaw(startAtFuncOffset))
362 );
363 }
364
365 for (const instr of additionalInitCode) {
366 funcBody.push(instr);
367 }
368
369 funcBody.push(t.instruction("end"));
370
371 /** @type {string[]} */
372 const funcResults = [];
373
374 // Code section
375 const funcSignature = t.signature(funcParams, funcResults);
376 const func = t.func(initFuncId, funcSignature, funcBody);
377
378 // Type section
379 const functype = t.typeInstruction(undefined, funcSignature);
380
381 // Func section
382 const funcindex = t.indexInFuncSection(nextTypeIndex);
383
384 // Export section
385 const moduleExport = t.moduleExport(
386 initFuncId.value,
387 t.moduleExportDescr("Func", nextFuncIndex)
388 );
389
390 return addWithAST(ast, bin, [func, moduleExport, funcindex, functype]);
391 };
392
393/**
394 * Extract mangle mappings from module
395 * @param {ModuleGraph} moduleGraph module graph
396 * @param {Module} module current module
397 * @param {boolean=} mangle mangle imports
398 * @returns {Mapping} mappings to mangled names
399 */
400const getUsedDependencyMap = (moduleGraph, module, mangle) => {
401 /** @type {Mapping} */
402 const map = new Map();
403 for (const usedDep of WebAssemblyUtils.getUsedDependencies(
404 moduleGraph,
405 module,
406 mangle
407 )) {
408 const dep = usedDep.dependency;
409 const request = dep.request;
410 const exportName = dep.name;
411 map.set(`${request}:${exportName}`, usedDep);
412 }
413 return map;
414};
415
416/**
417 * Represents the web assembly generator runtime component.
418 * @typedef {object} WebAssemblyGeneratorOptions
419 * @property {boolean=} mangleImports mangle imports
420 */
421
422class WebAssemblyGenerator extends Generator {
423 /**
424 * Creates an instance of WebAssemblyGenerator.
425 * @param {WebAssemblyGeneratorOptions} options options
426 */
427 constructor(options) {
428 super();
429 this.options = options;
430 }
431
432 /**
433 * Returns the source types available for this module.
434 * @param {NormalModule} module fresh module
435 * @returns {SourceTypes} available types (do not mutate)
436 */
437 getTypes(module) {
438 return WEBASSEMBLY_TYPES;
439 }
440
441 /**
442 * Returns the estimated size for the requested source type.
443 * @param {NormalModule} module the module
444 * @param {SourceType=} type source type
445 * @returns {number} estimate size of the module
446 */
447 getSize(module, type) {
448 const originalSource = module.originalSource();
449 if (!originalSource) {
450 return 0;
451 }
452 return originalSource.size();
453 }
454
455 /**
456 * Generates generated code for this runtime module.
457 * @param {NormalModule} module module for which the code should be generated
458 * @param {GenerateContext} generateContext context for generate
459 * @returns {Source | null} generated code
460 */
461 generate(module, { moduleGraph, runtime }) {
462 const bin =
463 /** @type {Buffer} */
464 (/** @type {Source} */ (module.originalSource()).source());
465
466 const initFuncId = t.identifier("");
467
468 // parse it
469 const ast = decode(bin, {
470 ignoreDataSection: true,
471 ignoreCodeSection: true,
472 ignoreCustomNameSection: true
473 });
474
475 const moduleContext = moduleContextFromModuleAST(ast.body[0]);
476
477 const importedGlobals = getImportedGlobals(ast);
478 const countImportedFunc = getCountImportedFunc(ast);
479 const startAtFuncOffset = moduleContext.getStart();
480 const nextFuncIndex = getNextFuncIndex(ast, countImportedFunc);
481 const nextTypeIndex = getNextTypeIndex(ast);
482
483 const usedDependencyMap = getUsedDependencyMap(
484 moduleGraph,
485 module,
486 this.options.mangleImports
487 );
488 const externalExports = new Set(
489 module.dependencies
490 .filter((d) => d instanceof WebAssemblyExportImportedDependency)
491 .map((d) => {
492 const wasmDep = /** @type {WebAssemblyExportImportedDependency} */ (
493 d
494 );
495 return wasmDep.exportName;
496 })
497 );
498
499 /** @type {t.Instruction[]} */
500 const additionalInitCode = [];
501
502 const transform = compose(
503 rewriteExportNames({
504 ast,
505 moduleGraph,
506 module,
507 externalExports,
508 runtime
509 }),
510
511 removeStartFunc({ ast }),
512
513 rewriteImportedGlobals({ ast, additionalInitCode }),
514
515 rewriteImports({
516 ast,
517 usedDependencyMap
518 }),
519
520 addInitFunction({
521 ast,
522 initFuncId,
523 importedGlobals,
524 additionalInitCode,
525 startAtFuncOffset,
526 nextFuncIndex,
527 nextTypeIndex
528 })
529 );
530
531 const newBin = transform(/** @type {ArrayBuffer} */ (bin.buffer));
532 const newBuf = Buffer.from(newBin);
533
534 return new RawSource(newBuf);
535 }
536
537 /**
538 * Generates fallback output for the provided error condition.
539 * @param {Error} error the error
540 * @param {NormalModule} module module for which the code should be generated
541 * @param {GenerateContext} generateContext context for generate
542 * @returns {Source | null} generated code
543 */
544 generateError(error, module, generateContext) {
545 return new RawSource(error.message);
546 }
547
548 /**
549 * Updates the hash with the data contributed by this instance.
550 * @param {Hash} hash hash that will be modified
551 * @param {UpdateHashContext} updateHashContext context for updating hash
552 */
553 updateHash(hash, updateHashContext) {
554 if (this.options.mangleImports) {
555 hash.update("mangle-imports");
556 }
557 }
558}
559
560module.exports = WebAssemblyGenerator;
Note: See TracBrowser for help on using the repository browser.