Browse Source

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

pull/191/merge
Eusebiu Marcu 16 years ago
parent
commit
ce96b7f6ca
  1. 87
      ICSharpCode.Decompiler/Ast/AstBuilder.cs
  2. 47
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  3. 13
      ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs
  4. 12
      ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
  5. 242
      ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  6. 11
      ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs
  7. 8
      ICSharpCode.Decompiler/CecilExtensions.cs
  8. 27
      ICSharpCode.Decompiler/DecompilerSettings.cs
  9. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  10. 103
      ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs
  11. 3
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  12. 122
      ICSharpCode.Decompiler/ILAst/ILInlining.cs
  13. 213
      ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs
  14. 167
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  15. 18
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  16. 6
      ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs
  17. 36
      ICSharpCode.Decompiler/Tests/ArrayInitializers.cs
  18. 3
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  19. 118
      ICSharpCode.Decompiler/Tests/InitializerTests.cs
  20. 60
      ICSharpCode.Decompiler/Tests/Switch.cs
  21. 23
      ICSharpCode.Decompiler/Tests/ValueTypes.cs
  22. 15
      ICSharpCode.Decompiler/Tests/YieldReturn.cs
  23. 43
      ILSpy/BamlDecompiler.cs
  24. 13
      ILSpy/CSharpLanguage.cs
  25. 2
      ILSpy/ILAstLanguage.cs
  26. 1
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/IPatternAstVisitor.cs
  27. 42
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/OptionalNode.cs
  28. 5
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs
  29. 29
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs
  30. 10
      NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs
  31. 1
      NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

87
ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -48,6 +48,11 @@ namespace ICSharpCode.Decompiler.Ast @@ -48,6 +48,11 @@ namespace ICSharpCode.Decompiler.Ast
return true;
if (settings.YieldReturn && YieldReturnDecompiler.IsCompilerGeneratorEnumerator(type))
return true;
} else if (type != null && type.IsCompilerGenerated()) {
if (type.Name.StartsWith("<PrivateImplementationDetails>", StringComparison.Ordinal))
return true;
if (type.Name.StartsWith("<>", StringComparison.Ordinal) && type.Name.Contains("AnonymousType"))
return true;
}
FieldDefinition field = member as FieldDefinition;
if (field != null && field.IsCompilerGenerated()) {
@ -752,43 +757,55 @@ namespace ICSharpCode.Decompiler.Ast @@ -752,43 +757,55 @@ namespace ICSharpCode.Decompiler.Ast
ConvertCustomAttributes(astProp, propDef);
return astProp;
}
CustomEventDeclaration CreateEvent(EventDefinition eventDef)
AttributedNode CreateEvent(EventDefinition eventDef)
{
CustomEventDeclaration astEvent = new CustomEventDeclaration();
ConvertCustomAttributes(astEvent, eventDef);
astEvent.AddAnnotation(eventDef);
astEvent.Name = CleanName(eventDef.Name);
astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef);
if (eventDef.AddMethod == null || !eventDef.AddMethod.HasOverrides)
astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod);
else
astEvent.PrivateImplementationType = ConvertType(eventDef.AddMethod.Overrides.First().DeclaringType);
if (eventDef.AddMethod != null) {
// Create mapping - used in debugger
MethodMapping methodMapping = eventDef.AddMethod.CreateCodeMapping(CSharpCodeMapping.SourceCodeMappings);
astEvent.AddAccessor = new Accessor {
Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.AddMethod, context)
}.WithAnnotation(eventDef.AddMethod);
ConvertAttributes(astEvent.AddAccessor, eventDef.AddMethod);
if (methodMapping != null)
astEvent.AddAccessor.AddAnnotation(methodMapping);
}
if (eventDef.RemoveMethod != null) {
// Create mapping - used in debugger
MethodMapping methodMapping = eventDef.RemoveMethod.CreateCodeMapping(CSharpCodeMapping.SourceCodeMappings);
astEvent.RemoveAccessor = new Accessor {
Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.RemoveMethod, context)
}.WithAnnotation(eventDef.RemoveMethod);
ConvertAttributes(astEvent.RemoveAccessor, eventDef.RemoveMethod);
if (methodMapping != null)
astEvent.RemoveAccessor.AddAnnotation(methodMapping);
if (eventDef.AddMethod != null && eventDef.AddMethod.IsAbstract) {
// An abstract event cannot be custom
EventDeclaration astEvent = new EventDeclaration();
ConvertCustomAttributes(astEvent, eventDef);
astEvent.AddAnnotation(eventDef);
astEvent.Variables.Add(new VariableInitializer(CleanName(eventDef.Name)));
astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef);
if (!eventDef.DeclaringType.IsInterface)
astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod);
return astEvent;
} else {
CustomEventDeclaration astEvent = new CustomEventDeclaration();
ConvertCustomAttributes(astEvent, eventDef);
astEvent.AddAnnotation(eventDef);
astEvent.Name = CleanName(eventDef.Name);
astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef);
if (eventDef.AddMethod == null || !eventDef.AddMethod.HasOverrides)
astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod);
else
astEvent.PrivateImplementationType = ConvertType(eventDef.AddMethod.Overrides.First().DeclaringType);
if (eventDef.AddMethod != null) {
// Create mapping - used in debugger
MethodMapping methodMapping = eventDef.AddMethod.CreateCodeMapping(CSharpCodeMapping.SourceCodeMappings);
astEvent.AddAccessor = new Accessor {
Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.AddMethod, context)
}.WithAnnotation(eventDef.AddMethod);
ConvertAttributes(astEvent.AddAccessor, eventDef.AddMethod);
if (methodMapping != null)
astEvent.AddAccessor.AddAnnotation(methodMapping);
}
if (eventDef.RemoveMethod != null) {
// Create mapping - used in debugger
MethodMapping methodMapping = eventDef.RemoveMethod.CreateCodeMapping(CSharpCodeMapping.SourceCodeMappings);
astEvent.RemoveAccessor = new Accessor {
Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.RemoveMethod, context)
}.WithAnnotation(eventDef.RemoveMethod);
ConvertAttributes(astEvent.RemoveAccessor, eventDef.RemoveMethod);
if (methodMapping != null)
astEvent.RemoveAccessor.AddAnnotation(methodMapping);
}
return astEvent;
}
return astEvent;
}
FieldDeclaration CreateField(FieldDefinition fieldDef)

47
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -408,9 +408,11 @@ namespace ICSharpCode.Decompiler.Ast @@ -408,9 +408,11 @@ namespace ICSharpCode.Decompiler.Ast
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 });
return new AssignmentExpression(((DirectionExpression)args[0]).Expression.Detach(), MakeDefaultValue((TypeReference)operand));
else
return InlineAssembly(byteCode, args);
case ILCode.DefaultValue:
return MakeDefaultValue((TypeReference)operand);
case ILCode.Jmp: return InlineAssembly(byteCode, args);
case ILCode.Ldarg: {
if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) {
@ -433,7 +435,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -433,7 +435,7 @@ namespace ICSharpCode.Decompiler.Ast
return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand));
}
case ILCode.Ldc_I4: return AstBuilder.MakePrimitive((int)operand, byteCode.InferredType);
case ILCode.Ldc_I8:
case ILCode.Ldc_I8: return AstBuilder.MakePrimitive((long)operand, byteCode.InferredType);
case ILCode.Ldc_R4:
case ILCode.Ldc_R8:
case ILCode.Ldc_Decimal:
@ -523,10 +525,49 @@ namespace ICSharpCode.Decompiler.Ast @@ -523,10 +525,49 @@ namespace ICSharpCode.Decompiler.Ast
return new Ast.YieldBreakStatement();
case ILCode.YieldReturn:
return new Ast.YieldStatement { Expression = arg1 };
case ILCode.InitCollection: {
ObjectCreateExpression oce = (ObjectCreateExpression)arg1;
oce.Initializer = new ArrayInitializerExpression();
for (int i = 1; i < args.Count; i++) {
ArrayInitializerExpression aie = args[i] as ArrayInitializerExpression;
if (aie != null && aie.Elements.Count == 1)
oce.Initializer.Elements.Add(aie.Elements.Single().Detach());
else
oce.Initializer.Elements.Add(args[i]);
}
return oce;
}
case ILCode.InitCollectionAddMethod: {
var collectionInit = new ArrayInitializerExpression();
collectionInit.Elements.AddRange(args);
return collectionInit;
}
default: throw new Exception("Unknown OpCode: " + byteCode.Code);
}
}
Expression MakeDefaultValue(TypeReference type)
{
TypeDefinition typeDef = type.Resolve();
if (typeDef != null) {
if (TypeAnalysis.IsIntegerOrEnum(typeDef))
return AstBuilder.MakePrimitive(0, typeDef);
else if (!typeDef.IsValueType)
return new NullReferenceExpression();
switch (typeDef.FullName) {
case "System.Nullable`1":
return new NullReferenceExpression();
case "System.Single":
return new PrimitiveExpression(0f);
case "System.Double":
return new PrimitiveExpression(0.0);
case "System.Decimal":
return new PrimitiveExpression(0m);
}
}
return new DefaultValueExpression { Type = AstBuilder.ConvertType(type) };
}
static AstNode TransformCall(bool isVirtual, object operand, MethodDefinition methodDef, List<Ast.Expression> args)
{
Cecil.MethodReference cecilMethod = ((MethodReference)operand);
@ -619,7 +660,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -619,7 +660,7 @@ namespace ICSharpCode.Decompiler.Ast
}
}
// Default invocation
AdjustArgumentsForMethodCall(cecilMethod, methodArgs);
AdjustArgumentsForMethodCall(cecilMethodDef ?? cecilMethod, methodArgs);
return target.Invoke(cecilMethod.Name, ConvertTypeArguments(cecilMethod), methodArgs).WithAnnotation(cecilMethod);
}

13
ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs

@ -50,15 +50,18 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -50,15 +50,18 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
};
static readonly AstNode thisCallPattern = new ExpressionStatement(new ThisReferenceExpression().Invoke(".ctor", new Repeat(new AnyNode())));
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{
var instanceCtors = typeDeclaration.Members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
if (instanceCtors.Length > 0 && typeDeclaration.ClassType == NRefactory.TypeSystem.ClassType.Class) {
var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => thisCallPattern.Match(ctor.Body.Statements.FirstOrDefault()) == null).ToArray();
if (instanceCtorsNotChainingWithThis.Length > 0 && typeDeclaration.ClassType == NRefactory.TypeSystem.ClassType.Class) {
// Recognize field initializers:
// Convert first statement in all ctors (if all ctors have the same statement) into a field initializer.
bool allSame;
do {
Match m = fieldInitializerPattern.Match(instanceCtors[0].Body.FirstOrDefault());
Match m = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[0].Body.FirstOrDefault());
if (m == null)
break;
@ -70,12 +73,12 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -70,12 +73,12 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
break;
allSame = true;
for (int i = 1; i < instanceCtors.Length; i++) {
if (instanceCtors[0].Body.First().Match(instanceCtors[i].Body.FirstOrDefault()) == null)
for (int i = 1; i < instanceCtorsNotChainingWithThis.Length; i++) {
if (instanceCtors[0].Body.First().Match(instanceCtorsNotChainingWithThis[i].Body.FirstOrDefault()) == null)
allSame = false;
}
if (allSame) {
foreach (var ctor in instanceCtors)
foreach (var ctor in instanceCtorsNotChainingWithThis)
ctor.Body.First().Remove();
fieldOrEventDecl.GetChildrenByRole(AstNode.Roles.Variable).Single().Initializer = m.Get<Expression>("initializer").Single().Detach();
}

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

@ -108,8 +108,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -108,8 +108,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
if (!context.Settings.AnonymousMethods)
return false; // anonymous method decompilation is disabled
// Anonymous methods are defined in the same assembly, so there's no need to Resolve().
MethodDefinition method = methodRef as MethodDefinition;
// Anonymous methods are defined in the same assembly
MethodDefinition method = methodRef.ResolveWithinSameModule();
if (!IsAnonymousMethod(context, method))
return false;
@ -173,11 +173,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -173,11 +173,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
if (stmt.Variables.Count() != 1)
continue;
var variable = stmt.Variables.Single();
TypeDefinition type = stmt.Type.Annotation<TypeDefinition>();
TypeDefinition type = stmt.Type.Annotation<TypeReference>().ResolveWithinSameModule();
if (!IsPotentialClosure(context, type))
continue;
ObjectCreateExpression oce = variable.Initializer as ObjectCreateExpression;
if (oce == null || oce.Type.Annotation<TypeReference>() != type || oce.Arguments.Any() || !oce.Initializer.IsNull)
if (oce == null || oce.Type.Annotation<TypeReference>().ResolveWithinSameModule() != type || oce.Arguments.Any() || !oce.Initializer.IsNull)
continue;
// Looks like we found a display class creation. Now let's verify that the variable is used only for field accesses:
bool ok = true;
@ -222,7 +222,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -222,7 +222,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
isParameter = parameterOccurrances.Count(c => c == param) == 1;
}
if (isParameter) {
dict[m.Get<MemberReferenceExpression>("left").Single().Annotation<FieldReference>()] = right;
dict[m.Get<MemberReferenceExpression>("left").Single().Annotation<FieldReference>().ResolveWithinSameModule()] = right;
cur.Remove();
} else {
break;
@ -247,7 +247,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -247,7 +247,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
if (identExpr.Identifier == variable.Name) {
MemberReferenceExpression mre = (MemberReferenceExpression)identExpr.Parent;
AstNode replacement;
if (dict.TryGetValue(mre.Annotation<FieldReference>(), out replacement)) {
if (dict.TryGetValue(mre.Annotation<FieldReference>().ResolveWithinSameModule(), out replacement)) {
mre.ReplaceWith(replacement.Clone());
}
}

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

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
// 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 ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.PatternMatching;
@ -31,6 +32,10 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -31,6 +32,10 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
TransformForeach(compilationUnit);
TransformFor(compilationUnit);
TransformDoWhile(compilationUnit);
if (context.Settings.LockStatement)
TransformLock(compilationUnit);
if (context.Settings.SwitchStatementOnString)
TransformSwitchOnString(compilationUnit);
if (context.Settings.AutomaticProperties)
TransformAutomaticProperties(compilationUnit);
if (context.Settings.AutomaticEvents)
@ -88,7 +93,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -88,7 +93,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public void TransformUsings(AstNode compilationUnit)
{
foreach (AstNode node in compilationUnit.Descendants.ToArray()) {
foreach (AstNode node in compilationUnit.Descendants.OfType<VariableDeclarationStatement>().ToArray()) {
Match m1 = variableDeclPattern.Match(node);
if (m1 == null) continue;
AstNode tryCatch = node.NextSibling;
@ -179,7 +184,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -179,7 +184,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public void TransformForeach(AstNode compilationUnit)
{
foreach (AstNode node in compilationUnit.Descendants.ToArray()) {
foreach (AstNode node in compilationUnit.Descendants.OfType<UsingStatement>().ToArray()) {
Match m = foreachPattern.Match(node);
if (m == null)
continue;
@ -226,7 +231,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -226,7 +231,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public void TransformFor(AstNode compilationUnit)
{
foreach (AstNode node in compilationUnit.Descendants.ToArray()) {
foreach (AstNode node in compilationUnit.Descendants.OfType<VariableDeclarationStatement>().ToArray()) {
Match m1 = variableDeclPattern.Match(node);
if (m1 == null) continue;
AstNode next = node.NextSibling;
@ -300,6 +305,201 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -300,6 +305,201 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
#endregion
#region lock
static readonly AstNode lockFlagInitPattern = new VariableDeclarationStatement {
Type = new PrimitiveType("bool"),
Variables = {
new NamedNode(
"variable",
new VariableInitializer {
Initializer = new PrimitiveExpression(false)
}
)
}};
static readonly AstNode lockTryCatchPattern = new TryCatchStatement {
TryBlock = new BlockStatement {
new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke(
"Enter", new AnyNode("enter"),
new DirectionExpression {
FieldDirection = FieldDirection.Ref,
Expression = new NamedNode("flag", new IdentifierExpression())
}),
new Repeat(new AnyNode()).ToStatement()
},
FinallyBlock = new BlockStatement {
new IfElseStatement {
Condition = new Backreference("flag"),
TrueStatement = new BlockStatement {
new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Exit", new NamedNode("exit", new IdentifierExpression()))
}
}
}};
public void TransformLock(AstNode compilationUnit)
{
foreach (AstNode node in compilationUnit.Descendants.OfType<VariableDeclarationStatement>().ToArray()) {
Match m1 = lockFlagInitPattern.Match(node);
if (m1 == null) continue;
AstNode tryCatch = node.NextSibling;
while (simpleVariableDefinition.Match(tryCatch) != null)
tryCatch = tryCatch.NextSibling;
Match m2 = lockTryCatchPattern.Match(tryCatch);
if (m2 == null) continue;
if (m1.Get<VariableInitializer>("variable").Single().Name == m2.Get<IdentifierExpression>("flag").Single().Identifier) {
Expression enter = m2.Get<Expression>("enter").Single();
IdentifierExpression exit = m2.Get<IdentifierExpression>("exit").Single();
if (exit.Match(enter) == null) {
// If exit and enter are not the same, then enter must be "exit = ..."
AssignmentExpression assign = enter as AssignmentExpression;
if (assign == null)
continue;
if (exit.Match(assign.Left) == null)
continue;
enter = assign.Right;
// Remove 'exit' variable:
bool ok = false;
for (AstNode tmp = node.NextSibling; tmp != tryCatch; tmp = tmp.NextSibling) {
VariableDeclarationStatement v = (VariableDeclarationStatement)tmp;
if (v.Variables.Single().Name == exit.Identifier) {
ok = true;
v.Remove();
break;
}
}
if (!ok)
continue;
}
// transform the code into a lock statement:
LockStatement l = new LockStatement();
l.Expression = enter.Detach();
l.EmbeddedStatement = ((TryCatchStatement)tryCatch).TryBlock.Detach();
((BlockStatement)l.EmbeddedStatement).Statements.First().Remove(); // Remove 'Enter()' call
tryCatch.ReplaceWith(l);
node.Remove(); // remove flag variable
}
}
}
#endregion
#region switch on strings
static readonly IfElseStatement switchOnStringPattern = new IfElseStatement {
Condition = new BinaryOperatorExpression {
Left = new AnyNode("switchExpr"),
Operator = BinaryOperatorType.InEquality,
Right = new NullReferenceExpression()
},
TrueStatement = new BlockStatement {
new IfElseStatement {
Condition = new BinaryOperatorExpression {
Left = new AnyNode("cachedDict"),
Operator = BinaryOperatorType.Equality,
Right = new NullReferenceExpression()
},
TrueStatement = new AnyNode("dictCreation")
},
new VariableDeclarationStatement {
Type = new PrimitiveType("int"),
Variables = { new NamedNode("intVar", new VariableInitializer()) }
},
new IfElseStatement {
Condition = new Backreference("cachedDict").ToExpression().Invoke(
"TryGetValue",
new NamedNode("switchVar", new IdentifierExpression()),
new DirectionExpression {
FieldDirection = FieldDirection.Out,
Expression = new IdentifierExpressionBackreference("intVar")
}),
TrueStatement = new BlockStatement {
Statements = {
new NamedNode(
"switch", new SwitchStatement {
Expression = new IdentifierExpressionBackreference("intVar"),
SwitchSections = { new Repeat(new AnyNode()) }
})
}
}
},
new Repeat(new AnyNode("nonNullDefaultStmt")).ToStatement()
},
FalseStatement = new OptionalNode("nullStmt", new BlockStatement { Statements = { new Repeat(new AnyNode()) } })
};
public void TransformSwitchOnString(AstNode compilationUnit)
{
foreach (AstNode node in compilationUnit.Descendants.OfType<IfElseStatement>().ToArray()) {
Match m = switchOnStringPattern.Match(node);
if (m == null)
continue;
if (m.Has("nonNullDefaultStmt") && !m.Has("nullStmt"))
continue;
// switchVar must be the same as switchExpr; or switchExpr must be an assignment and switchVar the left side of that assignment
if (m.Get("switchVar").Single().Match(m.Get("switchExpr").Single()) == null) {
AssignmentExpression assign = m.Get("switchExpr").Single() as AssignmentExpression;
if (m.Get("switchVar").Single().Match(assign.Left) == null)
continue;
}
FieldReference cachedDictField = m.Get("cachedDict").Single().Annotation<FieldReference>();
if (cachedDictField == null || !cachedDictField.DeclaringType.Name.StartsWith("<PrivateImplementationDetails>", StringComparison.Ordinal))
continue;
List<Statement> dictCreation = m.Get<BlockStatement>("dictCreation").Single().Statements.ToList();
List<KeyValuePair<string, int>> dict = BuildDictionary(dictCreation);
SwitchStatement sw = m.Get<SwitchStatement>("switch").Single();
sw.Expression = m.Get<Expression>("switchExpr").Single().Detach();
foreach (SwitchSection section in sw.SwitchSections) {
List<CaseLabel> labels = section.CaseLabels.ToList();
section.CaseLabels.Clear();
foreach (CaseLabel label in labels) {
PrimitiveExpression expr = label.Expression as PrimitiveExpression;
if (expr == null || !(expr.Value is int))
continue;
int val = (int)expr.Value;
foreach (var pair in dict) {
if (pair.Value == val)
section.CaseLabels.Add(new CaseLabel { Expression = new PrimitiveExpression(pair.Key) });
}
}
}
if (m.Has("nullStmt")) {
SwitchSection section = new SwitchSection();
section.CaseLabels.Add(new CaseLabel { Expression = new NullReferenceExpression() });
BlockStatement block = m.Get<BlockStatement>("nullStmt").Single();
block.Statements.Add(new BreakStatement());
section.Statements.Add(block.Detach());
sw.SwitchSections.Add(section);
if (m.Has("nonNullDefaultStmt")) {
section = new SwitchSection();
section.CaseLabels.Add(new CaseLabel());
block = new BlockStatement();
block.Statements.AddRange(m.Get<Statement>("nonNullDefaultStmt").Select(s => s.Detach()));
block.Add(new BreakStatement());
section.Statements.Add(block);
sw.SwitchSections.Add(section);
}
}
node.ReplaceWith(sw);
}
}
List<KeyValuePair<string, int>> BuildDictionary(List<Statement> dictCreation)
{
List<KeyValuePair<string, int>> dict = new List<KeyValuePair<string, int>>();
for (int i = 0; i < dictCreation.Count; i++) {
ExpressionStatement es = dictCreation[i] as ExpressionStatement;
if (es == null)
continue;
InvocationExpression ie = es.Expression as InvocationExpression;
if (ie == null)
continue;
PrimitiveExpression arg1 = ie.Arguments.ElementAtOrDefault(0) as PrimitiveExpression;
PrimitiveExpression arg2 = ie.Arguments.ElementAtOrDefault(1) as PrimitiveExpression;
if (arg1 != null && arg2 != null && arg1.Value is string && arg2.Value is int)
dict.Add(new KeyValuePair<string, int>((string)arg1.Value, (int)arg2.Value));
}
return dict;
}
#endregion
#region Automatic Properties
static readonly PropertyDeclaration automaticPropertyPattern = new PropertyDeclaration {
Attributes = { new Repeat(new AnyNode()) },
@ -361,7 +561,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -361,7 +561,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
#endregion
#region Automatic Events
Accessor automaticEventPatternV4 = new Accessor {
static readonly Accessor automaticEventPatternV4 = new Accessor {
Body = new BlockStatement {
new VariableDeclarationStatement {
Type = new AnyNode("type"),
@ -391,7 +591,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -391,7 +591,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}},
new AssignmentExpression {
Left = new IdentifierExpressionBackreference("var1"),
Right = new AnyNode("Interlocked").ToType().Invoke(
Right = new TypePattern(typeof(System.Threading.Interlocked)).ToType().Invoke(
"CompareExchange",
new AstType[] { new Backreference("type") }, // type argument
new Expression[] { // arguments
@ -419,10 +619,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -419,10 +619,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
var combineMethod = m.Get("delegateCombine").Single().Parent.Annotation<MethodReference>();
if (combineMethod == null || combineMethod.Name != (isAddAccessor ? "Combine" : "Remove"))
return false;
if (combineMethod.DeclaringType.FullName != "System.Delegate")
return false;
var ice = m.Get("Interlocked").Single().Annotation<TypeReference>();
return ice != null && ice.FullName == "System.Threading.Interlocked";
return combineMethod.DeclaringType.FullName == "System.Delegate";
}
void TransformAutomaticEvents(AstNode compilationUnit)
@ -454,5 +651,32 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -454,5 +651,32 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
}
#endregion
#region Pattern Matching Helpers
sealed class TypePattern : Pattern
{
readonly string ns;
readonly string name;
public TypePattern(Type type)
{
this.ns = type.Namespace;
this.name = type.Name;
}
protected override bool DoMatch(AstNode other, Match match)
{
if (other == null)
return false;
TypeReference tr = other.Annotation<TypeReference>();
return tr != null && tr.Namespace == ns && tr.Name == name;
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
throw new NotImplementedException();
}
}
#endregion
}
}

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

@ -205,9 +205,16 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -205,9 +205,16 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return null;
}
bool IsWithoutSideEffects(Expression left)
static bool IsWithoutSideEffects(Expression left)
{
return left is IdentifierExpression; // TODO
if (left is ThisReferenceExpression)
return true;
if (left is IdentifierExpression)
return true;
MemberReferenceExpression mre = left as MemberReferenceExpression;
if (mre != null)
return mre.Annotation<FieldReference>() != null && IsWithoutSideEffects(mre.Target);
return false;
}
void IAstTransform.Run(AstNode node)

8
ICSharpCode.Decompiler/CecilExtensions.cs

@ -159,6 +159,14 @@ namespace ICSharpCode.Decompiler @@ -159,6 +159,14 @@ namespace ICSharpCode.Decompiler
return accessorMethods;
}
public static TypeDefinition ResolveWithinSameModule(this TypeReference type)
{
if (type != null && type.GetElementType().Module == type.Module)
return type.Resolve();
else
return null;
}
public static FieldDefinition ResolveWithinSameModule(this FieldReference field)
{
if (field != null && field.DeclaringType.GetElementType().Module == field.Module)

27
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -101,6 +101,33 @@ namespace ICSharpCode.Decompiler @@ -101,6 +101,33 @@ namespace ICSharpCode.Decompiler
}
}
bool lockStatement = true;
/// <summary>
/// Decompile lock statements.
/// </summary>
public bool LockStatement {
get { return lockStatement; }
set {
if (lockStatement != value) {
lockStatement = value;
OnPropertyChanged("LockStatement");
}
}
}
bool switchStatementOnString = true;
public bool SwitchStatementOnString {
get { return switchStatementOnString; }
set {
if (switchStatementOnString != value) {
switchStatementOnString = value;
OnPropertyChanged("SwitchStatementOnString");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -89,7 +89,7 @@ @@ -89,7 +89,7 @@
<Compile Include="FlowAnalysis\SsaVariable.cs" />
<Compile Include="FlowAnalysis\TransformToSsa.cs" />
<Compile Include="GraphVizGraph.cs" />
<Compile Include="ILAst\ArrayInitializers.cs" />
<Compile Include="ILAst\InitializerPeepholeTransforms.cs" />
<Compile Include="ILAst\DefaultDictionary.cs" />
<Compile Include="ILAst\GotoRemoval.cs" />
<Compile Include="ILAst\ILAstBuilder.cs" />

103
ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs

@ -1,103 +0,0 @@ @@ -1,103 +0,0 @@
// 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 ICSharpCode.Decompiler.ILAst
{
/// <summary>
/// IL AST transformation that introduces array initializers.
/// </summary>
public static class ArrayInitializers
{
public static PeepholeTransform Transform(ILBlock method)
{
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));
return delegate(ILBlock block, ref int i) {
if (!newArrPattern.Match(block.Body[i]))
return;
ILExpression newArrInst = ((ILExpression)block.Body[i]).Arguments[0];
int arrayLength = (int)newArrInst.Arguments[0].Operand;
if (arrayLength == 0)
return;
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 -= new ILInlining(method).InlineInto(block, i + 1) - 1;
}
return;
}
}
if (i + 1 + arrayLength > block.Body.Count)
return;
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 -= new ILInlining(method).InlineInto(block, i + 1) - 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;
}
}
}

3
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -259,12 +259,15 @@ namespace ICSharpCode.Decompiler.ILAst @@ -259,12 +259,15 @@ namespace ICSharpCode.Decompiler.ILAst
LogicAnd,
LogicOr,
InitArray, // Array Initializer
InitCollection, // Collection Initializer: first arg is newobj, remaining args are InitCollectionAddMethod method calls
InitCollectionAddMethod,
TernaryOp, // ?:
LoopOrSwitchBreak,
LoopContinue,
Ldc_Decimal,
YieldBreak,
YieldReturn,
DefaultValue, // default(T)
Pattern // used for ILAst pattern nodes
}

122
ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -15,8 +15,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -15,8 +15,8 @@ namespace ICSharpCode.Decompiler.ILAst
public class ILInlining
{
readonly ILBlock method;
Dictionary<ILVariable, int> numStloc = new Dictionary<ILVariable, int>();
Dictionary<ILVariable, int> numLdloc = new Dictionary<ILVariable, int>();
internal Dictionary<ILVariable, int> numStloc = new Dictionary<ILVariable, int>();
internal Dictionary<ILVariable, int> numLdloc = new Dictionary<ILVariable, int>();
Dictionary<ILVariable, int> numLdloca = new Dictionary<ILVariable, int>();
Dictionary<ParameterDefinition, int> numStarg = new Dictionary<ParameterDefinition, int>();
@ -25,6 +25,17 @@ namespace ICSharpCode.Decompiler.ILAst @@ -25,6 +25,17 @@ namespace ICSharpCode.Decompiler.ILAst
public ILInlining(ILBlock method)
{
this.method = method;
AnalyzeMethod();
}
void AnalyzeMethod()
{
numStloc.Clear();
numLdloc.Clear();
numLdloca.Clear();
numStarg.Clear();
numLdarga.Clear();
// Analyse the whole method
foreach(ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
ILVariable locVar = expr.Operand as ILVariable;
@ -64,7 +75,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -64,7 +75,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression nextExpr = body[i + 1] as ILExpression;
ILVariable locVar;
ILExpression expr;
if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineIfPossible(block, i, aggressive: false)) {
if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(block, i, aggressive: false)) {
i = Math.Max(0, i - 1); // Go back one step
} else {
i++;
@ -76,7 +87,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -76,7 +87,7 @@ namespace ICSharpCode.Decompiler.ILAst
/// Inlines instructions before pos into block.Body[pos].
/// </summary>
/// <returns>The number of instructions that were inlined.</returns>
public int InlineInto(ILBlock block, int pos)
public int InlineInto(ILBlock block, int pos, bool aggressive)
{
if (pos >= block.Body.Count)
return 0;
@ -85,7 +96,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -85,7 +96,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression expr = block.Body[pos] as ILExpression;
if (expr == null || expr.Code != ILCode.Stloc)
break;
if (InlineIfPossible(block, pos))
if (InlineOneIfPossible(block, pos, aggressive))
count++;
else
break;
@ -93,10 +104,26 @@ namespace ICSharpCode.Decompiler.ILAst @@ -93,10 +104,26 @@ namespace ICSharpCode.Decompiler.ILAst
return count;
}
/// <summary>
/// Aggressively inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
/// If inlining was possible; we will continue to inline (non-aggressively) into the the combined instruction.
/// </summary>
/// <remarks>
/// After the operation, pos will point to the new combined instruction.
/// </remarks>
public bool InlineIfPossible(ILBlock block, ref int pos)
{
if (InlineOneIfPossible(block, pos, true)) {
pos -= InlineInto(block, pos, false);
return true;
}
return false;
}
/// <summary>
/// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
/// </summary>
public bool InlineIfPossible(ILBlock block, int pos, bool aggressive = true)
public bool InlineOneIfPossible(ILBlock block, int pos, bool aggressive)
{
ILVariable v;
ILExpression inlinedExpression;
@ -120,6 +147,12 @@ namespace ICSharpCode.Decompiler.ILAst @@ -120,6 +147,12 @@ namespace ICSharpCode.Decompiler.ILAst
// ensure the variable is accessed only a single time
if (!(numStloc.GetOrDefault(v) == 1 && numLdloc.GetOrDefault(v) == 1 && numLdloca.GetOrDefault(v) == 0))
return false;
if (next is ILCondition)
next = ((ILCondition)next).Condition;
else if (next is ILWhileLoop)
next = ((ILWhileLoop)next).Condition;
ILExpression parent;
int pos;
if (FindLoadInNext(next as ILExpression, v, inlinedExpression, out parent, out pos) == true) {
@ -150,6 +183,16 @@ namespace ICSharpCode.Decompiler.ILAst @@ -150,6 +183,16 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
/// <summary>
/// Gets whether 'expressionBeingMoved' can be inlined into 'expr'.
/// </summary>
public bool CanInlineInto(ILExpression expr, ILVariable v, ILExpression expressionBeingMoved)
{
ILExpression parent;
int pos;
return FindLoadInNext(expr, v, expressionBeingMoved, out parent, out pos) == true;
}
/// <summary>
/// Finds the position to inline to.
/// </summary>
@ -177,6 +220,17 @@ namespace ICSharpCode.Decompiler.ILAst @@ -177,6 +220,17 @@ namespace ICSharpCode.Decompiler.ILAst
if (r != null)
return r;
}
if (IsSafeForInlineOver(expr, expressionBeingMoved))
return null; // continue searching
else
return false; // abort, inlining not possible
}
/// <summary>
/// Determines whether it is save to move 'expressionBeingMoved' past 'expr'
/// </summary>
bool IsSafeForInlineOver(ILExpression expr, ILExpression expressionBeingMoved)
{
switch (expr.Code) {
case ILCode.Ldloc:
ILVariable loadedVar = (ILVariable)expr.Operand;
@ -188,9 +242,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -188,9 +242,8 @@ namespace ICSharpCode.Decompiler.ILAst
if (potentialStore.Code == ILCode.Stloc && potentialStore.Operand == loadedVar)
return false;
}
// the expression is loading a non-forbidden variable:
// we're allowed to continue searching
return null;
// the expression is loading a non-forbidden variable
return true;
case ILCode.Ldarg:
// Also try moving over ldarg instructions - this is necessary because an earlier copy propagation
// step might have introduced ldarg in place of an ldloc that would be skipped.
@ -201,12 +254,18 @@ namespace ICSharpCode.Decompiler.ILAst @@ -201,12 +254,18 @@ namespace ICSharpCode.Decompiler.ILAst
if (potentialStore.Code == ILCode.Starg && potentialStore.Operand == loadedParam)
return false;
}
return null;
return true;
case ILCode.Ldloca:
case ILCode.Ldarga:
// Continue searching:
// It is always safe to move code past an instruction that loads a constant.
return null;
case ILCode.Ldflda:
case ILCode.Ldsflda:
case ILCode.Ldelema:
// address-loading instructions are safe if their arguments are safe
foreach (ILExpression arg in expr.Arguments) {
if (!IsSafeForInlineOver(arg, expressionBeingMoved))
return false;
}
return true;
default:
// abort, inlining is not possible
return false;
@ -223,24 +282,50 @@ namespace ICSharpCode.Decompiler.ILAst @@ -223,24 +282,50 @@ namespace ICSharpCode.Decompiler.ILAst
/// </summary>
public void CopyPropagation()
{
// Perform 'dup' removal prior to copy propagation:
foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
for (int i = 0; i < expr.Arguments.Count; i++) {
if (expr.Arguments[i].Code == ILCode.Dup) {
ILExpression child = expr.Arguments[i].Arguments[0];
child.ILRanges.AddRange(expr.Arguments[i].ILRanges);
expr.Arguments[i] = child;
}
}
}
foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = block.Body.Count - 1; i >= 0; i--) {
for (int i = 0; i < block.Body.Count; i++) {
ILVariable v;
ILExpression ldArg;
if (block.Body[i].Match(ILCode.Stloc, out v, out ldArg)
&& numStloc.GetOrDefault(v) == 1 && numLdloca.GetOrDefault(v) == 0
&& CanPerformCopyPropagation(ldArg))
{
// un-inline the arguments of the ldArg instruction
ILVariable[] uninlinedArgs = new ILVariable[ldArg.Arguments.Count];
for (int j = 0; j < uninlinedArgs.Length; j++) {
uninlinedArgs[j] = new ILVariable { IsGenerated = true, Name = v.Name + "_cp_" + j };
block.Body.Insert(i++, new ILExpression(ILCode.Stloc, uninlinedArgs[j], ldArg.Arguments[j]));
}
// perform copy propagation:
foreach (var expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
if (expr.Code == ILCode.Ldloc && expr.Operand == v) {
expr.Code = ldArg.Code;
expr.Operand = ldArg.Operand;
for (int j = 0; j < uninlinedArgs.Length; j++) {
expr.Arguments.Add(new ILExpression(ILCode.Ldloc, uninlinedArgs[j]));
}
}
}
block.Body.RemoveAt(i);
InlineInto(block, i); // maybe inlining gets possible after the removal of block.Body[i]
if (uninlinedArgs.Length > 0) {
// if we un-inlined stuff; we need to update the usage counters
AnalyzeMethod();
}
InlineInto(block, i, aggressive: false); // maybe inlining gets possible after the removal of block.Body[i]
i -= uninlinedArgs.Length + 1;
}
}
}
@ -251,7 +336,12 @@ namespace ICSharpCode.Decompiler.ILAst @@ -251,7 +336,12 @@ namespace ICSharpCode.Decompiler.ILAst
switch (ldArg.Code) {
case ILCode.Ldloca:
case ILCode.Ldarga:
return true; // ldloca/ldarga always return the same value for a given operand, so they can be safely copied
case ILCode.Ldelema:
case ILCode.Ldflda:
case ILCode.Ldsflda:
// All address-loading instructions always return the same value for a given operand/argument combination,
// so they can be safely copied.
return true;
case ILCode.Ldarg:
// arguments can be copied only if they aren't assigned to (directly or indirectly via ldarga)
ParameterDefinition pd = (ParameterDefinition)ldArg.Operand;

213
ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs

@ -0,0 +1,213 @@ @@ -0,0 +1,213 @@
// 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 ICSharpCode.Decompiler.ILAst
{
/// <summary>
/// IL AST transformation that introduces array, object and collection initializers.
/// </summary>
public class InitializerPeepholeTransforms
{
readonly ILBlock method;
#region Array Initializers
StoreToVariable newArrPattern;
ILCall initializeArrayPattern;
public InitializerPeepholeTransforms(ILBlock method)
{
this.method = method;
newArrPattern = new StoreToVariable(new ILExpression(
ILCode.Newarr, ILExpression.AnyOperand, new ILExpression(ILCode.Ldc_I4, ILExpression.AnyOperand)));
initializeArrayPattern = new ILCall(
"System.Runtime.CompilerServices.RuntimeHelpers", "InitializeArray",
new LoadFromVariable(newArrPattern), new ILExpression(ILCode.Ldtoken, ILExpression.AnyOperand));
}
public void TransformArrayInitializers(ILBlock block, ref int i)
{
if (!newArrPattern.Match(block.Body[i]))
return;
ILExpression newArrInst = ((ILExpression)block.Body[i]).Arguments[0];
int arrayLength = (int)newArrInst.Arguments[0].Operand;
if (arrayLength == 0)
return;
if (initializeArrayPattern.Match(block.Body.ElementAtOrDefault(i + 1))) {
if (HandleStaticallyInitializedArray(newArrPattern, block, i, newArrInst, arrayLength)) {
i -= new ILInlining(method).InlineInto(block, i + 1, aggressive: true) - 1;
}
return;
}
List<ILExpression> operands = new List<ILExpression>();
int numberOfInstructionsToRemove = 0;
for (int j = i + 1; j < block.Body.Count; j++) {
ILExpression expr = block.Body[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)
break;
int pos = (int)expr.Arguments[1].Operand;
const int maxConsecutiveDefaultValueExpressions = 10;
if (pos < operands.Count || pos > operands.Count + maxConsecutiveDefaultValueExpressions)
break;
while (operands.Count < pos)
operands.Add(new ILExpression(ILCode.DefaultValue, newArrInst.Operand));
operands.Add(expr.Arguments[2]);
numberOfInstructionsToRemove++;
}
if (operands.Count == arrayLength) {
((ILExpression)block.Body[i]).Arguments[0] = new ILExpression(
ILCode.InitArray, newArrInst.Operand, operands.ToArray());
block.Body.RemoveRange(i + 1, numberOfInstructionsToRemove);
i -= new ILInlining(method).InlineInto(block, i + 1, aggressive: true) - 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 newArrPattern, ILBlock block, int i, ILExpression newArrInst, int arrayLength)
{
FieldDefinition field = ((ILExpression)block.Body[i + 1]).Arguments[1].Operand as FieldDefinition;
if (field == null || field.InitialValue == null)
return false;
ILExpression[] newArr = new ILExpression[arrayLength];
if (DecodeArrayInitializer(TypeAnalysis.GetTypeCode(newArrInst.Operand as TypeReference), field.InitialValue, newArr)) {
block.Body[i] = new ILExpression(ILCode.Stloc, newArrPattern.LastVariable, new ILExpression(ILCode.InitArray, newArrInst.Operand, newArr));
block.Body.RemoveAt(i + 1);
return true;
}
return false;
}
static bool DecodeArrayInitializer(TypeCode elementType, byte[] initialValue, ILExpression[] output)
{
switch (elementType) {
case TypeCode.Boolean:
case TypeCode.SByte:
case TypeCode.Byte:
if (initialValue.Length == output.Length) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_I4, (int)initialValue[j]);
}
return true;
}
return false;
case TypeCode.Char:
case TypeCode.Int16:
case TypeCode.UInt16:
if (initialValue.Length == output.Length * 2) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_I4, (int)BitConverter.ToInt16(initialValue, j * 2));
}
return true;
}
return false;
case TypeCode.Int32:
case TypeCode.UInt32:
if (initialValue.Length == output.Length * 4) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_I4, BitConverter.ToInt32(initialValue, j * 4));
}
return true;
}
return false;
case TypeCode.Int64:
case TypeCode.UInt64:
if (initialValue.Length == output.Length * 8) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_I8, BitConverter.ToInt64(initialValue, j * 8));
}
return true;
}
return false;
case TypeCode.Single:
if (initialValue.Length == output.Length * 4) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_R4, BitConverter.ToSingle(initialValue, j * 4));
}
return true;
}
return false;
case TypeCode.Double:
if (initialValue.Length == output.Length * 8) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_R8, BitConverter.ToDouble(initialValue, j * 8));
}
return true;
}
return false;
default:
return false;
}
}
#endregion
#region Collection Initilializers
public void TransformCollectionInitializers(ILBlock block, ref int i)
{
ILVariable v;
ILExpression expr;
if (!block.Body[i].Match(ILCode.Stloc, out v, out expr) || expr.Code != ILCode.Newobj)
return;
MethodReference ctor = (MethodReference)expr.Operand;
TypeDefinition td = ctor.DeclaringType.Resolve();
if (td == null || !td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections"))
return;
// This is a collection: we can convert Add() calls into a collection initializer
ILExpression collectionInitializer = new ILExpression(ILCode.InitCollection, null, expr);
for (int j = i + 1; j < block.Body.Count; j++) {
MethodReference addMethod;
List<ILExpression> args;
if (!block.Body[j].Match(ILCode.Callvirt, out addMethod, out args))
break;
if (addMethod.Name != "Add" || !addMethod.HasThis || args.Count < 2 || args[0].Code != ILCode.Ldloc || args[0].Operand != v)
break;
collectionInitializer.Arguments.Add((ILExpression)block.Body[j]);
}
// ensure we added at least one additional arg to the collection initializer:
if (collectionInitializer.Arguments.Count == 1)
return;
ILInlining inline = new ILInlining(method);
ILExpression followingExpr = block.Body.ElementAtOrDefault(i + collectionInitializer.Arguments.Count) as ILExpression;
if (inline.CanInlineInto(followingExpr, v, collectionInitializer)) {
block.Body.RemoveRange(i + 1, collectionInitializer.Arguments.Count - 1);
((ILExpression)block.Body[i]).Arguments[0] = collectionInitializer;
// Change add methods into InitCollectionAddMethod:
for (int j = 1; j < collectionInitializer.Arguments.Count; j++) {
collectionInitializer.Arguments[j].Arguments.RemoveAt(0);
collectionInitializer.Arguments[j].Code = ILCode.InitCollectionAddMethod;
}
inline = new ILInlining(method); // refresh variable usage info
if (inline.InlineIfPossible(block, ref i))
i++; // retry transformations on the new combined instruction
}
}
#endregion
}
}

167
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -25,13 +25,16 @@ namespace ICSharpCode.Decompiler.ILAst @@ -25,13 +25,16 @@ namespace ICSharpCode.Decompiler.ILAst
transforms.context = context;
transforms.method = method;
InitializerPeepholeTransforms initializerTransforms = new InitializerPeepholeTransforms(method);
PeepholeTransform[] blockTransforms = {
ArrayInitializers.Transform(method),
transforms.CachedDelegateInitialization
initializerTransforms.TransformArrayInitializers,
initializerTransforms.TransformCollectionInitializers,
transforms.CachedDelegateInitialization,
transforms.MakeAssignmentExpression
};
Func<ILExpression, ILExpression>[] exprTransforms = {
EliminateDups,
HandleDecimalConstants
HandleDecimalConstants,
SimplifyLdObjAndStObj
};
// Traverse in post order so that nested blocks are transformed first. This is required so that
// patterns on the parent block can assume that all nested blocks are already transformed.
@ -45,9 +48,15 @@ namespace ICSharpCode.Decompiler.ILAst @@ -45,9 +48,15 @@ namespace ICSharpCode.Decompiler.ILAst
expr = block.Body[i] as ILExpression;
if (expr != null) {
// apply expr transforms to top-level expr in block
foreach (var t in exprTransforms)
expr = t(expr);
bool modified = ApplyExpressionTransforms(ref expr, exprTransforms);
block.Body[i] = expr;
if (modified) {
ILInlining inlining = new ILInlining(method);
if (inlining.InlineIfPossible(block, ref i)) {
i++; // retry all transforms on the new combined instruction
continue;
}
}
}
// apply block transforms
foreach (var t in blockTransforms) {
@ -63,20 +72,29 @@ namespace ICSharpCode.Decompiler.ILAst @@ -63,20 +72,29 @@ namespace ICSharpCode.Decompiler.ILAst
// apply expr transforms to all arguments
for (int i = 0; i < expr.Arguments.Count; i++) {
ILExpression arg = expr.Arguments[i];
foreach (var t in exprTransforms)
arg = t(arg);
ApplyExpressionTransforms(ref arg, exprTransforms);
expr.Arguments[i] = arg;
}
}
}
}
static ILExpression EliminateDups(ILExpression expr)
static bool ApplyExpressionTransforms(ref ILExpression expr, Func<ILExpression, ILExpression>[] exprTransforms)
{
if (expr.Code == ILCode.Dup)
return expr.Arguments.Single();
else
return expr;
bool modifiedInAnyIteration = false;
bool modified;
do {
modified = false;
ILExpression oldExpr = expr;
ILCode oldOpCode = oldExpr.Code;
foreach (var t in exprTransforms)
expr = t(expr);
if (expr != oldExpr || oldOpCode != expr.Code) {
modified = true;
modifiedInAnyIteration = true;
}
} while (modified);
return modifiedInAnyIteration;
}
#region HandleDecimalConstants
@ -120,6 +138,54 @@ namespace ICSharpCode.Decompiler.ILAst @@ -120,6 +138,54 @@ namespace ICSharpCode.Decompiler.ILAst
}
#endregion
#region SimplifyLdObjAndStObj
static ILExpression SimplifyLdObjAndStObj(ILExpression expr)
{
if (expr.Code == ILCode.Initobj) {
expr.Code = ILCode.Stobj;
expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand));
}
if (expr.Code == ILCode.Stobj) {
switch (expr.Arguments[0].Code) {
case ILCode.Ldelema:
return SimplifyLdObjOrStObj(expr, ILCode.Stelem_Any);
case ILCode.Ldloca:
return SimplifyLdObjOrStObj(expr, ILCode.Stloc);
case ILCode.Ldarga:
return SimplifyLdObjOrStObj(expr, ILCode.Starg);
case ILCode.Ldflda:
return SimplifyLdObjOrStObj(expr, ILCode.Stfld);
case ILCode.Ldsflda:
return SimplifyLdObjOrStObj(expr, ILCode.Stsfld);
}
} else if (expr.Code == ILCode.Ldobj) {
switch (expr.Arguments[0].Code) {
case ILCode.Ldelema:
return SimplifyLdObjOrStObj(expr, ILCode.Ldelem_Any);
case ILCode.Ldloca:
return SimplifyLdObjOrStObj(expr, ILCode.Ldloc);
case ILCode.Ldarga:
return SimplifyLdObjOrStObj(expr, ILCode.Ldarg);
case ILCode.Ldflda:
return SimplifyLdObjOrStObj(expr, ILCode.Ldfld);
case ILCode.Ldsflda:
return SimplifyLdObjOrStObj(expr, ILCode.Ldsfld);
}
}
return expr;
}
static ILExpression SimplifyLdObjOrStObj(ILExpression expr, ILCode newCode)
{
ILExpression lda = expr.Arguments[0];
lda.Code = newCode;
if (expr.Code == ILCode.Stobj)
lda.Arguments.Add(expr.Arguments[1]);
lda.ILRanges.AddRange(expr.ILRanges);
return lda;
}
#endregion
#region CachedDelegateInitialization
void CachedDelegateInitialization(ILBlock block, ref int i)
{
@ -139,11 +205,11 @@ namespace ICSharpCode.Decompiler.ILAst @@ -139,11 +205,11 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression condition = c.Condition.Arguments.Single() as ILExpression;
if (condition == null || condition.Code != ILCode.Ldsfld)
return;
FieldDefinition field = condition.Operand as FieldDefinition; // field is defined in current assembly
FieldDefinition field = ((FieldReference)condition.Operand).ResolveWithinSameModule(); // field is defined in current assembly
if (field == null || !field.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
return;
ILExpression stsfld = c.TrueBlock.Body[0] as ILExpression;
if (!(stsfld != null && stsfld.Code == ILCode.Stsfld && stsfld.Operand == field))
if (!(stsfld != null && stsfld.Code == ILCode.Stsfld && ((FieldReference)stsfld.Operand).ResolveWithinSameModule() == field))
return;
ILExpression newObj = stsfld.Arguments[0];
if (!(newObj.Code == ILCode.Newobj && newObj.Arguments.Count == 2))
@ -152,18 +218,20 @@ namespace ICSharpCode.Decompiler.ILAst @@ -152,18 +218,20 @@ namespace ICSharpCode.Decompiler.ILAst
return;
if (newObj.Arguments[1].Code != ILCode.Ldftn)
return;
MethodDefinition anonymousMethod = newObj.Arguments[1].Operand as MethodDefinition; // method is defined in current assembly
MethodDefinition anonymousMethod = ((MethodReference)newObj.Arguments[1].Operand).ResolveWithinSameModule(); // method is defined in current assembly
if (!Ast.Transforms.DelegateConstruction.IsAnonymousMethod(context, anonymousMethod))
return;
ILExpression expr = block.Body.ElementAtOrDefault(i + 1) as ILExpression;
if (expr != null && expr.GetSelfAndChildrenRecursive<ILExpression>().Count(e => e.Code == ILCode.Ldsfld && e.Operand == field) == 1) {
foreach (ILExpression parent in expr.GetSelfAndChildrenRecursive<ILExpression>()) {
ILNode followingNode = block.Body.ElementAtOrDefault(i + 1);
if (followingNode != null && followingNode.GetSelfAndChildrenRecursive<ILExpression>().Count(
e => e.Code == ILCode.Ldsfld && ((FieldReference)e.Operand).ResolveWithinSameModule() == field) == 1)
{
foreach (ILExpression parent in followingNode.GetSelfAndChildrenRecursive<ILExpression>()) {
for (int j = 0; j < parent.Arguments.Count; j++) {
if (parent.Arguments[j].Code == ILCode.Ldsfld && parent.Arguments[j].Operand == field) {
if (parent.Arguments[j].Code == ILCode.Ldsfld && ((FieldReference)parent.Arguments[j].Operand).ResolveWithinSameModule() == field) {
parent.Arguments[j] = newObj;
block.Body.RemoveAt(i);
i -= new ILInlining(method).InlineInto(block, i);
i -= new ILInlining(method).InlineInto(block, i, aggressive: true);
return;
}
}
@ -171,5 +239,62 @@ namespace ICSharpCode.Decompiler.ILAst @@ -171,5 +239,62 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
#endregion
#region MakeAssignmentExpression
void MakeAssignmentExpression(ILBlock block, ref int i)
{
// expr_44 = ...
// stloc(v, expr_44)
// ->
// expr_44 = stloc(v, ...))
ILVariable exprVar;
ILExpression initializer;
if (!(block.Body[i].Match(ILCode.Stloc, out exprVar, out initializer) && exprVar.IsGenerated))
return;
ILExpression stloc1 = block.Body.ElementAtOrDefault(i + 1) as ILExpression;
if (!(stloc1 != null && stloc1.Code == ILCode.Stloc && stloc1.Arguments[0].Code == ILCode.Ldloc && stloc1.Arguments[0].Operand == exprVar))
return;
ILInlining inlining;
ILExpression store2 = block.Body.ElementAtOrDefault(i + 2) as ILExpression;
if (StoreCanBeConvertedToAssignment(store2, exprVar)) {
// expr_44 = ...
// stloc(v1, expr_44)
// anystore(v2, expr_44)
// ->
// stloc(v1, anystore(v2, ...))
inlining = new ILInlining(method);
if (inlining.numLdloc.GetOrDefault(exprVar) == 2 && inlining.numStloc.GetOrDefault(exprVar) == 1) {
block.Body.RemoveAt(i + 2); // remove store2
block.Body.RemoveAt(i); // remove expr = ...
stloc1.Arguments[0] = store2;
store2.Arguments[store2.Arguments.Count - 1] = initializer;
if (inlining.InlineIfPossible(block, ref i)) {
i++; // retry transformations on the new combined instruction
}
return;
}
}
block.Body.RemoveAt(i + 1); // remove stloc
stloc1.Arguments[0] = initializer;
((ILExpression)block.Body[i]).Arguments[0] = stloc1;
inlining = new ILInlining(method);
if (inlining.InlineIfPossible(block, ref i)) {
i++; // retry transformations on the new combined instruction
}
}
bool StoreCanBeConvertedToAssignment(ILExpression store, ILVariable exprVar)
{
if (store != null && (store.Code == ILCode.Stloc || store.Code == ILCode.Stfld || store.Code == ILCode.Stsfld)) {
return store.Arguments.Last().Code == ILCode.Ldloc && store.Arguments.Last().Operand == exprVar;
}
return false;
}
#endregion
}
}

18
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -203,6 +203,18 @@ namespace ICSharpCode.Decompiler.ILAst @@ -203,6 +203,18 @@ namespace ICSharpCode.Decompiler.ILAst
}
return ctor.DeclaringType;
}
case ILCode.InitCollection:
return InferTypeForExpression(expr.Arguments[0], expectedType);
case ILCode.InitCollectionAddMethod:
{
MethodReference addMethod = (MethodReference)expr.Operand;
if (forceInferChildren) {
for (int i = 1; i < addMethod.Parameters.Count; i++) {
InferTypeForExpression(expr.Arguments[i-1], SubstituteTypeArgs(addMethod.Parameters[i].ParameterType, addMethod));
}
}
return addMethod.DeclaringType;
}
#endregion
#region Load/Store Fields
case ILCode.Ldfld:
@ -219,11 +231,11 @@ namespace ICSharpCode.Decompiler.ILAst @@ -219,11 +231,11 @@ namespace ICSharpCode.Decompiler.ILAst
InferTypeForExpression(expr.Arguments[0], ((FieldReference)expr.Operand).DeclaringType);
InferTypeForExpression(expr.Arguments[1], GetFieldType((FieldReference)expr.Operand));
}
return null;
return GetFieldType((FieldReference)expr.Operand);
case ILCode.Stsfld:
if (forceInferChildren)
InferTypeForExpression(expr.Arguments[0], GetFieldType((FieldReference)expr.Operand));
return null;
return GetFieldType((FieldReference)expr.Operand);
#endregion
#region Reference/Pointer instructions
case ILCode.Ldind_I:
@ -260,6 +272,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -260,6 +272,8 @@ namespace ICSharpCode.Decompiler.ILAst
return null;
case ILCode.Initobj:
return null;
case ILCode.DefaultValue:
return (TypeReference)expr.Operand;
case ILCode.Localloc:
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.Int32);

6
ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs

@ -60,6 +60,12 @@ namespace ICSharpCode.Decompiler.ILAst @@ -60,6 +60,12 @@ namespace ICSharpCode.Decompiler.ILAst
method.Body.Clear();
method.EntryGoto = null;
method.Body.AddRange(yrd.newBody);
// Repeat the inlining/copy propagation optimization because the conversion of field access
// to local variables can open up additional inlining possibilities.
ILInlining inlining = new ILInlining(method);
inlining.InlineAllVariables();
inlining.CopyPropagation();
}
void Run()

36
ICSharpCode.Decompiler/Tests/ArrayInitializers.cs

@ -1,36 +0,0 @@ @@ -1,36 +0,0 @@
// 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 }
});
}
}

3
ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -53,6 +53,7 @@ @@ -53,6 +53,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Switch.cs" />
<Compile Include="YieldReturn.cs" />
<None Include="Types\S_EnumSamples.cs" />
<None Include="CustomAttributes\S_AssemblyCustomAttribute.cs" />
@ -68,7 +69,7 @@ @@ -68,7 +69,7 @@
<Compile Include="CodeSampleFileParser.cs" />
<Compile Include="CustomAttributes\CustomAttributeTests.cs" />
<Compile Include="DecompilerTestBase.cs" />
<Compile Include="ArrayInitializers.cs" />
<Compile Include="InitializerTests.cs" />
<Compile Include="ExceptionHandling.cs" />
<Compile Include="Generics.cs" />
<Compile Include="MultidimensionalArray.cs" />

118
ICSharpCode.Decompiler/Tests/InitializerTests.cs

@ -0,0 +1,118 @@ @@ -0,0 +1,118 @@
// 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;
public class InitializerTests
{
// Helper methods used to ensure initializers used within expressions work correctly
static void X(object a, object b)
{
}
static object Y()
{
return null;
}
#region Array Initializers
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, 0, b, 0, 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 }
});
}
public static void ArrayBoolean()
{
X(Y(), new bool[] { true, false, true, false, false, false, true, true });
}
public static void ArrayByte()
{
X(Y(), new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 254, 255 });
}
public static void ArraySByte()
{
X(Y(), new sbyte[] { -128, -127, 0, 1, 2, 3, 4, 127 });
}
public static void ArrayShort()
{
X(Y(), new short[] { -32768, -1, 0, 1, 32767 });
}
public static void ArrayUShort()
{
X(Y(), new ushort[] { 0, 1, 32767, 32768, 65534, 65535 });
}
public static void ArrayInt()
{
X(Y(), new int[] { 1, -2, 2000000000, 4, 5, -6, 7, 8, 9, 10 });
}
public static void ArrayUInt()
{
X(Y(), new uint[] { 1, 2000000000, 3000000000, 4, 5, 6, 7, 8, 9, 10 });
}
public static void ArrayLong()
{
X(Y(), new long[] { -4999999999999999999, -1, 0, 1, 4999999999999999999 });
}
public static void ArrayULong()
{
X(Y(), new ulong[] { 1, 2000000000, 3000000000, 4, 5, 6, 7, 8, 4999999999999999999, 9999999999999999999 });
}
public static void ArrayFloat()
{
X(Y(), new float[] { -1.5f, 0f, 1.5f, float.NegativeInfinity, float.PositiveInfinity, float.NaN });
}
public static void ArrayDouble()
{
X(Y(), new double[] { -1.5, 0.0, 1.5, double.NegativeInfinity, double.PositiveInfinity, double.NaN });
}
public static void ArrayDecimal()
{
X(Y(), new decimal[] { -100m, 0m, 100m, decimal.MinValue, decimal.MaxValue, 0.0000001m });
}
public static void ArrayString()
{
X(Y(), new string[] { "", null, "Hello", "World" });
}
#endregion
public static void CollectionInitializerList()
{
X(Y(), new List<int> { 1, 2, 3 });
}
public static void CollectionInitializerDictionary()
{
X(Y(), new Dictionary<string, int> {
{ "First", 1 },
{ "Second", 2 },
{ "Third" , 3 }
});
}
}

60
ICSharpCode.Decompiler/Tests/Switch.cs

@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
// 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 Switch
{
public static string ShortSwitchOverString(string text)
{
switch (text) {
case "First case":
return "Text";
default:
return "Default";
}
}
public static string SwitchOverString1(string text)
{
switch (text) {
case "First case":
return "Text1";
case "Second case":
case "2nd case":
return "Text2";
case "Third case":
return "Text3";
case "Fourth case":
return "Text4";
case "Fifth case":
return "Text5";
case "Sixth case":
return "Text6";
case null:
return null;
default:
return "Default";
}
}
public static string SwitchOverString2()
{
switch (Environment.UserName) {
case "First case":
return "Text1";
case "Second case":
return "Text2";
case "Third case":
return "Text3";
case "Fourth case":
return "Text4";
case "Fifth case":
return "Text5";
case "Sixth case":
return "Text6";
default:
return "Default";
}
}
}

23
ICSharpCode.Decompiler/Tests/ValueTypes.cs

@ -87,4 +87,27 @@ public static class ValueTypes @@ -87,4 +87,27 @@ public static class ValueTypes
// test passing through by-ref arguments
Copy4(ref p, out o);
}
public static void Issue56(int i, out string str)
{
str = "qq";
str += i.ToString();
}
public static void CopyAroundAndModifyField(S s)
{
S locS = s;
locS.Field += 10;
s = locS;
}
static int[] MakeArray()
{
return null;
}
public static void IncrementArrayLocation()
{
MakeArray()[Environment.TickCount]++;
}
}

15
ICSharpCode.Decompiler/Tests/YieldReturn.cs

@ -31,6 +31,21 @@ public static class YieldReturn @@ -31,6 +31,21 @@ public static class YieldReturn
yield return 2;
}
public static IEnumerable<int> YieldReturnInLock1(object o)
{
lock (o) {
yield return 1;
}
}
public static IEnumerable<int> YieldReturnInLock2(object o)
{
lock (o) {
yield return 1;
o = null;
yield return 2;
}
}
public static IEnumerable<string> YieldReturnWithNestedTryFinally(bool breakInMiddle)
{

43
ILSpy/BamlDecompiler.cs

@ -10,7 +10,7 @@ using System.Threading.Tasks; @@ -10,7 +10,7 @@ using System.Threading.Tasks;
using System.Windows.Baml2006;
using System.Xaml;
using System.Xml;
using System.Xml.Linq;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
@ -32,7 +32,8 @@ namespace ICSharpCode.ILSpy.Baml @@ -32,7 +32,8 @@ namespace ICSharpCode.ILSpy.Baml
Assembly assembly = Assembly.LoadFile(containingAssemblyFile);
Baml2006Reader reader = new Baml2006Reader(bamlCode, new XamlReaderSettings() { ValuesMustBeString = true, LocalAssembly = assembly });
XamlXmlWriter writer = new XamlXmlWriter(new XmlTextWriter(w) { Formatting = Formatting.Indented }, reader.SchemaContext);
XDocument doc = new XDocument();
XamlXmlWriter writer = new XamlXmlWriter(doc.CreateWriter(), reader.SchemaContext);
while (reader.Read()) {
switch (reader.NodeType) {
case XamlNodeType.None:
@ -64,7 +65,43 @@ namespace ICSharpCode.ILSpy.Baml @@ -64,7 +65,43 @@ namespace ICSharpCode.ILSpy.Baml
throw new Exception("Invalid value for XamlNodeType");
}
}
return w.ToString();
writer.Close();
// Fix namespace references
string suffixToRemove = ";assembly=" + assembly.GetName().Name;
foreach (XAttribute attrib in doc.Root.Attributes()) {
if (attrib.Name.Namespace == XNamespace.Xmlns) {
if (attrib.Value.EndsWith(suffixToRemove, StringComparison.Ordinal)) {
string newNamespace = attrib.Value.Substring(0, attrib.Value.Length - suffixToRemove.Length);
ChangeXmlNamespace(doc, attrib.Value, newNamespace);
attrib.Value = newNamespace;
}
}
}
// Convert x:Key into an attribute where possible
XName xKey = XName.Get("Key", "http://schemas.microsoft.com/winfx/2006/xaml");
foreach (XElement e in doc.Descendants(xKey).ToList()) {
if (e.Nodes().Count() != 1)
continue;
XText text = e.Nodes().Single() as XText;
if (text != null) {
e.Parent.SetAttributeValue(xKey, text.Value);
e.Remove();
}
}
return doc.ToString();
}
/// <summary>
/// Changes all references from oldNamespace to newNamespace in the document.
/// </summary>
void ChangeXmlNamespace(XDocument doc, XNamespace oldNamespace, XNamespace newNamespace)
{
foreach (XElement e in doc.Descendants()) {
if (e.Name.Namespace == oldNamespace)
e.Name = newNamespace + e.Name.LocalName;
}
}
}

13
ILSpy/CSharpLanguage.cs

@ -125,6 +125,8 @@ namespace ICSharpCode.ILSpy @@ -125,6 +125,8 @@ namespace ICSharpCode.ILSpy
WriteProjectFile(new TextOutputWriter(output), files, assembly.MainModule);
} else {
foreach (TypeDefinition type in assembly.MainModule.Types) {
if (AstBuilder.MemberIsHidden(type, options.DecompilerSettings))
continue;
AstBuilder codeDomBuilder = CreateAstBuilder(options, type);
codeDomBuilder.AddType(type);
codeDomBuilder.GenerateCode(output, transformAbortCondition);
@ -266,9 +268,18 @@ namespace ICSharpCode.ILSpy @@ -266,9 +268,18 @@ namespace ICSharpCode.ILSpy
#endregion
#region WriteCodeFilesInProject
bool IncludeTypeWhenDecompilingProject(TypeDefinition type, DecompilationOptions options)
{
if (type.Name == "<Module>" || AstBuilder.MemberIsHidden(type, options.DecompilerSettings))
return false;
if (type.Namespace == "XamlGeneratedNamespace" && type.Name == "GeneratedInternalTypeHelper")
return false;
return true;
}
IEnumerable<Tuple<string, string>> WriteCodeFilesInProject(AssemblyDefinition assembly, DecompilationOptions options, HashSet<string> directories)
{
var files = assembly.MainModule.Types.Where(t => t.Name != "<Module>").GroupBy(
var files = assembly.MainModule.Types.Where(t => IncludeTypeWhenDecompilingProject(t, options)).GroupBy(
delegate (TypeDefinition type) {
string file = TextView.DecompilerTextView.CleanUpName(type.Name) + this.FileExtension;
if (string.IsNullOrEmpty(type.Namespace)) {

2
ILSpy/ILAstLanguage.cs

@ -80,7 +80,7 @@ namespace ICSharpCode.ILSpy @@ -80,7 +80,7 @@ namespace ICSharpCode.ILSpy
internal static IEnumerable<ILAstLanguage> GetDebugLanguages()
{
yield return new ILAstLanguage { name = "ILAst (unoptimized)", inlineVariables = false };
string nextName = "ILAst (variable inlining)";
string nextName = "ILAst (variable splitting)";
foreach (ILAstOptimizationStep step in Enum.GetValues(typeof(ILAstOptimizationStep))) {
yield return new ILAstLanguage { name = nextName, abortBeforeStep = step };
nextName = "ILAst (after " + step + ")";

1
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/IPatternAstVisitor.cs

@ -17,6 +17,7 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching @@ -17,6 +17,7 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
S VisitChoice(Choice choice, T data);
S VisitNamedNode(NamedNode namedNode, T data);
S VisitRepeat(Repeat repeat, T data);
S VisitOptionalNode(OptionalNode optionalNode, T data);
S VisitIdentifierExpressionBackreference(IdentifierExpressionBackreference identifierExpressionBackreference, T data);
}
}

42
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/OptionalNode.cs

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
// 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.Diagnostics;
namespace ICSharpCode.NRefactory.CSharp.PatternMatching
{
public class OptionalNode : Pattern
{
public static readonly Role<AstNode> ElementRole = new Role<AstNode>("Element", AstNode.Null);
public OptionalNode(AstNode childNode)
{
AddChild(childNode, ElementRole);
}
public OptionalNode(string groupName, AstNode childNode) : this(new NamedNode(groupName, childNode))
{
}
internal override bool DoMatchCollection(Role role, AstNode pos, Match match, Stack<Pattern.PossibleMatch> backtrackingStack)
{
backtrackingStack.Push(new PossibleMatch(pos, match.CheckPoint()));
return GetChildByRole(ElementRole).DoMatch(pos, match);
}
protected internal override bool DoMatch(AstNode other, Match match)
{
if (other == null || other.IsNull)
return true;
else
return GetChildByRole(ElementRole).DoMatch(other, match);
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return ((IPatternAstVisitor<T, S>)visitor).VisitOptionalNode(this, data);
}
}
}

5
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs

@ -74,6 +74,11 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching @@ -74,6 +74,11 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
return p != null ? new AttributeSectionPlaceholder(p) : null;
}
public static implicit operator SwitchSection(Pattern p)
{
return p != null ? new SwitchSectionPlaceholder(p) : null;
}
// Make debugging easier by giving Patterns a ToString() implementation
public override string ToString()
{

29
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs

@ -181,4 +181,33 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching @@ -181,4 +181,33 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
return child.DoMatchCollection(role, pos, match, backtrackingStack);
}
}
sealed class SwitchSectionPlaceholder : SwitchSection
{
readonly AstNode child;
public SwitchSectionPlaceholder(AstNode child)
{
this.child = child;
}
public override NodeType NodeType {
get { return NodeType.Placeholder; }
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return ((IPatternAstVisitor<T, S>)visitor).VisitPlaceholder(this, child, data);
}
protected internal override bool DoMatch(AstNode other, Match match)
{
return child.DoMatch(other, match);
}
internal override bool DoMatchCollection(Role role, AstNode pos, Match match, Stack<Pattern.PossibleMatch> backtrackingStack)
{
return child.DoMatchCollection(role, pos, match, backtrackingStack);
}
}
}

10
NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs

@ -2170,6 +2170,16 @@ namespace ICSharpCode.NRefactory.CSharp @@ -2170,6 +2170,16 @@ namespace ICSharpCode.NRefactory.CSharp
RPar();
return EndNode(repeat);
}
object IPatternAstVisitor<object, object>.VisitOptionalNode(OptionalNode optionalNode, object data)
{
StartNode(optionalNode);
WriteKeyword("optional");
LPar();
optionalNode.GetChildByRole(OptionalNode.ElementRole).AcceptVisitor(this, data);
RPar();
return EndNode(optionalNode);
}
#endregion
}
}

1
NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -107,6 +107,7 @@ @@ -107,6 +107,7 @@
<Compile Include="CSharp\Ast\PatternMatching\IPatternAstVisitor.cs" />
<Compile Include="CSharp\Ast\PatternMatching\Match.cs" />
<Compile Include="CSharp\Ast\PatternMatching\NamedNode.cs" />
<Compile Include="CSharp\Ast\PatternMatching\OptionalNode.cs" />
<Compile Include="CSharp\Ast\PatternMatching\Repeat.cs" />
<Compile Include="CSharp\Ast\PatternMatching\Pattern.cs" />
<Compile Include="CSharp\Ast\PatternMatching\Placeholder.cs" />

Loading…
Cancel
Save