Spaces:
Sleeping
Sleeping
| // TODO(sven): add flow in here | |
| import { isSignature, isNumberLiteral } from "@webassemblyjs/ast"; | |
| import { assert } from "mamacro"; | |
| export function moduleContextFromModuleAST(m) { | |
| const moduleContext = new ModuleContext(); | |
| assert(m.type === "Module"); | |
| m.fields.forEach(field => { | |
| switch (field.type) { | |
| case "Start": { | |
| moduleContext.setStart(field.index); | |
| break; | |
| } | |
| case "TypeInstruction": { | |
| moduleContext.addType(field); | |
| break; | |
| } | |
| case "Func": { | |
| moduleContext.addFunction(field); | |
| break; | |
| } | |
| case "Global": { | |
| moduleContext.defineGlobal(field); | |
| break; | |
| } | |
| case "ModuleImport": { | |
| switch (field.descr.type) { | |
| case "GlobalType": { | |
| moduleContext.importGlobal( | |
| field.descr.valtype, | |
| field.descr.mutability | |
| ); | |
| break; | |
| } | |
| case "Memory": { | |
| moduleContext.addMemory( | |
| field.descr.limits.min, | |
| field.descr.limits.max | |
| ); | |
| break; | |
| } | |
| case "FuncImportDescr": { | |
| moduleContext.importFunction(field.descr); | |
| break; | |
| } | |
| case "Table": { | |
| // FIXME(sven): not implemented yet | |
| break; | |
| } | |
| default: | |
| throw new Error( | |
| "Unsupported ModuleImport of type " + | |
| JSON.stringify(field.descr.type) | |
| ); | |
| } | |
| break; | |
| } | |
| case "Memory": { | |
| moduleContext.addMemory(field.limits.min, field.limits.max); | |
| break; | |
| } | |
| } | |
| }); | |
| return moduleContext; | |
| } | |
| /** | |
| * Module context for type checking | |
| */ | |
| export class ModuleContext { | |
| constructor() { | |
| this.funcs = []; | |
| this.funcsOffsetByIdentifier = []; | |
| this.types = []; | |
| this.globals = []; | |
| this.globalsOffsetByIdentifier = []; | |
| this.mems = []; | |
| // Current stack frame | |
| this.locals = []; | |
| this.labels = []; | |
| this.return = []; | |
| this.debugName = "unknown"; | |
| this.start = null; | |
| } | |
| /** | |
| * Set start segment | |
| */ | |
| setStart(index) { | |
| this.start = index.value; | |
| } | |
| /** | |
| * Get start function | |
| */ | |
| getStart() { | |
| return this.start; | |
| } | |
| /** | |
| * Reset the active stack frame | |
| */ | |
| newContext(debugName, expectedResult) { | |
| this.locals = []; | |
| this.labels = [expectedResult]; | |
| this.return = expectedResult; | |
| this.debugName = debugName; | |
| } | |
| /** | |
| * Functions | |
| */ | |
| addFunction(func /*: Func*/) { | |
| // eslint-disable-next-line prefer-const | |
| let { params: args = [], results: result = [] } = func.signature || {}; | |
| args = args.map(arg => arg.valtype); | |
| this.funcs.push({ args, result }); | |
| if (typeof func.name !== "undefined") { | |
| this.funcsOffsetByIdentifier[func.name.value] = this.funcs.length - 1; | |
| } | |
| } | |
| importFunction(funcimport) { | |
| if (isSignature(funcimport.signature)) { | |
| // eslint-disable-next-line prefer-const | |
| let { params: args, results: result } = funcimport.signature; | |
| args = args.map(arg => arg.valtype); | |
| this.funcs.push({ args, result }); | |
| } else { | |
| assert(isNumberLiteral(funcimport.signature)); | |
| const typeId = funcimport.signature.value; | |
| assert(this.hasType(typeId)); | |
| const signature = this.getType(typeId); | |
| this.funcs.push({ | |
| args: signature.params.map(arg => arg.valtype), | |
| result: signature.results | |
| }); | |
| } | |
| if (typeof funcimport.id !== "undefined") { | |
| // imports are first, we can assume their index in the array | |
| this.funcsOffsetByIdentifier[funcimport.id.value] = this.funcs.length - 1; | |
| } | |
| } | |
| hasFunction(index) { | |
| return typeof this.getFunction(index) !== "undefined"; | |
| } | |
| getFunction(index) { | |
| if (typeof index !== "number") { | |
| throw new Error("getFunction only supported for number index"); | |
| } | |
| return this.funcs[index]; | |
| } | |
| getFunctionOffsetByIdentifier(name) { | |
| assert(typeof name === "string"); | |
| return this.funcsOffsetByIdentifier[name]; | |
| } | |
| /** | |
| * Labels | |
| */ | |
| addLabel(result) { | |
| this.labels.unshift(result); | |
| } | |
| hasLabel(index) { | |
| return this.labels.length > index && index >= 0; | |
| } | |
| getLabel(index) { | |
| return this.labels[index]; | |
| } | |
| popLabel() { | |
| this.labels.shift(); | |
| } | |
| /** | |
| * Locals | |
| */ | |
| hasLocal(index) { | |
| return typeof this.getLocal(index) !== "undefined"; | |
| } | |
| getLocal(index) { | |
| return this.locals[index]; | |
| } | |
| addLocal(type) { | |
| this.locals.push(type); | |
| } | |
| /** | |
| * Types | |
| */ | |
| addType(type) { | |
| assert(type.functype.type === "Signature"); | |
| this.types.push(type.functype); | |
| } | |
| hasType(index) { | |
| return this.types[index] !== undefined; | |
| } | |
| getType(index) { | |
| return this.types[index]; | |
| } | |
| /** | |
| * Globals | |
| */ | |
| hasGlobal(index) { | |
| return this.globals.length > index && index >= 0; | |
| } | |
| getGlobal(index) { | |
| return this.globals[index].type; | |
| } | |
| getGlobalOffsetByIdentifier(name) { | |
| assert(typeof name === "string"); | |
| return this.globalsOffsetByIdentifier[name]; | |
| } | |
| defineGlobal(global /*: Global*/) { | |
| const type = global.globalType.valtype; | |
| const mutability = global.globalType.mutability; | |
| this.globals.push({ type, mutability }); | |
| if (typeof global.name !== "undefined") { | |
| this.globalsOffsetByIdentifier[global.name.value] = | |
| this.globals.length - 1; | |
| } | |
| } | |
| importGlobal(type, mutability) { | |
| this.globals.push({ type, mutability }); | |
| } | |
| isMutableGlobal(index) { | |
| return this.globals[index].mutability === "var"; | |
| } | |
| isImmutableGlobal(index) { | |
| return this.globals[index].mutability === "const"; | |
| } | |
| /** | |
| * Memories | |
| */ | |
| hasMemory(index) { | |
| return this.mems.length > index && index >= 0; | |
| } | |
| addMemory(min, max) { | |
| this.mems.push({ min, max }); | |
| } | |
| getMemory(index) { | |
| return this.mems[index]; | |
| } | |
| } | |