pascalabcnet/VisualPascalABCNETLinux/MonoDebugging/Evaluation/NRefactoryExpressionEvaluatorVisitor.cs
2023-04-10 21:04:27 +02:00

1220 lines
43 KiB
C#

//
// NRefactoryExpressionEvaluatorVisitor.cs
//
// Author: Jeffrey Stedfast <jeff@xamarin.com>
//
// Copyright (c) 2013 Xamarin Inc.
//
// 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.Reflection;
using System.Collections.Generic;
using Mono.Debugging.Client;
namespace Mono.Debugging.Evaluation
{
/*
public class NRefactoryExpressionEvaluatorVisitor : CSharpSyntaxVisitor<ValueReference>
{
readonly Dictionary<string, ValueReference> userVariables;
readonly EvaluationOptions options;
readonly EvaluationContext ctx;
readonly object expectedType;
readonly string expression;
public NRefactoryExpressionEvaluatorVisitor (EvaluationContext ctx, string expression, object expectedType, Dictionary<string,ValueReference> userVariables)
{
this.ctx = ctx;
this.expression = expression;
this.expectedType = expectedType;
this.userVariables = userVariables;
this.options = ctx.Options;
}
static Exception ParseError (string message, params object[] args)
{
return new EvaluatorException (message, args);
}
static Exception NotSupported ()
{
return new NotSupportedExpressionException ();
}
static string ResolveTypeName (SyntaxNode type)
{
string name = type.ToString ();
if (name.StartsWith ("global::", StringComparison.Ordinal))
name = name.Substring ("global::".Length);
var index = name.IndexOf('<');
if (index > -1)
name = name.Substring(0, index);
return name;
}
static long GetInteger (object val)
{
try {
return Convert.ToInt64 (val);
} catch {
throw ParseError ("Expected integer value.");
}
}
long ConvertToInt64 (object val)
{
if (val is IntPtr)
return ((IntPtr) val).ToInt64 ();
if (ctx.Adapter.IsEnum (ctx, val)) {
var type = ctx.Adapter.GetType (ctx, "System.Int64");
var result = ctx.Adapter.Cast (ctx, val, type);
return (long) ctx.Adapter.TargetObjectToObject (ctx, result);
}
return Convert.ToInt64 (val);
}
static Type GetCommonOperationType (object v1, object v2)
{
if (v1 is double || v2 is double)
return typeof (double);
if (v1 is float || v2 is float)
return typeof (double);
return typeof (long);
}
static Type GetCommonType (object v1, object v2)
{
var t1 = Type.GetTypeCode (v1.GetType ());
var t2 = Type.GetTypeCode (v2.GetType ());
if (t1 < TypeCode.Int32 && t2 < TypeCode.Int32)
return typeof (int);
switch ((TypeCode) Math.Max ((int) t1, (int) t2)) {
case TypeCode.Byte: return typeof (byte);
case TypeCode.Decimal: return typeof (decimal);
case TypeCode.Double: return typeof (double);
case TypeCode.Int16: return typeof (short);
case TypeCode.Int32: return typeof (int);
case TypeCode.Int64: return typeof (long);
case TypeCode.SByte: return typeof (sbyte);
case TypeCode.Single: return typeof (float);
case TypeCode.UInt16: return typeof (ushort);
case TypeCode.UInt32: return typeof (uint);
case TypeCode.UInt64: return typeof (ulong);
default: throw new Exception (((TypeCode) Math.Max ((int) t1, (int) t2)).ToString ());
}
}
static object EvaluateOperation (SyntaxKind op, double v1, double v2)
{
switch (op) {
case SyntaxKind.AddExpression: return v1 + v2;
case SyntaxKind.DivideExpression: return v1 / v2;
case SyntaxKind.MultiplyExpression: return v1 * v2;
case SyntaxKind.SubtractExpression: return v1 - v2;
case SyntaxKind.GreaterThanExpression: return v1 > v2;
case SyntaxKind.GreaterThanOrEqualExpression: return v1 >= v2;
case SyntaxKind.LessThanExpression: return v1 < v2;
case SyntaxKind.LessThanOrEqualExpression: return v1 <= v2;
case SyntaxKind.EqualsExpression: return v1 == v2;
case SyntaxKind.NotEqualsExpression: return v1 != v2;
default: throw ParseError ("Invalid binary operator.");
}
}
static object EvaluateOperation (SyntaxKind op, long v1, long v2)
{
switch (op) {
case SyntaxKind.AddExpression: return v1 + v2;
case SyntaxKind.BitwiseAndExpression: return v1 & v2;
case SyntaxKind.BitwiseOrExpression: return v1 | v2;
case SyntaxKind.ExclusiveOrExpression: return v1 ^ v2;
case SyntaxKind.DivideExpression: return v1 / v2;
case SyntaxKind.ModuloExpression: return v1 % v2;
case SyntaxKind.MultiplyExpression: return v1 * v2;
case SyntaxKind.LeftShiftExpression: return v1 << (int) v2;
case SyntaxKind.RightShiftExpression: return v1 >> (int) v2;
case SyntaxKind.SubtractExpression: return v1 - v2;
case SyntaxKind.GreaterThanExpression: return v1 > v2;
case SyntaxKind.GreaterThanOrEqualExpression: return v1 >= v2;
case SyntaxKind.LessThanExpression: return v1 < v2;
case SyntaxKind.LessThanOrEqualExpression: return v1 <= v2;
case SyntaxKind.EqualsExpression: return v1 == v2;
case SyntaxKind.NotEqualsExpression: return v1 != v2;
default: throw ParseError ("Invalid binary operator.");
}
}
static bool CheckReferenceEquality (EvaluationContext ctx, object v1, object v2)
{
if (v1 == null && v2 == null)
return true;
if (v1 == null || v2 == null)
return false;
object objectType = ctx.Adapter.GetType (ctx, "System.Object");
object[] argTypes = { objectType, objectType };
object[] args = { v1, v2 };
object result = ctx.Adapter.RuntimeInvoke (ctx, objectType, null, "ReferenceEquals", argTypes, args);
var literal = LiteralValueReference.CreateTargetObjectLiteral (ctx, "result", result);
return (bool) literal.ObjectValue;
}
static bool CheckEquality (EvaluationContext ctx, bool negate, object type1, object type2, object targetVal1, object targetVal2, object val1, object val2)
{
if (val1 == null && val2 == null)
return !negate;
if (val1 == null || val2 == null)
return negate;
string method = negate ? "op_Inequality" : "op_Equality";
object[] argTypes = { type1, type2 };
object target, targetType;
object[] args;
if (ctx.Adapter.HasMethod (ctx, type1, method, argTypes, BindingFlags.Public | BindingFlags.Static)) {
args = new [] { targetVal1, targetVal2 };
targetType = type1;
target = null;
negate = false;
} else if (ctx.Adapter.HasMethod (ctx, type2, method, argTypes, BindingFlags.Public | BindingFlags.Static)) {
args = new [] { targetVal1, targetVal2 };
targetType = type2;
target = null;
negate = false;
} else {
method = ctx.Adapter.IsValueType (type1) ? "Equals" : "ReferenceEquals";
targetType = ctx.Adapter.GetType (ctx, "System.Object");
argTypes = new [] { targetType, targetType };
args = new [] { targetVal1, targetVal2 };
target = null;
}
object result = ctx.Adapter.RuntimeInvoke (ctx, targetType, target, method, argTypes, args);
var literal = LiteralValueReference.CreateTargetObjectLiteral (ctx, "result", result);
bool retval = (bool) literal.ObjectValue;
return negate ? !retval : retval;
}
static ValueReference EvaluateOverloadedOperator (EvaluationContext ctx, string expression, SyntaxKind op, object type1, object type2, object targetVal1, object targetVal2, object val1, object val2)
{
object[] args = new [] { targetVal1, targetVal2 };
object[] argTypes = { type1, type2 };
object targetType = null;
string methodName = null;
switch (op) {
case SyntaxKind.BitwiseAndExpression: methodName = "op_BitwiseAnd"; break;
case SyntaxKind.BitwiseOrExpression: methodName = "op_BitwiseOr"; break;
case SyntaxKind.ExclusiveOrExpression: methodName = "op_ExclusiveOr"; break;
case SyntaxKind.GreaterThanExpression: methodName = "op_GreaterThan"; break;
case SyntaxKind.GreaterThanOrEqualExpression: methodName = "op_GreaterThanOrEqual"; break;
case SyntaxKind.EqualsExpression: methodName = "op_Equality"; break;
case SyntaxKind.NotEqualsExpression: methodName = "op_Inequality"; break;
case SyntaxKind.LessThanExpression: methodName = "op_LessThan"; break;
case SyntaxKind.LessThanOrEqualExpression: methodName = "op_LessThanOrEqual"; break;
case SyntaxKind.AddExpression: methodName = "op_Addition"; break;
case SyntaxKind.SubtractExpression: methodName = "op_Subtraction"; break;
case SyntaxKind.MultiplyExpression: methodName = "op_Multiply"; break;
case SyntaxKind.DivideExpression: methodName = "op_Division"; break;
case SyntaxKind.ModuloExpression: methodName = "op_Modulus"; break;
case SyntaxKind.LeftShiftExpression: methodName = "op_LeftShift"; break;
case SyntaxKind.RightShiftExpression: methodName = "op_RightShift"; break;
}
if (methodName == null)
throw ParseError ("Invalid operands in binary operator.");
if (ctx.Adapter.HasMethod (ctx, type1, methodName, argTypes, BindingFlags.Public | BindingFlags.Static)) {
targetType = type1;
} else if (ctx.Adapter.HasMethod (ctx, type2, methodName, argTypes, BindingFlags.Public | BindingFlags.Static)) {
targetType = type2;
} else {
throw ParseError ("Invalid operands in binary operator.");
}
object result = ctx.Adapter.RuntimeInvoke (ctx, targetType, null, methodName, argTypes, args);
return LiteralValueReference.CreateTargetObjectLiteral (ctx, expression, result);
}
ValueReference EvaluateBinaryOperatorExpression (SyntaxKind op, ValueReference left, ExpressionSyntax rightExp)
{
if (op == SyntaxKind.LogicalAndExpression) {
var val = left.ObjectValue;
if (!(val is bool))
throw ParseError ("Left operand of logical And must be a boolean.");
if (!(bool) val)
return LiteralValueReference.CreateObjectLiteral (ctx, expression, false);
var vr = Visit (rightExp);
if (vr == null || ctx.Adapter.GetTypeName (ctx, vr.Type) != "System.Boolean")
throw ParseError ("Right operand of logical And must be a boolean.");
return vr;
}
if (op == SyntaxKind.LogicalOrExpression) {
var val = left.ObjectValue;
if (!(val is bool))
throw ParseError ("Left operand of logical Or must be a boolean.");
if ((bool) val)
return LiteralValueReference.CreateObjectLiteral (ctx, expression, true);
var vr = Visit (rightExp);
if (vr == null || ctx.Adapter.GetTypeName (ctx, vr.Type) != "System.Boolean")
throw ParseError ("Right operand of logical Or must be a boolean.");
return vr;
}
var right = Visit (rightExp);
var targetVal1 = left.Value;
var targetVal2 = right.Value;
var type1 = ctx.Adapter.GetValueType (ctx, targetVal1);
var type2 = ctx.Adapter.GetValueType (ctx, targetVal2);
var val1 = left.ObjectValue;
var val2 = right.ObjectValue;
object res = null;
if (ctx.Adapter.IsNullableType (ctx, type1) && ctx.Adapter.NullableHasValue (ctx, type1, val1)) {
if (val2 == null) {
if (op == SyntaxKind.EqualsExpression)
return LiteralValueReference.CreateObjectLiteral (ctx, expression, false);
if (op == SyntaxKind.NotEqualsExpression)
return LiteralValueReference.CreateObjectLiteral (ctx, expression, true);
}
ValueReference nullable = ctx.Adapter.NullableGetValue (ctx, type1, val1);
targetVal1 = nullable.Value;
val1 = nullable.ObjectValue;
type1 = nullable.Type;
}
if (ctx.Adapter.IsNullableType (ctx, type2) && ctx.Adapter.NullableHasValue (ctx, type2, val2)) {
if (val1 == null) {
if (op == SyntaxKind.EqualsExpression)
return LiteralValueReference.CreateObjectLiteral (ctx, expression, false);
if (op == SyntaxKind.NotEqualsExpression)
return LiteralValueReference.CreateObjectLiteral (ctx, expression, true);
}
ValueReference nullable = ctx.Adapter.NullableGetValue (ctx, type2, val2);
targetVal2 = nullable.Value;
val2 = nullable.ObjectValue;
type2 = nullable.Type;
}
if (val1 is string || val2 is string) {
switch (op) {
case SyntaxKind.AddExpression:
if (val1 != null && val2 != null) {
if (!(val1 is string))
val1 = ctx.Adapter.CallToString (ctx, targetVal1);
if (!(val2 is string))
val2 = ctx.Adapter.CallToString (ctx, targetVal2);
res = (string) val1 + (string) val2;
} else if (val1 != null) {
res = val1.ToString ();
} else if (val2 != null) {
res = val2.ToString ();
}
return LiteralValueReference.CreateObjectLiteral (ctx, expression, res);
case SyntaxKind.EqualsExpression:
if ((val1 == null || val1 is string) && (val2 == null || val2 is string))
return LiteralValueReference.CreateObjectLiteral (ctx, expression, ((string) val1) == ((string) val2));
break;
case SyntaxKind.NotEqualsExpression:
if ((val1 == null || val1 is string) && (val2 == null || val2 is string))
return LiteralValueReference.CreateObjectLiteral (ctx, expression, ((string) val1) != ((string) val2));
break;
}
}
if (val1 == null || (!ctx.Adapter.IsPrimitive (ctx, targetVal1) && !ctx.Adapter.IsEnum (ctx, targetVal1))) {
switch (op) {
case SyntaxKind.EqualsExpression:
return LiteralValueReference.CreateObjectLiteral (ctx, expression, CheckEquality (ctx, false, type1, type2, targetVal1, targetVal2, val1, val2));
case SyntaxKind.NotEqualsExpression:
return LiteralValueReference.CreateObjectLiteral (ctx, expression, CheckEquality (ctx, true, type1, type2, targetVal1, targetVal2, val1, val2));
default:
if (val1 != null && val2 != null)
return EvaluateOverloadedOperator (ctx, expression, op, type1, type2, targetVal1, targetVal2, val1, val2);
break;
}
}
if ((val1 is bool) && (val2 is bool)) {
switch (op) {
case SyntaxKind.ExclusiveOrExpression:
return LiteralValueReference.CreateObjectLiteral (ctx, expression, (bool) val1 ^ (bool) val2);
case SyntaxKind.EqualsExpression:
return LiteralValueReference.CreateObjectLiteral (ctx, expression, (bool) val1 == (bool) val2);
case SyntaxKind.NotEqualsExpression:
return LiteralValueReference.CreateObjectLiteral (ctx, expression, (bool) val1 != (bool) val2);
}
}
if (val1 == null || val2 == null || (val1 is bool) || (val2 is bool))
throw ParseError ("Invalid operands in binary operator.");
var commonType = GetCommonOperationType (val1, val2);
if (commonType == typeof (double)) {
double v1, v2;
try {
v1 = Convert.ToDouble (val1);
v2 = Convert.ToDouble (val2);
} catch {
throw ParseError ("Invalid operands in binary operator.");
}
res = EvaluateOperation (op, v1, v2);
} else {
var v1 = ConvertToInt64 (val1);
var v2 = ConvertToInt64 (val2);
res = EvaluateOperation (op, v1, v2);
}
if (!(res is bool) && !(res is string)) {
if (ctx.Adapter.IsEnum (ctx, targetVal1)) {
object tval = ctx.Adapter.Cast (ctx, ctx.Adapter.CreateValue (ctx, res), ctx.Adapter.GetValueType (ctx, targetVal1));
return LiteralValueReference.CreateTargetObjectLiteral (ctx, expression, tval);
}
if (ctx.Adapter.IsEnum (ctx, targetVal2)) {
object tval = ctx.Adapter.Cast (ctx, ctx.Adapter.CreateValue (ctx, res), ctx.Adapter.GetValueType (ctx, targetVal2));
return LiteralValueReference.CreateTargetObjectLiteral (ctx, expression, tval);
}
var targetType = GetCommonType (val1, val2);
if (targetType != typeof (IntPtr))
res = Convert.ChangeType (res, targetType);
else
res = new IntPtr ((long) res);
}
return LiteralValueReference.CreateObjectLiteral (ctx, expression, res);
}
static object[] UpdateDelayedTypes (object[] types, Tuple<int, object>[] updates, ref bool alreadyUpdated)
{
if (alreadyUpdated || types == null || updates == null || types.Length < updates.Length || updates.Length == 0)
return types;
for (int x = 0; x < updates.Length; x++) {
int index = updates[x].Item1;
types[index] = updates[x].Item2;
}
alreadyUpdated = true;
return types;
}
#region IAstVisitor implementation
public override ValueReference VisitArrayCreationExpression (ArrayCreationExpressionSyntax node)
{
var type = Visit(node.Type.ElementType);
if (type == null)
throw ParseError ("Invalid type in array creation.");
var lengths = Array.Empty<int>();
if (node.Type is ArrayTypeSyntax ats) {
lengths = new int[ats.RankSpecifiers[0].Sizes.Count];
for (int i = 0; i < lengths.Length; i++) {
var arsi = ats.RankSpecifiers[0].Sizes[i];
lengths [i] = (int)Convert.ChangeType (Visit(arsi).ObjectValue, typeof (int));
}
}
var array = ctx.Adapter.CreateArray (ctx, type.Type, lengths);
if (node.Initializer?.Expressions.Count > 0) {
var arrayAdaptor = ctx.Adapter.CreateArrayAdaptor (ctx, array);
int index = 0;
foreach (var el in LinearElements(node.Initializer.Expressions)) {
arrayAdaptor.SetElement (new int [] { index++ }, Visit(el).Value);
}
}
return LiteralValueReference.CreateTargetObjectLiteral (ctx, expression, array);
}
IEnumerable<ExpressionSyntax> LinearElements (SeparatedSyntaxList<ExpressionSyntax> elements)
{
foreach (var el in elements) {
if (el is ArrayCreationExpressionSyntax arrCre) {
foreach (var el2 in LinearElements (arrCre.Initializer.Expressions)) {
yield return el2;
}
} else if (el is InitializerExpressionSyntax ies) {
foreach (var el2 in LinearElements (ies.Expressions)) {
yield return el2;
}
} else
yield return el;
}
}
public override ValueReference VisitAssignmentExpression (AssignmentExpressionSyntax node)
{
if (!options.AllowMethodEvaluation)
throw new ImplicitEvaluationDisabledException ();
var left = Visit (node.Left);
if (node.Kind () == SyntaxKind.SimpleAssignmentExpression) {
var right = Visit (node.Right);
if (left is UserVariableReference) {
left.Value = right.Value;
} else {
var castedValue = ctx.Adapter.TryCast (ctx, right.Value, left.Type);
left.Value = castedValue;
}
} else {
SyntaxKind op;
switch (node.Kind ()) {
case SyntaxKind.AddAssignmentExpression: op = SyntaxKind.AddExpression; break;
case SyntaxKind.SubtractAssignmentExpression: op = SyntaxKind.SubtractExpression; break;
case SyntaxKind.MultiplyAssignmentExpression: op = SyntaxKind.MultiplyExpression; break;
case SyntaxKind.DivideAssignmentExpression: op = SyntaxKind.DivideExpression; break;
case SyntaxKind.ModuloAssignmentExpression: op = SyntaxKind.ModuloExpression; break;
case SyntaxKind.LeftShiftAssignmentExpression: op = SyntaxKind.LeftShiftExpression; break;
case SyntaxKind.RightShiftAssignmentExpression: op = SyntaxKind.RightShiftExpression; break;
case SyntaxKind.AndAssignmentExpression: op = SyntaxKind.BitwiseAndExpression; break;
case SyntaxKind.OrAssignmentExpression: op = SyntaxKind.BitwiseOrExpression; break;
case SyntaxKind.ExclusiveOrAssignmentExpression: op = SyntaxKind.ExclusiveOrExpression; break;
default: throw ParseError ("Invalid operator in assignment.");
}
var result = EvaluateBinaryOperatorExpression (op, left, node.Right);
left.Value = result.Value;
}
return left;
}
public override ValueReference VisitBaseExpression (BaseExpressionSyntax node)
{
var self = ctx.Adapter.GetThisReference (ctx);
if (self != null)
return LiteralValueReference.CreateTargetBaseObjectLiteral (ctx, expression, self.Value);
throw ParseError ("'base' reference not available in static methods.");
}
public override ValueReference VisitBinaryExpression (BinaryExpressionSyntax node)
{
if (node.IsKind (SyntaxKind.AsExpression)) {
var type = Visit (node.Right) as TypeValueReference;
if (type == null)
throw ParseError ("Invalid type in cast.");
var val = Visit (node.Left);
var result = ctx.Adapter.TryCast (ctx, val.Value, type.Type);
if (result == null)
return new NullValueReference (ctx, type.Type);
return LiteralValueReference.CreateTargetObjectLiteral (ctx, expression, result, type.Type);
}
if (node.IsKind (SyntaxKind.IsExpression)) {
var type = (Visit (node.Right) as TypeValueReference)?.Type;
if (type == null)
throw ParseError ("Invalid type in 'is' expression.");
if (ctx.Adapter.IsNullableType (ctx, type))
type = ctx.Adapter.GetGenericTypeArguments (ctx, type).Single ();
var val = Visit (node.Left).Value;
if (ctx.Adapter.IsNull (ctx, val))
return LiteralValueReference.CreateObjectLiteral (ctx, expression, false);
var valueIsPrimitive = ctx.Adapter.IsPrimitive (ctx, val);
var typeIsPrimitive = ctx.Adapter.IsPrimitiveType (type);
if (valueIsPrimitive != typeIsPrimitive)
return LiteralValueReference.CreateObjectLiteral (ctx, expression, false);
if (typeIsPrimitive)
return LiteralValueReference.CreateObjectLiteral (ctx, expression, ctx.Adapter.GetTypeName (ctx, type) == ctx.Adapter.GetValueTypeName (ctx, val));
return LiteralValueReference.CreateObjectLiteral (ctx, expression, ctx.Adapter.TryCast (ctx, val, type) != null);
}
var left = this.Visit (node.Left);
return EvaluateBinaryOperatorExpression (node.Kind (), left, node.Right);
}
public override ValueReference VisitCastExpression (CastExpressionSyntax node)
{
var type = Visit(node.Type) as TypeValueReference;
if (type == null)
throw ParseError ("Invalid type in cast.");
var val = Visit(node.Expression);
object result = ctx.Adapter.TryCast (ctx, val.Value, type.Type);
if (result == null)
throw ParseError ("Invalid cast.");
return LiteralValueReference.CreateTargetObjectLiteral (ctx, expression, result, type.Type);
}
public override ValueReference VisitCheckedExpression (CheckedExpressionSyntax node)
{
throw NotSupported ();
}
public override ValueReference VisitConditionalExpression (ConditionalExpressionSyntax node)
{
ValueReference val = Visit(node.Condition);
if (val is TypeValueReference)
throw NotSupported ();
if ((bool)val.ObjectValue)
return Visit (node.WhenTrue);;
return Visit (node.WhenFalse);
}
public override ValueReference VisitDefaultExpression (DefaultExpressionSyntax node)
{
var type = Visit(node.Type) as TypeValueReference;
if (type == null)
throw ParseError ("Invalid type in 'default' expression.");
if (ctx.Adapter.IsClass (ctx, type.Type))
return LiteralValueReference.CreateTargetObjectLiteral (ctx, expression, ctx.Adapter.CreateNullValue (ctx, type.Type), type.Type);
if (ctx.Adapter.IsValueType (type.Type))
return LiteralValueReference.CreateTargetObjectLiteral (ctx, expression, ctx.Adapter.CreateValue (ctx, type.Type, new object [0]), type.Type);
switch (ctx.Adapter.GetTypeName (ctx, type.Type)) {
case "System.Boolean": return LiteralValueReference.CreateObjectLiteral (ctx, expression, false);
case "System.Char": return LiteralValueReference.CreateObjectLiteral (ctx, expression, '\0');
case "System.Byte": return LiteralValueReference.CreateObjectLiteral (ctx, expression, (byte) 0);
case "System.SByte": return LiteralValueReference.CreateObjectLiteral (ctx, expression, (sbyte) 0);
case "System.Int16": return LiteralValueReference.CreateObjectLiteral (ctx, expression, (short) 0);
case "System.UInt16": return LiteralValueReference.CreateObjectLiteral (ctx, expression, (ushort) 0);
case "System.Int32": return LiteralValueReference.CreateObjectLiteral (ctx, expression, (int) 0);
case "System.UInt32": return LiteralValueReference.CreateObjectLiteral (ctx, expression, (uint) 0);
case "System.Int64": return LiteralValueReference.CreateObjectLiteral (ctx, expression, (long) 0);
case "System.UInt64": return LiteralValueReference.CreateObjectLiteral (ctx, expression, (ulong) 0);
case "System.Decimal": return LiteralValueReference.CreateObjectLiteral (ctx, expression, (decimal) 0);
case "System.Single": return LiteralValueReference.CreateObjectLiteral (ctx, expression, (float) 0);
case "System.Double": return LiteralValueReference.CreateObjectLiteral (ctx, expression, (double) 0);
default: throw new Exception ($"Unexpected type {ctx.Adapter.GetTypeName (ctx, type.Type)}");
}
}
public override ValueReference VisitIdentifierName (IdentifierNameSyntax node)
{
var name = node.Identifier.ValueText;
if (name == "__EXCEPTION_OBJECT__")
return ctx.Adapter.GetCurrentException (ctx);
// Look in user defined variables
ValueReference userVar;
if (userVariables.TryGetValue (name, out userVar))
return userVar;
// Look in variables
ValueReference var = ctx.Adapter.GetLocalVariable (ctx, name);
if (var != null)
return var;
// Look in parameters
var = ctx.Adapter.GetParameter (ctx, name);
if (var != null)
return var;
// Look in instance fields and properties
ValueReference self = ctx.Adapter.GetThisReference (ctx);
if (self != null) {
// check for fields and properties in this instance
// first try if current type has field or property
var = ctx.Adapter.GetMember (ctx, self, ctx.Adapter.GetEnclosingType (ctx), self.Value, name);
if (var != null)
return var;
var = ctx.Adapter.GetMember (ctx, self, self.Type, self.Value, name);
if (var != null)
return var;
}
// Look in static fields & properties of the enclosing type and all parent types
object type = ctx.Adapter.GetEnclosingType (ctx);
object vtype = type;
while (vtype != null) {
// check for static fields and properties
var = ctx.Adapter.GetMember (ctx, null, vtype, null, name);
if (var != null)
return var;
vtype = ctx.Adapter.GetParentType (ctx, vtype);
}
// Look in types
vtype = ctx.Adapter.GetType (ctx, name);
if (vtype != null)
return new TypeValueReference (ctx, vtype);
if (self == null && ctx.Adapter.HasMember (ctx, type, name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) {
string message = string.Format ("An object reference is required for the non-static field, method, or property '{0}.{1}'.",
ctx.Adapter.GetDisplayTypeName (ctx, type), name);
throw ParseError (message);
}
// Assume a namespace
return new NamespaceValueReference(ctx, name);
}
public override ValueReference VisitElementAccessExpression (ElementAccessExpressionSyntax node)
{
int n = 0;
var target = Visit(node.Expression);
if (target is TypeValueReference)
throw NotSupported ();
if (ctx.Adapter.IsArray (ctx, target.Value)) {
int[] indexes = new int [node.ArgumentList.Arguments.Count];
foreach (var arg in node.ArgumentList.Arguments) {
var index = Visit(arg);
indexes[n++] = (int) Convert.ChangeType (index.ObjectValue, typeof (int));
}
return new ArrayValueReference (ctx, target.Value, indexes);
}
object[] args = new object [node.ArgumentList.Arguments.Count];
foreach (var arg in node.ArgumentList.Arguments)
args[n++] = Visit (arg).Value;
var indexer = ctx.Adapter.GetIndexerReference (ctx, target.Value, target.Type, args);
if (indexer == null)
throw NotSupported ();
return indexer;
}
string ResolveMethodName (SyntaxNode invocationExpression, out object[] typeArgs)
{
if (invocationExpression is IdentifierNameSyntax id) {
typeArgs = null;
return id.Identifier.ValueText;
}
if (invocationExpression is GenericNameSyntax gns) {
if (gns.Arity > 0) {
var args = new List<object> ();
foreach (var arg in gns.TypeArgumentList.Arguments) {
var type = Visit(arg);
args.Add (type.Type);
}
typeArgs = args.ToArray ();
} else {
typeArgs = null;
}
return gns.Identifier.ValueText;
}
typeArgs = null;
var fullName = invocationExpression.ToString();
var lastIndexOfDot = fullName.LastIndexOf(".");
return fullName.Substring(lastIndexOfDot + 1);
}
public override ValueReference VisitInvocationExpression (InvocationExpressionSyntax node)
{
if (!options.AllowMethodEvaluation)
throw new ImplicitEvaluationDisabledException ();
bool invokeBaseMethod = false;
bool allArgTypesAreResolved = true;
ValueReference target = null;
string methodName;
var types = new object [node.ArgumentList.Arguments.Count];
var args = new object [node.ArgumentList.Arguments.Count];
object[] typeArgs = null;
int n = 0;
foreach (var arg in node.ArgumentList.Arguments) {
var vref = this.Visit (arg);
args[n] = vref.Value;
types[n] = ctx.Adapter.GetValueType (ctx, args[n]);
if (ctx.Adapter.IsDelayedType (ctx, types[n]))
allArgTypesAreResolved = false;
n++;
}
object vtype = null;
Tuple<int, object>[] resolvedLambdaTypes;
if (node.Expression is MemberAccessExpressionSyntax field) {
target = Visit (field.Expression);
if (field.Expression is BaseExpressionSyntax)
invokeBaseMethod = true;
methodName = ResolveMethodName (field, out typeArgs);
} else if (node.Expression is SyntaxNode method && (method is IdentifierNameSyntax || method is GenericNameSyntax)) {
var vref = ctx.Adapter.GetThisReference (ctx);
methodName = ResolveMethodName (method, out typeArgs);
if (vref != null && ctx.Adapter.HasMethod (ctx, vref.Type, methodName, typeArgs, types, BindingFlags.Instance)) {
vtype = ctx.Adapter.GetEnclosingType (ctx);
// There is an instance method for 'this', although it may not have an exact signature match. Check it now.
if (ctx.Adapter.HasMethod (ctx, vref.Type, methodName, typeArgs, types, BindingFlags.Instance)) {
target = vref;
} else {
// There isn't an instance method with exact signature match.
// If there isn't a static method, then use the instance method,
// which will report the signature match error when invoked
if (!ctx.Adapter.HasMethod (ctx, vtype, methodName, typeArgs, types, BindingFlags.Static))
target = vref;
}
} else {
if (ctx.Adapter.HasMethod (ctx, ctx.Adapter.GetEnclosingType (ctx), methodName, types, BindingFlags.Instance))
throw new EvaluatorException ("Cannot invoke an instance method from a static method.");
target = null;
}
} else {
throw NotSupported ();
}
if (vtype == null)
vtype = target != null ? target.Type : ctx.Adapter.GetEnclosingType (ctx);
object vtarget = (target is TypeValueReference) || target == null ? null : target.Value;
var hasMethod = ctx.Adapter.HasMethod (ctx, vtype, methodName, typeArgs, types, BindingFlags.Instance | BindingFlags.Static, out resolvedLambdaTypes);
if (hasMethod)
types = UpdateDelayedTypes (types, resolvedLambdaTypes, ref allArgTypesAreResolved);
if (invokeBaseMethod) {
vtype = ctx.Adapter.GetBaseType (ctx, vtype);
} else if (target != null && !hasMethod) {
// Look for LINQ extension methods...
var linq = ctx.Adapter.GetType (ctx, "System.Linq.Enumerable");
if (linq != null) {
object[] xtypeArgs = typeArgs;
if (xtypeArgs == null) {
// try to infer the generic type arguments from the type of the object...
object xtype = vtype;
while (xtype != null && !ctx.Adapter.IsGenericType (ctx, xtype))
xtype = ctx.Adapter.GetBaseType (ctx, xtype);
if (xtype != null)
xtypeArgs = ctx.Adapter.GetTypeArgs (ctx, xtype);
}
if (xtypeArgs == null && ctx.Adapter.IsArray (ctx, vtarget)) {
xtypeArgs = new object [] { ctx.Adapter.CreateArrayAdaptor (ctx, vtarget).ElementType };
}
if (xtypeArgs != null) {
var xtypes = new object[types.Length + 1];
Array.Copy (types, 0, xtypes, 1, types.Length);
xtypes[0] = vtype;
var xargs = new object[args.Length + 1];
Array.Copy (args, 0, xargs, 1, args.Length);
xargs[0] = vtarget;
if (ctx.Adapter.HasMethod (ctx, linq, methodName, xtypeArgs, xtypes, BindingFlags.Static, out resolvedLambdaTypes)) {
vtarget = null;
vtype = linq;
typeArgs = xtypeArgs;
types = UpdateDelayedTypes (xtypes, resolvedLambdaTypes, ref allArgTypesAreResolved);
args = xargs;
}
}
}
}
if (!allArgTypesAreResolved) {
// TODO: Show detailed error message for why lambda types were not
// resolved. Major causes are:
// 1. there is no matched method
// 2. matched method exists, but the lambda body has some invalid
// expressions and does not compile
throw NotSupported ();
}
object result = ctx.Adapter.RuntimeInvoke (ctx, vtype, vtarget, methodName, typeArgs, types, args);
if (result != null)
return LiteralValueReference.CreateTargetObjectLiteral (ctx, expression, result);
return LiteralValueReference.CreateVoidReturnLiteral (ctx, expression);
}
public override ValueReference VisitParenthesizedLambdaExpression (ParenthesizedLambdaExpressionSyntax node)
{
return VisitLambdaExpression(node);
}
public override ValueReference VisitSimpleLambdaExpression (SimpleLambdaExpressionSyntax node)
{
return VisitLambdaExpression (node);
}
ValueReference VisitLambdaExpression (LambdaExpressionSyntax node)
{
if (node.AsyncKeyword != default)
throw NotSupported ();
var parent = node.Parent;
while (parent != null && parent is ParenthesizedExpressionSyntax)
parent = parent.Parent;
if (parent is InvocationExpressionSyntax || parent is CastExpressionSyntax || parent is ArgumentSyntax) {
var visitor = new LambdaBodyOutputVisitor (ctx, userVariables);
visitor.Visit (node);
var values = visitor.GetLocalValues ();
object val = ctx.Adapter.CreateDelayedLambdaValue (ctx, node.ToString (), values);
if (val != null)
return LiteralValueReference.CreateTargetObjectLiteral (ctx, expression, val);
}
throw NotSupported ();
}
public override ValueReference VisitMemberAccessExpression (MemberAccessExpressionSyntax node)
{
if (node.Name is GenericNameSyntax gns) {
var expr = Visit (node.Expression);
if (expr is TypeValueReference tvr && node.Expression is GenericNameSyntax gnsOuter) {
// Thing<string>.Done<int> is
// Thing`1.Done`1 with type arguments System.String and System.Int32
// Thing`1 is node.Expression
// Done`1 is node.Name
var outer = MakeGenericTypeName (gnsOuter, gnsOuter.Identifier.ValueText);
var typeArgsOuter = GetGenericTypeArgs(gnsOuter);
var inner = MakeGenericTypeName(gns, gns.Identifier.ValueText);
var typeArgsInner = GetGenericTypeArgs(gns);
string nestedTypeName = $"{outer}.{inner}";
var nestedType = ctx.Adapter.GetType(ctx, nestedTypeName, typeArgsOuter.Concat(typeArgsInner).ToArray());
return new TypeValueReference(ctx, nestedType);
}
var typeArgs = GetGenericTypeArgs (gns);
string typeName = $"{ResolveTypeName (node)}`{gns.TypeArgumentList.Arguments.Count}";
var type1 = ctx.Adapter.GetType (ctx, typeName, typeArgs);
return new TypeValueReference (ctx, type1);
}
if (node.Name is IdentifierNameSyntax)
{
var name = ResolveTypeName(node);
var type = ctx.Adapter.GetType(ctx, name);
if (type != null)
return new TypeValueReference(ctx, type);
}
var target = Visit (node.Expression);
var member = target.GetChild(node.Name.Identifier.ValueText, ctx.Options);
if (member != null)
return member;
if (!(target is TypeValueReference)) {
if (ctx.Adapter.IsNull(ctx, target.Value))
throw new EvaluatorException("{0} is null", target.Name);
}
throw new EvaluatorException("{0} is null", target.Name);
}
object [] GetGenericTypeArgs (GenericNameSyntax gns)
{
object [] typeArgs = new object [gns.TypeArgumentList.Arguments.Count];
for (var i = 0; i < gns.TypeArgumentList.Arguments.Count; i++) {
var typeArg = Visit (gns.TypeArgumentList.Arguments [i]);
typeArgs [i] = typeArg.Type;
}
return typeArgs;
}
public override ValueReference VisitLiteralExpression (LiteralExpressionSyntax node)
{
if (node.Kind() == SyntaxKind.NullLiteralExpression)
return new NullValueReference (ctx, ctx.Adapter.GetType (ctx, "System.Object"));
return base.VisitLiteralExpression (node);
}
public override ValueReference VisitObjectCreationExpression (ObjectCreationExpressionSyntax node)
{
var type = Visit(node.Type) as TypeValueReference;
var args = new List<object> ();
foreach (var arg in node.ArgumentList.Arguments) {
var val = Visit(arg);
args.Add (val != null ? val.Value : null);
}
return LiteralValueReference.CreateTargetObjectLiteral (ctx, expression, ctx.Adapter.CreateValue (ctx, type.Type, args.ToArray ()));
}
public override ValueReference VisitParenthesizedExpression (ParenthesizedExpressionSyntax node)
{
return Visit (node.Expression);
}
public override ValueReference VisitPredefinedType (PredefinedTypeSyntax node)
{
var type = ctx.Adapter.GetType(ctx, node.Resolve());
return new TypeValueReference (ctx, type);
}
public override ValueReference VisitThisExpression (ThisExpressionSyntax node)
{
var self = ctx.Adapter.GetThisReference (ctx);
if (self == null)
throw ParseError ("'this' reference not available in the current evaluation context.");
return self;
}
public override ValueReference VisitTypeOfExpression (TypeOfExpressionSyntax node)
{
var name = ResolveTypeName (node.Type);
var type = ctx.Adapter.GetType(ctx, name);
if (type == null)
throw ParseError ("Could not load type: {0}", name);
object result = ctx.Adapter.CreateTypeObject (ctx, type);
if (result == null)
throw NotSupported ();
return LiteralValueReference.CreateTargetObjectLiteral (ctx, name, result);
}
public override ValueReference VisitPostfixUnaryExpression (PostfixUnaryExpressionSyntax node)
{
var vref = Visit (node.Operand);
var val = vref.ObjectValue;
object newVal;
long num;
switch (node.Kind ()) {
case SyntaxKind.PostDecrementExpression:
if (val is decimal) {
newVal = ((decimal)val) - 1;
} else if (val is double) {
newVal = ((double)val) - 1;
} else if (val is float) {
newVal = ((float)val) - 1;
} else {
num = GetInteger (val) - 1;
newVal = Convert.ChangeType (num, val.GetType ());
}
vref.Value = ctx.Adapter.CreateValue (ctx, newVal);
break;
case SyntaxKind.PostIncrementExpression:
if (val is decimal) {
newVal = ((decimal)val) + 1;
} else if (val is double) {
newVal = ((double)val) + 1;
} else if (val is float) {
newVal = ((float)val) + 1;
} else {
num = GetInteger (val) + 1;
newVal = Convert.ChangeType (num, val.GetType ());
}
vref.Value = ctx.Adapter.CreateValue (ctx, newVal);
break;
default:
throw NotSupported ();
}
return LiteralValueReference.CreateObjectLiteral (ctx, expression, val);
}
public override ValueReference VisitPrefixUnaryExpression (PrefixUnaryExpressionSyntax node)
{
var vref = Visit (node.Operand);
var val = vref.ObjectValue;
object newVal;
long num;
switch (node.Kind ()) {
case SyntaxKind.BitwiseNotExpression:
num = ~GetInteger (val);
val = Convert.ChangeType (num, val.GetType ());
break;
case SyntaxKind.UnaryMinusExpression:
if (val is decimal) {
val = -(decimal)val;
} else if (val is double) {
val = -(double)val;
} else if (val is float) {
val = -(float)val;
} else {
num = -GetInteger (val);
val = Convert.ChangeType (num, val.GetType ());
}
break;
case SyntaxKind.LogicalNotExpression:
if (!(val is bool))
throw ParseError ("Expected boolean type in Not operator.");
val = !(bool)val;
break;
case SyntaxKind.PreDecrementExpression:
if (val is decimal) {
val = ((decimal)val) - 1;
} else if (val is double) {
val = ((double)val) - 1;
} else if (val is float) {
val = ((float)val) - 1;
} else {
num = GetInteger (val) - 1;
val = Convert.ChangeType (num, val.GetType ());
}
vref.Value = ctx.Adapter.CreateValue (ctx, val);
break;
case SyntaxKind.PreIncrementExpression:
if (val is decimal) {
val = ((decimal)val) + 1;
} else if (val is double) {
val = ((double)val) + 1;
} else if (val is float) {
val = ((float)val) + 1;
} else {
num = GetInteger (val) + 1;
val = Convert.ChangeType (num, val.GetType ());
}
vref.Value = ctx.Adapter.CreateValue (ctx, val);
break;
case SyntaxKind.UnaryPlusExpression:
break;
default:
throw NotSupported ();
}
return LiteralValueReference.CreateObjectLiteral (ctx, expression, val);
}
public override ValueReference VisitArgument (ArgumentSyntax node)
{
return this.Visit(node.Expression);
}
public override ValueReference VisitAliasQualifiedName(AliasQualifiedNameSyntax node)
{
//return null;
return this.Visit(node.Name);
}
public override ValueReference VisitNullableType (NullableTypeSyntax node)
{
var genericTypeArgument = new[] { Visit(node.ElementType).Type };
var type1 = ctx.Adapter.GetType(ctx, "System.Nullable`1", genericTypeArgument);
return new TypeValueReference(ctx, type1);
}
public override ValueReference VisitGenericName (GenericNameSyntax node)
{
object [] typeArgs = new object [node.TypeArgumentList.Arguments.Count];
for (var i = 0; i < node.TypeArgumentList.Arguments.Count; i++) {
var typeArg = Visit (node.TypeArgumentList.Arguments [i]);
typeArgs [i] = typeArg.Type;
}
string typeName = node.Identifier.ValueText;
typeName = MakeGenericTypeName(node, typeName);// $"{typeName}`{node.TypeArgumentList.Arguments.Count}";
var type1 = ctx.Adapter.GetType (ctx, typeName, typeArgs);
return new TypeValueReference (ctx, type1);
}
static string MakeGenericTypeName(GenericNameSyntax node, string typeName)
{
if (node.Parent is QualifiedNameSyntax qns && qns.Left is IdentifierNameSyntax ins)
return $"{ins.Identifier.Value}.{typeName}`{node.TypeArgumentList.Arguments.Count}";
if (node.Parent is QualifiedNameSyntax qns2 && qns2.Left is QualifiedNameSyntax left)
return $"{left}.{typeName}`{node.TypeArgumentList.Arguments.Count}";
return $"{typeName}`{node.TypeArgumentList.Arguments.Count}";
;
}
public override ValueReference VisitQualifiedName(QualifiedNameSyntax node)
{
if(node.Right is GenericNameSyntax) {
return Visit(node.Right);
}
var type1 = ctx.Adapter.GetType(ctx, node.ToString());
return new TypeValueReference(ctx, type1);
}
public override ValueReference DefaultVisit (SyntaxNode node)
{
if (node is LiteralExpressionSyntax syntax)
{
return LiteralValueReference.CreateObjectLiteral(ctx, expression, syntax.Token.Value);
}
throw NotSupported();
}
#endregion
}*/
}