diff --git a/Debugger/Debugger.Core/NRefactory/Visitors/ExpressionEvaluator.cs b/Debugger/Debugger.Core/NRefactory/Visitors/ExpressionEvaluator.cs index 0799f17d9..e43f5eaeb 100644 --- a/Debugger/Debugger.Core/NRefactory/Visitors/ExpressionEvaluator.cs +++ b/Debugger/Debugger.Core/NRefactory/Visitors/ExpressionEvaluator.cs @@ -304,7 +304,7 @@ namespace ICSharpCode.NRefactory.Visitors } TypedValue right; - if (op == BinaryOperatorType.None) { + if (op == BinaryOperatorType.Any) { right = Evaluate(assignmentExpression.Right); } else { BinaryOperatorExpression binOpExpr = new BinaryOperatorExpression(); diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index a1192db9a..4fc88ba5a 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -31,9 +31,14 @@ namespace Decompiler MethodDefinition method = member as MethodDefinition; if (method != null && (method.IsGetter || method.IsSetter || method.IsAddOn || method.IsRemoveOn)) return true; + if (method != null && method.Name.StartsWith("<", StringComparison.Ordinal) && method.IsCompilerGenerated()) + return true; TypeDefinition type = member as TypeDefinition; if (type != null && type.DeclaringType != null && type.Name.StartsWith("<>c__DisplayClass", StringComparison.Ordinal) && type.IsCompilerGenerated()) return true; + FieldDefinition field = member as FieldDefinition; + if (field != null && field.Name.StartsWith("CS$<>", StringComparison.Ordinal) && field.IsCompilerGenerated()) + return true; return false; } @@ -411,6 +416,9 @@ namespace Decompiler else modifiers |= Modifiers.Override; } + if (!methodDef.HasBody && !methodDef.IsAbstract) + modifiers |= Modifiers.Extern; + return modifiers; } #endregion @@ -419,6 +427,7 @@ namespace Decompiler { // Add fields foreach(FieldDefinition fieldDef in typeDef.Fields) { + if (MemberIsHidden(fieldDef)) continue; astType.AddChild(CreateField(fieldDef), TypeDeclaration.MemberRole); } @@ -489,7 +498,8 @@ namespace Decompiler ConstructorDeclaration astMethod = new ConstructorDeclaration(); astMethod.AddAnnotation(methodDef); - astMethod.AddAnnotation(methodMapping); + if (methodMapping != null) + astMethod.AddAnnotation(methodMapping); astMethod.Modifiers = ConvertModifiers(methodDef); if (methodDef.IsStatic) { // don't show visibility for static ctors @@ -515,7 +525,9 @@ namespace Decompiler astProp.Getter = new Accessor { Body = AstMethodBodyBuilder.CreateMethodBody(propDef.GetMethod, context) }.WithAnnotation(propDef.GetMethod); - astProp.Getter.AddAnnotation(methodMapping); + + if (methodMapping != null) + astProp.Getter.AddAnnotation(methodMapping); } if (propDef.SetMethod != null) { // Create mapping - used in debugger @@ -524,7 +536,9 @@ namespace Decompiler astProp.Setter = new Accessor { Body = AstMethodBodyBuilder.CreateMethodBody(propDef.SetMethod, context) }.WithAnnotation(propDef.SetMethod); - astProp.Setter.AddAnnotation(methodMapping); + + if (methodMapping != null) + astProp.Setter.AddAnnotation(methodMapping); } return astProp; } @@ -543,7 +557,9 @@ namespace Decompiler astEvent.AddAccessor = new Accessor { Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.AddMethod, context) }.WithAnnotation(eventDef.AddMethod); - astEvent.AddAccessor.AddAnnotation(methodMapping); + + if (methodMapping != null) + astEvent.AddAccessor.AddAnnotation(methodMapping); } if (eventDef.RemoveMethod != null) { // Create mapping - used in debugger @@ -552,7 +568,9 @@ namespace Decompiler astEvent.RemoveAccessor = new Accessor { Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.RemoveMethod, context) }.WithAnnotation(eventDef.RemoveMethod); - astEvent.RemoveAccessor.AddAnnotation(methodMapping); + + if (methodMapping != null) + astEvent.RemoveAccessor.AddAnnotation(methodMapping); } return astEvent; } diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index eeb8bfb36..1194b24c8 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -176,12 +176,6 @@ namespace Decompiler return args; } - AstNode TransformExpression(ILExpression expr) - { - List args = TransformExpressionArguments(expr); - return TransformByteCode(expr, args); - } - Ast.Expression MakeBranchCondition(ILExpression expr) { switch(expr.Code) { @@ -204,22 +198,11 @@ namespace Decompiler List args = TransformExpressionArguments(expr); Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; - TypeReference arg1Type = args.Count >= 1 ? expr.Arguments[0].InferredType : null; switch((Code)expr.Code) { case Code.Brfalse: - if (arg1Type == typeSystem.Boolean) - return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); - else if (TypeAnalysis.IsIntegerOrEnum(typeSystem, arg1Type)) - return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, new PrimitiveExpression(0)); - else - return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, new NullReferenceExpression()); + return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); case Code.Brtrue: - if (arg1Type == typeSystem.Boolean) - return arg1; - else if (TypeAnalysis.IsIntegerOrEnum(typeSystem, arg1Type)) - return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, new PrimitiveExpression(0)); - else - return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, new NullReferenceExpression()); + return arg1; case Code.Beq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); case Code.Bge: @@ -245,21 +228,6 @@ namespace Decompiler } } - AstNode TransformByteCode(ILExpression byteCode, List args) - { - try { - AstNode ret = TransformByteCode_Internal(byteCode, args); - // ret.UserData["Type"] = byteCode.Type; - return ret; - } catch (NotImplementedException) { - // Output the operand of the unknown IL code as well - if (byteCode.Operand != null) { - args.Insert(0, new IdentifierExpression(FormatByteCodeOperand(byteCode.Operand))); - } - return new IdentifierExpression(byteCode.Code.GetName()).Invoke(args); - } - } - static string FormatByteCodeOperand(object operand) { if (operand == null) { @@ -285,7 +253,18 @@ namespace Decompiler } } - AstNode TransformByteCode_Internal(ILExpression byteCode, List args) + AstNode TransformExpression(ILExpression expr) + { + List args = TransformExpressionArguments(expr); + AstNode node = TransformByteCode(expr, args); + Expression astExpr = node as Expression; + if (astExpr != null) + return Convert(astExpr, expr.InferredType, expr.ExpectedType); + else + return node; + } + + AstNode TransformByteCode(ILExpression byteCode, List args) { ILCode opCode = byteCode.Code; object operand = byteCode.Operand; @@ -381,48 +360,58 @@ namespace Decompiler case Code.Bne_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2), branchCommand); #endregion #region Comparison - case Code.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, ConvertIntToBool(arg2)); + case Code.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); case Code.Cgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); - case Code.Cgt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); + case Code.Cgt_Un: + // can also mean Inequality, when used with object references + { + TypeReference arg1Type = byteCode.Arguments[0].InferredType; + if (arg1Type != null && !arg1Type.IsValueType) + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); + else + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); + } case Code.Clt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); case Code.Clt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); #endregion #region Conversions - case Code.Conv_I: return arg1.CastTo(typeof(int)); // TODO - case Code.Conv_I1: return arg1.CastTo(typeof(SByte)); - case Code.Conv_I2: return arg1.CastTo(typeof(Int16)); - case Code.Conv_I4: return arg1.CastTo(typeof(Int32)); - case Code.Conv_I8: return arg1.CastTo(typeof(Int64)); - case Code.Conv_U: return arg1.CastTo(typeof(uint)); // TODO - case Code.Conv_U1: return arg1.CastTo(typeof(Byte)); - case Code.Conv_U2: return arg1.CastTo(typeof(UInt16)); - case Code.Conv_U4: return arg1.CastTo(typeof(UInt32)); - case Code.Conv_U8: return arg1.CastTo(typeof(UInt64)); + case Code.Conv_I1: + case Code.Conv_I2: + case Code.Conv_I4: + case Code.Conv_I8: + case Code.Conv_U1: + case Code.Conv_U2: + case Code.Conv_U4: + case Code.Conv_U8: + return arg1; // conversion is handled by Convert() function using the info from type analysis + case Code.Conv_I: return arg1.CastTo(typeof(IntPtr)); // TODO + case Code.Conv_U: return arg1.CastTo(typeof(UIntPtr)); // TODO case Code.Conv_R4: return arg1.CastTo(typeof(float)); case Code.Conv_R8: return arg1.CastTo(typeof(double)); case Code.Conv_R_Un: return arg1.CastTo(typeof(double)); // TODO - case Code.Conv_Ovf_I: return arg1.CastTo(typeof(int)); - case Code.Conv_Ovf_I1: return arg1.CastTo(typeof(SByte)); - case Code.Conv_Ovf_I2: return arg1.CastTo(typeof(Int16)); - case Code.Conv_Ovf_I4: return arg1.CastTo(typeof(Int32)); - case Code.Conv_Ovf_I8: return arg1.CastTo(typeof(Int64)); - case Code.Conv_Ovf_U: return arg1.CastTo(typeof(uint)); - case Code.Conv_Ovf_U1: return arg1.CastTo(typeof(Byte)); - case Code.Conv_Ovf_U2: return arg1.CastTo(typeof(UInt16)); - case Code.Conv_Ovf_U4: return arg1.CastTo(typeof(UInt32)); - case Code.Conv_Ovf_U8: return arg1.CastTo(typeof(UInt64)); + case Code.Conv_Ovf_I1: + case Code.Conv_Ovf_I2: + case Code.Conv_Ovf_I4: + case Code.Conv_Ovf_I8: + case Code.Conv_Ovf_U1: + case Code.Conv_Ovf_U2: + case Code.Conv_Ovf_U4: + case Code.Conv_Ovf_U8: + case Code.Conv_Ovf_I1_Un: + case Code.Conv_Ovf_I2_Un: + case Code.Conv_Ovf_I4_Un: + case Code.Conv_Ovf_I8_Un: + case Code.Conv_Ovf_U1_Un: + case Code.Conv_Ovf_U2_Un: + case Code.Conv_Ovf_U4_Un: + case Code.Conv_Ovf_U8_Un: + return arg1; // conversion was handled by Convert() function using the info from type analysis + case Code.Conv_Ovf_I: return arg1.CastTo(typeof(IntPtr)); // TODO + case Code.Conv_Ovf_U: return arg1.CastTo(typeof(UIntPtr)); + case Code.Conv_Ovf_I_Un: return arg1.CastTo(typeof(IntPtr)); + case Code.Conv_Ovf_U_Un: return arg1.CastTo(typeof(UIntPtr)); - case Code.Conv_Ovf_I_Un: return arg1.CastTo(typeof(int)); - case Code.Conv_Ovf_I1_Un: return arg1.CastTo(typeof(SByte)); - case Code.Conv_Ovf_I2_Un: return arg1.CastTo(typeof(Int16)); - case Code.Conv_Ovf_I4_Un: return arg1.CastTo(typeof(Int32)); - case Code.Conv_Ovf_I8_Un: return arg1.CastTo(typeof(Int64)); - case Code.Conv_Ovf_U_Un: return arg1.CastTo(typeof(uint)); - case Code.Conv_Ovf_U1_Un: return arg1.CastTo(typeof(Byte)); - case Code.Conv_Ovf_U2_Un: return arg1.CastTo(typeof(UInt16)); - case Code.Conv_Ovf_U4_Un: return arg1.CastTo(typeof(UInt32)); - case Code.Conv_Ovf_U8_Un: return arg1.CastTo(typeof(UInt64)); case Code.Castclass: case Code.Unbox_Any: return arg1.CastTo(operandAsTypeRef); @@ -504,20 +493,7 @@ namespace Decompiler return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand)); } case Code.Ldc_I4: - if (byteCode.InferredType == typeSystem.Boolean && (int)operand == 0) - return new Ast.PrimitiveExpression(false); - else if (byteCode.InferredType == typeSystem.Boolean && (int)operand == 1) - return new Ast.PrimitiveExpression(true); - if (byteCode.InferredType != null) { // cannot rely on IsValueType, it's not set for typerefs (but is set for typespecs) - TypeDefinition enumDefinition = byteCode.InferredType.Resolve(); - if (enumDefinition != null && enumDefinition.IsEnum) { - foreach (FieldDefinition field in enumDefinition.Fields) { - if (field.IsStatic && object.Equals(CSharpPrimitiveCast.Cast(TypeCode.Int32, field.Constant, false), operand)) - return AstBuilder.ConvertType(enumDefinition).Member(field.Name).WithAnnotation(field); - } - } - } - return new Ast.PrimitiveExpression(operand); + return PrimitiveExpression((int)operand, byteCode.InferredType); case Code.Ldc_I8: case Code.Ldc_R4: case Code.Ldc_R8: @@ -580,7 +556,6 @@ namespace Decompiler case Code.Refanyval: return InlineAssembly(byteCode, args); case Code.Ret: { if (methodDef.ReturnType.FullName != "System.Void") { - arg1 = Convert(arg1, methodDef.ReturnType); return new Ast.ReturnStatement { Expression = arg1 }; } else { return new Ast.ReturnStatement(); @@ -719,19 +694,57 @@ namespace Decompiler return new DirectionExpression { Expression = expr, FieldDirection = FieldDirection.Ref }; } - static Ast.Expression Convert(Ast.Expression expr, Cecil.TypeReference reqType) + Ast.Expression Convert(Ast.Expression expr, Cecil.TypeReference actualType, Cecil.TypeReference reqType) { - if (reqType == null) { + if (reqType == null || actualType == reqType) { return expr; } else { + bool actualIsIntegerOrEnum = TypeAnalysis.IsIntegerOrEnum(typeSystem, actualType); + bool requiredIsIntegerOrEnum = TypeAnalysis.IsIntegerOrEnum(typeSystem, reqType); + + if (reqType == typeSystem.Boolean) { + if (actualIsIntegerOrEnum) { + return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, PrimitiveExpression(0, actualType)); + } else { + return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, new NullReferenceExpression()); + } + } + if (actualType == typeSystem.Boolean && requiredIsIntegerOrEnum) { + return new ConditionalExpression { + Condition = expr, + TrueExpression = PrimitiveExpression(1, reqType), + FalseExpression = PrimitiveExpression(0, reqType) + }; + } + if (actualIsIntegerOrEnum && requiredIsIntegerOrEnum) { + return expr.CastTo(AstBuilder.ConvertType(reqType)); + } return expr; } } - static Ast.Expression ConvertIntToBool(Ast.Expression astInt) + Expression PrimitiveExpression(long val, TypeReference type) { - return astInt; - // return new Ast.ParenthesizedExpression(new Ast.BinaryOperatorExpression(astInt, BinaryOperatorType.InEquality, new Ast.PrimitiveExpression(0, "0"))); + if (type == typeSystem.Boolean && val == 0) + return new Ast.PrimitiveExpression(false); + else if (type == typeSystem.Boolean && val == 1) + return new Ast.PrimitiveExpression(true); + if (type != null) { // cannot rely on type.IsValueType, it's not set for typerefs (but is set for typespecs) + TypeDefinition enumDefinition = type.Resolve(); + if (enumDefinition != null && enumDefinition.IsEnum) { + foreach (FieldDefinition field in enumDefinition.Fields) { + if (field.IsStatic && object.Equals(CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false), val)) + return AstBuilder.ConvertType(enumDefinition).Member(field.Name).WithAnnotation(field); + else if (!field.IsStatic && field.IsRuntimeSpecialName) + type = field.FieldType; // use primitive type of the enum + } + } + } + TypeCode code = TypeAnalysis.GetTypeCode(typeSystem, type); + if (code == TypeCode.Object) + return new Ast.PrimitiveExpression((int)val); + else + return new Ast.PrimitiveExpression(CSharpPrimitiveCast.Cast(code, val, false)); } } } diff --git a/ICSharpCode.Decompiler/Ast/CommentStatement.cs b/ICSharpCode.Decompiler/Ast/CommentStatement.cs index fb36235bd..6f0135c72 100644 --- a/ICSharpCode.Decompiler/Ast/CommentStatement.cs +++ b/ICSharpCode.Decompiler/Ast/CommentStatement.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.CSharp.PatternMatching; namespace Decompiler { @@ -34,5 +35,11 @@ namespace Decompiler cs.Remove(); } } + + protected override bool DoMatch(AstNode other, Match match) + { + CommentStatement o = other as CommentStatement; + return o != null && MatchString(comment, o.comment); + } } } diff --git a/ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs b/ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs index bd4150537..ba60db563 100644 --- a/ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs +++ b/ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs @@ -26,17 +26,21 @@ namespace Decompiler /// The type of the new variable /// The name of the new variable /// Whether the variable is allowed to be placed inside a loop - public static void DeclareVariable(AstNode node, AstType type, string name, bool allowPassIntoLoops = true) + public static VariableDeclarationStatement DeclareVariable(AstNode node, AstType type, string name, bool allowPassIntoLoops = true) { + VariableDeclarationStatement result = null; AstNode pos = FindInsertPos(node, name, allowPassIntoLoops); if (pos != null) { Match m = assignmentPattern.Match(pos); if (m != null && m.Get("ident").Single().Identifier == name) { - pos.ReplaceWith(new VariableDeclarationStatement(type, name, m.Get("init").Single().Detach())); + result = new VariableDeclarationStatement(type, name, m.Get("init").Single().Detach()); + pos.ReplaceWith(result); } else { - pos.Parent.InsertChildBefore(pos, new VariableDeclarationStatement(type, name), BlockStatement.StatementRole); + result = new VariableDeclarationStatement(type, name); + pos.Parent.InsertChildBefore(pos, result, BlockStatement.StatementRole); } } + return result; } static AstNode FindInsertPos(AstNode node, string name, bool allowPassIntoLoops) diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 1909350b1..d5bf7d8b2 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -32,6 +32,10 @@ namespace Decompiler.Transforms } } + internal sealed class CapturedVariableAnnotation + { + } + public DelegateConstruction(DecompilerContext context) : base(context) { } @@ -240,7 +244,9 @@ namespace Decompiler.Transforms } // Now insert the variable declarations (we can do this after the replacements only so that the scope detection works): foreach (var tuple in variablesToDeclare) { - DeclareVariableInSmallestScope.DeclareVariable(blockStatement, tuple.Item1, tuple.Item2, allowPassIntoLoops: false); + var newVarDecl = DeclareVariableInSmallestScope.DeclareVariable(blockStatement, tuple.Item1, tuple.Item2, allowPassIntoLoops: false); + if (newVarDecl != null) + newVarDecl.Variables.Single().AddAnnotation(new CapturedVariableAnnotation()); } } return null; diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index ebce4a4db..15dc59905 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -19,26 +19,35 @@ namespace Decompiler.Transforms { TransformUsings(compilationUnit); TransformForeach(compilationUnit); + TransformFor(compilationUnit); } - #region using - static readonly AstNode usingVarDeclPattern = new VariableDeclarationStatement { + /// + /// $type $variable = $initializer; + /// + static readonly AstNode variableDeclPattern = new VariableDeclarationStatement { Type = new AnyNode("type").ToType(), Variables = { new NamedNode( "variable", new VariableInitializer { - Initializer = new AnyNode().ToExpression() + Initializer = new AnyNode("initializer").ToExpression() } ).ToVariable() } }; + + /// + /// Variable declaration without initializer. + /// static readonly AstNode simpleVariableDefinition = new VariableDeclarationStatement { Type = new AnyNode().ToType(), Variables = { new VariableInitializer() // any name but no initializer } }; + + #region using static readonly AstNode usingTryCatchPattern = new TryCatchStatement { TryBlock = new AnyNode("body").ToBlock(), FinallyBlock = new BlockStatement { @@ -65,7 +74,7 @@ namespace Decompiler.Transforms public void TransformUsings(AstNode compilationUnit) { foreach (AstNode node in compilationUnit.Descendants.ToArray()) { - Match m1 = usingVarDeclPattern.Match(node); + Match m1 = variableDeclPattern.Match(node); if (m1 == null) continue; AstNode tryCatch = node.NextSibling; while (simpleVariableDefinition.Match(tryCatch) != null) @@ -103,25 +112,23 @@ namespace Decompiler.Transforms ).ToVariable() } }, - EmbeddedStatement = new BlockStatement { - new ForStatement { - EmbeddedStatement = new BlockStatement { - new IfElseStatement { - Condition = new UnaryOperatorExpression( - UnaryOperatorType.Not, - new NamedNode("enumeratorIdent", new IdentifierExpression()).ToExpression().Invoke("MoveNext") - ), - TrueStatement = new BlockStatement { - new BreakStatement() - }, - FalseStatement = new BlockStatement { + EmbeddedStatement = new Choice { + // There are two forms of the foreach statement: + // one where the item variable is declared inside the loop, + // and one where it is declared outside of the loop. + // In the former case, we can apply the foreach pattern only if the variable wasn't captured. + { "itemVariableInsideLoop", + new BlockStatement { + new WhileStatement { + Condition = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Invoke("MoveNext"), + EmbeddedStatement = new BlockStatement { new VariableDeclarationStatement { Type = new AnyNode("itemType").ToType(), Variables = { new NamedNode( "itemVariable", new VariableInitializer { - Initializer = new Backreference("enumeratorIdent").ToExpression().Member("Current") + Initializer = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Member("Current") } ).ToVariable() } @@ -130,8 +137,29 @@ namespace Decompiler.Transforms } } } + }, + { "itemVariableOutsideLoop", + new BlockStatement { + new VariableDeclarationStatement { + Type = new AnyNode("itemType").ToType(), + Variables = { + new NamedNode("itemVariable", new VariableInitializer()).ToVariable() + } + }, + new WhileStatement { + Condition = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Invoke("MoveNext"), + EmbeddedStatement = new BlockStatement { + new AssignmentExpression { + Left = new IdentifierExpressionBackreference("itemVariable").ToExpression(), + Operator = AssignmentOperatorType.Assign, + Right = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Member("Current") + }, + new Repeat(new AnyNode("statement")).ToStatement() + } + } + } } - } + }.ToStatement() }; public void TransformForeach(AstNode compilationUnit) @@ -141,21 +169,73 @@ namespace Decompiler.Transforms if (m == null) continue; VariableInitializer enumeratorVar = m.Get("enumeratorVariable").Single(); - if (enumeratorVar.Name != m.Get("enumeratorIdent").Single().Identifier) - continue; VariableInitializer itemVar = m.Get("itemVariable").Single(); + if (m.Has("itemVariableInsideLoop") && itemVar.Annotation() != null) { + // cannot move captured variables out of loops + continue; + } BlockStatement newBody = new BlockStatement(); foreach (Statement stmt in m.Get("statement")) newBody.Add(stmt.Detach()); node.ReplaceWith( new ForeachStatement { VariableType = m.Get("itemType").Single().Detach(), - VariableName = enumeratorVar.Name, + VariableName = itemVar.Name, InExpression = m.Get("collection").Single().Detach(), EmbeddedStatement = newBody }); } } #endregion + + #region for + WhileStatement forPattern = new WhileStatement { + Condition = new BinaryOperatorExpression { + Left = new NamedNode("ident", new IdentifierExpression()).ToExpression(), + Operator = BinaryOperatorType.Any, + Right = new AnyNode("endExpr").ToExpression() + }, + EmbeddedStatement = new BlockStatement { + new Repeat(new AnyNode("statement")).ToStatement(), + new NamedNode( + "increment", + new ExpressionStatement( + new AssignmentExpression { + Left = new Backreference("ident").ToExpression(), + Operator = AssignmentOperatorType.Any, + Right = new AnyNode().ToExpression() + })).ToStatement(), + new ContinueStatement() + } + }; + + public void TransformFor(AstNode compilationUnit) + { + foreach (AstNode node in compilationUnit.Descendants.ToArray()) { + Match m1 = variableDeclPattern.Match(node); + if (m1 == null) continue; + AstNode next = node.NextSibling; + while (simpleVariableDefinition.Match(next) != null) + next = next.NextSibling; + Match m2 = forPattern.Match(next); + if (m2 == null) continue; + // ensure the variable in the for pattern is the same as in the declaration + if (m1.Get("variable").Single().Name != m2.Get("ident").Single().Identifier) + continue; + WhileStatement loop = (WhileStatement)next; + node.Remove(); + BlockStatement newBody = new BlockStatement(); + foreach (Statement stmt in m2.Get("statement")) + newBody.Add(stmt.Detach()); + loop.ReplaceWith( + new ForStatement { + Initializers = { (VariableDeclarationStatement)node }, + Condition = loop.Condition.Detach(), + Iterators = { m2.Get("increment").Single().Detach() }, + EmbeddedStatement = newBody + }); + } + } + #endregion } } diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index 667084ba8..82c05d985 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -203,6 +203,7 @@ namespace Decompiler // Mapping to the original instructions (useful for debugging) public List ILRanges { get; set; } + public TypeReference ExpectedType { get; set; } public TypeReference InferredType { get; set; } public ILExpression(ILCode code, object operand, params ILExpression[] args) @@ -296,6 +297,15 @@ namespace Decompiler if (this.InferredType != null) { output.Write(':'); this.InferredType.WriteTo(output, true, true); + if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) { + output.Write("[exp:"); + this.ExpectedType.WriteTo(output, true, true); + output.Write(']'); + } + } else if (this.ExpectedType != null) { + output.Write("[exp:"); + this.ExpectedType.WriteTo(output, true, true); + output.Write(']'); } output.Write('('); bool first = true; diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index 202ea5a1a..d1ccc1249 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -55,7 +55,7 @@ namespace Decompiler } bool anyArgumentIsMissingType = expr.Arguments.Any(a => a.InferredType == null); if (expr.InferredType == null || anyArgumentIsMissingType) - expr.InferredType = InferTypeForExpression(expr, null, forceInferChildren: anyArgumentIsMissingType); + expr.InferredType = InferTypeForExpression(expr, expr.ExpectedType, forceInferChildren: anyArgumentIsMissingType); } foreach (ILNode child in node.GetChildren()) { InferTypes(child); @@ -97,6 +97,7 @@ namespace Decompiler /// The inferred type TypeReference InferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false) { + expr.ExpectedType = expectedType; if (forceInferChildren || expr.InferredType == null) expr.InferredType = DoInferTypeForExpression(expr, expectedType, forceInferChildren); return expr.InferredType; @@ -552,13 +553,16 @@ namespace Decompiler TypeReference leftPreferred = DoInferTypeForExpression(left, null); TypeReference rightPreferred = DoInferTypeForExpression(right, null); if (leftPreferred == rightPreferred) { - return left.InferredType = right.InferredType = leftPreferred; + return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred; } else if (rightPreferred == DoInferTypeForExpression(left, rightPreferred)) { - return left.InferredType = right.InferredType = rightPreferred; + return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = rightPreferred; } else if (leftPreferred == DoInferTypeForExpression(right, leftPreferred)) { - return left.InferredType = right.InferredType = leftPreferred; + return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred; } else { - return left.InferredType = right.InferredType = TypeWithMoreInformation(leftPreferred, rightPreferred); + left.ExpectedType = right.ExpectedType = TypeWithMoreInformation(leftPreferred, rightPreferred); + left.InferredType = DoInferTypeForExpression(left, left.ExpectedType); + right.InferredType = DoInferTypeForExpression(left, right.ExpectedType); + return left.ExpectedType; } } @@ -637,5 +641,39 @@ namespace Decompiler return true; return null; } + + public static TypeCode GetTypeCode(TypeSystem typeSystem, TypeReference type) + { + if (type == typeSystem.Boolean) + return TypeCode.Boolean; + else if (type == typeSystem.Byte) + return TypeCode.Byte; + else if (type == typeSystem.Char) + return TypeCode.Char; + else if (type == typeSystem.Double) + return TypeCode.Double; + else if (type == typeSystem.Int16) + return TypeCode.Int16; + else if (type == typeSystem.Int32) + return TypeCode.Int32; + else if (type == typeSystem.Int64) + return TypeCode.Int64; + else if (type == typeSystem.Single) + return TypeCode.Single; + else if (type == typeSystem.Double) + return TypeCode.Double; + else if (type == typeSystem.SByte) + return TypeCode.SByte; + else if (type == typeSystem.UInt16) + return TypeCode.UInt16; + else if (type == typeSystem.UInt32) + return TypeCode.UInt32; + else if (type == typeSystem.UInt64) + return TypeCode.UInt64; + else if (type == typeSystem.String) + return TypeCode.String; + else + return TypeCode.Object; + } } } diff --git a/ICSharpCode.Decompiler/Tests/StackTests/StackTests.exe b/ICSharpCode.Decompiler/Tests/StackTests/StackTests.exe new file mode 100644 index 000000000..db6194ad6 Binary files /dev/null and b/ICSharpCode.Decompiler/Tests/StackTests/StackTests.exe differ diff --git a/ICSharpCode.Decompiler/Tests/StackTests/StackTests.il b/ICSharpCode.Decompiler/Tests/StackTests/StackTests.il new file mode 100644 index 000000000..51cee3754 --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/StackTests/StackTests.il @@ -0,0 +1,132 @@ +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) + .ver 4:0:0:0 +} +.assembly StackTests +{ + .hash algorithm 0x00008004 + .ver 1:0:4059:39717 +} +.module StackTests.exe +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000003 // ILONLY 32BITREQUIRED + +.class private auto ansi beforefieldinit StackTests.Program extends [mscorlib]System.Object +{ + .method public hidebysig static void Main(string[] args) cil managed + { + .entrypoint + .maxstack 8 + + ldc.i4.0 + call string StackTests.Program::Test1(bool cond) + call void [mscorlib]System.Console::WriteLine(string) // false + + ldc.i4.1 + call string StackTests.Program::Test1(bool cond) + call void [mscorlib]System.Console::WriteLine(string) // true + + ldc.i4.0 + ldc.i4.0 + ldc.i4.0 + call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2) + call void [mscorlib]System.Console::WriteLine(int32) // 11 + + ldc.i4.0 + ldc.i4.1 + ldc.i4.0 + call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2) + call void [mscorlib]System.Console::WriteLine(int32) // 21 + + ldc.i4.1 + ldc.i4.1 + ldc.i4.1 + call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2) + call void [mscorlib]System.Console::WriteLine(int32) // 32 + + ldc.i4.2 + ldc.i4.1 + ldc.i4.0 + call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2) + call void [mscorlib]System.Console::WriteLine(int32) // 23 + + ret + } + + .method public hidebysig static string Test1(bool cond) cil managed + { + ldarg.0 + brtrue TRUE + + FALSE: + ldstr "false" + br EXIT + + TRUE: + ldstr "true" + + EXIT: + ret + } + + .method public hidebysig static int32 Test2(int32 switch1, int32 br1, int32 br2) cil managed + { + ldarg.0 + switch (ENTRY1, ENTRY2, ENTRY3) + ldc.i4.0 + ret + + ENTRY1: + ldc.i4.1 + br BRANCH1 + + ENTRY2: + ldc.i4.2 + br BRANCH1 + + ENTRY3: + ldc.i4.3 + br BRANCH2 + + BRANCH1: + ldarg.1 + brtrue BRANCH2 + + EXIT1: + ldc.i4 10 + add + ret + + BRANCH2: + ldarg.2 + brtrue.s EXIT3 + + EXIT2: + ldc.i4 20 + add + ret + + EXIT3: + ldc.i4 30 + add + ret + } + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method Program::.ctor + +} // end of class StackTests.Program + + +// ============================================================= diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index 099a8f3dc..d4fa06fdd 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -72,7 +72,7 @@ namespace ICSharpCode.Decompiler.Tests } while ((line2 = r2.ReadLine()) != null) { ok = false; - diff.WriteLine("+" + line1); + diff.WriteLine("+" + line2); } return ok; } diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 39b918a7a..5a8f98842 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -26,6 +26,8 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; + +using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.TreeView; using ILSpy.Debugger.AvalonEdit; diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs index 5a5218773..033ad7913 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using ICSharpCode.NRefactory.CSharp.PatternMatching; @@ -156,29 +157,45 @@ namespace ICSharpCode.NRefactory.CSharp internal bool DoMatch(AstNodeCollection other, Match match) { - AstNode cur1 = this.node.FirstChild; - AstNode cur2 = other.node.FirstChild; - while (true) { - while (cur1 != null && cur1.Role != role) + Stack patternStack = new Stack(); + Stack stack = new Stack(); + patternStack.Push(this.node.FirstChild); + stack.Push(new Pattern.PossibleMatch(other.node.FirstChild, match.CheckPoint())); + while (stack.Count > 0) { + AstNode cur1 = patternStack.Pop(); + AstNode cur2 = stack.Peek().NextOther; + match.RestoreCheckPoint(stack.Pop().Checkpoint); + bool success = true; + while (cur1 != null && success) { + while (cur1 != null && cur1.Role != role) + cur1 = cur1.NextSibling; + while (cur2 != null && cur2.Role != role) + cur2 = cur2.NextSibling; + if (cur1 == null) + break; + + Pattern pattern = cur1 as Pattern; + if (pattern == null && cur1.NodeType == NodeType.Placeholder) + pattern = cur1.GetChildByRole(TypePlaceholder.ChildRole) as Pattern; + if (pattern != null) { + Debug.Assert(stack.Count == patternStack.Count); + success = pattern.DoMatchCollection(role, cur2, match, stack); + Debug.Assert(stack.Count >= patternStack.Count); + while (stack.Count > patternStack.Count) + patternStack.Push(cur1.NextSibling); + } else { + success = cur1.DoMatch(cur2, match); + } cur1 = cur1.NextSibling; + if (cur2 != null) + cur2 = cur2.NextSibling; + } while (cur2 != null && cur2.Role != role) cur2 = cur2.NextSibling; - if (cur1 == null || cur2 == null) - break; - Pattern pattern = cur1 as Pattern; - if (pattern == null && cur1.NodeType == NodeType.Pattern) - pattern = cur1.GetChildByRole(TypePlaceholder.ChildRole) as Pattern; - if (pattern != null) { - if (!pattern.DoMatchCollection(role, ref cur2, match)) - return false; - } else { - if (!cur1.DoMatch(cur2, match)) - return false; - cur2 = cur2.NextSibling; - } - cur1 = cur1.NextSibling; + if (success && cur2 == null) + return true; } - return cur1 == null && cur2 == null; + return false; } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/AssignmentExpression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/AssignmentExpression.cs index f3b31a750..6db96ec89 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/AssignmentExpression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/AssignmentExpression.cs @@ -75,7 +75,8 @@ namespace ICSharpCode.NRefactory.CSharp protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { AssignmentExpression o = other as AssignmentExpression; - return o != null && this.Left.DoMatch(o.Left, match) && this.Operator == o.Operator && this.Right.DoMatch(o.Right, match); + return o != null && (this.Operator == AssignmentOperatorType.Any || this.Operator == o.Operator) + && this.Left.DoMatch(o.Left, match) && this.Right.DoMatch(o.Right, match); } public static string GetOperatorSymbol(AssignmentOperatorType op) @@ -136,5 +137,8 @@ namespace ICSharpCode.NRefactory.CSharp BitwiseOr, /// left ^= right ExclusiveOr, + + /// Any operator (for pattern matching) + Any } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/BinaryOperatorExpression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/BinaryOperatorExpression.cs index ab50392b6..171e3e31c 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/BinaryOperatorExpression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/BinaryOperatorExpression.cs @@ -1,6 +1,6 @@ // // BinaryOperatorExpression.cs -// +// // Author: // Mike Krüger // @@ -75,7 +75,8 @@ namespace ICSharpCode.NRefactory.CSharp protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { BinaryOperatorExpression o = other as BinaryOperatorExpression; - return o != null && this.Left.DoMatch(o.Left, match) && this.Operator == o.Operator && this.Right.DoMatch(o.Right, match); + return o != null && (this.Operator == BinaryOperatorType.Any || this.Operator == o.Operator) + && this.Left.DoMatch(o.Left, match) && this.Right.DoMatch(o.Right, match); } public static string GetOperatorSymbol(BinaryOperatorType op) @@ -175,6 +176,9 @@ namespace ICSharpCode.NRefactory.CSharp /// left ?? right NullCoalescing, - None + /// + /// Any binary operator (used in pattern matching) + /// + Any } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/Expression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/Expression.cs index c83e740c2..3055ae7fb 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/Expression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/Expression.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; namespace ICSharpCode.NRefactory.CSharp @@ -50,6 +51,14 @@ namespace ICSharpCode.NRefactory.CSharp return (Expression)base.Clone(); } + // Make debugging easier by giving Expressions a ToString() implementation + public override string ToString() + { + StringWriter w = new StringWriter(); + AcceptVisitor(new OutputVisitor(w, new CSharpFormattingPolicy()), null); + return w.ToString(); + } + public Expression ReplaceWith(Func replaceFunction) { if (replaceFunction == null) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PrimitiveExpression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PrimitiveExpression.cs index ca1b27443..cc4fbe1c0 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PrimitiveExpression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PrimitiveExpression.cs @@ -31,6 +31,8 @@ namespace ICSharpCode.NRefactory.CSharp /// public class PrimitiveExpression : Expression { + public static readonly object AnyValue = new object(); + AstLocation startLocation; public override AstLocation StartLocation { get { @@ -70,7 +72,7 @@ namespace ICSharpCode.NRefactory.CSharp protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { PrimitiveExpression o = other as PrimitiveExpression; - return o != null && object.Equals(this.Value, o.Value); + return o != null && (this.Value == AnyValue || object.Equals(this.Value, o.Value)); } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/NodeType.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/NodeType.cs index 946b838d6..5d831be04 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/NodeType.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/NodeType.cs @@ -44,7 +44,8 @@ namespace ICSharpCode.NRefactory.CSharp Expression, Token, QueryClause, - Pattern + Pattern, + Placeholder } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/AnyNode.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/AnyNode.cs index 028e7d7fd..e992da9ce 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/AnyNode.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/AnyNode.cs @@ -12,6 +12,10 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching { readonly string groupName; + public string GroupName { + get { return groupName; } + } + public AnyNode(string groupName = null) { this.groupName = groupName; @@ -22,5 +26,10 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching match.Add(this.groupName, other); return other != null && !other.IsNull; } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return ((IPatternAstVisitor)visitor).VisitAnyNode(this, data); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Backreference.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Backreference.cs index db3e3eceb..5d30b072e 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Backreference.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Backreference.cs @@ -13,6 +13,10 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching { readonly string referencedGroupName; + public string ReferencedGroupName { + get { return referencedGroupName; } + } + public Backreference(string referencedGroupName) { if (referencedGroupName == null) @@ -24,5 +28,43 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching { return match.Get(referencedGroupName).Last().Match(other) != null; } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return ((IPatternAstVisitor)visitor).VisitBackreference(this, data); + } + } + + /// + /// Matches identifier expressions that have the same identifier as the referenced variable/type definition/method definition. + /// + public class IdentifierExpressionBackreference : Pattern + { + readonly string referencedGroupName; + + public string ReferencedGroupName { + get { return referencedGroupName; } + } + + public IdentifierExpressionBackreference(string referencedGroupName) + { + if (referencedGroupName == null) + throw new ArgumentNullException("referencedGroupName"); + this.referencedGroupName = referencedGroupName; + } + + protected internal override bool DoMatch(AstNode other, Match match) + { + IdentifierExpression ident = other as IdentifierExpression; + if (ident == null || ident.TypeArguments.Any()) + return false; + AstNode referenced = match.Get(referencedGroupName).Last(); + return ident.Identifier == referenced.GetChildByRole(AstNode.Roles.Identifier).Name; + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return ((IPatternAstVisitor)visitor).VisitIdentifierExpressionBackreference(this, data); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Choice.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Choice.cs index 6435cc324..b2352e402 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Choice.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Choice.cs @@ -3,13 +3,14 @@ using System; using System.Collections; +using System.Collections.Generic; namespace ICSharpCode.NRefactory.CSharp.PatternMatching { /// /// Matches one of several alternatives. /// - public class Choice : Pattern, IEnumerable + public class Choice : Pattern, IEnumerable { public static readonly Role AlternativeRole = new Role("Alternative", AstNode.Null); @@ -35,9 +36,19 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching return false; } + IEnumerator IEnumerable.GetEnumerator() + { + return GetChildrenByRole(AlternativeRole).GetEnumerator(); + } + IEnumerator IEnumerable.GetEnumerator() { return GetChildrenByRole(AlternativeRole).GetEnumerator(); } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return ((IPatternAstVisitor)visitor).VisitChoice(this, data); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/IPatternAstVisitor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/IPatternAstVisitor.cs new file mode 100644 index 000000000..7b29a00c4 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/IPatternAstVisitor.cs @@ -0,0 +1,22 @@ +// 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; + +namespace ICSharpCode.NRefactory.CSharp.PatternMatching +{ + /// + /// Extended AST visitor that works for patterns. + /// + public interface IPatternAstVisitor : IAstVisitor + { + S VisitPlaceholder(AstNode placeholder, AstNode child, T data); + + S VisitAnyNode(AnyNode anyNode, T data); + S VisitBackreference(Backreference backreference, T data); + S VisitChoice(Choice choice, T data); + S VisitNamedNode(NamedNode namedNode, T data); + S VisitRepeat(Repeat repeat, T data); + S VisitIdentifierExpressionBackreference(IdentifierExpressionBackreference identifierExpressionBackreference, T data); + } +} diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/NamedGroup.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/NamedNode.cs similarity index 78% rename from NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/NamedGroup.cs rename to NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/NamedNode.cs index 5cf361a6e..baf955684 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/NamedGroup.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/NamedNode.cs @@ -14,6 +14,10 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching readonly string groupName; + public string GroupName { + get { return groupName; } + } + public NamedNode(string groupName, AstNode childNode) { this.groupName = groupName; @@ -25,5 +29,10 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching match.Add(this.groupName, other); return GetChildByRole(ElementRole).DoMatch(other, match); } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return ((IPatternAstVisitor)visitor).VisitNamedNode(this, data); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs index ab5efbe8d..073415f51 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs @@ -2,6 +2,9 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; namespace ICSharpCode.NRefactory.CSharp.PatternMatching { @@ -14,16 +17,21 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching get { return NodeType.Pattern; } } - protected internal virtual bool DoMatchCollection(Role role, ref AstNode other, Match match) + internal struct PossibleMatch { - bool result = DoMatch(other, match); - other = other.NextSibling; - return result; + public readonly AstNode NextOther; // next node after the last matched node + public readonly int Checkpoint; // checkpoint + + public PossibleMatch(AstNode nextOther, int checkpoint) + { + this.NextOther = nextOther; + this.Checkpoint = checkpoint; + } } - public override S AcceptVisitor(IAstVisitor visitor, T data) + internal virtual bool DoMatchCollection(Role role, AstNode pos, Match match, Stack backtrackingStack) { - return default(S); + return DoMatch(pos, match); } public AstType ToType() @@ -50,5 +58,13 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching { return new VariablePlaceholder(this); } + + // Make debugging easier by giving Patterns a ToString() implementation + public override string ToString() + { + StringWriter w = new StringWriter(); + AcceptVisitor(new OutputVisitor(w, new CSharpFormattingPolicy()), null); + return w.ToString(); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs index f84faaa39..2bac1c6e1 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs @@ -15,12 +15,12 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching } public override NodeType NodeType { - get { return GetChildByRole(TypePlaceholder.ChildRole).NodeType; } + get { return NodeType.Placeholder; } } public override S AcceptVisitor(IAstVisitor visitor, T data) { - return GetChildByRole(TypePlaceholder.ChildRole).AcceptVisitor(visitor, data); + return ((IPatternAstVisitor)visitor).VisitPlaceholder(this, GetChildByRole(TypePlaceholder.ChildRole), data); } protected internal override bool DoMatch(AstNode other, Match match) @@ -37,12 +37,12 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching } public override NodeType NodeType { - get { return GetChildByRole(TypePlaceholder.ChildRole).NodeType; } + get { return NodeType.Placeholder; } } public override S AcceptVisitor(IAstVisitor visitor, T data) { - return GetChildByRole(TypePlaceholder.ChildRole).AcceptVisitor(visitor, data); + return ((IPatternAstVisitor)visitor).VisitPlaceholder(this, GetChildByRole(TypePlaceholder.ChildRole), data); } protected internal override bool DoMatch(AstNode other, Match match) @@ -59,12 +59,12 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching } public override NodeType NodeType { - get { return GetChildByRole(TypePlaceholder.ChildRole).NodeType; } + get { return NodeType.Placeholder; } } public override S AcceptVisitor(IAstVisitor visitor, T data) { - return GetChildByRole(TypePlaceholder.ChildRole).AcceptVisitor(visitor, data); + return ((IPatternAstVisitor)visitor).VisitPlaceholder(this, GetChildByRole(TypePlaceholder.ChildRole), data); } protected internal override bool DoMatch(AstNode other, Match match) @@ -81,12 +81,12 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching } public override NodeType NodeType { - get { return GetChildByRole(TypePlaceholder.ChildRole).NodeType; } + get { return NodeType.Placeholder; } } public override S AcceptVisitor(IAstVisitor visitor, T data) { - return GetChildByRole(TypePlaceholder.ChildRole).AcceptVisitor(visitor, data); + return ((IPatternAstVisitor)visitor).VisitPlaceholder(this, GetChildByRole(TypePlaceholder.ChildRole), data); } protected internal override bool DoMatch(AstNode other, Match match) @@ -103,12 +103,12 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching } public override NodeType NodeType { - get { return GetChildByRole(TypePlaceholder.ChildRole).NodeType; } + get { return NodeType.Placeholder; } } public override S AcceptVisitor(IAstVisitor visitor, T data) { - return GetChildByRole(TypePlaceholder.ChildRole).AcceptVisitor(visitor, data); + return ((IPatternAstVisitor)visitor).VisitPlaceholder(this, GetChildByRole(TypePlaceholder.ChildRole), data); } protected internal override bool DoMatch(AstNode other, Match match) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Repeat.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Repeat.cs index 3c2bc5653..c968bce45 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Repeat.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Repeat.cs @@ -2,6 +2,7 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; +using System.Collections.Generic; using System.Diagnostics; namespace ICSharpCode.NRefactory.CSharp.PatternMatching @@ -20,24 +21,22 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching AddChild(childNode, ElementRole); } - protected internal override bool DoMatchCollection(Role role, ref AstNode other, Match match) + internal override bool DoMatchCollection(Role role, AstNode pos, Match match, Stack backtrackingStack) { - Debug.Assert(other != null && other.Role == role); + Debug.Assert(pos == null || pos.Role == role); int matchCount = 0; - var lastValidCheckpoint = match.CheckPoint(); + if (this.MinCount <= 0) + backtrackingStack.Push(new PossibleMatch(pos, match.CheckPoint())); AstNode element = GetChildByRole(ElementRole); - AstNode pos = other; - while (pos != null && element.DoMatch(pos, match)) { + while (matchCount < this.MaxCount && pos != null && element.DoMatch(pos, match)) { matchCount++; - lastValidCheckpoint = match.CheckPoint(); do { pos = pos.NextSibling; } while (pos != null && pos.Role != role); - // set 'other' (=pointer in collection) to the next node after the valid match - other = pos; + if (matchCount >= this.MinCount) + backtrackingStack.Push(new PossibleMatch(pos, match.CheckPoint())); } - match.RestoreCheckPoint(lastValidCheckpoint); // restote old checkpoint after failed match - return matchCount >= MinCount && matchCount <= MaxCount; + return false; // never do a normal (single-element) match; always make the caller look at the results on the back-tracking stack. } protected internal override bool DoMatch(AstNode other, Match match) @@ -45,7 +44,12 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching if (other == null || other.IsNull) return this.MinCount <= 0; else - return GetChildByRole(ElementRole).DoMatch(other, match); + return this.MaxCount >= 1 && GetChildByRole(ElementRole).DoMatch(other, match); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return ((IPatternAstVisitor)visitor).VisitRepeat(this, data); } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/CheckedStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/CheckedStatement.cs index 1461a6407..d80dc6d57 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/CheckedStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/CheckedStatement.cs @@ -1,4 +1,4 @@ -// +// // CheckedStatement.cs // // Author: @@ -44,5 +44,11 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitCheckedStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CheckedStatement o = other as CheckedStatement; + return o != null && this.Body.DoMatch(o.Body, match); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ContinueStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ContinueStatement.cs index 7dc06c596..1de6b5d50 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ContinueStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ContinueStatement.cs @@ -1,4 +1,4 @@ -// +// // ContinueStatement.cs // // Author: @@ -39,5 +39,11 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitContinueStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ContinueStatement o = other as ContinueStatement; + return o != null; + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/DoWhileStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/DoWhileStatement.cs index 51d5f4708..378fd43ab 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/DoWhileStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/DoWhileStatement.cs @@ -1,4 +1,4 @@ -// +// // DoWhileStatement.cs // // Author: @@ -68,6 +68,12 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitDoWhileStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + DoWhileStatement o = other as DoWhileStatement; + return o != null && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match) && this.Condition.DoMatch(o.Condition, match); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/EmptyStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/EmptyStatement.cs index 7a9528b86..3ee53863e 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/EmptyStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/EmptyStatement.cs @@ -1,4 +1,4 @@ -// +// // EmptyStatement.cs // // Author: @@ -52,5 +52,11 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitEmptyStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + EmptyStatement o = other as EmptyStatement; + return o != null; + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/FixedStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/FixedStatement.cs index d500c8dbf..b1b7759f0 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/FixedStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/FixedStatement.cs @@ -63,5 +63,11 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitFixedStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + FixedStatement o = other as FixedStatement; + return o != null && this.Variables.DoMatch(o.Variables, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ForeachStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ForeachStatement.cs index ca75d6d0d..6caf3883e 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ForeachStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ForeachStatement.cs @@ -1,4 +1,4 @@ -// +// // ForeachStatement.cs // // Author: @@ -75,5 +75,12 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitForeachStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ForeachStatement o = other as ForeachStatement; + return o != null && this.VariableType.DoMatch(o.VariableType, match) && MatchString(this.VariableName, o.VariableName) + && this.InExpression.DoMatch(o.InExpression, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/GotoStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/GotoStatement.cs index ee1784ad7..2eeeb0bc3 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/GotoStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/GotoStatement.cs @@ -56,14 +56,6 @@ namespace ICSharpCode.NRefactory.CSharp } } - /// - /// Used for "goto case LabelExpression;" - /// - public Expression LabelExpression { - get { return GetChildByRole (Roles.Expression); } - set { SetChildByRole (Roles.Expression, value); } - } - public CSharpTokenNode SemicolonToken { get { return GetChildByRole (Roles.Semicolon); } } @@ -72,6 +64,12 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitGotoStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + GotoStatement o = other as GotoStatement; + return o != null && MatchString(this.Label, o.Label); + } } /// @@ -89,18 +87,6 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (CaseKeywordRole); } } - public string Label { - get { - return GetChildByRole (Roles.Identifier).Name; - } - set { - if (string.IsNullOrEmpty(value)) - SetChildByRole(Roles.Identifier, null); - else - SetChildByRole(Roles.Identifier, new Identifier(value, AstLocation.Empty)); - } - } - /// /// Used for "goto case LabelExpression;" /// @@ -117,6 +103,12 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitGotoCaseStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + GotoCaseStatement o = other as GotoCaseStatement; + return o != null && this.LabelExpression.DoMatch(o.LabelExpression, match); + } } /// @@ -142,5 +134,11 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitGotoDefaultStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + GotoDefaultStatement o = other as GotoDefaultStatement; + return o != null; + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/LabelStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/LabelStatement.cs index da9dc5273..3ebdc3302 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/LabelStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/LabelStatement.cs @@ -1,4 +1,4 @@ -// +// // LabelStatement.cs // // Author: @@ -40,9 +40,19 @@ namespace ICSharpCode.NRefactory.CSharp } } + public CSharpTokenNode Colon { + get { return GetChildByRole (Roles.Colon); } + } + public override S AcceptVisitor (IAstVisitor visitor, T data) { return visitor.VisitLabelStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + LabelStatement o = other as LabelStatement; + return o != null && MatchString(this.Label, o.Label); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/LockStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/LockStatement.cs index 3866dbd17..7298e4f1d 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/LockStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/LockStatement.cs @@ -1,4 +1,4 @@ -// +// // LockStatement.cs // // Author: @@ -57,5 +57,11 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitLockStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + LockStatement o = other as LockStatement; + return o != null && this.Expression.DoMatch(o.Expression, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ReturnStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ReturnStatement.cs index 938fcb679..d8368d442 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ReturnStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ReturnStatement.cs @@ -1,4 +1,4 @@ -// +// // ReturnStatement.cs // // Author: @@ -48,5 +48,11 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitReturnStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ReturnStatement o = other as ReturnStatement; + return o != null && this.Expression.DoMatch(o.Expression, match); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/Statement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/Statement.cs index 1725255f4..0d678ff59 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/Statement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/Statement.cs @@ -52,10 +52,5 @@ namespace ICSharpCode.NRefactory.CSharp public override NodeType NodeType { get { return NodeType.Statement; } } - - protected internal override bool DoMatch(AstNode other, ICSharpCode.NRefactory.CSharp.PatternMatching.Match match) - { - throw new NotImplementedException(); - } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/SwitchStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/SwitchStatement.cs index f8218d507..09b82be63 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/SwitchStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/SwitchStatement.cs @@ -69,6 +69,12 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitSwitchStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + SwitchStatement o = other as SwitchStatement; + return o != null && this.Expression.DoMatch(o.Expression, match) && this.SwitchSections.DoMatch(o.SwitchSections, match); + } } public class SwitchSection : AstNode diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ThrowStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ThrowStatement.cs index 545dc6ac3..fc9f34237 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ThrowStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ThrowStatement.cs @@ -1,4 +1,4 @@ -// +// // ThrowStatement.cs // // Author: @@ -48,5 +48,11 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitThrowStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ThrowStatement o = other as ThrowStatement; + return o != null && this.Expression.DoMatch(o.Expression, match); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/UncheckedStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/UncheckedStatement.cs index ec55c0cbc..a0bef129e 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/UncheckedStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/UncheckedStatement.cs @@ -1,4 +1,4 @@ -// +// // UncheckedStatement.cs // // Author: @@ -44,5 +44,11 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitUncheckedStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UncheckedStatement o = other as UncheckedStatement; + return o != null && this.Body.DoMatch(o.Body, match); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/UnsafeStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/UnsafeStatement.cs index d9a45a860..c5ccb63af 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/UnsafeStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/UnsafeStatement.cs @@ -1,4 +1,4 @@ -// +// // UnsafeStatement.cs // // Author: @@ -44,5 +44,11 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitUnsafeStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UnsafeStatement o = other as UnsafeStatement; + return o != null && this.Body.DoMatch(o.Body, match); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/WhileStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/WhileStatement.cs index 9eec22b2f..01470b3ab 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/WhileStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/WhileStatement.cs @@ -1,4 +1,4 @@ -// +// // WhileStatement.cs // // Author: @@ -59,5 +59,11 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitWhileStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + WhileStatement o = other as WhileStatement; + return o != null && this.Condition.DoMatch(o.Condition, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/YieldBreakStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/YieldBreakStatement.cs index 2982dbf41..4c572615a 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/YieldBreakStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/YieldBreakStatement.cs @@ -1,4 +1,4 @@ -// +// // YieldBreakStatement.cs // // Author: @@ -50,5 +50,11 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitYieldBreakStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + YieldBreakStatement o = other as YieldBreakStatement; + return o != null; + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/YieldStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/YieldStatement.cs index 531d21ec5..001815f3a 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/YieldStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/YieldStatement.cs @@ -1,4 +1,4 @@ -// +// // YieldStatement.cs // // Author: @@ -55,5 +55,11 @@ namespace ICSharpCode.NRefactory.CSharp { return visitor.VisitYieldStatement (this, data); } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + YieldStatement o = other as YieldStatement; + return o != null && this.Expression.DoMatch(o.Expression, match); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs index cfa2a8634..cf880653e 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs @@ -8,6 +8,8 @@ using System.Globalization; using System.IO; using System.Linq; using System.Text; + +using ICSharpCode.NRefactory.CSharp.PatternMatching; using ICSharpCode.NRefactory.TypeSystem; namespace ICSharpCode.NRefactory.CSharp @@ -15,7 +17,7 @@ namespace ICSharpCode.NRefactory.CSharp /// /// Outputs the AST. /// - public class OutputVisitor : IAstVisitor + public class OutputVisitor : IPatternAstVisitor { readonly IOutputFormatter formatter; readonly CSharpFormattingPolicy policy; @@ -1989,5 +1991,91 @@ namespace ICSharpCode.NRefactory.CSharp return EndNode(identifier); } #endregion + + #region Pattern Nodes + object IPatternAstVisitor.VisitPlaceholder(AstNode placeholder, AstNode child, object data) + { + StartNode(placeholder); + child.AcceptVisitor(this, data); + return EndNode(placeholder); + } + + object IPatternAstVisitor.VisitAnyNode(AnyNode anyNode, object data) + { + StartNode(anyNode); + if (!string.IsNullOrEmpty(anyNode.GroupName)) { + WriteIdentifier(anyNode.GroupName); + WriteToken(":", AstNode.Roles.Colon); + } + WriteKeyword("anyNode"); + return EndNode(anyNode); + } + + object IPatternAstVisitor.VisitBackreference(Backreference backreference, object data) + { + StartNode(backreference); + WriteKeyword("backreference"); + LPar(); + WriteIdentifier(backreference.ReferencedGroupName); + RPar(); + return EndNode(backreference); + } + + object IPatternAstVisitor.VisitIdentifierExpressionBackreference(IdentifierExpressionBackreference identifierExpressionBackreference, object data) + { + StartNode(identifierExpressionBackreference); + WriteKeyword("identifierBackreference"); + LPar(); + WriteIdentifier(identifierExpressionBackreference.ReferencedGroupName); + RPar(); + return EndNode(identifierExpressionBackreference); + } + + object IPatternAstVisitor.VisitChoice(Choice choice, object data) + { + StartNode(choice); + WriteKeyword("choice"); + Space(); + LPar(); + NewLine(); + formatter.Indent(); + foreach (AstNode alternative in choice) { + alternative.AcceptVisitor(this, data); + if (alternative != choice.LastChild) + WriteToken(",", AstNode.Roles.Comma); + NewLine(); + } + formatter.Unindent(); + RPar(); + return EndNode(choice); + } + + object IPatternAstVisitor.VisitNamedNode(NamedNode namedNode, object data) + { + StartNode(namedNode); + if (!string.IsNullOrEmpty(namedNode.GroupName)) { + WriteIdentifier(namedNode.GroupName); + WriteToken(":", AstNode.Roles.Colon); + } + namedNode.GetChildByRole(NamedNode.ElementRole).AcceptVisitor(this, data); + return EndNode(namedNode); + } + + object IPatternAstVisitor.VisitRepeat(Repeat repeat, object data) + { + StartNode(repeat); + WriteKeyword("repeat"); + LPar(); + if (repeat.MinCount != 0 || repeat.MaxCount != int.MaxValue) { + WriteIdentifier(repeat.MinCount.ToString()); + WriteToken(",", AstNode.Roles.Comma); + WriteIdentifier(repeat.MaxCount.ToString()); + WriteToken(",", AstNode.Roles.Comma); + } + repeat.GetChildByRole(Repeat.ElementRole).AcceptVisitor(this, data); + RPar(); + return EndNode(repeat); + } + #endregion } } diff --git a/NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index 46ae326fb..a0aae81d2 100644 --- a/NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -98,8 +98,9 @@ + - +