| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  | const { RawSource } = require("webpack-sources"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const { edit, add } = require("@webassemblyjs/wasm-edit"); | 
					
						
							|  |  |  | const { decode } = require("@webassemblyjs/wasm-parser"); | 
					
						
							|  |  |  | const t = require("@webassemblyjs/ast"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function compose(...fns) { | 
					
						
							|  |  |  | 	return fns.reverse().reduce((prevFn, nextFn) => { | 
					
						
							|  |  |  | 		return value => nextFn(prevFn(value)); | 
					
						
							|  |  |  | 	}, value => value); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | // Utility functions
 | 
					
						
							| 
									
										
										
										
											2018-03-09 19:04:27 +08:00
										 |  |  | const isGlobalImport = moduleImport => moduleImport.descr.type === "GlobalType"; | 
					
						
							| 
									
										
										
										
											2018-03-29 20:53:59 +08:00
										 |  |  | const initFuncId = t.identifier("__webpack_init__"); | 
					
						
							| 
									
										
										
										
											2018-03-09 19:04:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  |  * Removes the start instruction | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2018-03-14 21:49:33 +08:00
										 |  |  |  * @param {Object} state - unused state | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  |  * @returns {ArrayBuffer} bin' | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | const removeStartFunc = state => bin => { | 
					
						
							|  |  |  | 	return edit(bin, { | 
					
						
							|  |  |  | 		Start(path) { | 
					
						
							|  |  |  | 			path.remove(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-15 01:11:13 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Retrieve the start function | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param {Object} ast - Module's AST | 
					
						
							|  |  |  |  * @returns {t.Identifier | undefined} - node if any | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | function getStartFuncIndex(ast) { | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  | 	let startAtFuncIndex; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | 	t.traverse(ast, { | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  | 		Start(path) { | 
					
						
							|  |  |  | 			startAtFuncIndex = path.node.index; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | 	return startAtFuncIndex; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-15 01:11:13 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Get imported globals | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param {Object} ast - Module's AST | 
					
						
							|  |  |  |  * @returns {Array<t.ModuleImport>} - nodes | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | function getImportedGlobals(ast) { | 
					
						
							|  |  |  | 	const importedGlobals = []; | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | 	t.traverse(ast, { | 
					
						
							|  |  |  | 		ModuleImport({ node }) { | 
					
						
							|  |  |  | 			if (isGlobalImport(node) === true) { | 
					
						
							|  |  |  | 				importedGlobals.push(node); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | 	return importedGlobals; | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-15 01:11:13 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Get next func index | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2018-03-26 17:01:12 +08:00
										 |  |  |  * Funcs are referenced by their index in the type section, we just return the | 
					
						
							|  |  |  |  * next index. | 
					
						
							| 
									
										
										
										
											2018-03-15 01:11:13 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * @param {Object} ast - Module's AST | 
					
						
							|  |  |  |  * @returns {t.indexLiteral} - index | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function getNextFuncIndex(ast) { | 
					
						
							| 
									
										
										
										
											2018-03-26 17:01:12 +08:00
										 |  |  | 	const typeSectionMetadata = t.getSectionMetadata(ast, "type"); | 
					
						
							| 
									
										
										
										
											2018-03-15 01:11:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-26 17:01:12 +08:00
										 |  |  | 	if (typeof typeSectionMetadata === "undefined") { | 
					
						
							|  |  |  | 		return t.indexLiteral(0); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-15 01:11:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-26 17:01:12 +08:00
										 |  |  | 	return t.indexLiteral(typeSectionMetadata.vectorOfSize); | 
					
						
							| 
									
										
										
										
											2018-03-15 01:11:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  |  * Rewrite the import globals: | 
					
						
							|  |  |  |  * - removes the ModuleImport instruction | 
					
						
							|  |  |  |  * - injects at the same offset a mutable global of the same time | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Since the imported globals are before the other global declarations, our | 
					
						
							|  |  |  |  * indices will be preserved. | 
					
						
							| 
									
										
										
										
											2018-03-09 19:04:27 +08:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  |  * Note that globals will become mutable. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2018-03-14 21:49:33 +08:00
										 |  |  |  * @param {Object} state - unused state | 
					
						
							| 
									
										
										
										
											2018-03-09 19:04:27 +08:00
										 |  |  |  * @returns {ArrayBuffer} bin' | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-03-13 00:22:05 +08:00
										 |  |  | const rewriteImportedGlobals = state => bin => { | 
					
						
							|  |  |  | 	const newGlobals = []; | 
					
						
							| 
									
										
										
										
											2018-03-10 02:03:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-13 00:22:05 +08:00
										 |  |  | 	bin = edit(bin, { | 
					
						
							|  |  |  | 		ModuleImport(path) { | 
					
						
							|  |  |  | 			if (isGlobalImport(path.node) === true) { | 
					
						
							|  |  |  | 				const globalType = path.node.descr; | 
					
						
							| 
									
										
										
										
											2018-03-10 02:03:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-13 00:22:05 +08:00
										 |  |  | 				globalType.mutability = "var"; | 
					
						
							| 
									
										
										
										
											2018-03-10 02:03:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-13 00:22:05 +08:00
										 |  |  | 				newGlobals.push( | 
					
						
							|  |  |  | 					t.global(globalType, [ | 
					
						
							| 
									
										
										
										
											2018-03-10 02:03:33 +08:00
										 |  |  | 						t.objectInstruction("const", "i32", [t.numberLiteral(0)]) | 
					
						
							| 
									
										
										
										
											2018-03-13 00:22:05 +08:00
										 |  |  | 					]) | 
					
						
							|  |  |  | 				); | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-13 00:22:05 +08:00
										 |  |  | 				path.remove(); | 
					
						
							| 
									
										
										
										
											2018-03-09 19:04:27 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-03-13 00:22:05 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-13 00:22:05 +08:00
										 |  |  | 	// Add global declaration instructions
 | 
					
						
							|  |  |  | 	return add(bin, newGlobals); | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-03-09 19:04:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  |  * Add an init function. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The init function fills the globals given input arguments. | 
					
						
							| 
									
										
										
										
											2018-03-09 19:04:27 +08:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2018-03-14 21:49:33 +08:00
										 |  |  |  * @param {Object} state - transformation state | 
					
						
							| 
									
										
										
										
											2018-03-09 19:04:27 +08:00
										 |  |  |  * @returns {ArrayBuffer} bin' | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | const addInitFunction = ({ | 
					
						
							|  |  |  | 	startAtFuncIndex, | 
					
						
							|  |  |  | 	importedGlobals, | 
					
						
							| 
									
										
										
										
											2018-03-15 01:11:13 +08:00
										 |  |  | 	funcSectionMetadata, | 
					
						
							|  |  |  | 	nextFuncIndex | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | }) => bin => { | 
					
						
							|  |  |  | 	const funcParams = importedGlobals.map(importedGlobal => { | 
					
						
							|  |  |  | 		// used for debugging
 | 
					
						
							|  |  |  | 		const id = t.identifier(`${importedGlobal.module}.${importedGlobal.name}`); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return t.funcParam(importedGlobal.descr.valtype, id); | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | 	const funcBody = importedGlobals.reduce((acc, importedGlobal, index) => { | 
					
						
							|  |  |  | 		const args = [t.indexLiteral(index)]; | 
					
						
							|  |  |  | 		const body = [ | 
					
						
							|  |  |  | 			t.instruction("get_local", args), | 
					
						
							|  |  |  | 			t.instruction("set_global", args) | 
					
						
							|  |  |  | 		]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return [...acc, ...body]; | 
					
						
							|  |  |  | 	}, []); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (typeof startAtFuncIndex !== "undefined") { | 
					
						
							|  |  |  | 		funcBody.push(t.callInstruction(startAtFuncIndex)); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const funcResults = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const func = t.func(initFuncId, funcParams, funcResults, funcBody); | 
					
						
							| 
									
										
										
										
											2018-03-10 02:03:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	const functype = t.typeInstructionFunc(func.params, func.result); | 
					
						
							| 
									
										
										
										
											2018-03-15 01:11:13 +08:00
										 |  |  | 	const funcindex = t.indexInFuncSection(nextFuncIndex); | 
					
						
							| 
									
										
										
										
											2018-03-10 02:03:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-15 01:11:13 +08:00
										 |  |  | 	const moduleExport = t.moduleExport(initFuncId.value, "Func", nextFuncIndex); | 
					
						
							| 
									
										
										
										
											2018-03-10 02:03:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return add(bin, [func, moduleExport, funcindex, functype]); | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | class WebAssemblyGenerator { | 
					
						
							|  |  |  | 	generate(module) { | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  | 		const bin = module.originalSource().source(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | 		const ast = decode(bin, { | 
					
						
							|  |  |  | 			ignoreDataSection: true | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const importedGlobals = getImportedGlobals(ast); | 
					
						
							|  |  |  | 		const funcSectionMetadata = t.getSectionMetadata(ast, "func"); | 
					
						
							|  |  |  | 		const startAtFuncIndex = getStartFuncIndex(ast); | 
					
						
							| 
									
										
										
										
											2018-03-15 01:11:13 +08:00
										 |  |  | 		const nextFuncIndex = getNextFuncIndex(ast); | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		const transform = compose( | 
					
						
							|  |  |  | 			removeStartFunc(), | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-13 00:22:05 +08:00
										 |  |  | 			rewriteImportedGlobals(), | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			addInitFunction({ | 
					
						
							|  |  |  | 				importedGlobals, | 
					
						
							|  |  |  | 				funcSectionMetadata, | 
					
						
							| 
									
										
										
										
											2018-03-15 01:11:13 +08:00
										 |  |  | 				startAtFuncIndex, | 
					
						
							|  |  |  | 				nextFuncIndex | 
					
						
							| 
									
										
										
										
											2018-03-12 18:43:20 +08:00
										 |  |  | 			}) | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  | 		const newBin = transform(bin); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return new RawSource(newBin); | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = WebAssemblyGenerator; |