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