2
// NRefactoryExpressionEvaluator.cs
4
// Authors: Lluis Sanchez Gual <lluis@novell.com>
5
// Jeffrey Stedfast <jeff@xamarin.com>
7
// Copyright (c) 2008 Novell, Inc (http://www.novell.com)
8
// Copyright (c) 2012 Xamarin Inc. (http://www.xamarin.com)
10
// Permission is hereby granted, free of charge, to any person obtaining a copy
11
// of this software and associated documentation files (the "Software"), to deal
12
// in the Software without restriction, including without limitation the rights
13
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
// copies of the Software, and to permit persons to whom the Software is
15
// furnished to do so, subject to the following conditions:
17
// The above copyright notice and this permission notice shall be included in
18
// all copies or substantial portions of the Software.
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30
using System.Collections.Generic;
32
using Mono.Debugging.Client;
34
using ICSharpCode.NRefactory.CSharp;
36
namespace Mono.Debugging.Evaluation
38
public class NRefactoryExpressionEvaluator : ExpressionEvaluator
40
Dictionary<string,ValueReference> userVariables = new Dictionary<string, ValueReference> ();
42
public override ValueReference Evaluate (EvaluationContext ctx, string expression, object expectedType)
44
expression = expression.TrimStart ();
46
if (expression.Length > 0 && expression[0] == '?')
47
expression = expression.Substring (1).Trim ();
49
if (expression.Length > 3 && expression.StartsWith ("var", StringComparison.Ordinal) && char.IsWhiteSpace (expression[3])) {
50
expression = expression.Substring (4).Trim (' ', '\t');
51
string variable = null;
53
for (int n = 0; n < expression.Length; n++) {
54
if (!char.IsLetterOrDigit (expression[n]) && expression[n] != '_') {
55
variable = expression.Substring (0, n);
56
if (!expression.Substring (n).Trim (' ', '\t').StartsWith ("=", StringComparison.Ordinal))
61
if (n == expression.Length - 1) {
62
variable = expression;
68
if (!string.IsNullOrEmpty (variable))
69
userVariables[variable] = new UserVariableReference (ctx, variable);
71
if (expression == null)
75
expression = ReplaceExceptionTag (expression, ctx.Options.CurrentExceptionTag);
77
var expr = new CSharpParser ().ParseExpression (expression);
79
throw new EvaluatorException ("Could not parse expression '{0}'", expression);
81
var evaluator = new NRefactoryExpressionEvaluatorVisitor (ctx, expression, expectedType, userVariables);
82
return expr.AcceptVisitor<ValueReference> (evaluator);
85
public override string Resolve (DebuggerSession session, SourceLocation location, string exp)
87
return Resolve (session, location, exp, false);
90
string Resolve (DebuggerSession session, SourceLocation location, string expression, bool tryTypeOf)
92
expression = expression.TrimStart ();
94
if (expression.Length > 0 && expression[0] == '?')
95
return "?" + Resolve (session, location, expression.Substring (1).Trim ());
97
if (expression.Length > 3 && expression.StartsWith ("var", StringComparison.Ordinal) && char.IsWhiteSpace (expression[3]))
98
return "var " + Resolve (session, location, expression.Substring (4).Trim (' ', '\t'));
100
expression = ReplaceExceptionTag (expression, session.Options.EvaluationOptions.CurrentExceptionTag);
102
Expression expr = new CSharpParser ().ParseExpression (expression);
106
var resolver = new NRefactoryExpressionResolverVisitor (session, location, expression);
107
expr.AcceptVisitor (resolver);
109
string resolved = resolver.GetResolvedExpression ();
110
if (resolved == expression && !tryTypeOf && (expr is BinaryOperatorExpression) && IsTypeName (expression)) {
111
// This is a hack to be able to parse expressions such as "List<string>". The NRefactory parser
112
// can parse a single type name, so a solution is to wrap it around a typeof(). We do it if
113
// the evaluation fails.
114
string res = Resolve (session, location, "typeof(" + expression + ")", true);
115
return res.Substring (7, res.Length - 8);
121
public override ValidationResult ValidateExpression (EvaluationContext ctx, string expression)
123
expression = expression.TrimStart ();
125
if (expression.Length > 0 && expression[0] == '?')
126
expression = expression.Substring (1).Trim ();
128
if (expression.Length > 3 && expression.StartsWith ("var", StringComparison.Ordinal) && char.IsWhiteSpace (expression[3]))
129
expression = expression.Substring (4).Trim ();
131
expression = ReplaceExceptionTag (expression, ctx.Options.CurrentExceptionTag);
133
// Required as a workaround for a bug in the parser (it won't parse simple expressions like numbers)
134
if (!expression.EndsWith (";", StringComparison.Ordinal))
137
var parser = new CSharpParser ();
138
parser.ParseExpression (expression);
140
if (parser.HasErrors)
141
return new ValidationResult (false, parser.Errors.First ().Message);
143
return new ValidationResult (true, null);
146
string ReplaceExceptionTag (string exp, string tag)
148
// FIXME: Don't replace inside string literals
149
return exp.Replace (tag, "__EXCEPTION_OBJECT__");
152
bool IsTypeName (string name)
155
bool res = ParseTypeName (name + "$", ref pos);
156
return res && pos >= name.Length;
159
bool ParseTypeName (string name, ref int pos)
161
EatSpaces (name, ref pos);
162
if (!ParseName (name, ref pos))
165
EatSpaces (name, ref pos);
166
if (!ParseGenericArgs (name, ref pos))
169
EatSpaces (name, ref pos);
170
if (!ParseIndexer (name, ref pos))
173
EatSpaces (name, ref pos);
177
void EatSpaces (string name, ref int pos)
179
while (char.IsWhiteSpace (name[pos]))
183
bool ParseName (string name, ref int pos)
185
if (name[0] == 'g' && pos < name.Length - 8 && name.Substring (pos, 8) == "global::")
190
while (char.IsLetterOrDigit (name[pos]))
196
if (name[pos] != '.')
204
bool ParseGenericArgs (string name, ref int pos)
206
if (name [pos] != '<')
210
EatSpaces (name, ref pos);
213
if (!ParseTypeName (name, ref pos))
216
EatSpaces (name, ref pos);
217
char c = name [pos++];
229
bool ParseIndexer (string name, ref int pos)
231
if (name [pos] != '[')
236
EatSpaces (name, ref pos);
237
} while (name [pos] == ',');
239
return name [pos++] == ']';