diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs
index 21c79edb4..aaa539f11 100644
--- a/ICSharpCode.Decompiler/IL/ILReader.cs
+++ b/ICSharpCode.Decompiler/IL/ILReader.cs
@@ -242,6 +242,8 @@ namespace ICSharpCode.Decompiler.IL
isFirstElement = false;
else
output.Write(", ");
+ output.WriteReference(element.Name, element, isLocal: true);
+ output.Write(":");
output.Write(element.StackType);
}
output.Write(']');
@@ -575,6 +577,7 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Or:
return BinaryNumeric(OpCode.BitOr);
case ILOpCode.Pop:
+ Pop();
return new Nop();
case ILOpCode.Rem:
return BinaryNumeric(OpCode.Rem, false, Sign.Signed);
diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
index f3c16eb8f..c8628a7a6 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
@@ -88,7 +88,7 @@ namespace ICSharpCode.Decompiler.IL
}
///
- /// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
+ /// Inlines the stloc instruction at block.Instructions[pos] into the next instruction, if possible.
///
public bool InlineOneIfPossible(Block block, int pos, bool aggressive)
{
diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj
index 37c10c221..206cc1c8b 100644
--- a/ILSpy.AddIn/ILSpy.AddIn.csproj
+++ b/ILSpy.AddIn/ILSpy.AddIn.csproj
@@ -206,7 +206,7 @@
{1E85EFF9-E370-4683-83E4-8A3D063FF791}
ILSpy
-
+
{63E6915C-7EA4-4D76-AB28-0D7191EEA626}
Mono.Cecil.Pdb
diff --git a/doc/ILAst.txt b/doc/ILAst.txt
index 0049717b6..506814ae4 100644
--- a/doc/ILAst.txt
+++ b/doc/ILAst.txt
@@ -4,83 +4,14 @@ translate the IL code into the 'ILAst'.
An ILAst node (ILExpression in the code) usually has other nodes as arguments,
and performs a computation with the result of those arguments.
-A result of a node is either
- * a value (which can be computed on)
+The evaluation of a node results in either:
+ * a value
* void (which is invalid as an argument, but nodes in blocks may produce void results)
* a thrown exception (which stops further evaluation until a matching catch block)
* the execution of a branch instruction (which also stops evaluation until we reach the block container that contains the branch target)
-An ILAst node may also access the IL evaluation stack. When discussing this stack, we will use the notation
-[2, 1, ...] to mean the stack where the value '2' is on top.
-The IL evaluation stack is manipulated by the following instructions:
- * Peek - returns value on top of stack as result, leaves stack unmodified
- * Pop - returns value on top of stack as result, pops the value from the stack
-
-An IL block will evaluate all instructions contained in the block, and will implicitly push the result
-of every instruction to the stack (only if the result is a value).
-For example, starting with an empty stack [], execution of the block:
- {
- ldc.i4 1
- ldc.i4 2
- }
-will result in the stack [2, 1].
-
-Initially, every IL instruction is converted to a corresponding ILAst instruction that uses 'Pop' instructions as arguments.
-For example, IL 'sub' will become 'sub(pop, pop)'.
-
-This actually poses a problem for the ILAst semantics - we want evaluation as the arguments to happen
-left-to-right (as in C#). Yet, to correctly model the semantics of the IL 'sub' instruction, we need to
-pop all the arguments at once without reversing them.
-Starting with the stack [2, 1], the IL 'sub' instruction produces the result -1!
-But if we evaluated the pop instructions in the left-to-right order, we would get sub(2, 1) = +1.
-
-To demonstrate the effect of the evaluation order, we will use a squaring function with the side
-effect of logging the operation to the console:
- 'int square(int val) { Console.WriteLine("{0} squared is {1}", val, val * val); return val*val; }':
-
-Now, the ILAst instruction 'add(square(2), square(3))' will produce the output
- 2 squared is 4
- 3 squared is 9
-and produces the result 13. Note that the evaluation here happens from left to right.
-
-However, consider the program:
- 'add(square(pop), square(pop))'
-starting with the stack [3, 2].
-We want our ILAst instruction to have the same effect as an IL instruction, essentially 'popping all the necessary values at once'.
-This means the expected result is the same as with 'add(square(2), square(3))'.
-Despite the square calls happening left-to-right, we need to execute the pop instructions right-to-left!
-
-Logically, we consider 'pop' to not really be an ILAst instruction, but more like a placeholder for filling in a stack value.
-Therefore, we define the semantics of ILAst instructions in two phases:
- * Phase 1: a right-to-left pass replacing the 'pop' instructions with the values from the stack
- * Phase 2: a left-to-right pass performing the actual evaluation.
-
-Things become even more tricky if we allow for inline blocks within expressions. These may occur for some C# language
-constructs like object initializers.
-For example, consider the ILAst for 'new List { 1 }.Length':
-
- call get_Length(
- { newobj List()
- call Add(peek, ldc.i4 1)
- pop
- }) // inline blocks evaluate to the value of their last instruction
-
-When evaluating the 'call get_Length' instruction, in phase 1 we cannot completely replace all
-'peek' and 'pop' instructions with values from the stack, because the List object is not yet pushed to the stack.
-We use a simple solution to this problem: phase 1 does not traverse into blocks, and only replaces all peek/pop
-instructions reachable without entering a new block.
-When phase 2 of the call get_Length then actually evaluates the nested block, the block runs
-phase 1 for its first instruction, then phase 2 for the first instruction, then pushes the result (if its a value),
-and then starts the same process again at phase 1 for the second instruction.
-
-Note that this whole discussion was only necessary in order to have clear semantics for every possible ILAst.
-These tricky semantics are mostly irrelevant for the actual ILAsts occurring during decompilation.
-This is because initially all instructions start with their 'pop' placeholders being in a contiguous sequence
-at the beginning of their left-to-right evaluation order.
-Because the inlining step that takes an instruction from a block and uses it to replace the matching 'pop' placeholder
-in the following instruction has to put that instruction into the first 'pop' in phase1-order, it will always
-replace the right-most 'pop', which is the last 'pop' in phase-2 evaluation order. This means
-the remaining placeholders stay a contiguous sequence at the beginning of their left-to-right evaluation order.
-
-It does have some implications on inlining, though: we cannot inline blocks that look at more stack values
-than just the ones they push themselves.
+The main differences between IL and ILAst are:
+ * ILAst instructions may form trees
+ * Types are explicit, not implicit
+ * There is no evaluation stack
+ * Instead, "stack slot" variables are introduced