Example #1
0
 internal static Expression /*!*/ ConvertExpression(Expression /*!*/ expr, Type /*!*/ toType, RubyContext /*!*/ context, Expression /*!*/ contextExpression,
                                                    bool implicitProtocolConversions)
 {
     return
         (ImplicitConvert(expr, expr.Type, toType) ??
          ExplicitConvert(expr, expr.Type, toType) ??
          AstUtils.LightDynamic(ProtocolConversionAction.GetConversionAction(context, toType, implicitProtocolConversions), toType, expr));
 }
        internal static void BuildConversion(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, Type /*!*/ resultType,
                                             params ProtocolConversionAction /*!*/[] /*!*/ conversions)
        {
            Assert.NotNull(metaBuilder, args, conversions);
            Debug.Assert(args.SimpleArgumentCount == 0 && !args.Signature.HasBlock && !args.Signature.HasSplattedArgument && !args.Signature.HasRhsArgument);
            Debug.Assert(!args.Signature.HasScope);

            // implicit conversions should only depend on the static type:
            foreach (var conversion in conversions)
            {
                if (conversion.TryImplicitConversion(metaBuilder, args))
                {
                    metaBuilder.AddObjectTypeRestriction(args.Target, args.TargetExpression);

                    if (!metaBuilder.Error)
                    {
                        metaBuilder.Result = ConvertResult(metaBuilder.Result, resultType);
                    }
                    return;
                }
            }

            RubyClass                targetClass = args.RubyContext.GetImmediateClassOf(args.Target);
            Expression               targetClassNameConstant = AstUtils.Constant(targetClass.GetNonSingletonClass().Name, typeof(string));
            MethodResolutionResult   respondToMethod, methodMissing = MethodResolutionResult.NotFound;
            ProtocolConversionAction selectedConversion = null;
            RubyMemberInfo           conversionMethod   = null;

            using (targetClass.Context.ClassHierarchyLocker()) {
                // check for type version:
                metaBuilder.AddTargetTypeTest(args.Target, targetClass, args.TargetExpression, args.MetaContext,
                                              ArrayUtils.Insert(Symbols.RespondTo, Symbols.MethodMissing, ArrayUtils.ConvertAll(conversions, (c) => c.ToMethodName))
                                              );

                // we can optimize if Kernel#respond_to? method is not overridden:
                respondToMethod = targetClass.ResolveMethodForSiteNoLock(Symbols.RespondTo, VisibilityContext.AllVisible);
                if (respondToMethod.Found && respondToMethod.Info.DeclaringModule == targetClass.Context.KernelModule && respondToMethod.Info is RubyLibraryMethodInfo)   // TODO: better override detection
                {
                    respondToMethod = MethodResolutionResult.NotFound;

                    // get the first applicable conversion:
                    foreach (var conversion in conversions)
                    {
                        selectedConversion = conversion;
                        conversionMethod   = targetClass.ResolveMethodForSiteNoLock(conversion.ToMethodName, VisibilityContext.AllVisible).Info;
                        if (conversionMethod != null)
                        {
                            break;
                        }
                        else
                        {
                            // find method_missing - we need to add "to_xxx" methods to the missing methods table:
                            if (!methodMissing.Found)
                            {
                                methodMissing = targetClass.ResolveMethodNoLock(Symbols.MethodMissing, VisibilityContext.AllVisible);
                            }
                            methodMissing.InvalidateSitesOnMissingMethodAddition(conversion.ToMethodName, targetClass.Context);
                        }
                    }
                }
            }

            if (!respondToMethod.Found)
            {
                if (conversionMethod == null)
                {
                    // error:
                    selectedConversion.SetError(metaBuilder, args, targetClassNameConstant, resultType);
                    return;
                }
                else
                {
                    // invoke target.to_xxx() and validate it; returns an instance of TTargetType:
                    conversionMethod.BuildCall(metaBuilder, args, selectedConversion.ToMethodName);

                    if (!metaBuilder.Error)
                    {
                        metaBuilder.Result = ConvertResult(
                            selectedConversion.MakeValidatorCall(args, targetClassNameConstant, metaBuilder.Result),
                            resultType
                            );
                    }
                    return;
                }
            }

            // slow path: invoke respond_to?, to_xxx and result validation:
            for (int i = conversions.Length - 1; i >= 0; i--)
            {
                string toMethodName = conversions[i].ToMethodName;

                var conversionCallSite = AstUtils.LightDynamic(
                    RubyCallAction.Make(args.RubyContext, toMethodName, RubyCallSignature.WithImplicitSelf(0)),
                    args.TargetExpression
                    );

                metaBuilder.Result = Ast.Condition(
                    // If

                    // respond_to?()
                    Methods.IsTrue.OpCall(
                        AstUtils.LightDynamic(
                            RubyCallAction.Make(args.RubyContext, Symbols.RespondTo, RubyCallSignature.WithImplicitSelf(1)),
                            args.TargetExpression,
                            Ast.Constant(args.RubyContext.CreateSymbol(toMethodName, RubyEncoding.Binary))
                            )
                        ),

                    // Then

                    // to_xxx():
                    ConvertResult(
                        conversions[i].MakeValidatorCall(args, targetClassNameConstant, conversionCallSite),
                        resultType
                        ),

                    // Else

                    (i < conversions.Length - 1) ? metaBuilder.Result :
                    conversions[i].MakeErrorExpression(args, targetClassNameConstant, resultType)
                    );
            }
        }
Example #3
0
        internal static Convertibility CanConvertFrom(DynamicMetaObject fromArg, Type /*!*/ fromType, Type /*!*/ toType, bool toNotNullable,
                                                      NarrowingLevel level, bool explicitProtocolConversions, bool implicitProtocolConversions)
        {
            ContractUtils.RequiresNotNull(fromType, "fromType");
            ContractUtils.RequiresNotNull(toType, "toType");

            var metaConvertible     = fromArg as IConvertibleMetaObject;
            var rubyMetaConvertible = fromArg as IConvertibleRubyMetaObject;

            //
            // narrowing level 0:
            //

            if (toType == fromType)
            {
                return(Convertibility.AlwaysConvertible);
            }

            if (fromType == typeof(DynamicNull))
            {
                if (toNotNullable)
                {
                    return(Convertibility.NotConvertible);
                }

                if (toType.IsGenericType && toType.GetGenericTypeDefinition() == typeof(Nullable <>))
                {
                    return(Convertibility.AlwaysConvertible);
                }

                if (!toType.IsValueType)
                {
                    // null convertible to any reference type:
                    return(Convertibility.AlwaysConvertible);
                }
                else if (toType == typeof(bool))
                {
                    return(Convertibility.AlwaysConvertible);
                }
                else if (!ProtocolConversionAction.HasDefaultConversion(toType))
                {
                    // null not convertible to a value type unless a protocol conversion is allowed:
                    return(Convertibility.NotConvertible);
                }
            }

            // blocks:
            if (fromType == typeof(MissingBlockParam))
            {
                return(new Convertibility(toType == typeof(BlockParam) && !toNotNullable, null));
            }

            if (fromType == typeof(BlockParam) && toType == typeof(MissingBlockParam))
            {
                return(Convertibility.AlwaysConvertible);
            }

            if (toType.IsAssignableFrom(fromType))
            {
                return(Convertibility.AlwaysConvertible);
            }

            if (HasImplicitNumericConversion(fromType, toType))
            {
                return(Convertibility.AlwaysConvertible);
            }

            if (CompilerHelpers.GetImplicitConverter(fromType, toType) != null)
            {
                return(Convertibility.AlwaysConvertible);
            }

            if (rubyMetaConvertible != null)
            {
                return(rubyMetaConvertible.IsConvertibleTo(toType, false));
            }
            else if (metaConvertible != null)
            {
                return(new Convertibility(metaConvertible.CanConvertTo(toType, false), null));
            }

            //
            // narrowing level 1:
            //

            if (level < NarrowingLevel.One)
            {
                return(Convertibility.NotConvertible);
            }

            if (explicitProtocolConversions && ProtocolConversionAction.HasDefaultConversion(toType))
            {
                return(Convertibility.AlwaysConvertible);
            }

            //
            // narrowing level 2:
            //

            if (level < NarrowingLevel.Two)
            {
                return(Convertibility.NotConvertible);
            }

            if (HasExplicitNumericConversion(fromType, toType))
            {
                return(Convertibility.AlwaysConvertible);
            }

            if (CompilerHelpers.GetExplicitConverter(fromType, toType) != null)
            {
                return(Convertibility.AlwaysConvertible);
            }

            if (CompilerHelpers.HasTypeConverter(fromType, toType))
            {
                return(Convertibility.AlwaysConvertible);
            }

            if (fromType == typeof(char) && toType == typeof(string))
            {
                return(Convertibility.AlwaysConvertible);
            }

            if (toType == typeof(bool))
            {
                return(Convertibility.AlwaysConvertible);
            }

            if (rubyMetaConvertible != null)
            {
                return(rubyMetaConvertible.IsConvertibleTo(toType, true));
            }
            else if (metaConvertible != null)
            {
                return(new Convertibility(metaConvertible.CanConvertTo(toType, true), null));
            }

            //
            // narrowing level 3:
            //

            if (level < NarrowingLevel.Three)
            {
                return(Convertibility.NotConvertible);
            }

            if (implicitProtocolConversions && ProtocolConversionAction.HasDefaultConversion(toType))
            {
                return(Convertibility.AlwaysConvertible);
            }

            // A COM object can potentially be converted to the given interface, but might also be not so use this only as the last resort:
            if (TypeUtils.IsComObjectType(fromType) && toType.IsInterface)
            {
                return(Convertibility.AlwaysConvertible);
            }

            return(Convertibility.NotConvertible);
        }
 protected override bool Build(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, bool defaultFallback)
 {
     Debug.Assert(defaultFallback, "custom fallback not supported");
     ProtocolConversionAction.BuildConversion(metaBuilder, args, _resultType, _conversions);
     return(true);
 }