Browse Source

Add class VariableScope.

pull/728/head
Daniel Grunwald 9 years ago
parent
commit
c171dbd520
  1. 4
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 5
      ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitions.cs
  3. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  4. 14
      ICSharpCode.Decompiler/IL/ILReader.cs
  5. 17
      ICSharpCode.Decompiler/IL/ILVariable.cs
  6. 2
      ICSharpCode.Decompiler/IL/Instructions.cs
  7. 3
      ICSharpCode.Decompiler/IL/Instructions.tt
  8. 4
      ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs
  9. 4
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  10. 4
      ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
  11. 10
      ICSharpCode.Decompiler/IL/Instructions/Branch.cs
  12. 1
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  13. 18
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  14. 4
      ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
  15. 6
      ICSharpCode.Decompiler/IL/Instructions/Leave.cs
  16. 19
      ICSharpCode.Decompiler/IL/Instructions/LocalVarInstructions.cs
  17. 13
      ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs
  18. 162
      ICSharpCode.Decompiler/IL/Instructions/VariableScope.cs
  19. 3
      ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs

4
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -557,12 +557,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -557,12 +557,12 @@ namespace ICSharpCode.Decompiler.CSharp
var specializingTypeSystem = GetSpecializingTypeSystem(decompilationContext);
var ilReader = new ILReader(specializingTypeSystem);
var function = ilReader.ReadIL(methodDefinition.Body, CancellationToken);
function.CheckInvariant();
function.CheckInvariant(ILPhase.Normal);
var context = new ILTransformContext { TypeSystem = specializingTypeSystem, CancellationToken = CancellationToken };
foreach (var transform in ilTransforms) {
CancellationToken.ThrowIfCancellationRequested();
transform.Run(function, context);
function.CheckInvariant();
function.CheckInvariant(ILPhase.Normal);
}
var statementBuilder = new StatementBuilder(decompilationContext, method);
var body = statementBuilder.ConvertAsBlock(function.Body);

5
ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitions.cs

@ -21,14 +21,11 @@ using System; @@ -21,14 +21,11 @@ using System;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Implements the "reaching defintions" analysis.
/// Implements the "reaching definitions" analysis.
///
/// https://en.wikipedia.org/wiki/Reaching_definition
/// </summary>
public class ReachingDefinitions
{
public ReachingDefinitions()
{
}
}
}

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -115,6 +115,7 @@ @@ -115,6 +115,7 @@
<Compile Include="IL\Instructions\TryInstruction.cs" />
<Compile Include="IL\Instructions\UnaryInstruction.cs" />
<Compile Include="IL\IInlineContext.cs" />
<Compile Include="IL\Instructions\VariableScope.cs" />
<Compile Include="IL\NRTypeExtensions.cs" />
<Compile Include="IL\SlotInfo.cs" />
<Compile Include="IL\Transforms\CopyPropagation.cs" />

14
ICSharpCode.Decompiler/IL/ILReader.cs

@ -235,7 +235,7 @@ namespace ICSharpCode.Decompiler.IL @@ -235,7 +235,7 @@ namespace ICSharpCode.Decompiler.IL
ILInstruction decodedInstruction = DecodeInstruction();
if (decodedInstruction.ResultType == StackType.Unknown)
Warn("Unknown result type (might be due to invalid IL)");
decodedInstruction.CheckInvariant();
decodedInstruction.CheckInvariant(ILPhase.InILReader);
decodedInstruction.ILRange = new Interval(start, reader.Position);
UnpackPush(decodedInstruction).ILRange = decodedInstruction.ILRange;
instructionBuilder.Add(decodedInstruction);
@ -298,6 +298,7 @@ namespace ICSharpCode.Decompiler.IL @@ -298,6 +298,7 @@ namespace ICSharpCode.Decompiler.IL
function.Variables.AddRange(parameterVariables);
function.Variables.AddRange(localVariables);
function.Variables.AddRange(stackVariables);
function.Variables.AddRange(variableByExceptionHandler.Values);
function.AddRef(); // mark the root node
return function;
}
@ -968,21 +969,22 @@ namespace ICSharpCode.Decompiler.IL @@ -968,21 +969,22 @@ namespace ICSharpCode.Decompiler.IL
}
switch (method.DeclaringType.Kind) {
case TypeKind.Array:
var type = ((ICSharpCode.NRefactory.TypeSystem.ArrayType)method.DeclaringType).ElementType;
var elementType = ((ICSharpCode.NRefactory.TypeSystem.ArrayType)method.DeclaringType).ElementType;
if (opCode == OpCode.NewObj)
return Push(new NewArr(type, arguments));
return Push(new NewArr(elementType, arguments));
if (method.Name == "Set") {
var target = arguments[0];
var value = arguments.Last();
var indices = arguments.Skip(1).Take(arguments.Length - 2).ToArray();
return new StObj(new LdElema(type, target, indices), value, type);
return new StObj(new LdElema(elementType, target, indices), value, elementType);
}
if (method.Name == "Get") {
var target = arguments[0];
var indices = arguments.Skip(1).ToArray();
return Push(new LdObj(new LdElema(type, target, indices), type));
return Push(new LdObj(new LdElema(elementType, target, indices), elementType));
}
throw new NotImplementedException();
Warn("Unknown method called on array type: " + method.Name);
goto default;
default:
var call = CallInstruction.Create(opCode, method);
call.Arguments.AddRange(arguments);

17
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -60,6 +60,23 @@ namespace ICSharpCode.Decompiler.IL @@ -60,6 +60,23 @@ namespace ICSharpCode.Decompiler.IL
public string Name { get; set; }
/// <summary>
/// Gets the scope in which this variable is declared.
/// </summary>
/// <remarks>
/// This property is set automatically when the variable is added to the <c>VariableScope.Variables</c> collection.
/// </remarks>
public ILVariableScope Scope { get; internal set; }
/// <summary>
/// Gets the index of this variable within the <c>VariableScope.Variables</c> collection.
/// </summary>
/// <remarks>
/// This property is set automatically when the variable is added to the <c>VariableScope.Variables</c> collection.
/// It may change if an item with a lower index is removed from the collection.
/// </remarks>
public int IndexInScope { get; internal set; }
/// <summary>
/// Number of ldloc instructions referencing this variable.
/// </summary>

2
ICSharpCode.Decompiler/IL/Instructions.cs

@ -442,7 +442,7 @@ namespace ICSharpCode.Decompiler.IL @@ -442,7 +442,7 @@ namespace ICSharpCode.Decompiler.IL
}
/// <summary>A container of IL blocks.</summary>
public sealed partial class ILFunction : ILInstruction
public sealed partial class ILFunction : ILVariableScope
{
public static readonly SlotInfo BodySlot = new SlotInfo("Body");
ILInstruction body;

3
ICSharpCode.Decompiler/IL/Instructions.tt

@ -41,7 +41,8 @@ @@ -41,7 +41,8 @@
new OpCode("ILFunction", "A container of IL blocks.",
CustomChildren(new [] {
new ChildInfo("body")
}), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("O")),
}), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("O")
) { BaseClass = "ILVariableScope" },
new OpCode("BlockContainer", "A container of IL blocks.",
VoidResult, CustomConstructor, CustomVariableName("container")),
new OpCode("Block", "A block of IL instructions.",

4
ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs

@ -110,9 +110,9 @@ namespace ICSharpCode.Decompiler.IL @@ -110,9 +110,9 @@ namespace ICSharpCode.Decompiler.IL
}
}
internal override void CheckInvariant()
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant();
base.CheckInvariant(phase);
Debug.Assert(CompoundAssignmentType == CompoundAssignmentType.None || IsValidCompoundAssignmentTarget(Left));
}

4
ICSharpCode.Decompiler/IL/Instructions/Block.cs

@ -91,9 +91,9 @@ namespace ICSharpCode.Decompiler.IL @@ -91,9 +91,9 @@ namespace ICSharpCode.Decompiler.IL
return clone;
}
internal override void CheckInvariant()
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant();
base.CheckInvariant(phase);
for (int i = 0; i < Instructions.Count - 1; i++) {
// only the last instruction may have an unreachable endpoint
Debug.Assert(!Instructions[i].HasFlag(InstructionFlags.EndPointUnreachable));

4
ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs

@ -139,9 +139,9 @@ namespace ICSharpCode.Decompiler.IL @@ -139,9 +139,9 @@ namespace ICSharpCode.Decompiler.IL
return BlockSlot;
}
internal override void CheckInvariant()
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant();
base.CheckInvariant(phase);
Debug.Assert(EntryPoint == Blocks[0]);
Debug.Assert(!IsConnected || EntryPoint.IncomingEdgeCount >= 1);
Debug.Assert(Blocks.All(b => b.HasFlag(InstructionFlags.EndPointUnreachable)));

10
ICSharpCode.Decompiler/IL/Instructions/Branch.cs

@ -31,10 +31,6 @@ namespace ICSharpCode.Decompiler.IL @@ -31,10 +31,6 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
/// <remarks>
/// When jumping to the entrypoint of the current block container, the branch represents a <c>continue</c> statement.
///
/// Phase-1 execution of a branch is a no-op.
/// Phase-2 execution removes PopCount elements from the evaluation stack
/// and jumps to the target block.
/// </remarks>
partial class Branch : SimpleInstruction
{
@ -92,10 +88,10 @@ namespace ICSharpCode.Decompiler.IL @@ -92,10 +88,10 @@ namespace ICSharpCode.Decompiler.IL
get { return targetBlock != null ? targetBlock.Label : CecilExtensions.OffsetToString(TargetILOffset); }
}
internal override void CheckInvariant()
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant();
if (targetBlock != null) {
base.CheckInvariant(phase);
if (phase > ILPhase.InILReader) {
Debug.Assert(targetBlock.Parent is BlockContainer);
Debug.Assert(this.IsDescendantOf(targetBlock.Parent));
}

1
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -26,7 +26,6 @@ namespace ICSharpCode.Decompiler.IL @@ -26,7 +26,6 @@ namespace ICSharpCode.Decompiler.IL
partial class ILFunction
{
public readonly MethodDefinition Method;
public readonly IList<ILVariable> Variables = new List<ILVariable>();
public ILFunction(MethodDefinition method, ILInstruction body) : base(OpCode.ILFunction)
{

18
ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs

@ -27,6 +27,20 @@ using ICSharpCode.Decompiler.CSharp; @@ -27,6 +27,20 @@ using ICSharpCode.Decompiler.CSharp;
namespace ICSharpCode.Decompiler.IL
{
internal enum ILPhase
{
/// <summary>
/// Reading the individual instructions.
/// * Variables don't have scopes yet as the ILFunction is not created yet.
/// * Branches point to IL offsets, not blocks.
/// </summary>
InILReader,
/// <summary>
/// The usual invariants are established.
/// </summary>
Normal,
}
/// <summary>
/// Represents a decoded IL instruction
/// </summary>
@ -47,7 +61,7 @@ namespace ICSharpCode.Decompiler.IL @@ -47,7 +61,7 @@ namespace ICSharpCode.Decompiler.IL
}
[Conditional("DEBUG")]
internal virtual void CheckInvariant()
internal virtual void CheckInvariant(ILPhase phase)
{
foreach (var child in Children) {
Debug.Assert(child.Parent == this);
@ -55,7 +69,7 @@ namespace ICSharpCode.Decompiler.IL @@ -55,7 +69,7 @@ namespace ICSharpCode.Decompiler.IL
// if child flags are invalid, parent flags must be too
Debug.Assert(child.flags != invalidFlags || this.flags == invalidFlags);
Debug.Assert(child.IsConnected == this.IsConnected);
child.CheckInvariant();
child.CheckInvariant(phase);
}
}

4
ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs

@ -40,9 +40,9 @@ namespace ICSharpCode.Decompiler.IL @@ -40,9 +40,9 @@ namespace ICSharpCode.Decompiler.IL
this.FalseInst = falseInst ?? new Nop();
}
internal override void CheckInvariant()
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant();
base.CheckInvariant(phase);
Debug.Assert(condition.ResultType == StackType.I4);
}

6
ICSharpCode.Decompiler/IL/Instructions/Leave.cs

@ -81,10 +81,10 @@ namespace ICSharpCode.Decompiler.IL @@ -81,10 +81,10 @@ namespace ICSharpCode.Decompiler.IL
get { return targetContainer != null ? targetContainer.EntryPoint.Label : string.Empty; }
}
internal override void CheckInvariant()
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant();
Debug.Assert(targetContainer == null || this.IsDescendantOf(targetContainer));
base.CheckInvariant(phase);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(targetContainer));
}
public override void WriteTo(ITextOutput output)

19
ICSharpCode.Decompiler/IL/Instructions/LocalVarInstructions.cs

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Diagnostics;
namespace ICSharpCode.Decompiler.IL
{
@ -33,6 +34,12 @@ namespace ICSharpCode.Decompiler.IL @@ -33,6 +34,12 @@ namespace ICSharpCode.Decompiler.IL
variable.LoadCount--;
base.Disconnected();
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Scope));
}
}
partial class LdLoca
@ -48,6 +55,12 @@ namespace ICSharpCode.Decompiler.IL @@ -48,6 +55,12 @@ namespace ICSharpCode.Decompiler.IL
variable.AddressCount--;
base.Disconnected();
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Scope));
}
}
partial class StLoc
@ -63,5 +76,11 @@ namespace ICSharpCode.Decompiler.IL @@ -63,5 +76,11 @@ namespace ICSharpCode.Decompiler.IL
variable.StoreCount--;
base.Disconnected();
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Scope));
}
}
}

13
ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs

@ -46,8 +46,6 @@ namespace ICSharpCode.Decompiler.IL @@ -46,8 +46,6 @@ namespace ICSharpCode.Decompiler.IL
/// Try-catch statement.
/// </summary>
/// <remarks>
/// The evaluation stack does not need to be empty when entering or leaving a try-catch-block.
/// All try or catch blocks with reachable endpoint must produce compatible stacks.
/// The return value of the try or catch blocks is ignored, the TryCatch always returns void.
/// </remarks>
partial class TryCatch : TryInstruction
@ -125,20 +123,19 @@ namespace ICSharpCode.Decompiler.IL @@ -125,20 +123,19 @@ namespace ICSharpCode.Decompiler.IL
///
/// When an exception occurs in the try block of the parent try.catch statement, the runtime searches
/// the nearest enclosing TryCatchHandler with a matching variable type and
/// assigns the exception object to the <see cref="Variable"/>.
/// Then, the evaluation stack is cleared and the <see cref="Filter"/> is executed
/// (first phase-1 execution, which should be a no-op given the empty stack, then phase-2 execution).
/// assigns the exception object to the <see cref="Variable"/>, and executes the <see cref="Filter"/>.
/// If the filter evaluates to 0, the exception is not caught and the runtime looks for the next catch handler.
/// If the filter evaluates to 1, the stack is unwound, the exception caught and assigned to the <see cref="Variable"/>,
/// the evaluation stack is cleared again, and the <see cref="Body"/> is executed (again, phase-1 + phase-2).
/// and the <see cref="Body"/> is executed.
/// </summary>
partial class TryCatchHandler
{
internal override void CheckInvariant()
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant();
base.CheckInvariant(phase);
Debug.Assert(Parent is TryCatch);
Debug.Assert(filter.ResultType == StackType.I4);
Debug.Assert(this.IsDescendantOf(variable.Scope));
}
public override StackType ResultType {

162
ICSharpCode.Decompiler/IL/Instructions/VariableScope.cs

@ -0,0 +1,162 @@ @@ -0,0 +1,162 @@
// Copyright (c) 2016 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>
/// An ILInstruction that provides a scope for local variables.
/// </summary>
public abstract class ILVariableScope : ILInstruction
{
public readonly ILVariableCollection Variables;
protected ILVariableScope(OpCode opCode) : base(opCode)
{
this.Variables = new ILVariableCollection(this);
}
internal override void CheckInvariant(ILPhase phase)
{
for (int i = 0; i < Variables.Count; i++) {
Debug.Assert(Variables[i].Scope == this);
Debug.Assert(Variables[i].IndexInScope == i);
}
base.CheckInvariant(phase);
}
}
/// <summary>
/// The collection of variables in a <c>ILVariableScope</c>.
/// </summary>
public class ILVariableCollection : ICollection<ILVariable>, IReadOnlyList<ILVariable>
{
readonly ILVariableScope scope;
readonly List<ILVariable> list = new List<ILVariable>();
internal ILVariableCollection(ILVariableScope scope)
{
this.scope = scope;
}
/// <summary>
/// Gets a variable given its <c>IndexInScope</c>.
/// </summary>
public ILVariable this[int index] {
get {
return list[index];
}
}
public bool Add(ILVariable item)
{
if (item.Scope != null) {
if (item.Scope == scope)
return false;
else
throw new ArgumentException("Variable already belongs to another scope");
}
item.Scope = scope;
item.IndexInScope = list.Count;
list.Add(item);
return true;
}
void ICollection<ILVariable>.Add(ILVariable item)
{
Add(item);
}
public void Clear()
{
foreach (var v in list) {
v.Scope = null;
}
list.Clear();
}
public bool Contains(ILVariable item)
{
Debug.Assert(item.Scope != scope || list[item.IndexInScope] == item);
return item.Scope == scope;
}
public bool Remove(ILVariable item)
{
if (item.Scope != scope)
return false;
Debug.Assert(list[item.IndexInScope] == item);
RemoveAt(item.IndexInScope);
return true;
}
void RemoveAt(int index)
{
// swap-remove index
list[index] = list[list.Count - 1];
list[index].IndexInScope = index;
list.RemoveAt(list.Count - 1);
}
/// <summary>
/// Remove variables that have StoreCount == LoadCount == AddressCount == 0.
/// </summary>
public void RemoveDead()
{
for (int i = 0; i < list.Count;) {
var v = list[i];
if (v.StoreCount == 0 && v.LoadCount == 0 && v.AddressCount == 0) {
RemoveAt(i);
} else {
i++;
}
}
}
public int Count {
get { return list.Count; }
}
public void CopyTo(ILVariable[] array, int arrayIndex)
{
list.CopyTo(array, arrayIndex);
}
bool ICollection<ILVariable>.IsReadOnly {
get { return false; }
}
public List<ILVariable>.Enumerator GetEnumerator()
{
return list.GetEnumerator();
}
IEnumerator<ILVariable> IEnumerable<ILVariable>.GetEnumerator()
{
return GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

3
ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs

@ -49,8 +49,9 @@ namespace ICSharpCode.Decompiler.IL @@ -49,8 +49,9 @@ namespace ICSharpCode.Decompiler.IL
};
block.Instructions.Insert(i++, new StLoc(uninlinedArgs[j], arg));
}
v.Scope.Variables.AddRange(uninlinedArgs);
// perform copy propagation:
foreach (var expr in function.Descendants) {
foreach (var expr in v.Scope.Descendants) {
if (expr.MatchLdLoc(v)) {
var clone = copiedExpr.Clone();
for (int j = 0; j < uninlinedArgs.Length; j++) {

Loading…
Cancel
Save