/// <summary> /// Generates MondInstanceFunction bindings for the given methods. /// </summary> internal static IEnumerable <Tuple <string, MondInstanceFunction> > BindInstance( string className, ICollection <MethodInfo> methods, Type type = null, MethodType methodType = MethodType.Normal, string nameOverride = null) { if (className == null) { throw new ArgumentNullException(nameof(className)); } if (type == null && methods.Any(m => !m.IsStatic)) { throw new MondBindingException("BindInstance requires a type for non-static methods"); } var methodTables = BuildMethodTables((IEnumerable <MethodBase>)methods, methodType, nameOverride); foreach (var table in methodTables) { #if !NO_EXPRESSIONS BindCallFactory callFactory = (e, m, p, a, r) => BindFunctionCall(e, m, type, true, p, a, r); yield return(Tuple.Create(table.Name, BindImpl <MondInstanceFunction, MondValue>(className, table, true, callFactory))); #else yield return(Tuple.Create(table.Name, BindInstanceImpl(className, table, nameOverride, type == null))); #endif } }
private static TFunc BindImpl <TFunc, TReturn>( string moduleName, MethodTable methodTable, bool instanceFunction, BindCallFactory callFactory) { var errorPrefix = BindingError.ErrorPrefix(moduleName, methodTable.Name); 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]; var statements = new List <Expression>(); var returnLabel = Expression.Label(typeof(TReturn)); var argumentsLength = Expression.PropertyOrField(argumentsParam, "Length"); for (var i = 0; i < methodTable.Methods.Count; i++) { var dispatches = BuildDispatchExpression(errorPrefix, methodTable.Methods[i], i, parameters, instanceFunction, returnLabel, callFactory); if (dispatches.Count == 0) { continue; } var requiredArgCount = Expression.Constant(i); var argLengthEqual = Expression.Equal(argumentsLength, requiredArgCount); var argBranch = Expression.IfThen(argLengthEqual, Expression.Block(dispatches)); statements.Add(argBranch); } foreach (var group in methodTable.ParamsMethods.GroupBy(p => p.RequiredMondParameterCount)) { var dispatches = BuildDispatchExpression(errorPrefix, group, int.MaxValue, parameters, instanceFunction, returnLabel, callFactory); var requiredArgCount = Expression.Constant(group.Key); var argLengthAtLeast = Expression.GreaterThanOrEqual(argumentsLength, requiredArgCount); var argBranch = Expression.IfThen(argLengthAtLeast, Expression.Block(dispatches)); statements.Add(argBranch); } statements.Add(ThrowParameterTypeError(errorPrefix, methodTable)); statements.Add(Expression.Label(returnLabel, Expression.Default(typeof(TReturn)))); var block = Expression.Block(statements); return(Expression.Lambda <TFunc>(block, parameters).Compile()); }
/// <summary> /// Generates a MondConstructor binding for the given constructors. /// </summary> internal static MondConstructor BindConstructor(string className, ICollection <ConstructorInfo> constructors) { if (className == null) { throw new ArgumentNullException(nameof(className)); } var methodTable = BuildMethodTables((IEnumerable <MethodBase>)constructors, MethodType.Constructor).FirstOrDefault(); if (methodTable == null || (methodTable.Methods.Count == 0 && methodTable.ParamsMethods.Count == 0)) { return(null); } #if !NO_EXPRESSIONS BindCallFactory callFactory = (e, m, p, a, r) => BindConstructorCall(m, a, r); return(BindImpl <MondConstructor, object>(className, methodTable, true, callFactory)); #else return(BindConstructorImpl(className, methodTable)); #endif }
/// <summary> /// Generates MondFunction bindings for the given methods. /// </summary> internal static IEnumerable <Tuple <string, MondFunction> > BindStatic( string moduleName, ICollection <MethodInfo> methods, MethodType methodType = MethodType.Normal, string nameOverride = null) { if (methods.Any(m => !m.IsStatic)) { throw new MondBindingException("BindStatic only supports static methods"); } var methodTables = BuildMethodTables(methods, methodType, nameOverride); foreach (var table in methodTables) { #if !NO_EXPRESSIONS BindCallFactory callFactory = (e, m, p, a, r) => BindFunctionCall(e, m, null, false, p, a, r); yield return(Tuple.Create(table.Name, BindImpl <MondFunction, MondValue>(moduleName, table, false, callFactory))); #else yield return(Tuple.Create(table.Name, BindImpl(moduleName, table, nameOverride))); #endif } }
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()); }
private static List <Expression> BuildDispatchExpression( string errorPrefix, IEnumerable <Method> methods, int checkedArguments, List <ParameterExpression> parameters, bool instanceFunction, LabelTarget returnLabel, BindCallFactory callFactory) { var stateParam = parameters[0]; var argumentsParam = parameters[instanceFunction ? 2 : 1]; var instanceParam = parameters[instanceFunction ? 1 : 0]; Func <int, Expression> argumentIndex = i => Expression.ArrayIndex(argumentsParam, Expression.Constant(i)); var result = new List <Expression>(); foreach (var method in methods) { Expression typeCondition = Expression.Constant(true); if (checkedArguments > 0 && method.ValueParameters.Count > 0) { typeCondition = method.ValueParameters .Take(checkedArguments) .Select((p, i) => TypeCheck(argumentIndex(i), p)) .Aggregate(Expression.AndAlso); } var arguments = new List <Expression>(); var j = 0; foreach (var param in method.Parameters) { switch (param.Type) { case ParameterType.Value: if (j < checkedArguments) { arguments.Add(param.Conversion(argumentIndex(j++))); } else { arguments.Add(Expression.Constant(param.Info.DefaultValue, param.Info.ParameterType)); } break; case ParameterType.Params: var sliceMethod = typeof(MondFunctionBinder).GetMethod("Slice", BindingFlags.NonPublic | BindingFlags.Static); arguments.Add(Expression.Call(sliceMethod, argumentsParam, Expression.Constant(method.RequiredMondParameterCount))); break; case ParameterType.Instance: arguments.Add(instanceParam); break; case ParameterType.State: arguments.Add(stateParam); break; default: throw new NotSupportedException(); } } var callExpression = callFactory(errorPrefix, method, parameters, arguments, returnLabel); result.Add(Expression.IfThen(typeCondition, callExpression)); } return(result); }