public static DynamicMetaObject BindCatchAllPrimitive(DynamicMetaObject target, DynamicMetaObject[] args, Type returnType) { var method = typeof(BonsaiPrimitives).GetMethodsWith<CatchAllPrimitiveAttribute>( (mi, capi) => capi.Type == null || ( target.LimitType.IsAssignableFrom(capi.Type) || ( capi.Type.IsGenericTypeDefinition && target.LimitType.IsGenericType && capi.Type.GetGenericArguments().Length == target.LimitType.GetGenericArguments().Length && capi.Type.MakeGenericType(target.LimitType.GetGenericArguments()).IsAssignableFrom(target.LimitType)))) .FirstOrDefault(); // assume that if the method is generic than the matched type is also generic and it gets the same parameters if (method != null && method.IsGenericMethodDefinition) method = method.MakeGenericMethod(target.LimitType.GetGenericArguments()); if (method != null) { return new DynamicMetaObject( Expression.Convert( Expression.Call( null, method, new Expression[] { Expression.Convert(target.Expression, method.GetParameters()[0].ParameterType), args[0].Expression, Expression.NewArrayInit( typeof(object), args.Subarray(1).Select(a => Expression.Convert(a.Expression, typeof(object)))) }), returnType), BindingRestrictions.GetTypeRestriction(target.Expression, target.Value.GetType())); } else { throw new Exception("Binding failed"); } }
public static bool TryBind( DynamicMetaObject target, DynamicMetaObject[] arguments, Type returnType, out DynamicMetaObject result) { result = null; if (target.Value == null) return false; var value = target.Value; if (arguments.Length >= 2 && arguments[1].HasValue && arguments[1].Value is SymbolId) { var methodName = (SymbolId)arguments[1].Value; var method = typeof(BonsaiPrimitives).GetMethodsWith<PrimitiveAttribute>( (mi, pi) => pi.Method == methodName && ( pi.Type == null || ( target.LimitType.IsAssignableFrom(pi.Type) || ( pi.Type.IsGenericTypeDefinition && target.LimitType.IsGenericType && pi.Type.MakeGenericType(target.LimitType.GetGenericArguments()).IsAssignableFrom(target.LimitType))))).FirstOrDefault(); // assume that if the method is generic than the matched type is also generic and it gets the same parameters if (method != null && method.IsGenericMethodDefinition) method = method.MakeGenericMethod(target.LimitType.GetGenericArguments()); if (method != null) { result = new DynamicMetaObject( Expression.Convert( Expression.Call( null, method, new Expression[] { Expression.Convert(target.Expression, method.GetParameters()[0].ParameterType), arguments[0].Expression, Expression.NewArrayInit( typeof(object), arguments.Subarray(2).Select(a => Expression.Convert(a.Expression, typeof(object)))) }), returnType), BindingRestrictions.GetTypeRestriction(target.Expression, value.GetType())); return true; } } return false; }
public override DynamicMetaObject FallbackInvoke( DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion) { var value = target.Value; // TODO: handle null value if (value == null) { throw new NotImplementedException("target.Value is null."); } // if target is a delegate, call it immediately if (target.LimitType.IsSubclassOf(typeof(Delegate))) { var parms = target.LimitType.GetMethod("Invoke").GetParameters(); Expression[] callArgs = new Expression[args.Length]; // convert the parameters, but skip the 1st parameter, which is the scope for (int i = 1; i < args.Length; i++) { Expression argExpr = args[i].Expression; argExpr = Expression.Convert(argExpr, parms[i-1].ParameterType); callArgs[i-1] = argExpr; } var expression = Expression.Invoke( Expression.Convert(target.Expression, target.LimitType), callArgs); return new DynamicMetaObject( expression, BindingRestrictions.Empty); } // "normal" objects (i.e. non-dynamic instances) evaluate to themselves when called with no arguments if (args.Length == 1) { if (value != null && value.GetType() == ReturnType) return target; else return new DynamicMetaObject(Expression.Convert(target.Expression, ReturnType), BindingRestrictions.Empty); } // try calling a primitive DynamicMetaObject result = null; if (BonsaiPrimitives.TryBind(target, args, this.ReturnType, out result)) return result; // if the second argument is a symbol, fallback to invoking a // static (as-in "non-dynamic") member named like the symbol if (args[1].Value is SymbolId) { string name = ((SymbolId)args[1].Value).ToString(); bool isSetter = false; // setters end with =, so remove that when searching for members if (name.EndsWith("=")) { name = name.Substring(0, name.Length - 1); isSetter = true; } var members = target.LimitType.GetMember( name, MemberTypes.Property | MemberTypes.Method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic); // method call if (!isSetter && members.Length > 0 && members[0] is MethodInfo) { var method = (MethodInfo)members[0]; if (method.IsGenericMethod) { var genericArgs = new Type[method.GetGenericArguments().Length]; for (int i = 0; i < genericArgs.Length; i++) genericArgs[i] = typeof(object); method = method.MakeGenericMethod(genericArgs); } var parms = method.GetParameters(); Expression[] callArgs = new Expression[args.Length-2]; // skip the target and the scope for (int i = 2; i < args.Length; i++) { Expression argExpr = args[i].Expression; argExpr = Expression.Convert(argExpr, parms[i-2].ParameterType); callArgs[i-2] = argExpr; } Expression callExpr = Expression.Call( target.Expression.ConvertTo(target.LimitType), (MethodInfo)method, callArgs); if (method.ReturnType == typeof(void)) { callExpr = Expression.Block( callExpr, Expression.Constant(null)); } return new DynamicMetaObject( callExpr.ConvertTo(this.ReturnType), BindingRestrictions.Empty); } // getter if (!isSetter && members.Length == 1 && members[0] is PropertyInfo) { var property = (PropertyInfo)members[0]; var propParams = property.GetIndexParameters(); if (propParams.Length == args.Length - 2) { return new DynamicMetaObject( Expression.Convert( Expression.Property( Expression.Convert(target.Expression, target.LimitType), (PropertyInfo)members[0], args.Subarray(2).Select((a, i) => Expression.Convert(a.Expression, propParams[i].ParameterType))), this.ReturnType), BindingRestrictions.Empty); } } // setter if (isSetter && members.Length == 1 && members[0] is PropertyInfo) { var method = ((PropertyInfo)members[0]).GetSetMethod(); var propParams = method.GetParameters(); if (propParams.Length == args.Length - 2) { Expression callExpr = Expression.Call( Expression.Convert(target.Expression, target.LimitType), method, args.Subarray(2).Select((a, i) => Expression.Convert(a.Expression, propParams[i].ParameterType))); if (method.ReturnType == typeof(void)) { callExpr = Expression.Block( callExpr, Expression.Constant(null)); } return new DynamicMetaObject(Expression.Convert(callExpr, this.ReturnType), BindingRestrictions.Empty); } } } // when all else fails, try invoking a CatchAllPrimitive return BonsaiPrimitives.BindCatchAllPrimitive(target, args, this.ReturnType); }