Browse Source

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

Conflicts:
	ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
	NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj
pull/52/head
Artur Zgodziski 15 years ago
parent
commit
983be3401c
  1. 24
      ICSharpCode.Decompiler/Ast/AstBuilder.cs
  2. 98
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  3. 2
      ICSharpCode.Decompiler/Disassembler/ILStructure.cs
  4. 3
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  5. 113
      ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs
  6. 5
      ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
  7. 324
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  8. 21
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  9. 163
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  10. 94
      ICSharpCode.Decompiler/ILAst/ILInlining.cs
  11. 103
      ICSharpCode.Decompiler/ILAst/Pattern.cs
  12. 26
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  13. 36
      ICSharpCode.Decompiler/Tests/ArrayInitializers.cs
  14. 59
      ICSharpCode.Decompiler/Tests/BooleanConsumedAsInteger.il
  15. 25
      ICSharpCode.Decompiler/Tests/ExceptionHandling.cs
  16. 12
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  17. 32
      ICSharpCode.Decompiler/Tests/MultidimensionalArray.cs
  18. 3
      ILSpy/AboutPage.cs
  19. 109
      ILSpy/CSharpLanguage.cs
  20. 4
      ILSpy/ILAstLanguage.cs
  21. 6
      ILSpy/Properties/AssemblyInfo.template.cs
  22. 38
      ILSpy/TextView/DecompilerTextView.cs
  23. 2
      ILSpy/TreeNodes/AssemblyTreeNode.cs
  24. 46
      ILSpy/TreeNodes/ResourceEntryNode.cs
  25. 5
      ILSpy/TreeNodes/ResourceListTreeNode.cs
  26. 74
      NRefactory/ICSharpCode.NRefactory.Tests/CSharp/InsertParenthesesVisitorTests.cs
  27. 17
      NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/InsertParenthesesVisitor.cs
  28. 16
      NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs
  29. 4
      NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

24
ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -134,7 +134,7 @@ namespace Decompiler @@ -134,7 +134,7 @@ namespace Decompiler
TypeDeclaration astType = new TypeDeclaration();
astType.AddAnnotation(typeDef);
astType.Modifiers = ConvertModifiers(typeDef);
astType.Name = typeDef.Name;
astType.Name = CleanName(typeDef.Name);
if (typeDef.IsEnum) { // NB: Enum is value type
astType.ClassType = ClassType.Enum;
@ -167,7 +167,7 @@ namespace Decompiler @@ -167,7 +167,7 @@ namespace Decompiler
astType.AddChild(ConvertType(field.FieldType), TypeDeclaration.BaseTypeRole);
} else {
EnumMemberDeclaration enumMember = new EnumMemberDeclaration();
enumMember.Name = field.Name;
enumMember.Name = CleanName(field.Name);
astType.AddChild(enumMember, TypeDeclaration.MemberRole);
}
}
@ -187,6 +187,14 @@ namespace Decompiler @@ -187,6 +187,14 @@ namespace Decompiler
return astType;
}
string CleanName(string name)
{
int pos = name.LastIndexOf('`');
if (pos >= 0)
name = name.Substring(0, pos);
return name;
}
#region Convert Type Reference
/// <summary>
/// Converts a type reference.
@ -456,7 +464,7 @@ namespace Decompiler @@ -456,7 +464,7 @@ namespace Decompiler
MethodDeclaration astMethod = new MethodDeclaration();
astMethod.AddAnnotation(methodDef);
astMethod.ReturnType = ConvertType(methodDef.ReturnType, methodDef.MethodReturnType);
astMethod.Name = methodDef.Name;
astMethod.Name = CleanName(methodDef.Name);
astMethod.TypeParameters.AddRange(MakeTypeParameters(methodDef.GenericParameters));
astMethod.Parameters.AddRange(MakeParameters(methodDef.Parameters));
astMethod.Constraints.AddRange(MakeConstraints(methodDef.GenericParameters));
@ -472,7 +480,7 @@ namespace Decompiler @@ -472,7 +480,7 @@ namespace Decompiler
{
return genericParameters.Select(
gp => new TypeParameterDeclaration {
Name = gp.Name,
Name = CleanName(gp.Name),
Variance = gp.IsContravariant ? VarianceModifier.Contravariant : gp.IsCovariant ? VarianceModifier.Covariant : VarianceModifier.Invariant
});
}
@ -492,7 +500,7 @@ namespace Decompiler @@ -492,7 +500,7 @@ namespace Decompiler
// don't show visibility for static ctors
astMethod.Modifiers &= ~Modifiers.VisibilityMask;
}
astMethod.Name = methodDef.DeclaringType.Name;
astMethod.Name = CleanName(methodDef.DeclaringType.Name);
astMethod.Parameters.AddRange(MakeParameters(methodDef.Parameters));
astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, context);
return astMethod;
@ -503,7 +511,7 @@ namespace Decompiler @@ -503,7 +511,7 @@ namespace Decompiler
PropertyDeclaration astProp = new PropertyDeclaration();
astProp.AddAnnotation(propDef);
astProp.Modifiers = ConvertModifiers(propDef.GetMethod ?? propDef.SetMethod);
astProp.Name = propDef.Name;
astProp.Name = CleanName(propDef.Name);
astProp.ReturnType = ConvertType(propDef.PropertyType, propDef);
if (propDef.GetMethod != null) {
astProp.Getter = new Accessor {
@ -523,7 +531,7 @@ namespace Decompiler @@ -523,7 +531,7 @@ namespace Decompiler
{
CustomEventDeclaration astEvent = new CustomEventDeclaration();
astEvent.AddAnnotation(eventDef);
astEvent.Name = eventDef.Name;
astEvent.Name = CleanName(eventDef.Name);
astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef);
astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod);
if (eventDef.AddMethod != null) {
@ -543,7 +551,7 @@ namespace Decompiler @@ -543,7 +551,7 @@ namespace Decompiler
{
FieldDeclaration astField = new FieldDeclaration();
astField.AddAnnotation(fieldDef);
VariableInitializer initializer = new VariableInitializer(fieldDef.Name);
VariableInitializer initializer = new VariableInitializer(CleanName(fieldDef.Name));
astField.AddChild(initializer, FieldDeclaration.Roles.Variable);
astField.ReturnType = ConvertType(fieldDef.FieldType, fieldDef);
astField.Modifiers = ConvertModifiers(fieldDef);

98
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -181,13 +181,13 @@ namespace Decompiler @@ -181,13 +181,13 @@ namespace Decompiler
switch(expr.Code) {
case ILCode.LogicNot:
return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(expr.Arguments[0]));
case ILCode.LogicAnd:
case ILCode.BrLogicAnd:
return new Ast.BinaryOperatorExpression(
MakeBranchCondition(expr.Arguments[0]),
BinaryOperatorType.ConditionalAnd,
MakeBranchCondition(expr.Arguments[1])
);
case ILCode.LogicOr:
case ILCode.BrLogicOr:
return new Ast.BinaryOperatorExpression(
MakeBranchCondition(expr.Arguments[0]),
BinaryOperatorType.ConditionalOr,
@ -255,8 +255,7 @@ namespace Decompiler @@ -255,8 +255,7 @@ namespace Decompiler
AstNode TransformExpression(ILExpression expr)
{
List<Ast.Expression> args = TransformExpressionArguments(expr);
AstNode node = TransformByteCode(expr, args);
AstNode node = TransformByteCode(expr);
Expression astExpr = node as Expression;
if (astExpr != null)
return Convert(astExpr, expr.InferredType, expr.ExpectedType);
@ -264,22 +263,44 @@ namespace Decompiler @@ -264,22 +263,44 @@ namespace Decompiler
return node;
}
AstNode TransformByteCode(ILExpression byteCode, List<Ast.Expression> args)
AstNode TransformByteCode(ILExpression byteCode)
{
ILCode opCode = byteCode.Code;
object operand = byteCode.Operand;
AstType operandAsTypeRef = AstBuilder.ConvertType(operand as Cecil.TypeReference);
ILExpression operandAsByteCode = operand as ILExpression;
// Do branches first because TransformExpressionArguments does not work on arguments that are branches themselfs
// TODO: We should probably have virtual instructions for these and not abuse branch codes as expressions
switch(opCode) {
case ILCode.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name);
case ILCode.Brfalse:
case ILCode.Brtrue:
case ILCode.Beq:
case ILCode.Bge:
case ILCode.Bge_Un:
case ILCode.Bgt:
case ILCode.Bgt_Un:
case ILCode.Ble:
case ILCode.Ble_Un:
case ILCode.Blt:
case ILCode.Blt_Un:
case ILCode.Bne_Un:
case ILCode.BrLogicAnd:
case ILCode.BrLogicOr:
return new Ast.IfElseStatement() {
Condition = MakeBranchCondition(byteCode),
TrueStatement = new BlockStatement() {
new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name)
}
};
}
List<Ast.Expression> args = TransformExpressionArguments(byteCode);
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;
BlockStatement branchCommand = null;
if (byteCode.Operand is ILLabel) {
branchCommand = new BlockStatement();
branchCommand.Add(new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name));
}
switch((Code)opCode) {
#region Arithmetic
case Code.Add: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
@ -307,10 +328,21 @@ namespace Decompiler @@ -307,10 +328,21 @@ namespace Decompiler
#endregion
#region Arrays
case Code.Newarr:
case (Code)ILCode.InitArray:
{
var ace = new Ast.ArrayCreateExpression();
ace.Type = operandAsTypeRef;
ace.Arguments.Add(arg1);
ComposedType ct = operandAsTypeRef as ComposedType;
if (ct != null) {
// change "new (int[,])[10] to new int[10][,]"
ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers);
}
if (opCode == ILCode.InitArray) {
ace.Initializer = new ArrayInitializerExpression();
ace.Initializer.Elements.AddRange(args);
} else {
ace.Arguments.Add(arg1);
}
return ace;
}
case Code.Ldlen:
@ -340,24 +372,8 @@ namespace Decompiler @@ -340,24 +372,8 @@ namespace Decompiler
case Code.Stelem_R4:
case Code.Stelem_R8:
case Code.Stelem_Ref:
return new Ast.AssignmentExpression(arg1.Indexer(arg2), arg3);
case Code.Stelem_Any:
return InlineAssembly(byteCode, args);
#endregion
#region Branching
case Code.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name);
case Code.Brfalse: return new Ast.IfElseStatement(new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1), branchCommand);
case Code.Brtrue: return new Ast.IfElseStatement(arg1, branchCommand);
case Code.Beq: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2), branchCommand);
case Code.Bge: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2), branchCommand);
case Code.Bge_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2), branchCommand);
case Code.Bgt: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2), branchCommand);
case Code.Bgt_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2), branchCommand);
case Code.Ble: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2), branchCommand);
case Code.Ble_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2), branchCommand);
case Code.Blt: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2), branchCommand);
case Code.Blt_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2), branchCommand);
case Code.Bne_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2), branchCommand);
return new Ast.AssignmentExpression(arg1.Indexer(arg2), arg3);
#endregion
#region Comparison
case Code.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2);
@ -562,14 +578,18 @@ namespace Decompiler @@ -562,14 +578,18 @@ namespace Decompiler
case Code.Newobj:
{
Cecil.TypeReference declaringType = ((MethodReference)operand).DeclaringType;
// TODO: Ensure that the corrent overloaded constructor is called
/*if (declaringType is ArrayType) { shouldn't this be newarr?
return new Ast.ArrayCreateExpression {
Type = AstBuilder.ConvertType((ArrayType)declaringType),
Arguments = args
};
}*/
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);
@ -633,6 +653,12 @@ namespace Decompiler @@ -633,6 +653,12 @@ namespace Decompiler
}
}
if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) {
return 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());
}
// Resolve the method to figure out whether it is an accessor:
Cecil.MethodDefinition cecilMethodDef = cecilMethod.Resolve();
if (cecilMethodDef != null) {

2
ICSharpCode.Decompiler/Disassembler/ILStructure.cs

@ -92,7 +92,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -92,7 +92,7 @@ namespace ICSharpCode.Decompiler.Disassembler
AddNestedStructure(new ILStructure(ILStructureType.Try, eh.TryStart.Offset, eh.TryEnd.Offset, eh));
if (eh.HandlerType == ExceptionHandlerType.Filter)
AddNestedStructure(new ILStructure(ILStructureType.Filter, eh.FilterStart.Offset, eh.FilterEnd.Offset, eh));
AddNestedStructure(new ILStructure(ILStructureType.Handler, eh.HandlerStart.Offset, eh.HandlerEnd.Offset, eh));
AddNestedStructure(new ILStructure(ILStructureType.Handler, eh.HandlerStart.Offset, eh.HandlerEnd == null ? body.CodeSize : eh.HandlerEnd.Offset, eh));
}
// Very simple loop detection: look for backward branches
List<KeyValuePair<Instruction, Instruction>> allBranches = FindAllBranches(body);

3
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -89,10 +89,13 @@ @@ -89,10 +89,13 @@
<Compile Include="FlowAnalysis\SsaVariable.cs" />
<Compile Include="FlowAnalysis\TransformToSsa.cs" />
<Compile Include="GraphVizGraph.cs" />
<Compile Include="ILAst\ArrayInitializers.cs" />
<Compile Include="ILAst\ILAstBuilder.cs" />
<Compile Include="ILAst\ILAstOptimizer.cs" />
<Compile Include="ILAst\ILAstTypes.cs" />
<Compile Include="ILAst\ILCodes.cs" />
<Compile Include="ILAst\ILInlining.cs" />
<Compile Include="ILAst\Pattern.cs" />
<Compile Include="ILAst\TypeAnalysis.cs" />
<Compile Include="ITextOutput.cs" />
<Compile Include="Options.cs" />

113
ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs

@ -0,0 +1,113 @@ @@ -0,0 +1,113 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
namespace Decompiler
{
/// <summary>
/// IL AST transformation that introduces array initializers.
/// </summary>
public class ArrayInitializers
{
public static void Transform(ILBlock method)
{
// TODO: move this somewhere else
// Eliminate 'dups':
foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
for (int i = 0; i < expr.Arguments.Count; i++) {
if (expr.Arguments[i].Code == ILCode.Dup)
expr.Arguments[i] = expr.Arguments[i].Arguments[0];
}
}
var newArrPattern = new StoreToVariable(new ILExpression(ILCode.Newarr, ILExpression.AnyOperand, new ILExpression(ILCode.Ldc_I4, ILExpression.AnyOperand)));
var arg1 = new StoreToVariable(new LoadFromVariable(newArrPattern)) { MustBeGenerated = true };
var arg2 = new StoreToVariable(new LoadFromVariable(newArrPattern)) { MustBeGenerated = true };
var initializeArrayPattern = new ILCall(
"System.Runtime.CompilerServices.RuntimeHelpers", "InitializeArray",
new LoadFromVariable(arg1), new ILExpression(ILCode.Ldtoken, ILExpression.AnyOperand));
foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = block.Body.Count - 1; i >= 0; i--) {
if (!newArrPattern.Match(block.Body[i]))
continue;
ILExpression newArrInst = ((ILExpression)block.Body[i]).Arguments[0];
int arrayLength = (int)newArrInst.Arguments[0].Operand;
if (arrayLength == 0)
continue;
if (arg1.Match(block.Body.ElementAtOrDefault(i + 1)) && arg2.Match(block.Body.ElementAtOrDefault(i + 2))) {
if (initializeArrayPattern.Match(block.Body.ElementAtOrDefault(i + 3))) {
if (HandleStaticallyInitializedArray(arg2, block, i, newArrInst, arrayLength)) {
i -= ILInlining.InlineInto(block, i + 1, method) - 1;
}
continue;
}
}
if (i + 1 + arrayLength > block.Body.Count)
continue;
List<ILExpression> operands = new List<ILExpression>();
for (int j = 0; j < arrayLength; j++) {
ILExpression expr = block.Body[i + 1 + j] as ILExpression;
if (expr == null || !IsStoreToArray(expr.Code))
break;
if (!(expr.Arguments[0].Code == ILCode.Ldloc && expr.Arguments[0].Operand == newArrPattern.LastVariable))
break;
if (!(expr.Arguments[1].Code == ILCode.Ldc_I4 && (int)expr.Arguments[1].Operand == j))
break;
operands.Add(expr.Arguments[2]);
}
if (operands.Count == arrayLength) {
((ILExpression)block.Body[i]).Arguments[0] = new ILExpression(
ILCode.InitArray, newArrInst.Operand, operands.ToArray());
block.Body.RemoveRange(i + 1, arrayLength);
i -= ILInlining.InlineInto(block, i + 1, method) - 1;
}
}
}
}
static bool IsStoreToArray(ILCode code)
{
switch (code) {
case ILCode.Stelem_Any:
case ILCode.Stelem_I:
case ILCode.Stelem_I1:
case ILCode.Stelem_I2:
case ILCode.Stelem_I4:
case ILCode.Stelem_I8:
case ILCode.Stelem_R4:
case ILCode.Stelem_R8:
case ILCode.Stelem_Ref:
return true;
default:
return false;
}
}
static bool HandleStaticallyInitializedArray(StoreToVariable arg2, ILBlock block, int i, ILExpression newArrInst, int arrayLength)
{
FieldDefinition field = ((ILExpression)block.Body[i + 3]).Arguments[1].Operand as FieldDefinition;
if (field == null || field.InitialValue == null)
return false;
switch (TypeAnalysis.GetTypeCode(newArrInst.Operand as TypeReference)) {
case TypeCode.Int32:
case TypeCode.UInt32:
if (field.InitialValue.Length == arrayLength * 4) {
ILExpression[] newArr = new ILExpression[arrayLength];
for (int j = 0; j < newArr.Length; j++) {
newArr[j] = new ILExpression(ILCode.Ldc_I4, BitConverter.ToInt32(field.InitialValue, j * 4));
}
block.Body[i] = new ILExpression(ILCode.Stloc, arg2.LastVariable, new ILExpression(ILCode.InitArray, newArrInst.Operand, newArr));
block.Body.RemoveRange(i + 1, 3);
return true;
}
break;
}
return false;
}
}
}

5
ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs

@ -567,8 +567,9 @@ namespace Decompiler @@ -567,8 +567,9 @@ namespace Decompiler
int startIndex;
for (startIndex = 0; body[startIndex].Offset != eh.HandlerStart.Offset; startIndex++);
int endInclusiveIndex;
// Note that the end(exclusiove) instruction may not necessarly be in our body
for (endInclusiveIndex = 0; body[endInclusiveIndex].Next.Offset != eh.HandlerEnd.Offset; endInclusiveIndex++);
if (eh.HandlerEnd == null) endInclusiveIndex = body.Count - 1;
// Note that the end(exclusive) instruction may not necessarly be in our body
else for (endInclusiveIndex = 0; body[endInclusiveIndex].Next.Offset != eh.HandlerEnd.Offset; endInclusiveIndex++);
int count = 1 + endInclusiveIndex - startIndex;
HashSet<ExceptionHandler> nestedEHs = new HashSet<ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < eh.HandlerEnd.Offset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= eh.HandlerEnd.Offset)));
ehs.ExceptWith(nestedEHs);

324
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -1,6 +1,8 @@ @@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.FlowAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;
@ -15,6 +17,7 @@ namespace Decompiler.ControlFlow @@ -15,6 +17,7 @@ namespace Decompiler.ControlFlow
FlattenNestedMovableBlocks,
SimpleGotoRemoval,
RemoveDeadLabels,
HandleArrayInitializers,
TypeInference,
None
}
@ -22,7 +25,6 @@ namespace Decompiler.ControlFlow @@ -22,7 +25,6 @@ namespace Decompiler.ControlFlow
public class ILAstOptimizer
{
Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>();
Dictionary<ILLabel, int> labelRefCount;
public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None)
{
@ -31,8 +33,9 @@ namespace Decompiler.ControlFlow @@ -31,8 +33,9 @@ namespace Decompiler.ControlFlow
SplitToBasicBlocks(block);
}
OptimizeShortCircuits(method);
if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return;
UpdateLabelRefCounts(method);
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
ControlFlowGraph graph;
graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand);
@ -42,7 +45,6 @@ namespace Decompiler.ControlFlow @@ -42,7 +45,6 @@ namespace Decompiler.ControlFlow
}
if (abortBeforeStep == ILAstOptimizationStep.FindConditions) return;
UpdateLabelRefCounts(method);
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
ControlFlowGraph graph;
graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand);
@ -63,6 +65,9 @@ namespace Decompiler.ControlFlow @@ -63,6 +65,9 @@ namespace Decompiler.ControlFlow
if (abortBeforeStep == ILAstOptimizationStep.RemoveDeadLabels) return;
RemoveDeadLabels(method);
if (abortBeforeStep == ILAstOptimizationStep.HandleArrayInitializers) return;
ArrayInitializers.Transform(method);
if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return;
TypeAnalysis.Run(context, method);
}
@ -99,8 +104,8 @@ namespace Decompiler.ControlFlow @@ -99,8 +104,8 @@ namespace Decompiler.ControlFlow
// Insert split
if (currNode is ILLabel ||
lastNode is ILTryCatchBlock ||
currNode is ILTryCatchBlock ||
lastNode is ILTryCatchBlock ||
currNode is ILTryCatchBlock ||
(lastNode is ILExpression) && ((ILExpression)lastNode).IsBranch() ||
(currNode is ILExpression) && (((ILExpression)currNode).IsBranch() && basicBlock.Body.Count > 0))
{
@ -131,6 +136,109 @@ namespace Decompiler.ControlFlow @@ -131,6 +136,109 @@ namespace Decompiler.ControlFlow
return;
}
void OptimizeShortCircuits(ILBlock method)
{
AnalyseLabels(method);
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
bool modified;
do {
modified = false;
for (int i = 0; i < block.Body.Count;) {
if (TrySimplifyShortCircuit(block.Body, (ILBasicBlock)block.Body[i])) {
modified = true;
} else {
i++;
}
}
} while(modified);
}
}
Dictionary<ILLabel, int> labelGlobalRefCount;
Dictionary<ILLabel, ILBasicBlock> labelToBasicBlock;
void AnalyseLabels(ILBlock method)
{
labelGlobalRefCount = new Dictionary<ILLabel, int>();
foreach(ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.GetBranchTargets())) {
if (!labelGlobalRefCount.ContainsKey(target))
labelGlobalRefCount[target] = 0;
labelGlobalRefCount[target]++;
}
labelToBasicBlock = new Dictionary<ILLabel, ILBasicBlock>();
foreach(ILBasicBlock bb in method.GetSelfAndChildrenRecursive<ILBasicBlock>()) {
foreach(ILLabel label in bb.GetChildren().OfType<ILLabel>()) {
labelToBasicBlock[label] = bb;
}
}
}
bool IsConditionalBranch(ILBasicBlock bb, ref ILExpression branchExpr, ref ILLabel trueLabel, ref ILLabel falseLabel)
{
if (bb.Body.Count == 1) {
branchExpr = bb.Body[0] as ILExpression;
if (branchExpr != null && branchExpr.Operand is ILLabel && branchExpr.Arguments.Count > 0) {
trueLabel = (ILLabel)branchExpr.Operand;
falseLabel = (ILLabel)((ILExpression)bb.FallthoughGoto).Operand;
return true;
}
}
return false;
}
// scope is modified if successful
bool TrySimplifyShortCircuit(List<ILNode> scope, ILBasicBlock head)
{
Debug.Assert(scope.Contains(head));
ILExpression branchExpr = null;
ILLabel trueLabel = null;
ILLabel falseLabel = null;
if(IsConditionalBranch(head, ref branchExpr, ref trueLabel, ref falseLabel)) {
for (int pass = 0; pass < 2; pass++) {
// On the second pass, swap labels and negate expression of the first branch
// It is slightly ugly, but much better then copy-pasting this whole block
ILLabel nextLabel = (pass == 0) ? trueLabel : falseLabel;
ILLabel otherLablel = (pass == 0) ? falseLabel : trueLabel;
bool negate = (pass == 1);
ILBasicBlock nextBasicBlock = labelToBasicBlock[nextLabel];
ILExpression nextBranchExpr = null;
ILLabel nextTrueLablel = null;
ILLabel nextFalseLabel = null;
if (scope.Contains(nextBasicBlock) &&
nextBasicBlock != head &&
labelGlobalRefCount[nextBasicBlock.EntryLabel] == 1 &&
IsConditionalBranch(nextBasicBlock, ref nextBranchExpr, ref nextTrueLablel, ref nextFalseLabel) &&
(otherLablel == nextFalseLabel || otherLablel == nextTrueLablel))
{
// We are using the branches as expressions now, so do not keep their labels alive
branchExpr.Operand = null;
nextBranchExpr.Operand = null;
// Create short cicuit branch
if (otherLablel == nextFalseLabel) {
head.Body[0] = new ILExpression(ILCode.BrLogicAnd, nextTrueLablel, negate ? new ILExpression(ILCode.LogicNot, null, branchExpr) : branchExpr, nextBranchExpr);
} else {
head.Body[0] = new ILExpression(ILCode.BrLogicOr, nextTrueLablel, negate ? branchExpr : new ILExpression(ILCode.LogicNot, null, branchExpr), nextBranchExpr);
}
head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel);
// Remove the inlined branch from scope
labelGlobalRefCount[nextBasicBlock.EntryLabel] = 0;
if (!scope.Remove(nextBasicBlock))
throw new Exception("Element not found");
return true;
}
}
}
return false;
}
ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel)
{
int index = 0;
@ -203,24 +311,27 @@ namespace Decompiler.ControlFlow @@ -203,24 +311,27 @@ namespace Decompiler.ControlFlow
ControlFlowNode node = agenda.Dequeue();
if (scope.Contains(node)
&& node.DominanceFrontier.Contains(node)
&& (node != entryPoint || !excludeEntryPoint))
&& node.DominanceFrontier.Contains(node)
&& (node != entryPoint || !excludeEntryPoint))
{
HashSet<ControlFlowNode> loopContents = FindDominatedNodes(scope, node);
ILWhileLoop loop = new ILWhileLoop();
ILCondition cond;
HashSet<ControlFlowNode> condNodes;
ILLabel condLabel;
if (TryMatchCondition(loopContents, new ControlFlowNode[]{}, node, out cond, out condNodes, out condLabel)) {
loopContents.ExceptWith(condNodes);
scope.ExceptWith(condNodes);
ILBasicBlock basicBlock = node.UserData as ILBasicBlock;
ILExpression branchExpr = null;
ILLabel trueLabel = null;
ILLabel falseLabel = null;
if(basicBlock != null && IsConditionalBranch(basicBlock, ref branchExpr, ref trueLabel, ref falseLabel)) {
loopContents.Remove(node);
scope.Remove(node);
branchExpr.Operand = null; // Do not keep label alive
// Use loop to implement condition
loop.Condition = cond.Condition;
loop.PreLoopLabel = condLabel;
loop.PostLoopGoto = cond.FalseBlock.EntryGoto;
loop.BodyBlock = new ILBlock() { EntryGoto = cond.TrueBlock.EntryGoto };
loop.Condition = branchExpr;
loop.PreLoopLabel = basicBlock.EntryLabel;
loop.PostLoopGoto = new ILExpression(ILCode.Br, falseLabel);
loop.BodyBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) };
} else {
// Give the block some explicit entry point
ILLabel entryLabel = new ILLabel() { Name = "Loop_" + (nextBlockIndex++) };
@ -249,16 +360,6 @@ namespace Decompiler.ControlFlow @@ -249,16 +360,6 @@ namespace Decompiler.ControlFlow
return result;
}
void UpdateLabelRefCounts(ILBlock method)
{
labelRefCount = new Dictionary<ILLabel, int>();
foreach(ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.GetBranchTargets())) {
if (!labelRefCount.ContainsKey(target))
labelRefCount[target] = 0;
labelRefCount[target]++;
}
}
List<ILNode> FindConditions(HashSet<ControlFlowNode> scope, ControlFlowNode entryNode)
{
List<ILNode> result = new List<ILNode>();
@ -288,11 +389,25 @@ namespace Decompiler.ControlFlow @@ -288,11 +389,25 @@ namespace Decompiler.ControlFlow
// 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;
ILSwitch ilSwitch = new ILSwitch() {
Condition = condBranch,
DefaultGoto = block.FallthoughGoto
};
result.Add(new ILBasicBlock() {
EntryLabel = block.EntryLabel, // Keep the entry label
Body = { ilSwitch }
});
// Remove the item so that it is not picked up as content
if (!scope.Remove(node))
throw new Exception("Item is not in set");
// Pull in code of cases
ControlFlowNode fallTarget = null;
labelToCfNode.TryGetValue((ILLabel)block.FallthoughGoto.Operand, out fallTarget);
@ -300,14 +415,14 @@ namespace Decompiler.ControlFlow @@ -300,14 +415,14 @@ namespace Decompiler.ControlFlow
if (fallTarget != null)
frontiers.UnionWith(fallTarget.DominanceFrontier);
foreach(ILLabel condLabel in (ILLabel[])condBranch.Operand) {
foreach(ILLabel condLabel in caseLabels) {
ControlFlowNode condTarget = null;
labelToCfNode.TryGetValue(condLabel, out condTarget);
if (condTarget != null)
frontiers.UnionWith(condTarget.DominanceFrontier);
}
foreach(ILLabel condLabel in (ILLabel[])condBranch.Operand) {
foreach(ILLabel condLabel in caseLabels) {
ControlFlowNode condTarget = null;
labelToCfNode.TryGetValue(condLabel, out condTarget);
@ -321,34 +436,36 @@ namespace Decompiler.ControlFlow @@ -321,34 +436,36 @@ namespace Decompiler.ControlFlow
}
ilSwitch.CaseBlocks.Add(caseBlock);
}
}
// Two-way branch
ILExpression branchExpr = null;
ILLabel trueLabel = null;
ILLabel falseLabel = null;
if(IsConditionalBranch(block, ref branchExpr, ref trueLabel, ref falseLabel)) {
// The labels will not be used - kill them
condBranch.Operand = null;
// The branch label will not be used - kill it
branchExpr.Operand = null;
// Convert the basic block to ILCondition
ILCondition ilCond = new ILCondition() {
Condition = branchExpr,
TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) },
FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, falseLabel) }
};
result.Add(new ILBasicBlock() {
EntryLabel = block.EntryLabel, // Keep the entry label
Body = { ilSwitch }
EntryLabel = block.EntryLabel, // Keep the entry label
Body = { ilCond }
});
scope.Remove(node);
}
// Two-way branch
ILCondition ilCond;
HashSet<ControlFlowNode> matchedNodes;
ILLabel condEntryLabel;
if (TryMatchCondition(scope, new ControlFlowNode[] {}, node, out ilCond, out matchedNodes, out condEntryLabel)) {
// The branch labels will not be used - kill them
foreach(ILExpression expr in ilCond.Condition.GetSelfAndChildrenRecursive<ILExpression>()) {
if (expr.GetBranchTargets().Any()) {
expr.Operand = null;
}
}
// Remove the item immediately so that it is not picked up as content
if (!scope.Remove(node))
throw new Exception("Item is not in set");
ControlFlowNode trueTarget = null;
labelToCfNode.TryGetValue((ILLabel)ilCond.TrueBlock.EntryGoto.Operand, out trueTarget);
labelToCfNode.TryGetValue(trueLabel, out trueTarget);
ControlFlowNode falseTarget = null;
labelToCfNode.TryGetValue((ILLabel)ilCond.FalseBlock.EntryGoto.Operand, out falseTarget);
labelToCfNode.TryGetValue(falseLabel, out falseTarget);
// Pull in the conditional code
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
@ -367,12 +484,6 @@ namespace Decompiler.ControlFlow @@ -367,12 +484,6 @@ namespace Decompiler.ControlFlow
scope.ExceptWith(content);
ilCond.FalseBlock.Body.AddRange(FindConditions(content, falseTarget));
}
result.Add(new ILBasicBlock() {
EntryLabel = condEntryLabel, // Keep the entry label
Body = { ilCond }
});
scope.ExceptWith(matchedNodes);
}
}
@ -397,107 +508,6 @@ namespace Decompiler.ControlFlow @@ -397,107 +508,6 @@ namespace Decompiler.ControlFlow
return result;
}
bool TryMatchCondition(HashSet<ControlFlowNode> scope, IEnumerable<ControlFlowNode> scopeExcept, ControlFlowNode head, out ILCondition condition, out HashSet<ControlFlowNode> matchedNodes, out ILLabel entryLabel)
{
condition = null;
matchedNodes = null;
entryLabel = null;
if (!scope.Contains(head) || scopeExcept.Contains(head))
return false;
ILBasicBlock basicBlock = head.UserData as ILBasicBlock;
if (basicBlock == null || basicBlock.Body.Count != 1)
return false;
ILExpression condBranch = basicBlock.Body[0] as ILExpression;
if (condBranch != null && condBranch.Operand is ILLabel && condBranch.Arguments.Count > 0) {
// We have found a two-way condition
condition = new ILCondition() {
Condition = condBranch,
TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, condBranch.Operand) },
FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, basicBlock.FallthoughGoto.Operand) }
};
// We are done with the node so "remove" it from scope
scopeExcept = scopeExcept.Union(new[] {head});
matchedNodes = new HashSet<ControlFlowNode>() { head };
entryLabel = basicBlock.EntryLabel;
// Optimize short-circut expressions
while(true) {
// Consider condition.TrueBlock
{
ILLabel nextLabel = (ILLabel)condition.TrueBlock.EntryGoto.Operand;
ControlFlowNode nextTarget;
labelToCfNode.TryGetValue(nextLabel, out nextTarget);
ILCondition nextCond;
HashSet<ControlFlowNode> nextMatchedNodes;
ILLabel nextEnteryLabel;
if (nextTarget != null &&
TryMatchCondition(scope, scopeExcept, nextTarget, out nextCond, out nextMatchedNodes, out nextEnteryLabel) &&
labelRefCount[nextEnteryLabel] == 1)
{
if (condition.FalseBlock.EntryGoto.Operand == nextCond.FalseBlock.EntryGoto.Operand) {
condition.Condition = new ILExpression(ILCode.LogicAnd, null, condition.Condition, nextCond.Condition);
condition.TrueBlock = nextCond.TrueBlock;
condition.FalseBlock = nextCond.FalseBlock;
scopeExcept = scopeExcept.Union(nextMatchedNodes);
matchedNodes.UnionWith(nextMatchedNodes);
continue;
}
if (condition.FalseBlock.EntryGoto.Operand == nextCond.TrueBlock.EntryGoto.Operand) {
condition.Condition = new ILExpression(ILCode.LogicOr, null, new ILExpression(ILCode.LogicNot, null, condition.Condition), nextCond.Condition);
condition.TrueBlock = nextCond.TrueBlock;
condition.FalseBlock = nextCond.FalseBlock;
scopeExcept = scopeExcept.Union(nextMatchedNodes);
matchedNodes.UnionWith(nextMatchedNodes);
continue;
}
}
}
// Consider condition.FalseBlock
{
ILLabel nextLabel = (ILLabel)condition.FalseBlock.EntryGoto.Operand;
ControlFlowNode nextTarget;
labelToCfNode.TryGetValue(nextLabel, out nextTarget);
ILCondition nextCond;
HashSet<ControlFlowNode> nextMatchedNodes;
ILLabel nextEnteryLabel;
if (nextTarget != null &&
TryMatchCondition(scope, scopeExcept, nextTarget, out nextCond, out nextMatchedNodes, out nextEnteryLabel) &&
labelRefCount[nextEnteryLabel] == 1)
{
if (condition.TrueBlock.EntryGoto.Operand == nextCond.FalseBlock.EntryGoto.Operand) {
condition.Condition = new ILExpression(ILCode.LogicAnd, null, new ILExpression(ILCode.LogicNot, null, condition.Condition), nextCond.Condition);
condition.TrueBlock = nextCond.TrueBlock;
condition.FalseBlock = nextCond.FalseBlock;
scopeExcept = scopeExcept.Union(nextMatchedNodes);
matchedNodes.UnionWith(nextMatchedNodes);
continue;
}
if (condition.TrueBlock.EntryGoto.Operand == nextCond.TrueBlock.EntryGoto.Operand) {
condition.Condition = new ILExpression(ILCode.LogicOr, null, condition.Condition, nextCond.Condition);
condition.TrueBlock = nextCond.TrueBlock;
condition.FalseBlock = nextCond.FalseBlock;
scopeExcept = scopeExcept.Union(nextMatchedNodes);
matchedNodes.UnionWith(nextMatchedNodes);
continue;
}
}
}
break;
}
return true;
}
return false;
}
static HashSet<ControlFlowNode> FindDominatedNodes(HashSet<ControlFlowNode> scope, ControlFlowNode head)
{
var exitNodes = head.DominanceFrontier.SelectMany(n => n.Predecessors);
@ -507,7 +517,7 @@ namespace Decompiler.ControlFlow @@ -507,7 +517,7 @@ namespace Decompiler.ControlFlow
while(agenda.Count > 0) {
ControlFlowNode addNode = agenda.First();
agenda.Remove(addNode);
if (scope.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) {
foreach (var predecessor in addNode.Predecessors) {
agenda.Add(predecessor);

21
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -207,6 +207,8 @@ namespace Decompiler @@ -207,6 +207,8 @@ namespace Decompiler
public TypeReference ExpectedType { get; set; }
public TypeReference InferredType { get; set; }
public static readonly object AnyOperand = new object();
public ILExpression(ILCode code, object operand, params ILExpression[] args)
{
this.Code = code;
@ -269,6 +271,25 @@ namespace Decompiler @@ -269,6 +271,25 @@ namespace Decompiler
return ranges;
}
public virtual bool Match(ILNode other)
{
ILExpression expr = other as ILExpression;
return expr != null && this.Code == expr.Code
&& (this.Operand == AnyOperand || object.Equals(this.Operand, expr.Operand))
&& Match(this.Arguments, expr.Arguments);
}
protected static bool Match(IList<ILExpression> a, IList<ILExpression> b)
{
if (a.Count != b.Count)
return false;
for (int i = 0; i < a.Count; i++) {
if (!a[i].Match(b[i]))
return false;
}
return true;
}
public override void WriteTo(ITextOutput output)
{
if (Operand is ILVariable && ((ILVariable)Operand).IsGenerated) {

163
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -256,8 +256,11 @@ namespace Decompiler @@ -256,8 +256,11 @@ namespace Decompiler
// Virtual codes - defined for convenience
Ldexception, // Operand holds the CatchType for catch handler, null for filter
LogicNot,
LogicAnd,
LogicOr
BrLogicAnd,
BrLogicOr,
InitArray, // Array Initializer
Pattern // used for ILAst pattern nodes
}
public static class ILCodeUtil
@ -288,26 +291,26 @@ namespace Decompiler @@ -288,26 +291,26 @@ namespace Decompiler
public static int? GetPopCount(this Instruction inst)
{
switch(inst.OpCode.StackBehaviourPop) {
case StackBehaviour.Pop0: return 0;
case StackBehaviour.Pop1: return 1;
case StackBehaviour.Popi: return 1;
case StackBehaviour.Popref: return 1;
case StackBehaviour.Pop1_pop1: return 2;
case StackBehaviour.Popi_pop1: return 2;
case StackBehaviour.Popi_popi: return 2;
case StackBehaviour.Popi_popi8: return 2;
case StackBehaviour.Popi_popr4: return 2;
case StackBehaviour.Popi_popr8: return 2;
case StackBehaviour.Popref_pop1: return 2;
case StackBehaviour.Popref_popi: return 2;
case StackBehaviour.Popi_popi_popi: return 3;
case StackBehaviour.Popref_popi_popi: return 3;
case StackBehaviour.Popref_popi_popi8: return 3;
case StackBehaviour.Popref_popi_popr4: return 3;
case StackBehaviour.Popref_popi_popr8: return 3;
case StackBehaviour.Popref_popi_popref: return 3;
case StackBehaviour.PopAll: return null;
case StackBehaviour.Varpop:
case StackBehaviour.Pop0: return 0;
case StackBehaviour.Pop1: return 1;
case StackBehaviour.Popi: return 1;
case StackBehaviour.Popref: return 1;
case StackBehaviour.Pop1_pop1: return 2;
case StackBehaviour.Popi_pop1: return 2;
case StackBehaviour.Popi_popi: return 2;
case StackBehaviour.Popi_popi8: return 2;
case StackBehaviour.Popi_popr4: return 2;
case StackBehaviour.Popi_popr8: return 2;
case StackBehaviour.Popref_pop1: return 2;
case StackBehaviour.Popref_popi: return 2;
case StackBehaviour.Popi_popi_popi: return 3;
case StackBehaviour.Popref_popi_popi: return 3;
case StackBehaviour.Popref_popi_popi8: return 3;
case StackBehaviour.Popref_popi_popr4: return 3;
case StackBehaviour.Popref_popi_popr8: return 3;
case StackBehaviour.Popref_popi_popref: return 3;
case StackBehaviour.PopAll: return null;
case StackBehaviour.Varpop:
switch(inst.OpCode.Code) {
case Code.Call:
case Code.Callvirt:
@ -317,28 +320,28 @@ namespace Decompiler @@ -317,28 +320,28 @@ namespace Decompiler
} else {
return cecilMethod.Parameters.Count;
}
case Code.Calli: throw new NotImplementedException();
case Code.Ret: return null;
case Code.Calli: throw new NotImplementedException();
case Code.Ret: return null;
case Code.Newobj:
MethodReference ctorMethod = ((MethodReference)inst.Operand);
return ctorMethod.Parameters.Count;
default: throw new Exception("Unknown Varpop opcode");
default: throw new Exception("Unknown Varpop opcode");
}
default: throw new Exception("Unknown pop behaviour: " + inst.OpCode.StackBehaviourPop);
default: throw new Exception("Unknown pop behaviour: " + inst.OpCode.StackBehaviourPop);
}
}
public static int GetPushCount(this Instruction inst)
{
switch(inst.OpCode.StackBehaviourPush) {
case StackBehaviour.Push0: return 0;
case StackBehaviour.Push1: return 1;
case StackBehaviour.Push1_push1: return 2;
case StackBehaviour.Pushi: return 1;
case StackBehaviour.Pushi8: return 1;
case StackBehaviour.Pushr4: return 1;
case StackBehaviour.Pushr8: return 1;
case StackBehaviour.Pushref: return 1;
case StackBehaviour.Push0: return 0;
case StackBehaviour.Push1: return 1;
case StackBehaviour.Push1_push1: return 2;
case StackBehaviour.Pushi: return 1;
case StackBehaviour.Pushi8: return 1;
case StackBehaviour.Pushr4: return 1;
case StackBehaviour.Pushr8: return 1;
case StackBehaviour.Pushref: return 1;
case StackBehaviour.Varpush: // Happens only for calls
switch(inst.OpCode.Code) {
case Code.Call:
@ -349,59 +352,59 @@ namespace Decompiler @@ -349,59 +352,59 @@ namespace Decompiler
} else {
return 1;
}
case Code.Calli: throw new NotImplementedException();
default: throw new Exception("Unknown Varpush opcode");
case Code.Calli: throw new NotImplementedException();
default: throw new Exception("Unknown Varpush opcode");
}
default: throw new Exception("Unknown push behaviour: " + inst.OpCode.StackBehaviourPush);
default: throw new Exception("Unknown push behaviour: " + inst.OpCode.StackBehaviourPush);
}
}
public static void ExpandMacro(ref ILCode code, ref object operand, MethodBody methodBody)
{
switch (code) {
case ILCode.__Ldarg_0: code = ILCode.Ldarg; operand = methodBody.GetParameter(0); break;
case ILCode.__Ldarg_1: code = ILCode.Ldarg; operand = methodBody.GetParameter(1); break;
case ILCode.__Ldarg_2: code = ILCode.Ldarg; operand = methodBody.GetParameter(2); break;
case ILCode.__Ldarg_3: code = ILCode.Ldarg; operand = methodBody.GetParameter(3); break;
case ILCode.__Ldloc_0: code = ILCode.Ldloc; operand = methodBody.Variables[0]; break;
case ILCode.__Ldloc_1: code = ILCode.Ldloc; operand = methodBody.Variables[1]; break;
case ILCode.__Ldloc_2: code = ILCode.Ldloc; operand = methodBody.Variables[2]; break;
case ILCode.__Ldloc_3: code = ILCode.Ldloc; operand = methodBody.Variables[3]; break;
case ILCode.__Stloc_0: code = ILCode.Stloc; operand = methodBody.Variables[0]; break;
case ILCode.__Stloc_1: code = ILCode.Stloc; operand = methodBody.Variables[1]; break;
case ILCode.__Stloc_2: code = ILCode.Stloc; operand = methodBody.Variables[2]; break;
case ILCode.__Stloc_3: code = ILCode.Stloc; operand = methodBody.Variables[3]; break;
case ILCode.__Ldarg_S: code = ILCode.Ldarg; break;
case ILCode.__Ldarga_S: code = ILCode.Ldarga; break;
case ILCode.__Starg_S: code = ILCode.Starg; break;
case ILCode.__Ldloc_S: code = ILCode.Ldloc; break;
case ILCode.__Ldloca_S: code = ILCode.Ldloca; break;
case ILCode.__Stloc_S: code = ILCode.Stloc; break;
case ILCode.__Ldc_I4_M1: code = ILCode.Ldc_I4; operand = -1; break;
case ILCode.__Ldc_I4_0: code = ILCode.Ldc_I4; operand = 0; break;
case ILCode.__Ldc_I4_1: code = ILCode.Ldc_I4; operand = 1; break;
case ILCode.__Ldc_I4_2: code = ILCode.Ldc_I4; operand = 2; break;
case ILCode.__Ldc_I4_3: code = ILCode.Ldc_I4; operand = 3; break;
case ILCode.__Ldc_I4_4: code = ILCode.Ldc_I4; operand = 4; break;
case ILCode.__Ldc_I4_5: code = ILCode.Ldc_I4; operand = 5; break;
case ILCode.__Ldc_I4_6: code = ILCode.Ldc_I4; operand = 6; break;
case ILCode.__Ldc_I4_7: code = ILCode.Ldc_I4; operand = 7; break;
case ILCode.__Ldc_I4_8: code = ILCode.Ldc_I4; operand = 8; break;
case ILCode.__Ldc_I4_S: code = ILCode.Ldc_I4; operand = (int) (sbyte) operand; break;
case ILCode.__Br_S: code = ILCode.Br; break;
case ILCode.__Brfalse_S: code = ILCode.Brfalse; break;
case ILCode.__Brtrue_S: code = ILCode.Brtrue; break;
case ILCode.__Beq_S: code = ILCode.Beq; break;
case ILCode.__Bge_S: code = ILCode.Bge; break;
case ILCode.__Bgt_S: code = ILCode.Bgt; break;
case ILCode.__Ble_S: code = ILCode.Ble; break;
case ILCode.__Blt_S: code = ILCode.Blt; break;
case ILCode.__Bne_Un_S: code = ILCode.Bne_Un; break;
case ILCode.__Bge_Un_S: code = ILCode.Bge_Un; break;
case ILCode.__Bgt_Un_S: code = ILCode.Bgt_Un; break;
case ILCode.__Ble_Un_S: code = ILCode.Ble_Un; break;
case ILCode.__Blt_Un_S: code = ILCode.Blt_Un; break;
case ILCode.__Leave_S: code = ILCode.Leave; break;
case ILCode.__Ldarg_0: code = ILCode.Ldarg; operand = methodBody.GetParameter(0); break;
case ILCode.__Ldarg_1: code = ILCode.Ldarg; operand = methodBody.GetParameter(1); break;
case ILCode.__Ldarg_2: code = ILCode.Ldarg; operand = methodBody.GetParameter(2); break;
case ILCode.__Ldarg_3: code = ILCode.Ldarg; operand = methodBody.GetParameter(3); break;
case ILCode.__Ldloc_0: code = ILCode.Ldloc; operand = methodBody.Variables[0]; break;
case ILCode.__Ldloc_1: code = ILCode.Ldloc; operand = methodBody.Variables[1]; break;
case ILCode.__Ldloc_2: code = ILCode.Ldloc; operand = methodBody.Variables[2]; break;
case ILCode.__Ldloc_3: code = ILCode.Ldloc; operand = methodBody.Variables[3]; break;
case ILCode.__Stloc_0: code = ILCode.Stloc; operand = methodBody.Variables[0]; break;
case ILCode.__Stloc_1: code = ILCode.Stloc; operand = methodBody.Variables[1]; break;
case ILCode.__Stloc_2: code = ILCode.Stloc; operand = methodBody.Variables[2]; break;
case ILCode.__Stloc_3: code = ILCode.Stloc; operand = methodBody.Variables[3]; break;
case ILCode.__Ldarg_S: code = ILCode.Ldarg; break;
case ILCode.__Ldarga_S: code = ILCode.Ldarga; break;
case ILCode.__Starg_S: code = ILCode.Starg; break;
case ILCode.__Ldloc_S: code = ILCode.Ldloc; break;
case ILCode.__Ldloca_S: code = ILCode.Ldloca; break;
case ILCode.__Stloc_S: code = ILCode.Stloc; break;
case ILCode.__Ldc_I4_M1: code = ILCode.Ldc_I4; operand = -1; break;
case ILCode.__Ldc_I4_0: code = ILCode.Ldc_I4; operand = 0; break;
case ILCode.__Ldc_I4_1: code = ILCode.Ldc_I4; operand = 1; break;
case ILCode.__Ldc_I4_2: code = ILCode.Ldc_I4; operand = 2; break;
case ILCode.__Ldc_I4_3: code = ILCode.Ldc_I4; operand = 3; break;
case ILCode.__Ldc_I4_4: code = ILCode.Ldc_I4; operand = 4; break;
case ILCode.__Ldc_I4_5: code = ILCode.Ldc_I4; operand = 5; break;
case ILCode.__Ldc_I4_6: code = ILCode.Ldc_I4; operand = 6; break;
case ILCode.__Ldc_I4_7: code = ILCode.Ldc_I4; operand = 7; break;
case ILCode.__Ldc_I4_8: code = ILCode.Ldc_I4; operand = 8; break;
case ILCode.__Ldc_I4_S: code = ILCode.Ldc_I4; operand = (int) (sbyte) operand; break;
case ILCode.__Br_S: code = ILCode.Br; break;
case ILCode.__Brfalse_S: code = ILCode.Brfalse; break;
case ILCode.__Brtrue_S: code = ILCode.Brtrue; break;
case ILCode.__Beq_S: code = ILCode.Beq; break;
case ILCode.__Bge_S: code = ILCode.Bge; break;
case ILCode.__Bgt_S: code = ILCode.Bgt; break;
case ILCode.__Ble_S: code = ILCode.Ble; break;
case ILCode.__Blt_S: code = ILCode.Blt; break;
case ILCode.__Bne_Un_S: code = ILCode.Bne_Un; break;
case ILCode.__Bge_Un_S: code = ILCode.Bge_Un; break;
case ILCode.__Bgt_Un_S: code = ILCode.Bgt_Un; break;
case ILCode.__Ble_Un_S: code = ILCode.Ble_Un; break;
case ILCode.__Blt_Un_S: code = ILCode.Blt_Un; break;
case ILCode.__Leave_S: code = ILCode.Leave; break;
}
}

94
ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -0,0 +1,94 @@ @@ -0,0 +1,94 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Diagnostics;
using System.Linq;
namespace Decompiler
{
/// <summary>
/// Performs inlining transformations.
/// </summary>
public class ILInlining
{
/// <summary>
/// Inlines instructions before pos into block.Body[pos].
/// </summary>
/// <returns>The number of instructions that were inlined.</returns>
public static int InlineInto(ILBlock block, int pos, ILBlock method)
{
int count = 0;
while (--pos >= 0) {
ILExpression expr = block.Body[pos] as ILExpression;
if (expr == null || expr.Code != ILCode.Stloc)
break;
if (InlineIfPossible(block, pos, method))
count++;
else
break;
}
return count;
}
/// <summary>
/// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
/// </summary>
public static bool InlineIfPossible(ILBlock block, int pos, ILBlock method)
{
if (InlineIfPossible((ILExpression)block.Body[pos], block.Body.ElementAtOrDefault(pos+1), method)) {
block.Body.RemoveAt(pos);
return true;
}
return false;
}
/// <summary>
/// Inlines 'expr' into 'next', if possible.
/// </summary>
public static bool InlineIfPossible(ILExpression expr, ILNode next, ILBlock method)
{
if (expr.Code != ILCode.Stloc)
throw new ArgumentException("expr must be stloc");
// ensure the variable is only accessed only a single time
if (method.GetSelfAndChildrenRecursive<ILExpression>().Count(e => e != expr && e.Operand == expr.Operand) != 1)
return false;
ILExpression parent;
int pos;
if (FindLoadInNext(next as ILExpression, (ILVariable)expr.Operand, out parent, out pos) == true) {
parent.Arguments[pos] = expr.Arguments[0];
return true;
}
return false;
}
/// <summary>
/// Finds the position to inline to.
/// </summary>
/// <returns>true = found; false = cannot continue search; null = not found</returns>
static bool? FindLoadInNext(ILExpression expr, ILVariable v, out ILExpression parent, out int pos)
{
parent = null;
pos = 0;
if (expr == null)
return false;
for (int i = 0; i < expr.Arguments.Count; i++) {
ILExpression arg = expr.Arguments[i];
if (arg.Code == ILCode.Ldloc && arg.Operand == v) {
parent = expr;
pos = i;
return true;
}
bool? r = FindLoadInNext(arg, v, out parent, out pos);
if (r != null)
return r;
}
return IsWithoutSideEffects(expr.Code) ? (bool?)null : false;
}
static bool IsWithoutSideEffects(ILCode code)
{
return code == ILCode.Ldloc;
}
}
}

103
ICSharpCode.Decompiler/ILAst/Pattern.cs

@ -0,0 +1,103 @@ @@ -0,0 +1,103 @@
// 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;
namespace Decompiler
{
public interface IVariablePattern
{
bool MatchVariable(ILVariable v);
}
public class StoreToVariable : ILExpression, IVariablePattern
{
public bool MustBeGenerated;
public ILExpression LastMatch;
public ILVariable LastVariable {
get {
return LastMatch != null ? LastMatch.Operand as ILVariable : null;
}
}
public StoreToVariable(ILExpression arg) : base(ILCode.Pattern, null, arg)
{
}
public override bool Match(ILNode other)
{
ILExpression expr = other as ILExpression;
if (expr != null && expr.Code == ILCode.Stloc && (!MustBeGenerated || ((ILVariable)expr.Operand).IsGenerated) && Match(this.Arguments, expr.Arguments)) {
this.LastMatch = expr;
return true;
} else {
return false;
}
}
bool IVariablePattern.MatchVariable(ILVariable v)
{
return v == LastMatch.Operand;
}
}
public class LoadFromVariable : ILExpression
{
IVariablePattern v;
public LoadFromVariable(IVariablePattern v) : base(ILCode.Pattern, null)
{
this.v = v;
}
public override bool Match(ILNode other)
{
ILExpression expr = other as ILExpression;
return expr != null && expr.Code == ILCode.Ldloc && v.MatchVariable(expr.Operand as ILVariable);
}
}
public class AnyILExpression : ILExpression
{
public ILExpression LastMatch;
public AnyILExpression() : base(ILCode.Pattern, null)
{
}
public override bool Match(ILNode other)
{
if (other is ILExpression) {
LastMatch = (ILExpression)other;
return true;
} else {
return false;
}
}
}
public class ILCall : ILExpression
{
string fullClassName;
string methodName;
public ILCall(string fullClassName, string methodName, params ILExpression[] args) : base(ILCode.Pattern, null, args)
{
this.fullClassName = fullClassName;
this.methodName = methodName;
}
public override bool Match(ILNode other)
{
ILExpression expr = other as ILExpression;
if (expr != null && expr.Code == ILCode.Call) {
MethodReference r = (MethodReference)expr.Operand;
if (r.Name == methodName && r.DeclaringType.FullName == fullClassName)
return Match(this.Arguments, expr.Arguments);
}
return false;
}
}
}

26
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -112,13 +112,13 @@ namespace Decompiler @@ -112,13 +112,13 @@ namespace Decompiler
InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean);
}
return typeSystem.Boolean;
case ILCode.LogicAnd:
case ILCode.LogicOr:
case ILCode.BrLogicAnd:
case ILCode.BrLogicOr:
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean);
InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean);
InferTypeForExpression(expr.Arguments[1], typeSystem.Boolean);
}
return typeSystem.Boolean;
return null;
#endregion
#region Variable load/store
case ILCode.Stloc:
@ -286,7 +286,9 @@ namespace Decompiler @@ -286,7 +286,9 @@ namespace Decompiler
case ILCode.Ldvirtftn:
return typeSystem.IntPtr;
case ILCode.Ldc_I4:
return (IsIntegerOrEnum(expectedType) || IsBoolean(expectedType)) ? expectedType : typeSystem.Int32;
if (IsBoolean(expectedType) && ((int)expr.Operand == 0 || (int)expr.Operand == 1))
return typeSystem.Boolean;
return IsIntegerOrEnum(expectedType) ? expectedType : typeSystem.Int32;
case ILCode.Ldc_I8:
return (IsIntegerOrEnum(expectedType)) ? expectedType : typeSystem.Int64;
case ILCode.Ldc_R4:
@ -308,6 +310,12 @@ namespace Decompiler @@ -308,6 +310,12 @@ namespace Decompiler
if (forceInferChildren)
InferTypeForExpression(expr.Arguments.Single(), typeSystem.Int32);
return new ArrayType((TypeReference)expr.Operand);
case ILCode.InitArray:
if (forceInferChildren) {
foreach (ILExpression arg in expr.Arguments)
InferTypeForExpression(arg, (TypeReference)expr.Operand);
}
return new ArrayType((TypeReference)expr.Operand);
case ILCode.Ldlen:
return typeSystem.Int32;
case ILCode.Ldelem_U1:
@ -522,7 +530,11 @@ namespace Decompiler @@ -522,7 +530,11 @@ namespace Decompiler
if (gp.Owner.GenericParameterType == GenericParameterType.Method) {
return ((GenericInstanceMethod)member).GenericArguments[gp.Position];
} else {
return ((GenericInstanceType)member.DeclaringType).GenericArguments[gp.Position];
if (member.DeclaringType is ArrayType) {
return ((ArrayType)member.DeclaringType).ElementType;
} else {
return ((GenericInstanceType)member.DeclaringType).GenericArguments[gp.Position];
}
}
}
return type;
@ -561,7 +573,7 @@ namespace Decompiler @@ -561,7 +573,7 @@ namespace Decompiler
} else {
left.ExpectedType = right.ExpectedType = TypeWithMoreInformation(leftPreferred, rightPreferred);
left.InferredType = DoInferTypeForExpression(left, left.ExpectedType);
right.InferredType = DoInferTypeForExpression(left, right.ExpectedType);
right.InferredType = DoInferTypeForExpression(right, right.ExpectedType);
return left.ExpectedType;
}
}

36
ICSharpCode.Decompiler/Tests/ArrayInitializers.cs

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
// 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 ArrayInitializers
{
// Helper methods used to ensure array initializers used within expressions work correctly
static void X(object a, object b)
{
}
static object Y()
{
return null;
}
public static void Array1()
{
X(Y(), new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
}
public static void Array2(int a, int b, int c)
{
X(Y(), new int[] { a, b, c });
}
public static void NestedArray(int a, int b, int c)
{
X(Y(), new int[][] {
new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
new int[] { a, b, c },
new int[] { 1, 2, 3, 4, 5, 6 }
});
}
}

59
ICSharpCode.Decompiler/Tests/BooleanConsumedAsInteger.il

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 4:0:0:0
}
.assembly BooleanConsumedAsInteger
{
.hash algorithm 0x00008004
.ver 1:0:0:0
}
.module BooleanConsumedAsInteger.exe
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000003 // ILONLY 32BITREQUIRED
.class private auto ansi beforefieldinit BooleanConsumedAsInteger.Program extends [mscorlib]System.Object
{
.method public hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 8
ret
}
.method public hidebysig static int32 ReturnBoolAsInt() cil managed
{
ldnull
ldnull
call bool [mscorlib] System.Object::Equals(object, object)
ret
}
.method public hidebysig static int32 BitwiseOperationOnBool() cil managed
{
ldnull
ldnull
call bool [mscorlib] System.Object::Equals(object, object)
ldc.i4 255
and
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
} // end of class StackTests.Program
// =============================================================

25
ICSharpCode.Decompiler/Tests/ExceptionHandling.cs

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
// 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 ExceptionHandling
{
public void MethodEndingWithEndFinally()
{
try {
throw null;
} finally {
Console.WriteLine();
}
}
public void MethodEndingWithRethrow()
{
try {
throw null;
} catch {
throw;
}
}
}

12
ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -67,6 +67,10 @@ @@ -67,6 +67,10 @@
<Compile Include="CodeSampleFileParser.cs" />
<Compile Include="CustomAttributes\CustomAttributeTests.cs" />
<Compile Include="DecompilerTestBase.cs" />
<Compile Include="ArrayInitializers.cs" />
<Compile Include="ExceptionHandling.cs" />
<Compile Include="MultidimensionalArray.cs" />
<Compile Include="TestRunner.cs" />
<None Include="Types\ValueTypes.cs" />
</ItemGroup>
@ -80,6 +84,12 @@ @@ -80,6 +84,12 @@
<Name>ICSharpCode.Decompiler</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Folder Include="StackTests" />
</ItemGroup>
<ItemGroup>
<None Include="BooleanConsumedAsInteger.il" />
<None Include="StackTests\StackTests.il" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
</Project>

32
ICSharpCode.Decompiler/Tests/MultidimensionalArray.cs

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
public static class MultidimensionalArray
{
internal class Generic<T, S> where T : new()
{
private T[,] a = new T[20, 20];
private S[,][] b = new S[20, 20][];
public T this[int i, int j]
{
get { return a[i, j]; }
set { 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;
}
}
public static int[][,] MakeArray()
{
return new int[10][,];
}
}

3
ILSpy/AboutPage.cs

@ -77,7 +77,7 @@ namespace ICSharpCode.ILSpy @@ -77,7 +77,7 @@ namespace ICSharpCode.ILSpy
delegate (Task<AvailableVersionInfo> task) {
try {
stackPanel.Children.Clear();
ShowAvailableVersion(latestAvailableVersion, stackPanel);
ShowAvailableVersion(task.Result, stackPanel);
} catch (Exception ex) {
AvalonEditTextOutput exceptionOutput = new AvalonEditTextOutput();
exceptionOutput.WriteLine(ex.ToString());
@ -128,6 +128,7 @@ namespace ICSharpCode.ILSpy @@ -128,6 +128,7 @@ namespace ICSharpCode.ILSpy
{
var tcs = new TaskCompletionSource<AvailableVersionInfo>();
WebClient wc = new WebClient();
wc.Proxy = new WebProxy() { UseDefaultCredentials = true };
wc.DownloadDataCompleted += delegate(object sender, DownloadDataCompletedEventArgs e) {
if (e.Error != null) {
tcs.SetException(e.Error);

109
ILSpy/CSharpLanguage.cs

@ -17,12 +17,15 @@ @@ -17,12 +17,15 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Resources;
using System.Threading.Tasks;
using System.Xaml;
using System.Xml;
using Decompiler;
using Decompiler.Transforms;
using ICSharpCode.Decompiler;
@ -115,7 +118,9 @@ namespace ICSharpCode.ILSpy @@ -115,7 +118,9 @@ namespace ICSharpCode.ILSpy
{
if (options.FullDecompilation) {
if (options.SaveAsProjectDirectory != null) {
var files = WriteFilesInProject(assembly, options);
HashSet<string> directories = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var files = WriteCodeFilesInProject(assembly, options, directories).ToList();
files.AddRange(WriteResourceFilesInProject(assembly, fileName, options, directories));
WriteProjectFile(new TextOutputWriter(output), files, assembly.MainModule);
} else {
foreach (TypeDefinition type in assembly.MainModule.Types) {
@ -130,7 +135,8 @@ namespace ICSharpCode.ILSpy @@ -130,7 +135,8 @@ namespace ICSharpCode.ILSpy
}
}
void WriteProjectFile(TextWriter writer, IEnumerable<string> files, ModuleDefinition module)
#region WriteProjectFile
void WriteProjectFile(TextWriter writer, IEnumerable<Tuple<string, string>> files, ModuleDefinition module)
{
const string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
string platformName;
@ -236,13 +242,15 @@ namespace ICSharpCode.ILSpy @@ -236,13 +242,15 @@ namespace ICSharpCode.ILSpy
}
w.WriteEndElement(); // </ItemGroup> (References)
w.WriteStartElement("ItemGroup"); // Code
foreach (string file in files.OrderBy(f => f, StringComparer.OrdinalIgnoreCase)) {
w.WriteStartElement("Compile");
w.WriteAttributeString("Include", file);
foreach (IGrouping<string, string> gr in (from f in files group f.Item2 by f.Item1 into g orderby g.Key select g)) {
w.WriteStartElement("ItemGroup");
foreach (string file in gr.OrderBy(f => f, StringComparer.OrdinalIgnoreCase)) {
w.WriteStartElement(gr.Key);
w.WriteAttributeString("Include", file);
w.WriteEndElement();
}
w.WriteEndElement();
}
w.WriteEndElement();
w.WriteStartElement("Import");
w.WriteAttributeString("Project", "$(MSBuildToolsPath)\\Microsoft.CSharp.targets");
@ -251,8 +259,10 @@ namespace ICSharpCode.ILSpy @@ -251,8 +259,10 @@ namespace ICSharpCode.ILSpy
w.WriteEndDocument();
}
}
#endregion
IEnumerable<string> WriteFilesInProject(AssemblyDefinition assembly, DecompilationOptions options)
#region WriteCodeFilesInProject
IEnumerable<Tuple<string, string>> WriteCodeFilesInProject(AssemblyDefinition assembly, DecompilationOptions options, HashSet<string> directories)
{
var files = assembly.MainModule.Types.Where(t => t.Name != "<Module>").GroupBy(
delegate (TypeDefinition type) {
@ -261,11 +271,11 @@ namespace ICSharpCode.ILSpy @@ -261,11 +271,11 @@ namespace ICSharpCode.ILSpy
return file;
} else {
string dir = TextView.DecompilerTextView.CleanUpName(type.Namespace);
Directory.CreateDirectory(Path.Combine(options.SaveAsProjectDirectory, dir));
if (directories.Add(dir))
Directory.CreateDirectory(Path.Combine(options.SaveAsProjectDirectory, dir));
return Path.Combine(dir, file);
}
}, StringComparer.OrdinalIgnoreCase).ToList();
AstMethodBodyBuilder.ClearUnhandledOpcodes();
Parallel.ForEach(
files,
@ -279,8 +289,83 @@ namespace ICSharpCode.ILSpy @@ -279,8 +289,83 @@ namespace ICSharpCode.ILSpy
}
});
AstMethodBodyBuilder.PrintNumberOfUnhandledOpcodes();
return files.Select(f => f.Key);
return files.Select(f => Tuple.Create("Compile", f.Key));
}
#endregion
#region WriteResourceFilesInProject
IEnumerable<Tuple<string, string>> WriteResourceFilesInProject(AssemblyDefinition assembly, string assemblyFileName, DecompilationOptions options, HashSet<string> directories)
{
AppDomain bamlDecompilerAppDomain = null;
try {
foreach (EmbeddedResource r in assembly.MainModule.Resources.OfType<EmbeddedResource>()) {
string fileName;
Stream s = r.GetResourceStream();
s.Position = 0;
if (r.Name.EndsWith(".g.resources", StringComparison.OrdinalIgnoreCase)) {
IEnumerable<DictionaryEntry> rs = null;
try {
rs = new ResourceSet(s).Cast<DictionaryEntry>();
} catch (ArgumentException) {
}
if (rs != null && rs.All(e => e.Value is Stream)) {
foreach (var pair in rs) {
fileName = Path.Combine(((string)pair.Key).Split('/').Select(p => TextView.DecompilerTextView.CleanUpName(p)).ToArray());
string dirName = Path.GetDirectoryName(fileName);
if (!string.IsNullOrEmpty(dirName) && directories.Add(dirName)) {
Directory.CreateDirectory(Path.Combine(options.SaveAsProjectDirectory, dirName));
}
Stream entryStream = (Stream)pair.Value;
entryStream.Position = 0;
if (fileName.EndsWith(".baml", StringComparison.OrdinalIgnoreCase)) {
MemoryStream ms = new MemoryStream();
entryStream.CopyTo(ms);
BamlDecompiler decompiler = TreeNodes.ResourceEntryNode.CreateBamlDecompilerInAppDomain(ref bamlDecompilerAppDomain, assemblyFileName);
string xaml = null;
try {
xaml = decompiler.DecompileBaml(ms, assemblyFileName);
} catch (XamlXmlWriterException) {} // ignore XAML writer exceptions
if (xaml != null) {
File.WriteAllText(Path.Combine(options.SaveAsProjectDirectory, Path.ChangeExtension(fileName, ".xaml")), xaml);
yield return Tuple.Create("Page", Path.ChangeExtension(fileName, ".xaml"));
continue;
}
}
using (FileStream fs = new FileStream(Path.Combine(options.SaveAsProjectDirectory, fileName), FileMode.Create, FileAccess.Write)) {
entryStream.CopyTo(fs);
}
yield return Tuple.Create("Resource", fileName);
}
continue;
}
}
fileName = GetFileNameForResource(r.Name, directories);
using (FileStream fs = new FileStream(Path.Combine(options.SaveAsProjectDirectory, fileName), FileMode.Create, FileAccess.Write)) {
s.CopyTo(fs);
}
yield return Tuple.Create("EmbeddedResource", fileName);
}
} finally {
if (bamlDecompilerAppDomain != null)
AppDomain.Unload(bamlDecompilerAppDomain);
}
}
string GetFileNameForResource(string fullName, HashSet<string> directories)
{
string[] splitName = fullName.Split('.');
string fileName = TextView.DecompilerTextView.CleanUpName(fullName);
for (int i = splitName.Length - 1; i > 0; i--) {
string ns = string.Join(".", splitName, 0, i);
if (directories.Contains(ns)) {
string name = string.Join(".", splitName, i, splitName.Length - i);
fileName = Path.Combine(ns, TextView.DecompilerTextView.CleanUpName(name));
break;
}
}
return fileName;
}
#endregion
AstBuilder CreateAstBuilder(DecompilationOptions options, TypeDefinition currentType)
{

4
ILSpy/ILAstLanguage.cs

@ -47,6 +47,10 @@ namespace ICSharpCode.ILSpy @@ -47,6 +47,10 @@ namespace ICSharpCode.ILSpy
public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options)
{
if (!method.HasBody) {
return;
}
ILAstBuilder astBuilder = new ILAstBuilder();
ILBlock ilMethod = new ILBlock();
ilMethod.Body = astBuilder.Build(method, inlineVariables);

6
ILSpy/Properties/AssemblyInfo.template.cs

@ -31,11 +31,11 @@ using System.Runtime.InteropServices; @@ -31,11 +31,11 @@ using System.Runtime.InteropServices;
internal static class RevisionClass
{
public const string Major = "0";
public const string Minor = "1";
public const string Major = "1";
public const string Minor = "0";
public const string Build = "0";
public const string Revision = "$INSERTREVISION$";
public const string VersionName = "alpha";
public const string VersionName = null;
public const string FullVersion = Major + "." + Minor + "." + Build + ".$INSERTREVISION$$INSERTBRANCHPOSTFIX$$INSERTVERSIONNAMEPOSTFIX$";
}

38
ILSpy/TextView/DecompilerTextView.cs

@ -264,21 +264,31 @@ namespace ICSharpCode.ILSpy.TextView @@ -264,21 +264,31 @@ namespace ICSharpCode.ILSpy.TextView
Thread thread = new Thread(new ThreadStart(
delegate {
try {
AvalonEditTextOutput textOutput = new AvalonEditTextOutput();
textOutput.LengthLimit = outputLengthLimit;
DecompileNodes(context, textOutput);
textOutput.PrepareDocument();
tcs.SetResult(textOutput);
#if DEBUG
} catch (AggregateException ex) {
tcs.SetException(ex);
} catch (OperationCanceledException ex) {
tcs.SetException(ex);
#else
} catch (Exception ex) {
tcs.SetException(ex);
#if DEBUG
if (Debugger.IsAttached) {
try {
AvalonEditTextOutput textOutput = new AvalonEditTextOutput();
textOutput.LengthLimit = outputLengthLimit;
DecompileNodes(context, textOutput);
textOutput.PrepareDocument();
tcs.SetResult(textOutput);
} catch (AggregateException ex) {
tcs.SetException(ex);
} catch (OperationCanceledException ex) {
tcs.SetException(ex);
}
} else
#endif
{
try {
AvalonEditTextOutput textOutput = new AvalonEditTextOutput();
textOutput.LengthLimit = outputLengthLimit;
DecompileNodes(context, textOutput);
textOutput.PrepareDocument();
tcs.SetResult(textOutput);
} catch (Exception ex) {
tcs.SetException(ex);
}
}
}));
thread.Start();

2
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -210,7 +210,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -210,7 +210,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (string.IsNullOrEmpty(language.ProjectFileExtension))
return false;
SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = DecompilerTextView.CleanUpName(assembly.ShortName);
dlg.FileName = DecompilerTextView.CleanUpName(assembly.ShortName) + language.ProjectFileExtension;
dlg.Filter = language.Name + " project|*" + language.ProjectFileExtension + "|" + language.Name + " single file|*" + language.FileExtension + "|All files|*.*";
if (dlg.ShowDialog() == true) {
DecompilationOptions options = new DecompilationOptions();

46
ILSpy/TreeNodes/ResourceEntryNode.cs

@ -86,24 +86,36 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -86,24 +86,36 @@ namespace ICSharpCode.ILSpy.TreeNodes
{
var asm = this.Ancestors().OfType<AssemblyTreeNode>().FirstOrDefault().LoadedAssembly;
// Construct and initialize settings for a second AppDomain.
AppDomainSetup bamlDecompilerAppDomainSetup = new AppDomainSetup();
bamlDecompilerAppDomainSetup.ApplicationBase = "file:///" + Path.GetDirectoryName(asm.FileName);
bamlDecompilerAppDomainSetup.DisallowBindingRedirects = false;
bamlDecompilerAppDomainSetup.DisallowCodeDownload = true;
bamlDecompilerAppDomainSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
AppDomain bamlDecompilerAppDomain = null;
try {
BamlDecompiler decompiler = CreateBamlDecompilerInAppDomain(ref bamlDecompilerAppDomain, asm.FileName);
MemoryStream bamlStream = new MemoryStream();
value.Position = 0;
value.CopyTo(bamlStream);
output.Write(decompiler.DecompileBaml(bamlStream, asm.FileName));
return true;
} finally {
if (bamlDecompilerAppDomain != null)
AppDomain.Unload(bamlDecompilerAppDomain);
}
}
public static BamlDecompiler CreateBamlDecompilerInAppDomain(ref AppDomain appDomain, string assemblyFileName)
{
if (appDomain == null) {
// Construct and initialize settings for a second AppDomain.
AppDomainSetup bamlDecompilerAppDomainSetup = new AppDomainSetup();
bamlDecompilerAppDomainSetup.ApplicationBase = "file:///" + Path.GetDirectoryName(assemblyFileName);
bamlDecompilerAppDomainSetup.DisallowBindingRedirects = false;
bamlDecompilerAppDomainSetup.DisallowCodeDownload = true;
bamlDecompilerAppDomainSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
// Create the second AppDomain.
AppDomain bamlDecompilerAppDomain = AppDomain.CreateDomain("BamlDecompiler AD", null, bamlDecompilerAppDomainSetup);
BamlDecompiler decompiler = (BamlDecompiler)bamlDecompilerAppDomain.CreateInstanceFromAndUnwrap(typeof(BamlDecompiler).Assembly.Location, typeof(BamlDecompiler).FullName);
MemoryStream bamlStream = new MemoryStream();
value.Position = 0;
value.CopyTo(bamlStream);
output.Write(decompiler.DecompileBaml(bamlStream, asm.FileName));
return true;
// Create the second AppDomain.
appDomain = AppDomain.CreateDomain("BamlDecompiler AD", null, bamlDecompilerAppDomainSetup);
}
return (BamlDecompiler)appDomain.CreateInstanceFromAndUnwrap(typeof(BamlDecompiler).Assembly.Location, typeof(BamlDecompiler).FullName);
}
public override bool Save(DecompilerTextView textView)

5
ILSpy/TreeNodes/ResourceListTreeNode.cs

@ -156,9 +156,10 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -156,9 +156,10 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (er != null) {
try {
Stream s = er.GetResourceStream();
s.Position = 0;
if (er.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) {
ResourceSet set = new ResourceSet(s);
foreach (DictionaryEntry entry in set.Cast<DictionaryEntry>().OrderBy(e => e.Key.ToString())) {
ResourceReader reader = new ResourceReader(s);
foreach (DictionaryEntry entry in reader.Cast<DictionaryEntry>().OrderBy(e => e.Key.ToString())) {
if (entry.Value is Stream)
Children.Add(new ResourceEntryNode(entry.Key.ToString(), (Stream)entry.Value));
}

74
NRefactory/ICSharpCode.NRefactory.Tests/CSharp/InsertParenthesesVisitorTests.cs

@ -159,7 +159,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -159,7 +159,7 @@ namespace ICSharpCode.NRefactory.CSharp
public void MethodCallOnQueryExpression()
{
Expression expr = new QueryExpression {
Clauses = new QueryClause[] {
Clauses = {
new QueryFromClause {
Identifier = "a",
Expression = new IdentifierExpression("b")
@ -178,7 +178,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -178,7 +178,7 @@ namespace ICSharpCode.NRefactory.CSharp
public void SumOfQueries()
{
QueryExpression query = new QueryExpression {
Clauses = new QueryClause[] {
Clauses = {
new QueryFromClause {
Identifier = "a",
Expression = new IdentifierExpression("b")
@ -206,7 +206,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -206,7 +206,7 @@ namespace ICSharpCode.NRefactory.CSharp
public void QueryInTypeTest()
{
Expression expr = new QueryExpression {
Clauses = new QueryClause[] {
Clauses = {
new QueryFromClause {
Identifier = "a",
Expression = new IdentifierExpression("b")
@ -252,5 +252,73 @@ namespace ICSharpCode.NRefactory.CSharp @@ -252,5 +252,73 @@ namespace ICSharpCode.NRefactory.CSharp
Assert.AreEqual("(++a)++", InsertRequired(expr));
Assert.AreEqual("(++a)++", InsertReadable(expr));
}
[Test]
public void Logical1()
{
Expression expr = new BinaryOperatorExpression(
new BinaryOperatorExpression(
new IdentifierExpression("a"),
BinaryOperatorType.ConditionalAnd,
new IdentifierExpression("b")
),
BinaryOperatorType.ConditionalAnd,
new IdentifierExpression("c")
);
Assert.AreEqual("a && b && c", InsertRequired(expr));
Assert.AreEqual("a && b && c", InsertReadable(expr));
}
[Test]
public void Logical2()
{
Expression expr = new BinaryOperatorExpression(
new IdentifierExpression("a"),
BinaryOperatorType.ConditionalAnd,
new BinaryOperatorExpression(
new IdentifierExpression("b"),
BinaryOperatorType.ConditionalAnd,
new IdentifierExpression("c")
)
);
Assert.AreEqual("a && (b && c)", InsertRequired(expr));
Assert.AreEqual("a && (b && c)", InsertReadable(expr));
}
[Test]
public void Logical3()
{
Expression expr = new BinaryOperatorExpression(
new IdentifierExpression("a"),
BinaryOperatorType.ConditionalOr,
new BinaryOperatorExpression(
new IdentifierExpression("b"),
BinaryOperatorType.ConditionalAnd,
new IdentifierExpression("c")
)
);
Assert.AreEqual("a || b && c", InsertRequired(expr));
Assert.AreEqual("a || (b && c)", InsertReadable(expr));
}
[Test]
public void Logical4()
{
Expression expr = new BinaryOperatorExpression(
new IdentifierExpression("a"),
BinaryOperatorType.ConditionalAnd,
new BinaryOperatorExpression(
new IdentifierExpression("b"),
BinaryOperatorType.ConditionalOr,
new IdentifierExpression("c")
)
);
Assert.AreEqual("a && (b || c)", InsertRequired(expr));
Assert.AreEqual("a && (b || c)", InsertReadable(expr));
}
}
}

17
NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/InsertParenthesesVisitor.cs

@ -186,7 +186,13 @@ namespace ICSharpCode.NRefactory.CSharp @@ -186,7 +186,13 @@ namespace ICSharpCode.NRefactory.CSharp
}
} else {
if (InsertParenthesesForReadability && precedence < Equality) {
ParenthesizeIfRequired(binaryOperatorExpression.Left, Equality);
// In readable mode, boost the priority of the left-hand side if the operator
// there isn't the same as the operator on this expression.
if (GetBinaryOperatorType(binaryOperatorExpression.Left) == binaryOperatorExpression.Operator) {
ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence);
} else {
ParenthesizeIfRequired(binaryOperatorExpression.Left, Equality);
}
ParenthesizeIfRequired(binaryOperatorExpression.Right, Equality);
} else {
// all other binary operators are left-associative
@ -197,6 +203,15 @@ namespace ICSharpCode.NRefactory.CSharp @@ -197,6 +203,15 @@ namespace ICSharpCode.NRefactory.CSharp
return base.VisitBinaryOperatorExpression(binaryOperatorExpression, data);
}
BinaryOperatorType? GetBinaryOperatorType(Expression expr)
{
BinaryOperatorExpression boe = expr as BinaryOperatorExpression;
if (boe != null)
return boe.Operator;
else
return null;
}
public override object VisitIsExpression(IsExpression isExpression, object data)
{
if (InsertParenthesesForReadability) {

16
NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs

@ -462,7 +462,17 @@ namespace ICSharpCode.NRefactory.CSharp @@ -462,7 +462,17 @@ namespace ICSharpCode.NRefactory.CSharp
else
style = BraceStyle.EndOfLine;
OpenBrace(style);
WriteCommaSeparatedList(arrayInitializerExpression.Children);
bool isFirst = true;
foreach (AstNode node in arrayInitializerExpression.Children) {
if (isFirst) {
isFirst = false;
} else {
Comma(node);
NewLine();
}
node.AcceptVisitor(this, null);
}
NewLine();
CloseBrace(style);
return EndNode(arrayInitializerExpression);
}
@ -566,7 +576,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -566,7 +576,7 @@ namespace ICSharpCode.NRefactory.CSharp
WriteKeyword("checked");
LPar();
Space(policy.WithinCheckedExpressionParantheses);
checkedExpression.AcceptVisitor(this, data);
checkedExpression.Expression.AcceptVisitor(this, data);
Space(policy.WithinCheckedExpressionParantheses);
RPar();
return EndNode(checkedExpression);
@ -933,7 +943,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -933,7 +943,7 @@ namespace ICSharpCode.NRefactory.CSharp
WriteKeyword("unchecked");
LPar();
Space(policy.WithinCheckedExpressionParantheses);
uncheckedExpression.AcceptVisitor(this, data);
uncheckedExpression.Expression.AcceptVisitor(this, data);
Space(policy.WithinCheckedExpressionParantheses);
RPar();
return EndNode(uncheckedExpression);

4
NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -340,10 +340,6 @@ @@ -340,10 +340,6 @@
<Compile Include="CSharp\Ast\Statements\DoWhileStatement.cs" />
<Compile Include="CSharp\Ast\Statements\YieldBreakStatement.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="CSharp\" />
<Folder Include="CSharp\Ast\PatternMatching" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Mono.Cecil\Mono.Cecil.csproj">
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>

Loading…
Cancel
Save