238 lines
7 KiB
C#
238 lines
7 KiB
C#
//
|
|
// NRefactoryExpressionEvaluator.cs
|
|
//
|
|
// Authors: Lluis Sanchez Gual <lluis@novell.com>
|
|
// Jeffrey Stedfast <jeff@xamarin.com>
|
|
//
|
|
// Copyright (c) 2008 Novell, Inc (http://www.novell.com)
|
|
// Copyright (c) 2012 Xamarin Inc. (http://www.xamarin.com)
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
using System;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
|
|
using Mono.Debugging.Client;
|
|
|
|
namespace Mono.Debugging.Evaluation
|
|
{
|
|
/*public class NRefactoryExpressionEvaluator : ExpressionEvaluator
|
|
{
|
|
readonly Dictionary<string, ValueReference> userVariables = new Dictionary<string, ValueReference> ();
|
|
|
|
public override ValueReference Evaluate (EvaluationContext ctx, string expression, object expectedType)
|
|
{
|
|
expression = expression.Trim ();
|
|
|
|
if (expression.Length > 0 && expression[0] == '?')
|
|
expression = expression.Substring (1).TrimStart ();
|
|
|
|
if (expression.Length > 3 && expression.StartsWith ("var", StringComparison.Ordinal) && char.IsWhiteSpace (expression[3])) {
|
|
expression = expression.Substring (4).TrimStart ();
|
|
string variable = null;
|
|
|
|
for (int n = 0; n < expression.Length; n++) {
|
|
if (!char.IsLetterOrDigit (expression[n]) && expression[n] != '_') {
|
|
variable = expression.Substring (0, n);
|
|
if (!expression.Substring (n).TrimStart ().StartsWith ("=", StringComparison.Ordinal))
|
|
variable = null;
|
|
break;
|
|
}
|
|
|
|
if (n == expression.Length - 1) {
|
|
variable = expression;
|
|
expression = null;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty (variable))
|
|
userVariables[variable] = new UserVariableReference (ctx, variable);
|
|
|
|
if (expression == null)
|
|
return null;
|
|
}
|
|
|
|
expression = ReplaceExceptionTag (expression, ctx.Options.CurrentExceptionTag);
|
|
|
|
var expr = SyntaxFactory.ParseExpression (expression);
|
|
if (expr == null)
|
|
throw new EvaluatorException ("Could not parse expression '{0}'", expression);
|
|
|
|
var evaluator = new NRefactoryExpressionEvaluatorVisitor (ctx, expression, expectedType, userVariables);
|
|
|
|
return evaluator.Visit (expr);
|
|
}
|
|
|
|
public override string Resolve (DebuggerSession session, SourceLocation location, string exp)
|
|
{
|
|
return Resolve (session, location, exp, false);
|
|
}
|
|
|
|
string Resolve (DebuggerSession session, SourceLocation location, string expression, bool tryTypeOf)
|
|
{
|
|
expression = expression.Trim ();
|
|
|
|
if (expression.Length > 0 && expression[0] == '?')
|
|
return "?" + Resolve (session, location, expression.Substring (1).Trim ());
|
|
|
|
if (expression.Length > 3 && expression.StartsWith ("var", StringComparison.Ordinal) && char.IsWhiteSpace (expression[3]))
|
|
return "var " + Resolve (session, location, expression.Substring (4).Trim (' ', '\t'));
|
|
|
|
expression = ReplaceExceptionTag (expression, session.Options.EvaluationOptions.CurrentExceptionTag);
|
|
|
|
|
|
var expr = SyntaxFactory.ParseExpression (expression);
|
|
if (expr == null)
|
|
return expression;
|
|
|
|
var resolver = new NRefactoryExpressionResolverVisitor (session, location, expression);
|
|
resolver.Visit (expr);
|
|
|
|
string resolved = resolver.GetResolvedExpression ();
|
|
if (resolved == expression && !tryTypeOf && (expr is BinaryExpressionSyntax) && IsTypeName (expression)) {
|
|
// This is a hack to be able to parse expressions such as "List<string>". The NRefactory parser
|
|
// can parse a single type name, so a solution is to wrap it around a typeof(). We do it if
|
|
// the evaluation fails.
|
|
string res = Resolve (session, location, "typeof(" + expression + ")", true);
|
|
return res.Substring (7, res.Length - 8);
|
|
}
|
|
|
|
return resolved;
|
|
}
|
|
|
|
public override ValidationResult ValidateExpression (EvaluationContext ctx, string expression)
|
|
{
|
|
expression = expression.Trim ();
|
|
|
|
if (expression.Length > 0 && expression[0] == '?')
|
|
expression = expression.Substring (1).TrimStart ();
|
|
|
|
if (expression.Length > 3 && expression.StartsWith ("var", StringComparison.Ordinal) && char.IsWhiteSpace (expression[3]))
|
|
expression = expression.Substring (4).TrimStart ();
|
|
|
|
expression = ReplaceExceptionTag (expression, ctx.Options.CurrentExceptionTag);
|
|
|
|
ParseOptions options = new CSharpParseOptions ();
|
|
var expr = SyntaxFactory.ParseExpression (expression, options: options);
|
|
|
|
if (expr.ContainsDiagnostics)
|
|
return new ValidationResult (false, expr.GetDiagnostics().First ().GetMessage());
|
|
|
|
return new ValidationResult (true, null);
|
|
}
|
|
|
|
string ReplaceExceptionTag (string exp, string tag)
|
|
{
|
|
// FIXME: Don't replace inside string literals
|
|
return exp.Replace (tag, "__EXCEPTION_OBJECT__");
|
|
}
|
|
|
|
bool IsTypeName (string name)
|
|
{
|
|
int pos = 0;
|
|
bool res = ParseTypeName (name + "$", ref pos);
|
|
return res && pos >= name.Length;
|
|
}
|
|
|
|
bool ParseTypeName (string name, ref int pos)
|
|
{
|
|
EatSpaces (name, ref pos);
|
|
if (!ParseName (name, ref pos))
|
|
return false;
|
|
|
|
EatSpaces (name, ref pos);
|
|
if (!ParseGenericArgs (name, ref pos))
|
|
return false;
|
|
|
|
EatSpaces (name, ref pos);
|
|
if (!ParseIndexer (name, ref pos))
|
|
return false;
|
|
|
|
EatSpaces (name, ref pos);
|
|
return true;
|
|
}
|
|
|
|
void EatSpaces (string name, ref int pos)
|
|
{
|
|
while (char.IsWhiteSpace (name[pos]))
|
|
pos++;
|
|
}
|
|
|
|
bool ParseName (string name, ref int pos)
|
|
{
|
|
if (name[0] == 'g' && pos < name.Length - 8 && name.Substring (pos, 8) == "global::")
|
|
pos += 8;
|
|
|
|
do {
|
|
int oldp = pos;
|
|
while (char.IsLetterOrDigit (name[pos]))
|
|
pos++;
|
|
|
|
if (oldp == pos)
|
|
return false;
|
|
|
|
if (name[pos] != '.')
|
|
return true;
|
|
|
|
pos++;
|
|
} while (true);
|
|
}
|
|
|
|
bool ParseGenericArgs (string name, ref int pos)
|
|
{
|
|
if (name [pos] != '<')
|
|
return true;
|
|
|
|
pos++;
|
|
EatSpaces (name, ref pos);
|
|
|
|
while (true) {
|
|
if (!ParseTypeName (name, ref pos))
|
|
return false;
|
|
|
|
EatSpaces (name, ref pos);
|
|
char c = name [pos++];
|
|
|
|
if (c == '>')
|
|
return true;
|
|
|
|
if (c == ',')
|
|
continue;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool ParseIndexer (string name, ref int pos)
|
|
{
|
|
if (name [pos] != '[')
|
|
return true;
|
|
|
|
do {
|
|
pos++;
|
|
EatSpaces (name, ref pos);
|
|
} while (name [pos] == ',');
|
|
|
|
return name [pos++] == ']';
|
|
}
|
|
}*/
|
|
}
|