From d4c275410eada96489efcf7b7b2d1bc85f06e266 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 19:21:11 +0100 Subject: [PATCH] Implemented removal of closures ("display class" introduced by C# compiler for anonymous methods) --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 37 +++-- .../Ast/AstMethodBodyBuilder.cs | 51 ++++--- .../Ast/DecompilerContext.cs | 21 +++ .../Ast/Transforms/ContextTrackingVisitor.cs | 69 +++++++++ .../Ast/Transforms/DelegateConstruction.cs | 135 ++++++++++++++---- .../Ast/Transforms/TransformationPipeline.cs | 12 +- .../ICSharpCode.Decompiler.csproj | 2 + ICSharpCode.Decompiler/Tests/TestRunner.cs | 2 +- ILSpy/CSharpLanguage.cs | 25 ++-- 9 files changed, 280 insertions(+), 74 deletions(-) create mode 100644 ICSharpCode.Decompiler/Ast/DecompilerContext.cs create mode 100644 ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 0f2560437..d4c841fd1 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -13,10 +13,17 @@ namespace Decompiler { public class AstBuilder { - public CancellationToken CancellationToken { get; set; } + DecompilerContext context = new DecompilerContext(); CompilationUnit astCompileUnit = new CompilationUnit(); Dictionary astNamespaces = new Dictionary(); + public AstBuilder(DecompilerContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + this.context = context; + } + public void GenerateCode(ITextOutput output) { GenerateCode(output, null); @@ -24,7 +31,7 @@ namespace Decompiler public void GenerateCode(ITextOutput output, Predicate> transformAbortCondition) { - Transforms.TransformationPipeline.RunTransformationsUntil(astCompileUnit, transformAbortCondition, this.CancellationToken); + Transforms.TransformationPipeline.RunTransformationsUntil(astCompileUnit, transformAbortCondition, context); astCompileUnit.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }, null); var outputFormatter = new TextOutputFormatter(output); @@ -105,6 +112,7 @@ namespace Decompiler public TypeDeclaration CreateType(TypeDefinition typeDef) { TypeDeclaration astType = new TypeDeclaration(); + astType.AddAnnotation(typeDef); astType.Modifiers = ConvertModifiers(typeDef); astType.Name = typeDef.Name; @@ -409,12 +417,13 @@ namespace Decompiler MethodDeclaration CreateMethod(MethodDefinition methodDef) { MethodDeclaration astMethod = new MethodDeclaration(); + astMethod.AddAnnotation(methodDef); astMethod.Name = methodDef.Name; astMethod.ReturnType = ConvertType(methodDef.ReturnType, methodDef.MethodReturnType); astMethod.Parameters = MakeParameters(methodDef.Parameters); if (!methodDef.DeclaringType.IsInterface) { astMethod.Modifiers = ConvertModifiers(methodDef); - astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, this.CancellationToken); + astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, context); } return astMethod; } @@ -422,31 +431,33 @@ namespace Decompiler ConstructorDeclaration CreateConstructor(MethodDefinition methodDef) { ConstructorDeclaration astMethod = new ConstructorDeclaration(); + astMethod.AddAnnotation(methodDef); astMethod.Modifiers = ConvertModifiers(methodDef); if (methodDef.IsStatic) { // don't show visibility for static ctors astMethod.Modifiers &= ~Modifiers.VisibilityMask; } astMethod.Parameters = MakeParameters(methodDef.Parameters); - astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, this.CancellationToken); + astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, context); return astMethod; } PropertyDeclaration CreateProperty(PropertyDefinition propDef) { PropertyDeclaration astProp = new PropertyDeclaration(); + astProp.AddAnnotation(propDef); astProp.Modifiers = ConvertModifiers(propDef.GetMethod ?? propDef.SetMethod); astProp.Name = propDef.Name; astProp.ReturnType = ConvertType(propDef.PropertyType, propDef); if (propDef.GetMethod != null) { astProp.Getter = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(propDef.GetMethod, this.CancellationToken) - }; + Body = AstMethodBodyBuilder.CreateMethodBody(propDef.GetMethod, context) + }.WithAnnotation(propDef.GetMethod); } if (propDef.SetMethod != null) { astProp.Setter = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(propDef.SetMethod, this.CancellationToken) - }; + Body = AstMethodBodyBuilder.CreateMethodBody(propDef.SetMethod, context) + }.WithAnnotation(propDef.SetMethod); } return astProp; } @@ -454,18 +465,19 @@ namespace Decompiler CustomEventDeclaration CreateEvent(EventDefinition eventDef) { CustomEventDeclaration astEvent = new CustomEventDeclaration(); + astEvent.AddAnnotation(eventDef); astEvent.Name = eventDef.Name; astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef); astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod); if (eventDef.AddMethod != null) { astEvent.AddAccessor = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.AddMethod, this.CancellationToken) - }; + Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.AddMethod, context) + }.WithAnnotation(eventDef.AddMethod); } if (eventDef.RemoveMethod != null) { astEvent.RemoveAccessor = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.RemoveMethod, this.CancellationToken) - }; + Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.RemoveMethod, context) + }.WithAnnotation(eventDef.RemoveMethod); } return astEvent; } @@ -473,6 +485,7 @@ namespace Decompiler FieldDeclaration CreateField(FieldDefinition fieldDef) { FieldDeclaration astField = new FieldDeclaration(); + astField.AddAnnotation(fieldDef); VariableInitializer initializer = new VariableInitializer(fieldDef.Name); astField.AddChild(initializer, FieldDeclaration.Roles.Variable); astField.ReturnType = ConvertType(fieldDef.FieldType, fieldDef); diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 68cff0617..cbdc86518 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -17,25 +17,32 @@ namespace Decompiler { MethodDefinition methodDef; TypeSystem typeSystem; - CancellationToken cancellationToken; + DecompilerContext context; HashSet definedLocalVars = new HashSet(); - public static BlockStatement CreateMethodBody(MethodDefinition methodDef, CancellationToken cancellationToken) + public static BlockStatement CreateMethodBody(MethodDefinition methodDef, DecompilerContext context) { - AstMethodBodyBuilder builder = new AstMethodBodyBuilder(); - builder.cancellationToken = cancellationToken; - builder.methodDef = methodDef; - builder.typeSystem = methodDef.Module.TypeSystem; - if (Debugger.IsAttached) { - return builder.CreateMethodBody(); - } else { - try { + MethodDefinition oldCurrentMethod = context.CurrentMethod; + Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef); + context.CurrentMethod = methodDef; + try { + AstMethodBodyBuilder builder = new AstMethodBodyBuilder(); + builder.methodDef = methodDef; + builder.context = context; + builder.typeSystem = methodDef.Module.TypeSystem; + if (Debugger.IsAttached) { return builder.CreateMethodBody(); - } catch (OperationCanceledException) { - throw; - } catch (Exception ex) { - throw new ICSharpCode.Decompiler.DecompilerException(methodDef, ex); + } else { + try { + return builder.CreateMethodBody(); + } catch (OperationCanceledException) { + throw; + } catch (Exception ex) { + throw new ICSharpCode.Decompiler.DecompilerException(methodDef, ex); + } } + } finally { + context.CurrentMethod = oldCurrentMethod; } } @@ -60,15 +67,15 @@ namespace Decompiler { if (methodDef.Body == null) return null; - cancellationToken.ThrowIfCancellationRequested(); + context.CancellationToken.ThrowIfCancellationRequested(); ILBlock ilMethod = new ILBlock(); ILAstBuilder astBuilder = new ILAstBuilder(); ilMethod.Body = astBuilder.Build(methodDef, true); - cancellationToken.ThrowIfCancellationRequested(); + context.CancellationToken.ThrowIfCancellationRequested(); ILAstOptimizer bodyGraph = new ILAstOptimizer(); bodyGraph.Optimize(methodDef, ilMethod); - cancellationToken.ThrowIfCancellationRequested(); + context.CancellationToken.ThrowIfCancellationRequested(); List intNames = new List(new string[] {"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"}); Dictionary typeNames = new Dictionary(); @@ -110,7 +117,7 @@ namespace Decompiler // astBlock.Children.Add(astLocalVar); } - cancellationToken.ThrowIfCancellationRequested(); + context.CancellationToken.ThrowIfCancellationRequested(); Ast.BlockStatement astBlock = TransformBlock(ilMethod); CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments return astBlock; @@ -528,7 +535,7 @@ namespace Decompiler expr.TypeArguments = ConvertTypeArguments(cecilMethod); expr.AddAnnotation(cecilMethod); return new IdentifierExpression("ldftn").Invoke(expr) - .WithAnnotation(new Transforms.DelegateConstruction.Annotation(false, methodDef.DeclaringType)); + .WithAnnotation(new Transforms.DelegateConstruction.Annotation(false)); } case Code.Ldvirtftn: { @@ -537,7 +544,7 @@ namespace Decompiler expr.TypeArguments = ConvertTypeArguments(cecilMethod); expr.AddAnnotation(cecilMethod); return new IdentifierExpression("ldvirtftn").Invoke(expr) - .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true, methodDef.DeclaringType)); + .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true)); } case Code.Calli: throw new NotImplementedException(); @@ -557,13 +564,13 @@ namespace Decompiler if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) { return new Ast.ThisReferenceExpression(); } else { - return new Ast.IdentifierExpression(((ParameterDefinition)operand).Name); + return new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand); } case Code.Ldarga: if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) { return MakeRef(new Ast.ThisReferenceExpression()); } else { - return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name)); + return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand)); } case Code.Ldc_I4: if (byteCode.InferredType == typeSystem.Boolean && (int)operand == 0) diff --git a/ICSharpCode.Decompiler/Ast/DecompilerContext.cs b/ICSharpCode.Decompiler/Ast/DecompilerContext.cs new file mode 100644 index 000000000..108032e40 --- /dev/null +++ b/ICSharpCode.Decompiler/Ast/DecompilerContext.cs @@ -0,0 +1,21 @@ +// 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.Threading; +using Mono.Cecil; + +namespace Decompiler +{ + public class DecompilerContext + { + public CancellationToken CancellationToken; + public TypeDefinition CurrentType; + public MethodDefinition CurrentMethod; + + public DecompilerContext Clone() + { + return (DecompilerContext)MemberwiseClone(); + } + } +} diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs b/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs new file mode 100644 index 000000000..73b3d98fd --- /dev/null +++ b/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs @@ -0,0 +1,69 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Diagnostics; +using ICSharpCode.NRefactory.CSharp; +using Mono.Cecil; + +namespace Decompiler.Transforms +{ + /// + /// Base class for AST visitors that need the current type/method context info. + /// + public abstract class ContextTrackingVisitor : DepthFirstAstVisitor + { + protected readonly DecompilerContext context; + + protected ContextTrackingVisitor(DecompilerContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + this.context = context; + } + + public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) + { + TypeDefinition oldType = context.CurrentType; + try { + context.CurrentType = typeDeclaration.Annotation(); + return base.VisitTypeDeclaration(typeDeclaration, data); + } finally { + context.CurrentType = oldType; + } + } + + public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) + { + Debug.Assert(context.CurrentMethod == null); + try { + context.CurrentMethod = methodDeclaration.Annotation(); + return base.VisitMethodDeclaration(methodDeclaration, data); + } finally { + context.CurrentMethod = null; + } + } + + public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) + { + Debug.Assert(context.CurrentMethod == null); + try { + context.CurrentMethod = constructorDeclaration.Annotation(); + return base.VisitConstructorDeclaration(constructorDeclaration, data); + } finally { + context.CurrentMethod = null; + } + } + + public override object VisitAccessor(Accessor accessor, object data) + { + Debug.Assert(context.CurrentMethod == null); + try { + context.CurrentMethod = accessor.Annotation(); + return base.VisitAccessor(accessor, data); + } finally { + context.CurrentMethod = null; + } + } + } +} diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index da30a39c8..15a098b9d 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -2,9 +2,9 @@ // 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 System.Threading; - using ICSharpCode.Decompiler; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; @@ -15,10 +15,8 @@ namespace Decompiler.Transforms /// Converts "new Action(obj, ldftn(func))" into "new Action(obj.func)". /// For anonymous methods, creates an AnonymousMethodExpression. /// - public class DelegateConstruction : DepthFirstAstVisitor + public class DelegateConstruction : ContextTrackingVisitor { - public CancellationToken CancellationToken { get; set; } - internal sealed class Annotation { /// @@ -26,18 +24,16 @@ namespace Decompiler.Transforms /// public readonly bool IsVirtual; - /// - /// The method being decompiled. - /// - public readonly TypeDefinition ContainingType; - - public Annotation(bool isVirtual, TypeDefinition containingType) + public Annotation(bool isVirtual) { this.IsVirtual = isVirtual; - this.ContainingType = containingType; } } + public DelegateConstruction(DecompilerContext context) : base(context) + { + } + public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data) { if (objectCreateExpression.Arguments.Count() == 2) { @@ -48,14 +44,14 @@ namespace Decompiler.Transforms IdentifierExpression methodIdent = (IdentifierExpression)((InvocationExpression)func).Arguments.Single(); MethodReference method = methodIdent.Annotation(); if (method != null) { - if (HandleAnonymousMethod(objectCreateExpression, obj, method, annotation.ContainingType)) + if (HandleAnonymousMethod(objectCreateExpression, obj, method)) return null; // Perform the transformation to "new Action(obj.func)". obj.Remove(); methodIdent.Remove(); if (!annotation.IsVirtual && obj is ThisReferenceExpression) { // maybe it's getting the pointer of a base method? - if (method.DeclaringType != annotation.ContainingType) { + if (method.DeclaringType != context.CurrentType) { obj = new BaseReferenceExpression(); } } @@ -94,25 +90,21 @@ namespace Decompiler.Transforms return base.VisitObjectCreateExpression(objectCreateExpression, data); } - bool HandleAnonymousMethod(ObjectCreateExpression objectCreateExpression, Expression target, MethodReference methodRef, TypeDefinition containingType) + bool HandleAnonymousMethod(ObjectCreateExpression objectCreateExpression, Expression target, MethodReference methodRef) { // 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 (!(method.IsCompilerGenerated() || method.DeclaringType.IsCompilerGenerated())) + if (!(method.IsCompilerGenerated() || IsPotentialClosure(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, this.CancellationToken); - TransformationPipeline.RunTransformationsUntil(body, v => v is DelegateConstruction, this.CancellationToken); + + DecompilerContext subContext = context.Clone(); + subContext.CurrentMethod = method; + BlockStatement body = AstMethodBodyBuilder.CreateMethodBody(method, subContext); + TransformationPipeline.RunTransformationsUntil(body, v => v is DelegateConstruction, subContext); body.AcceptVisitor(this, null); AnonymousMethodExpression ame = new AnonymousMethodExpression(); @@ -132,5 +124,100 @@ namespace Decompiler.Transforms objectCreateExpression.ReplaceWith(ame); return true; } + + bool IsPotentialClosure(TypeDefinition potentialDisplayClass) + { + if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGenerated()) + return false; + // check that methodContainingType is within containingType + while (potentialDisplayClass != context.CurrentType) { + potentialDisplayClass = potentialDisplayClass.DeclaringType; + if (potentialDisplayClass == null) + return false; + } + return true; + } + + public override object VisitBlockStatement(BlockStatement blockStatement, object data) + { + base.VisitBlockStatement(blockStatement, data); + foreach (VariableDeclarationStatement stmt in blockStatement.Statements.OfType()) { + if (stmt.Variables.Count() != 1) + continue; + var variable = stmt.Variables.Single(); + TypeDefinition type = stmt.Type.Annotation(); + if (!IsPotentialClosure(type)) + continue; + ObjectCreateExpression oce = variable.Initializer as ObjectCreateExpression; + if (oce == null || oce.Type.Annotation() != 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; + foreach (var identExpr in blockStatement.Descendants.OfType()) { + if (identExpr.Identifier == variable.Name) { + if (!(identExpr.Parent is MemberReferenceExpression && identExpr.Parent.Annotation() != null)) + ok = false; + } + } + if (!ok) + continue; + Dictionary dict = new Dictionary(); + // Delete the variable declaration statement: + AstNode cur; + AstNode next = stmt.NextSibling; + stmt.Remove(); + for (cur = next; cur != null; cur = next) { + next = cur.NextSibling; + + // Delete any following statements as long as they assign simple variables to the display class: + // Test for the pattern: + // "variableName.MemberName = right;" + ExpressionStatement es = cur as ExpressionStatement; + if (es == null) + break; + AssignmentExpression ae = es.Expression as AssignmentExpression; + if (ae == null || ae.Operator != AssignmentOperatorType.Assign) + break; + MemberReferenceExpression left = ae.Left as MemberReferenceExpression; + if (left == null || !IsParameter(ae.Right)) + break; + if (!(left.Target is IdentifierExpression) || (left.Target as IdentifierExpression).Identifier != variable.Name) + break; + dict[left.MemberName] = ae.Right; + es.Remove(); + } + + // Now create variables for all fields of the display class (except for those that we already handled) + foreach (FieldDefinition field in type.Fields) { + if (dict.ContainsKey(field.Name)) + continue; + VariableDeclarationStatement newVarDecl = new VariableDeclarationStatement(); + newVarDecl.Type = AstBuilder.ConvertType(field.FieldType, field); + newVarDecl.Variables = new [] { new VariableInitializer(field.Name) }; + blockStatement.InsertChildBefore(cur, newVarDecl, BlockStatement.StatementRole); + dict[field.Name] = new IdentifierExpression(field.Name); + } + + // Now figure out where the closure was accessed and use the simpler replacement expression there: + foreach (var identExpr in blockStatement.Descendants.OfType()) { + if (identExpr.Identifier == variable.Name) { + MemberReferenceExpression mre = (MemberReferenceExpression)identExpr.Parent; + Expression replacement; + if (dict.TryGetValue(mre.MemberName, out replacement)) { + mre.ReplaceWith(replacement.Clone()); + } + } + } + } + return null; + } + + bool IsParameter(Expression expr) + { + if (expr is ThisReferenceExpression) + return true; + IdentifierExpression ident = expr as IdentifierExpression; + return ident != null && ident.Annotation() != null; + } } } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs index 89c18c575..e5b5fd644 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs @@ -9,22 +9,22 @@ namespace Decompiler.Transforms { public static class TransformationPipeline { - public static IAstVisitor[] CreatePipeline(CancellationToken cancellationToken) + public static IAstVisitor[] CreatePipeline(DecompilerContext context) { return new IAstVisitor[] { new PushNegation(), - new DelegateConstruction() { CancellationToken = cancellationToken }, + new DelegateConstruction(context), new ConvertConstructorCallIntoInitializer(), new ReplaceMethodCallsWithOperators(), }; } - public static void RunTransformationsUntil(AstNode node, Predicate> abortCondition, CancellationToken cancellationToken) + public static void RunTransformationsUntil(AstNode node, Predicate> abortCondition, DecompilerContext context) { if (node == null) return; for (int i = 0; i < 4; i++) { - cancellationToken.ThrowIfCancellationRequested(); + context.CancellationToken.ThrowIfCancellationRequested(); if (Options.ReduceAstJumps) { node.AcceptVisitor(new Transforms.Ast.RemoveGotos(), null); node.AcceptVisitor(new Transforms.Ast.RemoveDeadLabels(), null); @@ -37,8 +37,8 @@ namespace Decompiler.Transforms } } - foreach (var visitor in CreatePipeline(cancellationToken)) { - cancellationToken.ThrowIfCancellationRequested(); + foreach (var visitor in CreatePipeline(context)) { + context.CancellationToken.ThrowIfCancellationRequested(); if (abortCondition != null && abortCondition(visitor)) return; node.AcceptVisitor(visitor, null); diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index c2df0e929..dfe2997d9 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -52,8 +52,10 @@ + + diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index 9c3ba5c9d..099a8f3dc 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.Tests { string code = File.ReadAllText(fileName); AssemblyDefinition assembly = Compile(code); - AstBuilder decompiler = new AstBuilder(); + AstBuilder decompiler = new AstBuilder(new DecompilerContext()); decompiler.AddAssembly(assembly); StringWriter output = new StringWriter(); decompiler.GenerateCode(new PlainTextOutput(output)); diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs index ee92456e6..f7364caaa 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/CSharpLanguage.cs @@ -45,7 +45,7 @@ namespace ICSharpCode.ILSpy internal static IEnumerable GetDebugLanguages() { string lastTransformName = "no transforms"; - foreach (Type _transformType in TransformationPipeline.CreatePipeline(CancellationToken.None).Select(v => v.GetType()).Distinct()) { + foreach (Type _transformType in TransformationPipeline.CreatePipeline(new DecompilerContext()).Select(v => v.GetType()).Distinct()) { Type transformType = _transformType; // copy for lambda yield return new CSharpLanguage { transformAbortCondition = v => transformType.IsInstanceOfType(v), @@ -69,35 +69,35 @@ namespace ICSharpCode.ILSpy public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = CreateAstBuilder(options); + AstBuilder codeDomBuilder = CreateAstBuilder(options, method.DeclaringType); codeDomBuilder.AddMethod(method); codeDomBuilder.GenerateCode(output, transformAbortCondition); } public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = CreateAstBuilder(options); + AstBuilder codeDomBuilder = CreateAstBuilder(options, property.DeclaringType); codeDomBuilder.AddProperty(property); codeDomBuilder.GenerateCode(output, transformAbortCondition); } public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = CreateAstBuilder(options); + AstBuilder codeDomBuilder = CreateAstBuilder(options, field.DeclaringType); codeDomBuilder.AddField(field); codeDomBuilder.GenerateCode(output, transformAbortCondition); } public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = CreateAstBuilder(options); + AstBuilder codeDomBuilder = CreateAstBuilder(options, ev.DeclaringType); codeDomBuilder.AddEvent(ev); codeDomBuilder.GenerateCode(output, transformAbortCondition); } public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = CreateAstBuilder(options); + AstBuilder codeDomBuilder = CreateAstBuilder(options, type); codeDomBuilder.AddType(type); codeDomBuilder.GenerateCode(output, transformAbortCondition); } @@ -106,7 +106,7 @@ namespace ICSharpCode.ILSpy { if (options.FullDecompilation) { foreach (TypeDefinition type in assembly.MainModule.Types) { - AstBuilder codeDomBuilder = CreateAstBuilder(options); + AstBuilder codeDomBuilder = CreateAstBuilder(options, type); codeDomBuilder.AddType(type); codeDomBuilder.GenerateCode(output, transformAbortCondition); output.WriteLine(); @@ -116,9 +116,13 @@ namespace ICSharpCode.ILSpy } } - AstBuilder CreateAstBuilder(DecompilationOptions options) + AstBuilder CreateAstBuilder(DecompilationOptions options, TypeDefinition currentType) { - return new AstBuilder { CancellationToken = options.CancellationToken }; + return new AstBuilder( + new DecompilerContext { + CancellationToken = options.CancellationToken, + CurrentType = currentType + }); } public override string TypeToString(TypeReference type, bool includeNamespace, ICustomAttributeProvider typeAttributes) @@ -163,6 +167,9 @@ namespace ICSharpCode.ILSpy MethodDefinition method = member as MethodDefinition; if (method != null && (method.IsGetter || method.IsSetter || method.IsAddOn || method.IsRemoveOn)) return false; + TypeDefinition type = member as TypeDefinition; + if (type != null && type.Name.StartsWith("<>c__DisplayClass", StringComparison.Ordinal) && type.IsCompilerGenerated()) + return false; return true; } }