public float GetFloat32(int paramIdx, bool throwIfNotStrongType = false) { if (paramIdx >= this.functionType.paramTypes.Count) { throw new System.Exception("Attempting to access int parameter out of bounds."); } FunctionType.DataOrgInfo doi = this.functionType.paramTypes[paramIdx]; if (throwIfNotStrongType == true && doi.type != Bin.TypeID.Float32) { throw new System.Exception("Attempting to access float parameter of different type."); } int paramOnStack = this.stackEnterIdx + (int)this.functionType.GetParamStackOffset(paramIdx); switch (doi.type) { case Bin.TypeID.Int32: return(System.BitConverter.ToInt32(executionContext.stack, paramOnStack)); case Bin.TypeID.Float32: return(System.BitConverter.ToSingle(executionContext.stack, paramOnStack)); case Bin.TypeID.Int64: return(System.BitConverter.ToInt64(executionContext.stack, paramOnStack)); case Bin.TypeID.Float64: return((float)System.BitConverter.ToDouble(executionContext.stack, paramOnStack)); } throw new System.Exception("Attempt to access imported function parameter of unknown type."); }
public object GetObject(int paramIdx) { if (paramIdx >= this.functionType.paramTypes.Count) { throw new System.Exception("Attempting to access parameter out of bounds."); } FunctionType.DataOrgInfo doi = this.functionType.paramTypes[paramIdx]; int paramOnStack = this.stackEnterIdx + (int)this.functionType.GetParamStackOffset(paramIdx); switch (doi.type) { case Bin.TypeID.Int32: return(System.BitConverter.ToInt32(executionContext.stack, paramOnStack)); case Bin.TypeID.Float32: return(System.BitConverter.ToSingle(executionContext.stack, paramOnStack)); case Bin.TypeID.Int64: return(System.BitConverter.ToInt64(executionContext.stack, paramOnStack)); case Bin.TypeID.Float64: return(System.BitConverter.ToDouble(executionContext.stack, paramOnStack)); } throw new System.Exception("Attempt to access imported function parameter of unknown type."); }
public List <object> GetParamsAsObjects() { List <object> ret = new List <object>(); for (int i = 0; i < this.functionType.paramTypes.Count; ++i) { FunctionType.DataOrgInfo doi = this.functionType.paramTypes[i]; int paramOnStack = this.stackEnterIdx + (int)this.functionType.GetParamStackOffset(i); switch (doi.type) { case Bin.TypeID.Int32: ret.Add(System.BitConverter.ToInt32(executionContext.stack, paramOnStack)); break; case Bin.TypeID.Float32: ret.Add(System.BitConverter.ToSingle(executionContext.stack, paramOnStack)); break; case Bin.TypeID.Int64: ret.Add(System.BitConverter.ToInt64(executionContext.stack, paramOnStack)); break; case Bin.TypeID.Float64: ret.Add(System.BitConverter.ToDouble(executionContext.stack, paramOnStack)); break; default: throw new System.Exception("Attempt to access imported function parameter of unknown type."); } } return(ret); }
public byte [] ConvertObjectsToResult(List <object> lstObjs) { int retCt = this.functionType.resultTypes.Count; if (retCt == 0 && lstObjs == null || lstObjs.Count == 0) { return(new byte[0]); } if (retCt != lstObjs.Count) { throw new System.Exception("Result count mismatch while converting objects to results buffer."); } byte [] ret = new byte[this.functionType.totalResultSize]; for (int i = 0; i < retCt; ++i) { FunctionType.DataOrgInfo doi = this.functionType.resultTypes[i]; int resultPos = (int)this.functionType.GetResultStackOffset(i); byte[] rb; switch (doi.type) { case Bin.TypeID.Int32: rb = System.BitConverter.GetBytes((int)lstObjs[i]); break; case Bin.TypeID.Float32: rb = System.BitConverter.GetBytes((float)lstObjs[i]); break; case Bin.TypeID.Int64: rb = System.BitConverter.GetBytes((long)lstObjs[i]); break; case Bin.TypeID.Float64: rb = System.BitConverter.GetBytes((double)lstObjs[i]); break; default: throw new System.Exception(); } // We're making somewhat's quirky rules where the ImportFunctionUtil isn't // categorized as the core low-level part, so we're going to keep the code // safe. And AFAICT, there isn't a safe byte conversion that writes directly // in a byte array - so we write to an array and then transfer it, which has // quite a few places where overhead is incurred. for (int j = 0; j < rb.Length; ++j) { ret[resultPos + j] = rb[j]; } } return(ret); }
unsafe public static Module LoadBinary(byte *pb, ref uint idx, int endIdx) { // https://www.reddit.com/r/WebAssembly/comments/9vq019/is_anyone_learning_webassembly_in_binary_im_stuck/ // https://webassembly.github.io/wabt/demo/wat2wasm/ if (*((int *)(&pb[idx])) != BinParse.WASM_BINARY_MAGIC) { return(null); } idx += 4; if (*(int *)&pb[idx] != BinParse.WASM_BINARY_VERSION) { return(null); } idx += 4; Module ret = new Module(); while (idx < endIdx) { Bin.Section sectionCode = (Bin.Section)pb[idx]; ++idx; uint sectionSize = BinParse.LoadUnsignedLEB32(pb, ref idx); if (sectionCode == Bin.Section.CustomSec) { uint end = idx + sectionSize; while (true) { return(ret); // TODO: Considered end and unprocessed for now. // // uint nameLen = LoadUnsignedLEB32(pb, ref idx); // string customSecName = LoadString(pb, nameLen, ref idx); // // uint subType = LoadUnsignedLEB32(pb, ref idx); // uint subSize = LoadUnsignedLEB32(pb, ref idx); } } else if (sectionCode == Bin.Section.TypeSec) { uint numTypes = BinParse.LoadUnsignedLEB32(pb, ref idx); for (uint i = 0; i < numTypes; ++i) { Bin.TypeID type = (Bin.TypeID)BinParse.LoadUnsignedLEB32(pb, ref idx); if (type == Bin.TypeID.Function) { FunctionType fty = new FunctionType(); fty.typeid = (uint)type; ret.types.Add(fty); uint numParams = BinParse.LoadUnsignedLEB32(pb, ref idx); for (uint j = 0; j < numParams; ++j) { FunctionType.DataOrgInfo paramInfo = new FunctionType.DataOrgInfo(); paramInfo.type = (Bin.TypeID)BinParse.LoadUnsignedLEB32(pb, ref idx); fty.paramTypes.Add(paramInfo); } uint numResults = BinParse.LoadUnsignedLEB32(pb, ref idx); for (uint j = 0; j < numResults; ++j) { FunctionType.DataOrgInfo resultInfo = new FunctionType.DataOrgInfo(); resultInfo.type = (Bin.TypeID)BinParse.LoadUnsignedLEB32(pb, ref idx); fty.resultTypes.Add(resultInfo); } fty.InitializeOrganization(); } else { } } } else if (sectionCode == Bin.Section.ImportSec) { uint numImports = BinParse.LoadUnsignedLEB32(pb, ref idx); for (uint i = 0; i < numImports; ++i) { uint modnameLen = BinParse.LoadUnsignedLEB32(pb, ref idx); string modName = LoadString(pb, modnameLen, ref idx); uint fieldnameLen = BinParse.LoadUnsignedLEB32(pb, ref idx); string fieldName = LoadString(pb, fieldnameLen, ref idx); ImportType importTy = (ImportType)BinParse.LoadUnsignedLEB32(pb, ref idx); switch (importTy) { case ImportType.TypeIndex: { uint fnTyIdx = BinParse.LoadUnsignedLEB32(pb, ref idx); FunctionType fnTy = ret.types[(int)fnTyIdx]; ret.storeDecl.AddFunctionImp(modName, fieldName, fnTy); } break; case ImportType.TableType: { const int FLAG_HASMAX = 0x01; const int FLAG_OTHERS = ~(FLAG_HASMAX); // We may be able top unify parts of this code with non-imported tables uint type = BinParse.LoadUnsignedLEB32(pb, ref idx); uint flags = BinParse.LoadUnsignedLEB32(pb, ref idx); uint initial = BinParse.LoadUnsignedLEB32(pb, ref idx); uint?max = null; if ((flags & FLAG_HASMAX) != 0) { max = BinParse.LoadUnsignedLEB32(pb, ref idx); } if ((flags & FLAG_OTHERS) != 0) { throw new System.Exception("Encountered unknown flags for imported table."); } ret.storeDecl.AddTableImp(modName, fieldName, (Bin.TypeID)type, initial, max); } break; case ImportType.MemType: { const int FLAG_HASMAX = 0x01; const int FLAG_OTHERS = ~(FLAG_HASMAX); // We may be able to unify parts of this code with non-imported memory uint flags = BinParse.LoadUnsignedLEB32(pb, ref idx); uint initial = BinParse.LoadUnsignedLEB32(pb, ref idx); uint?max = null; if ((flags & FLAG_HASMAX) != 0) { max = BinParse.LoadUnsignedLEB32(pb, ref idx); } if ((flags & FLAG_OTHERS) != 0) { throw new System.Exception("Encountered unknown flags for imported memory."); } ret.storeDecl.AddMemoryImp(modName, fieldName, initial, initial, max); } break; case ImportType.GlobalType: { uint type = BinParse.LoadUnsignedLEB32(pb, ref idx); uint mutability = BinParse.LoadUnsignedLEB32(pb, ref idx); ret.storeDecl.AddGlobalImp(modName, fieldName, (Bin.TypeID)type, mutability != 0); } break; } } } else if (sectionCode == Bin.Section.FunctionSec) { uint numFunctions = BinParse.LoadUnsignedLEB32(pb, ref idx); for (uint i = 0; i < numFunctions; ++i) { Function function = new Function(ret); uint fnType = BinParse.LoadUnsignedLEB32(pb, ref idx); function.typeidx = fnType; function.fnType = ret.types[(int)fnType]; ret.storeDecl.AddFunctionLoc(function.fnType); ret.functions.Add(function); } } else if (sectionCode == Bin.Section.TableSec) { uint numTables = BinParse.LoadUnsignedLEB32(pb, ref idx); for (uint i = 0; i < numTables; ++i) { const int FLAG_HASMAX = 0x01; const int FLAG_OTHERS = ~(FLAG_HASMAX); Bin.TypeID ty = (Bin.TypeID)BinParse.LoadUnsignedLEB32(pb, ref idx); uint flags = BinParse.LoadUnsignedLEB32(pb, ref idx); uint initial = BinParse.LoadUnsignedLEB32(pb, ref idx); uint?max = null; if ((flags & FLAG_HASMAX) != 0) { max = BinParse.LoadUnsignedLEB32(pb, ref idx); } if ((flags & FLAG_OTHERS) != 0) { throw new System.Exception("Table section contains unsupported flags."); } ret.storeDecl.AddTableLoc(ty, initial, max); } } else if (sectionCode == Bin.Section.MemorySec) { // Prepare the declaration of memory regions. // // Note that this is only prepping for the data payloads, actual // parsing of that data happens in the Data section. uint numMems = BinParse.LoadUnsignedLEB32(pb, ref idx); // Right now this is assumed to be 1 for (uint i = 0; i < numMems; ++i) { const int FLAG_HASMAX = 0x01; const int FLAG_OTHERS = ~(FLAG_HASMAX); uint memFlags = BinParse.LoadUnsignedLEB32(pb, ref idx); uint memInitialPageCt = BinParse.LoadUnsignedLEB32(pb, ref idx); uint?memMaxPageCt = null; if ((memFlags & FLAG_HASMAX) != 0) { memMaxPageCt = BinParse.LoadUnsignedLEB32(pb, ref idx); } if ((memFlags & FLAG_OTHERS) != 0) { throw new System.Exception("Memory section contains unsupported flags."); } ret.storeDecl.AddMemoryLoc(memInitialPageCt, memInitialPageCt, memMaxPageCt); } } else if (sectionCode == Bin.Section.GlobalSec) { uint numGlobals = BinParse.LoadUnsignedLEB32(pb, ref idx); for (uint i = 0; i < numGlobals; ++i) { const int FLAG_MUTABLE = 0x01; const int FLAG_OTHERS = ~(FLAG_MUTABLE); uint globType = BinParse.LoadUnsignedLEB32(pb, ref idx); uint globFlags = BinParse.LoadUnsignedLEB32(pb, ref idx); bool mutable = (globFlags & FLAG_MUTABLE) != 0; if ((globFlags & FLAG_OTHERS) != 0) { throw new System.Exception("Global section contains unsupported flags."); } // For now we're just going to assume they do a type.const, then the value, // and then an end. // // I actually haven't read the specs to see what's allowed here. if (globType == (int)Bin.TypeID.Int32) { AssertConsumeByte(pb, ref idx, (byte)Instruction.i32_const); int idef = BinParse.LoadSignedLEB32(pb, ref idx); AssertConsumeByte(pb, ref idx, (byte)Instruction.end); ret.storeDecl.AddGlobalLoc(idef, mutable); } else if (globType == (int)Bin.TypeID.Float32) { AssertConsumeByte(pb, ref idx, (byte)Instruction.f32_const); float fdef = *(float *)&pb[idx]; idx += 4; AssertConsumeByte(pb, ref idx, (byte)Instruction.end); ret.storeDecl.AddGlobalLoc(fdef, mutable); } else if (globType == (int)Bin.TypeID.Int64) { AssertConsumeByte(pb, ref idx, (byte)Instruction.i64_const); long ldef = BinParse.LoadSignedLEB64(pb, ref idx); AssertConsumeByte(pb, ref idx, (byte)Instruction.end); ret.storeDecl.AddGlobalLoc(ldef, mutable); } else if (globType == (int)Bin.TypeID.Float64) { AssertConsumeByte(pb, ref idx, (byte)Instruction.f64_const); double ddef = *(float *)&pb[idx]; idx += 8; AssertConsumeByte(pb, ref idx, (byte)Instruction.end); ret.storeDecl.AddGlobalLoc(ddef, mutable); } else { throw new System.Exception("Unexpected global type."); } } } else if (sectionCode == Bin.Section.ExportSec) { uint numExports = BinParse.LoadUnsignedLEB32(pb, ref idx); for (uint i = 0; i < numExports; ++i) { uint strLen = BinParse.LoadUnsignedLEB32(pb, ref idx); string name = LoadString(pb, strLen, ref idx); uint kind = BinParse.LoadUnsignedLEB32(pb, ref idx); uint index = BinParse.LoadUnsignedLEB32(pb, ref idx); Export export = new Export(); export.name = name; export.kind = (ImportType)kind; export.index = index; ret.exports.Add(export); } } else if (sectionCode == Bin.Section.StartSec) { ret.startFnIndex = BinParse.LoadUnsignedLEB32(pb, ref idx); ret.ValidateStartFunction(true); } else if (sectionCode == Bin.Section.ElementSec) { uint numSegments = BinParse.LoadUnsignedLEB32(pb, ref idx); if (numSegments == 0) { continue; } if (ret.storeDecl.tables.Count < 1) { throw new System.Exception("Element(s) specified when no tables are defined."); } DefTable defTable = ret.storeDecl.tables[0]; for (uint i = 0; i < numSegments; ++i) { // Table index uint flags = BinParse.LoadUnsignedLEB32(pb, ref idx); DefSegment ds = new DefSegment(pb, ref idx, false); Bin.TypeID tabTy = ret.storeDecl.tables[0].type; uint tySize = DataStore.GetTypeIDSize(tabTy); uint elemCt = BinParse.LoadUnsignedLEB32(pb, ref idx); ds.data = new byte[elemCt * tySize]; fixed(byte *ptabdefs = ds.data) { switch (tabTy) { case Bin.TypeID.FuncRef: case Bin.TypeID.Int32: for (int j = 0; j < elemCt; ++j) { ((int *)ptabdefs)[j] = BinParse.LoadSignedLEB32(pb, ref idx); } break; case Bin.TypeID.Float32: for (int j = 0; j < elemCt; ++j) { ((float *)ptabdefs)[j] = *(float *)pb; idx += 4; } break; case Bin.TypeID.Int64: for (int j = 0; j < elemCt; ++j) { ((long *)ptabdefs)[j] = BinParse.LoadSignedLEB64(pb, ref idx); } break; case Bin.TypeID.Float64: for (int j = 0; j < elemCt; ++j) { ((double *)ptabdefs)[j] = *(double *)pb; idx += 8; } break; } } } } else if (sectionCode == Bin.Section.CodeSec) { uint numFunctions = BinParse.LoadUnsignedLEB32(pb, ref idx); for (uint i = 0; i < numFunctions; ++i) { Function function = ret.functions[(int)i]; uint bodySize = BinParse.LoadUnsignedLEB32(pb, ref idx); uint end = idx + bodySize; uint localsCount = BinParse.LoadUnsignedLEB32(pb, ref idx); for (int j = 0; j < localsCount; ++j) { // The number of consecutive occurences of this type uint localTyCt = BinParse.LoadUnsignedLEB32(pb, ref idx); // The type to place on the stack. The quantity of how many // is specified in localTyCt. uint type = BinParse.LoadUnsignedLEB32(pb, ref idx); for (int k = 0; k < localTyCt; ++k) { FunctionType.DataOrgInfo doi = new FunctionType.DataOrgInfo(); doi.type = (Bin.TypeID)type; function.localTypes.Add(doi); } } function.InitializeOrganization(); uint size = end - idx; function.expression = new byte[size]; System.Runtime.InteropServices.Marshal.Copy( (System.IntPtr)(int *)(&pb[idx]), function.expression, (int)0, (int)size); idx = end; } for (uint i = 0; i < numFunctions; ++i) { ret.functions[(int)i].ExpandExpressionToBeUsable(ret); } } else if (sectionCode == Bin.Section.DataSec) { uint numData = BinParse.LoadUnsignedLEB32(pb, ref idx); if (numData == 0) { continue; } if (ret.storeDecl.memories.Count < 1) { throw new System.Exception("Data(s) specified when no mems are defined."); } for (uint i = 0; i < numData; ++i) { uint segHeaderFlags = BinParse.LoadUnsignedLEB32(pb, ref idx); DefMem dmem = ret.storeDecl.memories[0]; DefSegment ds = new DefSegment(pb, ref idx, false); uint dataSz = BinParse.LoadUnsignedLEB32(pb, ref idx); ds.data = new byte[dataSz]; // We're going to do the copy manually, but if there's a C# // low-level copy function that also does this, that would be // prefered. for (uint j = 0; j < dataSz; ++j) { ds.data[j] = pb[idx + j]; } dmem.AddDefault(ds); //ret.storeDecl.memories[(int)i] = dmem; ret.storeDecl.memories[0] = dmem; idx += dataSz; } } else { throw new System.Exception("Encountered unknown Module section."); } } ++idx; return(ret); }