Browse Source

Merge branch 'master' of git://github.com/icsharpcode/ILSpy into Debugger

pull/191/merge
Eusebiu Marcu 15 years ago
parent
commit
38b25a1a83
  1. 2
      Debugger/Debugger.Core/NRefactory/Visitors/ExpressionEvaluator.cs
  2. 28
      ICSharpCode.Decompiler/Ast/AstBuilder.cs
  3. 187
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  4. 7
      ICSharpCode.Decompiler/Ast/CommentStatement.cs
  5. 10
      ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs
  6. 8
      ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
  7. 122
      ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  8. 10
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  9. 48
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  10. BIN
      ICSharpCode.Decompiler/Tests/StackTests/StackTests.exe
  11. 132
      ICSharpCode.Decompiler/Tests/StackTests/StackTests.il
  12. 2
      ICSharpCode.Decompiler/Tests/TestRunner.cs
  13. 2
      ILSpy/MainWindow.xaml.cs
  14. 55
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs
  15. 6
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/AssignmentExpression.cs
  16. 10
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/BinaryOperatorExpression.cs
  17. 9
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/Expression.cs
  18. 4
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PrimitiveExpression.cs
  19. 3
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/NodeType.cs
  20. 9
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/AnyNode.cs
  21. 42
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Backreference.cs
  22. 13
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Choice.cs
  23. 22
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/IPatternAstVisitor.cs
  24. 9
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/NamedNode.cs
  25. 28
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs
  26. 20
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs
  27. 26
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Repeat.cs
  28. 8
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/CheckedStatement.cs
  29. 8
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ContinueStatement.cs
  30. 8
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/DoWhileStatement.cs
  31. 8
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/EmptyStatement.cs
  32. 6
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/FixedStatement.cs
  33. 9
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ForeachStatement.cs
  34. 38
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/GotoStatement.cs
  35. 12
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/LabelStatement.cs
  36. 8
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/LockStatement.cs
  37. 8
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ReturnStatement.cs
  38. 5
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/Statement.cs
  39. 6
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/SwitchStatement.cs
  40. 8
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ThrowStatement.cs
  41. 8
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/UncheckedStatement.cs
  42. 8
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/UnsafeStatement.cs
  43. 8
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/WhileStatement.cs
  44. 8
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/YieldBreakStatement.cs
  45. 8
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/YieldStatement.cs
  46. 90
      NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs
  47. 3
      NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

2
Debugger/Debugger.Core/NRefactory/Visitors/ExpressionEvaluator.cs

@ -304,7 +304,7 @@ namespace ICSharpCode.NRefactory.Visitors
} }
TypedValue right; TypedValue right;
if (op == BinaryOperatorType.None) { if (op == BinaryOperatorType.Any) {
right = Evaluate(assignmentExpression.Right); right = Evaluate(assignmentExpression.Right);
} else { } else {
BinaryOperatorExpression binOpExpr = new BinaryOperatorExpression(); BinaryOperatorExpression binOpExpr = new BinaryOperatorExpression();

28
ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -31,9 +31,14 @@ namespace Decompiler
MethodDefinition method = member as MethodDefinition; MethodDefinition method = member as MethodDefinition;
if (method != null && (method.IsGetter || method.IsSetter || method.IsAddOn || method.IsRemoveOn)) if (method != null && (method.IsGetter || method.IsSetter || method.IsAddOn || method.IsRemoveOn))
return true; return true;
if (method != null && method.Name.StartsWith("<", StringComparison.Ordinal) && method.IsCompilerGenerated())
return true;
TypeDefinition type = member as TypeDefinition; TypeDefinition type = member as TypeDefinition;
if (type != null && type.DeclaringType != null && type.Name.StartsWith("<>c__DisplayClass", StringComparison.Ordinal) && type.IsCompilerGenerated()) if (type != null && type.DeclaringType != null && type.Name.StartsWith("<>c__DisplayClass", StringComparison.Ordinal) && type.IsCompilerGenerated())
return true; return true;
FieldDefinition field = member as FieldDefinition;
if (field != null && field.Name.StartsWith("CS$<>", StringComparison.Ordinal) && field.IsCompilerGenerated())
return true;
return false; return false;
} }
@ -411,6 +416,9 @@ namespace Decompiler
else else
modifiers |= Modifiers.Override; modifiers |= Modifiers.Override;
} }
if (!methodDef.HasBody && !methodDef.IsAbstract)
modifiers |= Modifiers.Extern;
return modifiers; return modifiers;
} }
#endregion #endregion
@ -419,6 +427,7 @@ namespace Decompiler
{ {
// Add fields // Add fields
foreach(FieldDefinition fieldDef in typeDef.Fields) { foreach(FieldDefinition fieldDef in typeDef.Fields) {
if (MemberIsHidden(fieldDef)) continue;
astType.AddChild(CreateField(fieldDef), TypeDeclaration.MemberRole); astType.AddChild(CreateField(fieldDef), TypeDeclaration.MemberRole);
} }
@ -489,7 +498,8 @@ namespace Decompiler
ConstructorDeclaration astMethod = new ConstructorDeclaration(); ConstructorDeclaration astMethod = new ConstructorDeclaration();
astMethod.AddAnnotation(methodDef); astMethod.AddAnnotation(methodDef);
astMethod.AddAnnotation(methodMapping); if (methodMapping != null)
astMethod.AddAnnotation(methodMapping);
astMethod.Modifiers = ConvertModifiers(methodDef); astMethod.Modifiers = ConvertModifiers(methodDef);
if (methodDef.IsStatic) { if (methodDef.IsStatic) {
// don't show visibility for static ctors // don't show visibility for static ctors
@ -515,7 +525,9 @@ namespace Decompiler
astProp.Getter = new Accessor { astProp.Getter = new Accessor {
Body = AstMethodBodyBuilder.CreateMethodBody(propDef.GetMethod, context) Body = AstMethodBodyBuilder.CreateMethodBody(propDef.GetMethod, context)
}.WithAnnotation(propDef.GetMethod); }.WithAnnotation(propDef.GetMethod);
astProp.Getter.AddAnnotation(methodMapping);
if (methodMapping != null)
astProp.Getter.AddAnnotation(methodMapping);
} }
if (propDef.SetMethod != null) { if (propDef.SetMethod != null) {
// Create mapping - used in debugger // Create mapping - used in debugger
@ -524,7 +536,9 @@ namespace Decompiler
astProp.Setter = new Accessor { astProp.Setter = new Accessor {
Body = AstMethodBodyBuilder.CreateMethodBody(propDef.SetMethod, context) Body = AstMethodBodyBuilder.CreateMethodBody(propDef.SetMethod, context)
}.WithAnnotation(propDef.SetMethod); }.WithAnnotation(propDef.SetMethod);
astProp.Setter.AddAnnotation(methodMapping);
if (methodMapping != null)
astProp.Setter.AddAnnotation(methodMapping);
} }
return astProp; return astProp;
} }
@ -543,7 +557,9 @@ namespace Decompiler
astEvent.AddAccessor = new Accessor { astEvent.AddAccessor = new Accessor {
Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.AddMethod, context) Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.AddMethod, context)
}.WithAnnotation(eventDef.AddMethod); }.WithAnnotation(eventDef.AddMethod);
astEvent.AddAccessor.AddAnnotation(methodMapping);
if (methodMapping != null)
astEvent.AddAccessor.AddAnnotation(methodMapping);
} }
if (eventDef.RemoveMethod != null) { if (eventDef.RemoveMethod != null) {
// Create mapping - used in debugger // Create mapping - used in debugger
@ -552,7 +568,9 @@ namespace Decompiler
astEvent.RemoveAccessor = new Accessor { astEvent.RemoveAccessor = new Accessor {
Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.RemoveMethod, context) Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.RemoveMethod, context)
}.WithAnnotation(eventDef.RemoveMethod); }.WithAnnotation(eventDef.RemoveMethod);
astEvent.RemoveAccessor.AddAnnotation(methodMapping);
if (methodMapping != null)
astEvent.RemoveAccessor.AddAnnotation(methodMapping);
} }
return astEvent; return astEvent;
} }

187
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -176,12 +176,6 @@ namespace Decompiler
return args; return args;
} }
AstNode TransformExpression(ILExpression expr)
{
List<Ast.Expression> args = TransformExpressionArguments(expr);
return TransformByteCode(expr, args);
}
Ast.Expression MakeBranchCondition(ILExpression expr) Ast.Expression MakeBranchCondition(ILExpression expr)
{ {
switch(expr.Code) { switch(expr.Code) {
@ -204,22 +198,11 @@ namespace Decompiler
List<Ast.Expression> args = TransformExpressionArguments(expr); List<Ast.Expression> args = TransformExpressionArguments(expr);
Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; Ast.Expression arg1 = args.Count >= 1 ? args[0] : null;
Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; Ast.Expression arg2 = args.Count >= 2 ? args[1] : null;
TypeReference arg1Type = args.Count >= 1 ? expr.Arguments[0].InferredType : null;
switch((Code)expr.Code) { switch((Code)expr.Code) {
case Code.Brfalse: case Code.Brfalse:
if (arg1Type == typeSystem.Boolean) return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1);
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());
case Code.Brtrue: case Code.Brtrue:
if (arg1Type == typeSystem.Boolean) return arg1;
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());
case Code.Beq: case Code.Beq:
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2);
case Code.Bge: case Code.Bge:
@ -245,21 +228,6 @@ namespace Decompiler
} }
} }
AstNode TransformByteCode(ILExpression byteCode, List<Ast.Expression> 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) static string FormatByteCodeOperand(object operand)
{ {
if (operand == null) { if (operand == null) {
@ -285,7 +253,18 @@ namespace Decompiler
} }
} }
AstNode TransformByteCode_Internal(ILExpression byteCode, List<Ast.Expression> args) AstNode TransformExpression(ILExpression expr)
{
List<Ast.Expression> 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<Ast.Expression> args)
{ {
ILCode opCode = byteCode.Code; ILCode opCode = byteCode.Code;
object operand = byteCode.Operand; 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); case Code.Bne_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2), branchCommand);
#endregion #endregion
#region Comparison #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: 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: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2);
case Code.Clt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); case Code.Clt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2);
#endregion #endregion
#region Conversions #region Conversions
case Code.Conv_I: return arg1.CastTo(typeof(int)); // TODO case Code.Conv_I1:
case Code.Conv_I1: return arg1.CastTo(typeof(SByte)); case Code.Conv_I2:
case Code.Conv_I2: return arg1.CastTo(typeof(Int16)); case Code.Conv_I4:
case Code.Conv_I4: return arg1.CastTo(typeof(Int32)); case Code.Conv_I8:
case Code.Conv_I8: return arg1.CastTo(typeof(Int64)); case Code.Conv_U1:
case Code.Conv_U: return arg1.CastTo(typeof(uint)); // TODO case Code.Conv_U2:
case Code.Conv_U1: return arg1.CastTo(typeof(Byte)); case Code.Conv_U4:
case Code.Conv_U2: return arg1.CastTo(typeof(UInt16)); case Code.Conv_U8:
case Code.Conv_U4: return arg1.CastTo(typeof(UInt32)); return arg1; // conversion is handled by Convert() function using the info from type analysis
case Code.Conv_U8: return arg1.CastTo(typeof(UInt64)); 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_R4: return arg1.CastTo(typeof(float));
case Code.Conv_R8: return arg1.CastTo(typeof(double)); case Code.Conv_R8: return arg1.CastTo(typeof(double));
case Code.Conv_R_Un: return arg1.CastTo(typeof(double)); // TODO 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:
case Code.Conv_Ovf_I1: return arg1.CastTo(typeof(SByte)); case Code.Conv_Ovf_I2:
case Code.Conv_Ovf_I2: return arg1.CastTo(typeof(Int16)); case Code.Conv_Ovf_I4:
case Code.Conv_Ovf_I4: return arg1.CastTo(typeof(Int32)); case Code.Conv_Ovf_I8:
case Code.Conv_Ovf_I8: return arg1.CastTo(typeof(Int64)); case Code.Conv_Ovf_U1:
case Code.Conv_Ovf_U: return arg1.CastTo(typeof(uint)); case Code.Conv_Ovf_U2:
case Code.Conv_Ovf_U1: return arg1.CastTo(typeof(Byte)); case Code.Conv_Ovf_U4:
case Code.Conv_Ovf_U2: return arg1.CastTo(typeof(UInt16)); case Code.Conv_Ovf_U8:
case Code.Conv_Ovf_U4: return arg1.CastTo(typeof(UInt32)); case Code.Conv_Ovf_I1_Un:
case Code.Conv_Ovf_U8: return arg1.CastTo(typeof(UInt64)); 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.Castclass:
case Code.Unbox_Any: case Code.Unbox_Any:
return arg1.CastTo(operandAsTypeRef); return arg1.CastTo(operandAsTypeRef);
@ -504,20 +493,7 @@ namespace Decompiler
return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand)); return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand));
} }
case Code.Ldc_I4: case Code.Ldc_I4:
if (byteCode.InferredType == typeSystem.Boolean && (int)operand == 0) return PrimitiveExpression((int)operand, byteCode.InferredType);
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);
case Code.Ldc_I8: case Code.Ldc_I8:
case Code.Ldc_R4: case Code.Ldc_R4:
case Code.Ldc_R8: case Code.Ldc_R8:
@ -580,7 +556,6 @@ namespace Decompiler
case Code.Refanyval: return InlineAssembly(byteCode, args); case Code.Refanyval: return InlineAssembly(byteCode, args);
case Code.Ret: { case Code.Ret: {
if (methodDef.ReturnType.FullName != "System.Void") { if (methodDef.ReturnType.FullName != "System.Void") {
arg1 = Convert(arg1, methodDef.ReturnType);
return new Ast.ReturnStatement { Expression = arg1 }; return new Ast.ReturnStatement { Expression = arg1 };
} else { } else {
return new Ast.ReturnStatement(); return new Ast.ReturnStatement();
@ -719,19 +694,57 @@ namespace Decompiler
return new DirectionExpression { Expression = expr, FieldDirection = FieldDirection.Ref }; 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; return expr;
} else { } 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; return expr;
} }
} }
static Ast.Expression ConvertIntToBool(Ast.Expression astInt) Expression PrimitiveExpression(long val, TypeReference type)
{ {
return astInt; if (type == typeSystem.Boolean && val == 0)
// return new Ast.ParenthesizedExpression(new Ast.BinaryOperatorExpression(astInt, BinaryOperatorType.InEquality, new Ast.PrimitiveExpression(0, "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));
} }
} }
} }

7
ICSharpCode.Decompiler/Ast/CommentStatement.cs

@ -4,6 +4,7 @@
using System; using System;
using System.Linq; using System.Linq;
using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.PatternMatching;
namespace Decompiler namespace Decompiler
{ {
@ -34,5 +35,11 @@ namespace Decompiler
cs.Remove(); cs.Remove();
} }
} }
protected override bool DoMatch(AstNode other, Match match)
{
CommentStatement o = other as CommentStatement;
return o != null && MatchString(comment, o.comment);
}
} }
} }

10
ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs

@ -26,17 +26,21 @@ namespace Decompiler
/// <param name="type">The type of the new variable</param> /// <param name="type">The type of the new variable</param>
/// <param name="name">The name of the new variable</param> /// <param name="name">The name of the new variable</param>
/// <param name="allowPassIntoLoops">Whether the variable is allowed to be placed inside a loop</param> /// <param name="allowPassIntoLoops">Whether the variable is allowed to be placed inside a loop</param>
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); AstNode pos = FindInsertPos(node, name, allowPassIntoLoops);
if (pos != null) { if (pos != null) {
Match m = assignmentPattern.Match(pos); Match m = assignmentPattern.Match(pos);
if (m != null && m.Get<IdentifierExpression>("ident").Single().Identifier == name) { if (m != null && m.Get<IdentifierExpression>("ident").Single().Identifier == name) {
pos.ReplaceWith(new VariableDeclarationStatement(type, name, m.Get<Expression>("init").Single().Detach())); result = new VariableDeclarationStatement(type, name, m.Get<Expression>("init").Single().Detach());
pos.ReplaceWith(result);
} else { } 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) static AstNode FindInsertPos(AstNode node, string name, bool allowPassIntoLoops)

8
ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs

@ -32,6 +32,10 @@ namespace Decompiler.Transforms
} }
} }
internal sealed class CapturedVariableAnnotation
{
}
public DelegateConstruction(DecompilerContext context) : base(context) 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): // Now insert the variable declarations (we can do this after the replacements only so that the scope detection works):
foreach (var tuple in variablesToDeclare) { 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; return null;

122
ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs

@ -19,26 +19,35 @@ namespace Decompiler.Transforms
{ {
TransformUsings(compilationUnit); TransformUsings(compilationUnit);
TransformForeach(compilationUnit); TransformForeach(compilationUnit);
TransformFor(compilationUnit);
} }
#region using /// <summary>
static readonly AstNode usingVarDeclPattern = new VariableDeclarationStatement { /// $type $variable = $initializer;
/// </summary>
static readonly AstNode variableDeclPattern = new VariableDeclarationStatement {
Type = new AnyNode("type").ToType(), Type = new AnyNode("type").ToType(),
Variables = { Variables = {
new NamedNode( new NamedNode(
"variable", "variable",
new VariableInitializer { new VariableInitializer {
Initializer = new AnyNode().ToExpression() Initializer = new AnyNode("initializer").ToExpression()
} }
).ToVariable() ).ToVariable()
} }
}; };
/// <summary>
/// Variable declaration without initializer.
/// </summary>
static readonly AstNode simpleVariableDefinition = new VariableDeclarationStatement { static readonly AstNode simpleVariableDefinition = new VariableDeclarationStatement {
Type = new AnyNode().ToType(), Type = new AnyNode().ToType(),
Variables = { Variables = {
new VariableInitializer() // any name but no initializer new VariableInitializer() // any name but no initializer
} }
}; };
#region using
static readonly AstNode usingTryCatchPattern = new TryCatchStatement { static readonly AstNode usingTryCatchPattern = new TryCatchStatement {
TryBlock = new AnyNode("body").ToBlock(), TryBlock = new AnyNode("body").ToBlock(),
FinallyBlock = new BlockStatement { FinallyBlock = new BlockStatement {
@ -65,7 +74,7 @@ namespace Decompiler.Transforms
public void TransformUsings(AstNode compilationUnit) public void TransformUsings(AstNode compilationUnit)
{ {
foreach (AstNode node in compilationUnit.Descendants.ToArray()) { foreach (AstNode node in compilationUnit.Descendants.ToArray()) {
Match m1 = usingVarDeclPattern.Match(node); Match m1 = variableDeclPattern.Match(node);
if (m1 == null) continue; if (m1 == null) continue;
AstNode tryCatch = node.NextSibling; AstNode tryCatch = node.NextSibling;
while (simpleVariableDefinition.Match(tryCatch) != null) while (simpleVariableDefinition.Match(tryCatch) != null)
@ -103,25 +112,23 @@ namespace Decompiler.Transforms
).ToVariable() ).ToVariable()
} }
}, },
EmbeddedStatement = new BlockStatement { EmbeddedStatement = new Choice {
new ForStatement { // There are two forms of the foreach statement:
EmbeddedStatement = new BlockStatement { // one where the item variable is declared inside the loop,
new IfElseStatement { // and one where it is declared outside of the loop.
Condition = new UnaryOperatorExpression( // In the former case, we can apply the foreach pattern only if the variable wasn't captured.
UnaryOperatorType.Not, { "itemVariableInsideLoop",
new NamedNode("enumeratorIdent", new IdentifierExpression()).ToExpression().Invoke("MoveNext") new BlockStatement {
), new WhileStatement {
TrueStatement = new BlockStatement { Condition = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Invoke("MoveNext"),
new BreakStatement() EmbeddedStatement = new BlockStatement {
},
FalseStatement = new BlockStatement {
new VariableDeclarationStatement { new VariableDeclarationStatement {
Type = new AnyNode("itemType").ToType(), Type = new AnyNode("itemType").ToType(),
Variables = { Variables = {
new NamedNode( new NamedNode(
"itemVariable", "itemVariable",
new VariableInitializer { new VariableInitializer {
Initializer = new Backreference("enumeratorIdent").ToExpression().Member("Current") Initializer = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Member("Current")
} }
).ToVariable() ).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) public void TransformForeach(AstNode compilationUnit)
@ -141,21 +169,73 @@ namespace Decompiler.Transforms
if (m == null) if (m == null)
continue; continue;
VariableInitializer enumeratorVar = m.Get<VariableInitializer>("enumeratorVariable").Single(); VariableInitializer enumeratorVar = m.Get<VariableInitializer>("enumeratorVariable").Single();
if (enumeratorVar.Name != m.Get<IdentifierExpression>("enumeratorIdent").Single().Identifier)
continue;
VariableInitializer itemVar = m.Get<VariableInitializer>("itemVariable").Single(); VariableInitializer itemVar = m.Get<VariableInitializer>("itemVariable").Single();
if (m.Has("itemVariableInsideLoop") && itemVar.Annotation<DelegateConstruction.CapturedVariableAnnotation>() != null) {
// cannot move captured variables out of loops
continue;
}
BlockStatement newBody = new BlockStatement(); BlockStatement newBody = new BlockStatement();
foreach (Statement stmt in m.Get<Statement>("statement")) foreach (Statement stmt in m.Get<Statement>("statement"))
newBody.Add(stmt.Detach()); newBody.Add(stmt.Detach());
node.ReplaceWith( node.ReplaceWith(
new ForeachStatement { new ForeachStatement {
VariableType = m.Get<AstType>("itemType").Single().Detach(), VariableType = m.Get<AstType>("itemType").Single().Detach(),
VariableName = enumeratorVar.Name, VariableName = itemVar.Name,
InExpression = m.Get<Expression>("collection").Single().Detach(), InExpression = m.Get<Expression>("collection").Single().Detach(),
EmbeddedStatement = newBody EmbeddedStatement = newBody
}); });
} }
} }
#endregion #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<VariableInitializer>("variable").Single().Name != m2.Get<IdentifierExpression>("ident").Single().Identifier)
continue;
WhileStatement loop = (WhileStatement)next;
node.Remove();
BlockStatement newBody = new BlockStatement();
foreach (Statement stmt in m2.Get<Statement>("statement"))
newBody.Add(stmt.Detach());
loop.ReplaceWith(
new ForStatement {
Initializers = { (VariableDeclarationStatement)node },
Condition = loop.Condition.Detach(),
Iterators = { m2.Get<Statement>("increment").Single().Detach() },
EmbeddedStatement = newBody
});
}
}
#endregion
} }
} }

10
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -203,6 +203,7 @@ namespace Decompiler
// Mapping to the original instructions (useful for debugging) // Mapping to the original instructions (useful for debugging)
public List<ILRange> ILRanges { get; set; } public List<ILRange> ILRanges { get; set; }
public TypeReference ExpectedType { get; set; }
public TypeReference InferredType { get; set; } public TypeReference InferredType { get; set; }
public ILExpression(ILCode code, object operand, params ILExpression[] args) public ILExpression(ILCode code, object operand, params ILExpression[] args)
@ -296,6 +297,15 @@ namespace Decompiler
if (this.InferredType != null) { if (this.InferredType != null) {
output.Write(':'); output.Write(':');
this.InferredType.WriteTo(output, true, true); 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('('); output.Write('(');
bool first = true; bool first = true;

48
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -55,7 +55,7 @@ namespace Decompiler
} }
bool anyArgumentIsMissingType = expr.Arguments.Any(a => a.InferredType == null); bool anyArgumentIsMissingType = expr.Arguments.Any(a => a.InferredType == null);
if (expr.InferredType == null || anyArgumentIsMissingType) 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()) { foreach (ILNode child in node.GetChildren()) {
InferTypes(child); InferTypes(child);
@ -97,6 +97,7 @@ namespace Decompiler
/// <returns>The inferred type</returns> /// <returns>The inferred type</returns>
TypeReference InferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false) TypeReference InferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false)
{ {
expr.ExpectedType = expectedType;
if (forceInferChildren || expr.InferredType == null) if (forceInferChildren || expr.InferredType == null)
expr.InferredType = DoInferTypeForExpression(expr, expectedType, forceInferChildren); expr.InferredType = DoInferTypeForExpression(expr, expectedType, forceInferChildren);
return expr.InferredType; return expr.InferredType;
@ -552,13 +553,16 @@ namespace Decompiler
TypeReference leftPreferred = DoInferTypeForExpression(left, null); TypeReference leftPreferred = DoInferTypeForExpression(left, null);
TypeReference rightPreferred = DoInferTypeForExpression(right, null); TypeReference rightPreferred = DoInferTypeForExpression(right, null);
if (leftPreferred == rightPreferred) { 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)) { } 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)) { } else if (leftPreferred == DoInferTypeForExpression(right, leftPreferred)) {
return left.InferredType = right.InferredType = leftPreferred; return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred;
} else { } 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 true;
return null; 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;
}
} }
} }

BIN
ICSharpCode.Decompiler/Tests/StackTests/StackTests.exe

Binary file not shown.

132
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
// =============================================================

2
ICSharpCode.Decompiler/Tests/TestRunner.cs

@ -72,7 +72,7 @@ namespace ICSharpCode.Decompiler.Tests
} }
while ((line2 = r2.ReadLine()) != null) { while ((line2 = r2.ReadLine()) != null) {
ok = false; ok = false;
diff.WriteLine("+" + line1); diff.WriteLine("+" + line2);
} }
return ok; return ok;
} }

2
ILSpy/MainWindow.xaml.cs

@ -26,6 +26,8 @@ using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.TreeView; using ICSharpCode.TreeView;
using ILSpy.Debugger.AvalonEdit; using ILSpy.Debugger.AvalonEdit;

55
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs

@ -4,6 +4,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using ICSharpCode.NRefactory.CSharp.PatternMatching; using ICSharpCode.NRefactory.CSharp.PatternMatching;
@ -156,29 +157,45 @@ namespace ICSharpCode.NRefactory.CSharp
internal bool DoMatch(AstNodeCollection<T> other, Match match) internal bool DoMatch(AstNodeCollection<T> other, Match match)
{ {
AstNode cur1 = this.node.FirstChild; Stack<AstNode> patternStack = new Stack<AstNode>();
AstNode cur2 = other.node.FirstChild; Stack<Pattern.PossibleMatch> stack = new Stack<Pattern.PossibleMatch>();
while (true) { patternStack.Push(this.node.FirstChild);
while (cur1 != null && cur1.Role != role) 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; cur1 = cur1.NextSibling;
if (cur2 != null)
cur2 = cur2.NextSibling;
}
while (cur2 != null && cur2.Role != role) while (cur2 != null && cur2.Role != role)
cur2 = cur2.NextSibling; cur2 = cur2.NextSibling;
if (cur1 == null || cur2 == null) if (success && cur2 == null)
break; return true;
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;
} }
return cur1 == null && cur2 == null; return false;
} }
} }
} }

6
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) protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{ {
AssignmentExpression o = other as AssignmentExpression; 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) public static string GetOperatorSymbol(AssignmentOperatorType op)
@ -136,5 +137,8 @@ namespace ICSharpCode.NRefactory.CSharp
BitwiseOr, BitwiseOr,
/// <summary>left ^= right</summary> /// <summary>left ^= right</summary>
ExclusiveOr, ExclusiveOr,
/// <summary>Any operator (for pattern matching)</summary>
Any
} }
} }

10
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/BinaryOperatorExpression.cs

@ -1,6 +1,6 @@
// //
// BinaryOperatorExpression.cs // BinaryOperatorExpression.cs
// //
// Author: // Author:
// Mike Krüger <mkrueger@novell.com> // Mike Krüger <mkrueger@novell.com>
// //
@ -75,7 +75,8 @@ namespace ICSharpCode.NRefactory.CSharp
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{ {
BinaryOperatorExpression o = other as BinaryOperatorExpression; 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) public static string GetOperatorSymbol(BinaryOperatorType op)
@ -175,6 +176,9 @@ namespace ICSharpCode.NRefactory.CSharp
/// <summary>left ?? right</summary> /// <summary>left ?? right</summary>
NullCoalescing, NullCoalescing,
None /// <summary>
/// Any binary operator (used in pattern matching)
/// </summary>
Any
} }
} }

9
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/Expression.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
namespace ICSharpCode.NRefactory.CSharp namespace ICSharpCode.NRefactory.CSharp
@ -50,6 +51,14 @@ namespace ICSharpCode.NRefactory.CSharp
return (Expression)base.Clone(); 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<Expression, Expression> replaceFunction) public Expression ReplaceWith(Func<Expression, Expression> replaceFunction)
{ {
if (replaceFunction == null) if (replaceFunction == null)

4
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PrimitiveExpression.cs

@ -31,6 +31,8 @@ namespace ICSharpCode.NRefactory.CSharp
/// </summary> /// </summary>
public class PrimitiveExpression : Expression public class PrimitiveExpression : Expression
{ {
public static readonly object AnyValue = new object();
AstLocation startLocation; AstLocation startLocation;
public override AstLocation StartLocation { public override AstLocation StartLocation {
get { get {
@ -70,7 +72,7 @@ namespace ICSharpCode.NRefactory.CSharp
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{ {
PrimitiveExpression o = other as PrimitiveExpression; 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));
} }
} }
} }

3
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/NodeType.cs

@ -44,7 +44,8 @@ namespace ICSharpCode.NRefactory.CSharp
Expression, Expression,
Token, Token,
QueryClause, QueryClause,
Pattern Pattern,
Placeholder
} }
} }

9
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/AnyNode.cs

@ -12,6 +12,10 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
{ {
readonly string groupName; readonly string groupName;
public string GroupName {
get { return groupName; }
}
public AnyNode(string groupName = null) public AnyNode(string groupName = null)
{ {
this.groupName = groupName; this.groupName = groupName;
@ -22,5 +26,10 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
match.Add(this.groupName, other); match.Add(this.groupName, other);
return other != null && !other.IsNull; return other != null && !other.IsNull;
} }
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return ((IPatternAstVisitor<T, S>)visitor).VisitAnyNode(this, data);
}
} }
} }

42
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Backreference.cs

@ -13,6 +13,10 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
{ {
readonly string referencedGroupName; readonly string referencedGroupName;
public string ReferencedGroupName {
get { return referencedGroupName; }
}
public Backreference(string referencedGroupName) public Backreference(string referencedGroupName)
{ {
if (referencedGroupName == null) if (referencedGroupName == null)
@ -24,5 +28,43 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
{ {
return match.Get(referencedGroupName).Last().Match(other) != null; return match.Get(referencedGroupName).Last().Match(other) != null;
} }
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return ((IPatternAstVisitor<T, S>)visitor).VisitBackreference(this, data);
}
}
/// <summary>
/// Matches identifier expressions that have the same identifier as the referenced variable/type definition/method definition.
/// </summary>
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<T, S>(IAstVisitor<T, S> visitor, T data)
{
return ((IPatternAstVisitor<T, S>)visitor).VisitIdentifierExpressionBackreference(this, data);
}
} }
} }

13
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Choice.cs

@ -3,13 +3,14 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
namespace ICSharpCode.NRefactory.CSharp.PatternMatching namespace ICSharpCode.NRefactory.CSharp.PatternMatching
{ {
/// <summary> /// <summary>
/// Matches one of several alternatives. /// Matches one of several alternatives.
/// </summary> /// </summary>
public class Choice : Pattern, IEnumerable public class Choice : Pattern, IEnumerable<AstNode>
{ {
public static readonly Role<AstNode> AlternativeRole = new Role<AstNode>("Alternative", AstNode.Null); public static readonly Role<AstNode> AlternativeRole = new Role<AstNode>("Alternative", AstNode.Null);
@ -35,9 +36,19 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
return false; return false;
} }
IEnumerator<AstNode> IEnumerable<AstNode>.GetEnumerator()
{
return GetChildrenByRole(AlternativeRole).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
return GetChildrenByRole(AlternativeRole).GetEnumerator(); return GetChildrenByRole(AlternativeRole).GetEnumerator();
} }
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return ((IPatternAstVisitor<T, S>)visitor).VisitChoice(this, data);
}
} }
} }

22
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
{
/// <summary>
/// Extended AST visitor that works for patterns.
/// </summary>
public interface IPatternAstVisitor<in T, out S> : IAstVisitor<T, S>
{
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);
}
}

9
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/NamedGroup.cs → NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/NamedNode.cs

@ -14,6 +14,10 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
readonly string groupName; readonly string groupName;
public string GroupName {
get { return groupName; }
}
public NamedNode(string groupName, AstNode childNode) public NamedNode(string groupName, AstNode childNode)
{ {
this.groupName = groupName; this.groupName = groupName;
@ -25,5 +29,10 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
match.Add(this.groupName, other); match.Add(this.groupName, other);
return GetChildByRole(ElementRole).DoMatch(other, match); return GetChildByRole(ElementRole).DoMatch(other, match);
} }
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return ((IPatternAstVisitor<T, S>)visitor).VisitNamedNode(this, data);
}
} }
} }

28
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) // This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace ICSharpCode.NRefactory.CSharp.PatternMatching namespace ICSharpCode.NRefactory.CSharp.PatternMatching
{ {
@ -14,16 +17,21 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
get { return NodeType.Pattern; } get { return NodeType.Pattern; }
} }
protected internal virtual bool DoMatchCollection(Role role, ref AstNode other, Match match) internal struct PossibleMatch
{ {
bool result = DoMatch(other, match); public readonly AstNode NextOther; // next node after the last matched node
other = other.NextSibling; public readonly int Checkpoint; // checkpoint
return result;
public PossibleMatch(AstNode nextOther, int checkpoint)
{
this.NextOther = nextOther;
this.Checkpoint = checkpoint;
}
} }
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data) internal virtual bool DoMatchCollection(Role role, AstNode pos, Match match, Stack<PossibleMatch> backtrackingStack)
{ {
return default(S); return DoMatch(pos, match);
} }
public AstType ToType() public AstType ToType()
@ -50,5 +58,13 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
{ {
return new VariablePlaceholder(this); 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();
}
} }
} }

20
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs

@ -15,12 +15,12 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
} }
public override NodeType NodeType { public override NodeType NodeType {
get { return GetChildByRole(TypePlaceholder.ChildRole).NodeType; } get { return NodeType.Placeholder; }
} }
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data) public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{ {
return GetChildByRole(TypePlaceholder.ChildRole).AcceptVisitor(visitor, data); return ((IPatternAstVisitor<T, S>)visitor).VisitPlaceholder(this, GetChildByRole(TypePlaceholder.ChildRole), data);
} }
protected internal override bool DoMatch(AstNode other, Match match) protected internal override bool DoMatch(AstNode other, Match match)
@ -37,12 +37,12 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
} }
public override NodeType NodeType { public override NodeType NodeType {
get { return GetChildByRole(TypePlaceholder.ChildRole).NodeType; } get { return NodeType.Placeholder; }
} }
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data) public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{ {
return GetChildByRole(TypePlaceholder.ChildRole).AcceptVisitor(visitor, data); return ((IPatternAstVisitor<T, S>)visitor).VisitPlaceholder(this, GetChildByRole(TypePlaceholder.ChildRole), data);
} }
protected internal override bool DoMatch(AstNode other, Match match) protected internal override bool DoMatch(AstNode other, Match match)
@ -59,12 +59,12 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
} }
public override NodeType NodeType { public override NodeType NodeType {
get { return GetChildByRole(TypePlaceholder.ChildRole).NodeType; } get { return NodeType.Placeholder; }
} }
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data) public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{ {
return GetChildByRole(TypePlaceholder.ChildRole).AcceptVisitor(visitor, data); return ((IPatternAstVisitor<T, S>)visitor).VisitPlaceholder(this, GetChildByRole(TypePlaceholder.ChildRole), data);
} }
protected internal override bool DoMatch(AstNode other, Match match) protected internal override bool DoMatch(AstNode other, Match match)
@ -81,12 +81,12 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
} }
public override NodeType NodeType { public override NodeType NodeType {
get { return GetChildByRole(TypePlaceholder.ChildRole).NodeType; } get { return NodeType.Placeholder; }
} }
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data) public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{ {
return GetChildByRole(TypePlaceholder.ChildRole).AcceptVisitor(visitor, data); return ((IPatternAstVisitor<T, S>)visitor).VisitPlaceholder(this, GetChildByRole(TypePlaceholder.ChildRole), data);
} }
protected internal override bool DoMatch(AstNode other, Match match) protected internal override bool DoMatch(AstNode other, Match match)
@ -103,12 +103,12 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
} }
public override NodeType NodeType { public override NodeType NodeType {
get { return GetChildByRole(TypePlaceholder.ChildRole).NodeType; } get { return NodeType.Placeholder; }
} }
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data) public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{ {
return GetChildByRole(TypePlaceholder.ChildRole).AcceptVisitor(visitor, data); return ((IPatternAstVisitor<T, S>)visitor).VisitPlaceholder(this, GetChildByRole(TypePlaceholder.ChildRole), data);
} }
protected internal override bool DoMatch(AstNode other, Match match) protected internal override bool DoMatch(AstNode other, Match match)

26
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) // This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
namespace ICSharpCode.NRefactory.CSharp.PatternMatching namespace ICSharpCode.NRefactory.CSharp.PatternMatching
@ -20,24 +21,22 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
AddChild(childNode, ElementRole); 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<Pattern.PossibleMatch> backtrackingStack)
{ {
Debug.Assert(other != null && other.Role == role); Debug.Assert(pos == null || pos.Role == role);
int matchCount = 0; int matchCount = 0;
var lastValidCheckpoint = match.CheckPoint(); if (this.MinCount <= 0)
backtrackingStack.Push(new PossibleMatch(pos, match.CheckPoint()));
AstNode element = GetChildByRole(ElementRole); AstNode element = GetChildByRole(ElementRole);
AstNode pos = other; while (matchCount < this.MaxCount && pos != null && element.DoMatch(pos, match)) {
while (pos != null && element.DoMatch(pos, match)) {
matchCount++; matchCount++;
lastValidCheckpoint = match.CheckPoint();
do { do {
pos = pos.NextSibling; pos = pos.NextSibling;
} while (pos != null && pos.Role != role); } while (pos != null && pos.Role != role);
// set 'other' (=pointer in collection) to the next node after the valid match if (matchCount >= this.MinCount)
other = pos; backtrackingStack.Push(new PossibleMatch(pos, match.CheckPoint()));
} }
match.RestoreCheckPoint(lastValidCheckpoint); // restote old checkpoint after failed match return false; // never do a normal (single-element) match; always make the caller look at the results on the back-tracking stack.
return matchCount >= MinCount && matchCount <= MaxCount;
} }
protected internal override bool DoMatch(AstNode other, Match match) protected internal override bool DoMatch(AstNode other, Match match)
@ -45,7 +44,12 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
if (other == null || other.IsNull) if (other == null || other.IsNull)
return this.MinCount <= 0; return this.MinCount <= 0;
else else
return GetChildByRole(ElementRole).DoMatch(other, match); return this.MaxCount >= 1 && GetChildByRole(ElementRole).DoMatch(other, match);
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return ((IPatternAstVisitor<T, S>)visitor).VisitRepeat(this, data);
} }
} }
} }

8
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/CheckedStatement.cs

@ -1,4 +1,4 @@
// //
// CheckedStatement.cs // CheckedStatement.cs
// //
// Author: // Author:
@ -44,5 +44,11 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitCheckedStatement (this, data); 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);
}
} }
} }

8
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ContinueStatement.cs

@ -1,4 +1,4 @@
// //
// ContinueStatement.cs // ContinueStatement.cs
// //
// Author: // Author:
@ -39,5 +39,11 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitContinueStatement (this, data); return visitor.VisitContinueStatement (this, data);
} }
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
ContinueStatement o = other as ContinueStatement;
return o != null;
}
} }
} }

8
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/DoWhileStatement.cs

@ -1,4 +1,4 @@
// //
// DoWhileStatement.cs // DoWhileStatement.cs
// //
// Author: // Author:
@ -68,6 +68,12 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitDoWhileStatement (this, data); 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);
}
} }
} }

8
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/EmptyStatement.cs

@ -1,4 +1,4 @@
// //
// EmptyStatement.cs // EmptyStatement.cs
// //
// Author: // Author:
@ -52,5 +52,11 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitEmptyStatement (this, data); return visitor.VisitEmptyStatement (this, data);
} }
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
EmptyStatement o = other as EmptyStatement;
return o != null;
}
} }
} }

6
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/FixedStatement.cs

@ -63,5 +63,11 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitFixedStatement (this, data); 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);
}
} }
} }

9
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ForeachStatement.cs

@ -1,4 +1,4 @@
// //
// ForeachStatement.cs // ForeachStatement.cs
// //
// Author: // Author:
@ -75,5 +75,12 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitForeachStatement (this, data); 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);
}
} }
} }

38
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/GotoStatement.cs

@ -56,14 +56,6 @@ namespace ICSharpCode.NRefactory.CSharp
} }
} }
/// <summary>
/// Used for "goto case LabelExpression;"
/// </summary>
public Expression LabelExpression {
get { return GetChildByRole (Roles.Expression); }
set { SetChildByRole (Roles.Expression, value); }
}
public CSharpTokenNode SemicolonToken { public CSharpTokenNode SemicolonToken {
get { return GetChildByRole (Roles.Semicolon); } get { return GetChildByRole (Roles.Semicolon); }
} }
@ -72,6 +64,12 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitGotoStatement (this, data); 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);
}
} }
/// <summary> /// <summary>
@ -89,18 +87,6 @@ namespace ICSharpCode.NRefactory.CSharp
get { return GetChildByRole (CaseKeywordRole); } 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));
}
}
/// <summary> /// <summary>
/// Used for "goto case LabelExpression;" /// Used for "goto case LabelExpression;"
/// </summary> /// </summary>
@ -117,6 +103,12 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitGotoCaseStatement (this, data); 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);
}
} }
/// <summary> /// <summary>
@ -142,5 +134,11 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitGotoDefaultStatement (this, data); return visitor.VisitGotoDefaultStatement (this, data);
} }
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
GotoDefaultStatement o = other as GotoDefaultStatement;
return o != null;
}
} }
} }

12
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/LabelStatement.cs

@ -1,4 +1,4 @@
// //
// LabelStatement.cs // LabelStatement.cs
// //
// Author: // Author:
@ -40,9 +40,19 @@ namespace ICSharpCode.NRefactory.CSharp
} }
} }
public CSharpTokenNode Colon {
get { return GetChildByRole (Roles.Colon); }
}
public override S AcceptVisitor<T, S> (IAstVisitor<T, S> visitor, T data) public override S AcceptVisitor<T, S> (IAstVisitor<T, S> visitor, T data)
{ {
return visitor.VisitLabelStatement (this, 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);
}
} }
} }

8
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/LockStatement.cs

@ -1,4 +1,4 @@
// //
// LockStatement.cs // LockStatement.cs
// //
// Author: // Author:
@ -57,5 +57,11 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitLockStatement (this, data); 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);
}
} }
} }

8
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ReturnStatement.cs

@ -1,4 +1,4 @@
// //
// ReturnStatement.cs // ReturnStatement.cs
// //
// Author: // Author:
@ -48,5 +48,11 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitReturnStatement (this, data); 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);
}
} }
} }

5
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/Statement.cs

@ -52,10 +52,5 @@ namespace ICSharpCode.NRefactory.CSharp
public override NodeType NodeType { public override NodeType NodeType {
get { return NodeType.Statement; } get { return NodeType.Statement; }
} }
protected internal override bool DoMatch(AstNode other, ICSharpCode.NRefactory.CSharp.PatternMatching.Match match)
{
throw new NotImplementedException();
}
} }
} }

6
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/SwitchStatement.cs

@ -69,6 +69,12 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitSwitchStatement (this, data); 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 public class SwitchSection : AstNode

8
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ThrowStatement.cs

@ -1,4 +1,4 @@
// //
// ThrowStatement.cs // ThrowStatement.cs
// //
// Author: // Author:
@ -48,5 +48,11 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitThrowStatement (this, data); 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);
}
} }
} }

8
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/UncheckedStatement.cs

@ -1,4 +1,4 @@
// //
// UncheckedStatement.cs // UncheckedStatement.cs
// //
// Author: // Author:
@ -44,5 +44,11 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitUncheckedStatement (this, data); 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);
}
} }
} }

8
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/UnsafeStatement.cs

@ -1,4 +1,4 @@
// //
// UnsafeStatement.cs // UnsafeStatement.cs
// //
// Author: // Author:
@ -44,5 +44,11 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitUnsafeStatement (this, data); 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);
}
} }
} }

8
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/WhileStatement.cs

@ -1,4 +1,4 @@
// //
// WhileStatement.cs // WhileStatement.cs
// //
// Author: // Author:
@ -59,5 +59,11 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitWhileStatement (this, data); 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);
}
} }
} }

8
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/YieldBreakStatement.cs

@ -1,4 +1,4 @@
// //
// YieldBreakStatement.cs // YieldBreakStatement.cs
// //
// Author: // Author:
@ -50,5 +50,11 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitYieldBreakStatement (this, data); return visitor.VisitYieldBreakStatement (this, data);
} }
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
YieldBreakStatement o = other as YieldBreakStatement;
return o != null;
}
} }
} }

8
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/YieldStatement.cs

@ -1,4 +1,4 @@
// //
// YieldStatement.cs // YieldStatement.cs
// //
// Author: // Author:
@ -55,5 +55,11 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
return visitor.VisitYieldStatement (this, data); 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);
}
} }
} }

90
NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs

@ -8,6 +8,8 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using ICSharpCode.NRefactory.CSharp.PatternMatching;
using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp namespace ICSharpCode.NRefactory.CSharp
@ -15,7 +17,7 @@ namespace ICSharpCode.NRefactory.CSharp
/// <summary> /// <summary>
/// Outputs the AST. /// Outputs the AST.
/// </summary> /// </summary>
public class OutputVisitor : IAstVisitor<object, object> public class OutputVisitor : IPatternAstVisitor<object, object>
{ {
readonly IOutputFormatter formatter; readonly IOutputFormatter formatter;
readonly CSharpFormattingPolicy policy; readonly CSharpFormattingPolicy policy;
@ -1989,5 +1991,91 @@ namespace ICSharpCode.NRefactory.CSharp
return EndNode(identifier); return EndNode(identifier);
} }
#endregion #endregion
#region Pattern Nodes
object IPatternAstVisitor<object, object>.VisitPlaceholder(AstNode placeholder, AstNode child, object data)
{
StartNode(placeholder);
child.AcceptVisitor(this, data);
return EndNode(placeholder);
}
object IPatternAstVisitor<object, object>.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<object, object>.VisitBackreference(Backreference backreference, object data)
{
StartNode(backreference);
WriteKeyword("backreference");
LPar();
WriteIdentifier(backreference.ReferencedGroupName);
RPar();
return EndNode(backreference);
}
object IPatternAstVisitor<object, object>.VisitIdentifierExpressionBackreference(IdentifierExpressionBackreference identifierExpressionBackreference, object data)
{
StartNode(identifierExpressionBackreference);
WriteKeyword("identifierBackreference");
LPar();
WriteIdentifier(identifierExpressionBackreference.ReferencedGroupName);
RPar();
return EndNode(identifierExpressionBackreference);
}
object IPatternAstVisitor<object, object>.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<object, object>.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<object, object>.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
} }
} }

3
NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -98,8 +98,9 @@
<Compile Include="CSharp\Ast\PatternMatching\Choice.cs" /> <Compile Include="CSharp\Ast\PatternMatching\Choice.cs" />
<Compile Include="CSharp\Ast\PatternMatching\AnyNode.cs" /> <Compile Include="CSharp\Ast\PatternMatching\AnyNode.cs" />
<Compile Include="CSharp\Ast\PatternMatching\Backreference.cs" /> <Compile Include="CSharp\Ast\PatternMatching\Backreference.cs" />
<Compile Include="CSharp\Ast\PatternMatching\IPatternAstVisitor.cs" />
<Compile Include="CSharp\Ast\PatternMatching\Match.cs" /> <Compile Include="CSharp\Ast\PatternMatching\Match.cs" />
<Compile Include="CSharp\Ast\PatternMatching\NamedGroup.cs" /> <Compile Include="CSharp\Ast\PatternMatching\NamedNode.cs" />
<Compile Include="CSharp\Ast\PatternMatching\Repeat.cs" /> <Compile Include="CSharp\Ast\PatternMatching\Repeat.cs" />
<Compile Include="CSharp\Ast\PatternMatching\Pattern.cs" /> <Compile Include="CSharp\Ast\PatternMatching\Pattern.cs" />
<Compile Include="CSharp\Ast\PatternMatching\Placeholder.cs" /> <Compile Include="CSharp\Ast\PatternMatching\Placeholder.cs" />

Loading…
Cancel
Save