Move intermediate instruction decompiling to decompiler class
This commit is contained in:
Родитель
e803b5ea5b
Коммит
83f9e771ba
|
@ -1,6 +1,4 @@
|
|||
using System.Diagnostics;
|
||||
using WasmLib.FileFormat;
|
||||
using WasmLib.Utils;
|
||||
|
||||
namespace WasmLib.Decompilation.Intermediate
|
||||
{
|
||||
|
@ -24,75 +22,6 @@ namespace WasmLib.Decompilation.Intermediate
|
|||
public abstract string OperationStringFormat { get; }
|
||||
public virtual string? Comment => null;
|
||||
|
||||
// TODO: this should be in IntermediateRepresentationDecompiler or IntermediateContext
|
||||
public void Handle(ref IntermediateContext context)
|
||||
{
|
||||
if (context.RestOfBlockUnreachable && IsImplicit) {
|
||||
#if DEBUG
|
||||
context.WriteFull("// omitted implicit instruction because rest of block is unreachable");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
var args = new Variable[PopCount];
|
||||
|
||||
for (int i = 0; i < PopCount; i++) {
|
||||
args[i] = context.Pop();
|
||||
Debug.Assert(PopTypes[i] == args[i].Type || PopTypes[i] == ValueKind.Any || args[i].Type == ValueKind.Any);
|
||||
}
|
||||
|
||||
context.RestOfBlockUnreachable = RestOfBlockUnreachable;
|
||||
|
||||
// NOTE: really ugly and slow, but can't be replaced with string.format since input is dynamic and can contain {}
|
||||
string s = OperationStringFormat.SafeFormat(args);
|
||||
|
||||
Debug.Assert(PushCount <= 1);
|
||||
if (PushCount > 0) {
|
||||
s = $"{context.Push(PushTypes[0])} = {s}";
|
||||
}
|
||||
|
||||
if (HasBlock) {
|
||||
s += " {";
|
||||
}
|
||||
|
||||
if (Comment != null) {
|
||||
s += " // " + Comment;
|
||||
}
|
||||
|
||||
context.WriteFull(s);
|
||||
|
||||
if (HasBlock) {
|
||||
HandleBlock(ref context, Block1!);
|
||||
|
||||
if (Block2 != null) {
|
||||
context.WriteFull("} else {");
|
||||
HandleBlock(ref context, Block2);
|
||||
}
|
||||
|
||||
context.WriteFull("}");
|
||||
}
|
||||
|
||||
static void HandleBlock(ref IntermediateContext context2, in ControlBlock block)
|
||||
{
|
||||
context2.EnterBlock();
|
||||
|
||||
foreach (IntermediateInstruction instruction in block.Instructions) {
|
||||
instruction.Handle(ref context2);
|
||||
}
|
||||
|
||||
// if stack has values left on it, and we expect a return value
|
||||
if (block.HasReturn && !context2.RestOfBlockUnreachable) {
|
||||
Debug.Assert(context2.StackIndices.Peek() != context2.Stack.Count);
|
||||
|
||||
var popped = context2.Pop();
|
||||
Debug.Assert(popped.Type == block.ValueKind);
|
||||
context2.WriteFull($"block_return {popped}");
|
||||
}
|
||||
|
||||
context2.ExitBlock();
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => GetType().Name.Replace("Instruction", string.Empty);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using WasmLib.Decompilation.Intermediate;
|
||||
using WasmLib.FileFormat;
|
||||
using WasmLib.Utils;
|
||||
|
||||
namespace WasmLib.Decompilation
|
||||
{
|
||||
|
@ -29,7 +31,7 @@ namespace WasmLib.Decompilation
|
|||
|
||||
// write all IR while simulating the stack
|
||||
foreach (IntermediateInstruction instruction in instructions) {
|
||||
instruction.Handle(ref context);
|
||||
HandleInstruction(ref context, instruction);
|
||||
}
|
||||
|
||||
output.WriteLine("}");
|
||||
|
@ -44,5 +46,73 @@ namespace WasmLib.Decompilation
|
|||
|
||||
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)
|
||||
{
|
||||
context2.EnterBlock();
|
||||
|
||||
foreach (IntermediateInstruction instr in block.Instructions) {
|
||||
HandleInstruction(ref context2, instr);
|
||||
}
|
||||
|
||||
// if stack has values left on it, and we expect a return value
|
||||
if (block.HasReturn && !context2.RestOfBlockUnreachable) {
|
||||
Debug.Assert(context2.StackIndices.Peek() != context2.Stack.Count);
|
||||
|
||||
var popped = context2.Pop();
|
||||
Debug.Assert(popped.Type == block.ValueKind);
|
||||
context2.WriteFull($"block_return {popped}");
|
||||
}
|
||||
|
||||
context2.ExitBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче