1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | "use strict";
|
7 |
|
8 | const { RawSource } = require("webpack-sources");
|
9 | const Generator = require("../Generator");
|
10 | const WebAssemblyUtils = require("./WebAssemblyUtils");
|
11 |
|
12 | const t = require("@webassemblyjs/ast");
|
13 | const { moduleContextFromModuleAST } = require("@webassemblyjs/ast");
|
14 | const { editWithAST, addWithAST } = require("@webassemblyjs/wasm-edit");
|
15 | const { decode } = require("@webassemblyjs/wasm-parser");
|
16 |
|
17 | const WebAssemblyExportImportedDependency = require("../dependencies/WebAssemblyExportImportedDependency");
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 | const compose = (...fns) => {
|
39 | return fns.reduce(
|
40 | (prevFn, nextFn) => {
|
41 | return value => nextFn(prevFn(value));
|
42 | },
|
43 | value => value
|
44 | );
|
45 | };
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 | const removeStartFunc = state => bin => {
|
54 | return editWithAST(state.ast, bin, {
|
55 | Start(path) {
|
56 | path.remove();
|
57 | }
|
58 | });
|
59 | };
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 | const getImportedGlobals = ast => {
|
68 | const importedGlobals = [];
|
69 |
|
70 | t.traverse(ast, {
|
71 | ModuleImport({ node }) {
|
72 | if (t.isGlobalType(node.descr)) {
|
73 | importedGlobals.push(node);
|
74 | }
|
75 | }
|
76 | });
|
77 |
|
78 | return importedGlobals;
|
79 | };
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 | const getCountImportedFunc = ast => {
|
88 | let count = 0;
|
89 |
|
90 | t.traverse(ast, {
|
91 | ModuleImport({ node }) {
|
92 | if (t.isFuncImportDescr(node.descr)) {
|
93 | count++;
|
94 | }
|
95 | }
|
96 | });
|
97 |
|
98 | return count;
|
99 | };
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 | const getNextTypeIndex = ast => {
|
108 | const typeSectionMetadata = t.getSectionMetadata(ast, "type");
|
109 |
|
110 | if (typeSectionMetadata === undefined) {
|
111 | return t.indexLiteral(0);
|
112 | }
|
113 |
|
114 | return t.indexLiteral(typeSectionMetadata.vectorOfSize.value);
|
115 | };
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 | const getNextFuncIndex = (ast, countImportedFunc) => {
|
129 | const funcSectionMetadata = t.getSectionMetadata(ast, "func");
|
130 |
|
131 | if (funcSectionMetadata === undefined) {
|
132 | return t.indexLiteral(0 + countImportedFunc);
|
133 | }
|
134 |
|
135 | const vectorOfSize = funcSectionMetadata.vectorOfSize.value;
|
136 |
|
137 | return t.indexLiteral(vectorOfSize + countImportedFunc);
|
138 | };
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 | const createDefaultInitForGlobal = globalType => {
|
146 | if (globalType.valtype[0] === "i") {
|
147 |
|
148 | return t.objectInstruction("const", globalType.valtype, [
|
149 | t.numberLiteralFromRaw(66)
|
150 | ]);
|
151 | } else if (globalType.valtype[0] === "f") {
|
152 |
|
153 | return t.objectInstruction("const", globalType.valtype, [
|
154 | t.floatLiteral(66, false, false, "66")
|
155 | ]);
|
156 | } else {
|
157 | throw new Error("unknown type: " + globalType.valtype);
|
158 | }
|
159 | };
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 | const rewriteImportedGlobals = state => bin => {
|
175 | const additionalInitCode = state.additionalInitCode;
|
176 | const newGlobals = [];
|
177 |
|
178 | bin = editWithAST(state.ast, bin, {
|
179 | ModuleImport(path) {
|
180 | if (t.isGlobalType(path.node.descr)) {
|
181 | const globalType = path.node.descr;
|
182 |
|
183 | globalType.mutability = "var";
|
184 |
|
185 | const init = [
|
186 | createDefaultInitForGlobal(globalType),
|
187 | t.instruction("end")
|
188 | ];
|
189 |
|
190 | newGlobals.push(t.global(globalType, init));
|
191 |
|
192 | path.remove();
|
193 | }
|
194 | },
|
195 |
|
196 |
|
197 |
|
198 | Global(path) {
|
199 | const { node } = path;
|
200 | const [init] = node.init;
|
201 |
|
202 | if (init.id === "get_global") {
|
203 | node.globalType.mutability = "var";
|
204 |
|
205 | const initialGlobalIdx = init.args[0];
|
206 |
|
207 | node.init = [
|
208 | createDefaultInitForGlobal(node.globalType),
|
209 | t.instruction("end")
|
210 | ];
|
211 |
|
212 | additionalInitCode.push(
|
213 | |
214 |
|
215 |
|
216 |
|
217 |
|
218 | t.instruction("get_local", [initialGlobalIdx]),
|
219 | t.instruction("set_global", [t.indexLiteral(newGlobals.length)])
|
220 | );
|
221 | }
|
222 |
|
223 | newGlobals.push(node);
|
224 |
|
225 | path.remove();
|
226 | }
|
227 | });
|
228 |
|
229 |
|
230 | return addWithAST(state.ast, bin, newGlobals);
|
231 | };
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 |
|
239 |
|
240 |
|
241 |
|
242 |
|
243 | const rewriteExportNames =
|
244 | ({ ast, moduleGraph, module, externalExports, runtime }) =>
|
245 | bin => {
|
246 | return editWithAST(ast, bin, {
|
247 | ModuleExport(path) {
|
248 | const isExternal = externalExports.has(path.node.name);
|
249 | if (isExternal) {
|
250 | path.remove();
|
251 | return;
|
252 | }
|
253 | const usedName = moduleGraph
|
254 | .getExportsInfo(module)
|
255 | .getUsedName(path.node.name, runtime);
|
256 | if (!usedName) {
|
257 | path.remove();
|
258 | return;
|
259 | }
|
260 | path.node.name = usedName;
|
261 | }
|
262 | });
|
263 | };
|
264 |
|
265 |
|
266 |
|
267 |
|
268 |
|
269 |
|
270 |
|
271 |
|
272 | const rewriteImports =
|
273 | ({ ast, usedDependencyMap }) =>
|
274 | bin => {
|
275 | return editWithAST(ast, bin, {
|
276 | ModuleImport(path) {
|
277 | const result = usedDependencyMap.get(
|
278 | path.node.module + ":" + path.node.name
|
279 | );
|
280 |
|
281 | if (result !== undefined) {
|
282 | path.node.module = result.module;
|
283 | path.node.name = result.name;
|
284 | }
|
285 | }
|
286 | });
|
287 | };
|
288 |
|
289 |
|
290 |
|
291 |
|
292 |
|
293 |
|
294 |
|
295 |
|
296 |
|
297 |
|
298 |
|
299 |
|
300 |
|
301 |
|
302 |
|
303 |
|
304 | const addInitFunction =
|
305 | ({
|
306 | ast,
|
307 | initFuncId,
|
308 | startAtFuncOffset,
|
309 | importedGlobals,
|
310 | additionalInitCode,
|
311 | nextFuncIndex,
|
312 | nextTypeIndex
|
313 | }) =>
|
314 | bin => {
|
315 | const funcParams = importedGlobals.map(importedGlobal => {
|
316 |
|
317 | const id = t.identifier(
|
318 | `${importedGlobal.module}.${importedGlobal.name}`
|
319 | );
|
320 |
|
321 | return t.funcParam(importedGlobal.descr.valtype, id);
|
322 | });
|
323 |
|
324 | const funcBody = [];
|
325 | importedGlobals.forEach((importedGlobal, index) => {
|
326 | const args = [t.indexLiteral(index)];
|
327 | const body = [
|
328 | t.instruction("get_local", args),
|
329 | t.instruction("set_global", args)
|
330 | ];
|
331 |
|
332 | funcBody.push(...body);
|
333 | });
|
334 |
|
335 | if (typeof startAtFuncOffset === "number") {
|
336 | funcBody.push(
|
337 | t.callInstruction(t.numberLiteralFromRaw(startAtFuncOffset))
|
338 | );
|
339 | }
|
340 |
|
341 | for (const instr of additionalInitCode) {
|
342 | funcBody.push(instr);
|
343 | }
|
344 |
|
345 | funcBody.push(t.instruction("end"));
|
346 |
|
347 | const funcResults = [];
|
348 |
|
349 |
|
350 | const funcSignature = t.signature(funcParams, funcResults);
|
351 | const func = t.func(initFuncId, funcSignature, funcBody);
|
352 |
|
353 |
|
354 | const functype = t.typeInstruction(undefined, funcSignature);
|
355 |
|
356 |
|
357 | const funcindex = t.indexInFuncSection(nextTypeIndex);
|
358 |
|
359 |
|
360 | const moduleExport = t.moduleExport(
|
361 | initFuncId.value,
|
362 | t.moduleExportDescr("Func", nextFuncIndex)
|
363 | );
|
364 |
|
365 | return addWithAST(ast, bin, [func, moduleExport, funcindex, functype]);
|
366 | };
|
367 |
|
368 |
|
369 |
|
370 |
|
371 |
|
372 |
|
373 |
|
374 |
|
375 | const getUsedDependencyMap = (moduleGraph, module, mangle) => {
|
376 |
|
377 | const map = new Map();
|
378 | for (const usedDep of WebAssemblyUtils.getUsedDependencies(
|
379 | moduleGraph,
|
380 | module,
|
381 | mangle
|
382 | )) {
|
383 | const dep = usedDep.dependency;
|
384 | const request = dep.request;
|
385 | const exportName = dep.name;
|
386 | map.set(request + ":" + exportName, usedDep);
|
387 | }
|
388 | return map;
|
389 | };
|
390 |
|
391 | const TYPES = new Set(["webassembly"]);
|
392 |
|
393 | class WebAssemblyGenerator extends Generator {
|
394 | constructor(options) {
|
395 | super();
|
396 | this.options = options;
|
397 | }
|
398 |
|
399 | |
400 |
|
401 |
|
402 |
|
403 | getTypes(module) {
|
404 | return TYPES;
|
405 | }
|
406 |
|
407 | |
408 |
|
409 |
|
410 |
|
411 |
|
412 | getSize(module, type) {
|
413 | const originalSource = module.originalSource();
|
414 | if (!originalSource) {
|
415 | return 0;
|
416 | }
|
417 | return originalSource.size();
|
418 | }
|
419 |
|
420 | |
421 |
|
422 |
|
423 |
|
424 |
|
425 | generate(module, { moduleGraph, runtime }) {
|
426 | const bin = module.originalSource().source();
|
427 |
|
428 | const initFuncId = t.identifier("");
|
429 |
|
430 |
|
431 | const ast = decode(bin, {
|
432 | ignoreDataSection: true,
|
433 | ignoreCodeSection: true,
|
434 | ignoreCustomNameSection: true
|
435 | });
|
436 |
|
437 | const moduleContext = moduleContextFromModuleAST(ast.body[0]);
|
438 |
|
439 | const importedGlobals = getImportedGlobals(ast);
|
440 | const countImportedFunc = getCountImportedFunc(ast);
|
441 | const startAtFuncOffset = moduleContext.getStart();
|
442 | const nextFuncIndex = getNextFuncIndex(ast, countImportedFunc);
|
443 | const nextTypeIndex = getNextTypeIndex(ast);
|
444 |
|
445 | const usedDependencyMap = getUsedDependencyMap(
|
446 | moduleGraph,
|
447 | module,
|
448 | this.options.mangleImports
|
449 | );
|
450 | const externalExports = new Set(
|
451 | module.dependencies
|
452 | .filter(d => d instanceof WebAssemblyExportImportedDependency)
|
453 | .map(d => {
|
454 | const wasmDep = (
|
455 | d
|
456 | );
|
457 | return wasmDep.exportName;
|
458 | })
|
459 | );
|
460 |
|
461 |
|
462 | const additionalInitCode = [];
|
463 |
|
464 | const transform = compose(
|
465 | rewriteExportNames({
|
466 | ast,
|
467 | moduleGraph,
|
468 | module,
|
469 | externalExports,
|
470 | runtime
|
471 | }),
|
472 |
|
473 | removeStartFunc({ ast }),
|
474 |
|
475 | rewriteImportedGlobals({ ast, additionalInitCode }),
|
476 |
|
477 | rewriteImports({
|
478 | ast,
|
479 | usedDependencyMap
|
480 | }),
|
481 |
|
482 | addInitFunction({
|
483 | ast,
|
484 | initFuncId,
|
485 | importedGlobals,
|
486 | additionalInitCode,
|
487 | startAtFuncOffset,
|
488 | nextFuncIndex,
|
489 | nextTypeIndex
|
490 | })
|
491 | );
|
492 |
|
493 | const newBin = transform(bin);
|
494 |
|
495 | const newBuf = Buffer.from(newBin);
|
496 |
|
497 | return new RawSource(newBuf);
|
498 | }
|
499 | }
|
500 |
|
501 | module.exports = WebAssemblyGenerator;
|