diff --git a/ICSharpCode.Decompiler/CSharp/Annotations.cs b/ICSharpCode.Decompiler/CSharp/Annotations.cs index b9c4443c0..f44f6a477 100644 --- a/ICSharpCode.Decompiler/CSharp/Annotations.cs +++ b/ICSharpCode.Decompiler/CSharp/Annotations.cs @@ -110,6 +110,15 @@ namespace ICSharpCode.Decompiler.CSharp { return node.Annotation() ?? ErrorResolveResult.UnknownError; } + + public static ILVariable GetILVariable(this IdentifierExpression expr) + { + var rr = expr.Annotation() as ILVariableResolveResult; + if (rr != null) + return rr.Variable; + else + return null; + } } public class ILVariableResolveResult : ResolveResult diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 4d3a48c5f..13b1776b0 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -66,7 +66,7 @@ namespace ICSharpCode.Decompiler.CSharp List astTransforms = new List { //new PushNegation(), //new DelegateConstruction(context), - //new PatternStatementTransform(context), + new PatternStatementTransform(), new ReplaceMethodCallsWithOperators(), new IntroduceUnsafeModifier(), new AddCheckedBlocks(), @@ -196,7 +196,7 @@ namespace ICSharpCode.Decompiler.CSharp void RunTransforms(AstNode rootNode, ITypeResolveContext decompilationContext) { var typeSystemAstBuilder = CreateAstBuilder(decompilationContext); - var context = new TransformContext(typeSystem, decompilationContext, typeSystemAstBuilder, CancellationToken); + var context = new TransformContext(typeSystem, decompilationContext, typeSystemAstBuilder, settings, CancellationToken); foreach (var transform in astTransforms) { CancellationToken.ThrowIfCancellationRequested(); transform.Run(rootNode, context); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs index 99e996123..21814a06f 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs @@ -19,6 +19,7 @@ using System; using System.Diagnostics; using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.TypeSystem; using Mono.Cecil; namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -26,86 +27,81 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms /// /// Base class for AST visitors that need the current type/method context info. /// - public abstract class ContextTrackingVisitor : DepthFirstAstVisitor, IAstTransform + public abstract class ContextTrackingVisitor : DepthFirstAstVisitor { - protected readonly DecompilerContext context; + protected ITypeDefinition currentTypeDefinition; + protected IMethod currentMethod; - protected ContextTrackingVisitor(DecompilerContext context) + protected void Initialize(TransformContext context) { - if (context == null) - throw new ArgumentNullException("context"); - this.context = context; + currentTypeDefinition = context.DecompiledTypeDefinition; + currentMethod = context.DecompiledMember as IMethod; } - public override TResult VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) + public override TResult VisitTypeDeclaration(TypeDeclaration typeDeclaration) { - TypeDefinition oldType = context.CurrentType; + ITypeDefinition oldType = currentTypeDefinition; try { - context.CurrentType = typeDeclaration.Annotation(); - return base.VisitTypeDeclaration(typeDeclaration, data); + currentTypeDefinition = typeDeclaration.GetSymbol() as ITypeDefinition; + return base.VisitTypeDeclaration(typeDeclaration); } finally { - context.CurrentType = oldType; + currentTypeDefinition = oldType; } } - public override TResult VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) + public override TResult VisitMethodDeclaration(MethodDeclaration methodDeclaration) { - Debug.Assert(context.CurrentMethod == null); + Debug.Assert(currentMethod == null); try { - context.CurrentMethod = methodDeclaration.Annotation(); - return base.VisitMethodDeclaration(methodDeclaration, data); + currentMethod = methodDeclaration.GetSymbol() as IMethod; + return base.VisitMethodDeclaration(methodDeclaration); } finally { - context.CurrentMethod = null; + currentMethod = null; } } - public override TResult VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) + public override TResult VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) { - Debug.Assert(context.CurrentMethod == null); + Debug.Assert(currentMethod == null); try { - context.CurrentMethod = constructorDeclaration.Annotation(); - return base.VisitConstructorDeclaration(constructorDeclaration, data); + currentMethod = constructorDeclaration.GetSymbol() as IMethod; + return base.VisitConstructorDeclaration(constructorDeclaration); } finally { - context.CurrentMethod = null; + currentMethod = null; } } - public override TResult VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, object data) + public override TResult VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) { - Debug.Assert(context.CurrentMethod == null); + Debug.Assert(currentMethod == null); try { - context.CurrentMethod = destructorDeclaration.Annotation(); - return base.VisitDestructorDeclaration(destructorDeclaration, data); + currentMethod = destructorDeclaration.GetSymbol() as IMethod; + return base.VisitDestructorDeclaration(destructorDeclaration); } finally { - context.CurrentMethod = null; + currentMethod = null; } } - public override TResult VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, object data) + public override TResult VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) { - Debug.Assert(context.CurrentMethod == null); + Debug.Assert(currentMethod == null); try { - context.CurrentMethod = operatorDeclaration.Annotation(); - return base.VisitOperatorDeclaration(operatorDeclaration, data); + currentMethod = operatorDeclaration.GetSymbol() as IMethod; + return base.VisitOperatorDeclaration(operatorDeclaration); } finally { - context.CurrentMethod = null; + currentMethod = null; } } - public override TResult VisitAccessor(Accessor accessor, object data) + public override TResult VisitAccessor(Accessor accessor) { - Debug.Assert(context.CurrentMethod == null); + Debug.Assert(currentMethod == null); try { - context.CurrentMethod = accessor.Annotation(); - return base.VisitAccessor(accessor, data); + currentMethod = accessor.GetSymbol() as IMethod; + return base.VisitAccessor(accessor); } finally { - context.CurrentMethod = null; + currentMethod = null; } } - - void IAstTransform.Run(AstNode node) - { - node.AcceptVisitor(this, null); - } } } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index 924156461..d5a3d4030 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -21,7 +21,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using ICSharpCode.Decompiler.ILAst; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.Analysis; using ICSharpCode.NRefactory.PatternMatching; @@ -34,12 +33,17 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms /// public sealed class PatternStatementTransform : ContextTrackingVisitor, IAstTransform { - public PatternStatementTransform(DecompilerContext context) : base(context) + TransformContext context; + + public void Run(AstNode rootNode, TransformContext context) { + this.context = context; + base.Initialize(context); + rootNode.AcceptVisitor(this); } - + #region Visitor Overrides - protected override AstNode VisitChildren(AstNode node, object data) + protected override AstNode VisitChildren(AstNode node) { // Go through the children, and keep visiting a node as long as it changes. // Because some transforms delete/replace nodes before and after the node being transformed, we rely @@ -48,14 +52,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms AstNode oldChild; do { oldChild = child; - child = child.AcceptVisitor(this, data); + child = child.AcceptVisitor(this); Debug.Assert(child != null && child.Parent == node); } while (child != oldChild); } return node; } - public override AstNode VisitExpressionStatement(ExpressionStatement expressionStatement, object data) + public override AstNode VisitExpressionStatement(ExpressionStatement expressionStatement) { AstNode result; if (context.Settings.UsingStatement) @@ -75,25 +79,25 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (result != null) return result; } - return base.VisitExpressionStatement(expressionStatement, data); + return base.VisitExpressionStatement(expressionStatement); } - public override AstNode VisitUsingStatement(UsingStatement usingStatement, object data) + public override AstNode VisitUsingStatement(UsingStatement usingStatement) { if (context.Settings.ForEachStatement) { AstNode result = TransformForeach(usingStatement); if (result != null) return result; } - return base.VisitUsingStatement(usingStatement, data); + return base.VisitUsingStatement(usingStatement); } - public override AstNode VisitWhileStatement(WhileStatement whileStatement, object data) + public override AstNode VisitWhileStatement(WhileStatement whileStatement) { - return TransformDoWhile(whileStatement) ?? base.VisitWhileStatement(whileStatement, data); + return TransformDoWhile(whileStatement) ?? base.VisitWhileStatement(whileStatement); } - public override AstNode VisitIfElseStatement(IfElseStatement ifElseStatement, object data) + public override AstNode VisitIfElseStatement(IfElseStatement ifElseStatement) { if (context.Settings.SwitchStatementOnString) { AstNode result = TransformSwitchOnString(ifElseStatement); @@ -103,23 +107,23 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms AstNode simplifiedIfElse = SimplifyCascadingIfElseStatements(ifElseStatement); if (simplifiedIfElse != null) return simplifiedIfElse; - return base.VisitIfElseStatement(ifElseStatement, data); + return base.VisitIfElseStatement(ifElseStatement); } - public override AstNode VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data) + public override AstNode VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { if (context.Settings.AutomaticProperties) { AstNode result = TransformAutomaticProperties(propertyDeclaration); if (result != null) return result; } - return base.VisitPropertyDeclaration(propertyDeclaration, data); + return base.VisitPropertyDeclaration(propertyDeclaration); } - public override AstNode VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration, object data) + public override AstNode VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) { // first apply transforms to the accessor bodies - base.VisitCustomEventDeclaration(eventDeclaration, data); + base.VisitCustomEventDeclaration(eventDeclaration); if (context.Settings.AutomaticEvents) { AstNode result = TransformAutomaticEvents(eventDeclaration); if (result != null) @@ -128,14 +132,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return eventDeclaration; } - public override AstNode VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) + public override AstNode VisitMethodDeclaration(MethodDeclaration methodDeclaration) { - return TransformDestructor(methodDeclaration) ?? base.VisitMethodDeclaration(methodDeclaration, data); + return TransformDestructor(methodDeclaration) ?? base.VisitMethodDeclaration(methodDeclaration); } - public override AstNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement, object data) + public override AstNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement) { - return TransformTryCatchFinally(tryCatchStatement) ?? base.VisitTryCatchStatement(tryCatchStatement, data); + return TransformTryCatchFinally(tryCatchStatement) ?? base.VisitTryCatchStatement(tryCatchStatement); } #endregion @@ -216,13 +220,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms TryCatchStatement tryCatch = node.NextSibling as TryCatchStatement; Match m2 = usingTryCatchPattern.Match(tryCatch); if (!m2.Success) return null; + IL.ILVariable variable = m1.Get("variable").Single().GetILVariable(); string variableName = m1.Get("variable").Single().Identifier; - if (variableName != m2.Get("ident").Single().Identifier) + if (variable == null || variableName != m2.Get("ident").Single().Identifier) return null; if (m2.Has("valueType")) { // if there's no if(x!=null), then it must be a value type - ILVariable v = m1.Get("variable").Single().Annotation(); - if (v == null || v.Type == null || !v.Type.IsValueType) + if (variable.Type.IsReferenceType != false) return null; } @@ -271,7 +275,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms Name = variableName, Initializer = m1.Get("initializer").Single().Detach() }.CopyAnnotationsFrom(node.Expression) - .WithAnnotation(m1.Get("variable").Single().Annotation()) + .WithAnnotation(variable) } }.CopyAnnotationsFrom(node); } else { @@ -328,12 +332,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms blocks.Reverse(); // go from parent blocks to child blocks DefiniteAssignmentAnalysis daa = new DefiniteAssignmentAnalysis(blocks[0], context.CancellationToken); declarationPoint = null; - foreach (BlockStatement block in blocks) { + return false; + /*foreach (BlockStatement block in blocks) { if (!DeclareVariables.FindDeclarationPoint(daa, varDecl, block, out declarationPoint)) { return false; } } - return true; + return true;*/ } /// @@ -438,7 +443,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms VariableName = itemVar.Identifier, InExpression = m.Get("collection").Single().Detach(), EmbeddedStatement = newBody - }.WithAnnotation(itemVarDecl.Variables.Single().Annotation()); + }.WithAnnotation(itemVarDecl.Variables.Single().Annotation()); if (foreachStatement.InExpression is BaseReferenceExpression) { foreachStatement.InExpression = new ThisReferenceExpression().CopyAnnotationsFrom(foreachStatement.InExpression); } @@ -532,7 +537,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { VariableType = itemVarDecl.Type.Clone(), VariableName = itemVar.Identifier, - }.WithAnnotation(itemVarDecl.Variables.Single().Annotation()); + }.WithAnnotation(itemVarDecl.Variables.Single().Annotation()); BlockStatement body = new BlockStatement(); foreachStatement.EmbeddedStatement = body; ((BlockStatement)node.Parent).Statements.InsertBefore(node, foreachStatement); @@ -628,7 +633,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (m.Success) { DoWhileStatement doLoop = new DoWhileStatement(); doLoop.Condition = new UnaryOperatorExpression(UnaryOperatorType.Not, m.Get("condition").Single().Detach()); - doLoop.Condition.AcceptVisitor(new PushNegation(), null); + //doLoop.Condition.AcceptVisitor(new PushNegation(), null); BlockStatement block = (BlockStatement)whileLoop.EmbeddedStatement; block.Statements.Last().Remove(); // remove if statement doLoop.EmbeddedStatement = block.Detach(); @@ -1069,7 +1074,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms FieldDefinition field = eventDef.DeclaringType.Fields.FirstOrDefault(f => f.Name == ev.Name); if (field != null) { ed.AddAnnotation(field); - AstBuilder.ConvertAttributes(ed, field, "field"); + // TODO AstBuilder.ConvertAttributes(ed, field, "field"); } } @@ -1102,7 +1107,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms methodDef.Attributes.MoveTo(dd.Attributes); dd.Modifiers = methodDef.Modifiers & ~(Modifiers.Protected | Modifiers.Override); dd.Body = m.Get("body").Single().Detach(); - dd.Name = AstBuilder.CleanName(context.CurrentType.Name); + dd.Name = currentTypeDefinition.Name; methodDef.ReplaceWith(dd); return dd; } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/TransformContext.cs b/ICSharpCode.Decompiler/CSharp/Transforms/TransformContext.cs index bd869c6a7..212ef2457 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/TransformContext.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/TransformContext.cs @@ -32,6 +32,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms public readonly DecompilerTypeSystem TypeSystem; public readonly CancellationToken CancellationToken; public readonly TypeSystemAstBuilder TypeSystemAstBuilder; + public readonly DecompilerSettings Settings; + readonly ITypeResolveContext decompilationContext; /// @@ -48,11 +50,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms get { return decompilationContext.CurrentTypeDefinition; } } - internal TransformContext(DecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, TypeSystemAstBuilder typeSystemAstBuilder, CancellationToken cancellationToken) + internal TransformContext(DecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, TypeSystemAstBuilder typeSystemAstBuilder, DecompilerSettings settings, CancellationToken cancellationToken) { this.TypeSystem = typeSystem; this.decompilationContext = decompilationContext; this.TypeSystemAstBuilder = typeSystemAstBuilder; + this.Settings = settings; this.CancellationToken = cancellationToken; } } diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 6dcd50013..713ef6f4f 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -65,11 +65,13 @@ + + diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs index 5ec4a60ab..62ed58d60 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs @@ -105,6 +105,12 @@ namespace ICSharpCode.Decompiler.IL return a; } + /// + /// Gets whether this node (or any subnode) was modified since the last ResetDirty() call. + /// + /// + /// IsDirty is used by the LoopingTransform, and must not be used by individual transforms within the loop. + /// public bool IsDirty { get; private set; } protected void MakeDirty() @@ -113,6 +119,12 @@ namespace ICSharpCode.Decompiler.IL inst.IsDirty = true; } + /// + /// Marks this node (and all subnodes) as IsDirty=false. + /// + /// + /// IsDirty is used by the LoopingTransform, and must not be used by individual transforms within the loop. + /// public void ResetDirty() { foreach (ILInstruction inst in Descendants) diff --git a/ICSharpCode.Decompiler/IL/Transforms/InlineCompilerGeneratedVariables.cs b/ICSharpCode.Decompiler/IL/Transforms/InlineCompilerGeneratedVariables.cs index ffa33eef1..549142bf7 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/InlineCompilerGeneratedVariables.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/InlineCompilerGeneratedVariables.cs @@ -74,8 +74,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (block == null) return false; ILInstruction next = block.Instructions.ElementAtOrDefault(stloc.ChildIndex + 1); - if (next == null) - continue; // store at end of block might still be a dead store + // NB: next==null is considerd as a dead store if (ILInlining.CanInlineInto(next, v, stloc.Value)) { loadsAccountedFor++; storesToInline.Add(stloc); diff --git a/ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs index 786f745cc..4fd2efc98 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs @@ -22,6 +22,9 @@ using System.Collections.Generic; namespace ICSharpCode.Decompiler.IL { + /// + /// Repeats the child transforms until the ILAst no longer changes. + /// public class LoopingTransform : IILTransform { readonly IReadOnlyCollection children; diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj index dff537fe8..20c8ff590 100644 --- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -128,6 +128,7 @@ +