Browse Source

Merge branch 'master' of git://github.com/icsharpcode/ILSpy into cust-attr

pull/52/head
Artur Zgodziski 15 years ago
parent
commit
0e4ca6d164
  1. 5
      ICSharpCode.Decompiler/Ast/AstBuilder.cs
  2. 125
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  3. 2
      ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs
  4. 8
      ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs
  5. 12
      ICSharpCode.Decompiler/Ast/NameVariables.cs
  6. 20
      ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs
  7. 376
      ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
  8. 11
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  9. 3
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  10. 1
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  11. 2
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  12. 50
      ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs
  13. 74
      ICSharpCode.Decompiler/Tests/ValueTypes.cs
  14. 1
      ILSpy/MainWindow.xaml
  15. 7
      ILSpy/MainWindow.xaml.cs

5
ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -560,8 +560,9 @@ namespace Decompiler @@ -560,8 +560,9 @@ namespace Decompiler
astParam.Type = ConvertType(paramDef.ParameterType, paramDef);
astParam.Name = paramDef.Name;
if (!paramDef.IsIn && paramDef.IsOut) astParam.ParameterModifier = ParameterModifier.Out;
if (paramDef.IsIn && paramDef.IsOut) astParam.ParameterModifier = ParameterModifier.Ref;
if (paramDef.ParameterType is ByReferenceType) {
astParam.ParameterModifier = paramDef.IsOut ? ParameterModifier.Out : ParameterModifier.Ref;
}
// TODO: params, this
yield return astParam;

125
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -423,26 +423,36 @@ namespace Decompiler @@ -423,26 +423,36 @@ namespace Decompiler
return InlineAssembly(byteCode, args);
#endregion
#region Indirect
case Code.Ldind_I: return InlineAssembly(byteCode, args);
case Code.Ldind_I1: return InlineAssembly(byteCode, args);
case Code.Ldind_I2: return InlineAssembly(byteCode, args);
case Code.Ldind_I4: return InlineAssembly(byteCode, args);
case Code.Ldind_I8: return InlineAssembly(byteCode, args);
case Code.Ldind_U1: return InlineAssembly(byteCode, args);
case Code.Ldind_U2: return InlineAssembly(byteCode, args);
case Code.Ldind_U4: return InlineAssembly(byteCode, args);
case Code.Ldind_R4: return InlineAssembly(byteCode, args);
case Code.Ldind_R8: return InlineAssembly(byteCode, args);
case Code.Ldind_Ref: return InlineAssembly(byteCode, args);
case Code.Ldind_I:
case Code.Ldind_I1:
case Code.Ldind_I2:
case Code.Ldind_I4:
case Code.Ldind_I8:
case Code.Ldind_U1:
case Code.Ldind_U2:
case Code.Ldind_U4:
case Code.Ldind_R4:
case Code.Ldind_R8:
case Code.Ldind_Ref:
case Code.Ldobj:
if (args[0] is DirectionExpression)
return ((DirectionExpression)args[0]).Expression.Detach();
else
return InlineAssembly(byteCode, args);
case Code.Stind_I: return InlineAssembly(byteCode, args);
case Code.Stind_I1: return InlineAssembly(byteCode, args);
case Code.Stind_I2: return InlineAssembly(byteCode, args);
case Code.Stind_I4: return InlineAssembly(byteCode, args);
case Code.Stind_I8: return InlineAssembly(byteCode, args);
case Code.Stind_R4: return InlineAssembly(byteCode, args);
case Code.Stind_R8: return InlineAssembly(byteCode, args);
case Code.Stind_Ref: return InlineAssembly(byteCode, args);
case Code.Stind_I:
case Code.Stind_I1:
case Code.Stind_I2:
case Code.Stind_I4:
case Code.Stind_I8:
case Code.Stind_R4:
case Code.Stind_R8:
case Code.Stind_Ref:
case Code.Stobj:
if (args[0] is DirectionExpression)
return new AssignmentExpression(((DirectionExpression)args[0]).Expression.Detach(), args[1]);
else
return InlineAssembly(byteCode, args);
#endregion
case Code.Arglist: return InlineAssembly(byteCode, args);
case Code.Break: return InlineAssembly(byteCode, args);
@ -478,13 +488,24 @@ namespace Decompiler @@ -478,13 +488,24 @@ namespace Decompiler
case Code.Endfilter: return InlineAssembly(byteCode, args);
case Code.Endfinally: return null;
case Code.Initblk: return InlineAssembly(byteCode, args);
case Code.Initobj: return InlineAssembly(byteCode, args);
case Code.Initobj:
if (args[0] is DirectionExpression)
return new AssignmentExpression(((DirectionExpression)args[0]).Expression.Detach(), new DefaultValueExpression { Type = operandAsTypeRef });
else
return InlineAssembly(byteCode, args);
case Code.Jmp: return InlineAssembly(byteCode, args);
case Code.Ldarg:
if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) {
return new Ast.ThisReferenceExpression();
if (context.CurrentMethod.DeclaringType.IsValueType)
return MakeRef(new Ast.ThisReferenceExpression());
else
return new Ast.ThisReferenceExpression();
} else {
return new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand);
var expr = new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand);
if (((ParameterDefinition)operand).ParameterType is ByReferenceType)
return MakeRef(expr);
else
return expr;
}
case Code.Ldarga:
if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) {
@ -493,17 +514,21 @@ namespace Decompiler @@ -493,17 +514,21 @@ namespace Decompiler
return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand));
}
case Code.Ldc_I4:
return PrimitiveExpression((int)operand, byteCode.InferredType);
return MakePrimitive((int)operand, byteCode.InferredType);
case Code.Ldc_I8:
case Code.Ldc_R4:
case Code.Ldc_R8:
return new Ast.PrimitiveExpression(operand);
case Code.Ldfld:
if (arg1 is DirectionExpression)
arg1 = ((DirectionExpression)arg1).Expression.Detach();
return arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand);
case Code.Ldsfld:
return AstBuilder.ConvertType(((FieldReference)operand).DeclaringType)
.Member(((FieldReference)operand).Name).WithAnnotation(operand);
case Code.Stfld:
if (arg1 is DirectionExpression)
arg1 = ((DirectionExpression)arg1).Expression.Detach();
return new AssignmentExpression(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand), arg2);
case Code.Stsfld:
return new AssignmentExpression(
@ -517,11 +542,13 @@ namespace Decompiler @@ -517,11 +542,13 @@ namespace Decompiler
AstBuilder.ConvertType(((FieldReference)operand).DeclaringType)
.Member(((FieldReference)operand).Name).WithAnnotation(operand));
case Code.Ldloc:
localVariablesToDefine.Add((ILVariable)operand);
return new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand);
case Code.Ldloca:
localVariablesToDefine.Add((ILVariable)operand);
return MakeRef(new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand));
case Code.Ldnull: return new Ast.NullReferenceExpression();
case Code.Ldobj: return InlineAssembly(byteCode, args);
case Code.Ldnull:
return new Ast.NullReferenceExpression();
case Code.Ldstr: return new Ast.PrimitiveExpression(operand);
case Code.Ldtoken:
if (operand is Cecil.TypeReference) {
@ -562,17 +589,15 @@ namespace Decompiler @@ -562,17 +589,15 @@ namespace Decompiler
}
}
case Code.Rethrow: return new Ast.ThrowStatement();
case Code.Sizeof: return new Ast.SizeOfExpression { Type = AstBuilder.ConvertType(operand as TypeReference) };
case Code.Sizeof:
return new Ast.SizeOfExpression { Type = operandAsTypeRef };
case Code.Starg:
return new Ast.AssignmentExpression(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand), arg1);
case Code.Stloc: {
ILVariable locVar = (ILVariable)operand;
if (!localVariablesToDefine.Contains(locVar)) {
localVariablesToDefine.Add(locVar);
}
localVariablesToDefine.Add(locVar);
return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name).WithAnnotation(locVar), arg1);
}
case Code.Stobj: return InlineAssembly(byteCode, args);
case Code.Switch: return InlineAssembly(byteCode, args);
case Code.Tail: return InlineAssembly(byteCode, args);
case Code.Throw: return new Ast.ThrowStatement { Expression = arg1 };
@ -616,11 +641,22 @@ namespace Decompiler @@ -616,11 +641,22 @@ namespace Decompiler
if (prop.GetMethod == cecilMethodDef)
return target.Member(prop.Name).WithAnnotation(prop);
}
} else if (cecilMethodDef.IsGetter) { // with parameters
PropertyDefinition indexer = GetIndexer(cecilMethodDef);
if (indexer != null)
return target.Indexer(methodArgs).WithAnnotation(indexer);
} else if (cecilMethodDef.IsSetter && methodArgs.Count == 1) {
foreach (var prop in cecilMethodDef.DeclaringType.Properties) {
if (prop.SetMethod == cecilMethodDef)
return new Ast.AssignmentExpression(target.Member(prop.Name).WithAnnotation(prop), methodArgs[0]);
}
} else if (cecilMethodDef.IsSetter && methodArgs.Count > 1) {
PropertyDefinition indexer = GetIndexer(cecilMethodDef);
if (indexer != null)
return new AssignmentExpression(
target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)).WithAnnotation(indexer),
methodArgs[methodArgs.Count - 1]
);
} else if (cecilMethodDef.IsAddOn && methodArgs.Count == 1) {
foreach (var ev in cecilMethodDef.DeclaringType.Events) {
if (ev.AddMethod == cecilMethodDef) {
@ -647,6 +683,27 @@ namespace Decompiler @@ -647,6 +683,27 @@ namespace Decompiler
return target.Invoke(cecilMethod.Name, ConvertTypeArguments(cecilMethod), methodArgs).WithAnnotation(cecilMethod);
}
static PropertyDefinition GetIndexer(MethodDefinition cecilMethodDef)
{
TypeDefinition typeDef = cecilMethodDef.DeclaringType;
string indexerName = null;
foreach (CustomAttribute ca in typeDef.CustomAttributes) {
if (ca.Constructor.FullName == "System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") {
indexerName = ca.ConstructorArguments.Single().Value as string;
break;
}
}
if (indexerName == null)
return null;
foreach (PropertyDefinition prop in typeDef.Properties) {
if (prop.Name == indexerName) {
if (prop.GetMethod == cecilMethodDef || prop.SetMethod == cecilMethodDef)
return prop;
}
}
return null;
}
#if DEBUG
static readonly ConcurrentDictionary<ILCode, int> unhandledOpcodes = new ConcurrentDictionary<ILCode, int>();
#endif
@ -706,7 +763,7 @@ namespace Decompiler @@ -706,7 +763,7 @@ namespace Decompiler
if (TypeAnalysis.IsBoolean(actualType))
return expr;
if (actualIsIntegerOrEnum) {
return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, PrimitiveExpression(0, actualType));
return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, MakePrimitive(0, actualType));
} else {
return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, new NullReferenceExpression());
}
@ -714,8 +771,8 @@ namespace Decompiler @@ -714,8 +771,8 @@ namespace Decompiler
if (TypeAnalysis.IsBoolean(actualType) && requiredIsIntegerOrEnum) {
return new ConditionalExpression {
Condition = expr,
TrueExpression = PrimitiveExpression(1, reqType),
FalseExpression = PrimitiveExpression(0, reqType)
TrueExpression = MakePrimitive(1, reqType),
FalseExpression = MakePrimitive(0, reqType)
};
}
if (actualIsIntegerOrEnum && requiredIsIntegerOrEnum) {
@ -725,7 +782,7 @@ namespace Decompiler @@ -725,7 +782,7 @@ namespace Decompiler
}
}
Expression PrimitiveExpression(long val, TypeReference type)
Expression MakePrimitive(long val, TypeReference type)
{
if (TypeAnalysis.IsBoolean(type) && val == 0)
return new Ast.PrimitiveExpression(false);

2
ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs

@ -34,6 +34,8 @@ namespace Decompiler @@ -34,6 +34,8 @@ namespace Decompiler
Match m = assignmentPattern.Match(pos);
if (m != null && m.Get<IdentifierExpression>("ident").Single().Identifier == name) {
result = new VariableDeclarationStatement(type, name, m.Get<Expression>("init").Single().Detach());
result.Variables.Single().CopyAnnotationsFrom(((ExpressionStatement)pos).Expression);
result.CopyAnnotationsFrom(pos);
pos.ReplaceWith(result);
} else {
result = new VariableDeclarationStatement(type, name);

8
ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs

@ -15,6 +15,14 @@ namespace Decompiler @@ -15,6 +15,14 @@ namespace Decompiler
return node;
}
public static T CopyAnnotationsFrom<T>(this T node, AstNode other) where T : AstNode
{
foreach (var annotation in other.Annotations<object>()) {
node.AddAnnotation(annotation);
}
return node;
}
public static T Detach<T>(this T node) where T : AstNode
{
node.Remove();

12
ICSharpCode.Decompiler/Ast/NameVariables.cs

@ -25,6 +25,7 @@ namespace Decompiler @@ -25,6 +25,7 @@ namespace Decompiler
{ "System.Decimal", "num" },
{ "System.String", "text" },
{ "System.Object", "obj" },
{ "System.Char", "c" }
};
@ -98,17 +99,20 @@ namespace Decompiler @@ -98,17 +99,20 @@ namespace Decompiler
case ILCode.Ldfld:
// Use the field name only if it's not a field on this (avoid confusion between local variables and fields)
if (!(expr.Arguments[0].Code == ILCode.Ldarg && ((ParameterDefinition)expr.Arguments[0].Operand).Index < 0))
return ((FieldReference)expr.Operand).Name;
return CleanUpVariableName(((FieldReference)expr.Operand).Name);
break;
case ILCode.Ldsfld:
return ((FieldReference)expr.Operand).Name;
return CleanUpVariableName(((FieldReference)expr.Operand).Name);
case ILCode.Call:
case ILCode.Callvirt:
MethodReference mr = (MethodReference)expr.Operand;
if (mr.Name.StartsWith("get_", StringComparison.Ordinal))
if (mr.Name.StartsWith("get_", StringComparison.Ordinal) && mr.Parameters.Count == 0) {
// use name from properties, but not from indexers
return CleanUpVariableName(mr.Name.Substring(4));
else if (mr.Name.StartsWith("Get", StringComparison.Ordinal) && mr.Name.Length >= 4 && char.IsUpper(mr.Name[3]))
} else if (mr.Name.StartsWith("Get", StringComparison.Ordinal) && mr.Name.Length >= 4 && char.IsUpper(mr.Name[3])) {
// use name from Get-methods
return CleanUpVariableName(mr.Name.Substring(3));
}
break;
}
return null;

20
ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.CSharp.PatternMatching;
using Mono.Cecil;
using Ast = ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp;
@ -142,7 +143,7 @@ namespace Decompiler.Transforms @@ -142,7 +143,7 @@ namespace Decompiler.Transforms
public override object VisitAssignmentExpression(AssignmentExpression assignment, object data)
{
base.VisitAssignmentExpression(assignment, data);
// First, combine "x = x op y" into "x op= y"
// Combine "x = x op y" into "x op= y"
BinaryOperatorExpression binary = assignment.Right as BinaryOperatorExpression;
if (binary != null && assignment.Operator == AssignmentOperatorType.Assign) {
if (IsWithoutSideEffects(assignment.Left) && assignment.Left.Match(binary.Left) != null) {
@ -180,10 +181,27 @@ namespace Decompiler.Transforms @@ -180,10 +181,27 @@ namespace Decompiler.Transforms
}
if (assignment.Operator != AssignmentOperatorType.Assign) {
// If we found a shorter operator, get rid of the BinaryOperatorExpression:
assignment.CopyAnnotationsFrom(binary);
assignment.Right = binary.Right;
}
}
}
if (assignment.Operator == AssignmentOperatorType.Add || assignment.Operator == AssignmentOperatorType.Subtract) {
// detect increment/decrement
if (assignment.Right.Match(new PrimitiveExpression(1)) != null) {
// only if it's not a custom operator
if (assignment.Annotation<MethodReference>() == null) {
UnaryOperatorType type;
// When the parent is an expression statement, pre- or post-increment doesn't matter;
// so we can pick post-increment which is more commonly used (for (int i = 0; i < x; i++))
if (assignment.Parent is ExpressionStatement)
type = (assignment.Operator == AssignmentOperatorType.Add) ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement;
else
type = (assignment.Operator == AssignmentOperatorType.Add) ? UnaryOperatorType.Increment : UnaryOperatorType.Decrement;
assignment.ReplaceWith(new UnaryOperatorExpression(type, assignment.Left.Detach()).CopyAnnotationsFrom(assignment));
}
}
}
return null;
}

376
ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs

@ -12,8 +12,8 @@ namespace Decompiler @@ -12,8 +12,8 @@ namespace Decompiler
{
class StackSlot
{
public List<ByteCode> PushedBy; // Pushed by one of these; null element means exception pushed by CLR
public ILVariable LoadFrom;
public List<ByteCode> PushedBy; // One of those
public ILVariable LoadFrom; // Where can we get the value from in AST
public StackSlot()
{
@ -24,41 +24,93 @@ namespace Decompiler @@ -24,41 +24,93 @@ namespace Decompiler
this.PushedBy = new List<ByteCode>(1);
this.PushedBy.Add(pushedBy);
}
public static List<StackSlot> CloneStack(List<StackSlot> stack, int? popCount)
{
List<StackSlot> clone = new List<StackSlot>();
if (popCount.HasValue) {
if (popCount.Value > stack.Count) {
throw new Exception("Can not pop - the stack is empty");
}
for(int i = 0; i < stack.Count - popCount.Value; i++) {
clone.Add(new StackSlot() { PushedBy = new List<ByteCode>(stack[i].PushedBy) });
}
}
return clone;
}
}
class VariableSlot
{
public static List<ByteCode> Empty = new List<ByteCode>();
public List<ByteCode> StoredBy = Empty; // One of those
public bool StoredByAll; // Overestimate which is useful for exceptional control flow.
public static VariableSlot[] CloneVariableState(VariableSlot[] state)
{
VariableSlot[] clone = new ILAstBuilder.VariableSlot[state.Length];
if (VariableSlot.Empty.Count > 0)
throw new Exception("Constant data corrupted");
for (int i = 0; i < clone.Length; i++) {
VariableSlot varSlot = state[i];
clone[i] = new VariableSlot() {
StoredBy = varSlot.StoredBy.Count == 0 ? VariableSlot.Empty : new List<ByteCode>(varSlot.StoredBy),
StoredByAll = varSlot.StoredByAll
};
}
return clone;
}
public static VariableSlot[] MakeEmptyState(int varCount)
{
VariableSlot[] emptyVariableState = new VariableSlot[varCount];
for (int i = 0; i < emptyVariableState.Length; i++) {
emptyVariableState[i] = new VariableSlot();
}
return emptyVariableState;
}
public static VariableSlot[] MakeFullState(int varCount)
{
VariableSlot[] unknownVariableState = new VariableSlot[varCount];
for (int i = 0; i < unknownVariableState.Length; i++) {
unknownVariableState[i] = new VariableSlot() { StoredByAll = true };
}
return unknownVariableState;
}
}
class ByteCode
{
public ILLabel Label; // Non-null only if needed
public ILLabel Label; // Non-null only if needed
public int Offset;
public int EndOffset;
public ILCode Code;
public object Operand;
public int? PopCount; // Null means pop all
public int? PopCount; // Null means pop all
public int PushCount;
public string Name { get { return "IL_" + this.Offset.ToString("X2"); } }
public ByteCode Next;
public Instruction[] Prefixes; // Non-null only if needed
public List<StackSlot> StackBefore;
public List<ILVariable> StoreTo;
public Instruction[] Prefixes; // Non-null only if needed
public List<StackSlot> StackBefore;
public List<ILVariable> StoreTo; // Store result of instruction to those AST variables
public VariableSlot[] VariablesBefore;
public List<StackSlot> CloneStack(int? popCount)
{
List<StackSlot> clone = new List<StackSlot>();
if (popCount.HasValue) {
if (popCount.Value > this.StackBefore.Count) {
throw new Exception("Can not pop - the stack is empty");
}
for(int i = 0; i < this.StackBefore.Count - popCount.Value; i++) {
clone.Add(new StackSlot() { PushedBy = new List<ByteCode>(this.StackBefore[i].PushedBy) });
}
}
return clone;
}
public VariableDefinition OperandAsVariable { get { return (VariableDefinition)this.Operand; } }
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0}:{1} ", this.Name, this.Label != null ? " *" : "");
// Label
sb.Append(this.Name);
sb.Append(':');
if (this.Label != null)
sb.Append('*');
// Name
sb.Append(' ');
if (this.Prefixes != null) {
foreach (var prefix in this.Prefixes) {
sb.Append(prefix.OpCode.Name);
@ -66,18 +118,30 @@ namespace Decompiler @@ -66,18 +118,30 @@ namespace Decompiler
}
}
sb.Append(this.Code.GetName());
if (this.Operand is ILLabel) {
sb.Append(((ILLabel)this.Operand).Name);
} else if (this.Operand is ILLabel[]) {
foreach(ILLabel label in (ILLabel[])this.Operand) {
sb.Append(label.Name);
sb.Append(" ");
if (this.Operand != null) {
sb.Append(' ');
if (this.Operand is Instruction) {
sb.Append("IL_" + ((Instruction)this.Operand).Offset.ToString("X2"));
} else if (this.Operand is Instruction[]) {
foreach(Instruction inst in (Instruction[])this.Operand) {
sb.Append("IL_" + inst.Offset.ToString("X2"));
sb.Append(" ");
}
} else if (this.Operand is ILLabel) {
sb.Append(((ILLabel)this.Operand).Name);
} else if (this.Operand is ILLabel[]) {
foreach(ILLabel label in (ILLabel[])this.Operand) {
sb.Append(label.Name);
sb.Append(" ");
}
} else {
sb.Append(this.Operand.ToString());
}
} else {
sb.Append(this.Operand.ToString());
}
if (this.StackBefore != null) {
sb.Append(" StackBefore = {");
sb.Append(" StackBefore={");
bool first = true;
foreach (StackSlot slot in this.StackBefore) {
if (!first) sb.Append(",");
@ -91,8 +155,9 @@ namespace Decompiler @@ -91,8 +155,9 @@ namespace Decompiler
}
sb.Append("}");
}
if (this.StoreTo != null && this.StoreTo.Count > 0) {
sb.Append(" StoreTo = {");
sb.Append(" StoreTo={");
bool first = true;
foreach (ILVariable stackVar in this.StoreTo) {
if (!first) sb.Append(",");
@ -101,6 +166,29 @@ namespace Decompiler @@ -101,6 +166,29 @@ namespace Decompiler
}
sb.Append("}");
}
if (this.VariablesBefore != null) {
sb.Append(" VarsBefore={");
bool first = true;
foreach (VariableSlot varSlot in this.VariablesBefore) {
if (!first) sb.Append(",");
if (varSlot.StoredByAll) {
sb.Append("*");
} else if (varSlot.StoredBy.Count == 0) {
sb.Append("_");
} else {
bool first2 = true;
foreach (ByteCode storedBy in varSlot.StoredBy) {
if (!first2) sb.Append("|");
sb.AppendFormat("IL_{0:X2}", storedBy.Offset);
first2 = false;
}
}
first = false;
}
sb.Append("}");
}
return sb.ToString();
}
}
@ -167,18 +255,13 @@ namespace Decompiler @@ -167,18 +255,13 @@ namespace Decompiler
body[i].Next = body[i + 1];
}
Queue<ByteCode> agenda = new Queue<ByteCode>();
Stack<ByteCode> agenda = new Stack<ByteCode>();
// Add known states
body[0].StackBefore = new List<StackSlot>();
agenda.Enqueue(body[0]);
int varCount = methodDef.Body.Variables.Count;
// Add known states
if(methodDef.Body.HasExceptionHandlers) {
foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) {
ByteCode tryStart = instrToByteCode[ex.TryStart];
tryStart.StackBefore = new List<StackSlot>();
agenda.Enqueue(tryStart);
ByteCode handlerStart = instrToByteCode[ex.HandlerType == ExceptionHandlerType.Filter ? ex.FilterStart : ex.HandlerStart];
handlerStart.StackBefore = new List<StackSlot>();
if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter) {
@ -191,27 +274,39 @@ namespace Decompiler @@ -191,27 +274,39 @@ namespace Decompiler
ldexceptions[ex] = ldexception;
handlerStart.StackBefore.Add(new StackSlot(ldexception));
}
agenda.Enqueue(handlerStart);
// Control flow is not required to reach endfilter
if (ex.HandlerType == ExceptionHandlerType.Filter) {
ByteCode endFilter = instrToByteCode[ex.FilterEnd.Previous];
endFilter.StackBefore = new List<StackSlot>();
}
handlerStart.VariablesBefore = VariableSlot.MakeFullState(varCount);
agenda.Push(handlerStart);
}
}
body[0].StackBefore = new List<StackSlot>();
body[0].VariablesBefore = VariableSlot.MakeEmptyState(varCount);
agenda.Push(body[0]);
// Process agenda
while(agenda.Count > 0) {
ByteCode byteCode = agenda.Dequeue();
ByteCode byteCode = agenda.Pop();
// Calculate new stack
List<StackSlot> newStack = byteCode.CloneStack(byteCode.PopCount);
List<StackSlot> newStack = StackSlot.CloneStack(byteCode.StackBefore, byteCode.PopCount);
for (int i = 0; i < byteCode.PushCount; i++) {
newStack.Add(new StackSlot(byteCode));
}
// Apply the state to any successors
// Calculate new variable state
VariableSlot[] newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore);
if (byteCode.Code == ILCode.Stloc) {
int varIndex = ((VariableReference)byteCode.Operand).Index;
newVariableState[varIndex].StoredBy = new List<ByteCode>(1) { byteCode };
newVariableState[varIndex].StoredByAll = false;
}
// After the leave, finally block might have touched the variables
if (byteCode.Code == ILCode.Leave) {
newVariableState = VariableSlot.MakeFullState(varCount);
}
// Find all successors
List<ByteCode> branchTargets = new List<ByteCode>();
if (byteCode.Code.CanFallThough()) {
branchTargets.Add(byteCode.Next);
@ -233,21 +328,30 @@ namespace Decompiler @@ -233,21 +328,30 @@ namespace Decompiler
target.Label = new ILLabel() { Name = target.Name };
}
}
// Apply the state to successors
foreach (ByteCode branchTarget in branchTargets) {
if (branchTarget.StackBefore == null) {
branchTarget.StackBefore = newStack;
// Do not share one stack for several bytecodes
if (branchTargets.Count > 1) {
branchTarget.StackBefore = branchTarget.CloneStack(0);
if (branchTarget.StackBefore == null && branchTarget.VariablesBefore == null) {
if (branchTargets.Count == 1) {
branchTarget.StackBefore = newStack;
branchTarget.VariablesBefore = newVariableState;
} else {
// Do not share data for several bytecodes
branchTarget.StackBefore = StackSlot.CloneStack(newStack, 0);
branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState);
}
agenda.Enqueue(branchTarget);
agenda.Push(branchTarget);
} else {
if (branchTarget.StackBefore.Count != newStack.Count) {
throw new Exception("Inconsistent stack size at " + byteCode.Name);
}
// Merge stacks
// Be careful not to change our new data - it might be reused for several branch targets.
// In general, be careful that two bytecodes never share data structures.
bool modified = false;
// Merge stacks - modify the target
for (int i = 0; i < newStack.Count; i++) {
List<ByteCode> oldPushedBy = branchTarget.StackBefore[i].PushedBy;
List<ByteCode> newPushedBy = oldPushedBy.Union(newStack[i].PushedBy).ToList();
@ -257,8 +361,28 @@ namespace Decompiler @@ -257,8 +361,28 @@ namespace Decompiler
}
}
// Merge variables - modify the target
for (int i = 0; i < newVariableState.Length; i++) {
VariableSlot oldSlot = branchTarget.VariablesBefore[i];
VariableSlot newSlot = newVariableState[i];
// All can not be unioned further
if (!oldSlot.StoredByAll) {
if (newSlot.StoredByAll) {
oldSlot.StoredByAll = true;
modified = true;
} else {
List<ByteCode> oldStoredBy = oldSlot.StoredBy;
List<ByteCode> newStoredBy = oldStoredBy.Union(newSlot.StoredBy).ToList();
if (newStoredBy.Count > oldStoredBy.Count) {
oldSlot.StoredBy = newStoredBy;
modified = true;
}
}
}
}
if (modified) {
agenda.Enqueue(branchTarget);
agenda.Push(branchTarget);
}
}
}
@ -288,36 +412,8 @@ namespace Decompiler @@ -288,36 +412,8 @@ namespace Decompiler
}
}
// Convert local varibles
Variables = methodDef.Body.Variables.Select(v => new ILVariable() { Name = string.IsNullOrEmpty(v.Name) ? "var_" + v.Index : v.Name, Type = v.VariableType }).ToList();
int[] numReads = new int[Variables.Count];
int[] numWrites = new int[Variables.Count];
foreach(ByteCode byteCode in body) {
if (byteCode.Code == ILCode.Ldloc) {
int index = ((VariableDefinition)byteCode.Operand).Index;
byteCode.Operand = Variables[index];
numReads[index]++;
} else if (byteCode.Code == ILCode.Stloc) {
int index = ((VariableDefinition)byteCode.Operand).Index;
byteCode.Operand = Variables[index];
numWrites[index]++;
} else if (byteCode.Code == ILCode.Ldloca) {
int index = ((VariableDefinition)byteCode.Operand).Index;
byteCode.Operand = Variables[index];
// ldloca leads to an unknown numbers of reads/writes, so ensure we don't inline the variable
numReads[index] += 2;
numWrites[index] += 2;
}
}
// Find which variables we can inline
if (this.optimize) {
for (int i = 0; i < Variables.Count; i++) {
if (numReads[i] == 1 && numWrites[i] == 1) {
allowInline[Variables[i]] = true;
}
}
}
// Split and convert the normal local variables
ConvertLocalVariables(body);
// Convert branch targets to labels
foreach(ByteCode byteCode in body) {
@ -335,6 +431,108 @@ namespace Decompiler @@ -335,6 +431,108 @@ namespace Decompiler
return body;
}
class VariableInfo
{
public ILVariable Variable;
public List<ByteCode> Stores;
public List<ByteCode> Loads;
}
/// <summary>
/// If possible, separates local variables into several independent variables.
/// It should undo any compilers merging.
/// </summary>
void ConvertLocalVariables(List<ByteCode> body)
{
if (optimize) {
int varCount = methodDef.Body.Variables.Count;
this.Variables = new List<ILVariable>(varCount * 2);
for(int variableIndex = 0; variableIndex < varCount; variableIndex++) {
// Find all stores and loads for this variable
List<ByteCode> stores = body.Where(b => b.Code == ILCode.Stloc && b.Operand is VariableDefinition && b.OperandAsVariable.Index == variableIndex).ToList();
List<ByteCode> loads = body.Where(b => (b.Code == ILCode.Ldloc || b.Code == ILCode.Ldloca) && b.Operand is VariableDefinition && b.OperandAsVariable.Index == variableIndex).ToList();
TypeReference varType = methodDef.Body.Variables[variableIndex].VariableType;
List<VariableInfo> newVars;
// If any of the loads is from "all", use single variable
// If any of the loads is ldloca, fallback to single variable as well
if (loads.Any(b => b.VariablesBefore[variableIndex].StoredByAll || b.Code == ILCode.Ldloca)) {
newVars = new List<VariableInfo>(1) { new VariableInfo() {
Variable = new ILVariable() {
Name = "var_" + variableIndex,
Type = varType,
OriginalVariable = methodDef.Body.Variables[variableIndex]
},
Stores = stores,
Loads = loads
}};
} else {
// Create a new variable for each store
newVars = stores.Select(st => new VariableInfo() {
Variable = new ILVariable() {
Name = "var_" + variableIndex + "_" + st.Offset.ToString("X2"),
Type = varType,
OriginalVariable = methodDef.Body.Variables[variableIndex]
},
Stores = new List<ByteCode>() {st},
Loads = new List<ByteCode>()
}).ToList();
// Add loads to the data structure; merge variables if necessary
foreach(ByteCode load in loads) {
List<ByteCode> storedBy = load.VariablesBefore[variableIndex].StoredBy;
if (storedBy.Count == 0) {
throw new Exception("Load of uninitialized variable");
} else if (storedBy.Count == 1) {
VariableInfo newVar = newVars.Where(v => v.Stores.Contains(storedBy[0])).Single();
newVar.Loads.Add(load);
} else {
List<VariableInfo> mergeVars = newVars.Where(v => v.Stores.Union(storedBy).Any()).ToList();
VariableInfo mergedVar = new VariableInfo() {
Variable = mergeVars[0].Variable,
Stores = mergeVars.SelectMany(v => v.Stores).ToList(),
Loads = mergeVars.SelectMany(v => v.Loads).ToList()
};
mergedVar.Loads.Add(load);
newVars = newVars.Except(mergeVars).ToList();
newVars.Add(mergedVar);
}
}
// Permit inlining
foreach(VariableInfo newVar in newVars) {
if (newVar.Stores.Count == 1 && newVar.Loads.Count == 1) {
allowInline[newVar.Variable] = true;
}
}
}
// Set bytecode operands
foreach(VariableInfo newVar in newVars) {
foreach(ByteCode store in newVar.Stores) {
store.Operand = newVar.Variable;
}
foreach(ByteCode load in newVar.Loads) {
load.Operand = newVar.Variable;
}
}
// Record new variables to global list
this.Variables.AddRange(newVars.Select(v => v.Variable));
}
} else {
this.Variables = methodDef.Body.Variables.Select(v => new ILVariable() { Name = string.IsNullOrEmpty(v.Name) ? "var_" + v.Index : v.Name, Type = v.VariableType, OriginalVariable = v }).ToList();
foreach(ByteCode byteCode in body) {
if (byteCode.Code == ILCode.Ldloc || byteCode.Code == ILCode.Stloc || byteCode.Code == ILCode.Ldloca) {
int index = ((VariableDefinition)byteCode.Operand).Index;
byteCode.Operand = this.Variables[index];
}
}
}
}
List<ILNode> ConvertToAst(List<ByteCode> body, HashSet<ExceptionHandler> ehs)
{
List<ILNode> ast = new List<ILNode>();
@ -490,6 +688,7 @@ namespace Decompiler @@ -490,6 +688,7 @@ namespace Decompiler
// We are moving the expression evaluation past the other aguments.
// It is ok to pass ldloc because the expression can not contain stloc and thus the ldcoc will still return the same value
// Do not inline ldloca
if (arg.Code == ILCode.Ldloc) {
if (arg.Operand == currExpr.Operand) {
bool canInline;
@ -500,6 +699,9 @@ namespace Decompiler @@ -500,6 +699,9 @@ namespace Decompiler
currExpr.Arguments[0].ILRanges.AddRange(currExpr.ILRanges);
currExpr.Arguments[0].ILRanges.AddRange(nextExpr.Arguments[j].ILRanges);
// Remove from global list, if present
this.Variables.Remove((ILVariable)arg.Operand);
ast.RemoveAt(i);
nextExpr.Arguments[j] = currExpr.Arguments[0]; // Inline the stloc body
i -= 2; // Try the same index again

11
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -36,7 +36,7 @@ namespace Decompiler.ControlFlow @@ -36,7 +36,7 @@ namespace Decompiler.ControlFlow
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
ControlFlowGraph graph;
graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand);
graph.ComputeDominance();
graph.ComputeDominance(context.CancellationToken);
graph.ComputeDominanceFrontier();
block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint, false);
}
@ -49,7 +49,7 @@ namespace Decompiler.ControlFlow @@ -49,7 +49,7 @@ namespace Decompiler.ControlFlow
// TODO: Fix
if (graph == null)
continue;
graph.ComputeDominance();
graph.ComputeDominance(context.CancellationToken);
graph.ComputeDominanceFrontier();
block.Body = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint);
}
@ -194,6 +194,9 @@ namespace Decompiler.ControlFlow @@ -194,6 +194,9 @@ namespace Decompiler.ControlFlow
{
List<ILNode> result = new List<ILNode>();
// Do not modify entry data
scope = new HashSet<ControlFlowNode>(scope);
Queue<ControlFlowNode> agenda = new Queue<ControlFlowNode>();
agenda.Enqueue(entryPoint);
while(agenda.Count > 0) {
@ -241,6 +244,7 @@ namespace Decompiler.ControlFlow @@ -241,6 +244,7 @@ namespace Decompiler.ControlFlow
foreach(var node in scope) {
result.Add((ILNode)node.UserData);
}
scope.Clear();
return result;
}
@ -259,6 +263,9 @@ namespace Decompiler.ControlFlow @@ -259,6 +263,9 @@ namespace Decompiler.ControlFlow
{
List<ILNode> result = new List<ILNode>();
// Do not modify entry data
scope = new HashSet<ControlFlowNode>(scope);
HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>();
agenda.Add(entryNode);
while(agenda.Any()) {

3
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -30,7 +30,7 @@ namespace Decompiler @@ -30,7 +30,7 @@ namespace Decompiler
{
StringWriter w = new StringWriter();
WriteTo(new PlainTextOutput(w));
return w.ToString();
return w.ToString().Replace("\r\n", "; ");
}
public abstract void WriteTo(ITextOutput output);
@ -176,6 +176,7 @@ namespace Decompiler @@ -176,6 +176,7 @@ namespace Decompiler
public string Name;
public bool IsGenerated;
public TypeReference Type;
public VariableDefinition OriginalVariable;
public override string ToString()
{

1
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -646,6 +646,7 @@ namespace Decompiler @@ -646,6 +646,7 @@ namespace Decompiler
case MetadataType.IntPtr:
return true;
case MetadataType.Byte:
case MetadataType.Char:
case MetadataType.UInt16:
case MetadataType.UInt32:
case MetadataType.UInt64:

2
ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -59,7 +59,9 @@ @@ -59,7 +59,9 @@
<Compile Include="DelegateConstruction.cs" />
<Compile Include="CustomAttributes.cs" />
<Compile Include="Loops.cs" />
<Compile Include="PropertiesAndEvents.cs" />
<Compile Include="TestRunner.cs" />
<Compile Include="ValueTypes.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Mono.Cecil\Mono.Cecil.csproj">

50
ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Text;
public class PropertiesAndEvents
{
public int Getter(StringBuilder b)
{
return b.Length;
}
public void Setter(StringBuilder b)
{
b.Capacity = 100;
}
public char IndexerGetter(StringBuilder b)
{
return b[50];
}
public void IndexerSetter(StringBuilder b)
{
b[42] = 'b';
}
public int AutomaticProperty { get; set; }
public int CustomProperty {
get {
return this.AutomaticProperty;
}
set {
this.AutomaticProperty = value;
}
}
public event EventHandler AutomaticEvent;
public event EventHandler CustomEvent {
add {
this.AutomaticEvent += value;
}
remove {
this.AutomaticEvent -= value;
}
}
}

74
ICSharpCode.Decompiler/Tests/ValueTypes.cs

@ -0,0 +1,74 @@ @@ -0,0 +1,74 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
public static class ValueTypes
{
public struct S
{
public int Field;
public void SetField()
{
this.Field = 5;
}
public void MethodCalls()
{
this.SetField();
Test(this);
Test(ref this);
}
static void Test(S byVal)
{
}
static void Test(ref S byRef)
{
}
}
public static S InitObj1()
{
S s = default(S);
return s;
}
public static S InitObj2()
{
return default(S);
}
public static void InitObj3(out S p)
{
p = default(S);
}
public static S Copy1(S p)
{
return p;
}
public static S Copy2(ref S p)
{
return p;
}
public static void Copy3(S p, out S o)
{
o = p;
}
public static void Copy4(ref S p, out S o)
{
o = p;
}
public static void Copy4b(ref S p, out S o)
{
// test passing through by-ref arguments
Copy4(ref p, out o);
}
}

1
ILSpy/MainWindow.xaml

@ -10,7 +10,6 @@ @@ -10,7 +10,6 @@
MinHeight="200"
UseLayoutRounding="True"
TextOptions.TextFormattingMode="Display"
Icon="pack://application:,,,/ILSpy;component/images/ILSpy.ico"
FocusManager.FocusedElement="{Binding ElementName=treeView}"
>
<Window.Resources>

7
ILSpy/MainWindow.xaml.cs

@ -26,7 +26,7 @@ using System.Threading.Tasks; @@ -26,7 +26,7 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.TreeView;
using Microsoft.Win32;
@ -51,6 +51,11 @@ namespace ICSharpCode.ILSpy @@ -51,6 +51,11 @@ namespace ICSharpCode.ILSpy
this.sessionSettings = new SessionSettings(spySettings);
this.assemblyListManager = new AssemblyListManager(spySettings);
if (Environment.OSVersion.Version.Major >= 6)
this.Icon = new BitmapImage(new Uri("pack://application:,,,/ILSpy;component/images/ILSpy.ico"));
else
this.Icon = Images.AssemblyLoading;
this.DataContext = sessionSettings;
this.Left = sessionSettings.WindowBounds.Left;
this.Top = sessionSettings.WindowBounds.Top;

Loading…
Cancel
Save