// 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.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.ILAst; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.Analysis; using ICSharpCode.NRefactory.CSharp.PatternMatching; using Mono.Cecil; namespace ICSharpCode.Decompiler.Ast.Transforms { /// /// Finds the expanded form of using statements using pattern matching and replaces it with a UsingStatement. /// public sealed class PatternStatementTransform : ContextTrackingVisitor, IAstTransform { public PatternStatementTransform(DecompilerContext context) : base(context) { } #region Visitor Overrides protected override AstNode VisitChildren(AstNode node, object data) { // 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 // on the transform's return value to know where we need to keep iterating. for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { AstNode oldChild; do { oldChild = child; child = child.AcceptVisitor(this, data); Debug.Assert(child != null && child.Parent == node); } while (child != oldChild); } return node; } public override AstNode VisitExpressionStatement(ExpressionStatement expressionStatement, object data) { AstNode result; if (context.Settings.UsingStatement) { result = TransformUsings(expressionStatement); if (result != null) return result; } result = TransformFor(expressionStatement); if (result != null) return result; if (context.Settings.LockStatement) { result = TransformLock(expressionStatement); if (result != null) return result; } return base.VisitExpressionStatement(expressionStatement, data); } public override AstNode VisitUsingStatement(UsingStatement usingStatement, object data) { if (context.Settings.ForEachStatement) { AstNode result = TransformForeach(usingStatement); if (result != null) return result; } return base.VisitUsingStatement(usingStatement, data); } public override AstNode VisitWhileStatement(WhileStatement whileStatement, object data) { return TransformDoWhile(whileStatement) ?? base.VisitWhileStatement(whileStatement, data); } public override AstNode VisitIfElseStatement(IfElseStatement ifElseStatement, object data) { if (context.Settings.SwitchStatementOnString) { AstNode result = TransformSwitchOnString(ifElseStatement); if (result != null) return result; } return base.VisitIfElseStatement(ifElseStatement, data); } public override AstNode VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data) { if (context.Settings.AutomaticProperties) { AstNode result = TransformAutomaticProperties(propertyDeclaration); if (result != null) return result; } return base.VisitPropertyDeclaration(propertyDeclaration, data); } public override AstNode VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration, object data) { // first apply transforms to the accessor bodies base.VisitCustomEventDeclaration(eventDeclaration, data); if (context.Settings.AutomaticEvents) { AstNode result = TransformAutomaticEvents(eventDeclaration); if (result != null) return result; } return eventDeclaration; } public override AstNode VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) { return TransformDestructor(methodDeclaration) ?? base.VisitMethodDeclaration(methodDeclaration, data); } public override AstNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement, object data) { return TransformTryCatchFinally(tryCatchStatement) ?? base.VisitTryCatchStatement(tryCatchStatement, data); } #endregion /// /// $type $variable = $initializer; /// static readonly AstNode variableAssignPattern = new ExpressionStatement( new AssignmentExpression( new NamedNode("variable", new IdentifierExpression()), new AnyNode("initializer") )); #region using static readonly AstNode usingTryCatchPattern = new TryCatchStatement { TryBlock = new AnyNode("body"), FinallyBlock = new BlockStatement { new Choice { { "valueType", new ExpressionStatement(new NamedNode("ident", new IdentifierExpression()).ToExpression().Invoke("Dispose")) }, { "referenceType", new IfElseStatement { Condition = new BinaryOperatorExpression( new NamedNode("ident", new IdentifierExpression()), BinaryOperatorType.InEquality, new NullReferenceExpression() ), TrueStatement = new BlockStatement { new ExpressionStatement(new Backreference("ident").ToExpression().Invoke("Dispose")) } } } }.ToStatement() } }; public UsingStatement TransformUsings(ExpressionStatement node) { Match m1 = variableAssignPattern.Match(node); if (m1 == null) return null; AstNode tryCatch = node.NextSibling; Match m2 = usingTryCatchPattern.Match(tryCatch); if (m2 == null) return null; string variableName = m1.Get("variable").Single().Identifier; if (variableName == m2.Get("ident").Single().Identifier) { 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) return null; } node.Remove(); BlockStatement body = m2.Get("body").Single(); UsingStatement usingStatement = new UsingStatement(); usingStatement.ResourceAcquisition = node.Expression.Detach(); usingStatement.EmbeddedStatement = body.Detach(); tryCatch.ReplaceWith(usingStatement); // Move the variable declaration into the resource acquisition, if possible // This is necessary for the foreach-pattern to work on the result of the using-pattern VariableDeclarationStatement varDecl = FindVariableDeclaration(usingStatement, variableName); if (varDecl != null && varDecl.Parent is BlockStatement) { Statement declarationPoint; if (CanMoveVariableDeclarationIntoStatement(varDecl, usingStatement, out declarationPoint)) { Debug.Assert(declarationPoint == usingStatement); // Moving the variable into the UsingStatement is allowed: usingStatement.ResourceAcquisition = new VariableDeclarationStatement { Type = (AstType)varDecl.Type.Clone(), Variables = { new VariableInitializer { Name = variableName, Initializer = m1.Get("initializer").Single().Detach() }.CopyAnnotationsFrom(usingStatement.ResourceAcquisition) } }.CopyAnnotationsFrom(node); } } return usingStatement; } return null; } VariableDeclarationStatement FindVariableDeclaration(AstNode node, string identifier) { while (node != null) { while (node.PrevSibling != null) { node = node.PrevSibling; VariableDeclarationStatement varDecl = node as VariableDeclarationStatement; if (varDecl != null && varDecl.Variables.Count == 1 && varDecl.Variables.Single().Name == identifier) { return varDecl; } } node = node.Parent; } return null; } bool CanMoveVariableDeclarationIntoStatement(VariableDeclarationStatement varDecl, Statement targetStatement, out Statement declarationPoint) { Debug.Assert(targetStatement.Ancestors.Contains(varDecl.Parent)); // Find all blocks between targetStatement and varDecl.Parent List blocks = targetStatement.Ancestors.TakeWhile(block => block != varDecl.Parent).OfType().ToList(); blocks.Add((BlockStatement)varDecl.Parent); // also handle the varDecl.Parent block itself blocks.Reverse(); // go from parent blocks to child blocks DefiniteAssignmentAnalysis daa = new DefiniteAssignmentAnalysis(blocks[0], context.CancellationToken); declarationPoint = null; foreach (BlockStatement block in blocks) { if (!DeclareVariables.FindDeclarationPoint(daa, varDecl, block, out declarationPoint)) { return false; } } return true; } #endregion #region foreach static readonly UsingStatement foreachPattern = new UsingStatement { ResourceAcquisition = new VariableDeclarationStatement { Type = new AnyNode("enumeratorType"), Variables = { new NamedNode( "enumeratorVariable", new VariableInitializer { Initializer = new AnyNode("collection").ToExpression().Invoke("GetEnumerator") } ) } }, EmbeddedStatement = new BlockStatement { new Repeat( new VariableDeclarationStatement { Type = new AnyNode(), Variables = { new VariableInitializer() } }.WithName("variablesOutsideLoop") ).ToStatement(), new WhileStatement { Condition = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Invoke("MoveNext"), EmbeddedStatement = new BlockStatement { new Repeat( new VariableDeclarationStatement { Type = new AnyNode(), Variables = { new VariableInitializer() } }.WithName("variablesInsideLoop") ).ToStatement(), new AssignmentExpression { Left = new IdentifierExpression().WithName("itemVariable"), Operator = AssignmentOperatorType.Assign, Right = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Member("Current") }, new Repeat(new AnyNode("statement")).ToStatement() } }.WithName("loop") }}; public ForeachStatement TransformForeach(UsingStatement node) { Match m = foreachPattern.Match(node); if (m == null) return null; if (!(node.Parent is BlockStatement) && m.Has("variablesOutsideLoop")) { // if there are variables outside the loop, we need to put those into the parent block, and that won't work if the direct parent isn't a block return null; } VariableInitializer enumeratorVar = m.Get("enumeratorVariable").Single(); IdentifierExpression itemVar = m.Get("itemVariable").Single(); WhileStatement loop = m.Get("loop").Single(); // Find the declaration of the item variable: // Because we look only outside the loop, we won't make the mistake of moving a captured variable across the loop boundary VariableDeclarationStatement itemVarDecl = FindVariableDeclaration(loop, itemVar.Identifier); if (itemVarDecl == null || !(itemVarDecl.Parent is BlockStatement)) return null; // Now verify that we can move the variable declaration in front of the loop: Statement declarationPoint; CanMoveVariableDeclarationIntoStatement(itemVarDecl, loop, out declarationPoint); // We ignore the return value because we don't care whether we can move the variable into the loop // (that is possible only with non-captured variables). // We just care that we can move it in front of the loop: if (declarationPoint != loop) return null; BlockStatement newBody = new BlockStatement(); foreach (Statement stmt in m.Get("variablesInsideLoop")) newBody.Add(stmt.Detach()); foreach (Statement stmt in m.Get("statement")) newBody.Add(stmt.Detach()); ForeachStatement foreachStatement = new ForeachStatement { VariableType = (AstType)itemVarDecl.Type.Clone(), VariableName = itemVar.Identifier, InExpression = m.Get("collection").Single().Detach(), EmbeddedStatement = newBody }; node.ReplaceWith(foreachStatement); foreach (Statement stmt in m.Get("variablesOutsideLoop")) { ((BlockStatement)foreachStatement.Parent).Statements.InsertAfter(null, stmt); } return foreachStatement; } #endregion #region for static readonly WhileStatement forPattern = new WhileStatement { Condition = new BinaryOperatorExpression { Left = new NamedNode("ident", new IdentifierExpression()), Operator = BinaryOperatorType.Any, Right = new AnyNode("endExpr") }, EmbeddedStatement = new BlockStatement { Statements = { new Repeat(new AnyNode("statement")), new NamedNode( "increment", new ExpressionStatement( new AssignmentExpression { Left = new Backreference("ident"), Operator = AssignmentOperatorType.Any, Right = new AnyNode() })) } }}; public ForStatement TransformFor(ExpressionStatement node) { Match m1 = variableAssignPattern.Match(node); if (m1 == null) return null; AstNode next = node.NextSibling; Match m2 = forPattern.Match(next); if (m2 == null) return null; // ensure the variable in the for pattern is the same as in the declaration if (m1.Get("variable").Single().Identifier != m2.Get("ident").Single().Identifier) return null; WhileStatement loop = (WhileStatement)next; node.Remove(); BlockStatement newBody = new BlockStatement(); foreach (Statement stmt in m2.Get("statement")) newBody.Add(stmt.Detach()); ForStatement forStatement = new ForStatement(); forStatement.Initializers.Add(node); forStatement.Condition = loop.Condition.Detach(); forStatement.Iterators.Add(m2.Get("increment").Single().Detach()); forStatement.EmbeddedStatement = newBody; loop.ReplaceWith(forStatement); return forStatement; } #endregion #region doWhile static readonly WhileStatement doWhilePattern = new WhileStatement { Condition = new PrimitiveExpression(true), EmbeddedStatement = new BlockStatement { Statements = { new Repeat(new AnyNode("statement")), new IfElseStatement { Condition = new AnyNode("condition"), TrueStatement = new BlockStatement { new BreakStatement() } } } }}; public DoWhileStatement TransformDoWhile(WhileStatement whileLoop) { Match m = doWhilePattern.Match(whileLoop); if (m != null) { DoWhileStatement doLoop = new DoWhileStatement(); doLoop.Condition = new UnaryOperatorExpression(UnaryOperatorType.Not, m.Get("condition").Single().Detach()); doLoop.Condition.AcceptVisitor(new PushNegation(), null); BlockStatement block = (BlockStatement)whileLoop.EmbeddedStatement; block.Statements.Last().Remove(); // remove if statement doLoop.EmbeddedStatement = block.Detach(); whileLoop.ReplaceWith(doLoop); // we may have to extract variable definitions out of the loop if they were used in the condition: foreach (var varDecl in block.Statements.OfType()) { VariableInitializer v = varDecl.Variables.Single(); if (doLoop.Condition.DescendantsAndSelf.OfType().Any(i => i.Identifier == v.Name)) { AssignmentExpression assign = new AssignmentExpression(new IdentifierExpression(v.Name), v.Initializer.Detach()); // move annotations from v to assign: assign.CopyAnnotationsFrom(v); v.RemoveAnnotations(); // remove varDecl with assignment; and move annotations from varDecl to the ExpressionStatement: varDecl.ReplaceWith(new ExpressionStatement(assign).CopyAnnotationsFrom(varDecl)); varDecl.RemoveAnnotations(); // insert the varDecl above the do-while loop: doLoop.Parent.InsertChildBefore(doLoop, varDecl, BlockStatement.StatementRole); } } return doLoop; } return null; } #endregion #region lock static readonly AstNode lockFlagInitPattern = new ExpressionStatement( new AssignmentExpression( new NamedNode("variable", new IdentifierExpression()), new PrimitiveExpression(false) )); static readonly AstNode lockTryCatchPattern = new TryCatchStatement { TryBlock = new BlockStatement { new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke( "Enter", new AnyNode("enter"), new DirectionExpression { FieldDirection = FieldDirection.Ref, Expression = new NamedNode("flag", new IdentifierExpression()) }), new Repeat(new AnyNode()).ToStatement() }, FinallyBlock = new BlockStatement { new IfElseStatement { Condition = new Backreference("flag"), TrueStatement = new BlockStatement { new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Exit", new NamedNode("exit", new IdentifierExpression())) } } }}; public LockStatement TransformLock(ExpressionStatement node) { Match m1 = lockFlagInitPattern.Match(node); if (m1 == null) return null; AstNode tryCatch = node.NextSibling; Match m2 = lockTryCatchPattern.Match(tryCatch); if (m2 == null) return null; if (m1.Get("variable").Single().Identifier == m2.Get("flag").Single().Identifier) { Expression enter = m2.Get("enter").Single(); IdentifierExpression exit = m2.Get("exit").Single(); if (exit.Match(enter) == null) { // If exit and enter are not the same, then enter must be "exit = ..." AssignmentExpression assign = enter as AssignmentExpression; if (assign == null) return null; if (exit.Match(assign.Left) == null) return null; enter = assign.Right; // Remove 'exit' variable: bool ok = false; for (AstNode tmp = node.NextSibling; tmp != tryCatch; tmp = tmp.NextSibling) { VariableDeclarationStatement v = (VariableDeclarationStatement)tmp; if (v.Variables.Single().Name == exit.Identifier) { ok = true; v.Remove(); break; } } if (!ok) return null; } // transform the code into a lock statement: LockStatement l = new LockStatement(); l.Expression = enter.Detach(); l.EmbeddedStatement = ((TryCatchStatement)tryCatch).TryBlock.Detach(); ((BlockStatement)l.EmbeddedStatement).Statements.First().Remove(); // Remove 'Enter()' call tryCatch.ReplaceWith(l); node.Remove(); // remove flag variable return l; } return null; } #endregion #region switch on strings static readonly IfElseStatement switchOnStringPattern = new IfElseStatement { Condition = new BinaryOperatorExpression { Left = new AnyNode("switchExpr"), Operator = BinaryOperatorType.InEquality, Right = new NullReferenceExpression() }, TrueStatement = new BlockStatement { new IfElseStatement { Condition = new BinaryOperatorExpression { Left = new AnyNode("cachedDict"), Operator = BinaryOperatorType.Equality, Right = new NullReferenceExpression() }, TrueStatement = new AnyNode("dictCreation") }, new VariableDeclarationStatement { Type = new PrimitiveType("int"), Variables = { new NamedNode("intVar", new VariableInitializer()) } }, new IfElseStatement { Condition = new Backreference("cachedDict").ToExpression().Invoke( "TryGetValue", new NamedNode("switchVar", new IdentifierExpression()), new DirectionExpression { FieldDirection = FieldDirection.Out, Expression = new IdentifierExpressionBackreference("intVar") }), TrueStatement = new BlockStatement { Statements = { new NamedNode( "switch", new SwitchStatement { Expression = new IdentifierExpressionBackreference("intVar"), SwitchSections = { new Repeat(new AnyNode()) } }) } } }, new Repeat(new AnyNode("nonNullDefaultStmt")).ToStatement() }, FalseStatement = new OptionalNode("nullStmt", new BlockStatement { Statements = { new Repeat(new AnyNode()) } }) }; public SwitchStatement TransformSwitchOnString(IfElseStatement node) { Match m = switchOnStringPattern.Match(node); if (m == null) return null; if (m.Has("nonNullDefaultStmt") && !m.Has("nullStmt")) return null; // switchVar must be the same as switchExpr; or switchExpr must be an assignment and switchVar the left side of that assignment if (m.Get("switchVar").Single().Match(m.Get("switchExpr").Single()) == null) { AssignmentExpression assign = m.Get("switchExpr").Single() as AssignmentExpression; if (m.Get("switchVar").Single().Match(assign.Left) == null) return null; } FieldReference cachedDictField = m.Get("cachedDict").Single().Annotation(); if (cachedDictField == null || !cachedDictField.DeclaringType.Name.StartsWith("", StringComparison.Ordinal)) return null; List dictCreation = m.Get("dictCreation").Single().Statements.ToList(); List> dict = BuildDictionary(dictCreation); SwitchStatement sw = m.Get("switch").Single(); sw.Expression = m.Get("switchExpr").Single().Detach(); foreach (SwitchSection section in sw.SwitchSections) { List labels = section.CaseLabels.ToList(); section.CaseLabels.Clear(); foreach (CaseLabel label in labels) { PrimitiveExpression expr = label.Expression as PrimitiveExpression; if (expr == null || !(expr.Value is int)) continue; int val = (int)expr.Value; foreach (var pair in dict) { if (pair.Value == val) section.CaseLabels.Add(new CaseLabel { Expression = new PrimitiveExpression(pair.Key) }); } } } if (m.Has("nullStmt")) { SwitchSection section = new SwitchSection(); section.CaseLabels.Add(new CaseLabel { Expression = new NullReferenceExpression() }); BlockStatement block = m.Get("nullStmt").Single(); block.Statements.Add(new BreakStatement()); section.Statements.Add(block.Detach()); sw.SwitchSections.Add(section); if (m.Has("nonNullDefaultStmt")) { section = new SwitchSection(); section.CaseLabels.Add(new CaseLabel()); block = new BlockStatement(); block.Statements.AddRange(m.Get("nonNullDefaultStmt").Select(s => s.Detach())); block.Add(new BreakStatement()); section.Statements.Add(block); sw.SwitchSections.Add(section); } } node.ReplaceWith(sw); return sw; } List> BuildDictionary(List dictCreation) { List> dict = new List>(); for (int i = 0; i < dictCreation.Count; i++) { ExpressionStatement es = dictCreation[i] as ExpressionStatement; if (es == null) continue; InvocationExpression ie = es.Expression as InvocationExpression; if (ie == null) continue; PrimitiveExpression arg1 = ie.Arguments.ElementAtOrDefault(0) as PrimitiveExpression; PrimitiveExpression arg2 = ie.Arguments.ElementAtOrDefault(1) as PrimitiveExpression; if (arg1 != null && arg2 != null && arg1.Value is string && arg2.Value is int) dict.Add(new KeyValuePair((string)arg1.Value, (int)arg2.Value)); } return dict; } #endregion #region Automatic Properties static readonly PropertyDeclaration automaticPropertyPattern = new PropertyDeclaration { Attributes = { new Repeat(new AnyNode()) }, Modifiers = Modifiers.Any, ReturnType = new AnyNode(), Getter = new Accessor { Attributes = { new Repeat(new AnyNode()) }, Modifiers = Modifiers.Any, Body = new BlockStatement { new ReturnStatement { Expression = new NamedNode("fieldReference", new MemberReferenceExpression { Target = new ThisReferenceExpression() }) } } }, Setter = new Accessor { Attributes = { new Repeat(new AnyNode()) }, Modifiers = Modifiers.Any, Body = new BlockStatement { new AssignmentExpression { Left = new Backreference("fieldReference"), Right = new IdentifierExpression("value") } }}}; PropertyDeclaration TransformAutomaticProperties(PropertyDeclaration property) { PropertyDefinition cecilProperty = property.Annotation(); if (cecilProperty == null || cecilProperty.GetMethod == null || cecilProperty.SetMethod == null) return null; if (!(cecilProperty.GetMethod.IsCompilerGenerated() && cecilProperty.SetMethod.IsCompilerGenerated())) return null; Match m = automaticPropertyPattern.Match(property); if (m != null) { FieldDefinition field = m.Get("fieldReference").Single().Annotation().ResolveWithinSameModule(); if (field.IsCompilerGenerated()) { RemoveCompilerGeneratedAttribute(property.Getter.Attributes); RemoveCompilerGeneratedAttribute(property.Setter.Attributes); property.Getter.Body = null; property.Setter.Body = null; } } // Since the event instance is not changed, we can continue in the visitor as usual, so return null return null; } void RemoveCompilerGeneratedAttribute(AstNodeCollection attributeSections) { foreach (AttributeSection section in attributeSections) { foreach (var attr in section.Attributes) { TypeReference tr = attr.Type.Annotation(); if (tr != null && tr.Namespace == "System.Runtime.CompilerServices" && tr.Name == "CompilerGeneratedAttribute") { attr.Remove(); } } if (section.Attributes.Count == 0) section.Remove(); } } #endregion #region Automatic Events static readonly Accessor automaticEventPatternV4 = new Accessor { Body = new BlockStatement { new VariableDeclarationStatement { Type = new AnyNode("type"), Variables = { new AnyNode() } }, new VariableDeclarationStatement { Type = new Backreference("type"), Variables = { new AnyNode() } }, new VariableDeclarationStatement { Type = new Backreference("type"), Variables = { new AnyNode() } }, new AssignmentExpression { Left = new NamedNode("var1", new IdentifierExpression()), Operator = AssignmentOperatorType.Assign, Right = new NamedNode("field", new MemberReferenceExpression { Target = new ThisReferenceExpression() }) }, new DoWhileStatement { EmbeddedStatement = new BlockStatement { new AssignmentExpression(new NamedNode("var2", new IdentifierExpression()), new IdentifierExpressionBackreference("var1")), new AssignmentExpression { Left = new NamedNode("var3", new IdentifierExpression()), Operator = AssignmentOperatorType.Assign, Right = new AnyNode("delegateCombine").ToExpression().Invoke( new IdentifierExpressionBackreference("var2"), new IdentifierExpression("value") ).CastTo(new Backreference("type")) }, new AssignmentExpression { Left = new IdentifierExpressionBackreference("var1"), Right = new TypePattern(typeof(System.Threading.Interlocked)).ToType().Invoke( "CompareExchange", new AstType[] { new Backreference("type") }, // type argument new Expression[] { // arguments new DirectionExpression { FieldDirection = FieldDirection.Ref, Expression = new Backreference("field") }, new IdentifierExpressionBackreference("var3"), new IdentifierExpressionBackreference("var2") } )} }, Condition = new BinaryOperatorExpression { Left = new IdentifierExpressionBackreference("var1"), Operator = BinaryOperatorType.InEquality, Right = new IdentifierExpressionBackreference("var2") }} }}; bool CheckAutomaticEventV4Match(Match m, CustomEventDeclaration ev, bool isAddAccessor) { if (m == null) return false; if (m.Get("field").Single().MemberName != ev.Name) return false; // field name must match event name if (ev.ReturnType.Match(m.Get("type").Single()) == null) return false; // variable types must match event type var combineMethod = m.Get("delegateCombine").Single().Parent.Annotation(); if (combineMethod == null || combineMethod.Name != (isAddAccessor ? "Combine" : "Remove")) return false; return combineMethod.DeclaringType.FullName == "System.Delegate"; } EventDeclaration TransformAutomaticEvents(CustomEventDeclaration ev) { Match m1 = automaticEventPatternV4.Match(ev.AddAccessor); if (!CheckAutomaticEventV4Match(m1, ev, true)) return null; Match m2 = automaticEventPatternV4.Match(ev.RemoveAccessor); if (!CheckAutomaticEventV4Match(m2, ev, false)) return null; EventDeclaration ed = new EventDeclaration(); ev.Attributes.MoveTo(ed.Attributes); ed.ReturnType = ev.ReturnType.Detach(); ed.Modifiers = ev.Modifiers; ed.Variables.Add(new VariableInitializer(ev.Name)); ed.CopyAnnotationsFrom(ev); EventDefinition eventDef = ev.Annotation(); if (eventDef != null) { FieldDefinition field = eventDef.DeclaringType.Fields.FirstOrDefault(f => f.Name == ev.Name); if (field != null) { ed.AddAnnotation(field); AstBuilder.ConvertAttributes(ed, field, AttributeTarget.Field); } } ev.ReplaceWith(ed); return ed; } #endregion #region Destructor static readonly MethodDeclaration destructorPattern = new MethodDeclaration { Attributes = { new Repeat(new AnyNode()) }, Modifiers = Modifiers.Any, ReturnType = new PrimitiveType("void"), Name = "Finalize", Body = new BlockStatement { new TryCatchStatement { TryBlock = new AnyNode("body"), FinallyBlock = new BlockStatement { new BaseReferenceExpression().Invoke("Finalize") } } } }; DestructorDeclaration TransformDestructor(MethodDeclaration methodDef) { Match m = destructorPattern.Match(methodDef); if (m != null) { DestructorDeclaration dd = new DestructorDeclaration(); 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); methodDef.ReplaceWith(dd); return dd; } return null; } #endregion #region Try-Catch-Finally static readonly TryCatchStatement tryCatchFinallyPattern = new TryCatchStatement { TryBlock = new BlockStatement { new TryCatchStatement { TryBlock = new AnyNode(), CatchClauses = { new Repeat(new AnyNode()) } } }, FinallyBlock = new AnyNode() }; /// /// Simplify nested 'try { try {} catch {} } finally {}'. /// This transformation must run after the using/lock tranformations. /// TryCatchStatement TransformTryCatchFinally(TryCatchStatement tryFinally) { if (tryCatchFinallyPattern.Match(tryFinally) != null) { TryCatchStatement tryCatch = (TryCatchStatement)tryFinally.TryBlock.Statements.Single(); tryFinally.TryBlock = tryCatch.TryBlock.Detach(); tryCatch.CatchClauses.MoveTo(tryFinally.CatchClauses); } // Since the tryFinally instance is not changed, we can continue in the visitor as usual, so return null return null; } #endregion #region Pattern Matching Helpers sealed class TypePattern : Pattern { readonly string ns; readonly string name; public TypePattern(Type type) { this.ns = type.Namespace; this.name = type.Name; } protected override bool DoMatch(AstNode other, Match match) { if (other == null) return false; TypeReference tr = other.Annotation(); return tr != null && tr.Namespace == ns && tr.Name == name; } public override S AcceptVisitor(IAstVisitor visitor, T data) { throw new NotImplementedException(); } } #endregion } }