static bool IsDisposeInvocation(string variableName, Statement statement) { var memberReferenceExpr = new MemberReferenceExpression(new IdentifierExpression(variableName), "Dispose"); var pattern = new ExpressionStatement(new InvocationExpression(memberReferenceExpr)); return(pattern.Match(statement).Success); }
void HandleInstanceFieldInitializers(IEnumerable <AstNode> members) { var instanceCtors = members.OfType <ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray(); if (instanceCtorsNotChainingWithThis.Length > 0) { MethodDefinition ctorMethodDef = instanceCtorsNotChainingWithThis[0].Annotation <MethodDefinition>(); if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsValueType) { return; } // Recognize field initializers: // Convert first statement in all ctors (if all ctors have the same statement) into a field initializer. bool allSame; do { Match m = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[0].Body.FirstOrDefault()); if (!m.Success) { break; } FieldDefinition fieldDef = m.Get <AstNode>("fieldAccess").Single().Annotation <FieldReference>().ResolveWithinSameModule(); if (fieldDef == null) { break; } AstNode fieldOrEventDecl = members.FirstOrDefault(f => f.Annotation <FieldDefinition>() == fieldDef); if (fieldOrEventDecl == null) { break; } Expression initializer = m.Get <Expression>("initializer").Single(); // 'this'/'base' cannot be used in field initializers if (initializer.DescendantsAndSelf.Any(n => n is ThisReferenceExpression || n is BaseReferenceExpression)) { break; } allSame = true; for (int i = 1; i < instanceCtorsNotChainingWithThis.Length; i++) { if (!instanceCtors[0].Body.First().IsMatch(instanceCtorsNotChainingWithThis[i].Body.FirstOrDefault())) { allSame = false; } } if (allSame) { foreach (var ctor in instanceCtorsNotChainingWithThis) { ctor.Body.First().Remove(); } fieldOrEventDecl.GetChildrenByRole(AstNode.Roles.Variable).Single().Initializer = initializer.Detach(); } } while (allSame); } }
bool MatchLowerBound(int indexNum, out IL.ILVariable index, IL.ILVariable collection, Statement statement) { index = null; var m = variableAssignLowerBoundPattern.Match(statement); if (!m.Success) { return(false); } if (!int.TryParse(m.Get <PrimitiveExpression>("index").Single().Value.ToString(), out int i) || indexNum != i) { return(false); } index = m.Get <IdentifierExpression>("variable").Single().GetILVariable(); return(m.Get <IdentifierExpression>("collection").Single().GetILVariable() == collection); }
internal static Expression CheckNode(IfElseStatement node, out Expression rightSide) { rightSide = null; var match = ActionPattern.Match(node.Condition); if (!match.Success) { return(null); } var conditionExpression = match.Get <BinaryOperatorExpression>(expressionGroupName).Single(); bool isEqualityComparison = conditionExpression.Operator == BinaryOperatorType.Equality; Expression comparedNode = match.Get <Expression>(comparedNodeGroupName).Single(); Statement contentStatement; if (isEqualityComparison) { contentStatement = node.TrueStatement; if (!IsEmpty(node.FalseStatement)) { return(null); } } else { contentStatement = node.FalseStatement; if (!IsEmpty(node.TrueStatement)) { return(null); } } contentStatement = GetSimpleStatement(contentStatement); if (contentStatement == null) { return(null); } var leftExpressionPattern = PatternHelper.OptionalParentheses(comparedNode); var expressionPattern = new AssignmentExpression(leftExpressionPattern, AssignmentOperatorType.Assign, new AnyNode(valueOnNullGroupName)); var statementPattern = new ExpressionStatement(PatternHelper.OptionalParentheses(expressionPattern)); var statementMatch = statementPattern.Match(contentStatement); if (!statementMatch.Success) { return(null); } rightSide = statementMatch.Get <Expression>(valueOnNullGroupName).Single(); return(comparedNode); }
bool MatchForeachOnMultiDimArray(IL.ILVariable[] upperBounds, IL.ILVariable collection, Statement firstInitializerStatement, out IdentifierExpression foreachVariable, out IList <Statement> statements, out IL.ILVariable[] lowerBounds) { int i = 0; foreachVariable = null; statements = null; lowerBounds = new IL.ILVariable[upperBounds.Length]; Statement stmt = firstInitializerStatement; Match m = default(Match); while (i < upperBounds.Length && MatchLowerBound(i, out IL.ILVariable indexVariable, collection, stmt)) { m = forOnArrayMultiDimPattern.Match(stmt.GetNextStatement()); if (!m.Success) { return(false); } var upperBound = m.Get <IdentifierExpression>("upperBoundVariable").Single().GetILVariable(); if (upperBounds[i] != upperBound) { return(false); } stmt = m.Get <Statement>("lowerBoundAssign").Single(); lowerBounds[i] = indexVariable; i++; } var m2 = foreachVariableOnMultArrayAssignPattern.Match(stmt); if (!m2.Success) { return(false); } var collection2 = m2.Get <IdentifierExpression>("collection").Single().GetILVariable(); if (collection2 != collection) { return(false); } foreachVariable = m2.Get <IdentifierExpression>("variable").Single(); statements = m.Get <Statement>("statements").ToList(); return(true); }
protected override CodeAction GetAction(RefactoringContext context, VariableInitializer node) { var variableDecl = node.Parent as VariableDeclarationStatement; if (variableDecl == null || !node.Initializer.IsNull) { return(null); } var assignmentPattern = new ExpressionStatement( new AssignmentExpression(new IdentifierExpression(node.Name), new AnyNode("value"))); var nextSibling = variableDecl.GetNextSibling(n => n is Statement); var match = assignmentPattern.Match(nextSibling); if (!match.Success) { return(null); } return(new CodeAction(context.TranslateString("Join local variable declaration and assignment"), script => { var jointVariableDecl = new VariableDeclarationStatement(variableDecl.Type.Clone(), node.Name, match.Get <Expression> ("value").First().Clone()); script.Replace(nextSibling, jointVariableDecl); if (variableDecl.Variables.Count == 1) { script.Remove(variableDecl); } else { var newVariableDecl = new VariableDeclarationStatement { Type = variableDecl.Type.Clone() }; foreach (var variable in variableDecl.Variables.Where(variable => variable != node)) { newVariableDecl.Variables.Add((VariableInitializer)variable.Clone()); } script.Replace(variableDecl, newVariableDecl); } }, node.NameToken)); }
public override object VisitBlockStatement(BlockStatement blockStatement, object data) { int numberOfVariablesOutsideBlock = currentlyUsedVariableNames.Count; base.VisitBlockStatement(blockStatement, data); foreach (ExpressionStatement stmt in blockStatement.Statements.OfType <ExpressionStatement>().ToArray()) { Match displayClassAssignmentMatch = displayClassAssignmentPattern.Match(stmt); if (!displayClassAssignmentMatch.Success) { continue; } ILVariable variable = displayClassAssignmentMatch.Get <AstNode>("variable").Single().Annotation <ILVariable>(); if (variable == null) { continue; } TypeDefinition type = variable.Type.ResolveWithinSameModule(); if (!IsPotentialClosure(context, type)) { continue; } if (displayClassAssignmentMatch.Get <AstType>("type").Single().Annotation <TypeReference>().ResolveWithinSameModule() != type) { 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 <IdentifierExpression>()) { if (identExpr.Identifier == variable.Name && identExpr != displayClassAssignmentMatch.Get("variable").Single()) { //check if it a cross reference to another generated delegate class's member if (identExpr.Parent is AssignmentExpression) { if ((identExpr.Parent as AssignmentExpression).Left is MemberReferenceExpression ) { MemberReferenceExpression tleft = (MemberReferenceExpression)(identExpr.Parent as AssignmentExpression).Left; ILVariable v = tleft.Target.Annotation <ILVariable>(); if (v != null) { TypeDefinition vtype = v.Type.ResolveWithinSameModule(); if (vtype.IsNested && IsPotentialClosure(context, vtype)) { Console.WriteLine(identExpr.Parent.ToString() + " cross reference by delegate should be ok"); continue; } } } } if (!(identExpr.Parent is MemberReferenceExpression && identExpr.Parent.Annotation <FieldReference>() != null)) { ok = false; break; } } } if (!ok) { continue; } Dictionary <FieldReference, AstNode> dict = new Dictionary <FieldReference, AstNode>(); // Delete the variable declaration statement: VariableDeclarationStatement displayClassVarDecl = PatternStatementTransform.FindVariableDeclaration(stmt, variable.Name); if (displayClassVarDecl != null) { displayClassVarDecl.Remove(); } // Delete the assignment statement: AstNode cur = stmt.NextSibling; stmt.Remove(); // Delete any following statements as long as they assign parameters to the display class BlockStatement rootBlock = blockStatement.Ancestors.OfType <BlockStatement>().LastOrDefault() ?? blockStatement; List <ILVariable> parameterOccurrances = rootBlock.Descendants.OfType <IdentifierExpression>() .Select(n => n.Annotation <ILVariable>()).Where(p => p != null && p.IsParameter).ToList(); AstNode next; for (; cur != null; cur = next) { next = cur.NextSibling; // Test for the pattern: // "variableName.MemberName = right;" ExpressionStatement closureFieldAssignmentPattern = new ExpressionStatement( new AssignmentExpression( new NamedNode("left", new MemberReferenceExpression { Target = new IdentifierExpression(variable.Name), MemberName = Pattern.AnyString }), new AnyNode("right") ) ); Match m = closureFieldAssignmentPattern.Match(cur); if (m.Success) { FieldDefinition fieldDef = m.Get <MemberReferenceExpression>("left").Single().Annotation <FieldReference>().ResolveWithinSameModule(); AstNode right = m.Get <AstNode>("right").Single(); bool isParameter = false; bool isDisplayClassParentPointerAssignment = false; if (right is ThisReferenceExpression) { isParameter = true; } else if (right is IdentifierExpression) { // handle parameters only if the whole method contains no other occurrence except for 'right' ILVariable v = right.Annotation <ILVariable>(); isParameter = v.IsParameter && parameterOccurrances.Count(c => c == v) == 1; if (!isParameter && IsPotentialClosure(context, v.Type.ResolveWithinSameModule())) { // parent display class within the same method // (closure2.localsX = closure1;) isDisplayClassParentPointerAssignment = true; } } else if (right is MemberReferenceExpression) { // copy of parent display class reference from an outer lambda // closure2.localsX = this.localsY MemberReferenceExpression mre = m.Get <MemberReferenceExpression>("right").Single(); do { // descend into the targets of the mre as long as the field types are closures FieldDefinition fieldDef2 = mre.Annotation <FieldReference>().ResolveWithinSameModule(); if (fieldDef2 == null || !IsPotentialClosure(context, fieldDef2.FieldType.ResolveWithinSameModule())) { break; } // if we finally get to a this reference, it's copying a display class parent pointer if (mre.Target is ThisReferenceExpression) { isDisplayClassParentPointerAssignment = true; } mre = mre.Target as MemberReferenceExpression; } while (mre != null); } if (isParameter || isDisplayClassParentPointerAssignment) { dict[fieldDef] = right; cur.Remove(); } else { break; } } else { //why need to break? continue to match should be ok //break; } } // Now create variables for all fields of the display class (except for those that we already handled as parameters) List <Tuple <AstType, ILVariable> > variablesToDeclare = new List <Tuple <AstType, ILVariable> >(); foreach (FieldDefinition field in type.Fields) { if (field.IsStatic) { continue; // skip static fields } if (dict.ContainsKey(field)) // skip field if it already was handled as parameter { continue; } string capturedVariableName = field.Name; if (capturedVariableName.StartsWith("$VB$Local_", StringComparison.Ordinal) && capturedVariableName.Length > 10) { capturedVariableName = capturedVariableName.Substring(10); } EnsureVariableNameIsAvailable(blockStatement, capturedVariableName); currentlyUsedVariableNames.Add(capturedVariableName); ILVariable ilVar = new ILVariable { IsGenerated = true, Name = capturedVariableName, Type = field.FieldType, }; variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), ilVar)); dict[field] = new IdentifierExpression(capturedVariableName).WithAnnotation(ilVar); } // Now figure out where the closure was accessed and use the simpler replacement expression there: foreach (var identExpr in blockStatement.Descendants.OfType <IdentifierExpression>()) { if (identExpr.Identifier == variable.Name) { if (!(identExpr.Parent is MemberReferenceExpression)) { //will delete by the next round of the cross reference delegate class continue; } MemberReferenceExpression mre = (MemberReferenceExpression)identExpr.Parent; AstNode replacement; if (dict.TryGetValue(mre.Annotation <FieldReference>().ResolveWithinSameModule(), out replacement)) { mre.ReplaceWith(replacement.Clone()); } } } // Now insert the variable declarations (we can do this after the replacements only so that the scope detection works): Statement insertionPoint = blockStatement.Statements.FirstOrDefault(); foreach (var tuple in variablesToDeclare) { var newVarDecl = new VariableDeclarationStatement(tuple.Item1, tuple.Item2.Name); newVarDecl.Variables.Single().AddAnnotation(new CapturedVariableAnnotation()); newVarDecl.Variables.Single().AddAnnotation(tuple.Item2); blockStatement.Statements.InsertBefore(insertionPoint, newVarDecl); } } currentlyUsedVariableNames.RemoveRange(numberOfVariablesOutsideBlock, currentlyUsedVariableNames.Count - numberOfVariablesOutsideBlock); return(null); }
public override object VisitBlockStatement(BlockStatement blockStatement, object data) { base.VisitBlockStatement(blockStatement, data); foreach (VariableDeclarationStatement stmt in blockStatement.Statements.OfType<VariableDeclarationStatement>()) { if (stmt.Variables.Count() != 1) continue; var variable = stmt.Variables.Single(); TypeDefinition type = stmt.Type.Annotation<TypeDefinition>(); if (!IsPotentialClosure(type)) continue; ObjectCreateExpression oce = variable.Initializer as ObjectCreateExpression; if (oce == null || oce.Type.Annotation<TypeReference>() != 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<IdentifierExpression>()) { if (identExpr.Identifier == variable.Name) { if (!(identExpr.Parent is MemberReferenceExpression && identExpr.Parent.Annotation<FieldReference>() != null)) ok = false; } } if (!ok) continue; Dictionary<FieldReference, AstNode> dict = new Dictionary<FieldReference, AstNode>(); // Delete the variable declaration statement: AstNode cur = stmt.NextSibling; stmt.Remove(); if (blockStatement.Parent.NodeType == NodeType.Member || blockStatement.Parent is Accessor) { // Delete any following statements as long as they assign parameters to the display class // Do parameter handling only for closures created in the top scope (direct child of method/accessor) List<ParameterReference> parameterOccurrances = blockStatement.Descendants.OfType<IdentifierExpression>() .Select(n => n.Annotation<ParameterReference>()).Where(p => p != null).ToList(); AstNode next; for (; cur != null; cur = next) { next = cur.NextSibling; // Test for the pattern: // "variableName.MemberName = right;" ExpressionStatement closureFieldAssignmentPattern = new ExpressionStatement( new AssignmentExpression( new NamedNode("left", new MemberReferenceExpression { Target = new IdentifierExpression(variable.Name) }), new AnyNode("right") ) ); Match m = closureFieldAssignmentPattern.Match(cur); if (m != null) { AstNode right = m.Get("right").Single(); bool isParameter = false; if (right is ThisReferenceExpression) { isParameter = true; } else if (right is IdentifierExpression) { // handle parameters only if the whole method contains no other occurrance except for 'right' ParameterReference param = right.Annotation<ParameterReference>(); isParameter = parameterOccurrances.Count(c => c == param) == 1; } if (isParameter) { dict[m.Get<MemberReferenceExpression>("left").Single().Annotation<FieldReference>()] = right; cur.Remove(); } else { break; } } else { break; } } } // Now create variables for all fields of the display class (except for those that we already handled as parameters) List<Tuple<AstType, string>> variablesToDeclare = new List<Tuple<AstType, string>>(); foreach (FieldDefinition field in type.Fields) { if (dict.ContainsKey(field)) continue; variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), field.Name)); dict[field] = 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<IdentifierExpression>()) { if (identExpr.Identifier == variable.Name) { MemberReferenceExpression mre = (MemberReferenceExpression)identExpr.Parent; AstNode replacement; if (dict.TryGetValue(mre.Annotation<FieldReference>(), out replacement)) { mre.ReplaceWith(replacement.Clone()); } } } // Now insert the variable declarations (we can do this after the replacements only so that the scope detection works): foreach (var tuple in variablesToDeclare) { var newVarDecl = DeclareVariableInSmallestScope.DeclareVariable(blockStatement, tuple.Item1, tuple.Item2, allowPassIntoLoops: false); if (newVarDecl != null) newVarDecl.Variables.Single().AddAnnotation(new CapturedVariableAnnotation()); } } return null; }
internal void HandleInstanceFieldInitializers(IEnumerable <AstNode> members) { var instanceCtors = members.OfType <ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray(); if (instanceCtorsNotChainingWithThis.Length > 0) { var ctorMethodDef = instanceCtorsNotChainingWithThis[0].GetSymbol() as IMethod; if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsReferenceType == false) { return; } // Recognize field or property initializers: // Translate first statement in all ctors (if all ctors have the same statement) into an initializer. bool allSame; do { Match m = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[0].Body.FirstOrDefault()); if (!m.Success) { break; } IMember fieldOrPropertyOrEvent = (m.Get <AstNode>("fieldAccess").Single().GetSymbol() as IMember)?.MemberDefinition; if (!(fieldOrPropertyOrEvent is IField) && !(fieldOrPropertyOrEvent is IProperty) && !(fieldOrPropertyOrEvent is IEvent)) { break; } AstNode fieldOrPropertyOrEventDecl = members.FirstOrDefault(f => f.GetSymbol() == fieldOrPropertyOrEvent); if (fieldOrPropertyOrEventDecl == null) { break; } Expression initializer = m.Get <Expression>("initializer").Single(); // 'this'/'base' cannot be used in initializers if (initializer.DescendantsAndSelf.Any(n => n is ThisReferenceExpression || n is BaseReferenceExpression)) { break; } allSame = true; for (int i = 1; i < instanceCtorsNotChainingWithThis.Length; i++) { if (!instanceCtorsNotChainingWithThis[0].Body.First().IsMatch(instanceCtorsNotChainingWithThis[i].Body.FirstOrDefault())) { allSame = false; } } if (allSame) { foreach (var ctor in instanceCtorsNotChainingWithThis) { ctor.Body.First().Remove(); } if (fieldOrPropertyOrEventDecl is PropertyDeclaration pd) { pd.Initializer = initializer.Detach(); } else { fieldOrPropertyOrEventDecl.GetChildrenByRole(Roles.Variable).Single().Initializer = initializer.Detach(); } } } while (allSame); } }
void HandleInstanceFieldInitializers(IEnumerable <AstNode> members) { var instanceCtors = members.OfType <ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray(); if (instanceCtorsNotChainingWithThis.Length > 0) { MethodDef ctorMethodDef = instanceCtorsNotChainingWithThis[0].Annotation <MethodDef>(); if (ctorMethodDef != null && DnlibExtensions.IsValueType(ctorMethodDef.DeclaringType)) { return; } // Recognize field initializers: // Convert first statement in all ctors (if all ctors have the same statement) into a field initializer. bool allSame; do { Match m = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[0].Body.FirstOrDefault()); if (!m.Success) { break; } FieldDef fieldDef = m.Get <AstNode>("fieldAccess").Single().Annotation <IField>().ResolveFieldWithinSameModule(); if (fieldDef == null) { break; } AstNode fieldOrEventDecl = members.FirstOrDefault(f => f.Annotation <FieldDef>() == fieldDef); if (fieldOrEventDecl == null) { break; } Expression initializer = m.Get <Expression>("initializer").Single(); // 'this'/'base' cannot be used in field initializers if (initializer.DescendantsAndSelf.Any(n => n is ThisReferenceExpression || n is BaseReferenceExpression)) { break; } allSame = true; for (int i = 1; i < instanceCtorsNotChainingWithThis.Length; i++) { if (!instanceCtors[0].Body.First().IsMatch(instanceCtorsNotChainingWithThis[i].Body.FirstOrDefault())) { allSame = false; break; } } if (allSame) { var ctorIlRanges = new List <Tuple <MemberMapping, List <ILRange> > >(instanceCtorsNotChainingWithThis.Length); for (int i = 0; i < instanceCtorsNotChainingWithThis.Length; i++) { var ctor = instanceCtorsNotChainingWithThis[i]; var stmt = ctor.Body.First(); stmt.Remove(); var mm = ctor.Annotation <MemberMapping>() ?? ctor.Body.Annotation <MemberMapping>(); Debug.Assert(mm != null); if (mm != null) { ctorIlRanges.Add(Tuple.Create(mm, stmt.GetAllRecursiveILRanges())); } } var varInit = fieldOrEventDecl.GetChildrenByRole(Roles.Variable).Single(); initializer.Remove(); initializer.RemoveAllILRangesRecursive(); varInit.Initializer = initializer; fieldOrEventDecl.AddAnnotation(ctorIlRanges); } } while (allSame); } }
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) { var instanceCtors = typeDeclaration.Members.OfType <ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray(); if (instanceCtorsNotChainingWithThis.Length > 0 && typeDeclaration.ClassType == NRefactory.TypeSystem.ClassType.Class) { // Recognize field initializers: // Convert first statement in all ctors (if all ctors have the same statement) into a field initializer. bool allSame; do { Match m = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[0].Body.FirstOrDefault()); if (!m.Success) { break; } FieldDefinition fieldDef = m.Get <AstNode>("fieldAccess").Single().Annotation <FieldReference>().ResolveWithinSameModule(); if (fieldDef == null) { break; } AttributedNode fieldOrEventDecl = typeDeclaration.Members.FirstOrDefault(f => f.Annotation <FieldDefinition>() == fieldDef); if (fieldOrEventDecl == null) { break; } allSame = true; for (int i = 1; i < instanceCtorsNotChainingWithThis.Length; i++) { if (!instanceCtors[0].Body.First().IsMatch(instanceCtorsNotChainingWithThis[i].Body.FirstOrDefault())) { allSame = false; } } if (allSame) { foreach (var ctor in instanceCtorsNotChainingWithThis) { ctor.Body.First().Remove(); } fieldOrEventDecl.GetChildrenByRole(AstNode.Roles.Variable).Single().Initializer = m.Get <Expression>("initializer").Single().Detach(); } } while (allSame); } // Now convert base constructor calls to initializers: base.VisitTypeDeclaration(typeDeclaration, data); // Remove single empty constructor: if (instanceCtors.Length == 1) { ConstructorDeclaration emptyCtor = new ConstructorDeclaration(); emptyCtor.Modifiers = ((typeDeclaration.Modifiers & Modifiers.Abstract) == Modifiers.Abstract ? Modifiers.Protected : Modifiers.Public); emptyCtor.Body = new BlockStatement(); if (emptyCtor.IsMatch(instanceCtors[0])) { instanceCtors[0].Remove(); } } // Convert static constructor into field initializers if the class is BeforeFieldInit var staticCtor = typeDeclaration.Members.OfType <ConstructorDeclaration>().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static); if (staticCtor != null) { TypeDefinition typeDef = typeDeclaration.Annotation <TypeDefinition>(); if (typeDef != null && typeDef.IsBeforeFieldInit) { while (true) { ExpressionStatement es = staticCtor.Body.Statements.FirstOrDefault() as ExpressionStatement; if (es == null) { break; } AssignmentExpression assignment = es.Expression as AssignmentExpression; if (assignment == null || assignment.Operator != AssignmentOperatorType.Assign) { break; } FieldDefinition fieldDef = assignment.Left.Annotation <FieldReference>().ResolveWithinSameModule(); if (fieldDef == null || !fieldDef.IsStatic) { break; } FieldDeclaration fieldDecl = typeDeclaration.Members.OfType <FieldDeclaration>().FirstOrDefault(f => f.Annotation <FieldDefinition>() == fieldDef); if (fieldDecl == null) { break; } fieldDecl.Variables.Single().Initializer = assignment.Right.Detach(); es.Remove(); } if (staticCtor.Body.Statements.Count == 0) { staticCtor.Remove(); } } } return(null); }
public override object VisitBlockStatement(BlockStatement blockStatement, object data) { base.VisitBlockStatement(blockStatement, data); foreach (ExpressionStatement stmt in blockStatement.Statements.OfType <ExpressionStatement>().ToArray()) { Match displayClassAssignmentMatch = displayClassAssignmentPattern.Match(stmt); if (displayClassAssignmentMatch == null) { continue; } ILVariable variable = displayClassAssignmentMatch.Get("variable").Single().Annotation <ILVariable>(); if (variable == null) { continue; } TypeDefinition type = variable.Type.ResolveWithinSameModule(); if (!IsPotentialClosure(context, type)) { continue; } if (displayClassAssignmentMatch.Get("type").Single().Annotation <TypeReference>().ResolveWithinSameModule() != type) { 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 <IdentifierExpression>()) { if (identExpr.Identifier == variable.Name && identExpr != displayClassAssignmentMatch.Get("variable").Single()) { if (!(identExpr.Parent is MemberReferenceExpression && identExpr.Parent.Annotation <FieldReference>() != null)) { ok = false; } } } if (!ok) { continue; } Dictionary <FieldReference, AstNode> dict = new Dictionary <FieldReference, AstNode>(); // Delete the variable declaration statement: AstNode cur = stmt.NextSibling; stmt.Remove(); if (blockStatement.Parent.NodeType == NodeType.Member || blockStatement.Parent is Accessor) { // Delete any following statements as long as they assign parameters to the display class // Do parameter handling only for closures created in the top scope (direct child of method/accessor) List <ILVariable> parameterOccurrances = blockStatement.Descendants.OfType <IdentifierExpression>() .Select(n => n.Annotation <ILVariable>()).Where(p => p != null && p.IsParameter).ToList(); AstNode next; for (; cur != null; cur = next) { next = cur.NextSibling; // Test for the pattern: // "variableName.MemberName = right;" ExpressionStatement closureFieldAssignmentPattern = new ExpressionStatement( new AssignmentExpression( new NamedNode("left", new MemberReferenceExpression { Target = new IdentifierExpression(variable.Name) }), new AnyNode("right") ) ); Match m = closureFieldAssignmentPattern.Match(cur); if (m != null) { AstNode right = m.Get("right").Single(); bool isParameter = false; if (right is ThisReferenceExpression) { isParameter = true; } else if (right is IdentifierExpression) { // handle parameters only if the whole method contains no other occurrance except for 'right' ILVariable param = right.Annotation <ILVariable>(); isParameter = param.IsParameter && parameterOccurrances.Count(c => c == param) == 1; } if (isParameter) { dict[m.Get <MemberReferenceExpression>("left").Single().Annotation <FieldReference>().ResolveWithinSameModule()] = right; cur.Remove(); } else { break; } } else { break; } } } // Now create variables for all fields of the display class (except for those that we already handled as parameters) List <Tuple <AstType, string> > variablesToDeclare = new List <Tuple <AstType, string> >(); foreach (FieldDefinition field in type.Fields) { if (dict.ContainsKey(field)) { continue; } variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), field.Name)); dict[field] = 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 <IdentifierExpression>()) { if (identExpr.Identifier == variable.Name) { MemberReferenceExpression mre = (MemberReferenceExpression)identExpr.Parent; AstNode replacement; if (dict.TryGetValue(mre.Annotation <FieldReference>().ResolveWithinSameModule(), out replacement)) { mre.ReplaceWith(replacement.Clone()); } } } // Now insert the variable declarations (we can do this after the replacements only so that the scope detection works): Statement insertionPoint = blockStatement.Statements.FirstOrDefault(); foreach (var tuple in variablesToDeclare) { var newVarDecl = new VariableDeclarationStatement(tuple.Item1, tuple.Item2); newVarDecl.Variables.Single().AddAnnotation(new CapturedVariableAnnotation()); blockStatement.Statements.InsertBefore(insertionPoint, newVarDecl); } } return(null); }
void HandleInstanceFieldInitializers(IEnumerable <AstNode> members) { var instanceCtors = members.OfType <ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray(); if (instanceCtorsNotChainingWithThis.Length > 0) { var ctorMethodDef = instanceCtorsNotChainingWithThis[0].GetSymbol() as IMethod; if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsReferenceType == false) { return; } bool ctorIsUnsafe = instanceCtorsNotChainingWithThis.All(c => c.HasModifier(Modifiers.Unsafe)); if (!context.DecompileRun.RecordDecompilers.TryGetValue(ctorMethodDef.DeclaringTypeDefinition, out var record)) { record = null; } // Recognize field or property initializers: // Translate first statement in all ctors (if all ctors have the same statement) into an initializer. bool allSame; do { Match m = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[0].Body.FirstOrDefault()); if (!m.Success) { break; } IMember fieldOrPropertyOrEvent = (m.Get <AstNode>("fieldAccess").Single().GetSymbol() as IMember)?.MemberDefinition; if (!(fieldOrPropertyOrEvent is IField) && !(fieldOrPropertyOrEvent is IProperty) && !(fieldOrPropertyOrEvent is IEvent)) { break; } var fieldOrPropertyOrEventDecl = members.FirstOrDefault(f => f.GetSymbol() == fieldOrPropertyOrEvent) as EntityDeclaration; // Cannot transform if it is a custom event. if (fieldOrPropertyOrEventDecl is CustomEventDeclaration) { break; } Expression initializer = m.Get <Expression>("initializer").Single(); // 'this'/'base' cannot be used in initializers if (initializer.DescendantsAndSelf.Any(n => n is ThisReferenceExpression || n is BaseReferenceExpression)) { break; } if (initializer.Annotation <ILVariableResolveResult>()?.Variable.Kind == IL.VariableKind.Parameter) { // remove record ctor parameter assignments if (IsPropertyDeclaredByPrimaryCtor(fieldOrPropertyOrEvent as IProperty, record)) { initializer.Remove(); } else { break; } } else { // cannot transform if member is not found if (fieldOrPropertyOrEventDecl == null) { break; } } allSame = true; for (int i = 1; i < instanceCtorsNotChainingWithThis.Length; i++) { var otherMatch = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[i].Body.FirstOrDefault()); if (!otherMatch.Success) { allSame = false; break; } var otherMember = (otherMatch.Get <AstNode>("fieldAccess").Single().GetSymbol() as IMember)?.MemberDefinition; if (!otherMember.Equals(fieldOrPropertyOrEvent)) { allSame = false; } if (!initializer.IsMatch(otherMatch.Get <AstNode>("initializer").Single())) { allSame = false; } } if (allSame) { foreach (var ctor in instanceCtorsNotChainingWithThis) { ctor.Body.First().Remove(); } if (fieldOrPropertyOrEventDecl == null) { continue; } if (ctorIsUnsafe && IntroduceUnsafeModifier.IsUnsafe(initializer)) { fieldOrPropertyOrEventDecl.Modifiers |= Modifiers.Unsafe; } if (fieldOrPropertyOrEventDecl is PropertyDeclaration pd) { pd.Initializer = initializer.Detach(); } else { fieldOrPropertyOrEventDecl.GetChildrenByRole(Roles.Variable).Single().Initializer = initializer.Detach(); } } } while (allSame); } }
protected override CodeAction GetAction(RefactoringContext context, IfElseStatement node) { var match = ActionPattern.Match(node.Condition); if (!match.Success) { return(null); } var conditionExpression = match.Get <BinaryOperatorExpression>(expressionGroupName).Single(); bool isEqualityComparison = conditionExpression.Operator == BinaryOperatorType.Equality; Expression comparedNode = match.Get <Expression>(comparedNodeGroupName).Single(); Statement contentStatement; if (isEqualityComparison) { contentStatement = node.TrueStatement; if (!IsEmpty(node.FalseStatement)) { return(null); } } else { contentStatement = node.FalseStatement; if (!IsEmpty(node.TrueStatement)) { return(null); } } contentStatement = GetSimpleStatement(contentStatement); if (contentStatement == null) { return(null); } var leftExpressionPattern = PatternHelper.OptionalParentheses(comparedNode); var expressionPattern = new AssignmentExpression(leftExpressionPattern, AssignmentOperatorType.Assign, new AnyNode(valueOnNullGroupName)); var statementPattern = new ExpressionStatement(PatternHelper.OptionalParentheses(expressionPattern)); var statementMatch = statementPattern.Match(contentStatement); if (!statementMatch.Success) { return(null); } var rightSide = statementMatch.Get <Expression>(valueOnNullGroupName).Single(); return(new CodeAction(context.TranslateString("Convert if statement to ?? expression"), script => { var previousNode = node.GetPrevSibling(sibling => sibling is Statement); var previousDeclaration = previousNode as VariableDeclarationStatement; if (previousDeclaration != null && previousDeclaration.Variables.Count() == 1) { var variable = previousDeclaration.Variables.First(); var comparedNodeIdentifierExpression = comparedNode as IdentifierExpression; if (comparedNodeIdentifierExpression != null && comparedNodeIdentifierExpression.Identifier == variable.Name) { script.Replace(variable.Initializer, new BinaryOperatorExpression(variable.Initializer.Clone(), BinaryOperatorType.NullCoalescing, rightSide.Clone())); script.Remove(node); return; } } var previousExpressionStatement = previousNode as ExpressionStatement; if (previousExpressionStatement != null) { var previousAssignment = previousExpressionStatement.Expression as AssignmentExpression; if (previousAssignment != null && comparedNode.IsMatch(previousAssignment.Left)) { var newExpression = new BinaryOperatorExpression(previousAssignment.Right.Clone(), BinaryOperatorType.NullCoalescing, rightSide.Clone()); script.Replace(previousAssignment.Right, newExpression); script.Remove(node); return; } } var coalescedExpression = new BinaryOperatorExpression(comparedNode.Clone(), BinaryOperatorType.NullCoalescing, rightSide.Clone()); var newAssignment = new ExpressionStatement(new AssignmentExpression(comparedNode.Clone(), coalescedExpression)); script.Replace(node, newAssignment); }, node)); }