/// <summary> /// Visit a member access expression /// </summary> /// <param name="exp"></param> /// <returns></returns> protected EToken VisitMemberAccessExpression(MemberExpression exp) { EToken memberExp; if (exp.Expression == null) { // Método estáticos // TODO: El tipo ExpressionType.Unbox aquí es incorrecto, debería ser null memberExp = new EToken(exp.Type.FullName, ExpressionType.Unbox, null); } else { // Métodos en objetos memberExp = this.VisitExpression(exp.Expression, exp); } var memberName = exp.Member.Name; var memberType = this.GetUnderlyingType(exp.Member); if (memberExp.Type == ExpressionType.Constant) { // Nos permite evaluar sobre la marcha member accesors que SÍ podemos resolver if (memberExp.ClrType.IsNullable() && memberName == "Value") { // No hacer nada, ya que la llamada a Value() - que es un unboxing de un struct - // lo hace automáticamente el runtime de .Net } else { this.Arguments[memberExp.Text] = Dynamic.InvokeGet(this.Arguments[memberExp.Text], memberName); } return(new EToken(memberExp.Text, ExpressionType.Constant, memberType)); } if (memberExp.Type == ExpressionType.Unbox) { var expCallResult = ((FieldInfo)exp.Member).GetValue(null); return(new EToken(this.AddArgument(expCallResult), ExpressionType.Constant, memberType)); } return(new EToken($"{memberExp.Text}.{memberName}", exp.NodeType, memberType)); }
/// <summary> /// Visit a method call expression /// </summary> /// <param name="expCall"></param> /// <returns></returns> protected EToken VisitMethodCallExpression(MethodCallExpression expCall) { List <EToken> args = (from p in expCall.Arguments select this.VisitExpression(p, expCall)).ToList(); EToken expCallTargetExpression; if (expCall.Object == null) { // Here we should use FullName of declaring type, but a bug in Dynamic.Linq.Core prevents // usage of full namespaces and uses shortcut aliases only. // TODO: El tipo ExpressionType.Unbox aquí es incorrecto, debería ser null expCallTargetExpression = new EToken(expCall.Method?.DeclaringType?.Name, ExpressionType.Unbox, null); } else { // Métodos en objetos expCallTargetExpression = this.VisitExpression(expCall.Object, expCall); } bool canMaterializeMethodCall = args.All((i) => i.Type == ExpressionType.Constant) && (expCallTargetExpression.Type == ExpressionType.Constant || expCallTargetExpression.Type == ExpressionType.Unbox); if (canMaterializeMethodCall) { List <object> methodCallUnboxedArgs = new List <object>(); // Grab all arguments and remove them from the dictionary foreach (var arg in args) { methodCallUnboxedArgs.Add(this.PopArgument(arg.Text)); } object expCallResult; if (expCall.Object == null) { expCallResult = Dynamic.InvokeMember( InvokeContext.CreateStatic(expCall.Method?.DeclaringType), expCall.Method.Name, methodCallUnboxedArgs.ToArray()); } else { expCallResult = Dynamic.InvokeMember( this.PopArgument(expCallTargetExpression.Text), expCall.Method.Name, methodCallUnboxedArgs.ToArray()); } return(this.CheckMaterializationResult(expCallResult, expCall.Method.ReturnType)); } // If this method is LinqKit's Invoke() then we can do this ASAP because Dynamic.Linq.Core won't swallow complex method calls // We asume that the coder is looking for lazy evaluation if (expCall.Method.Name == "Invoke" && expCall.Method?.DeclaringType?.FullName.Contains("LinqKit") == true) { var targetOfInvoke = expCall.Arguments.First() as MethodCallExpression; var argumentsOfTargetInvoke = new List <object>(); foreach (var arg in targetOfInvoke.Arguments) { if (arg is ConstantExpression constant) { argumentsOfTargetInvoke.Add(constant.Value); } } // Materialize it! Currently only works with static calls var materializedTargetOfInvoke = Dynamic.InvokeMember( InvokeContext.CreateStatic(targetOfInvoke.Method?.DeclaringType), targetOfInvoke.Method.Name, argumentsOfTargetInvoke.ToArray()) as LambdaExpression; var result = this.VisitLambdaExpression(materializedTargetOfInvoke); // Replace the target source arguments for (int x = 0; x < materializedTargetOfInvoke.Parameters.Count; x++) { var expArg = materializedTargetOfInvoke.Parameters[x]; var externalArg = args[x + 1]; this.Parameters.Remove(expArg.Name); this.TempParameters.Remove(expArg.Name); result.Text = result.Text.Replace($"[{expArg.Name}]", externalArg.Text); } return(result); } return(new EToken($"{expCallTargetExpression}.{expCall.Method.Name}({args.StringJoinObject(", ")})", expCall.NodeType, expCall.Method.ReturnType)); }