Browse Source

Merge branch 'master' of git://github.com/icsharpcode/ILSpy into Debugger

pull/191/merge
Eusebiu Marcu 15 years ago
parent
commit
efd9d4de78
  1. 66
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 6
      ICSharpCode.Decompiler/Ast/NameVariables.cs
  3. 60
      ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs
  4. 2
      ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
  5. 3
      ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
  6. 10
      ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  7. 117
      ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs
  8. 12
      ICSharpCode.Decompiler/DecompilerSettings.cs
  9. 8
      ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
  10. 66
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  11. 39
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  12. 62
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  13. 10
      ICSharpCode.Decompiler/ILAst/PatternMatching.cs
  14. 359
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  15. 9
      ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs
  16. 84
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  17. 49
      ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs
  18. 17
      ICSharpCode.Decompiler/Tests/DelegateConstruction.cs
  19. 40
      ICSharpCode.Decompiler/Tests/ExceptionHandling.cs
  20. 4
      ICSharpCode.Decompiler/Tests/Generics.cs
  21. 6
      ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs
  22. 6
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  23. 217
      ICSharpCode.Decompiler/Tests/IncrementDecrement.cs
  24. 16
      ICSharpCode.Decompiler/Tests/Loops.cs
  25. 27
      ICSharpCode.Decompiler/Tests/MultidimensionalArray.cs
  26. 63
      ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs
  27. 94
      ICSharpCode.Decompiler/Tests/TestRunner.cs
  28. 69
      ICSharpCode.Decompiler/Tests/UnsafeCode.cs
  29. 57
      ICSharpCode.Decompiler/Tests/ValueTypes.cs

66
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -295,6 +295,17 @@ namespace ICSharpCode.Decompiler.Ast @@ -295,6 +295,17 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Shr_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2);
case ILCode.Neg: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Minus, arg1).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation);
case ILCode.Not: return new Ast.UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1);
case ILCode.PostIncrement:
case ILCode.PostIncrement_Ovf:
case ILCode.PostIncrement_Ovf_Un:
{
if (arg1 is DirectionExpression)
arg1 = ((DirectionExpression)arg1).Expression.Detach();
var uoe = new Ast.UnaryOperatorExpression(
(int)byteCode.Operand > 0 ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement, arg1);
uoe.AddAnnotation((byteCode.Code == ILCode.PostIncrement) ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation);
return uoe;
}
#endregion
#region Arrays
case ILCode.Newarr:
@ -328,7 +339,8 @@ namespace ICSharpCode.Decompiler.Ast @@ -328,7 +339,8 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Ldelem_Ref:
case ILCode.Ldelem_Any:
return arg1.Indexer(arg2);
case ILCode.Ldelema: return MakeRef(arg1.Indexer(arg2));
case ILCode.Ldelema:
return MakeRef(arg1.Indexer(arg2));
case ILCode.Stelem_I:
case ILCode.Stelem_I1:
case ILCode.Stelem_I2:
@ -339,6 +351,18 @@ namespace ICSharpCode.Decompiler.Ast @@ -339,6 +351,18 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Stelem_Ref:
case ILCode.Stelem_Any:
return new Ast.AssignmentExpression(arg1.Indexer(arg2), arg3);
case ILCode.CompoundAssignment:
{
BinaryOperatorExpression boe = (BinaryOperatorExpression)arg1;
return new Ast.AssignmentExpression {
Left = boe.Left.Detach(),
Operator = ReplaceMethodCallsWithOperators.GetAssignmentOperatorForBinaryOperator(boe.Operator),
Right = boe.Right.Detach()
}.CopyAnnotationsFrom(boe);
// We do not mark the resulting assignment as RestoreOriginalAssignOperatorAnnotation, because
// the operator cannot be translated back to the expanded form (as the left-hand expression
// would be evaluated twice, and might have side-effects)
}
#endregion
#region Comparison
case ILCode.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2);
@ -392,9 +416,10 @@ namespace ICSharpCode.Decompiler.Ast @@ -392,9 +416,10 @@ namespace ICSharpCode.Decompiler.Ast
}
return arg1;
}
case ILCode.Conv_R4: return arg1.CastTo(typeof(float));
case ILCode.Conv_R8: return arg1.CastTo(typeof(double));
case ILCode.Conv_R_Un: return arg1.CastTo(typeof(double)); // TODO
case ILCode.Conv_R4:
case ILCode.Conv_R8:
case ILCode.Conv_R_Un: // TODO
return arg1;
case ILCode.Conv_Ovf_I1:
case ILCode.Conv_Ovf_I2:
case ILCode.Conv_Ovf_I4:
@ -445,8 +470,14 @@ namespace ICSharpCode.Decompiler.Ast @@ -445,8 +470,14 @@ namespace ICSharpCode.Decompiler.Ast
#endregion
case ILCode.Arglist: return InlineAssembly(byteCode, args);
case ILCode.Break: return InlineAssembly(byteCode, args);
case ILCode.Call: return TransformCall(false, operand, methodDef, args);
case ILCode.Callvirt: return TransformCall(true, operand, methodDef, args);
case ILCode.Call:
case ILCode.CallGetter:
case ILCode.CallSetter:
return TransformCall(false, operand, args);
case ILCode.Callvirt:
case ILCode.CallvirtGetter:
case ILCode.CallvirtSetter:
return TransformCall(true, operand, args);
case ILCode.Ldftn: {
Cecil.MethodReference cecilMethod = ((MethodReference)operand);
var expr = new Ast.IdentifierExpression(cecilMethod.Name);
@ -502,7 +533,10 @@ namespace ICSharpCode.Decompiler.Ast @@ -502,7 +533,10 @@ namespace ICSharpCode.Decompiler.Ast
AstBuilder.ConvertType(((FieldReference)operand).DeclaringType)
.Member(((FieldReference)operand).Name).WithAnnotation(operand),
arg1);
case ILCode.Ldflda: return MakeRef(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand));
case ILCode.Ldflda:
if (arg1 is DirectionExpression)
arg1 = ((DirectionExpression)arg1).Expression.Detach();
return MakeRef(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand));
case ILCode.Ldsflda:
return MakeRef(
AstBuilder.ConvertType(((FieldReference)operand).DeclaringType)
@ -679,7 +713,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -679,7 +713,7 @@ namespace ICSharpCode.Decompiler.Ast
return new DefaultValueExpression { Type = AstBuilder.ConvertType(type) };
}
static AstNode TransformCall(bool isVirtual, object operand, MethodDefinition methodDef, List<Ast.Expression> args)
AstNode TransformCall(bool isVirtual, object operand, List<Ast.Expression> args)
{
Cecil.MethodReference cecilMethod = ((MethodReference)operand);
Ast.Expression target;
@ -699,7 +733,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -699,7 +733,7 @@ namespace ICSharpCode.Decompiler.Ast
}
if (target is ThisReferenceExpression && !isVirtual) {
// a non-virtual call on "this" might be a "base"-call.
if ((cecilMethod.DeclaringType.IsGenericInstance ? cecilMethod.DeclaringType.GetElementType() : cecilMethod.DeclaringType) != methodDef.DeclaringType) {
if (cecilMethod.DeclaringType.GetElementType() != methodDef.DeclaringType) {
// If we're not calling a method in the current class; we must be calling one in the base class.
target = new BaseReferenceExpression();
}
@ -717,6 +751,8 @@ namespace ICSharpCode.Decompiler.Ast @@ -717,6 +751,8 @@ namespace ICSharpCode.Decompiler.Ast
if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) {
return target.Indexer(methodArgs);
} else if (cecilMethod.Name == "Address" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) {
return MakeRef(target.Indexer(methodArgs));
} else if (cecilMethod.Name == "Set" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 2) {
return new AssignmentExpression(target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)), methodArgs.Last());
}
@ -880,7 +916,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -880,7 +916,7 @@ namespace ICSharpCode.Decompiler.Ast
Ast.Expression Convert(Ast.Expression expr, Cecil.TypeReference actualType, Cecil.TypeReference reqType)
{
if (reqType == null || actualType == reqType) {
if (actualType == null || reqType == null || TypeAnalysis.IsSameType(actualType, reqType)) {
return expr;
} else if (actualType is ByReferenceType && reqType is PointerType && expr is DirectionExpression) {
return Convert(
@ -923,10 +959,12 @@ namespace ICSharpCode.Decompiler.Ast @@ -923,10 +959,12 @@ namespace ICSharpCode.Decompiler.Ast
{
return expr.CastTo(AstBuilder.ConvertType(actualType));
}
if (actualIsIntegerOrEnum && requiredIsIntegerOrEnum) {
if (actualType.FullName == reqType.FullName)
return expr;
bool actualIsPrimitiveType = actualIsIntegerOrEnum
|| actualType.MetadataType == MetadataType.Single || actualType.MetadataType == MetadataType.Double;
bool requiredIsPrimitiveType = requiredIsIntegerOrEnum
|| reqType.MetadataType == MetadataType.Single || reqType.MetadataType == MetadataType.Double;
if (actualIsPrimitiveType && requiredIsPrimitiveType) {
return expr.CastTo(AstBuilder.ConvertType(reqType));
}
return expr;

6
ICSharpCode.Decompiler/Ast/NameVariables.cs

@ -159,6 +159,8 @@ namespace ICSharpCode.Decompiler.Ast @@ -159,6 +159,8 @@ namespace ICSharpCode.Decompiler.Ast
return CleanUpVariableName(((FieldReference)expr.Operand).Name);
case ILCode.Call:
case ILCode.Callvirt:
case ILCode.CallGetter:
case ILCode.CallvirtGetter:
MethodReference mr = (MethodReference)expr.Operand;
if (mr.Name.StartsWith("get_", StringComparison.OrdinalIgnoreCase) && mr.Parameters.Count == 0) {
// use name from properties, but not from indexers
@ -184,6 +186,10 @@ namespace ICSharpCode.Decompiler.Ast @@ -184,6 +186,10 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Call:
case ILCode.Callvirt:
case ILCode.Newobj:
case ILCode.CallGetter:
case ILCode.CallvirtGetter:
case ILCode.CallSetter:
case ILCode.CallvirtSetter:
MethodReference methodRef = (MethodReference)parent.Operand;
if (methodRef.Parameters.Count == 1 && i == parent.Arguments.Count - 1) {
// argument might be value of a setter

60
ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs

@ -140,6 +140,28 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -140,6 +140,28 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
}
class ConvertCompoundAssignment : InsertedNode
{
readonly Expression expression;
readonly bool isChecked;
public ConvertCompoundAssignment(Expression expression, bool isChecked)
{
this.expression = expression;
this.isChecked = isChecked;
}
public override void Insert()
{
AssignmentExpression assign = expression.Annotation<ReplaceMethodCallsWithOperators.RestoreOriginalAssignOperatorAnnotation>().Restore(expression);
expression.ReplaceWith(assign);
if (isChecked)
assign.Right = new CheckedExpression { Expression = assign.Right.Detach() };
else
assign.Right = new UncheckedExpression { Expression = assign.Right.Detach() };
}
}
class InsertedBlock : InsertedNode
{
readonly Statement firstStatement; // inclusive
@ -279,20 +301,38 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -279,20 +301,38 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
if (expr != null) {
CheckedUncheckedAnnotation annotation = expr.Annotation<CheckedUncheckedAnnotation>();
if (annotation != null) {
// If the annotation requires this node to be in a specific context, set the cost of the other context to infinite.
// If the annotation requires this node to be in a specific context, add a huge cost to the other context
// That huge cost gives us the option to ignore a required checked/unchecked expression when there wouldn't be any
// solution otherwise. (e.g. "for (checked(M().x += 1); true; unchecked(M().x += 2)) {}")
if (annotation.IsChecked)
result.CostInUncheckedContext = Cost.Infinite;
result.CostInUncheckedContext += new Cost(10000, 0);
else
result.CostInCheckedContext = Cost.Infinite;
result.CostInCheckedContext += new Cost(10000, 0);
}
// Embed this node in an checked/unchecked expression:
// We use '<' so that expressions are introduced on the deepest level possible (goal 3)
if (result.CostInCheckedContext + new Cost(0, 1) < result.CostInUncheckedContext) {
result.CostInUncheckedContext = result.CostInCheckedContext + new Cost(0, 1);
result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new InsertedExpression(expr, true);
} else if (result.CostInUncheckedContext + new Cost(0, 1) < result.CostInCheckedContext) {
result.CostInCheckedContext = result.CostInUncheckedContext + new Cost(0, 1);
result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new InsertedExpression(expr, false);
if (expr.Parent is ExpressionStatement) {
// We cannot use checked/unchecked for top-level-expressions.
// However, we could try converting a compound assignment (checked(a+=b);) or unary operator (checked(a++);)
// back to its old form.
if (expr.Annotation<ReplaceMethodCallsWithOperators.RestoreOriginalAssignOperatorAnnotation>() != null) {
// We use '<' so that expressions are introduced on the deepest level possible (goal 3)
if (result.CostInCheckedContext + new Cost(1, 1) < result.CostInUncheckedContext) {
result.CostInUncheckedContext = result.CostInCheckedContext + new Cost(1, 1);
result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new ConvertCompoundAssignment(expr, true);
} else if (result.CostInUncheckedContext + new Cost(1, 1) < result.CostInCheckedContext) {
result.CostInCheckedContext = result.CostInUncheckedContext + new Cost(1, 1);
result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new ConvertCompoundAssignment(expr, false);
}
}
} else {
// We use '<' so that expressions are introduced on the deepest level possible (goal 3)
if (result.CostInCheckedContext + new Cost(0, 1) < result.CostInUncheckedContext) {
result.CostInUncheckedContext = result.CostInCheckedContext + new Cost(0, 1);
result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new InsertedExpression(expr, true);
} else if (result.CostInUncheckedContext + new Cost(0, 1) < result.CostInCheckedContext) {
result.CostInCheckedContext = result.CostInUncheckedContext + new Cost(0, 1);
result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new InsertedExpression(expr, false);
}
}
}
return result;

2
ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs

@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
methodIdent.Remove();
if (!annotation.IsVirtual && obj is ThisReferenceExpression) {
// maybe it's getting the pointer of a base method?
if (method.DeclaringType != context.CurrentType) {
if (method.DeclaringType.GetElementType() != context.CurrentType) {
obj = new BaseReferenceExpression();
}
}

3
ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs

@ -24,6 +24,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -24,6 +24,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public void Run(AstNode compilationUnit)
{
if (!context.Settings.UsingDeclarations)
return;
// First determine all the namespaces that need to be imported:
compilationUnit.AcceptVisitor(this, null);

10
ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs

@ -584,7 +584,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -584,7 +584,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
Modifiers = Modifiers.Any,
Body = new BlockStatement {
new ReturnStatement {
Expression = new NamedNode("fieldReference", new MemberReferenceExpression { Target = new ThisReferenceExpression() })
Expression = new AnyNode("fieldReference")
}
}
},
@ -608,7 +608,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -608,7 +608,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
Match m = automaticPropertyPattern.Match(property);
if (m != null) {
FieldDefinition field = m.Get("fieldReference").Single().Annotation<FieldReference>().ResolveWithinSameModule();
if (field.IsCompilerGenerated()) {
if (field.IsCompilerGenerated() && field.DeclaringType == cecilProperty.DeclaringType) {
RemoveCompilerGeneratedAttribute(property.Getter.Attributes);
RemoveCompilerGeneratedAttribute(property.Setter.Attributes);
property.Getter.Body = null;
@ -643,7 +643,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -643,7 +643,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
new AssignmentExpression {
Left = new NamedNode("var1", new IdentifierExpression()),
Operator = AssignmentOperatorType.Assign,
Right = new NamedNode("field", new MemberReferenceExpression { Target = new ThisReferenceExpression() })
Right = new NamedNode(
"field",
new MemberReferenceExpression {
Target = new Choice { new ThisReferenceExpression(), new TypeReferenceExpression { Type = new AnyNode() } }
})
},
new DoWhileStatement {
EmbeddedStatement = new BlockStatement {

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

@ -140,49 +140,49 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -140,49 +140,49 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
}
/// <summary>
/// This annotation is used to convert a compound assignment "a += 2;" or increment operator "a++;"
/// back to the original "a = a + 2;". This is sometimes necessary when the checked/unchecked semantics
/// cannot be guaranteed otherwise (see CheckedUnchecked.ForWithCheckedInitializerAndUncheckedIterator test)
/// </summary>
public class RestoreOriginalAssignOperatorAnnotation
{
readonly BinaryOperatorExpression binaryOperatorExpression;
public RestoreOriginalAssignOperatorAnnotation(BinaryOperatorExpression binaryOperatorExpression)
{
this.binaryOperatorExpression = binaryOperatorExpression;
}
public AssignmentExpression Restore(Expression expression)
{
expression.RemoveAnnotations<RestoreOriginalAssignOperatorAnnotation>();
AssignmentExpression assign = expression as AssignmentExpression;
if (assign == null) {
UnaryOperatorExpression uoe = (UnaryOperatorExpression)expression;
assign = new AssignmentExpression(uoe.Expression.Detach(), new PrimitiveExpression(1));
} else {
assign.Operator = AssignmentOperatorType.Assign;
}
binaryOperatorExpression.Right = assign.Right.Detach();
assign.Right = binaryOperatorExpression;
return assign;
}
}
public override object VisitAssignmentExpression(AssignmentExpression assignment, object data)
{
base.VisitAssignmentExpression(assignment, data);
// 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) {
switch (binary.Operator) {
case BinaryOperatorType.Add:
assignment.Operator = AssignmentOperatorType.Add;
break;
case BinaryOperatorType.Subtract:
assignment.Operator = AssignmentOperatorType.Subtract;
break;
case BinaryOperatorType.Multiply:
assignment.Operator = AssignmentOperatorType.Multiply;
break;
case BinaryOperatorType.Divide:
assignment.Operator = AssignmentOperatorType.Divide;
break;
case BinaryOperatorType.Modulus:
assignment.Operator = AssignmentOperatorType.Modulus;
break;
case BinaryOperatorType.ShiftLeft:
assignment.Operator = AssignmentOperatorType.ShiftLeft;
break;
case BinaryOperatorType.ShiftRight:
assignment.Operator = AssignmentOperatorType.ShiftRight;
break;
case BinaryOperatorType.BitwiseAnd:
assignment.Operator = AssignmentOperatorType.BitwiseAnd;
break;
case BinaryOperatorType.BitwiseOr:
assignment.Operator = AssignmentOperatorType.BitwiseOr;
break;
case BinaryOperatorType.ExclusiveOr:
assignment.Operator = AssignmentOperatorType.ExclusiveOr;
break;
}
if (CanConvertToCompoundAssignment(assignment.Left) && assignment.Left.Match(binary.Left) != null) {
assignment.Operator = GetAssignmentOperatorForBinaryOperator(binary.Operator);
if (assignment.Operator != AssignmentOperatorType.Assign) {
// If we found a shorter operator, get rid of the BinaryOperatorExpression:
assignment.CopyAnnotationsFrom(binary);
assignment.Right = binary.Right;
assignment.AddAnnotation(new RestoreOriginalAssignOperatorAnnotation(binary));
}
}
}
@ -196,7 +196,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -196,7 +196,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
// 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
else
type = (assignment.Operator == AssignmentOperatorType.Add) ? UnaryOperatorType.Increment : UnaryOperatorType.Decrement;
assignment.ReplaceWith(new UnaryOperatorExpression(type, assignment.Left.Detach()).CopyAnnotationsFrom(assignment));
}
@ -205,16 +205,51 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -205,16 +205,51 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return null;
}
static bool IsWithoutSideEffects(Expression left)
public static AssignmentOperatorType GetAssignmentOperatorForBinaryOperator(BinaryOperatorType bop)
{
switch (bop) {
case BinaryOperatorType.Add:
return AssignmentOperatorType.Add;
case BinaryOperatorType.Subtract:
return AssignmentOperatorType.Subtract;
case BinaryOperatorType.Multiply:
return AssignmentOperatorType.Multiply;
case BinaryOperatorType.Divide:
return AssignmentOperatorType.Divide;
case BinaryOperatorType.Modulus:
return AssignmentOperatorType.Modulus;
case BinaryOperatorType.ShiftLeft:
return AssignmentOperatorType.ShiftLeft;
case BinaryOperatorType.ShiftRight:
return AssignmentOperatorType.ShiftRight;
case BinaryOperatorType.BitwiseAnd:
return AssignmentOperatorType.BitwiseAnd;
case BinaryOperatorType.BitwiseOr:
return AssignmentOperatorType.BitwiseOr;
case BinaryOperatorType.ExclusiveOr:
return AssignmentOperatorType.ExclusiveOr;
default:
return AssignmentOperatorType.Assign;
}
}
static bool CanConvertToCompoundAssignment(Expression left)
{
if (left is ThisReferenceExpression)
return true;
if (left is IdentifierExpression)
return true;
MemberReferenceExpression mre = left as MemberReferenceExpression;
if (mre != null)
return mre.Annotation<FieldReference>() != null && IsWithoutSideEffects(mre.Target);
return false;
return IsWithoutSideEffects(mre.Target);
IndexerExpression ie = left as IndexerExpression;
if (ie != null)
return IsWithoutSideEffects(ie.Target) && ie.Arguments.All(IsWithoutSideEffects);
UnaryOperatorExpression uoe = left as UnaryOperatorExpression;
if (uoe != null && uoe.Operator == UnaryOperatorType.Dereference)
return IsWithoutSideEffects(uoe.Expression);
return IsWithoutSideEffects(left);
}
static bool IsWithoutSideEffects(Expression left)
{
return left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression;
}
void IAstTransform.Run(AstNode node)

12
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -128,6 +128,18 @@ namespace ICSharpCode.Decompiler @@ -128,6 +128,18 @@ namespace ICSharpCode.Decompiler
}
}
bool usingDeclarations = true;
public bool UsingDeclarations {
get { return usingDeclarations; }
set {
if (usingDeclarations != value) {
usingDeclarations = value;
OnPropertyChanged("UsingDeclarations");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)

8
ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs

@ -701,7 +701,13 @@ namespace ICSharpCode.Decompiler.ILAst @@ -701,7 +701,13 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression expr = new ILExpression(byteCode.Code, byteCode.Operand);
expr.ILRanges.Add(ilRange);
expr.Prefixes = byteCode.Prefixes;
if (byteCode.Prefixes != null && byteCode.Prefixes.Length > 0) {
ILExpressionPrefix[] prefixes = new ILExpressionPrefix[byteCode.Prefixes.Length];
for (int i = 0; i < prefixes.Length; i++) {
prefixes[i] = new ILExpressionPrefix((ILCode)byteCode.Prefixes[i].OpCode.Code, byteCode.Prefixes[i].Operand);
}
expr.Prefixes = prefixes;
}
// Label for this instruction
if (byteCode.Label != null) {

66
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -16,6 +16,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -16,6 +16,7 @@ namespace ICSharpCode.Decompiler.ILAst
InlineVariables,
CopyPropagation,
YieldReturn,
PropertyAccessInstructions,
SplitToMovableBlocks,
TypeInference,
SimplifyShortCircuit,
@ -27,6 +28,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -27,6 +28,7 @@ namespace ICSharpCode.Decompiler.ILAst
TransformArrayInitializers,
TransformCollectionInitializers,
MakeAssignmentExpression,
IntroducePostIncrement,
InlineVariables2,
FindLoops,
FindConditions,
@ -79,6 +81,9 @@ namespace ICSharpCode.Decompiler.ILAst @@ -79,6 +81,9 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.YieldReturn) return;
YieldReturnDecompiler.Run(context, method);
if (abortBeforeStep == ILAstOptimizationStep.PropertyAccessInstructions) return;
IntroducePropertyAccessInstructions(method);
if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
SplitToBasicBlocks(block);
@ -120,6 +125,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -120,6 +125,10 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) return;
modified |= block.RunOptimization(MakeAssignmentExpression);
modified |= block.RunOptimization(MakeCompoundAssignments);
if (abortBeforeStep == ILAstOptimizationStep.IntroducePostIncrement) return;
modified |= block.RunOptimization(IntroducePostIncrement);
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return;
modified |= new ILInlining(method).InlineAllInBlock(block);
@ -281,6 +290,42 @@ namespace ICSharpCode.Decompiler.ILAst @@ -281,6 +290,42 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
/// <summary>
/// Converts call and callvirt instructions that read/write properties into CallGetter/CallSetter instructions.
///
/// CallGetter/CallSetter is used to allow the ILAst to represent "while ((SomeProperty = value) != null)".
/// </summary>
void IntroducePropertyAccessInstructions(ILBlock method)
{
foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) {
MethodReference cecilMethod = (MethodReference)expr.Operand;
if (cecilMethod.DeclaringType is ArrayType) {
switch (cecilMethod.Name) {
case "Get":
expr.Code = ILCode.CallGetter;
break;
case "Set":
expr.Code = ILCode.CallSetter;
break;
case "Address":
expr.Code = ILCode.CallGetter;
expr.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress));
break;
}
} else {
MethodDefinition cecilMethodDef = cecilMethod.Resolve();
if (cecilMethodDef != null) {
if (cecilMethodDef.IsGetter)
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallGetter : ILCode.CallvirtGetter;
else if (cecilMethodDef.IsSetter)
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter;
}
}
}
}
}
/// <summary>
/// Group input into a set of blocks that can be later arbitraliby schufled.
/// The method adds necessary branches to make control flow between blocks
@ -575,6 +620,27 @@ namespace ICSharpCode.Decompiler.ILAst @@ -575,6 +620,27 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
public static bool IsLoadFromArray(this ILCode code)
{
switch (code) {
case ILCode.Ldelem_Any:
case ILCode.Ldelem_I:
case ILCode.Ldelem_I1:
case ILCode.Ldelem_I2:
case ILCode.Ldelem_I4:
case ILCode.Ldelem_I8:
case ILCode.Ldelem_U1:
case ILCode.Ldelem_U2:
case ILCode.Ldelem_U4:
case ILCode.Ldelem_R4:
case ILCode.Ldelem_R8:
case ILCode.Ldelem_Ref:
return true;
default:
return false;
}
}
/// <summary>
/// Can the expression be used as a statement in C#?
/// </summary>

39
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -256,12 +256,24 @@ namespace ICSharpCode.Decompiler.ILAst @@ -256,12 +256,24 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
public class ILExpressionPrefix
{
public readonly ILCode Code;
public readonly object Operand;
public ILExpressionPrefix(ILCode code, object operand = null)
{
this.Code = code;
this.Operand = operand;
}
}
public class ILExpression : ILNode
{
public ILCode Code { get; set; }
public object Operand { get; set; }
public List<ILExpression> Arguments { get; set; }
public Instruction[] Prefixes { get; set; }
public ILExpressionPrefix[] Prefixes { get; set; }
// Mapping to the original instructions (useful for debugging)
public List<ILRange> ILRanges { get; set; }
@ -292,13 +304,24 @@ namespace ICSharpCode.Decompiler.ILAst @@ -292,13 +304,24 @@ namespace ICSharpCode.Decompiler.ILAst
this.ILRanges = new List<ILRange>(1);
}
public Instruction GetPrefix(Code code)
public void AddPrefix(ILExpressionPrefix prefix)
{
ILExpressionPrefix[] arr = this.Prefixes;
if (arr == null)
arr = new ILExpressionPrefix[1];
else
Array.Resize(ref arr, arr.Length + 1);
arr[arr.Length - 1] = prefix;
this.Prefixes = arr;
}
public ILExpressionPrefix GetPrefix(ILCode code)
{
var prefixes = this.Prefixes;
if (prefixes != null) {
foreach (Instruction i in prefixes) {
if (i.OpCode.Code == code)
return i;
foreach (ILExpressionPrefix p in prefixes) {
if (p.Code == code)
return p;
}
}
return null;
@ -349,9 +372,9 @@ namespace ICSharpCode.Decompiler.ILAst @@ -349,9 +372,9 @@ namespace ICSharpCode.Decompiler.ILAst
}
if (this.Prefixes != null) {
foreach (Instruction prefix in this.Prefixes) {
output.Write(prefix.OpCode.Name);
output.Write(' ');
foreach (var prefix in this.Prefixes) {
output.Write(prefix.Code.GetName());
output.Write(". ");
}
}

62
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -1,27 +1,5 @@ @@ -1,27 +1,5 @@
// Author:
// Jb Evain (jbevain@gmail.com)
//
// Copyright (c) 2008 - 2010 Jb Evain
//
// 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.
//
// 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 Mono.Cecil;
@ -268,9 +246,39 @@ namespace ICSharpCode.Decompiler.ILAst @@ -268,9 +246,39 @@ namespace ICSharpCode.Decompiler.ILAst
Ldc_Decimal,
YieldBreak,
YieldReturn,
DefaultValue, // default(T)
Pattern // used for ILAst pattern nodes
/// <summary>
/// Represents the 'default(T)' instruction.
/// </summary>
/// <remarks>Introduced by SimplifyLdObjAndStObj step</remarks>
DefaultValue,
/// <summary>
/// ILExpression with a single child: binary operator.
/// This expression means that the binary operator will also assign the new value to its left-hand side.
/// 'CompoundAssignment' must not be used for local variables, as inlining (and other) optimizations don't know that it modifies the variable.
/// </summary>
/// <remarks>Introduced by MakeCompoundAssignments step</remarks>
CompoundAssignment,
/// <summary>
/// Represents the post-increment operator.
/// The first argument is the address of the variable to increment (ldloca instruction).
/// The second arugment is the amount the variable is incremented by (ldc.i4 instruction)
/// </summary>
/// <remarks>Introduced by IntroducePostIncrement step</remarks>
PostIncrement,
PostIncrement_Ovf, // checked variant of PostIncrement
PostIncrement_Ovf_Un, // checked variant of PostIncrement, for unsigned integers
/// <summary>Calls the getter of a static property (or indexer), or of an instance property on 'base'</summary>
CallGetter,
/// <summary>Calls the getter of an instance property (or indexer)</summary>
CallvirtGetter,
/// <summary>Calls the setter of a static property (or indexer), or of an instance property on 'base'</summary>
/// <remarks>This allows us to represent "while ((SomeProperty = val) != null) {}"</remarks>
CallSetter,
/// <summary>Calls the setter of a instance property (or indexer)</summary>
CallvirtSetter,
/// <summary>Simulates getting the address of a property. Used as prefix on CallGetter or CallvirtGetter.</summary>
/// <remarks>Used for postincrement for properties, and to represent the Address() method on multi-dimensional arrays</remarks>
PropertyAddress
}
public static class ILCodeUtil

10
ICSharpCode.Decompiler/ILAst/PatternMatching.cs

@ -28,16 +28,6 @@ namespace ICSharpCode.Decompiler.ILAst @@ -28,16 +28,6 @@ namespace ICSharpCode.Decompiler.ILAst
return false;
}
public static bool Match<T>(this ILNode node, ILCode code, T operand)
{
ILExpression expr = node as ILExpression;
if (expr != null && expr.Prefixes == null && expr.Code == code) {
Debug.Assert(expr.Arguments.Count == 0);
return operand.Equals(expr.Operand);
}
return false;
}
public static bool Match(this ILNode node, ILCode code, out List<ILExpression> args)
{
ILExpression expr = node as ILExpression;

359
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -59,6 +59,18 @@ namespace ICSharpCode.Decompiler.ILAst @@ -59,6 +59,18 @@ namespace ICSharpCode.Decompiler.ILAst
static bool SimplifyLdObjAndStObj(List<ILNode> body, ILExpression expr, int pos)
{
bool modified = false;
expr = SimplifyLdObjAndStObj(expr, ref modified);
if (modified && body != null)
body[pos] = expr;
for (int i = 0; i < expr.Arguments.Count; i++) {
expr.Arguments[i] = SimplifyLdObjAndStObj(expr.Arguments[i], ref modified);
modified |= SimplifyLdObjAndStObj(null, expr.Arguments[i], -1);
}
return modified;
}
static ILExpression SimplifyLdObjAndStObj(ILExpression expr, ref bool modified)
{
if (expr.Code == ILCode.Initobj) {
expr.Code = ILCode.Stobj;
expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand));
@ -84,13 +96,17 @@ namespace ICSharpCode.Decompiler.ILAst @@ -84,13 +96,17 @@ namespace ICSharpCode.Decompiler.ILAst
}
if (newCode != null) {
arg.Code = newCode.Value;
if (expr.Code == ILCode.Stobj)
if (expr.Code == ILCode.Stobj) {
arg.InferredType = expr.InferredType;
arg.ExpectedType = expr.ExpectedType;
arg.Arguments.Add(arg2);
}
arg.ILRanges.AddRange(expr.ILRanges);
body[pos] = arg;
modified = true;
return arg;
} else {
return expr;
}
return modified;
}
#endregion
@ -174,16 +190,14 @@ namespace ICSharpCode.Decompiler.ILAst @@ -174,16 +190,14 @@ namespace ICSharpCode.Decompiler.ILAst
// stloc(v, exprVar)
// ->
// exprVar = stloc(v, ...))
ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression;
ILVariable exprVar;
ILExpression initializer;
if (!(expr.Match(ILCode.Stloc, out exprVar, out initializer) && exprVar.IsGenerated))
return false;
ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression;
ILVariable v;
ILExpression stLocArg;
if (expr.Match(ILCode.Stloc, out exprVar, out initializer) &&
exprVar.IsGenerated &&
nextExpr.Match(ILCode.Stloc, out v, out stLocArg) &&
stLocArg.Match(ILCode.Ldloc, exprVar))
{
if (nextExpr.Match(ILCode.Stloc, out v, out stLocArg) && stLocArg.MatchLdloc(exprVar)) {
ILExpression store2 = body.ElementAtOrDefault(pos + 2) as ILExpression;
if (StoreCanBeConvertedToAssignment(store2, exprVar)) {
// expr_44 = ...
@ -208,17 +222,340 @@ namespace ICSharpCode.Decompiler.ILAst @@ -208,17 +222,340 @@ namespace ICSharpCode.Decompiler.ILAst
nextExpr.Arguments[0] = initializer;
((ILExpression)body[pos]).Arguments[0] = nextExpr;
return true;
} else if ((nextExpr.Code == ILCode.Stsfld || nextExpr.Code == ILCode.CallSetter || nextExpr.Code == ILCode.CallvirtSetter) && nextExpr.Arguments.Count == 1) {
// exprVar = ...
// stsfld(fld, exprVar)
// ->
// exprVar = stsfld(fld, ...))
if (nextExpr.Arguments[0].MatchLdloc(exprVar)) {
body.RemoveAt(pos + 1); // remove stsfld
nextExpr.Arguments[0] = initializer;
((ILExpression)body[pos]).Arguments[0] = nextExpr;
return true;
}
}
return false;
}
bool StoreCanBeConvertedToAssignment(ILExpression store, ILVariable exprVar)
{
if (store != null && (store.Code == ILCode.Stloc || store.Code == ILCode.Stfld || store.Code == ILCode.Stsfld)) {
return store.Arguments.Last().Code == ILCode.Ldloc && store.Arguments.Last().Operand == exprVar;
if (store == null)
return false;
switch (store.Code) {
case ILCode.Stloc:
case ILCode.Stfld:
case ILCode.Stsfld:
case ILCode.Stobj:
case ILCode.CallSetter:
case ILCode.CallvirtSetter:
break;
default:
if (!store.Code.IsStoreToArray())
return false;
break;
}
return store.Arguments.Last().Code == ILCode.Ldloc && store.Arguments.Last().Operand == exprVar;
}
#endregion
#region MakeCompoundAssignments
bool MakeCompoundAssignments(List<ILNode> body, ILExpression expr, int pos)
{
bool modified = false;
modified |= MakeCompoundAssignment(expr);
// Static fields and local variables are not handled here - those are expressions without side effects
// and get handled by ReplaceMethodCallsWithOperators
// (which does a reversible transform to the short operator form, as the introduction of checked/unchecked might have to revert to the long form).
foreach (ILExpression arg in expr.Arguments) {
modified |= MakeCompoundAssignments(null, arg, -1);
}
if (modified && body != null)
new ILInlining(method).InlineInto(body, pos, aggressive: false);
return modified;
}
bool MakeCompoundAssignment(ILExpression expr)
{
// stelem.any(T, ldloc(array), ldloc(pos), <OP>(ldelem.any(T, ldloc(array), ldloc(pos)), <RIGHT>))
// or
// stobj(T, ldloc(ptr), <OP>(ldobj(T, ldloc(ptr)), <RIGHT>))
ILCode expectedLdelemCode;
switch (expr.Code) {
case ILCode.Stelem_Any:
expectedLdelemCode = ILCode.Ldelem_Any;
break;
case ILCode.Stfld:
expectedLdelemCode = ILCode.Ldfld;
break;
case ILCode.Stobj:
expectedLdelemCode = ILCode.Ldobj;
break;
case ILCode.CallSetter:
expectedLdelemCode = ILCode.CallGetter;
break;
case ILCode.CallvirtSetter:
expectedLdelemCode = ILCode.CallvirtGetter;
break;
default:
return false;
}
// all arguments except the last (so either array+pos, or ptr):
bool hasGeneratedVar = false;
for (int i = 0; i < expr.Arguments.Count - 1; i++) {
ILVariable inputVar;
if (!expr.Arguments[i].Match(ILCode.Ldloc, out inputVar))
return false;
hasGeneratedVar |= inputVar.IsGenerated;
}
// At least one of the variables must be generated; otherwise we just keep the expanded form.
// We do this because we want compound assignments to be represented in ILAst only when strictly necessary;
// other compound assignments will be introduced by ReplaceMethodCallsWithOperator
// (which uses a reversible transformation, see ReplaceMethodCallsWithOperator.RestoreOriginalAssignOperatorAnnotation)
if (!hasGeneratedVar)
return false;
ILExpression op = expr.Arguments.Last();
if (!CanBeRepresentedAsCompoundAssignment(op.Code))
return false;
ILExpression ldelem = op.Arguments[0];
if (ldelem.Code != expectedLdelemCode)
return false;
Debug.Assert(ldelem.Arguments.Count == expr.Arguments.Count - 1);
for (int i = 0; i < ldelem.Arguments.Count; i++) {
if (!ldelem.Arguments[i].MatchLdloc((ILVariable)expr.Arguments[i].Operand))
return false;
}
expr.Code = ILCode.CompoundAssignment;
expr.Operand = null;
expr.Arguments.RemoveRange(0, ldelem.Arguments.Count);
// result is "CompoundAssignment(<OP>(ldelem.any(...), <RIGHT>))"
return true;
}
static bool CanBeRepresentedAsCompoundAssignment(ILCode code)
{
switch (code) {
case ILCode.Add:
case ILCode.Add_Ovf:
case ILCode.Add_Ovf_Un:
case ILCode.Sub:
case ILCode.Sub_Ovf:
case ILCode.Sub_Ovf_Un:
case ILCode.Mul:
case ILCode.Mul_Ovf:
case ILCode.Mul_Ovf_Un:
case ILCode.Div:
case ILCode.Div_Un:
case ILCode.Rem:
case ILCode.Rem_Un:
case ILCode.And:
case ILCode.Or:
case ILCode.Xor:
case ILCode.Shl:
case ILCode.Shr:
case ILCode.Shr_Un:
return true;
default:
return false;
}
}
#endregion
#region IntroducePostIncrement
bool IntroducePostIncrement(List<ILNode> body, ILExpression expr, int pos)
{
bool modified = IntroducePostIncrementForVariables(body, expr, pos);
Debug.Assert(body[pos] == expr); // IntroducePostIncrementForVariables shouldn't change the expression reference
ILExpression newExpr = IntroducePostIncrementForInstanceFields(expr);
if (newExpr != null) {
modified = true;
body[pos] = newExpr;
new ILInlining(method).InlineIfPossible(body, ref pos);
}
return modified;
}
bool IntroducePostIncrementForVariables(List<ILNode> body, ILExpression expr, int pos)
{
// Works for variables and static fields/properties
// expr = ldloc(i)
// stloc(i, add(expr, ldc.i4(1)))
// ->
// expr = postincrement(1, ldloca(i))
ILVariable exprVar;
ILExpression exprInit;
if (!(expr.Match(ILCode.Stloc, out exprVar, out exprInit) && exprVar.IsGenerated))
return false;
if (!(exprInit.Code == ILCode.Ldloc || exprInit.Code == ILCode.Ldsfld || (exprInit.Code == ILCode.CallGetter && exprInit.Arguments.Count == 0)))
return false;
ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression;
if (nextExpr == null)
return false;
if (exprInit.Code == ILCode.CallGetter) {
if (!(nextExpr.Code == ILCode.CallSetter && IsGetterSetterPair(exprInit.Operand, nextExpr.Operand)))
return false;
} else {
if (!(nextExpr.Code == (exprInit.Code == ILCode.Ldloc ? ILCode.Stloc : ILCode.Stsfld) && nextExpr.Operand == exprInit.Operand))
return false;
}
ILExpression addExpr = nextExpr.Arguments[0];
int incrementAmount;
ILCode incrementCode = GetIncrementCode(addExpr, out incrementAmount);
if (!(incrementAmount != 0 && addExpr.Arguments[0].MatchLdloc(exprVar)))
return false;
if (exprInit.Code == ILCode.Ldloc)
exprInit.Code = ILCode.Ldloca;
else if (exprInit.Code == ILCode.CallGetter)
exprInit.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress));
else
exprInit.Code = ILCode.Ldsflda;
expr.Arguments[0] = new ILExpression(incrementCode, incrementAmount, exprInit);
body.RemoveAt(pos + 1); // TODO ILRanges
return true;
}
static bool IsGetterSetterPair(object getterOperand, object setterOperand)
{
MethodReference getter = getterOperand as MethodReference;
MethodReference setter = setterOperand as MethodReference;
if (getter == null || setter == null)
return false;
if (!TypeAnalysis.IsSameType(getter.DeclaringType, setter.DeclaringType))
return false;
MethodDefinition getterDef = getter.Resolve();
MethodDefinition setterDef = setter.Resolve();
if (getterDef == null || setterDef == null)
return false;
foreach (PropertyDefinition prop in getterDef.DeclaringType.Properties) {
if (prop.GetMethod == getterDef)
return prop.SetMethod == setterDef;
}
return false;
}
ILExpression IntroducePostIncrementForInstanceFields(ILExpression expr)
{
// stfld(field, ldloc(instance), add(stloc(helperVar, ldfld(field, ldloc(instance))), ldc.i4(1)))
// -> stloc(helperVar, postincrement(1, ldflda(field, ldloc(instance))))
// Also works for array elements and pointers:
// stelem.any(T, ldloc(instance), ldloc(pos), add(stloc(helperVar, ldelem.any(T, ldloc(instance), ldloc(pos))), ldc.i4(1)))
// -> stloc(helperVar, postincrement(1, ldelema(ldloc(instance), ldloc(pos))))
// stobj(T, ldloc(ptr), add(stloc(helperVar, ldobj(T, ldloc(ptr)), ldc.i4(1))))
// -> stloc(helperVar, postIncrement(1, ldloc(ptr)))
// callsetter(set_P, ldloc(instance), add(stloc(helperVar, callgetter(get_P, ldloc(instance))), ldc.i4(1)))
// -> stloc(helperVar, postIncrement(1, propertyaddress. callgetter(get_P, ldloc(instance))))
if (!(expr.Code == ILCode.Stfld || expr.Code.IsStoreToArray() || expr.Code == ILCode.Stobj || expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter))
return null;
// Test that all arguments except the last are ldloc (1 arg for fields and pointers, 2 args for arrays)
for (int i = 0; i < expr.Arguments.Count - 1; i++) {
if (expr.Arguments[i].Code != ILCode.Ldloc)
return null;
}
ILExpression addExpr = expr.Arguments[expr.Arguments.Count - 1];
int incrementAmount;
ILCode incrementCode = GetIncrementCode(addExpr, out incrementAmount);
ILVariable helperVar;
ILExpression initialValue;
if (!(incrementAmount != 0 && addExpr.Arguments[0].Match(ILCode.Stloc, out helperVar, out initialValue)))
return null;
if (expr.Code == ILCode.Stfld) {
if (initialValue.Code != ILCode.Ldfld)
return null;
// There might be two different FieldReference instances, so we compare the field's signatures:
FieldReference getField = (FieldReference)initialValue.Operand;
FieldReference setField = (FieldReference)expr.Operand;
if (!(TypeAnalysis.IsSameType(getField.DeclaringType, setField.DeclaringType)
&& getField.Name == setField.Name && TypeAnalysis.IsSameType(getField.FieldType, setField.FieldType)))
{
return null;
}
} else if (expr.Code == ILCode.Stobj) {
if (!(initialValue.Code == ILCode.Ldobj && initialValue.Operand == expr.Operand))
return null;
} else if (expr.Code == ILCode.CallSetter) {
if (!(initialValue.Code == ILCode.CallGetter && IsGetterSetterPair(initialValue.Operand, expr.Operand)))
return null;
} else if (expr.Code == ILCode.CallvirtSetter) {
if (!(initialValue.Code == ILCode.CallvirtGetter && IsGetterSetterPair(initialValue.Operand, expr.Operand)))
return null;
} else {
if (!initialValue.Code.IsLoadFromArray())
return null;
}
Debug.Assert(expr.Arguments.Count - 1 == initialValue.Arguments.Count);
for (int i = 0; i < initialValue.Arguments.Count; i++) {
if (!initialValue.Arguments[i].MatchLdloc((ILVariable)expr.Arguments[i].Operand))
return null;
}
ILExpression stloc = addExpr.Arguments[0];
if (expr.Code == ILCode.Stobj) {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue.Arguments[0]);
} else if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
initialValue.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress));
} else {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
initialValue.Code = (expr.Code == ILCode.Stfld ? ILCode.Ldflda : ILCode.Ldelema);
}
// TODO: ILRanges?
return stloc;
}
ILCode GetIncrementCode(ILExpression addExpr, out int incrementAmount)
{
ILCode incrementCode;
bool decrement = false;
switch (addExpr.Code) {
case ILCode.Add:
incrementCode = ILCode.PostIncrement;
break;
case ILCode.Add_Ovf:
incrementCode = ILCode.PostIncrement_Ovf;
break;
case ILCode.Add_Ovf_Un:
incrementCode = ILCode.PostIncrement_Ovf_Un;
break;
case ILCode.Sub:
incrementCode = ILCode.PostIncrement;
decrement = true;
break;
case ILCode.Sub_Ovf:
incrementCode = ILCode.PostIncrement_Ovf;
decrement = true;
break;
case ILCode.Sub_Ovf_Un:
incrementCode = ILCode.PostIncrement_Ovf_Un;
decrement = true;
break;
default:
incrementAmount = 0;
return ILCode.Nop;
}
if (addExpr.Arguments[1].Match(ILCode.Ldc_I4, out incrementAmount)) {
if (incrementAmount == -1 || incrementAmount == 1) { // TODO pointer increment?
if (decrement)
incrementAmount = -incrementAmount;
return incrementCode;
}
}
incrementAmount = 0;
return ILCode.Nop;
}
#endregion
#region IntroduceFixedStatements

9
ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs

@ -63,6 +63,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -63,6 +63,7 @@ namespace ICSharpCode.Decompiler.ILAst
bool isStloc = trueLocVar != null;
ILCode opCode = isStloc ? ILCode.Stloc : ILCode.Ret;
TypeReference retType = isStloc ? trueLocVar.Type : this.context.CurrentMethod.ReturnType;
bool retTypeIsBoolean = TypeAnalysis.IsBoolean(retType);
int leftBoolVal;
int rightBoolVal;
ILExpression newExpr;
@ -72,7 +73,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -72,7 +73,7 @@ namespace ICSharpCode.Decompiler.ILAst
// a ? b : true is equivalent to !a || b
// a ? b : false is equivalent to a && b
// a ? false : b is equivalent to !a && b
if (retType == typeSystem.Boolean &&
if (retTypeIsBoolean &&
trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) &&
falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) &&
((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0))
@ -84,14 +85,14 @@ namespace ICSharpCode.Decompiler.ILAst @@ -84,14 +85,14 @@ namespace ICSharpCode.Decompiler.ILAst
} else {
newExpr = new ILExpression(ILCode.LogicNot, null, condExpr);
}
} else if (retType == typeSystem.Boolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal)) {
} else if (retTypeIsBoolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal)) {
// It can be expressed as logical expression
if (leftBoolVal != 0) {
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, condExpr, falseExpr);
} else {
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr);
}
} else if (retType == typeSystem.Boolean && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal)) {
} else if (retTypeIsBoolean && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal)) {
// It can be expressed as logical expression
if (rightBoolVal != 0) {
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr);
@ -152,7 +153,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -152,7 +153,7 @@ namespace ICSharpCode.Decompiler.ILAst
head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out v, out leftExpr) &&
leftExpr.Match(ILCode.Ldloc, out leftVar) &&
head.MatchLastAndBr(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) &&
leftExpr2.Match(ILCode.Ldloc, leftVar) &&
leftExpr2.MatchLdloc(leftVar) &&
labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) &&
rightBB.MatchSingleAndBr(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) &&
v == v2 &&

84
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -231,7 +231,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -231,7 +231,7 @@ namespace ICSharpCode.Decompiler.ILAst
/// <returns>The inferred type</returns>
TypeReference InferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false)
{
if (expectedType != null && expr.ExpectedType != expectedType) {
if (expectedType != null && !IsSameType(expr.ExpectedType, expectedType)) {
expr.ExpectedType = expectedType;
if (expr.Code != ILCode.Stloc) // stloc is special case and never gets re-evaluated
forceInferChildren = true;
@ -295,12 +295,16 @@ namespace ICSharpCode.Decompiler.ILAst @@ -295,12 +295,16 @@ namespace ICSharpCode.Decompiler.ILAst
#region Call / NewObj
case ILCode.Call:
case ILCode.Callvirt:
case ILCode.CallGetter:
case ILCode.CallvirtGetter:
case ILCode.CallSetter:
case ILCode.CallvirtSetter:
{
MethodReference method = (MethodReference)expr.Operand;
if (forceInferChildren) {
for (int i = 0; i < expr.Arguments.Count; i++) {
if (i == 0 && method.HasThis) {
Instruction constraint = expr.GetPrefix(Code.Constrained);
ILExpressionPrefix constraint = expr.GetPrefix(ILCode.Constrained);
if (constraint != null)
InferTypeForExpression(expr.Arguments[i], new ByReferenceType((TypeReference)constraint.Operand));
else if (method.DeclaringType.IsValueType)
@ -312,7 +316,14 @@ namespace ICSharpCode.Decompiler.ILAst @@ -312,7 +316,14 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
}
return SubstituteTypeArgs(method.ReturnType, method);
if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) {
return SubstituteTypeArgs(method.Parameters.Last().ParameterType, method);
} else {
TypeReference type = SubstituteTypeArgs(method.ReturnType, method);
if (expr.GetPrefix(ILCode.PropertyAddress) != null && !(type is ByReferenceType))
type = new ByReferenceType(type);
return type;
}
}
case ILCode.Newobj:
{
@ -411,7 +422,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -411,7 +422,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (forceInferChildren) {
if (pointerType is PointerType)
InferTypeForExpression(expr.Arguments[0], new PointerType(operandType));
else if (operandType != expr.Operand)
else if (!IsSameType(operandType, expr.Operand as TypeReference))
InferTypeForExpression(expr.Arguments[0], new ByReferenceType(operandType));
InferTypeForExpression(expr.Arguments[1], operandType);
}
@ -431,6 +442,17 @@ namespace ICSharpCode.Decompiler.ILAst @@ -431,6 +442,17 @@ namespace ICSharpCode.Decompiler.ILAst
return typeSystem.IntPtr;
case ILCode.Sizeof:
return typeSystem.Int32;
case ILCode.PostIncrement:
case ILCode.PostIncrement_Ovf:
case ILCode.PostIncrement_Ovf_Un:
{
TypeReference elementType = UnpackPointer(InferTypeForExpression(expr.Arguments[0], null));
if (forceInferChildren && elementType != null) {
// Assign expected type to the child expression
InferTypeForExpression(expr.Arguments[0], new ByReferenceType(elementType));
}
return elementType;
}
#endregion
#region Arithmetic instructions
case ILCode.Not: // bitwise complement
@ -470,6 +492,14 @@ namespace ICSharpCode.Decompiler.ILAst @@ -470,6 +492,14 @@ namespace ICSharpCode.Decompiler.ILAst
if (forceInferChildren)
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
return InferTypeForExpression(expr.Arguments[0], typeSystem.UInt32);
case ILCode.CompoundAssignment:
{
TypeReference varType = InferTypeForExpression(expr.Arguments[0].Arguments[0], null);
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], varType);
}
return varType;
}
#endregion
#region Constant loading instructions
case ILCode.Ldnull:
@ -553,14 +583,16 @@ namespace ICSharpCode.Decompiler.ILAst @@ -553,14 +583,16 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Stelem_R8:
case ILCode.Stelem_Ref:
case ILCode.Stelem_Any:
if (forceInferChildren) {
{
ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType;
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
if (arrayType != null) {
InferTypeForExpression(expr.Arguments[2], arrayType.ElementType);
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
if (arrayType != null) {
InferTypeForExpression(expr.Arguments[2], arrayType.ElementType);
}
}
return arrayType != null ? arrayType.ElementType : null;
}
return null;
#endregion
#region Conversion instructions
case ILCode.Conv_I1:
@ -604,8 +636,14 @@ namespace ICSharpCode.Decompiler.ILAst @@ -604,8 +636,14 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Conv_Ovf_U_Un:
return HandleConversion(NativeInt, false, expr.Arguments[0], expectedType, typeSystem.UIntPtr);
case ILCode.Conv_R4:
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.Single);
}
return typeSystem.Single;
case ILCode.Conv_R8:
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.Double);
}
return typeSystem.Double;
case ILCode.Conv_R_Un:
return (expectedType != null && expectedType.MetadataType == MetadataType.Single) ? typeSystem.Single : typeSystem.Double;
@ -707,6 +745,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -707,6 +745,7 @@ namespace ICSharpCode.Decompiler.ILAst
TypeReference elementType = SubstituteTypeArgs(arrayType.ElementType, member);
if (elementType != arrayType.ElementType) {
ArrayType newArrayType = new ArrayType(elementType);
newArrayType.Dimensions.Clear(); // remove the single dimension that Cecil adds by default
foreach (ArrayDimension d in arrayType.Dimensions)
newArrayType.Dimensions.Add(d);
return newArrayType;
@ -784,11 +823,11 @@ namespace ICSharpCode.Decompiler.ILAst @@ -784,11 +823,11 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression right = expr.Arguments[1];
TypeReference leftPreferred = DoInferTypeForExpression(left, expectedType);
TypeReference rightPreferred = DoInferTypeForExpression(right, expectedType);
if (leftPreferred == rightPreferred) {
if (IsSameType(leftPreferred, rightPreferred)) {
return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred;
} else if (rightPreferred == DoInferTypeForExpression(left, rightPreferred)) {
} else if (IsSameType(rightPreferred, DoInferTypeForExpression(left, rightPreferred))) {
return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = rightPreferred;
} else if (leftPreferred == DoInferTypeForExpression(right, leftPreferred)) {
} else if (IsSameType(leftPreferred, DoInferTypeForExpression(right, leftPreferred))) {
return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred;
} else {
left.ExpectedType = right.ExpectedType = TypeWithMoreInformation(leftPreferred, rightPreferred);
@ -813,11 +852,11 @@ namespace ICSharpCode.Decompiler.ILAst @@ -813,11 +852,11 @@ namespace ICSharpCode.Decompiler.ILAst
InferTypeForExpression(left, typeSystem.IntPtr);
right.InferredType = right.ExpectedType = rightPreferred;
return rightPreferred;
} else if (leftPreferred == rightPreferred) {
} else if (IsSameType(leftPreferred, rightPreferred)) {
return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred;
} else if (rightPreferred == DoInferTypeForExpression(left, rightPreferred)) {
} else if (IsSameType(rightPreferred, DoInferTypeForExpression(left, rightPreferred))) {
return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = rightPreferred;
} else if (leftPreferred == DoInferTypeForExpression(right, leftPreferred)) {
} else if (IsSameType(leftPreferred, DoInferTypeForExpression(right, leftPreferred))) {
return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred;
} else {
left.ExpectedType = right.ExpectedType = TypeWithMoreInformation(leftPreferred, rightPreferred);
@ -839,11 +878,11 @@ namespace ICSharpCode.Decompiler.ILAst @@ -839,11 +878,11 @@ namespace ICSharpCode.Decompiler.ILAst
return leftPreferred;
} else {
TypeReference rightPreferred = DoInferTypeForExpression(right, expectedType);
if (leftPreferred == rightPreferred) {
if (IsSameType(leftPreferred, rightPreferred)) {
return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred;
} else if (rightPreferred == DoInferTypeForExpression(left, rightPreferred)) {
} else if (IsSameType(rightPreferred, DoInferTypeForExpression(left, rightPreferred))) {
return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = rightPreferred;
} else if (leftPreferred == DoInferTypeForExpression(right, leftPreferred)) {
} else if (IsSameType(leftPreferred, DoInferTypeForExpression(right, leftPreferred))) {
return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred;
} else {
left.ExpectedType = right.ExpectedType = TypeWithMoreInformation(leftPreferred, rightPreferred);
@ -1010,5 +1049,14 @@ namespace ICSharpCode.Decompiler.ILAst @@ -1010,5 +1049,14 @@ namespace ICSharpCode.Decompiler.ILAst
v.Type = null;
}
}
public static bool IsSameType(TypeReference type1, TypeReference type2)
{
if (type1 == type2)
return true;
if (type1 == null || type2 == null)
return false;
return type1.FullName == type2.FullName; // TODO: implement this more efficiently?
}
}
}

49
ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs

@ -1,39 +1,46 @@ @@ -1,39 +1,46 @@
// 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 class CheckedUnchecked
{
public void Operators(int a, int b)
public int Operators(int a, int b)
{
int c1 = checked(a + b);
int u1 = unchecked(a + b);
int c2 = checked(a - b);
int u2 = unchecked(a - b);
int c3 = checked(a * b);
int u3 = unchecked(a * b);
int c4 = checked(a / b);
int u4 = unchecked(a / b);
int c5 = checked(a % b);
int u5 = unchecked(a % b);
int num = checked(a + b);
int num2 = a + b;
int num3 = checked(a - b);
int num4 = a - b;
int num5 = checked(a * b);
int num6 = a * b;
int num7 = a / b;
int num8 = a % b;
// The division operators / and % only exist in one form (checked vs. unchecked doesn't matter for them)
return num * num2 * num3 * num4 * num5 * num6 * num7 * num8;
}
public void Cast(int a)
public int Cast(int a)
{
short c1 = checked((short)a);
short u1 = unchecked((short)a);
byte c2 = checked((byte)a);
byte u2 = unchecked((byte)a);
short num = checked((short)a);
short num2 = (short)a;
byte b = checked((byte)a);
byte b2 = (byte)a;
return num * num2 * b * b2;
}
public void ForWithCheckedIteratorAndUncheckedBody(int n)
{
checked {
for (int i = n + 1; i < n + 1; i++) {
unchecked {
n = i * i;
}
n = unchecked(i * i);
}
}
}
public void ForWithCheckedInitializerAndUncheckedIterator(int n)
{
checked {
int i = n;
for (i -= 10; i < n; i = unchecked(i + 1)) {
n--;
}
}
}

17
ICSharpCode.Decompiler/Tests/DelegateConstruction.cs

@ -46,7 +46,11 @@ public static class DelegateConstruction @@ -46,7 +46,11 @@ public static class DelegateConstruction
for (int i = 0; i < 10; i++)
{
int counter;
list.Add(x => counter = x);
list.Add(delegate(int x)
{
counter = x;
}
);
}
return list;
}
@ -57,13 +61,20 @@ public static class DelegateConstruction @@ -57,13 +61,20 @@ public static class DelegateConstruction
int counter;
for (int i = 0; i < 10; i++)
{
list.Add(x => counter = x);
list.Add(delegate(int x)
{
counter = x;
}
);
}
return list;
}
public static Action StaticAnonymousMethodNoClosure()
{
return delegate { Console.WriteLine(); };
return delegate
{
Console.WriteLine();
};
}
}

40
ICSharpCode.Decompiler/Tests/ExceptionHandling.cs

@ -7,42 +7,60 @@ public class ExceptionHandling @@ -7,42 +7,60 @@ public class ExceptionHandling
{
public void MethodEndingWithEndFinally()
{
try {
try
{
throw null;
} finally {
}
finally
{
Console.WriteLine();
}
}
public void MethodEndingWithRethrow()
{
try {
try
{
throw null;
} catch {
}
catch
{
throw;
}
}
public void TryCatchFinally()
{
try {
try
{
Console.WriteLine("Try");
} catch (Exception ex) {
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
} finally {
}
finally
{
Console.WriteLine("Finally");
}
}
public void TryCatchMultipleHandlers()
{
try {
try
{
Console.WriteLine("Try");
} catch (InvalidOperationException ex) {
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.Message);
} catch (Exception ex) {
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
} catch {
}
catch
{
Console.WriteLine("other");
}
}

4
ICSharpCode.Decompiler/Tests/Generics.cs

@ -23,7 +23,7 @@ public static class Generics @@ -23,7 +23,7 @@ public static class Generics
public void Size(int capacity)
{
Array.Resize(ref this.arr, capacity);
Array.Resize<T>(ref this.arr, capacity);
}
public void Grow(int capacity)
@ -43,7 +43,7 @@ public static class Generics @@ -43,7 +43,7 @@ public static class Generics
{
}
public static Dictionary<string, string>.KeyCollection.Enumerator GetEnumerator(Dictionary<string, string> d, MyArray<string>.NestedClass<int> nc)
public static Dictionary<string, string>.KeyCollection.Enumerator GetEnumerator(Dictionary<string, string> d, Generics.MyArray<string>.NestedClass<int> nc)
{
// Tests references to inner classes in generic classes
return d.Keys.GetEnumerator();

6
ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs

@ -21,6 +21,12 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -21,6 +21,12 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
if (section.Attributes.Count == 0)
section.Remove();
}
if (section.AttributeTarget == AttributeTarget.Module && type.Identifier == "UnverifiableCode")
{
attribute.Remove();
if (section.Attributes.Count == 0)
section.Remove();
}
return null;
}

6
ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
<ProjectGuid>{FEC0DA52-C4A6-4710-BE36-B484A20C5E22}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<OutputType>Exe</OutputType>
<OutputType>Library</OutputType>
<RootNamespace>ICSharpCode.Decompiler.Tests</RootNamespace>
<AssemblyName>ICSharpCode.Decompiler.Tests</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
@ -14,6 +14,7 @@ @@ -14,6 +14,7 @@
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<NoWarn>67,169</NoWarn>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'x86' ">
<PlatformTarget>x86</PlatformTarget>
@ -27,7 +28,6 @@ @@ -27,7 +28,6 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>Full</DebugType>
<Optimize>False</Optimize>
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
@ -35,7 +35,6 @@ @@ -35,7 +35,6 @@
<DebugSymbols>false</DebugSymbols>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
@ -54,6 +53,7 @@ @@ -54,6 +53,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="CheckedUnchecked.cs" />
<Compile Include="IncrementDecrement.cs" />
<Compile Include="Switch.cs" />
<Compile Include="UnsafeCode.cs" />
<Compile Include="YieldReturn.cs" />

217
ICSharpCode.Decompiler/Tests/IncrementDecrement.cs

@ -0,0 +1,217 @@ @@ -0,0 +1,217 @@
// 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 class IncrementDecrement
{
public class MutableClass
{
public int Field;
public int Property
{
get;
set;
}
public uint this[string name]
{
get
{
return 0u;
}
set
{
}
}
}
public static int StaticField;
public static int StaticProperty
{
get;
set;
}
private IncrementDecrement.MutableClass M()
{
return new IncrementDecrement.MutableClass();
}
private int[,] Array()
{
return null;
}
private unsafe int* GetPointer()
{
return null;
}
public int PreIncrementInAddition(int i, int j)
{
return i + ++j;
}
public int PreIncrementArrayElement(int[] array, int pos)
{
return --array[pos];
}
public int PreIncrementInstanceField()
{
return ++this.M().Field;
}
public int PreIncrementInstanceField2(IncrementDecrement.MutableClass m)
{
return ++m.Field;
}
public int PreIncrementInstanceProperty()
{
return ++this.M().Property;
}
public int PreIncrementStaticField()
{
return ++IncrementDecrement.StaticField;
}
public int PreIncrementStaticProperty()
{
return ++IncrementDecrement.StaticProperty;
}
// public uint PreIncrementIndexer(string name)
// {
// return ++this.M()[name];
// }
public int PreIncrementByRef(ref int i)
{
return ++i;
}
public unsafe int PreIncrementByPointer()
{
return ++(*this.GetPointer());
}
public int PreIncrement2DArray()
{
return ++this.Array()[1, 2];
}
public int CompoundAssignInstanceField()
{
return this.M().Field *= 10;
}
public int CompoundAssignInstanceProperty()
{
return this.M().Property *= 10;
}
public int CompoundAssignStaticField()
{
return IncrementDecrement.StaticField ^= 100;
}
public int CompoundAssignStaticProperty()
{
return IncrementDecrement.StaticProperty &= 10;
}
public int CompoundAssignArrayElement1(int[] array, int pos)
{
return array[pos] *= 10;
}
public int CompoundAssignArrayElement2(int[] array)
{
return array[Environment.TickCount] *= 10;
}
// public uint CompoundAssignIndexer(string name)
// {
// return this.M()[name] -= 2;
// }
public int CompoundAssignIncrement2DArray()
{
return this.Array()[1, 2] %= 10;
}
public int CompoundAssignByRef(ref int i)
{
return i <<= 2;
}
public unsafe double CompoundAssignByPointer(double* ptr)
{
return *ptr /= 1.5;
}
public int PostIncrementInAddition(int i, int j)
{
return i++ + j;
}
public int PostIncrementArrayElement(int[] array, int pos)
{
return array[pos]--;
}
public int PostIncrementStaticField()
{
return IncrementDecrement.StaticField++;
}
public int PostIncrementStaticProperty()
{
return IncrementDecrement.StaticProperty++;
}
public int PostIncrementInstanceField(IncrementDecrement.MutableClass m)
{
return m.Field++;
}
// public uint PostIncrementIndexer(string name)
// {
// return this.M()[name]++;
// }
public int PostIncrementInstanceField()
{
return this.M().Field--;
}
public int PostIncrementInstanceProperty()
{
return this.M().Property--;
}
public int PostIncrement2DArray()
{
return this.Array()[IncrementDecrement.StaticField, IncrementDecrement.StaticProperty]++;
}
public int PostIncrementByRef(ref int i)
{
return i++;
}
public unsafe int PostIncrementByPointer()
{
return (*this.GetPointer())++;
}
// public unsafe int PostIncrementOfPointer(int* ptr)
// {
// return *(ptr++);
// }
}

16
ICSharpCode.Decompiler/Tests/Loops.cs

@ -8,29 +8,33 @@ public class Loops @@ -8,29 +8,33 @@ public class Loops
{
public void ForEach(IEnumerable<string> enumerable)
{
foreach (string text in enumerable) {
text.ToLower();
foreach (string current in enumerable)
{
current.ToLower();
}
}
public void ForEachOverList(List<string> list)
{
// List has a struct as enumerator, so produces quite different IL than foreach over the IEnumerable interface
foreach (string text in list) {
text.ToLower();
foreach (string current in list)
{
current.ToLower();
}
}
public void ForEachOverArray(string[] array)
{
foreach (string text in array) {
foreach (string text in array)
{
text.ToLower();
}
}
public void ForOverArray(string[] array)
{
for (int i = 0; i < array.Length; i++) {
for (int i = 0; i < array.Length; i++)
{
array[i].ToLower();
}
}

27
ICSharpCode.Decompiler/Tests/MultidimensionalArray.cs

@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
using System;
public static class MultidimensionalArray
public class MultidimensionalArray
{
internal class Generic<T, S> where T : new()
{
@ -12,20 +12,31 @@ public static class MultidimensionalArray @@ -12,20 +12,31 @@ public static class MultidimensionalArray
public T this[int i, int j]
{
get { return a[i, j]; }
set { a[i, j] = value; }
get
{
return this.a[i, j];
}
set
{
this.a[i, j] = value;
}
}
public void TestB(S x, ref S y)
{
b[5, 3] = new S[10];
b[5, 3][0] = default(S);
b[5, 3][1] = x;
b[5, 3][2] = y;
this.b[5, 3] = new S[10];
this.b[5, 3][0] = default(S);
this.b[5, 3][1] = x;
this.b[5, 3][2] = y;
}
public void PassByReference(ref T arr)
{
this.PassByReference(ref this.a[10, 10]);
}
}
public static int[][,] MakeArray()
public int[][,] MakeArray()
{
return new int[10][,];
}

63
ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs

@ -6,48 +6,61 @@ using System.Text; @@ -6,48 +6,61 @@ using System.Text;
public class PropertiesAndEvents
{
public int Getter(StringBuilder b)
{
return b.Length;
}
public event EventHandler AutomaticEvent;
public void Setter(StringBuilder b)
[field: NonSerialized]
public event EventHandler AutomaticEventWithInitializer = delegate
{
b.Capacity = 100;
}
;
public char IndexerGetter(StringBuilder b)
public event EventHandler CustomEvent
{
return b[50];
add
{
this.AutomaticEvent += value;
}
remove
{
this.AutomaticEvent -= value;
}
}
public void IndexerSetter(StringBuilder b)
public int AutomaticProperty
{
b[42] = 'b';
get;
set;
}
public int AutomaticProperty { get; set; }
public int CustomProperty {
get {
public int CustomProperty
{
get
{
return this.AutomaticProperty;
}
set {
set
{
this.AutomaticProperty = value;
}
}
public event EventHandler AutomaticEvent;
public int Getter(StringBuilder b)
{
return b.Length;
}
[field: NonSerialized]
public event EventHandler AutomaticEventWithInitializer = delegate {};
public void Setter(StringBuilder b)
{
b.Capacity = 100;
}
public event EventHandler CustomEvent {
add {
this.AutomaticEvent += value;
}
remove {
this.AutomaticEvent -= value;
}
public char IndexerGetter(StringBuilder b)
{
return b[50];
}
public void IndexerSetter(StringBuilder b)
{
b[42] = 'b';
}
}

94
ICSharpCode.Decompiler/Tests/TestRunner.cs

@ -6,25 +6,94 @@ using System.CodeDom.Compiler; @@ -6,25 +6,94 @@ using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ICSharpCode.Decompiler.Ast;
using Microsoft.CSharp;
using Mono.Cecil;
using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests
{
[TestFixture]
public class TestRunner
{
public static void Main(string[] args)
[Test, Ignore("unncessary primitive casts")]
public void CheckedUnchecked()
{
if (args.Length == 1)
TestFile(args[0]);
else
TestFile(@"..\..\Tests\DelegateConstruction.cs");
Console.ReadKey();
TestFile(@"..\..\Tests\CheckedUnchecked.cs");
}
[Test, Ignore("Missing cast on null")]
public void DelegateConstruction()
{
TestFile(@"..\..\Tests\DelegateConstruction.cs");
}
[Test, Ignore("bug with variable-less catch")]
public void ExceptionHandling()
{
TestFile(@"..\..\Tests\ExceptionHandling.cs");
}
[Test]
public void Generics()
{
TestFile(@"..\..\Tests\Generics.cs");
}
[Test]
public void IncrementDecrement()
{
TestFile(@"..\..\Tests\IncrementDecrement.cs");
}
[Test, Ignore("Formatting issues (array initializers not on single line)")]
public void InitializerTests()
{
TestFile(@"..\..\Tests\InitializerTests.cs");
}
[Test, Ignore("ForEachOverArray not supported")]
public void Loops()
{
TestFile(@"..\..\Tests\Loops.cs");
}
[Test]
public void MultidimensionalArray()
{
TestFile(@"..\..\Tests\MultidimensionalArray.cs");
}
[Test]
public void PropertiesAndEvents()
{
TestFile(@"..\..\Tests\PropertiesAndEvents.cs");
}
[Test, Ignore]
public void Switch()
{
TestFile(@"..\..\Tests\Switch.cs");
}
[Test, Ignore("has incorrect casts to IntPtr")]
public void UnsafeCode()
{
TestFile(@"..\..\Tests\UnsafeCode.cs");
}
[Test, Ignore("IncrementArrayLocation not yet supported")]
public void ValueTypes()
{
TestFile(@"..\..\Tests\ValueTypes.cs");
}
[Test, Ignore("Redundant yield break; not removed")]
public void YieldReturn()
{
TestFile(@"..\..\Tests\YieldReturn.cs");
}
static void TestFile(string fileName)
{
string code = File.ReadAllText(fileName);
@ -49,19 +118,19 @@ namespace ICSharpCode.Decompiler.Tests @@ -49,19 +118,19 @@ namespace ICSharpCode.Decompiler.Tests
string line1, line2;
while ((line1 = r1.ReadLine()) != null) {
string trimmed = line1.Trim();
if (trimmed.Length == 0 || trimmed.StartsWith("//", StringComparison.Ordinal) || line1.StartsWith("using ", StringComparison.Ordinal)) {
if (trimmed.Length == 0 || trimmed.StartsWith("//", StringComparison.Ordinal) | trimmed.StartsWith("#", StringComparison.Ordinal)) {
diff.WriteLine(" " + line1);
continue;
}
line2 = r2.ReadLine();
while (line2 != null && (line2.StartsWith("using ", StringComparison.Ordinal) || line2.Trim().Length == 0))
while (line2 != null && string.IsNullOrWhiteSpace(line2))
line2 = r2.ReadLine();
if (line2 == null) {
ok = false;
diff.WriteLine("-" + line1);
continue;
}
if (line1 != line2) {
if (line1.Trim() != line2.Trim()) {
ok = false;
if (numberOfContinuousMistakes++ > 5)
return false;
@ -84,6 +153,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -84,6 +153,7 @@ namespace ICSharpCode.Decompiler.Tests
{
CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v4.0" } });
CompilerParameters options = new CompilerParameters();
options.CompilerOptions = "/unsafe";
options.ReferencedAssemblies.Add("System.Core.dll");
CompilerResults results = provider.CompileAssemblyFromSource(options, code);
try {

69
ICSharpCode.Decompiler/Tests/UnsafeCode.cs

@ -5,50 +5,65 @@ using System; @@ -5,50 +5,65 @@ using System;
public class UnsafeCode
{
public unsafe long ConvertDoubleToLong(double d)
public unsafe int* NullPointer
{
return *(long*)&d;
}
public unsafe int* NullPointer {
get {
get
{
return null;
}
}
public unsafe long ConvertDoubleToLong(double d)
{
return *(long*)(&d);
}
public unsafe void PassRefParameterAsPointer(ref int p)
{
fixed (int* ptr = &p)
PassPointerAsRefParameter(ptr);
{
this.PassPointerAsRefParameter(ptr);
}
}
public unsafe void PassPointerAsRefParameter(int* p)
{
PassRefParameterAsPointer(ref *p);
this.PassRefParameterAsPointer(ref *p);
}
public unsafe void AddressInMultiDimensionalArray(double[,] matrix)
{
fixed (double* ptr = &matrix[1, 2]) {
this.PointerReferenceExpression(ptr);
}
}
public unsafe void FixedStringAccess(string text)
{
fixed (char* c = text) {
char* tmp = c;
while (*tmp != 0) {
*tmp = 'A';
tmp++;
fixed (char* ptr = text)
{
char* ptr2 = ptr;
while (*ptr2 != 0)
{
*ptr2 = 'A';
ptr2++;
}
}
}
public unsafe void PutDoubleIntoLongArray1(long[] array, int index, double val)
{
fixed (long* l = array) {
((double*)l)[index] = val;
fixed (long* ptr = array)
{
((double*)ptr)[index] = val;
}
}
public unsafe void PutDoubleIntoLongArray2(long[] array, int index, double val)
{
fixed (long* l = &array[index]) {
*(double*)l = val;
fixed (long* ptr = &array[index])
{
*(double*)ptr = val;
}
}
@ -59,24 +74,26 @@ public class UnsafeCode @@ -59,24 +74,26 @@ public class UnsafeCode
public unsafe void FixMultipleStrings(string text)
{
fixed (char* c = text, d = Environment.UserName, e = text) {
*c = 'c';
*d = 'd';
*e = 'e';
fixed (char* ptr = text, userName = Environment.UserName, ptr2 = text)
{
*ptr = 'c';
*userName = 'd';
*ptr2 = 'e';
}
}
public unsafe string StackAlloc(int count)
{
char* a = stackalloc char[count];
for (int i = 0; i < count; i++) {
a[i] = (char)i;
char* ptr = stackalloc char[count];
for (int i = 0; i < count; i++)
{
ptr[i] = (char)i;
}
return PointerReferenceExpression((double*)a);
return this.PointerReferenceExpression((double*)ptr);
}
unsafe ~UnsafeCode()
{
PassPointerAsRefParameter(NullPointer);
this.PassPointerAsRefParameter(this.NullPointer);
}
}

57
ICSharpCode.Decompiler/Tests/ValueTypes.cs

@ -22,70 +22,71 @@ public static class ValueTypes @@ -22,70 +22,71 @@ public static class ValueTypes
public void MethodCalls()
{
this.SetField();
Test(this);
Test(ref this);
ValueTypes.S.Test(this);
ValueTypes.S.Test(ref this);
}
static void Test(S byVal)
private static void Test(ValueTypes.S byVal)
{
}
static void Test(ref S byRef)
private static void Test(ref ValueTypes.S byRef)
{
}
}
public static S InitObj1()
public static ValueTypes.S InitObj1()
{
S s = default(S);
return s;
ValueTypes.S result = default(ValueTypes.S);
ValueTypes.MakeArray();
return result;
}
public static S InitObj2()
public static ValueTypes.S InitObj2()
{
return default(S);
return default(ValueTypes.S);
}
public static void InitObj3(out S p)
public static void InitObj3(out ValueTypes.S p)
{
p = default(S);
p = default(ValueTypes.S);
}
public static S CallValueTypeCtor1()
public static ValueTypes.S CallValueTypeCtor1()
{
return new S(10);
return new ValueTypes.S(10);
}
public static S CallValueTypeCtor2()
public static ValueTypes.S CallValueTypeCtor2()
{
S s = new S(10);
return s;
ValueTypes.S result = new ValueTypes.S(10);
return result;
}
public static S Copy1(S p)
public static ValueTypes.S Copy1(ValueTypes.S p)
{
return p;
}
public static S Copy2(ref S p)
public static ValueTypes.S Copy2(ref ValueTypes.S p)
{
return p;
}
public static void Copy3(S p, out S o)
public static void Copy3(ValueTypes.S p, out ValueTypes.S o)
{
o = p;
}
public static void Copy4(ref S p, out S o)
public static void Copy4(ref ValueTypes.S p, out ValueTypes.S o)
{
o = p;
}
public static void Copy4b(ref S p, out S o)
public static void Copy4b(ref ValueTypes.S p, out ValueTypes.S o)
{
// test passing through by-ref arguments
Copy4(ref p, out o);
ValueTypes.Copy4(ref p, out o);
}
public static void Issue56(int i, out string str)
@ -94,20 +95,20 @@ public static class ValueTypes @@ -94,20 +95,20 @@ public static class ValueTypes
str += i.ToString();
}
public static void CopyAroundAndModifyField(S s)
public static void CopyAroundAndModifyField(ValueTypes.S s)
{
S locS = s;
locS.Field += 10;
s = locS;
ValueTypes.S s2 = s;
s2.Field += 10;
s = s2;
}
static int[] MakeArray()
private static int[] MakeArray()
{
return null;
}
public static void IncrementArrayLocation()
{
MakeArray()[Environment.TickCount]++;
ValueTypes.MakeArray()[Environment.TickCount]++;
}
}

Loading…
Cancel
Save