From fb97498d288123d368afb2eb5b2397835cb47e2d Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 15 Feb 2011 14:47:25 +0100 Subject: [PATCH] Add support for anonymous methods. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 27 +-------- .../ConvertConstructorCallIntoInitializer.cs | 2 +- .../Ast/Transforms/DelegateConstruction.cs | 55 ++++++++++++++++++- .../ReplaceMethodCallsWithOperators.cs | 2 +- .../Ast/Transforms/SimplifyTypeReferences.cs | 28 ---------- .../Ast/Transforms/TransformationPipeline.cs | 50 +++++++++++++++++ .../ICSharpCode.Decompiler.csproj | 2 +- 7 files changed, 109 insertions(+), 57 deletions(-) delete mode 100644 ICSharpCode.Decompiler/Ast/Transforms/SimplifyTypeReferences.cs create mode 100644 ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 1c80fe193..8dd99fc51 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -17,30 +17,7 @@ namespace Decompiler public void GenerateCode(ITextOutput output) { - astCompileUnit.AcceptVisitor(new Transforms.DelegateConstruction(), null); - - for (int i = 0; i < 4; i++) { - if (Options.ReduceAstJumps) { - astCompileUnit.AcceptVisitor(new Transforms.Ast.RemoveGotos(), null); - astCompileUnit.AcceptVisitor(new Transforms.Ast.RemoveDeadLabels(), null); - } - if (Options.ReduceAstLoops) { - astCompileUnit.AcceptVisitor(new Transforms.Ast.RestoreLoop(), null); - } - if (Options.ReduceAstOther) { - astCompileUnit.AcceptVisitor(new Transforms.Ast.RemoveEmptyElseBody(), null); - astCompileUnit.AcceptVisitor(new Transforms.Ast.PushNegation(), null); - } - } - if (Options.ReduceAstOther) { - astCompileUnit.AcceptVisitor(new Transforms.Ast.SimplifyTypeReferences(), null); - } - if (Options.ReduceAstLoops) { - //astCompileUnit.AcceptVisitor(new Transforms.Ast.RestoreLoop(), null); - } - - astCompileUnit.AcceptVisitor(new Transforms.Ast.ConvertConstructorCallIntoInitializer(), null); - astCompileUnit.AcceptVisitor(new Transforms.Ast.ReplaceMethodCallsWithOperators(), null); + Transforms.TransformationPipeline.RunTransformations(astCompileUnit); astCompileUnit.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }, null); var outputFormatter = new TextOutputFormatter(output); @@ -489,7 +466,7 @@ namespace Decompiler return astField; } - IEnumerable MakeParameters(IEnumerable paramCol) + public static IEnumerable MakeParameters(IEnumerable paramCol) { foreach(ParameterDefinition paramDef in paramCol) { ParameterDeclaration astParam = new ParameterDeclaration(); diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs index 247649042..36f6d4eb5 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs @@ -6,7 +6,7 @@ using System.Linq; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; -namespace Decompiler.Transforms.Ast +namespace Decompiler.Transforms { public class ConvertConstructorCallIntoInitializer : DepthFirstAstVisitor { diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 59ce084c1..245df79a1 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -10,6 +10,7 @@ namespace Decompiler.Transforms { /// /// Converts "new Action(obj, ldftn(func))" into "new Action(obj.func)". + /// For anonymous methods, creates an AnonymousMethodExpression. /// public class DelegateConstruction : DepthFirstAstVisitor { @@ -42,7 +43,9 @@ namespace Decompiler.Transforms IdentifierExpression methodIdent = (IdentifierExpression)((InvocationExpression)func).Arguments.Single(); MethodReference method = methodIdent.Annotation(); if (method != null) { - // Perform the transformation: + if (HandleAnonymousMethod(objectCreateExpression, obj, method, annotation.ContainingType)) + return null; + // Perform the transformation to "new Action(obj.func)". obj.Remove(); methodIdent.Remove(); if (!annotation.IsVirtual && obj is ThisReferenceExpression) { @@ -83,5 +86,55 @@ namespace Decompiler.Transforms } return base.VisitObjectCreateExpression(objectCreateExpression, data); } + + bool HandleAnonymousMethod(ObjectCreateExpression objectCreateExpression, Expression target, MethodReference methodRef, TypeDefinition containingType) + { + // Anonymous methods are defined in the same assembly, so there's no need to Resolve(). + MethodDefinition method = methodRef as MethodDefinition; + if (method == null || !method.Name.StartsWith("<", StringComparison.Ordinal)) + return false; + if (!(IsCompilerGenerated(method) || IsCompilerGenerated(method.DeclaringType))) + return false; + TypeDefinition methodContainingType = method.DeclaringType; + // check that methodContainingType is within containingType + while (methodContainingType != containingType) { + methodContainingType = methodContainingType.DeclaringType; + if (methodContainingType == null) + return false; + } + + // Decompile the anonymous method: + BlockStatement body = AstMethodBodyBuilder.CreateMethodBody(method); + TransformationPipeline.RunTransformationsUntil(body, v => v is DelegateConstruction); + body.AcceptVisitor(this, null); + + AnonymousMethodExpression ame = new AnonymousMethodExpression(); + if (method.Parameters.All(p => string.IsNullOrEmpty(p.Name))) { + ame.HasParameterList = false; + } else { + ame.HasParameterList = true; + ame.Parameters = AstBuilder.MakeParameters(method.Parameters); + } + ame.Body = body; + // Replace all occurrences of 'this' in the method body with the delegate's target: + foreach (AstNode node in body.Descendants) { + if (node is ThisReferenceExpression) + node.ReplaceWith(target.Clone()); + + } + objectCreateExpression.ReplaceWith(ame); + return true; + } + + bool IsCompilerGenerated(ICustomAttributeProvider provider) + { + if (provider.HasCustomAttributes) { + foreach (CustomAttribute a in provider.CustomAttributes) { + if (a.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute") + return true; + } + } + return false; + } } } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs index 2e2bb2dfa..1b949df27 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs @@ -5,7 +5,7 @@ using Mono.Cecil; using Ast = ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp; -namespace Decompiler.Transforms.Ast +namespace Decompiler.Transforms { /// /// Replaces method calls with the appropriate operator expressions. diff --git a/ICSharpCode.Decompiler/Ast/Transforms/SimplifyTypeReferences.cs b/ICSharpCode.Decompiler/Ast/Transforms/SimplifyTypeReferences.cs deleted file mode 100644 index c179ffdac..000000000 --- a/ICSharpCode.Decompiler/Ast/Transforms/SimplifyTypeReferences.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; - -using ICSharpCode.NRefactory.CSharp; - -namespace Decompiler.Transforms.Ast -{ - public class SimplifyTypeReferences: DepthFirstAstVisitor - { - string currentNamepace = string.Empty; - string currentClass = null; - - public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data) - { - currentNamepace = namespaceDeclaration.Name; - base.VisitNamespaceDeclaration(namespaceDeclaration, data); - currentNamepace = string.Empty; - return null; - } - - public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) - { - currentClass = currentNamepace + "." + typeDeclaration.Name; - base.VisitTypeDeclaration(typeDeclaration, data); - currentClass = null; - return null; - } - } -} diff --git a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs new file mode 100644 index 000000000..c11418dc3 --- /dev/null +++ b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs @@ -0,0 +1,50 @@ +// 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 ICSharpCode.NRefactory.CSharp; + +namespace Decompiler.Transforms +{ + public static class TransformationPipeline + { + static IAstVisitor[] CreatePipeline() + { + return new IAstVisitor[] { + new DelegateConstruction(), + new ConvertConstructorCallIntoInitializer(), + new ReplaceMethodCallsWithOperators() + }; + } + + public static void RunTransformations(AstNode node) + { + RunTransformationsUntil(node, v => false); + } + + public static void RunTransformationsUntil(AstNode node, Predicate> abortCondition) + { + if (node == null) + return; + for (int i = 0; i < 4; i++) { + if (Options.ReduceAstJumps) { + node.AcceptVisitor(new Transforms.Ast.RemoveGotos(), null); + node.AcceptVisitor(new Transforms.Ast.RemoveDeadLabels(), null); + } + if (Options.ReduceAstLoops) { + node.AcceptVisitor(new Transforms.Ast.RestoreLoop(), null); + } + if (Options.ReduceAstOther) { + node.AcceptVisitor(new Transforms.Ast.RemoveEmptyElseBody(), null); + node.AcceptVisitor(new Transforms.Ast.PushNegation(), null); + } + } + + foreach (var visitor in CreatePipeline()) { + if (abortCondition(visitor)) + return; + node.AcceptVisitor(visitor, null); + } + } + } +} diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 25cd96826..9beef196d 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -62,7 +62,7 @@ - +