From 016b54563f620ca71bc8d2a64c6f2983e80f08b7 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 10:50:59 +0100 Subject: [PATCH] Add cancellation support to the decompiler. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 16 +++++---- .../Ast/AstMethodBodyBuilder.cs | 9 ++++- .../Ast/Transforms/DelegateConstruction.cs | 8 +++-- .../Ast/Transforms/TransformationPipeline.cs | 16 ++++----- ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 36 ++++++------------- ILSpy/CSharpLanguage.cs | 22 +++++++----- 6 files changed, 54 insertions(+), 53 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index d3480ca42..0f2560437 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using ICSharpCode.Decompiler; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; @@ -12,6 +13,7 @@ namespace Decompiler { public class AstBuilder { + public CancellationToken CancellationToken { get; set; } CompilationUnit astCompileUnit = new CompilationUnit(); Dictionary astNamespaces = new Dictionary(); @@ -22,7 +24,7 @@ namespace Decompiler public void GenerateCode(ITextOutput output, Predicate> transformAbortCondition) { - Transforms.TransformationPipeline.RunTransformationsUntil(astCompileUnit, transformAbortCondition); + Transforms.TransformationPipeline.RunTransformationsUntil(astCompileUnit, transformAbortCondition, this.CancellationToken); astCompileUnit.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }, null); var outputFormatter = new TextOutputFormatter(output); @@ -412,7 +414,7 @@ namespace Decompiler astMethod.Parameters = MakeParameters(methodDef.Parameters); if (!methodDef.DeclaringType.IsInterface) { astMethod.Modifiers = ConvertModifiers(methodDef); - astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef); + astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, this.CancellationToken); } return astMethod; } @@ -426,7 +428,7 @@ namespace Decompiler astMethod.Modifiers &= ~Modifiers.VisibilityMask; } astMethod.Parameters = MakeParameters(methodDef.Parameters); - astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef); + astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, this.CancellationToken); return astMethod; } @@ -438,12 +440,12 @@ namespace Decompiler astProp.ReturnType = ConvertType(propDef.PropertyType, propDef); if (propDef.GetMethod != null) { astProp.Getter = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(propDef.GetMethod) + Body = AstMethodBodyBuilder.CreateMethodBody(propDef.GetMethod, this.CancellationToken) }; } if (propDef.SetMethod != null) { astProp.Setter = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(propDef.SetMethod) + Body = AstMethodBodyBuilder.CreateMethodBody(propDef.SetMethod, this.CancellationToken) }; } return astProp; @@ -457,12 +459,12 @@ namespace Decompiler astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod); if (eventDef.AddMethod != null) { astEvent.AddAccessor = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.AddMethod) + Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.AddMethod, this.CancellationToken) }; } if (eventDef.RemoveMethod != null) { astEvent.RemoveAccessor = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.RemoveMethod) + Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.RemoveMethod, this.CancellationToken) }; } return astEvent; diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index b80aa09ac..6edf089b1 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading; using Ast = ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp; using Cecil = Mono.Cecil; @@ -15,11 +16,13 @@ namespace Decompiler public class AstMethodBodyBuilder { MethodDefinition methodDef; + CancellationToken cancellationToken; HashSet definedLocalVars = new HashSet(); - public static BlockStatement CreateMethodBody(MethodDefinition methodDef) + public static BlockStatement CreateMethodBody(MethodDefinition methodDef, CancellationToken cancellationToken) { AstMethodBodyBuilder builder = new AstMethodBodyBuilder(); + builder.cancellationToken = cancellationToken; builder.methodDef = methodDef; if (Debugger.IsAttached) { return builder.CreateMethodBody(); @@ -55,12 +58,15 @@ namespace Decompiler { if (methodDef.Body == null) return null; + cancellationToken.ThrowIfCancellationRequested(); ILBlock ilMethod = new ILBlock(); ILAstBuilder astBuilder = new ILAstBuilder(); ilMethod.Body = astBuilder.Build(methodDef, true); + cancellationToken.ThrowIfCancellationRequested(); ILAstOptimizer bodyGraph = new ILAstOptimizer(); bodyGraph.Optimize(ilMethod); + cancellationToken.ThrowIfCancellationRequested(); List intNames = new List(new string[] {"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"}); Dictionary typeNames = new Dictionary(); @@ -102,6 +108,7 @@ namespace Decompiler // astBlock.Children.Add(astLocalVar); } + cancellationToken.ThrowIfCancellationRequested(); Ast.BlockStatement astBlock = TransformBlock(ilMethod); CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments return astBlock; diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index c2a4f7245..da30a39c8 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -3,6 +3,8 @@ using System; using System.Linq; +using System.Threading; + using ICSharpCode.Decompiler; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; @@ -15,6 +17,8 @@ namespace Decompiler.Transforms /// public class DelegateConstruction : DepthFirstAstVisitor { + public CancellationToken CancellationToken { get; set; } + internal sealed class Annotation { /// @@ -107,8 +111,8 @@ namespace Decompiler.Transforms } // Decompile the anonymous method: - BlockStatement body = AstMethodBodyBuilder.CreateMethodBody(method); - TransformationPipeline.RunTransformationsUntil(body, v => v is DelegateConstruction); + BlockStatement body = AstMethodBodyBuilder.CreateMethodBody(method, this.CancellationToken); + TransformationPipeline.RunTransformationsUntil(body, v => v is DelegateConstruction, this.CancellationToken); body.AcceptVisitor(this, null); AnonymousMethodExpression ame = new AnonymousMethodExpression(); diff --git a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs index 8c2d5b235..e0cc0a415 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs @@ -2,31 +2,28 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; +using System.Threading; using ICSharpCode.NRefactory.CSharp; namespace Decompiler.Transforms { public static class TransformationPipeline { - public static IAstVisitor[] CreatePipeline() + public static IAstVisitor[] CreatePipeline(CancellationToken cancellationToken) { return new IAstVisitor[] { - new DelegateConstruction(), + new DelegateConstruction() { CancellationToken = cancellationToken }, new ConvertConstructorCallIntoInitializer(), new ReplaceMethodCallsWithOperators() }; } - public static void RunTransformations(AstNode node) - { - RunTransformationsUntil(node, null); - } - - public static void RunTransformationsUntil(AstNode node, Predicate> abortCondition) + public static void RunTransformationsUntil(AstNode node, Predicate> abortCondition, CancellationToken cancellationToken) { if (node == null) return; for (int i = 0; i < 4; i++) { + cancellationToken.ThrowIfCancellationRequested(); if (Options.ReduceAstJumps) { node.AcceptVisitor(new Transforms.Ast.RemoveGotos(), null); node.AcceptVisitor(new Transforms.Ast.RemoveDeadLabels(), null); @@ -40,7 +37,8 @@ namespace Decompiler.Transforms } } - foreach (var visitor in CreatePipeline()) { + foreach (var visitor in CreatePipeline(cancellationToken)) { + cancellationToken.ThrowIfCancellationRequested(); if (abortCondition != null && abortCondition(visitor)) return; node.AcceptVisitor(visitor, null); diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index 0361aad76..ff79656b8 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -3,9 +3,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; + using Decompiler.ControlFlow; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; +using ICSharpCode.NRefactory.Utils; using Mono.Cecil; using Mono.Cecil.Cil; using Cecil = Mono.Cecil; @@ -16,31 +18,7 @@ namespace Decompiler { public IEnumerable GetSelfAndChildrenRecursive() where T: ILNode { - if (this is T) - yield return (T)this; - - Stack> stack = new Stack>(); - try { - stack.Push(GetChildren().GetEnumerator()); - while (stack.Count > 0) { - while (stack.Peek().MoveNext()) { - ILNode element = stack.Peek().Current; - if (element != null) { - if (element is T) - yield return (T)element; - IEnumerable children = element.GetChildren(); - if (children != null) { - stack.Push(children.GetEnumerator()); - } - } - } - stack.Pop().Dispose(); - } - } finally { - while (stack.Count > 0) { - stack.Pop().Dispose(); - } - } + return TreeTraversal.PreOrder(this, c => c != null ? c.GetChildren() : null).OfType(); } public virtual IEnumerable GetChildren() @@ -115,7 +93,9 @@ namespace Decompiler output.Write("catch "); output.WriteReference(ExceptionType.FullName, ExceptionType); output.WriteLine(" {"); + output.Indent(); base.WriteTo(output); + output.Unindent(); output.WriteLine("}"); } } @@ -137,14 +117,18 @@ namespace Decompiler public override void WriteTo(ITextOutput output) { output.WriteLine(".try {"); + output.Indent(); TryBlock.WriteTo(output); + output.Unindent(); output.WriteLine("}"); foreach (CatchBlock block in CatchBlocks) { block.WriteTo(output); } if (FinallyBlock != null) { output.WriteLine("finally {"); + output.Indent(); FinallyBlock.WriteTo(output); + output.Unindent(); output.WriteLine("}"); } } @@ -250,7 +234,7 @@ namespace Decompiler first = false; } foreach (ILExpression arg in this.Arguments) { - if (!first) output.Write(','); + if (!first) output.Write(", "); arg.WriteTo(output); first = false; } diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs index e8800f8b0..ee92456e6 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/CSharpLanguage.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using Decompiler; using Decompiler.Transforms; using ICSharpCode.Decompiler; @@ -34,7 +35,7 @@ namespace ICSharpCode.ILSpy public class CSharpLanguage : Language { string name = "C#"; - Predicate> transformAbortCondition; + Predicate> transformAbortCondition = null; public CSharpLanguage() { @@ -44,7 +45,7 @@ namespace ICSharpCode.ILSpy internal static IEnumerable GetDebugLanguages() { string lastTransformName = "no transforms"; - foreach (Type _transformType in TransformationPipeline.CreatePipeline().Select(v => v.GetType()).Distinct()) { + foreach (Type _transformType in TransformationPipeline.CreatePipeline(CancellationToken.None).Select(v => v.GetType()).Distinct()) { Type transformType = _transformType; // copy for lambda yield return new CSharpLanguage { transformAbortCondition = v => transformType.IsInstanceOfType(v), @@ -68,35 +69,35 @@ namespace ICSharpCode.ILSpy public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = new AstBuilder(); + AstBuilder codeDomBuilder = CreateAstBuilder(options); codeDomBuilder.AddMethod(method); codeDomBuilder.GenerateCode(output, transformAbortCondition); } public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = new AstBuilder(); + AstBuilder codeDomBuilder = CreateAstBuilder(options); codeDomBuilder.AddProperty(property); codeDomBuilder.GenerateCode(output, transformAbortCondition); } public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = new AstBuilder(); + AstBuilder codeDomBuilder = CreateAstBuilder(options); codeDomBuilder.AddField(field); codeDomBuilder.GenerateCode(output, transformAbortCondition); } public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = new AstBuilder(); + AstBuilder codeDomBuilder = CreateAstBuilder(options); codeDomBuilder.AddEvent(ev); codeDomBuilder.GenerateCode(output, transformAbortCondition); } public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = new AstBuilder(); + AstBuilder codeDomBuilder = CreateAstBuilder(options); codeDomBuilder.AddType(type); codeDomBuilder.GenerateCode(output, transformAbortCondition); } @@ -105,7 +106,7 @@ namespace ICSharpCode.ILSpy { if (options.FullDecompilation) { foreach (TypeDefinition type in assembly.MainModule.Types) { - AstBuilder codeDomBuilder = new AstBuilder(); + AstBuilder codeDomBuilder = CreateAstBuilder(options); codeDomBuilder.AddType(type); codeDomBuilder.GenerateCode(output, transformAbortCondition); output.WriteLine(); @@ -115,6 +116,11 @@ namespace ICSharpCode.ILSpy } } + AstBuilder CreateAstBuilder(DecompilationOptions options) + { + return new AstBuilder { CancellationToken = options.CancellationToken }; + } + public override string TypeToString(TypeReference type, bool includeNamespace, ICustomAttributeProvider typeAttributes) { AstType astType = AstBuilder.ConvertType(type, typeAttributes);