/// <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); } }
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 ) ); }
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; }