|
|
|
@ -25,6 +25,45 @@ using System.Threading.Tasks;
@@ -25,6 +25,45 @@ using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace ICSharpCode.Decompiler.IL |
|
|
|
|
{ |
|
|
|
|
/// <summary>
|
|
|
|
|
/// A block consists of a list of IL instructions.
|
|
|
|
|
/// <para>
|
|
|
|
|
/// Phase-1 execution of a block is a no-op: any peek/pop instructions within the block are ignored at this stage.
|
|
|
|
|
/// </para>
|
|
|
|
|
/// <para>
|
|
|
|
|
/// Phase-2 execution will execute the instructions in order, pseudo-code:
|
|
|
|
|
/// </para>
|
|
|
|
|
/// <code>
|
|
|
|
|
/// foreach (var inst in Instructions) {
|
|
|
|
|
/// inst.Phase1();
|
|
|
|
|
/// var result = inst.Phase2();
|
|
|
|
|
/// if (result != void) evalStack.Push(result);
|
|
|
|
|
/// }
|
|
|
|
|
/// </code>
|
|
|
|
|
/// <para>
|
|
|
|
|
/// If the execution reaches the end of the block, one element is popped from the evaluation stack and
|
|
|
|
|
/// is used as the return value of the block.
|
|
|
|
|
/// This means it is impossible for a block to return void: any block with ResultType void must
|
|
|
|
|
/// perform an unconditional branch!
|
|
|
|
|
/// </para>
|
|
|
|
|
/// TODO: that seems like a bad idea for the purposes of goto-removal, I think we'll want
|
|
|
|
|
/// separate classes for void-blocks and inline-blocks.
|
|
|
|
|
/// Or maybe let blocks evaluate to the return value of their last instruction, and use a final pop()
|
|
|
|
|
/// instruction for inline-block semantics.
|
|
|
|
|
/// TODO: actually I think it's a good idea to implement a small ILAst interpreter
|
|
|
|
|
/// public virtual ILInstruction Phase1(InterpreterState state);
|
|
|
|
|
/// public virtual InterpreterResult ILInstruction Phase2(InterpreterState state);
|
|
|
|
|
/// It's probably the easiest solution for specifying clear semantics, and
|
|
|
|
|
/// we might be able to use the interpreter logic for symbolic execution.
|
|
|
|
|
/// In fact, I think we'll need at least Phase1() in order to implement TransformStackIntoVariablesState()
|
|
|
|
|
/// without being incorrect about the pop-order in nested blocks.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// Fun fact: the empty block acts like a phase-2 pop instruction,
|
|
|
|
|
/// which is a slightly different behavior than the normal phase-1 <see cref="Pop"/> instruction!
|
|
|
|
|
/// However, this is just of theoretical interest; we currently don't plan to use inline blocks that
|
|
|
|
|
/// pop elements that they didn't push themselves.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
partial class Block : ILInstruction |
|
|
|
|
{ |
|
|
|
|
public readonly InstructionCollection<ILInstruction> Instructions; |
|
|
|
@ -42,6 +81,13 @@ namespace ICSharpCode.Decompiler.IL
@@ -42,6 +81,13 @@ namespace ICSharpCode.Decompiler.IL
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
internal override void CheckInvariant() |
|
|
|
|
{ |
|
|
|
|
base.CheckInvariant(); |
|
|
|
|
// if the end-point isn't unreachable, there's an implicit pop at the end of the block
|
|
|
|
|
Debug.Assert(this.HasFlag(InstructionFlags.EndPointUnreachable) || this.ResultType != StackType.Void); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the name of this block.
|
|
|
|
|
/// </summary>
|
|
|
|
@ -80,18 +126,40 @@ namespace ICSharpCode.Decompiler.IL
@@ -80,18 +126,40 @@ namespace ICSharpCode.Decompiler.IL
|
|
|
|
|
protected override InstructionFlags ComputeFlags() |
|
|
|
|
{ |
|
|
|
|
var flags = InstructionFlags.None; |
|
|
|
|
foreach (var inst in Instructions) |
|
|
|
|
foreach (var inst in Instructions) { |
|
|
|
|
flags |= inst.Flags; |
|
|
|
|
if (inst.ResultType != StackType.Void) { |
|
|
|
|
// implicit push
|
|
|
|
|
flags |= InstructionFlags.MayWriteEvaluationStack; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// implicit pop at end of block
|
|
|
|
|
if ((flags & InstructionFlags.EndPointUnreachable) == 0) |
|
|
|
|
flags |= InstructionFlags.MayWriteEvaluationStack; |
|
|
|
|
return Phase1Boundary(flags); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Adjust flags for a phase-1 boundary:
|
|
|
|
|
/// The MayPop and MayPeek flags are removed and converted into
|
|
|
|
|
/// MayReadEvaluationStack and/or MayWriteEvaluationStack flags.
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal static InstructionFlags Phase1Boundary(InstructionFlags flags) |
|
|
|
|
{ |
|
|
|
|
// Convert phase-1 flags to phase-2 flags
|
|
|
|
|
if ((flags & InstructionFlags.MayPop) != 0) |
|
|
|
|
flags |= InstructionFlags.MayWriteEvaluationStack; |
|
|
|
|
if ((flags & (InstructionFlags.MayPeek | InstructionFlags.MayPop)) != 0) |
|
|
|
|
flags |= InstructionFlags.MayReadEvaluationStack; |
|
|
|
|
// an inline block has no phase-1 effects
|
|
|
|
|
flags &= ~(InstructionFlags.MayPeek | InstructionFlags.MayPop); |
|
|
|
|
return flags; |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
internal override ILInstruction Inline(InstructionFlags flagsBefore, Stack<ILInstruction> instructionStack, out bool finished) |
|
|
|
|
{ |
|
|
|
|
if (Instructions.Count > 0) |
|
|
|
|
Instructions[0] = Instructions[0].Inline(flagsBefore, instructionStack, out finished); |
|
|
|
|
else |
|
|
|
|
finished = true; |
|
|
|
|
// an inline block has no phase-1 effects, so we're immediately done with inlining
|
|
|
|
|
finished = true; |
|
|
|
|
return this; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|