| 
									
										
										
										
											2017-10-30 20:56:57 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							| 
									
										
										
										
											2018-07-30 23:08:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-30 20:56:57 +08:00
										 |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-28 00:53:07 +08:00
										 |  |  | const t = require("@webassemblyjs/ast"); | 
					
						
							| 
									
										
										
										
											2018-05-30 18:45:05 +08:00
										 |  |  | const { | 
					
						
							|  |  |  | 	moduleContextFromModuleAST | 
					
						
							|  |  |  | } = require("@webassemblyjs/helper-module-context"); | 
					
						
							| 
									
										
										
										
											2018-07-30 23:08:51 +08:00
										 |  |  | const { decode } = require("@webassemblyjs/wasm-parser"); | 
					
						
							| 
									
										
										
										
											2019-11-30 03:24:13 +08:00
										 |  |  | const Parser = require("../Parser"); | 
					
						
							| 
									
										
										
										
											2018-12-30 18:50:04 +08:00
										 |  |  | const StaticExportsDependency = require("../dependencies/StaticExportsDependency"); | 
					
						
							| 
									
										
										
										
											2018-06-02 15:53:35 +08:00
										 |  |  | const WebAssemblyExportImportedDependency = require("../dependencies/WebAssemblyExportImportedDependency"); | 
					
						
							| 
									
										
										
										
											2018-07-30 23:08:51 +08:00
										 |  |  | const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency"); | 
					
						
							| 
									
										
										
										
											2017-10-30 20:56:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-11 02:19:30 +08:00
										 |  |  | /** @typedef {import("../Module")} Module */ | 
					
						
							| 
									
										
										
										
											2019-11-30 03:24:13 +08:00
										 |  |  | /** @typedef {import("../Parser").ParserState} ParserState */ | 
					
						
							|  |  |  | /** @typedef {import("../Parser").PreparsedAst} PreparsedAst */ | 
					
						
							| 
									
										
										
										
											2018-05-11 02:19:30 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-11 20:24:08 +08:00
										 |  |  | const JS_COMPAT_TYPES = new Set(["i32", "f32", "f64"]); | 
					
						
							| 
									
										
										
										
											2018-05-11 00:07:24 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2018-06-04 22:23:41 +08:00
										 |  |  |  * @param {t.Signature} signature the func signature | 
					
						
							| 
									
										
										
										
											2018-05-11 00:07:24 +08:00
										 |  |  |  * @returns {null | string} the type incompatible with js types | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-06-04 22:23:41 +08:00
										 |  |  | const getJsIncompatibleType = signature => { | 
					
						
							| 
									
										
										
										
											2018-05-11 00:07:24 +08:00
										 |  |  | 	for (const param of signature.params) { | 
					
						
							| 
									
										
										
										
											2018-06-04 22:23:41 +08:00
										 |  |  | 		if (!JS_COMPAT_TYPES.has(param.valtype)) { | 
					
						
							| 
									
										
										
										
											2018-05-11 00:07:24 +08:00
										 |  |  | 			return `${param.valtype} as parameter`; | 
					
						
							| 
									
										
										
										
											2018-06-04 22:23:41 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-05-11 00:07:24 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	for (const type of signature.results) { | 
					
						
							|  |  |  | 		if (!JS_COMPAT_TYPES.has(type)) return `${type} as result`; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return null; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-04 22:23:41 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * TODO why are there two different Signature types? | 
					
						
							|  |  |  |  * @param {t.FuncSignature} signature the func signature | 
					
						
							|  |  |  |  * @returns {null | string} the type incompatible with js types | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const getJsIncompatibleTypeOfFuncSignature = signature => { | 
					
						
							|  |  |  | 	for (const param of signature.args) { | 
					
						
							|  |  |  | 		if (!JS_COMPAT_TYPES.has(param)) { | 
					
						
							|  |  |  | 			return `${param} as parameter`; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for (const type of signature.result) { | 
					
						
							|  |  |  | 		if (!JS_COMPAT_TYPES.has(type)) return `${type} as result`; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return null; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-28 15:50:29 +08:00
										 |  |  | const decoderOpts = { | 
					
						
							|  |  |  | 	ignoreCodeSection: true, | 
					
						
							| 
									
										
										
										
											2018-05-30 18:45:05 +08:00
										 |  |  | 	ignoreDataSection: true, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// this will avoid having to lookup with identifiers in the ModuleContext
 | 
					
						
							|  |  |  | 	ignoreCustomNameSection: true | 
					
						
							| 
									
										
										
										
											2018-02-28 15:50:29 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-30 03:24:13 +08:00
										 |  |  | class WebAssemblyParser extends Parser { | 
					
						
							| 
									
										
										
										
											2017-10-30 20:56:57 +08:00
										 |  |  | 	constructor(options) { | 
					
						
							| 
									
										
										
										
											2019-11-30 03:24:13 +08:00
										 |  |  | 		super(); | 
					
						
							| 
									
										
										
										
											2018-07-30 20:25:40 +08:00
										 |  |  | 		this.hooks = Object.freeze({}); | 
					
						
							| 
									
										
										
										
											2017-10-30 20:56:57 +08:00
										 |  |  | 		this.options = options; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-30 03:24:13 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string | Buffer | PreparsedAst} source the source to parse | 
					
						
							|  |  |  | 	 * @param {ParserState} state the parser state | 
					
						
							|  |  |  | 	 * @returns {ParserState} the parser state | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	parse(source, state) { | 
					
						
							|  |  |  | 		if (!Buffer.isBuffer(source)) { | 
					
						
							|  |  |  | 			throw new Error("WebAssemblyParser input must be a Buffer"); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-14 08:21:44 +08:00
										 |  |  | 		// flag it as ESM
 | 
					
						
							| 
									
										
										
										
											2019-11-30 03:24:13 +08:00
										 |  |  | 		state.module.buildInfo.strict = true; | 
					
						
							| 
									
										
										
										
											2017-12-23 01:23:20 +08:00
										 |  |  | 		state.module.buildMeta.exportsType = "namespace"; | 
					
						
							| 
									
										
										
										
											2017-12-14 08:21:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-20 22:03:35 +08:00
										 |  |  | 		// parse it
 | 
					
						
							| 
									
										
										
										
											2019-11-30 03:24:13 +08:00
										 |  |  | 		const program = decode(source, decoderOpts); | 
					
						
							| 
									
										
										
										
											2018-05-30 18:45:05 +08:00
										 |  |  | 		const module = program.body[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const moduleContext = moduleContextFromModuleAST(module); | 
					
						
							| 
									
										
										
										
											2018-02-20 22:03:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// extract imports and exports
 | 
					
						
							| 
									
										
										
										
											2018-12-30 16:03:42 +08:00
										 |  |  | 		const exports = []; | 
					
						
							| 
									
										
										
										
											2018-10-18 23:35:36 +08:00
										 |  |  | 		let jsIncompatibleExports = (state.module.buildMeta.jsIncompatibleExports = undefined); | 
					
						
							| 
									
										
										
										
											2018-05-30 18:45:05 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-02 15:53:35 +08:00
										 |  |  | 		const importedGlobals = []; | 
					
						
							| 
									
										
										
										
											2018-05-30 18:45:05 +08:00
										 |  |  | 		t.traverse(module, { | 
					
						
							| 
									
										
										
										
											2018-03-01 22:07:43 +08:00
										 |  |  | 			ModuleExport({ node }) { | 
					
						
							| 
									
										
										
										
											2018-05-30 18:45:05 +08:00
										 |  |  | 				const descriptor = node.descr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if (descriptor.exportType === "Func") { | 
					
						
							|  |  |  | 					const funcidx = descriptor.id.value; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-04 22:23:41 +08:00
										 |  |  | 					/** @type {t.FuncSignature} */ | 
					
						
							| 
									
										
										
										
											2018-05-30 18:45:05 +08:00
										 |  |  | 					const funcSignature = moduleContext.getFunction(funcidx); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-04 22:23:41 +08:00
										 |  |  | 					const incompatibleType = getJsIncompatibleTypeOfFuncSignature( | 
					
						
							|  |  |  | 						funcSignature | 
					
						
							| 
									
										
										
										
											2018-05-30 18:45:05 +08:00
										 |  |  | 					); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-04 22:23:41 +08:00
										 |  |  | 					if (incompatibleType) { | 
					
						
							| 
									
										
										
										
											2018-10-18 23:35:36 +08:00
										 |  |  | 						if (jsIncompatibleExports === undefined) { | 
					
						
							|  |  |  | 							jsIncompatibleExports = state.module.buildMeta.jsIncompatibleExports = {}; | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2018-06-04 22:23:41 +08:00
										 |  |  | 						jsIncompatibleExports[node.name] = incompatibleType; | 
					
						
							| 
									
										
										
										
											2018-05-30 18:45:05 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-09 23:53:25 +08:00
										 |  |  | 				exports.push(node.name); | 
					
						
							| 
									
										
										
										
											2018-06-02 15:53:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				if (node.descr && node.descr.exportType === "Global") { | 
					
						
							|  |  |  | 					const refNode = importedGlobals[node.descr.id.value]; | 
					
						
							|  |  |  | 					if (refNode) { | 
					
						
							|  |  |  | 						const dep = new WebAssemblyExportImportedDependency( | 
					
						
							|  |  |  | 							node.name, | 
					
						
							|  |  |  | 							refNode.module, | 
					
						
							| 
									
										
										
										
											2018-10-24 17:24:39 +08:00
										 |  |  | 							refNode.name, | 
					
						
							|  |  |  | 							refNode.descr.valtype | 
					
						
							| 
									
										
										
										
											2018-06-02 15:53:35 +08:00
										 |  |  | 						); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						state.module.addDependency(dep); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Global({ node }) { | 
					
						
							|  |  |  | 				const init = node.init[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				let importNode = null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if (init.id === "get_global") { | 
					
						
							|  |  |  | 					const globalIdx = init.args[0].value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if (globalIdx < importedGlobals.length) { | 
					
						
							|  |  |  | 						importNode = importedGlobals[globalIdx]; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				importedGlobals.push(importNode); | 
					
						
							| 
									
										
										
										
											2018-02-20 22:03:35 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-01 22:07:43 +08:00
										 |  |  | 			ModuleImport({ node }) { | 
					
						
							| 
									
										
										
										
											2018-05-11 00:07:24 +08:00
										 |  |  | 				/** @type {false | string} */ | 
					
						
							| 
									
										
										
										
											2018-04-28 17:09:13 +08:00
										 |  |  | 				let onlyDirectImport = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-06 16:39:06 +08:00
										 |  |  | 				if (t.isMemory(node.descr) === true) { | 
					
						
							| 
									
										
										
										
											2018-05-11 00:07:24 +08:00
										 |  |  | 					onlyDirectImport = "Memory"; | 
					
						
							| 
									
										
										
										
											2018-06-06 16:39:06 +08:00
										 |  |  | 				} else if (t.isTable(node.descr) === true) { | 
					
						
							| 
									
										
										
										
											2018-05-11 00:07:24 +08:00
										 |  |  | 					onlyDirectImport = "Table"; | 
					
						
							| 
									
										
										
										
											2018-06-06 16:39:06 +08:00
										 |  |  | 				} else if (t.isFuncImportDescr(node.descr) === true) { | 
					
						
							| 
									
										
										
										
											2018-06-04 22:23:41 +08:00
										 |  |  | 					const incompatibleType = getJsIncompatibleType(node.descr.signature); | 
					
						
							| 
									
										
										
										
											2018-05-11 00:07:24 +08:00
										 |  |  | 					if (incompatibleType) { | 
					
						
							|  |  |  | 						onlyDirectImport = `Non-JS-compatible Func Sigurature (${incompatibleType})`; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-06-06 16:39:06 +08:00
										 |  |  | 				} else if (t.isGlobalType(node.descr) === true) { | 
					
						
							| 
									
										
										
										
											2018-06-04 22:23:41 +08:00
										 |  |  | 					const type = node.descr.valtype; | 
					
						
							|  |  |  | 					if (!JS_COMPAT_TYPES.has(type)) { | 
					
						
							|  |  |  | 						onlyDirectImport = `Non-JS-compatible Global Type (${type})`; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-03-29 20:45:56 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  | 				const dep = new WebAssemblyImportDependency( | 
					
						
							| 
									
										
										
										
											2018-05-09 23:53:25 +08:00
										 |  |  | 					node.module, | 
					
						
							|  |  |  | 					node.name, | 
					
						
							|  |  |  | 					node.descr, | 
					
						
							| 
									
										
										
										
											2018-04-28 17:09:13 +08:00
										 |  |  | 					onlyDirectImport | 
					
						
							| 
									
										
										
										
											2018-03-09 00:54:06 +08:00
										 |  |  | 				); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-20 22:03:35 +08:00
										 |  |  | 				state.module.addDependency(dep); | 
					
						
							| 
									
										
										
										
											2018-06-02 15:53:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-06 16:39:06 +08:00
										 |  |  | 				if (t.isGlobalType(node.descr)) { | 
					
						
							| 
									
										
										
										
											2018-06-02 15:53:35 +08:00
										 |  |  | 					importedGlobals.push(node); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-02-20 22:03:35 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-30 20:21:55 +08:00
										 |  |  | 		state.module.addDependency(new StaticExportsDependency(exports, true)); | 
					
						
							| 
									
										
										
										
											2018-12-30 16:03:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-20 22:03:35 +08:00
										 |  |  | 		return state; | 
					
						
							| 
									
										
										
										
											2017-10-30 20:56:57 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = WebAssemblyParser; |