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

main
Last change on this file was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 4 days ago

F4 Finalna Verzija

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