|
|
@ -8,6 +8,7 @@ using ICSharpCode.NRefactory.PrettyPrinter; |
|
|
|
using System; |
|
|
|
using System; |
|
|
|
using System.Collections; |
|
|
|
using System.Collections; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.Collections.Generic; |
|
|
|
|
|
|
|
using System.Diagnostics; |
|
|
|
using System.Text; |
|
|
|
using System.Text; |
|
|
|
using Debugger.MetaData; |
|
|
|
using Debugger.MetaData; |
|
|
|
using ICSharpCode.NRefactory; |
|
|
|
using ICSharpCode.NRefactory; |
|
|
@ -18,7 +19,7 @@ namespace Debugger |
|
|
|
{ |
|
|
|
{ |
|
|
|
public class ExpressionEvaluator: NotImplementedAstVisitor |
|
|
|
public class ExpressionEvaluator: NotImplementedAstVisitor |
|
|
|
{ |
|
|
|
{ |
|
|
|
/// <summary> Evaluate given expression. If you expression tree already, use overloads of this method.</summary>
|
|
|
|
/// <summary> Evaluate given expression. If you have expression tree already, use overloads of this method.</summary>
|
|
|
|
/// <returns> Returned value or null for statements </returns>
|
|
|
|
/// <returns> Returned value or null for statements </returns>
|
|
|
|
public static Value Evaluate(string code, SupportedLanguage language, StackFrame context) |
|
|
|
public static Value Evaluate(string code, SupportedLanguage language, StackFrame context) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -45,29 +46,12 @@ namespace Debugger |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static Dictionary<AppDomain, Dictionary<string, Value>> expressionCache = new Dictionary<AppDomain, Dictionary<string, Value>>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static Value Evaluate(INode code, StackFrame context) |
|
|
|
public static Value Evaluate(INode code, StackFrame context) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (context == null) throw new ArgumentNullException("context"); |
|
|
|
if (context == null) throw new ArgumentNullException("context"); |
|
|
|
if (context.IsInvalid) throw new DebuggerException("The context is no longer valid"); |
|
|
|
if (context.IsInvalid) throw new DebuggerException("The context is no longer valid"); |
|
|
|
|
|
|
|
|
|
|
|
string codeAsText = code.PrettyPrint(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Get value from cache if possible
|
|
|
|
|
|
|
|
if (expressionCache.ContainsKey(context.AppDomain) && |
|
|
|
|
|
|
|
expressionCache[context.AppDomain].ContainsKey(codeAsText)) { |
|
|
|
|
|
|
|
Value cached = expressionCache[context.AppDomain][codeAsText]; |
|
|
|
|
|
|
|
if (!cached.IsInvalid) { |
|
|
|
|
|
|
|
if (context.Process.Options.Verbose) { |
|
|
|
|
|
|
|
context.Process.TraceMessage(string.Format("Cached: {0}", codeAsText)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return cached; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Value result; |
|
|
|
Value result; |
|
|
|
DateTime start = Debugger.Util.HighPrecisionTimer.Now; |
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
result = (Value)code.AcceptVisitor(new ExpressionEvaluator(context), null); |
|
|
|
result = (Value)code.AcceptVisitor(new ExpressionEvaluator(context), null); |
|
|
|
} catch (GetValueException) { |
|
|
|
} catch (GetValueException) { |
|
|
@ -75,18 +59,6 @@ namespace Debugger |
|
|
|
} catch (NotImplementedException e) { |
|
|
|
} catch (NotImplementedException e) { |
|
|
|
throw new GetValueException(code, "Language feature not implemented: " + e.Message); |
|
|
|
throw new GetValueException(code, "Language feature not implemented: " + e.Message); |
|
|
|
} |
|
|
|
} |
|
|
|
DateTime end = Debugger.Util.HighPrecisionTimer.Now; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Store value in cache
|
|
|
|
|
|
|
|
if (!expressionCache.ContainsKey(context.AppDomain)) { |
|
|
|
|
|
|
|
expressionCache[context.AppDomain] = new Dictionary<string, Value>(); |
|
|
|
|
|
|
|
// TODO
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// expressionCache[context.AppDomain][codeAsText] = result;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (context.Process.Options.Verbose) { |
|
|
|
|
|
|
|
context.Process.TraceMessage(string.Format("Evaluated: {0} ({1} ms)", code, (end - start).TotalMilliseconds)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return result; |
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -129,6 +101,34 @@ namespace Debugger |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void AddToCache(INode expression, Value value) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Expressions are cleared then the process is resumed
|
|
|
|
|
|
|
|
context.Process.CachedExpressions[expression] = value; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool TryGetCached(INode expression, out Value cached) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Value val; |
|
|
|
|
|
|
|
if (context.Process.CachedExpressions.TryGetValue(expression, out val)) { |
|
|
|
|
|
|
|
if (val == null || !val.IsInvalid) { |
|
|
|
|
|
|
|
// context.Process.TraceMessage("Is cached: {0}", expression.PrettyPrint());
|
|
|
|
|
|
|
|
cached = val; |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
cached = null; |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Value EvalAndPermRef(INode expression) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Value val = (Value)expression.AcceptVisitor(this, null); |
|
|
|
|
|
|
|
if (val != null) |
|
|
|
|
|
|
|
val = val.GetPermanentReference(); |
|
|
|
|
|
|
|
AddToCache(expression, val); |
|
|
|
|
|
|
|
return val; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
StackFrame context; |
|
|
|
StackFrame context; |
|
|
|
|
|
|
|
|
|
|
@ -141,256 +141,344 @@ namespace Debugger |
|
|
|
this.context = context; |
|
|
|
this.context = context; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public override object VisitAssignmentExpression(AssignmentExpression assignmentExpression, object data) |
|
|
|
IDisposable LogEval(INode expression) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Stopwatch watch = new Stopwatch(); |
|
|
|
|
|
|
|
watch.Start(); |
|
|
|
|
|
|
|
return new CallbackOnDispose(delegate { |
|
|
|
|
|
|
|
if (!context.Process.CachedExpressions.ContainsKey(expression)) |
|
|
|
|
|
|
|
throw new DebuggerException("Result not added to cache"); |
|
|
|
|
|
|
|
watch.Stop(); |
|
|
|
|
|
|
|
context.Process.TraceMessage("Evaluated: {0} in {1} ms total", expression.PrettyPrint(), watch.ElapsedMilliseconds); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public override object VisitEmptyStatement(EmptyStatement emptyStatement, object data) |
|
|
|
{ |
|
|
|
{ |
|
|
|
BinaryOperatorType op; |
|
|
|
using(LogEval(emptyStatement)) { |
|
|
|
switch (assignmentExpression.Op) { |
|
|
|
return null; |
|
|
|
case AssignmentOperatorType.Assign: op = BinaryOperatorType.None; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.Add: op = BinaryOperatorType.Add; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.ConcatString: op = BinaryOperatorType.Concat; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.Subtract: op = BinaryOperatorType.Subtract; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.Multiply: op = BinaryOperatorType.Multiply; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.Divide: op = BinaryOperatorType.Divide; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.DivideInteger: op = BinaryOperatorType.DivideInteger; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.ShiftLeft: op = BinaryOperatorType.ShiftLeft; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.ShiftRight: op = BinaryOperatorType.ShiftRight; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.ExclusiveOr: op = BinaryOperatorType.ExclusiveOr; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.Modulus: op = BinaryOperatorType.Modulus; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.BitwiseAnd: op = BinaryOperatorType.BitwiseAnd; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.BitwiseOr: op = BinaryOperatorType.BitwiseOr; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.Power: op = BinaryOperatorType.Power; break; |
|
|
|
|
|
|
|
default: throw new GetValueException("Unknown operator " + assignmentExpression.Op); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Value right; |
|
|
|
public override object VisitExpressionStatement(ExpressionStatement expressionStatement, object data) |
|
|
|
if (op == BinaryOperatorType.None) { |
|
|
|
{ |
|
|
|
right = (Value)assignmentExpression.Right.AcceptVisitor(this, null); |
|
|
|
using(LogEval(expressionStatement)) { |
|
|
|
} else { |
|
|
|
EvalAndPermRef(expressionStatement.Expression); |
|
|
|
BinaryOperatorExpression binOpExpr = new BinaryOperatorExpression(); |
|
|
|
return null; |
|
|
|
binOpExpr.Left = assignmentExpression.Left; |
|
|
|
|
|
|
|
binOpExpr.Op = op; |
|
|
|
|
|
|
|
binOpExpr.Right = assignmentExpression.Right; |
|
|
|
|
|
|
|
right = (Value)VisitBinaryOperatorExpression(binOpExpr, null); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
right = right.GetPermanentReference(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Value left = ((Value)assignmentExpression.Left.AcceptVisitor(this, null)); |
|
|
|
public override object VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, object data) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Value cached; |
|
|
|
|
|
|
|
if (TryGetCached(parenthesizedExpression, out cached)) return cached; |
|
|
|
|
|
|
|
using(LogEval(parenthesizedExpression)) { |
|
|
|
|
|
|
|
Value res = EvalAndPermRef(parenthesizedExpression.Expression); |
|
|
|
|
|
|
|
|
|
|
|
if (!left.IsReference && left.Type.FullName != right.Type.FullName) { |
|
|
|
AddToCache(parenthesizedExpression, res); |
|
|
|
throw new GetValueException(string.Format("Type {0} expected, {1} seen", left.Type.FullName, right.Type.FullName)); |
|
|
|
return res; |
|
|
|
} |
|
|
|
} |
|
|
|
left.SetValue(right); |
|
|
|
|
|
|
|
return right; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public override object VisitBlockStatement(BlockStatement blockStatement, object data) |
|
|
|
public override object VisitBlockStatement(BlockStatement blockStatement, object data) |
|
|
|
{ |
|
|
|
{ |
|
|
|
foreach(INode statement in blockStatement.Children) { |
|
|
|
using(LogEval(blockStatement)) { |
|
|
|
statement.AcceptVisitor(this, null); |
|
|
|
foreach(INode statement in blockStatement.Children) { |
|
|
|
|
|
|
|
EvalAndPermRef(statement); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public override object VisitEmptyStatement(EmptyStatement emptyStatement, object data) |
|
|
|
/// <remarks> We have to put that in cache as well otherwise expaning (a = b).Prop will reevalute </remarks>
|
|
|
|
|
|
|
|
public override object VisitAssignmentExpression(AssignmentExpression assignmentExpression, object data) |
|
|
|
{ |
|
|
|
{ |
|
|
|
return null; |
|
|
|
Value cached; |
|
|
|
} |
|
|
|
if (TryGetCached(assignmentExpression, out cached)) return cached; |
|
|
|
|
|
|
|
using(LogEval(assignmentExpression)) { |
|
|
|
|
|
|
|
BinaryOperatorType op; |
|
|
|
|
|
|
|
switch (assignmentExpression.Op) { |
|
|
|
|
|
|
|
case AssignmentOperatorType.Assign: op = BinaryOperatorType.None; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.Add: op = BinaryOperatorType.Add; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.ConcatString: op = BinaryOperatorType.Concat; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.Subtract: op = BinaryOperatorType.Subtract; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.Multiply: op = BinaryOperatorType.Multiply; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.Divide: op = BinaryOperatorType.Divide; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.DivideInteger: op = BinaryOperatorType.DivideInteger; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.ShiftLeft: op = BinaryOperatorType.ShiftLeft; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.ShiftRight: op = BinaryOperatorType.ShiftRight; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.ExclusiveOr: op = BinaryOperatorType.ExclusiveOr; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.Modulus: op = BinaryOperatorType.Modulus; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.BitwiseAnd: op = BinaryOperatorType.BitwiseAnd; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.BitwiseOr: op = BinaryOperatorType.BitwiseOr; break; |
|
|
|
|
|
|
|
case AssignmentOperatorType.Power: op = BinaryOperatorType.Power; break; |
|
|
|
|
|
|
|
default: throw new GetValueException("Unknown operator " + assignmentExpression.Op); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public override object VisitExpressionStatement(ExpressionStatement expressionStatement, object data) |
|
|
|
Value right; |
|
|
|
{ |
|
|
|
if (op == BinaryOperatorType.None) { |
|
|
|
expressionStatement.Expression.AcceptVisitor(this, null); |
|
|
|
right = EvalAndPermRef(assignmentExpression.Right); |
|
|
|
return null; |
|
|
|
} else { |
|
|
|
|
|
|
|
BinaryOperatorExpression binOpExpr = new BinaryOperatorExpression(); |
|
|
|
|
|
|
|
binOpExpr.Left = assignmentExpression.Left; |
|
|
|
|
|
|
|
binOpExpr.Op = op; |
|
|
|
|
|
|
|
binOpExpr.Right = assignmentExpression.Right; |
|
|
|
|
|
|
|
right = (Value)VisitBinaryOperatorExpression(binOpExpr, null); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
right = right.GetPermanentReference(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Value left = EvalAndPermRef(assignmentExpression.Left); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!left.IsReference && left.Type.FullName != right.Type.FullName) { |
|
|
|
|
|
|
|
throw new GetValueException(string.Format("Type {0} expected, {1} seen", left.Type.FullName, right.Type.FullName)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
left.SetValue(right); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AddToCache(assignmentExpression, right); |
|
|
|
|
|
|
|
return right; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public override object VisitIdentifierExpression(IdentifierExpression identifierExpression, object data) |
|
|
|
public override object VisitIdentifierExpression(IdentifierExpression identifierExpression, object data) |
|
|
|
{ |
|
|
|
{ |
|
|
|
string identifier = identifierExpression.Identifier; |
|
|
|
Value cached; |
|
|
|
|
|
|
|
if (TryGetCached(identifierExpression, out cached)) return cached; |
|
|
|
if (identifier == "__exception") { |
|
|
|
using(LogEval(identifierExpression)) { |
|
|
|
if (context.Thread.CurrentException != null) { |
|
|
|
string identifier = identifierExpression.Identifier; |
|
|
|
return context.Thread.CurrentException.Value; |
|
|
|
|
|
|
|
} else { |
|
|
|
Value result = null; |
|
|
|
throw new GetValueException("No current exception"); |
|
|
|
|
|
|
|
|
|
|
|
if (identifier == "__exception") { |
|
|
|
|
|
|
|
if (context.Thread.CurrentException != null) { |
|
|
|
|
|
|
|
result = context.Thread.CurrentException.Value; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
throw new GetValueException("No current exception"); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Value arg = context.GetArgumentValue(identifier); |
|
|
|
result = result ?? context.GetArgumentValue(identifier); |
|
|
|
if (arg != null) return arg; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Value local = context.GetLocalVariableValue(identifier); |
|
|
|
result = result ?? context.GetLocalVariableValue(identifier); |
|
|
|
if (local != null) return local; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!context.MethodInfo.IsStatic) { |
|
|
|
if (result == null) { |
|
|
|
Value member = context.GetThisValue().GetMemberValue(identifier); |
|
|
|
if (!context.MethodInfo.IsStatic) { |
|
|
|
if (member != null) return member; |
|
|
|
// Can be null
|
|
|
|
} else { |
|
|
|
result = context.GetThisValue().GetMemberValue(identifier); |
|
|
|
MemberInfo memberInfo = context.MethodInfo.DeclaringType.GetMember(identifier); |
|
|
|
} else { |
|
|
|
if (memberInfo != null && memberInfo.IsStatic) { |
|
|
|
MemberInfo memberInfo = context.MethodInfo.DeclaringType.GetMember(identifier); |
|
|
|
return Value.GetMemberValue(null, memberInfo, null); |
|
|
|
if (memberInfo != null && memberInfo.IsStatic) { |
|
|
|
|
|
|
|
result = Value.GetMemberValue(null, memberInfo, null); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
throw new GetValueException("Identifier \"" + identifier + "\" not found in this context"); |
|
|
|
if (result == null) |
|
|
|
|
|
|
|
throw new GetValueException("Identifier \"" + identifier + "\" not found in this context"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AddToCache(identifierExpression, result); |
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public override object VisitIndexerExpression(IndexerExpression indexerExpression, object data) |
|
|
|
public override object VisitIndexerExpression(IndexerExpression indexerExpression, object data) |
|
|
|
{ |
|
|
|
{ |
|
|
|
List<Value> indexes = new List<Value>(); |
|
|
|
Value cached; |
|
|
|
foreach(Expression indexExpr in indexerExpression.Indexes) { |
|
|
|
if (TryGetCached(indexerExpression, out cached)) return cached; |
|
|
|
Value indexValue = ((Value)indexExpr.AcceptVisitor(this, null)).GetPermanentReference(); |
|
|
|
using(LogEval(indexerExpression)) { |
|
|
|
indexes.Add(indexValue); |
|
|
|
List<Value> indexes = new List<Value>(); |
|
|
|
} |
|
|
|
foreach(Expression indexExpr in indexerExpression.Indexes) { |
|
|
|
|
|
|
|
Value indexValue = EvalAndPermRef(indexExpr); |
|
|
|
|
|
|
|
indexes.Add(indexValue); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Value target = (Value)indexerExpression.TargetObject.AcceptVisitor(this, null); |
|
|
|
Value target = EvalAndPermRef(indexerExpression.TargetObject); |
|
|
|
|
|
|
|
|
|
|
|
if (target.Type.IsArray) { |
|
|
|
if (target.Type.IsArray) { |
|
|
|
List<int> intIndexes = new List<int>(); |
|
|
|
List<int> intIndexes = new List<int>(); |
|
|
|
foreach(Value index in indexes) { |
|
|
|
foreach(Value index in indexes) { |
|
|
|
if (!index.Type.IsInteger) throw new GetValueException("Integer expected for indexer"); |
|
|
|
if (!index.Type.IsInteger) throw new GetValueException("Integer expected for indexer"); |
|
|
|
intIndexes.Add((int)index.PrimitiveValue); |
|
|
|
intIndexes.Add((int)index.PrimitiveValue); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return target.GetArrayElement(intIndexes.ToArray()); |
|
|
|
} |
|
|
|
} |
|
|
|
return target.GetArrayElement(intIndexes.ToArray()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (target.Type.IsPrimitive && target.PrimitiveValue is string) { |
|
|
|
if (target.Type.IsPrimitive && target.PrimitiveValue is string) { |
|
|
|
if (indexes.Count == 1 && indexes[0].Type.IsInteger) { |
|
|
|
if (indexes.Count == 1 && indexes[0].Type.IsInteger) { |
|
|
|
int index = (int)indexes[0].PrimitiveValue; |
|
|
|
int index = (int)indexes[0].PrimitiveValue; |
|
|
|
return Eval.CreateValue(context.AppDomain, ((string)target.PrimitiveValue)[index]); |
|
|
|
return Eval.CreateValue(context.AppDomain, ((string)target.PrimitiveValue)[index]); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
throw new GetValueException("Expected single integer index"); |
|
|
|
throw new GetValueException("Expected single integer index"); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PropertyInfo pi = target.Type.GetProperty("Item"); |
|
|
|
PropertyInfo pi = target.Type.GetProperty("Item"); |
|
|
|
if (pi == null) throw new GetValueException("The object does not have an indexer property"); |
|
|
|
if (pi == null) throw new GetValueException("The object does not have an indexer property"); |
|
|
|
return target.GetPropertyValue(pi, indexes.ToArray()); |
|
|
|
Value result = target.GetPropertyValue(pi, indexes.ToArray()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AddToCache(indexerExpression, result); |
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data) |
|
|
|
public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Value target; |
|
|
|
Value cached; |
|
|
|
string methodName; |
|
|
|
if (TryGetCached(invocationExpression, out cached)) return cached; |
|
|
|
MemberReferenceExpression memberRef = invocationExpression.TargetObject as MemberReferenceExpression; |
|
|
|
using(LogEval(invocationExpression)) { |
|
|
|
if (memberRef != null) { |
|
|
|
Value target; |
|
|
|
target = ((Value)memberRef.TargetObject.AcceptVisitor(this, null)).GetPermanentReference(); |
|
|
|
string methodName; |
|
|
|
methodName = memberRef.MemberName; |
|
|
|
MemberReferenceExpression memberRef = invocationExpression.TargetObject as MemberReferenceExpression; |
|
|
|
} else { |
|
|
|
if (memberRef != null) { |
|
|
|
IdentifierExpression ident = invocationExpression.TargetObject as IdentifierExpression; |
|
|
|
target = EvalAndPermRef(memberRef.TargetObject); |
|
|
|
if (ident != null) { |
|
|
|
methodName = memberRef.MemberName; |
|
|
|
target = context.GetThisValue(); |
|
|
|
|
|
|
|
methodName = ident.Identifier; |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
throw new GetValueException("Member reference expected for method invocation"); |
|
|
|
IdentifierExpression ident = invocationExpression.TargetObject as IdentifierExpression; |
|
|
|
|
|
|
|
if (ident != null) { |
|
|
|
|
|
|
|
target = context.GetThisValue(); |
|
|
|
|
|
|
|
methodName = ident.Identifier; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
throw new GetValueException("Member reference expected for method invocation"); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
List<Value> args = new List<Value>(); |
|
|
|
|
|
|
|
foreach(Expression expr in invocationExpression.Arguments) { |
|
|
|
|
|
|
|
args.Add(EvalAndPermRef(expr)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
MethodInfo method = target.Type.GetMember(methodName, BindingFlags.Method | BindingFlags.IncludeSuperType) as MethodInfo; |
|
|
|
|
|
|
|
if (method == null) { |
|
|
|
|
|
|
|
throw new GetValueException("Method " + methodName + " not found"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Value result = target.InvokeMethod(method, args.ToArray()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AddToCache(invocationExpression, result); |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
List<Value> args = new List<Value>(); |
|
|
|
|
|
|
|
foreach(Expression expr in invocationExpression.Arguments) { |
|
|
|
|
|
|
|
args.Add(((Value)expr.AcceptVisitor(this, null)).GetPermanentReference()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
MethodInfo method = target.Type.GetMember(methodName, BindingFlags.Method | BindingFlags.IncludeSuperType) as MethodInfo; |
|
|
|
|
|
|
|
if (method == null) { |
|
|
|
|
|
|
|
throw new GetValueException("Method " + methodName + " not found"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return target.InvokeMethod(method, args.ToArray()); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public override object VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, object data) |
|
|
|
public override object VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, object data) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Value target = (Value)memberReferenceExpression.TargetObject.AcceptVisitor(this, null); |
|
|
|
Value cached; |
|
|
|
Value member = target.GetMemberValue(memberReferenceExpression.MemberName); |
|
|
|
if (TryGetCached(memberReferenceExpression, out cached)) return cached; |
|
|
|
if (member != null) { |
|
|
|
using(LogEval(memberReferenceExpression)) { |
|
|
|
|
|
|
|
Value target = EvalAndPermRef(memberReferenceExpression.TargetObject); |
|
|
|
|
|
|
|
Value member = target.GetMemberValue(memberReferenceExpression.MemberName); |
|
|
|
|
|
|
|
if (member == null) |
|
|
|
|
|
|
|
throw new GetValueException("Member \"" + memberReferenceExpression.MemberName + "\" not found"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AddToCache(memberReferenceExpression, member); |
|
|
|
return member; |
|
|
|
return member; |
|
|
|
} else { |
|
|
|
|
|
|
|
throw new GetValueException("Member \"" + memberReferenceExpression.MemberName + "\" not found"); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public override object VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, object data) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return parenthesizedExpression.Expression.AcceptVisitor(this, null); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public override object VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, object data) |
|
|
|
public override object VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, object data) |
|
|
|
{ |
|
|
|
{ |
|
|
|
return Eval.CreateValue(context.AppDomain, primitiveExpression.Value); |
|
|
|
Value cached; |
|
|
|
|
|
|
|
if (TryGetCached(primitiveExpression, out cached)) return cached; |
|
|
|
|
|
|
|
using(LogEval(primitiveExpression)){ |
|
|
|
|
|
|
|
Value result = Eval.CreateValue(context.AppDomain, primitiveExpression.Value); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AddToCache(primitiveExpression, result); |
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public override object VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, object data) |
|
|
|
public override object VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, object data) |
|
|
|
{ |
|
|
|
{ |
|
|
|
return context.GetThisValue(); |
|
|
|
Value cached; |
|
|
|
|
|
|
|
if (TryGetCached(thisReferenceExpression, out cached)) return cached; |
|
|
|
|
|
|
|
using(LogEval(thisReferenceExpression)) { |
|
|
|
|
|
|
|
Value result = context.GetThisValue(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AddToCache(thisReferenceExpression, result); |
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public override object VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data) |
|
|
|
public override object VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Value value = ((Value)unaryOperatorExpression.Expression.AcceptVisitor(this, null)); |
|
|
|
Value cached; |
|
|
|
UnaryOperatorType op = unaryOperatorExpression.Op; |
|
|
|
if (TryGetCached(unaryOperatorExpression, out cached)) return cached; |
|
|
|
|
|
|
|
using(LogEval(unaryOperatorExpression)) { |
|
|
|
if (op == UnaryOperatorType.Dereference) { |
|
|
|
Value value = EvalAndPermRef(unaryOperatorExpression.Expression); |
|
|
|
if (!value.Type.IsPointer) throw new GetValueException("Target object is not a pointer"); |
|
|
|
UnaryOperatorType op = unaryOperatorExpression.Op; |
|
|
|
return value.Dereference(); |
|
|
|
|
|
|
|
} |
|
|
|
if (op == UnaryOperatorType.Dereference) { |
|
|
|
|
|
|
|
if (!value.Type.IsPointer) throw new GetValueException("Target object is not a pointer"); |
|
|
|
|
|
|
|
return value.Dereference(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!value.Type.IsPrimitive) throw new GetValueException("Primitive value expected"); |
|
|
|
if (!value.Type.IsPrimitive) throw new GetValueException("Primitive value expected"); |
|
|
|
|
|
|
|
|
|
|
|
object val = value.PrimitiveValue; |
|
|
|
object val = value.PrimitiveValue; |
|
|
|
|
|
|
|
|
|
|
|
object result = null; |
|
|
|
object result = null; |
|
|
|
|
|
|
|
|
|
|
|
// Bool operation
|
|
|
|
// Bool operation
|
|
|
|
if (val is bool) { |
|
|
|
if (val is bool) { |
|
|
|
bool a = Convert.ToBoolean(val); |
|
|
|
bool a = Convert.ToBoolean(val); |
|
|
|
switch (op) { |
|
|
|
switch (op) { |
|
|
|
case UnaryOperatorType.Not: result = !a; break; |
|
|
|
case UnaryOperatorType.Not: result = !a; break; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Float operation
|
|
|
|
// Float operation
|
|
|
|
if (val is double || val is float) { |
|
|
|
if (val is double || val is float) { |
|
|
|
double a = Convert.ToDouble(val); |
|
|
|
double a = Convert.ToDouble(val); |
|
|
|
switch (op) { |
|
|
|
switch (op) { |
|
|
|
case UnaryOperatorType.Minus: result = -a; break; |
|
|
|
case UnaryOperatorType.Minus: result = -a; break; |
|
|
|
case UnaryOperatorType.Plus: result = +a; break; |
|
|
|
case UnaryOperatorType.Plus: result = +a; break; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Integer operation
|
|
|
|
// Integer operation
|
|
|
|
if (val is byte || val is sbyte || val is int || val is uint || val is long || val is ulong) { |
|
|
|
if (val is byte || val is sbyte || val is int || val is uint || val is long || val is ulong) { |
|
|
|
long a = Convert.ToInt64(val); |
|
|
|
long a = Convert.ToInt64(val); |
|
|
|
switch (op) { |
|
|
|
switch (op) { |
|
|
|
case UnaryOperatorType.Decrement: result = a - 1; break; |
|
|
|
case UnaryOperatorType.Decrement: result = a - 1; break; |
|
|
|
case UnaryOperatorType.Increment: result = a + 1; break; |
|
|
|
case UnaryOperatorType.Increment: result = a + 1; break; |
|
|
|
case UnaryOperatorType.PostDecrement: result = a; break; |
|
|
|
case UnaryOperatorType.PostDecrement: result = a; break; |
|
|
|
case UnaryOperatorType.PostIncrement: result = a; break; |
|
|
|
case UnaryOperatorType.PostIncrement: result = a; break; |
|
|
|
case UnaryOperatorType.Minus: result = -a; break; |
|
|
|
case UnaryOperatorType.Minus: result = -a; break; |
|
|
|
case UnaryOperatorType.Plus: result = a; break; |
|
|
|
case UnaryOperatorType.Plus: result = a; break; |
|
|
|
case UnaryOperatorType.BitNot: result = ~a; break; |
|
|
|
case UnaryOperatorType.BitNot: result = ~a; break; |
|
|
|
} |
|
|
|
} |
|
|
|
switch (op) { |
|
|
|
switch (op) { |
|
|
|
case UnaryOperatorType.Decrement: |
|
|
|
case UnaryOperatorType.Decrement: |
|
|
|
case UnaryOperatorType.PostDecrement: |
|
|
|
case UnaryOperatorType.PostDecrement: |
|
|
|
VisitAssignmentExpression(new AssignmentExpression(unaryOperatorExpression.Expression, AssignmentOperatorType.Subtract, new PrimitiveExpression(1)), null); |
|
|
|
VisitAssignmentExpression(new AssignmentExpression(unaryOperatorExpression.Expression, AssignmentOperatorType.Subtract, new PrimitiveExpression(1)), null); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case UnaryOperatorType.Increment: |
|
|
|
case UnaryOperatorType.Increment: |
|
|
|
case UnaryOperatorType.PostIncrement: |
|
|
|
case UnaryOperatorType.PostIncrement: |
|
|
|
VisitAssignmentExpression(new AssignmentExpression(unaryOperatorExpression.Expression, AssignmentOperatorType.Add, new PrimitiveExpression(1)), null); |
|
|
|
VisitAssignmentExpression(new AssignmentExpression(unaryOperatorExpression.Expression, AssignmentOperatorType.Add, new PrimitiveExpression(1)), null); |
|
|
|
break; |
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (result == null) throw new GetValueException("Unsuppored unary expression " + op); |
|
|
|
if (result == null) throw new GetValueException("Unsuppored unary expression " + op); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Value res = Eval.CreateValue(context.AppDomain, result); |
|
|
|
|
|
|
|
|
|
|
|
return Eval.CreateValue(context.AppDomain, result); |
|
|
|
AddToCache(unaryOperatorExpression, res); |
|
|
|
|
|
|
|
return res; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public override object VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data) |
|
|
|
public override object VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Value left = ((Value)binaryOperatorExpression.Left.AcceptVisitor(this, null)).GetPermanentReference(); |
|
|
|
Value cached; |
|
|
|
Value right = ((Value)binaryOperatorExpression.Right.AcceptVisitor(this, null)).GetPermanentReference(); |
|
|
|
if (TryGetCached(binaryOperatorExpression, out cached)) return cached; |
|
|
|
|
|
|
|
using(LogEval(binaryOperatorExpression)) { |
|
|
|
object result = VisitBinaryOperatorExpressionInternal(left, right, binaryOperatorExpression.Op); |
|
|
|
Value left = EvalAndPermRef(binaryOperatorExpression.Left); |
|
|
|
// Conver long to int if possible
|
|
|
|
Value right = EvalAndPermRef(binaryOperatorExpression.Right); |
|
|
|
if (result is long && int.MinValue <= (long)result && (long)result <= int.MaxValue) result = (int)(long)result; |
|
|
|
|
|
|
|
return Eval.CreateValue(context.AppDomain, result); |
|
|
|
object result = VisitBinaryOperatorExpressionInternal(left, right, binaryOperatorExpression.Op); |
|
|
|
|
|
|
|
// Conver long to int if possible
|
|
|
|
|
|
|
|
if (result is long && int.MinValue <= (long)result && (long)result <= int.MaxValue) result = (int)(long)result; |
|
|
|
|
|
|
|
Value res = Eval.CreateValue(context.AppDomain, result); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AddToCache(binaryOperatorExpression, res); |
|
|
|
|
|
|
|
return res; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public object VisitBinaryOperatorExpressionInternal(Value leftValue, Value rightValue, BinaryOperatorType op) |
|
|
|
public object VisitBinaryOperatorExpressionInternal(Value leftValue, Value rightValue, BinaryOperatorType op) |
|
|
|