private static Expression BindFunctionCall(MethodInfo method, Type instanceType, bool instanceFunction, List <ParameterExpression> parameters, IEnumerable <Expression> arguments, LabelTarget returnLabel) { var returnType = method.ReturnType; Expression callExpr; if (instanceFunction && instanceType != null) { // instance functions store the instance in UserData var userData = Expression.Convert(Expression.PropertyOrField(parameters[1], "UserData"), instanceType); callExpr = Expression.Call(userData, method, arguments); } else { callExpr = Expression.Call(method, arguments); } if (returnType != typeof(void)) { var output = callExpr; Func <Expression, Expression> outputConversion; if (ConversionMap.TryGetValue(returnType, out outputConversion)) { output = outputConversion(output); } callExpr = Expression.Return(returnLabel, Expression.Convert(output, typeof(MondValue))); } return(callExpr); }
private static T BindImpl <T>(string moduleName, string methodName, MethodBase method, bool instanceFunction, BindCallFactory callFactory) { // TODO: clean up everything below this line var arguments = GetArguments(method, instanceFunction).ToArray(); var errorPrefix = string.Format("{0}{1}{2}: ", moduleName ?? "", moduleName != null ? "." : "", methodName); var parameters = new List <ParameterExpression> { Expression.Parameter(typeof(MondState), "state"), Expression.Parameter(typeof(MondValue[]), "arguments") }; if (instanceFunction) { parameters.Insert(1, Expression.Parameter(typeof(MondValue), "instance")); } var argumentsParam = parameters[instanceFunction ? 2 : 1]; Func <int, Expression> argumentIndex = i => Expression.ArrayIndex(argumentsParam, Expression.Constant(i)); var statements = new List <Expression>(); var returnLabel = Expression.Label(typeof(MondValue)); // argument count check var argLength = Expression.Condition(Expression.Equal(argumentsParam, Expression.Constant(null)), Expression.Constant(0), Expression.PropertyOrField(argumentsParam, "Length")); var requiredArgLength = arguments.Count(a => a.Index >= 0); var argLengthError = string.Format("{0}must be called with {1} argument{2}", errorPrefix, requiredArgLength, requiredArgLength != 1 ? "s" : ""); statements.Add(Expression.IfThen(Expression.NotEqual(argLength, Expression.Constant(requiredArgLength)), Throw(argLengthError))); // argument type checks for (var i = 0; i < arguments.Length; i++) { var arg = arguments[i]; if (arg.Index < 0 || arg.Type == typeof(MondValue) || arg.Type == typeof(MondState)) { continue; } statements.Add(TypeCheck(errorPrefix, i + 1, argumentIndex(arg.Index), arg.Type)); } // call var callArgs = new List <Expression>(); foreach (var arg in arguments) { if (arg.Type == typeof(MondState)) { callArgs.Add(parameters[0]); continue; } if (arg.Type == typeof(MondValue)) { if (instanceFunction && arg.Name == "instance") { callArgs.Add(parameters[1]); } else { callArgs.Add(argumentIndex(arg.Index)); } continue; } var input = argumentIndex(arg.Index); Func <Expression, Expression> inputConversion; if (ConversionMap.TryGetValue(arg.Type, out inputConversion)) { input = inputConversion(input); } callArgs.Add(Expression.Convert(input, arg.Type)); } statements.Add(callFactory(parameters, callArgs, returnLabel)); // end / default return statements.Add(Expression.Label(returnLabel, Expression.Constant(MondValue.Undefined))); var block = Expression.Block(statements); return(Expression.Lambda <T>(block, parameters).Compile()); }