public Expression CalculateScriptFunctionCall(ScriptFunctionCall scriptFunctionCall, ParameterFinder parameterFinder, List <Expression> arguments) { var args = scriptFunctionCall.Arguments.Select(arg => GetExpressionBody(arg, parameterFinder, null)); //add first argument if it's being supplied (probably from ScriptPipeCall) if (arguments != null) { args = arguments.Union(args); } // we are attempting to pull the bottom target member up, so we know the method Name var toMember = scriptFunctionCall.Target as ScriptMemberExpression; if (toMember == null) { throw new NotSupportedException(); } else { var argumentTypes = args.ToNullSafe().Select(e => e.Type); var targetType = GetExpressionBody(toMember.Target, parameterFinder, null); ScriptVariable functionNameScript = toMember.Member; var methodInfo = memberFinder.FindMember(targetType.Type, functionNameScript.Name, argumentTypes.ToArray()); var convertedArgs = ConvertArgs(methodInfo as MethodInfo, args); return(ExpressionHelpers.CallMember(targetType, methodInfo, convertedArgs)); } }
public void FindGenericMultiMethod() { Type stringType = typeof(string); var abcConst = Expression.Constant("abc"); var argArray = new[] { stringType, stringType, stringType }; var result = memberFinder.FindMember(typeof(MemberCase), "PassIn", argArray); result.ShouldNotBeNull(); ExpressionHelpers.CallMember(Expression.Constant(new MemberCase()), result, new[] { abcConst, abcConst, abcConst }); }
public void FindGenericMethod() { Type stringType = typeof(string); var argArray = new[] { stringType }; var result = memberFinder.FindMember(typeof(MemberCase), "PassThrough", argArray); result.ShouldBeAssignableTo <MethodInfo>(); result.ShouldNotBeNull(); var abcconst = Expression.Constant("abc"); ExpressionHelpers.CallMember(Expression.Constant(new MemberCase()), result, new[] { abcconst }); }
public Expression GetExpressionBody(ScriptExpression scriptExpression, ParameterFinder parameterFinder, List <Expression> arguments) { switch (scriptExpression) { case ScriptVariableGlobal scriptVariableGlobal: var variable = parameterFinder.GetProperty(scriptVariableGlobal.Name); if (variable == null) { throw new SpanException($"Variable Not Found: {scriptVariableGlobal.Name}", scriptVariableGlobal.Span); } return(variable); case ScriptLiteral scriptLiteral: return(Expression.Constant(scriptLiteral.Value, scriptLiteral.Value.GetType())); case ScriptUnaryExpression scriptUnaryExpression: switch (scriptUnaryExpression.Operator) { case ScriptUnaryOperator.Not: var right = GetExpressionBody(scriptUnaryExpression.Right, parameterFinder, arguments); return(Expression.Not(right)); default: throw new SpanException($"Unknown ScriptUnaryOperator: {scriptUnaryExpression.Operator}", scriptUnaryExpression.Span); } case ScriptMemberExpression scriptMemberExpression: // it's impossible to tell if we have a member or a method, so we check for both var memberTarget = GetExpressionBody(scriptMemberExpression.Target, parameterFinder, null); var memberName = scriptMemberExpression.Member.Name; // TODO: we should remove the need to calculate a method with Args here, should not need to pass down info // still need the argument list as ScriptPipeCall still needs to pass the args in var argumentTypeList = arguments.ToNullSafe().Select(e => e.Type); var methodInfo = memberFinder.FindMember(memberTarget.Type, memberName, argumentTypeList); if (methodInfo != null) { var convertedArgs = ConvertArgs(methodInfo as MethodInfo, arguments); return(ExpressionHelpers.CallMember(memberTarget, methodInfo, convertedArgs)); } throw new SpanException($"Member Not Found: {memberName}", scriptMemberExpression.Span); case ScriptPipeCall scriptPipeCall: // I'm not a huge fan of this because it requires pushing args down to sub nodes, could cause issues with multi funtions tree var fromExpression = GetExpressionBody(scriptPipeCall.From, parameterFinder, null); // prepare args (input type + ScriptFunctionCall args ) var pipelineArgs = new List <Expression> { fromExpression }; switch (scriptPipeCall.To) { case ScriptFunctionCall scriptFunctionCall: pipelineArgs.AddRange(scriptFunctionCall.Arguments.Select(arg => GetExpressionBody(arg, parameterFinder, null))); return(GetExpressionBody(scriptFunctionCall.Target, parameterFinder, pipelineArgs)); case ScriptMemberExpression scriptMemberExpression: return(GetExpressionBody(scriptMemberExpression, parameterFinder, pipelineArgs)); case ScriptPipeCall toScriptPipeCall: var nestedfromExpression = GetExpressionBody(toScriptPipeCall.From, parameterFinder, pipelineArgs); return(GetExpressionBody(toScriptPipeCall.To, parameterFinder, new List <Expression> { nestedfromExpression })); default: throw new NotSupportedException("Pipeline Expression Not Supported"); } case ScriptFunctionCall scriptFunctionCall: return(CalculateScriptFunctionCall(scriptFunctionCall, parameterFinder, arguments)); case ScriptNestedExpression scriptNestedExpression: return(GetExpressionBody(scriptNestedExpression.Expression, parameterFinder, arguments)); case ScriptBinaryExpression scriptBinaryExpression: var leftExpression = GetExpressionBody(scriptBinaryExpression.Left, parameterFinder, null); var rightExpression = GetExpressionBody(scriptBinaryExpression.Right, parameterFinder, null); switch (scriptBinaryExpression.Operator) { case ScriptBinaryOperator.Add: leftExpression = ConvertIfNeeded(leftExpression, rightExpression.Type); rightExpression = ConvertIfNeeded(rightExpression, leftExpression.Type); return(Expression.Add(leftExpression, rightExpression)); case ScriptBinaryOperator.EmptyCoalescing: return(Expression.Coalesce(leftExpression, rightExpression)); case ScriptBinaryOperator.CompareEqual: return(Expression.Equal(leftExpression, rightExpression)); default: throw new SpanException($"Unknown ScriptBinaryExpression Operator: {scriptBinaryExpression.Operator}", scriptBinaryExpression.Span); } case ScriptIndexerExpression scriptIndexerExpression: //https://stackoverflow.com/questions/31924907/accessing-elements-of-types-with-indexers-using-expression-trees // TODO: consider wrapping an enumerable to an array var arrayTarget = GetExpressionBody(scriptIndexerExpression.Target, parameterFinder, null); var arrayIndex = GetExpressionBody(scriptIndexerExpression.Index, parameterFinder, null); var indexed = Expression.Property(arrayTarget, "Item", arrayIndex); return(indexed); default: throw new SpanException($"Unknown Expression Type: {scriptExpression?.GetType()}", scriptExpression.Span); } }