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."); }
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); }
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."); }
// 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); } }