Example #1
0
        private void InitFunctions(WebAssemblyFile file)
        {
            logger.Debug("Instanciating Functions.");

            Dictionary <uint, FuncType> type_info = new Dictionary <uint, FuncType>();

            for (uint i = 0; i < (uint)file.type.entries.Length; i++)
            {
                type_info[i] = file.type.entries[i];
                logger.ConditionalTrace($"Type {i} = {file.type.entries[i]}.");
            }

            logger.ConditionalTrace($"file.function.types.Length = {file.function.types.Length} and file.code.bodies.Length = {file.code.bodies.Length}.");
            if (file.function.types.Length != file.code.bodies.Length)
            {
                throw new Exception("Invalid file");
            }

            uint import_count = (uint)functions.Count;

            logger.ConditionalTrace($"Import count = {import_count}.");

            for (uint i = 0; i < (uint)file.code.bodies.Length; i++)
            {
                uint         index = file.function.types[i];
                FunctionBody body  = file.code.bodies[i];

                uint func_indx = i + import_count;

                logger.ConditionalTrace($"Function {func_indx} = {body}.");

                functions[func_indx] = new FunctionInstance
                {
                    module       = "this",
                    is_in_module = true,
                    code         = body.code,
                    parameters   = type_info[index].param_types,
                    return_type  = type_info[index].return_type,
                };

                List <WebAssemblyType> locals_unwrapped = new List <WebAssemblyType>();

                foreach (var local in body.locals)
                {
                    locals_unwrapped.AddRange(Enumerable.Repeat(local.type, (int)local.count));
                }

                functions[func_indx].locals = locals_unwrapped.ToArray();

                logger.ConditionalTrace($"Final object = {functions[func_indx]}.");
            }

            logger.Debug("Done instanciating Functions.");
        }
Example #2
0
        private ValueObject Invoke(uint func_id)
        {
            logger.Debug($"Invoking {func_id}.");

            FunctionInstance func = ctx.functions[func_id];

            ValueObject[] parameters = new ValueObject[func.parameters.Length];

            // Gather parameters from stack
            for (int i = 0; i < parameters.Length; i++)
            {
                parameters[i] = GetValueOrFail();
            }

            // Create our activation object and push on to the stack
            ActivationObject activation_object = new ActivationObject(ctx.functions[func_id], parameters);

            ctx.Push(activation_object);

            // If we're external, run host code
            if (!func.is_in_module)
            {
                logger.Debug("Executing host code.");
                func.host_code(activation_object.parameters);
            }
            else
            {
                Execute();
            }

            ValueObject result = GetValueOrFail();

            if (result.type != func.return_type)
            {
                logger.Debug($"Result of function was {result.type}, expected {func.return_type}.");
                Trap("Function corrupt.");
            }

            // Remember to pop our own activation instance off the record!
            // Double check as well that it is in fact us
            if (ctx.Peek() != WebAssemblyStackObjectType.ActivationObject || activation_object != ctx.PopActivation())
            {
                Trap("Function stack corrupt.");
            }

            return(result);
        }
Example #3
0
        private void InitImport(WebAssemblyFile file)
        {
            logger.Debug("Instanciating Export.");

            Dictionary <uint, FuncType> type_info = new Dictionary <uint, FuncType>();

            for (uint i = 0; i < (uint)file.type.entries.Length; i++)
            {
                type_info[i] = file.type.entries[i];
                logger.ConditionalTrace($"Type {i} = {file.type.entries[i]}.");
            }

            for (uint i = 0; i < (uint)file.import.entries.Length; i++)
            {
                var entry = file.import.entries[i];

                logger.Debug($"Entry = {entry}.");

                switch (entry.kind)
                {
                case WebAssemblyExternalKind.Function:
                    uint index = (uint)entry.type;

                    functions[i] = new FunctionInstance
                    {
                        module      = entry.module_str,
                        name        = entry.field_str,
                        parameters  = type_info[index].param_types,
                        return_type = type_info[index].return_type
                    };

                    logger.ConditionalTrace($"Function {i} = {functions[i]}.");
                    break;
                }
            }

            logger.Debug("Done instanciating Export.");
        }
Example #4
0
        // TODO: Can now transform Execute from a recursive function into an iterative function
        // Each time a new "frame" is added, we just update activation_object and func
        // ctx records the current pc so when we go back
        // Simply need to inline the Invoke method into Execute
        private void Execute()
        {
            try
            {
                ActivationObject activation_object = ctx.GetCurrentFunction();
                FunctionInstance func = activation_object.function;
                byte[]           code = func.code;

                logger.Info($"Entering Execute with function {func.module}.{func.name}.");
                logger.ConditionalTrace($"Function returns: {func.return_type}.");
                logger.ConditionalTrace("Function parameters: {0}.", WebAssemblyHelper.ToString(func.parameters, ", "));
                logger.ConditionalTrace("Function arguments: {0}.", WebAssemblyHelper.ToString(activation_object.parameters, ", "));
                logger.ConditionalTrace("Function locals: {0}.", WebAssemblyHelper.ToString(func.locals, ", "));

                //WebAssemblyHelper.ReinterpretHelper reinterpret_helper = new WebAssemblyHelper.ReinterpretHelper();

                bool continue_executing = true;

                // https://webassembly.github.io/spec/core/exec/instructions.html
                while (continue_executing)
                {
                    logger.ConditionalTrace($"PC = {activation_object.pc}.");
                    logger.Debug($"Executing: {(WebAssemblyOpcode)code[activation_object.pc]}.");

                    switch ((WebAssemblyOpcode)code[activation_object.pc])
                    {
                    case WebAssemblyOpcode.UNREACHABLE:
                    {
                        Trap("Unreachable reached!");

                        break;
                    }

                    case WebAssemblyOpcode.NOP:
                    {
                        break;
                    }

                    case WebAssemblyOpcode.END:
                    {
                        continue_executing = false;

                        break;
                    }

                    case WebAssemblyOpcode.LOOP:
                    {
                        // ignore result for now
                        activation_object.pc++;
                        var result = (WebAssemblyType)code[activation_object.pc];

                        Enter(new LabelObject(0, activation_object.pc - 2));

                        break;
                    }

                    case WebAssemblyOpcode.BR:
                    {
                        uint l = LEB128.ReadUInt32(code, ref activation_object.pc);

                        Exit(l);

                        break;
                    }

                    case WebAssemblyOpcode.BR_IF:
                    {
                        ValueObject c = GetValueOfTypeOrFail(WebAssemblyType.i32);
                        if (c.AsI32() != 0)
                        {
                            goto case WebAssemblyOpcode.BR;
                        }

                        break;
                    }

                    case WebAssemblyOpcode.LOCAL_GET:
                    {
                        uint local = LEB128.ReadUInt32(code, ref activation_object.pc);
                        if (local > func.locals.Length)
                        {
                            Trap("Function corrupt.");
                        }

                        ctx.Push(activation_object.locals[local]);

                        break;
                    }

                    case WebAssemblyOpcode.LOCAL_SET:
                    {
                        uint local = LEB128.ReadUInt32(code, ref activation_object.pc);
                        if (local > func.locals.Length)
                        {
                            Trap("Function corrupt.");
                        }

                        ValueObject val = GetValueOrFail();

                        activation_object.locals[local] = val;

                        break;
                    }

                    case WebAssemblyOpcode.LOCAL_TEE:
                    {
                        ValueObject val = GetValueOrFail();
                        ctx.Push(val);
                        ctx.Push(val);
                        goto case WebAssemblyOpcode.LOCAL_SET;
                    }

                    case WebAssemblyOpcode.I32_CONST:
                    {
                        ctx.Push(new ValueObject(LEB128.ReadUInt32(code, ref activation_object.pc)));
                        break;
                    }

                    case WebAssemblyOpcode.I64_CONST:
                    {
                        ctx.Push(new ValueObject(LEB128.ReadUInt64(code, ref activation_object.pc)));
                        break;
                    }

                    case WebAssemblyOpcode.F32_CONST:
                    {
                        ctx.Push(new ValueObject(BitConverter.ToSingle(code, (int)activation_object.pc + 1)));
                        activation_object.pc += 4;
                        break;
                    }

                    case WebAssemblyOpcode.F64_CONST:
                    {
                        ctx.Push(new ValueObject(BitConverter.ToDouble(code, (int)activation_object.pc + 1)));
                        activation_object.pc += 7;
                        break;
                    }

                    case WebAssemblyOpcode.I32_LOAD:
                    {
                        var(memory, location) = LoadOrFail(code, 32, ref activation_object.pc);

                        ctx.Push(new ValueObject(BitConverter.ToUInt32(memory, location)));

                        break;
                    }

                    case WebAssemblyOpcode.I64_LOAD:
                    {
                        var(memory, location) = LoadOrFail(code, 64, ref activation_object.pc);

                        ctx.Push(new ValueObject(BitConverter.ToUInt64(memory, location)));

                        break;
                    }

                    case WebAssemblyOpcode.F32_LOAD:
                    {
                        var(memory, location) = LoadOrFail(code, 32, ref activation_object.pc);

                        ctx.Push(new ValueObject(BitConverter.ToSingle(memory, location)));

                        break;
                    }

                    case WebAssemblyOpcode.F64_LOAD:
                    {
                        var(memory, location) = LoadOrFail(code, 64, ref activation_object.pc);

                        ctx.Push(new ValueObject(BitConverter.ToDouble(memory, location)));

                        break;
                    }

                    case WebAssemblyOpcode.I32_LOAD8_S:
                    {
                        var(memory, location) = LoadOrFail(code, 8, ref activation_object.pc);

                        ctx.Push(new ValueObject((uint)(sbyte)memory[location]));

                        break;
                    }

                    case WebAssemblyOpcode.I32_LOAD8_U:
                    {
                        var(memory, location) = LoadOrFail(code, 8, ref activation_object.pc);

                        ctx.Push(new ValueObject((uint)memory[location]));

                        break;
                    }

                    case WebAssemblyOpcode.I32_LOAD16_S:
                    {
                        var(memory, location) = LoadOrFail(code, 16, ref activation_object.pc);

                        ctx.Push(new ValueObject((uint)BitConverter.ToInt16(memory, (int)activation_object.pc)));

                        break;
                    }

                    case WebAssemblyOpcode.I32_LOAD16_U:
                    {
                        var(memory, location) = LoadOrFail(code, 16, ref activation_object.pc);

                        ctx.Push(new ValueObject(BitConverter.ToUInt16(memory, (int)activation_object.pc)));

                        break;
                    }

                    case WebAssemblyOpcode.I64_LOAD8_S:
                    {
                        var(memory, location) = LoadOrFail(code, 8, ref activation_object.pc);

                        ctx.Push(new ValueObject((ulong)(sbyte)memory[location]));

                        break;
                    }

                    case WebAssemblyOpcode.I64_LOAD8_U:
                    {
                        var(memory, location) = LoadOrFail(code, 8, ref activation_object.pc);

                        ctx.Push(new ValueObject((ulong)memory[location]));

                        break;
                    }

                    case WebAssemblyOpcode.I64_LOAD16_S:
                    {
                        var(memory, location) = LoadOrFail(code, 16, ref activation_object.pc);

                        ctx.Push(new ValueObject((ulong)BitConverter.ToInt16(memory, (int)activation_object.pc)));

                        break;
                    }

                    case WebAssemblyOpcode.I64_LOAD16_U:
                    {
                        var(memory, location) = LoadOrFail(code, 16, ref activation_object.pc);

                        ctx.Push(new ValueObject((ulong)BitConverter.ToUInt16(memory, (int)activation_object.pc)));

                        break;
                    }

                    case WebAssemblyOpcode.I64_LOAD32_S:
                    {
                        var(memory, location) = LoadOrFail(code, 32, ref activation_object.pc);

                        ctx.Push(new ValueObject((ulong)BitConverter.ToInt32(memory, (int)activation_object.pc)));

                        break;
                    }

                    case WebAssemblyOpcode.I64_LOAD32_U:
                    {
                        var(memory, location) = LoadOrFail(code, 32, ref activation_object.pc);

                        ctx.Push(new ValueObject((ulong)BitConverter.ToUInt32(memory, (int)activation_object.pc)));

                        break;
                    }

                    case WebAssemblyOpcode.I32_STORE:
                    {
                        var(value, memory, location) = StoreOrFail(code, 32, WebAssemblyType.i32, ref activation_object.pc);

                        Array.Copy(BitConverter.GetBytes(value.AsI32()), 0, memory, location, 32 / 8);

                        break;
                    }

                    case WebAssemblyOpcode.I64_STORE:
                    {
                        var(value, memory, location) = StoreOrFail(code, 64, WebAssemblyType.i64, ref activation_object.pc);

                        Array.Copy(BitConverter.GetBytes(value.AsI64()), 0, memory, location, 64 / 8);

                        break;
                    }

                    case WebAssemblyOpcode.F32_STORE:
                    {
                        var(value, memory, location) = StoreOrFail(code, 32, WebAssemblyType.f32, ref activation_object.pc);

                        Array.Copy(BitConverter.GetBytes(value.AsF32()), 0, memory, location, 32 / 8);

                        break;
                    }

                    case WebAssemblyOpcode.F64_STORE:
                    {
                        var(value, memory, location) = StoreOrFail(code, 64, WebAssemblyType.f64, ref activation_object.pc);

                        Array.Copy(BitConverter.GetBytes(value.AsF64()), 0, memory, location, 64 / 8);

                        break;
                    }

                    case WebAssemblyOpcode.I32_STORE8:
                    {
                        var(value, memory, location) = StoreOrFail(code, 32, WebAssemblyType.i32, ref activation_object.pc);

                        memory[location] = (byte)value.AsI32();

                        break;
                    }

                    case WebAssemblyOpcode.I32_STORE16:
                    {
                        var(value, memory, location) = StoreOrFail(code, 32, WebAssemblyType.i32, ref activation_object.pc);

                        Array.Copy(BitConverter.GetBytes((ushort)value.AsI32()), 0, memory, location, 16 / 8);

                        break;
                    }

                    case WebAssemblyOpcode.I64_STORE8:
                    {
                        var(value, memory, location) = StoreOrFail(code, 64, WebAssemblyType.i64, ref activation_object.pc);

                        memory[location] = (byte)value.AsI64();

                        break;
                    }

                    case WebAssemblyOpcode.I64_STORE16:
                    {
                        var(value, memory, location) = StoreOrFail(code, 64, WebAssemblyType.i64, ref activation_object.pc);

                        Array.Copy(BitConverter.GetBytes((ushort)value.AsI64()), 0, memory, location, 16 / 8);

                        break;
                    }

                    case WebAssemblyOpcode.I64_STORE32:
                    {
                        var(value, memory, location) = StoreOrFail(code, 64, WebAssemblyType.i64, ref activation_object.pc);

                        Array.Copy(BitConverter.GetBytes((uint)value.AsI64()), 0, memory, location, 32 / 8);

                        break;
                    }

                    case WebAssemblyOpcode.MEMORY_SIZE:
                    {
                        ctx.Push(new ValueObject((uint)ctx.linear_memory[0].pages));

                        break;
                    }

                    case WebAssemblyOpcode.MEMORY_GROW:
                    {
                        ValueObject n = GetValueOfTypeOrFail(WebAssemblyType.i32);

                        if (ctx.linear_memory[0].GrowMemory(n.AsI32()))
                        {
                            ctx.Push(n);
                        }
                        else
                        {
                            ctx.Push(new ValueObject(unchecked ((uint)-1)));
                        }

                        break;
                    }

                    case WebAssemblyOpcode.CALL:
                    {
                        uint new_func = LEB128.ReadUInt32(code, ref activation_object.pc);

                        ctx.Push(Invoke(new_func));

                        break;
                    }

                    case WebAssemblyOpcode.DROP:
                    {
                        GetValueOrFail();

                        break;
                    }

                    case WebAssemblyOpcode.SELECT:
                    {
                        ValueObject c    = GetValueOfTypeOrFail(WebAssemblyType.i32);
                        ValueObject val2 = GetValueOrFail();
                        ValueObject val1 = GetValueOrFail();

                        ctx.Push(c.AsI32() != 0 ? val1 : val2);

                        break;
                    }

                    default:
                    {
                        Trap($"Unknown Opcode {code[activation_object.pc]}.");

                        break;
                    }
                    }
                    activation_object.pc++;
                }
            }
            catch (WebAssemblyTrap)
            {
                throw;
            }
            catch (Exception ex)
            {
                logger.Debug(ex.StackTrace);

                Trap("Unexpected Error: " + ex.Message);
            }
        }