public void DecompileFunction(StreamWriter output, int functionIndex) { FunctionBody body = WasmModule.FunctionBodies[functionIndex]; FunctionSignature signature = WasmModule.FunctionTypes[WasmModule.Functions[functionIndex]]; // get IR var context = new IntermediateContext(signature, WasmModule, output); List <IntermediateInstruction> instructions = new IntermediateConverter(WasmModule, body, signature).Convert(); output.Write(signature.ToString($"fun_{functionIndex:X8}")); output.WriteLine(" {"); // write all IR while simulating the stack foreach (IntermediateInstruction instruction in instructions) { HandleInstruction(ref context, instruction); } output.WriteLine("}"); if (context.Indentation != 0) { throw new Exception("Function body has unbalanced indentation"); } if (context.Stack.Count != 0) { throw new Exception($"Unbalanced stack, found {context.Stack.Count} remaining values"); } output.WriteLine(); }
private static void HandleInstruction(ref IntermediateContext context, IntermediateInstruction instruction) { if (context.RestOfBlockUnreachable && instruction.IsImplicit) { #if DEBUG context.WriteFull("// omitted implicit instruction because rest of block is unreachable"); #endif return; } var args = new Variable[instruction.PopCount]; for (int i = 0; i < instruction.PopCount; i++) { args[i] = context.Pop(); Debug.Assert(instruction.PopTypes[i] == args[i].Type || instruction.PopTypes[i] == ValueKind.Any || args[i].Type == ValueKind.Any); } context.RestOfBlockUnreachable = instruction.RestOfBlockUnreachable; // NOTE: really ugly and slow, but can't be replaced with string.format since input is dynamic and can contain {} string s = instruction.OperationStringFormat.SafeFormat(args); Debug.Assert(instruction.PushCount <= 1); if (instruction.PushCount > 0) { s = $"{context.Push(instruction.PushTypes[0])} = {s}"; } if (instruction.HasBlock) { s += " {"; } if (instruction.Comment != null) { s += " // " + instruction.Comment; } context.WriteFull(s); if (instruction.HasBlock) { HandleBlock(ref context, instruction.Block1 !); if (instruction.Block2 != null) { context.WriteFull("} else {"); HandleBlock(ref context, instruction.Block2); } context.WriteFull("}"); }
static void HandleBlock(ref IntermediateContext context2, in ControlBlock block)