Browse Source

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

pull/191/merge
Eusebiu Marcu 16 years ago
parent
commit
45d37675db
  1. 432
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 129
      ICSharpCode.Decompiler/ILAst/GotoRemoval.cs
  3. 225
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  4. 29
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  5. 4
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  6. 2
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  7. 18
      NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs

432
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -84,9 +84,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -84,9 +84,7 @@ namespace ICSharpCode.Decompiler.Ast
{
Ast.BlockStatement astBlock = new BlockStatement();
if (block != null) {
if (block.EntryGoto != null)
astBlock.Add((Statement)TransformExpression(block.EntryGoto));
foreach(ILNode node in block.Body) {
foreach(ILNode node in block.GetChildren()) {
astBlock.AddRange(TransformNode(node));
}
}
@ -127,16 +125,18 @@ namespace ICSharpCode.Decompiler.Ast @@ -127,16 +125,18 @@ namespace ICSharpCode.Decompiler.Ast
};
} else if (node is ILSwitch) {
ILSwitch ilSwitch = (ILSwitch)node;
SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition.Arguments[0]) };
for (int i = 0; i < ilSwitch.CaseBlocks.Count; i++) {
SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition) };
foreach (var caseBlock in ilSwitch.CaseBlocks) {
SwitchSection section = new SwitchSection();
section.CaseLabels.Add(new CaseLabel() { Expression = new PrimitiveExpression(i) });
section.Statements.Add(TransformBlock(ilSwitch.CaseBlocks[i]));
if (caseBlock.Values != null) {
section.CaseLabels.AddRange(caseBlock.Values.Select(i => new CaseLabel() { Expression = AstBuilder.MakePrimitive(i, ilSwitch.Condition.InferredType) }));
} else {
section.CaseLabels.Add(new CaseLabel());
}
section.Statements.Add(TransformBlock(caseBlock));
switchStmt.SwitchSections.Add(section);
}
yield return switchStmt;
if (ilSwitch.DefaultGoto != null)
yield return (Statement)TransformExpression(ilSwitch.DefaultGoto);
} else if (node is ILTryCatchBlock) {
ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node);
var tryCatchStmt = new Ast.TryCatchStatement();
@ -161,41 +161,6 @@ namespace ICSharpCode.Decompiler.Ast @@ -161,41 +161,6 @@ namespace ICSharpCode.Decompiler.Ast
}
}
List<Ast.Expression> TransformExpressionArguments(ILExpression expr)
{
List<Ast.Expression> args = new List<Ast.Expression>();
// Args generated by nested expressions (which must be closed)
foreach(ILExpression arg in expr.Arguments) {
args.Add((Ast.Expression)TransformExpression(arg));
}
return args;
}
static string FormatByteCodeOperand(object operand)
{
if (operand == null) {
return string.Empty;
//} else if (operand is ILExpression) {
// return string.Format("IL_{0:X2}", ((ILExpression)operand).Offset);
} else if (operand is MethodReference) {
return ((MethodReference)operand).Name + "()";
} else if (operand is Cecil.TypeReference) {
return ((Cecil.TypeReference)operand).FullName;
} else if (operand is VariableDefinition) {
return ((VariableDefinition)operand).Name;
} else if (operand is ParameterDefinition) {
return ((ParameterDefinition)operand).Name;
} else if (operand is FieldReference) {
return ((FieldReference)operand).Name;
} else if (operand is string) {
return "\"" + operand + "\"";
} else if (operand is int) {
return operand.ToString();
} else {
return operand.ToString();
}
}
AstNode TransformExpression(ILExpression expr)
{
AstNode node = TransformByteCode(expr);
@ -211,57 +176,57 @@ namespace ICSharpCode.Decompiler.Ast @@ -211,57 +176,57 @@ namespace ICSharpCode.Decompiler.Ast
object operand = byteCode.Operand;
AstType operandAsTypeRef = AstBuilder.ConvertType(operand as Cecil.TypeReference);
List<Ast.Expression> args = TransformExpressionArguments(byteCode);
List<Ast.Expression> args = new List<Expression>();
foreach(ILExpression arg in byteCode.Arguments) {
args.Add((Ast.Expression)TransformExpression(arg));
}
Ast.Expression arg1 = args.Count >= 1 ? args[0] : null;
Ast.Expression arg2 = args.Count >= 2 ? args[1] : null;
Ast.Expression arg3 = args.Count >= 3 ? args[2] : null;
switch(byteCode.Code) {
#region Arithmetic
case ILCode.Add: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
case ILCode.Add_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
case ILCode.Add_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
case ILCode.Div: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2);
case ILCode.Div_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2);
case ILCode.Mul: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2);
case ILCode.Mul_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2);
case ILCode.Mul_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2);
case ILCode.Rem: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2);
case ILCode.Rem_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2);
case ILCode.Sub: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
case ILCode.Sub_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
case ILCode.Sub_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
case ILCode.And: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2);
case ILCode.Or: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseOr, arg2);
case ILCode.Xor: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ExclusiveOr, arg2);
case ILCode.Shl: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2);
case ILCode.Shr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2);
case ILCode.Shr_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2);
case ILCode.Neg: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Minus, arg1);
case ILCode.Not: return new Ast.UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1);
#endregion
#region Arrays
#region Arithmetic
case ILCode.Add: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
case ILCode.Add_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
case ILCode.Add_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
case ILCode.Div: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2);
case ILCode.Div_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2);
case ILCode.Mul: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2);
case ILCode.Mul_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2);
case ILCode.Mul_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2);
case ILCode.Rem: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2);
case ILCode.Rem_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2);
case ILCode.Sub: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
case ILCode.Sub_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
case ILCode.Sub_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
case ILCode.And: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2);
case ILCode.Or: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseOr, arg2);
case ILCode.Xor: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ExclusiveOr, arg2);
case ILCode.Shl: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2);
case ILCode.Shr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2);
case ILCode.Shr_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2);
case ILCode.Neg: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Minus, arg1);
case ILCode.Not: return new Ast.UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1);
#endregion
#region Arrays
case ILCode.Newarr:
case ILCode.InitArray:
{
var ace = new Ast.ArrayCreateExpression();
ace.Type = operandAsTypeRef;
ComposedType ct = operandAsTypeRef as ComposedType;
if (ct != null) {
// change "new (int[,])[10] to new int[10][,]"
ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers);
}
if (byteCode.Code == ILCode.InitArray) {
ace.Initializer = new ArrayInitializerExpression();
ace.Initializer.Elements.AddRange(args);
} else {
ace.Arguments.Add(arg1);
}
return ace;
case ILCode.InitArray: {
var ace = new Ast.ArrayCreateExpression();
ace.Type = operandAsTypeRef;
ComposedType ct = operandAsTypeRef as ComposedType;
if (ct != null) {
// change "new (int[,])[10] to new int[10][,]"
ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers);
}
if (byteCode.Code == ILCode.InitArray) {
ace.Initializer = new ArrayInitializerExpression();
ace.Initializer.Elements.AddRange(args);
} else {
ace.Arguments.Add(arg1);
}
case ILCode.Ldlen:
return arg1.Member("Length");
return ace;
}
case ILCode.Ldlen: return arg1.Member("Length");
case ILCode.Ldelem_I:
case ILCode.Ldelem_I1:
case ILCode.Ldelem_I2:
@ -275,9 +240,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -275,9 +240,7 @@ 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:
@ -288,33 +251,28 @@ namespace ICSharpCode.Decompiler.Ast @@ -288,33 +251,28 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Stelem_Ref:
case ILCode.Stelem_Any:
return new Ast.AssignmentExpression(arg1.Indexer(arg2), arg3);
#endregion
#region Comparison
case ILCode.Ceq:
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2);
case ILCode.Cgt:
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2);
case ILCode.Cgt_Un:
#endregion
#region Comparison
case ILCode.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2);
case ILCode.Cgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2);
case ILCode.Cgt_Un: {
// can also mean Inequality, when used with object references
{
TypeReference arg1Type = byteCode.Arguments[0].InferredType;
if (arg1Type != null && !arg1Type.IsValueType)
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2);
else
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2);
}
case ILCode.Clt:
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2);
case ILCode.Clt_Un:
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2);
#endregion
#region Logical
TypeReference arg1Type = byteCode.Arguments[0].InferredType;
if (arg1Type != null && !arg1Type.IsValueType)
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2);
else
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2);
}
case ILCode.Clt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2);
case ILCode.Clt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2);
#endregion
#region Logical
case ILCode.LogicNot: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1);
case ILCode.LogicAnd: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalAnd, arg2);
case ILCode.LogicOr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalOr, arg2);
case ILCode.TernaryOp: return new Ast.ConditionalExpression() { Condition = arg1, TrueExpression = arg2, FalseExpression = arg3 };
#endregion
#region Branch
#endregion
#region Branch
case ILCode.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name);
case ILCode.Brtrue:
return new Ast.IfElseStatement() {
@ -323,10 +281,10 @@ namespace ICSharpCode.Decompiler.Ast @@ -323,10 +281,10 @@ namespace ICSharpCode.Decompiler.Ast
new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name)
}
};
case ILCode.LoopBreak: return new Ast.BreakStatement();
case ILCode.LoopContinue: return new Ast.ContinueStatement();
#endregion
#region Conversions
case ILCode.LoopOrSwitchBreak: return new Ast.BreakStatement();
case ILCode.LoopContinue: return new Ast.ContinueStatement();
#endregion
#region Conversions
case ILCode.Conv_I1:
case ILCode.Conv_I2:
case ILCode.Conv_I4:
@ -336,12 +294,11 @@ namespace ICSharpCode.Decompiler.Ast @@ -336,12 +294,11 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Conv_U4:
case ILCode.Conv_U8:
return arg1; // conversion is handled by Convert() function using the info from type analysis
case ILCode.Conv_I: return arg1.CastTo(typeof(IntPtr)); // TODO
case ILCode.Conv_U: return arg1.CastTo(typeof(UIntPtr)); // TODO
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_I: return arg1.CastTo(typeof(IntPtr)); // TODO
case ILCode.Conv_U: return arg1.CastTo(typeof(UIntPtr)); // TODO
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_Ovf_I1:
case ILCode.Conv_Ovf_I2:
case ILCode.Conv_Ovf_I4:
@ -359,22 +316,17 @@ namespace ICSharpCode.Decompiler.Ast @@ -359,22 +316,17 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Conv_Ovf_U4_Un:
case ILCode.Conv_Ovf_U8_Un:
return arg1; // conversion was handled by Convert() function using the info from type analysis
case ILCode.Conv_Ovf_I: return arg1.CastTo(typeof(IntPtr)); // TODO
case ILCode.Conv_Ovf_U: return arg1.CastTo(typeof(UIntPtr));
case ILCode.Conv_Ovf_I_Un: return arg1.CastTo(typeof(IntPtr));
case ILCode.Conv_Ovf_U_Un: return arg1.CastTo(typeof(UIntPtr));
case ILCode.Castclass:
case ILCode.Unbox_Any:
return arg1.CastTo(operandAsTypeRef);
case ILCode.Isinst:
return arg1.CastAs(operandAsTypeRef);
case ILCode.Box:
return arg1;
case ILCode.Unbox:
return InlineAssembly(byteCode, args);
#endregion
#region Indirect
case ILCode.Conv_Ovf_I: return arg1.CastTo(typeof(IntPtr)); // TODO
case ILCode.Conv_Ovf_U: return arg1.CastTo(typeof(UIntPtr));
case ILCode.Conv_Ovf_I_Un: return arg1.CastTo(typeof(IntPtr));
case ILCode.Conv_Ovf_U_Un: return arg1.CastTo(typeof(UIntPtr));
case ILCode.Castclass: return arg1.CastTo(operandAsTypeRef);
case ILCode.Unbox_Any: return arg1.CastTo(operandAsTypeRef);
case ILCode.Isinst: return arg1.CastAs(operandAsTypeRef);
case ILCode.Box: return arg1;
case ILCode.Unbox: return InlineAssembly(byteCode, args);
#endregion
#region Indirect
case ILCode.Ldind_I:
case ILCode.Ldind_I1:
case ILCode.Ldind_I2:
@ -391,7 +343,6 @@ namespace ICSharpCode.Decompiler.Ast @@ -391,7 +343,6 @@ namespace ICSharpCode.Decompiler.Ast
return ((DirectionExpression)args[0]).Expression.Detach();
else
return InlineAssembly(byteCode, args);
case ILCode.Stind_I:
case ILCode.Stind_I1:
case ILCode.Stind_I2:
@ -405,49 +356,43 @@ namespace ICSharpCode.Decompiler.Ast @@ -405,49 +356,43 @@ namespace ICSharpCode.Decompiler.Ast
return new AssignmentExpression(((DirectionExpression)args[0]).Expression.Detach(), args[1]);
else
return InlineAssembly(byteCode, args);
#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.Ldftn:
{
Cecil.MethodReference cecilMethod = ((MethodReference)operand);
var expr = new Ast.IdentifierExpression(cecilMethod.Name);
expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod));
expr.AddAnnotation(cecilMethod);
return new IdentifierExpression("ldftn").Invoke(expr)
.WithAnnotation(new Transforms.DelegateConstruction.Annotation(false));
}
case ILCode.Ldvirtftn:
{
Cecil.MethodReference cecilMethod = ((MethodReference)operand);
var expr = new Ast.IdentifierExpression(cecilMethod.Name);
expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod));
expr.AddAnnotation(cecilMethod);
return new IdentifierExpression("ldvirtftn").Invoke(expr)
.WithAnnotation(new Transforms.DelegateConstruction.Annotation(true));
}
case ILCode.Calli: return InlineAssembly(byteCode, args);
case ILCode.Ckfinite: return InlineAssembly(byteCode, args);
case ILCode.Constrained: return InlineAssembly(byteCode, args);
case ILCode.Cpblk: return InlineAssembly(byteCode, args);
case ILCode.Cpobj: return InlineAssembly(byteCode, args);
case ILCode.Dup: return arg1;
case ILCode.Endfilter: return InlineAssembly(byteCode, args);
case ILCode.Endfinally: return null;
case ILCode.Initblk: return InlineAssembly(byteCode, args);
#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.Ldftn: {
Cecil.MethodReference cecilMethod = ((MethodReference)operand);
var expr = new Ast.IdentifierExpression(cecilMethod.Name);
expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod));
expr.AddAnnotation(cecilMethod);
return new IdentifierExpression("ldftn").Invoke(expr)
.WithAnnotation(new Transforms.DelegateConstruction.Annotation(false));
}
case ILCode.Ldvirtftn: {
Cecil.MethodReference cecilMethod = ((MethodReference)operand);
var expr = new Ast.IdentifierExpression(cecilMethod.Name);
expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod));
expr.AddAnnotation(cecilMethod);
return new IdentifierExpression("ldvirtftn").Invoke(expr)
.WithAnnotation(new Transforms.DelegateConstruction.Annotation(true));
}
case ILCode.Calli: return InlineAssembly(byteCode, args);
case ILCode.Ckfinite: return InlineAssembly(byteCode, args);
case ILCode.Constrained: return InlineAssembly(byteCode, args);
case ILCode.Cpblk: return InlineAssembly(byteCode, args);
case ILCode.Cpobj: return InlineAssembly(byteCode, args);
case ILCode.Dup: return arg1;
case ILCode.Endfilter: return InlineAssembly(byteCode, args);
case ILCode.Endfinally: return null;
case ILCode.Initblk: return InlineAssembly(byteCode, args);
case ILCode.Initobj:
if (args[0] is DirectionExpression)
return new AssignmentExpression(((DirectionExpression)args[0]).Expression.Detach(), new DefaultValueExpression { Type = operandAsTypeRef });
else
return InlineAssembly(byteCode, args);
case ILCode.Jmp:
return InlineAssembly(byteCode, args);
case ILCode.Ldarg:
case ILCode.Jmp: return InlineAssembly(byteCode, args);
case ILCode.Ldarg: {
if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) {
if (context.CurrentMethod.DeclaringType.IsValueType)
return MakeRef(new Ast.ThisReferenceExpression());
@ -460,14 +405,14 @@ namespace ICSharpCode.Decompiler.Ast @@ -460,14 +405,14 @@ namespace ICSharpCode.Decompiler.Ast
else
return expr;
}
}
case ILCode.Ldarga:
if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) {
return MakeRef(new Ast.ThisReferenceExpression());
} else {
return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand));
}
case ILCode.Ldc_I4:
return AstBuilder.MakePrimitive((int)operand, byteCode.InferredType);
case ILCode.Ldc_I4: return AstBuilder.MakePrimitive((int)operand, byteCode.InferredType);
case ILCode.Ldc_I8:
case ILCode.Ldc_R4:
case ILCode.Ldc_R8:
@ -489,8 +434,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -489,8 +434,7 @@ 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: return MakeRef(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand));
case ILCode.Ldsflda:
return MakeRef(
AstBuilder.ConvertType(((FieldReference)operand).DeclaringType)
@ -501,68 +445,61 @@ namespace ICSharpCode.Decompiler.Ast @@ -501,68 +445,61 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Ldloca:
localVariablesToDefine.Add((ILVariable)operand);
return MakeRef(new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand));
case ILCode.Ldnull:
return new Ast.NullReferenceExpression();
case ILCode.Ldstr:
return new Ast.PrimitiveExpression(operand);
case ILCode.Ldnull: return new Ast.NullReferenceExpression();
case ILCode.Ldstr: return new Ast.PrimitiveExpression(operand);
case ILCode.Ldtoken:
if (operand is Cecil.TypeReference) {
return new Ast.TypeOfExpression { Type = operandAsTypeRef }.Member("TypeHandle");
} else {
return InlineAssembly(byteCode, args);
}
case ILCode.Leave: return new GotoStatement() { Label = ((ILLabel)operand).Name };
case ILCode.Localloc: return InlineAssembly(byteCode, args);
case ILCode.Mkrefany: return InlineAssembly(byteCode, args);
case ILCode.Newobj:
{
Cecil.TypeReference declaringType = ((MethodReference)operand).DeclaringType;
if (declaringType is ArrayType) {
ComposedType ct = AstBuilder.ConvertType((ArrayType)declaringType) as ComposedType;
if (ct != null && ct.ArraySpecifiers.Count >= 1) {
var ace = new Ast.ArrayCreateExpression();
ct.ArraySpecifiers.First().Remove();
ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers);
ace.Type = ct;
ace.Arguments.AddRange(args);
return ace;
}
}
var oce = new Ast.ObjectCreateExpression();
oce.Type = AstBuilder.ConvertType(declaringType);
oce.Arguments.AddRange(args);
return oce.WithAnnotation(operand);
}
case ILCode.No: return InlineAssembly(byteCode, args);
case ILCode.Nop: return null;
case ILCode.Pop: return arg1;
case ILCode.Readonly: return InlineAssembly(byteCode, args);
case ILCode.Refanytype: return InlineAssembly(byteCode, args);
case ILCode.Refanyval: return InlineAssembly(byteCode, args);
case ILCode.Ret: {
if (methodDef.ReturnType.FullName != "System.Void") {
return new Ast.ReturnStatement { Expression = arg1 };
} else {
return new Ast.ReturnStatement();
case ILCode.Leave: return new GotoStatement() { Label = ((ILLabel)operand).Name };
case ILCode.Localloc: return InlineAssembly(byteCode, args);
case ILCode.Mkrefany: return InlineAssembly(byteCode, args);
case ILCode.Newobj: {
Cecil.TypeReference declaringType = ((MethodReference)operand).DeclaringType;
if (declaringType is ArrayType) {
ComposedType ct = AstBuilder.ConvertType((ArrayType)declaringType) as ComposedType;
if (ct != null && ct.ArraySpecifiers.Count >= 1) {
var ace = new Ast.ArrayCreateExpression();
ct.ArraySpecifiers.First().Remove();
ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers);
ace.Type = ct;
ace.Arguments.AddRange(args);
return ace;
}
}
case ILCode.Rethrow: return new Ast.ThrowStatement();
case ILCode.Sizeof:
return new Ast.SizeOfExpression { Type = operandAsTypeRef };
case ILCode.Starg:
return new Ast.AssignmentExpression(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand), arg1);
case ILCode.Stloc: {
ILVariable locVar = (ILVariable)operand;
localVariablesToDefine.Add(locVar);
return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name).WithAnnotation(locVar), arg1);
var oce = new Ast.ObjectCreateExpression();
oce.Type = AstBuilder.ConvertType(declaringType);
oce.Arguments.AddRange(args);
return oce.WithAnnotation(operand);
}
case ILCode.No: return InlineAssembly(byteCode, args);
case ILCode.Nop: return null;
case ILCode.Pop: return arg1;
case ILCode.Readonly: return InlineAssembly(byteCode, args);
case ILCode.Refanytype: return InlineAssembly(byteCode, args);
case ILCode.Refanyval: return InlineAssembly(byteCode, args);
case ILCode.Ret:
if (methodDef.ReturnType.FullName != "System.Void") {
return new Ast.ReturnStatement { Expression = arg1 };
} else {
return new Ast.ReturnStatement();
}
case ILCode.Switch: return InlineAssembly(byteCode, args);
case ILCode.Tail: return InlineAssembly(byteCode, args);
case ILCode.Throw: return new Ast.ThrowStatement { Expression = arg1 };
case ILCode.Unaligned: return InlineAssembly(byteCode, args);
case ILCode.Volatile: return InlineAssembly(byteCode, args);
default: throw new Exception("Unknown OpCode: " + byteCode.Code);
case ILCode.Rethrow: return new Ast.ThrowStatement();
case ILCode.Sizeof: return new Ast.SizeOfExpression { Type = operandAsTypeRef };
case ILCode.Starg: return new Ast.AssignmentExpression(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand), arg1);
case ILCode.Stloc: {
ILVariable locVar = (ILVariable)operand;
localVariablesToDefine.Add(locVar);
return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name).WithAnnotation(locVar), arg1);
}
case ILCode.Switch: return InlineAssembly(byteCode, args);
case ILCode.Tail: return InlineAssembly(byteCode, args);
case ILCode.Throw: return new Ast.ThrowStatement { Expression = arg1 };
case ILCode.Unaligned: return InlineAssembly(byteCode, args);
case ILCode.Volatile: return InlineAssembly(byteCode, args);
default: throw new Exception("Unknown OpCode: " + byteCode.Code);
}
}
@ -714,6 +651,31 @@ namespace ICSharpCode.Decompiler.Ast @@ -714,6 +651,31 @@ namespace ICSharpCode.Decompiler.Ast
return new IdentifierExpression(byteCode.Code.GetName()).Invoke(args);
}
static string FormatByteCodeOperand(object operand)
{
if (operand == null) {
return string.Empty;
//} else if (operand is ILExpression) {
// return string.Format("IL_{0:X2}", ((ILExpression)operand).Offset);
} else if (operand is MethodReference) {
return ((MethodReference)operand).Name + "()";
} else if (operand is Cecil.TypeReference) {
return ((Cecil.TypeReference)operand).FullName;
} else if (operand is VariableDefinition) {
return ((VariableDefinition)operand).Name;
} else if (operand is ParameterDefinition) {
return ((ParameterDefinition)operand).Name;
} else if (operand is FieldReference) {
return ((FieldReference)operand).Name;
} else if (operand is string) {
return "\"" + operand + "\"";
} else if (operand is int) {
return operand.ToString();
} else {
return operand.ToString();
}
}
static IEnumerable<AstType> ConvertTypeArguments(MethodReference cecilMethod)
{
GenericInstanceMethod g = cecilMethod as GenericInstanceMethod;

129
ICSharpCode.Decompiler/ILAst/GotoRemoval.cs

@ -29,9 +29,13 @@ namespace ICSharpCode.Decompiler.ILAst @@ -29,9 +29,13 @@ namespace ICSharpCode.Decompiler.ILAst
}
// Simplify gotos
foreach (ILExpression gotoExpr in method.GetSelfAndChildrenRecursive<ILExpression>().Where(e => e.Code == ILCode.Br || e.Code == ILCode.Leave)) {
TrySimplifyGoto(gotoExpr);
}
bool modified;
do {
modified = false;
foreach (ILExpression gotoExpr in method.GetSelfAndChildrenRecursive<ILExpression>().Where(e => e.Code == ILCode.Br || e.Code == ILCode.Leave)) {
modified |= TrySimplifyGoto(gotoExpr);
}
} while(modified);
RemoveRedundantCode(method);
}
@ -52,44 +56,79 @@ namespace ICSharpCode.Decompiler.ILAst @@ -52,44 +56,79 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
// Remove redundant break at the end of case
// Remove redundant case blocks altogether
foreach(ILSwitch ilSwitch in method.GetSelfAndChildrenRecursive<ILSwitch>()) {
foreach(ILBlock ilCase in ilSwitch.CaseBlocks) {
Debug.Assert(ilCase.EntryGoto == null);
int count = ilCase.Body.Count;
if (count >= 2) {
if (!ilCase.Body[count - 2].CanFallThough() &&
ilCase.Body[count - 1].Match(ILCode.LoopOrSwitchBreak)) {
ilCase.Body.RemoveAt(count - 1);
}
}
}
var defaultCase = ilSwitch.CaseBlocks.Where(cb => cb.Values == null).SingleOrDefault();
// If there is no default block, remove empty case blocks
if (defaultCase == null || (defaultCase.Body.Count == 1 && defaultCase.Body.Single().Match(ILCode.LoopOrSwitchBreak))) {
ilSwitch.CaseBlocks.RemoveAll(b => b.Body.Count == 1 && b.Body.Single().Match(ILCode.LoopOrSwitchBreak));
}
}
// Remove redundant return
if (method.Body.Count > 0 && method.Body.Last().Match(ILCode.Ret) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) {
method.Body.RemoveAt(method.Body.Count - 1);
}
}
IEnumerable<ILNode> GetParents(ILNode node)
{
ILNode current = node;
while(true) {
current = parent[current];
if (current == null)
yield break;
yield return current;
}
}
bool TrySimplifyGoto(ILExpression gotoExpr)
{
Debug.Assert(gotoExpr.Code == ILCode.Br || gotoExpr.Code == ILCode.Leave);
Debug.Assert(gotoExpr.Prefixes == null);
Debug.Assert(gotoExpr.Operand != null);
ILExpression target = Enter(gotoExpr, new HashSet<ILNode>());
ILNode target = Enter(gotoExpr, new HashSet<ILNode>());
if (target == null)
return false;
// The gotoExper is marked as visited because we do not want to
// walk over node which we plan to modify
// The simulated path always has to start in the same try-block
// in other for the same finally blocks to be executed.
if (target == Exit(gotoExpr, new HashSet<ILNode>() { gotoExpr })) {
gotoExpr.Code = ILCode.Nop;
gotoExpr.Operand = null;
target.ILRanges.AddRange(gotoExpr.ILRanges);
if (target is ILExpression)
((ILExpression)target).ILRanges.AddRange(gotoExpr.ILRanges);
gotoExpr.ILRanges.Clear();
return true;
}
ILWhileLoop loop = null;
ILNode current = gotoExpr;
while(loop == null && current != null) {
current = parent[current];
loop = current as ILWhileLoop;
}
if (loop != null && target == Exit(loop, new HashSet<ILNode>() { gotoExpr })) {
gotoExpr.Code = ILCode.LoopBreak;
ILNode breakBlock = GetParents(gotoExpr).Where(n => n is ILWhileLoop || n is ILSwitch).FirstOrDefault();
if (breakBlock != null && target == Exit(breakBlock, new HashSet<ILNode>() { gotoExpr })) {
gotoExpr.Code = ILCode.LoopOrSwitchBreak;
gotoExpr.Operand = null;
return true;
}
if (loop != null && target == Enter(loop, new HashSet<ILNode>() { gotoExpr })) {
ILNode continueBlock = GetParents(gotoExpr).Where(n => n is ILWhileLoop).FirstOrDefault();
if (continueBlock != null && target == Enter(continueBlock, new HashSet<ILNode>() { gotoExpr })) {
gotoExpr.Code = ILCode.LoopContinue;
gotoExpr.Operand = null;
return true;
@ -99,9 +138,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -99,9 +138,10 @@ namespace ICSharpCode.Decompiler.ILAst
}
/// <summary>
/// Get the first expression to be excecuted if the instruction pointer is at the start of the given node
/// Get the first expression to be excecuted if the instruction pointer is at the start of the given node.
/// Try blocks may not be entered in any way. If possible, the try block is returned as the node to be executed.
/// </summary>
ILExpression Enter(ILNode node, HashSet<ILNode> visitedNodes)
ILNode Enter(ILNode node, HashSet<ILNode> visitedNodes)
{
if (node == null)
throw new ArgumentNullException();
@ -117,9 +157,43 @@ namespace ICSharpCode.Decompiler.ILAst @@ -117,9 +157,43 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression expr = node as ILExpression;
if (expr != null) {
if (expr.Code == ILCode.Br || expr.Code == ILCode.Leave) {
return Enter((ILLabel)expr.Operand, visitedNodes);
ILLabel target = (ILLabel)expr.Operand;
// Early exit - same try-block
if (GetParents(expr).OfType<ILTryCatchBlock>().FirstOrDefault() == GetParents(target).OfType<ILTryCatchBlock>().FirstOrDefault())
return Enter(target, visitedNodes);
// Make sure we are not entering any try-block
var srcTryBlocks = GetParents(expr).OfType<ILTryCatchBlock>().Reverse().ToList();
var dstTryBlocks = GetParents(target).OfType<ILTryCatchBlock>().Reverse().ToList();
// Skip blocks that we are already in
int i = 0;
while(i < srcTryBlocks.Count && i < dstTryBlocks.Count && srcTryBlocks[i] == dstTryBlocks[i]) i++;
if (i == dstTryBlocks.Count) {
return Enter(target, visitedNodes);
} else {
ILTryCatchBlock dstTryBlock = dstTryBlocks[i];
// Check that the goto points to the start
ILTryCatchBlock current = dstTryBlock;
while(current != null) {
foreach(ILNode n in current.TryBlock.Body) {
if (n is ILLabel) {
if (n == target)
return dstTryBlock;
} else if (!n.Match(ILCode.Nop)) {
current = n as ILTryCatchBlock;
break;
}
}
}
return null;
}
} else if (expr.Code == ILCode.Nop) {
return Exit(expr, visitedNodes);
} else if (expr.Code == ILCode.LoopOrSwitchBreak) {
ILNode breakBlock = GetParents(expr).Where(n => n is ILWhileLoop || n is ILSwitch).First();
return Exit(breakBlock, new HashSet<ILNode>() { expr });
} else if (expr.Code == ILCode.LoopContinue) {
ILNode continueBlock = GetParents(expr).Where(n => n is ILWhileLoop).First();
return Enter(continueBlock, new HashSet<ILNode>() { expr });
} else {
return expr;
}
@ -152,7 +226,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -152,7 +226,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILTryCatchBlock tryCatch = node as ILTryCatchBlock;
if (tryCatch != null) {
return Enter(tryCatch.TryBlock, visitedNodes);
return tryCatch;
}
ILSwitch ilSwitch = node as ILSwitch;
@ -166,7 +240,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -166,7 +240,7 @@ namespace ICSharpCode.Decompiler.ILAst
/// <summary>
/// Get the first expression to be excecuted if the instruction pointer is at the end of the given node
/// </summary>
ILExpression Exit(ILNode node, HashSet<ILNode> visitedNodes)
ILNode Exit(ILNode node, HashSet<ILNode> visitedNodes)
{
if (node == null)
throw new ArgumentNullException();
@ -184,13 +258,20 @@ namespace ICSharpCode.Decompiler.ILAst @@ -184,13 +258,20 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
if (nodeParent is ILCondition ||
nodeParent is ILTryCatchBlock ||
nodeParent is ILSwitch)
{
if (nodeParent is ILCondition) {
return Exit(nodeParent, visitedNodes);
}
if (nodeParent is ILTryCatchBlock) {
// Finally blocks are completely ignored.
// We rely on the fact that try blocks can not be entered.
return Exit(nodeParent, visitedNodes);
}
if (nodeParent is ILSwitch) {
return null; // Implicit exit from switch is not allowed
}
if (nodeParent is ILWhileLoop) {
return Enter(nodeParent, visitedNodes);
}

225
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -101,6 +101,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -101,6 +101,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression expr = block.Body[i] as ILExpression;
if (expr != null && expr.Prefixes == null) {
switch(expr.Code) {
case ILCode.Switch:
case ILCode.Brtrue:
expr.Arguments.Single().ILRanges.AddRange(expr.ILRanges);
expr.ILRanges.Clear();
@ -235,40 +236,26 @@ namespace ICSharpCode.Decompiler.ILAst @@ -235,40 +236,26 @@ namespace ICSharpCode.Decompiler.ILAst
} while(modified);
}
bool IsStloc(ILBasicBlock bb, ref ILVariable locVar, ref ILExpression val, ref ILLabel fallLabel)
{
if (bb.Body.Count == 1) {
ILExpression expr;
if (bb.Body[0].Match(ILCode.Stloc, out expr)) {
locVar = (ILVariable)expr.Operand;
val = expr.Arguments[0];
fallLabel = (ILLabel)bb.FallthoughGoto.Operand;
return true;
}
}
return false;
}
// scope is modified if successful
bool TrySimplifyTernaryOperator(List<ILNode> scope, ILBasicBlock head)
{
Debug.Assert(scope.Contains(head));
ILExpression condExpr = null;
ILLabel trueLabel = null;
ILLabel falseLabel = null;
ILVariable trueLocVar = null;
ILExpression trueExpr = null;
ILLabel trueFall = null;
ILVariable falseLocVar = null;
ILExpression falseExpr = null;
ILLabel falseFall = null;
if(head.MatchBrTure(out condExpr, out trueLabel, out falseLabel) &&
ILExpression condExpr;
ILLabel trueLabel;
ILLabel falseLabel;
ILVariable trueLocVar;
ILExpression trueExpr;
ILLabel trueFall;
ILVariable falseLocVar;
ILExpression falseExpr;
ILLabel falseFall;
if(head.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) &&
labelGlobalRefCount[trueLabel] == 1 &&
labelGlobalRefCount[falseLabel] == 1 &&
IsStloc(labelToBasicBlock[trueLabel], ref trueLocVar, ref trueExpr, ref trueFall) &&
IsStloc(labelToBasicBlock[falseLabel], ref falseLocVar, ref falseExpr, ref falseFall) &&
labelToBasicBlock[trueLabel].Match(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) &&
labelToBasicBlock[falseLabel].Match(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall) &&
trueLocVar == falseLocVar &&
trueFall == falseFall)
{
@ -297,7 +284,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -297,7 +284,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression condExpr;
ILLabel trueLabel;
ILLabel falseLabel;
if(head.MatchBrTure(out condExpr, out trueLabel, out falseLabel)) {
if(head.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) {
for (int pass = 0; pass < 2; pass++) {
// On the second pass, swap labels and negate expression of the first branch
@ -313,7 +300,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -313,7 +300,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (scope.Contains(nextBasicBlock) &&
nextBasicBlock != head &&
labelGlobalRefCount[nextBasicBlock.EntryLabel] == 1 &&
nextBasicBlock.MatchBrTure(out nextCondExpr, out nextTrueLablel, out nextFalseLabel) &&
nextBasicBlock.Match(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) &&
(otherLablel == nextFalseLabel || otherLablel == nextTrueLablel))
{
// Create short cicuit branch
@ -354,9 +341,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -354,9 +341,7 @@ namespace ICSharpCode.Decompiler.ILAst
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count; i++) {
ILLabel targetLabel;
if (block.Body[i].Match(ILCode.Br, out targetLabel) ||
block.Body[i].Match(ILCode.Leave, out targetLabel))
{
if (block.Body[i].Match(ILCode.Br, out targetLabel) || block.Body[i].Match(ILCode.Leave, out targetLabel)) {
// Skip extra labels
while(nextSibling.ContainsKey(targetLabel) && nextSibling[targetLabel] is ILLabel) {
targetLabel = (ILLabel)nextSibling[targetLabel];
@ -364,18 +349,23 @@ namespace ICSharpCode.Decompiler.ILAst @@ -364,18 +349,23 @@ namespace ICSharpCode.Decompiler.ILAst
// Inline return statement
ILNode target;
ILExpression retExpr;
if (nextSibling.TryGetValue(targetLabel, out target) &&
target.Match(ILCode.Ret, out retExpr))
{
ILVariable locVar;
object constValue;
if (retExpr.Arguments.Count == 0) {
List<ILExpression> retArgs;
if (nextSibling.TryGetValue(targetLabel, out target)) {
if (target.Match(ILCode.Ret, out retArgs)) {
ILVariable locVar;
object constValue;
if (retArgs.Count == 0) {
block.Body[i] = new ILExpression(ILCode.Ret, null);
} else if (retArgs.Single().Match(ILCode.Ldloc, out locVar)) {
block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldloc, locVar));
} else if (retArgs.Single().Match(ILCode.Ldc_I4, out constValue)) {
block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldc_I4, constValue));
}
}
} else {
if (method.Body.Count > 0 && method.Body.Last() == targetLabel) {
// It exits the main method - so it is same as return;
block.Body[i] = new ILExpression(ILCode.Ret, null);
} else if (retExpr.Arguments.Single().Match(ILCode.Ldloc, out locVar)) {
block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldloc, locVar));
} else if (retExpr.Arguments.Single().Match(ILCode.Ldc_I4, out constValue)) {
block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldc_I4, constValue));
}
}
}
@ -466,7 +456,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -466,7 +456,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression condExpr;
ILLabel trueLabel;
ILLabel falseLabel;
if(basicBlock.MatchBrTure(out condExpr, out trueLabel, out falseLabel))
if(basicBlock.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel))
{
ControlFlowNode trueTarget;
labelToCfNode.TryGetValue(trueLabel, out trueTarget);
@ -579,28 +569,32 @@ namespace ICSharpCode.Decompiler.ILAst @@ -579,28 +569,32 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression condBranch = block.Body[0] as ILExpression;
// Switch
if (condBranch != null && condBranch.Operand is ILLabel[] && condBranch.Arguments.Count > 0) {
ILLabel[] caseLabels = (ILLabel[])condBranch.Operand;
// The labels will not be used - kill them
condBranch.Operand = null;
ILLabel[] caseLabels;
List<ILExpression> switchArgs;
if (condBranch.Match(ILCode.Switch, out caseLabels, out switchArgs)) {
ILSwitch ilSwitch = new ILSwitch() {
Condition = condBranch,
DefaultGoto = block.FallthoughGoto
ILSwitch ilSwitch = new ILSwitch() { Condition = switchArgs.Single() };
ILBasicBlock newBB = new ILBasicBlock() {
EntryLabel = block.EntryLabel, // Keep the entry label
Body = { ilSwitch },
FallthoughGoto = block.FallthoughGoto
};
result.Add(new ILBasicBlock() {
EntryLabel = block.EntryLabel, // Keep the entry label
Body = { ilSwitch }
});
result.Add(newBB);
// Remove the item so that it is not picked up as content
scope.RemoveOrThrow(node);
// Find the switch offset
int addValue = 0;
List<ILExpression> subArgs;
if (ilSwitch.Condition.Match(ILCode.Sub, out subArgs) && subArgs[1].Match(ILCode.Ldc_I4, out addValue)) {
ilSwitch.Condition = subArgs[0];
}
// Pull in code of cases
ILLabel fallLabel = (ILLabel)block.FallthoughGoto.Operand;
ControlFlowNode fallTarget = null;
labelToCfNode.TryGetValue((ILLabel)block.FallthoughGoto.Operand, out fallTarget);
labelToCfNode.TryGetValue(fallLabel, out fallTarget);
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
if (fallTarget != null)
@ -613,19 +607,44 @@ namespace ICSharpCode.Decompiler.ILAst @@ -613,19 +607,44 @@ namespace ICSharpCode.Decompiler.ILAst
frontiers.UnionWith(condTarget.DominanceFrontier);
}
foreach(ILLabel condLabel in caseLabels) {
ControlFlowNode condTarget = null;
labelToCfNode.TryGetValue(condLabel, out condTarget);
for (int i = 0; i < caseLabels.Length; i++) {
ILLabel condLabel = caseLabels[i];
// Find or create new case block
ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.Where(b => b.EntryGoto.Operand == condLabel).FirstOrDefault();
if (caseBlock == null) {
caseBlock = new ILSwitch.CaseBlock() {
Values = new List<int>(),
EntryGoto = new ILExpression(ILCode.Br, condLabel)
};
ilSwitch.CaseBlocks.Add(caseBlock);
ControlFlowNode condTarget = null;
labelToCfNode.TryGetValue(condLabel, out condTarget);
if (condTarget != null && !frontiers.Contains(condTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, condTarget);
scope.ExceptWith(content);
caseBlock.Body.AddRange(FindConditions(content, condTarget));
// Add explicit break which should not be used by default, but the goto removal might decide to use it
caseBlock.Body.Add(new ILBasicBlock() { Body = { new ILExpression(ILCode.LoopOrSwitchBreak, null) } });
}
}
caseBlock.Values.Add(i + addValue);
}
// Heuristis to determine if we want to use fallthough as default case
if (fallTarget != null && !frontiers.Contains(fallTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, fallTarget);
if (content.Any()) {
var caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, fallLabel) };
ilSwitch.CaseBlocks.Add(caseBlock);
newBB.FallthoughGoto = null;
ILBlock caseBlock = new ILBlock() {
EntryGoto = new ILExpression(ILCode.Br, condLabel)
};
if (condTarget != null && !frontiers.Contains(condTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, condTarget);
scope.ExceptWith(content);
caseBlock.Body.AddRange(FindConditions(content, condTarget));
caseBlock.Body.AddRange(FindConditions(content, fallTarget));
// Add explicit break which should not be used by default, but the goto removal might decide to use it
caseBlock.Body.Add(new ILBasicBlock() { Body = { new ILExpression(ILCode.LoopOrSwitchBreak, null) } });
}
ilSwitch.CaseBlocks.Add(caseBlock);
}
}
@ -633,7 +652,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -633,7 +652,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression condExpr;
ILLabel trueLabel;
ILLabel falseLabel;
if(block.MatchBrTure(out condExpr, out trueLabel, out falseLabel)) {
if(block.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) {
// Swap bodies since that seems to be the usual C# order
ILLabel temp = trueLabel;
@ -782,8 +801,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -782,8 +801,8 @@ namespace ICSharpCode.Decompiler.ILAst
for (int i = 0; i < block.Body.Count; i++) {
ILCondition cond = block.Body[i] as ILCondition;
if (cond != null) {
bool trueExits = cond.TrueBlock.Body.Count > 0 && !cond.TrueBlock.Body.Last().CanFallthough();
bool falseExits = cond.FalseBlock.Body.Count > 0 && !cond.FalseBlock.Body.Last().CanFallthough();
bool trueExits = cond.TrueBlock.Body.Count > 0 && !cond.TrueBlock.Body.Last().CanFallThough();
bool falseExits = cond.FalseBlock.Body.Count > 0 && !cond.FalseBlock.Body.Last().CanFallThough();
if (trueExits) {
// Move the false block after the condition
@ -823,51 +842,73 @@ namespace ICSharpCode.Decompiler.ILAst @@ -823,51 +842,73 @@ namespace ICSharpCode.Decompiler.ILAst
return expr != null && expr.Prefixes == null && expr.Code == code;
}
public static bool Match(this ILNode node, ILCode code, out ILExpression expr)
public static bool Match<T>(this ILNode node, ILCode code, out T operand)
{
expr = node as ILExpression;
return expr != null && expr.Prefixes == null && expr.Code == code;
ILExpression expr = node as ILExpression;
if (expr != null && expr.Prefixes == null && expr.Code == code) {
operand = (T)expr.Operand;
Debug.Assert(expr.Arguments.Count == 0);
return true;
}
operand = default(T);
return false;
}
public static bool Match<T>(this ILNode node, ILCode code, out T operand)
public static bool Match(this ILNode node, ILCode code, out List<ILExpression> args)
{
ILExpression expr = node as ILExpression;
if (expr != null && expr.Prefixes == null && expr.Code == code) {
Debug.Assert(expr.Operand == null);
args = expr.Arguments;
return true;
}
args = null;
return false;
}
public static bool Match<T>(this ILNode node, ILCode code, out T operand, out List<ILExpression> args)
{
ILExpression expr = node as ILExpression;
if (expr != null && expr.Prefixes == null && expr.Code == code) {
operand = (T)expr.Operand;
args = expr.Arguments;
return true;
}
operand = default(T);
args = null;
return false;
}
public static bool Match<T>(this ILNode node, ILCode code, out T operand, out ILExpression arg)
{
List<ILExpression> args;
if (node.Match(code, out operand, out args)) {
arg = args.Single();
return true;
}
arg = null;
return false;
}
public static bool MatchBrTure(this ILBasicBlock bb, out ILExpression condition, out ILLabel trueLabel, out ILLabel falseLabel)
public static bool Match<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel fallLabel)
{
if (bb.Body.Count == 1) {
if (bb.Body[0].Match(ILCode.Brtrue, out trueLabel)) {
condition = ((ILExpression)bb.Body[0]).Arguments.Single();
falseLabel = (ILLabel)((ILExpression)bb.FallthoughGoto).Operand;
if (bb.Body[0].Match(code, out operand, out arg)) {
fallLabel = (ILLabel)bb.FallthoughGoto.Operand;
return true;
}
}
condition = null;
trueLabel = null;
falseLabel = null;
operand = default(T);
arg = null;
fallLabel = null;
return false;
}
public static bool CanFallthough(this ILNode node)
public static bool CanFallThough(this ILNode node)
{
ILExpression expr = node as ILExpression;
if (expr != null) {
switch(expr.Code) {
case ILCode.Br:
case ILCode.Ret:
case ILCode.Throw:
case ILCode.Rethrow:
case ILCode.LoopContinue:
case ILCode.LoopBreak:
return false;
}
return expr.Code.CanFallThough();
}
return true;
}

29
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
@ -441,9 +442,24 @@ namespace ICSharpCode.Decompiler.ILAst @@ -441,9 +442,24 @@ namespace ICSharpCode.Decompiler.ILAst
public class ILSwitch: ILNode
{
public class CaseBlock: ILBlock
{
public List<int> Values; // null for the default case
public override void WriteTo(ITextOutput output)
{
Debug.Assert(Values.Count > 0);
foreach (int i in this.Values) {
output.WriteLine("case {0}:", i);
}
output.Indent();
base.WriteTo(output);
output.Unindent();
}
}
public ILExpression Condition;
public List<ILBlock> CaseBlocks = new List<ILBlock>();
public ILExpression DefaultGoto;
public List<CaseBlock> CaseBlocks = new List<CaseBlock>();
public override IEnumerable<ILNode> GetChildren()
{
@ -452,8 +468,6 @@ namespace ICSharpCode.Decompiler.ILAst @@ -452,8 +468,6 @@ namespace ICSharpCode.Decompiler.ILAst
foreach (ILBlock caseBlock in this.CaseBlocks) {
yield return caseBlock;
}
if (this.DefaultGoto != null)
yield return this.DefaultGoto;
}
public override void WriteTo(ITextOutput output)
@ -462,11 +476,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -462,11 +476,8 @@ namespace ICSharpCode.Decompiler.ILAst
Condition.WriteTo(output);
output.WriteLine(") {");
output.Indent();
for (int i = 0; i < CaseBlocks.Count; i++) {
output.WriteLine("case {0}:", i);
output.Indent();
CaseBlocks[i].WriteTo(output);
output.Unindent();
foreach (CaseBlock caseBlock in this.CaseBlocks) {
caseBlock.WriteTo(output);
}
output.Unindent();
output.WriteLine("}");

4
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -260,7 +260,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -260,7 +260,7 @@ namespace ICSharpCode.Decompiler.ILAst
LogicOr,
InitArray, // Array Initializer
TernaryOp, // ?:
LoopBreak,
LoopOrSwitchBreak,
LoopContinue,
Ldc_Decimal,
@ -286,6 +286,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -286,6 +286,8 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Endfinally:
case ILCode.Throw:
case ILCode.Rethrow:
case ILCode.LoopContinue:
case ILCode.LoopOrSwitchBreak:
return false;
default:
return true;

2
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -456,6 +456,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -456,6 +456,8 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Switch:
case ILCode.Throw:
case ILCode.Rethrow:
case ILCode.LoopOrSwitchBreak:
case ILCode.LoopContinue:
return null;
case ILCode.Ret:
if (forceInferChildren && expr.Arguments.Count == 1)

18
NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs

@ -1529,8 +1529,13 @@ namespace ICSharpCode.NRefactory.CSharp @@ -1529,8 +1529,13 @@ namespace ICSharpCode.NRefactory.CSharp
public object VisitSwitchSection(SwitchSection switchSection, object data)
{
StartNode(switchSection);
foreach (var label in switchSection.CaseLabels)
bool first = true;
foreach (var label in switchSection.CaseLabels) {
if (!first)
NewLine();
label.AcceptVisitor(this, data);
first = false;
}
foreach (var statement in switchSection.Statements)
statement.AcceptVisitor(this, data);
return EndNode(switchSection);
@ -1539,11 +1544,14 @@ namespace ICSharpCode.NRefactory.CSharp @@ -1539,11 +1544,14 @@ namespace ICSharpCode.NRefactory.CSharp
public object VisitCaseLabel(CaseLabel caseLabel, object data)
{
StartNode(caseLabel);
WriteKeyword("case");
Space();
caseLabel.Expression.AcceptVisitor(this, data);
if (caseLabel.Expression.IsNull) {
WriteKeyword("default");
} else {
WriteKeyword("case");
Space();
caseLabel.Expression.AcceptVisitor(this, data);
}
WriteToken(":", CaseLabel.Roles.Colon);
NewLine();
return EndNode(caseLabel);
}

Loading…
Cancel
Save