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);
        }
Exemplo n.º 5
0
        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);
        }