diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 14e429d05..49d783f46 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -296,50 +296,54 @@ namespace ICSharpCode.Decompiler.Ast.Transforms // Delete the variable declaration statement: AstNode cur = stmt.NextSibling; stmt.Remove(); - if (blockStatement.Parent.NodeType == NodeType.Member || blockStatement.Parent is Accessor) { - // Delete any following statements as long as they assign parameters to the display class - // Do parameter handling only for closures created in the top scope (direct child of method/accessor) - List parameterOccurrances = blockStatement.Descendants.OfType() - .Select(n => n.Annotation()).Where(p => p != null && p.IsParameter).ToList(); - AstNode next; - for (; cur != null; cur = next) { - next = cur.NextSibling; - - // Test for the pattern: - // "variableName.MemberName = right;" - ExpressionStatement closureFieldAssignmentPattern = new ExpressionStatement( - new AssignmentExpression( - new NamedNode("left", new MemberReferenceExpression { Target = new IdentifierExpression(variable.Name) }), - new AnyNode("right") - ) - ); - Match m = closureFieldAssignmentPattern.Match(cur); - if (m != null) { - AstNode right = m.Get("right").Single(); - bool isParameter = false; - if (right is ThisReferenceExpression) { - isParameter = true; - } else if (right is IdentifierExpression) { - // handle parameters only if the whole method contains no other occurrance except for 'right' - ILVariable param = right.Annotation(); - isParameter = param.IsParameter && parameterOccurrances.Count(c => c == param) == 1; - } - if (isParameter) { - dict[m.Get("left").Single().Annotation().ResolveWithinSameModule()] = right; - cur.Remove(); - } else { - break; + + // Delete any following statements as long as they assign parameters to the display class + BlockStatement rootBlock = blockStatement.Ancestors.OfType().LastOrDefault() ?? blockStatement; + List parameterOccurrances = rootBlock.Descendants.OfType() + .Select(n => n.Annotation()).Where(p => p != null && p.IsParameter).ToList(); + AstNode next; + for (; cur != null; cur = next) { + next = cur.NextSibling; + + // Test for the pattern: + // "variableName.MemberName = right;" + ExpressionStatement closureFieldAssignmentPattern = new ExpressionStatement( + new AssignmentExpression( + new NamedNode("left", new MemberReferenceExpression { Target = new IdentifierExpression(variable.Name) }), + new AnyNode("right") + ) + ); + Match m = closureFieldAssignmentPattern.Match(cur); + if (m != null) { + FieldDefinition fieldDef = m.Get("left").Single().Annotation().ResolveWithinSameModule(); + AstNode right = m.Get("right").Single(); + bool isParameter = false; + bool isDisplayClassParentPointerAssignment = false; + if (right is ThisReferenceExpression) { + isParameter = true; + } else if (right is IdentifierExpression) { + // handle parameters only if the whole method contains no other occurrance except for 'right' + ILVariable v = right.Annotation(); + isParameter = v.IsParameter && parameterOccurrances.Count(c => c == v) == 1; + if (!isParameter && TypeAnalysis.IsSameType(v.Type, fieldDef.FieldType) && IsPotentialClosure(context, v.Type.ResolveWithinSameModule())) { + isDisplayClassParentPointerAssignment = true; } + } + if (isParameter || isDisplayClassParentPointerAssignment) { + dict[fieldDef] = right; + cur.Remove(); } else { break; } + } else { + break; } } // Now create variables for all fields of the display class (except for those that we already handled as parameters) List> variablesToDeclare = new List>(); foreach (FieldDefinition field in type.Fields) { - if (dict.ContainsKey(field)) + if (dict.ContainsKey(field)) // skip field if it already was handled as parameter continue; EnsureVariableNameIsAvailable(blockStatement, field.Name); currentlyUsedVariableNames.Add(field.Name); diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index 844b223f7..e5e6fccc6 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -299,6 +299,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms InExpression = m.Get("collection").Single().Detach(), EmbeddedStatement = newBody }; + if (foreachStatement.InExpression is BaseReferenceExpression) + foreachStatement.InExpression = new ThisReferenceExpression(); node.ReplaceWith(foreachStatement); foreach (Statement stmt in m.Get("variablesOutsideLoop")) { ((BlockStatement)foreachStatement.Parent).Statements.InsertAfter(null, stmt.Detach()); diff --git a/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs b/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs index 1060215a1..b16b0fd94 100644 --- a/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs @@ -3,43 +3,82 @@ using System; using System.Collections.Generic; +using System.Linq; public static class DelegateConstruction { + class InstanceTests + { + public Action CaptureOfThis() + { + return delegate { + CaptureOfThis(); + }; + } + + public Action CaptureOfThisAndParameter(int a) + { + return delegate { + CaptureOfThisAndParameter(a); + }; + } + + public Action CaptureOfThisAndParameterInForEach(int a) + { + foreach (var item in Enumerable.Empty()) { + return delegate { + CaptureOfThisAndParameter(item + a); + }; + } + return null; + } + + public Action CaptureOfThisAndParameterInForEachWithItemCopy(int a) + { + foreach (var item in Enumerable.Empty()) { + int copyOfItem = item; + return delegate { + CaptureOfThisAndParameter(item + a + copyOfItem); + }; + } + return null; + } + } + public static void Test(this string a) { } - + public static Action ExtensionMethodUnbound() { return new Action(DelegateConstruction.Test); } - + public static Action ExtensionMethodBound() { return new Action("abc".Test); } - + public static Action ExtensionMethodBoundOnNull() { return new Action(((string)null).Test); } - + public static object StaticMethod() { return new Func(DelegateConstruction.ExtensionMethodBound); } - + public static object InstanceMethod() { return new Func("hello".ToUpper); } - + public static object InstanceMethodOnNull() { return new Func(((string)null).ToUpper); } - + public static List> AnonymousMethodStoreWithinLoop() { List> list = new List>(); @@ -54,7 +93,7 @@ public static class DelegateConstruction } return list; } - + public static List> AnonymousMethodStoreOutsideLoop() { List> list = new List>(); @@ -69,7 +108,7 @@ public static class DelegateConstruction } return list; } - + public static Action StaticAnonymousMethodNoClosure() { return delegate @@ -77,7 +116,7 @@ public static class DelegateConstruction Console.WriteLine(); }; } - + public static void NameConflict() { // i is captured variable, @@ -98,7 +137,7 @@ public static class DelegateConstruction } } } - + public static void NameConflict2(int j) { List> list = new List>(); @@ -109,7 +148,7 @@ public static class DelegateConstruction }); } } - + public static Action NameConflict3(int i) { return delegate(int j) {