internal static ValueObject GetInitExpr(InitExpr init, List <GlobalInstance> initialized_globals) { if (init.expr.Length < 3) { throw new Exception("Unexpected init expression."); } uint pc = 0; ValueObject ret = null; switch (init.expr[0]) { case (byte)WebAssemblyOpcode.I32_CONST: ret = new ValueObject(LEB128.ReadUInt32(init.expr, ref pc)); break; case (byte)WebAssemblyOpcode.I64_CONST: ret = new ValueObject(LEB128.ReadUInt64(init.expr, ref pc)); break; case (byte)WebAssemblyOpcode.F32_CONST: ret = new ValueObject(BitConverter.ToSingle(init.expr, (int)pc)); break; case (byte)WebAssemblyOpcode.F64_CONST: ret = new ValueObject(BitConverter.ToDouble(init.expr, (int)pc)); break; case (byte)WebAssemblyOpcode.GLOBAL_GET: GlobalInstance gi = initialized_globals[LEB128.ReadInt32(init.expr, ref pc)]; if (!gi.is_mutable) { throw new Exception("Unexpected init expression."); } ret = gi.value; break; default: throw new Exception("Invalid init expression. Expected only simple constant load instruction or global get."); } pc++; if (init.expr[pc] != (byte)WebAssemblyOpcode.END || init.expr.Length > pc) { throw new Exception("Invalid init expression."); } return(ret); }
private (byte[] memory, int location) LoadOrFail(byte[] code, uint size, ref uint pc) { // align is in bytes // it tells us how this value is aligned // in our implementation, we can disregard alignment int align = 1 << (int)LEB128.ReadUInt32(code, ref pc); uint offset = LEB128.ReadUInt32(code, ref pc); ValueObject i = GetValueOfTypeOrFail(WebAssemblyType.i32); long ea = i.AsI32() + offset; uint N = size / 8; if ((ea + N) > ctx.linear_memory[0].size) { Trap("Illegal memory access"); } logger.ConditionalTrace($"Memory location calculated = {ea}."); return(ctx.linear_memory[0].memory, (int)ea); }
// 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); } }