| // 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]; | 
|   } | 
| } |