// // NRefactoryExpressionEvaluatorVisitor.cs // // Author: Jeffrey Stedfast // // 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 { readonly Dictionary userVariables; readonly EvaluationOptions options; readonly EvaluationContext ctx; readonly object expectedType; readonly string expression; public NRefactoryExpressionEvaluatorVisitor (EvaluationContext ctx, string expression, object expectedType, Dictionary 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[] 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(); 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 LinearElements (SeparatedSyntaxList 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 (); 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[] 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.Done 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 (); 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 }*/ }