예제 #1
0
        /// <exception cref="MissingMethodException">The resolved method is Kernel#method_missing.</exception>
        internal static void Bind(MetaObjectBuilder /*!*/ metaBuilder, string /*!*/ methodName, CallArguments /*!*/ args)
        {
            metaBuilder.AddTargetTypeTest(args);

            RubyMemberInfo method = args.RubyContext.ResolveMethod(args.Target, methodName, true).InvalidateSitesOnOverride();

            if (method != null && RubyModule.IsMethodVisible(method, args.Signature.HasImplicitSelf))
            {
                method.BuildCall(metaBuilder, args, methodName);
            }
            else
            {
                // insert the method name argument into the args
                object symbol = SymbolTable.StringToId(methodName);
                args.InsertSimple(0, new MetaObject(Ast.Constant(symbol), Restrictions.Empty, symbol));

                BindToMethodMissing(metaBuilder, methodName, args, method != null);
            }
        }
예제 #2
0
        internal void SetRule(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args)
        {
            Assert.NotNull(metaBuilder, args);
            Debug.Assert(args.SimpleArgumentCount == 0 && !args.Signature.HasBlock && !args.Signature.HasSplattedArgument && !args.Signature.HasRhsArgument);
            Debug.Assert(args.Signature.HasScope);

            var ec = args.RubyContext;

            // implicit conversions should only depend on a static type:
            if (TryImplicitConversion(metaBuilder, args))
            {
                if (args.Target == null)
                {
                    metaBuilder.AddRestriction(Ast.Equal(args.TargetExpression, Ast.Constant(null, args.TargetExpression.Type)));
                }
                else
                {
                    metaBuilder.AddTypeRestriction(args.Target.GetType(), args.TargetExpression);
                }
                return;
            }

            // check for type version:
            metaBuilder.AddTargetTypeTest(args);

            string     toMethodName            = ToMethodName;
            Expression targetClassNameConstant = Ast.Constant(ec.GetClassOf(args.Target).Name);

            // Kernel#respond_to? method is not overridden => we can optimize
            RubyMemberInfo respondToMethod = ec.ResolveMethod(args.Target, Symbols.RespondTo, true).InvalidateSitesOnOverride();

            if (respondToMethod == null ||
                // the method is defined in library, hasn't been replaced by user defined method (TODO: maybe we should make this check better)
                (respondToMethod.DeclaringModule == ec.KernelModule && respondToMethod is RubyMethodGroupInfo))
            {
                RubyMemberInfo conversionMethod = ec.ResolveMethod(args.Target, toMethodName, false).InvalidateSitesOnOverride();
                if (conversionMethod == null)
                {
                    // error:
                    SetError(metaBuilder, targetClassNameConstant, args);
                    return;
                }
                else
                {
                    // invoke target.to_xxx() and validate it; returns an instance of TTargetType:
                    conversionMethod.BuildCall(metaBuilder, args, toMethodName);

                    if (!metaBuilder.Error && ConversionResultValidator != null)
                    {
                        metaBuilder.Result = ConversionResultValidator.OpCall(targetClassNameConstant, AstFactory.Box(metaBuilder.Result));
                    }
                    return;
                }
            }
            else if (!RubyModule.IsMethodVisible(respondToMethod, false))
            {
                // respond_to? is private:
                SetError(metaBuilder, targetClassNameConstant, args);
                return;
            }

            // slow path: invoke respond_to?, to_xxx and result validation:

            var conversionCallSite = Ast.Dynamic(
                RubyCallAction.Make(toMethodName, RubyCallSignature.WithScope(0)),
                typeof(object),
                args.ScopeExpression, args.TargetExpression
                );

            Expression opCall;

            metaBuilder.Result = Ast.Condition(
                // If

                // respond_to?()
                Methods.IsTrue.OpCall(
                    Ast.Dynamic(
                        RubyCallAction.Make(Symbols.RespondTo, RubyCallSignature.WithScope(1)),
                        typeof(object),
                        args.ScopeExpression, args.TargetExpression, Ast.Constant(SymbolTable.StringToId(toMethodName))
                        )
                    ),

                // Then

                // to_xxx():
                opCall = (ConversionResultValidator == null) ? conversionCallSite :
                         ConversionResultValidator.OpCall(targetClassNameConstant, conversionCallSite),

                // Else

                AstUtils.Convert(
                    (ConversionResultValidator == null) ? args.TargetExpression :
                    Ast.Convert(
                        Ast.Throw(Methods.CreateTypeConversionError.OpCall(targetClassNameConstant, Ast.Constant(TargetTypeName))),
                        typeof(object)
                        ),
                    opCall.Type
                    )
                );
        }
예제 #3
0
        internal static bool BindToMethodMissing(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ methodName,
            RubyMemberInfo methodMissing, RubyMethodVisibility incompatibleVisibility, bool isSuperCall, bool defaultFallback) {
            // Assumption: args already contain method name.
            
            // TODO: better check for builtin method
            if (methodMissing == null ||
                methodMissing.DeclaringModule == methodMissing.Context.KernelModule && methodMissing is RubyLibraryMethodInfo) {

                if (isSuperCall) {
                    metaBuilder.SetError(Methods.MakeMissingSuperException.OpCall(AstUtils.Constant(methodName)));
                } else if (incompatibleVisibility == RubyMethodVisibility.Private) {
                    metaBuilder.SetError(Methods.MakePrivateMethodCalledError.OpCall(
                        AstUtils.Convert(args.MetaContext.Expression, typeof(RubyContext)), args.TargetExpression, AstUtils.Constant(methodName))
                    );
                } else if (incompatibleVisibility == RubyMethodVisibility.Protected) {
                    metaBuilder.SetError(Methods.MakeProtectedMethodCalledError.OpCall(
                        AstUtils.Convert(args.MetaContext.Expression, typeof(RubyContext)), args.TargetExpression, AstUtils.Constant(methodName))
                    );
                } else if (defaultFallback) {
                    args.InsertMethodName(methodName);
                    methodMissing.BuildCall(metaBuilder, args, methodName);
                } else {
                    return false;
                }
            } else {
                args.InsertMethodName(methodName);
                methodMissing.BuildCall(metaBuilder, args, methodName);
            }

            return true;
        }