public MethodResolutionResult(RubyMemberInfo/*!*/ info, RubyModule/*!*/ owner, bool visible)
 {
     Assert.NotNull(info, owner);
     _info = info;
     _owner = owner;
     _visible = visible;
 }
Exemple #2
1
        internal UnboundMethod(RubyModule/*!*/ targetConstraint, string/*!*/ name, RubyMemberInfo/*!*/ info) {
            Assert.NotNull(targetConstraint, name, info);

            _name = name;
            _info = info;
            _targetConstraint = targetConstraint;
        }
Exemple #3
0
        private static MethodMissingBinding BindToKernelMethodMissing(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, string /*!*/ methodName,
                                                                      RubyMemberInfo methodMissing, RubyMethodVisibility incompatibleVisibility, bool isSuperCall)
        {
            // TODO: better specialization of method_missing methods
            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
                {
                    return(MethodMissingBinding.Fallback);
                }

                return(MethodMissingBinding.Error);
            }

            return(MethodMissingBinding.Custom);
        }
Exemple #4
0
        // Returns true if the call was bound (with success or failure), false if fallback should be performed.
        internal static bool BuildMethodMissingAccess(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, string /*!*/ methodName,
                                                      RubyMemberInfo methodMissing, RubyMethodVisibility incompatibleVisibility, bool isSuperCall, bool defaultFallback)
        {
            switch (BindToKernelMethodMissing(metaBuilder, args, methodName, methodMissing, incompatibleVisibility, isSuperCall))
            {
            case MethodMissingBinding.Custom:
                // we pretend we found the member and return a method that calls method_missing:
                Debug.Assert(!metaBuilder.Error);
                metaBuilder.Result = Methods.CreateBoundMissingMember.OpCall(
                    AstUtils.Convert(args.TargetExpression, typeof(object)),
                    Ast.Constant(methodMissing, typeof(RubyMemberInfo)),
                    Ast.Constant(methodName)
                    );
                return(true);

            case MethodMissingBinding.Error:
                // method_missing is defined in Kernel, error has been reported:
                return(true);

            case MethodMissingBinding.Fallback:
                // method_missing is defined in Kernel:
                if (defaultFallback)
                {
                    metaBuilder.SetError(Methods.MakeMissingMemberError.OpCall(Ast.Constant(methodName)));
                    return(true);
                }
                return(false);
            }
            throw Assert.Unreachable;
        }
Exemple #5
0
 internal static RubyMemberInfo/*!*/ SelectOverload(RubyContext/*!*/ context, RubyMemberInfo/*!*/ info, string/*!*/ name, object[]/*!*/ typeArgs) {
     RubyMemberInfo result = info.TrySelectOverload(Protocols.ToTypes(context, typeArgs));
     if (result == null) {
         throw RubyExceptions.CreateArgumentError("no overload of `{0}' matches given parameter types", name);
     }
     return result;
 }
Exemple #6
0
        public RubyRepresenter(RubyContext/*!*/ context, Serializer/*!*/ serializer, YamlOptions/*!*/ opts)
            : base(serializer, opts) {
            _context = context;
            _objectToYamlMethod = context.GetClass(typeof(object)).ResolveMethod("to_yaml", VisibilityContext.AllVisible).Info;

             _TagUri =
                CallSite<Func<CallSite, object, object>>.Create(
                RubyCallAction.Make(context, "taguri", RubyCallSignature.WithImplicitSelf(0))
            );

            _ToYamlStyle =
                CallSite<Func<CallSite, object, object>>.Create(
                RubyCallAction.Make(context, "to_yaml_style", RubyCallSignature.WithImplicitSelf(0))
            );

            _ToYamlNode =
                CallSite<Func<CallSite, object, RubyRepresenter, object>>.Create(
                RubyCallAction.Make(context, "to_yaml_node", RubyCallSignature.WithImplicitSelf(1))
            );

            _ToYaml =
                CallSite<Func<CallSite, object, RubyRepresenter, object>>.Create(
                RubyCallAction.Make(context, "to_yaml", RubyCallSignature.WithImplicitSelf(0))
            );

            _ToYamlProperties = 
                CallSite<Func<CallSite, object, object>>.Create(
                RubyCallAction.Make(context, "to_yaml_properties", RubyCallSignature.WithImplicitSelf(0))
            );
        }
Exemple #7
0
 internal static RubyMemberInfo/*!*/ BindGenericParameters(RubyContext/*!*/ context, RubyMemberInfo/*!*/ info, string/*!*/ name, object[]/*!*/ typeArgs) {
     RubyMemberInfo result = info.TryBindGenericParameters(Protocols.ToTypes(context, typeArgs));
     if (result == null) {
         throw RubyExceptions.CreateArgumentError("wrong number of generic arguments for `{0}'", name);
     }
     return result;
 }
Exemple #8
0
 public MethodResolutionResult(RubyMemberInfo /*!*/ info, RubyModule /*!*/ owner, bool visible)
 {
     Assert.NotNull(info, owner);
     _info    = info;
     _owner   = owner;
     _visible = visible;
 }
Exemple #9
0
 public RubyMethod(object target, RubyMemberInfo/*!*/ info, string/*!*/ name) {
     ContractUtils.RequiresNotNull(info, "info");
     ContractUtils.RequiresNotNull(name, "name");
                 
     _target = target;
     _info = info;
     _name = name;
 }
        public static MetaObject TryBind(RubyContext /*!*/ context, GetMemberBinder /*!*/ binder, MetaObject /*!*/ target)
        {
            Assert.NotNull(context, target);
            var metaBuilder       = new MetaObjectBuilder();
            var contextExpression = Ast.Constant(context);

            metaBuilder.AddTargetTypeTest(target.Value, target.Expression, context, contextExpression);

            RubyMemberInfo method = context.ResolveMethod(target.Value, binder.Name, true).InvalidateSitesOnOverride();

            if (method != null && RubyModule.IsMethodVisible(method, false))
            {
                // we need to create a bound member:
                metaBuilder.Result = Ast.Constant(new RubyMethod(target.Value, method, binder.Name));
            }
            else
            {
                // TODO:
                // We need to throw an exception if we don't find method_missing so that our version update optimization works:
                // This limits interop with other languages.
                //
                // class B           CLR type with method 'foo'
                // class C < B       Ruby class
                // x = C.new
                //
                // 1. x.GET("foo") from Ruby
                //    No method found or CLR method found -> fallback to Python
                //    Python might see its method foo or might just fallback to .NET,
                //    in any case it will add rule [1] with restriction on type of C w/o Ruby version check.
                // 2. B.define_method("foo")
                //    This doesn't update C due to the optimization (there is no overridden method foo in C).
                // 3. x.GET("foo") from Ruby
                //    This will not invoke the binder since the rule [1] is still valid.
                //
                object symbol = SymbolTable.StringToId(binder.Name);
                RubyCallAction.BindToMethodMissing(metaBuilder, binder.Name,
                                                   new CallArguments(
                                                       new MetaObject(contextExpression, Restrictions.Empty, context),
                                                       new[] {
                    target,
                    new MetaObject(Ast.Constant(symbol), Restrictions.Empty, symbol)
                },
                                                       RubyCallSignature.Simple(1)
                                                       ),
                                                   method != null
                                                   );
            }

            // TODO: we should return null if we fail, we need to throw exception for now:
            return(metaBuilder.CreateMetaObject(binder, MetaObject.EmptyMetaObjects));
        }
Exemple #11
0
        internal static MethodResolutionResult Resolve(MetaObjectBuilder /*!*/ metaBuilder, string /*!*/ methodName, CallArguments /*!*/ args,
                                                       out RubyMemberInfo methodMissing)
        {
            MethodResolutionResult method;
            var targetClass       = args.TargetClass;
            var visibilityContext = GetVisibilityContext(args.Signature, args.Scope);

            using (targetClass.Context.ClassHierarchyLocker()) {
                metaBuilder.AddTargetTypeTest(args.Target, targetClass, args.TargetExpression, args.MetaContext,
                                              new[] { methodName, Symbols.MethodMissing }
                                              );

                if (args.Signature.IsSuperCall)
                {
                    Debug.Assert(!args.Signature.IsVirtualCall && args.Signature.HasImplicitSelf);
                    method = targetClass.ResolveSuperMethodNoLock(methodName, targetClass).InvalidateSitesOnOverride();
                }
                else
                {
                    var options = args.Signature.IsVirtualCall ? MethodLookup.Virtual : MethodLookup.Default;
                    method = targetClass.ResolveMethodForSiteNoLock(methodName, visibilityContext, options);
                }

                if (!method.Found)
                {
                    methodMissing = targetClass.ResolveMethodMissingForSite(methodName, method.IncompatibleVisibility);
                }
                else
                {
                    methodMissing = null;
                }
            }

            // Whenever the current self's class changes we need to invalidate the rule, if a protected method is being called.
            if (method.Info != null && method.Info.IsProtected && visibilityContext.Class != null)
            {
                // We don't need to compare versions, just the class objects (super-class relationship cannot be changed).
                // Since we don't want to hold on a class object (to make it collectible) we compare references to the version handlers.
                metaBuilder.AddCondition(Ast.Equal(
                                             Methods.GetSelfClassVersionHandle.OpCall(AstUtils.Convert(args.MetaScope.Expression, typeof(RubyScope))),
                                             Ast.Constant(visibilityContext.Class.Version)
                                             ));
            }

            return(method);
        }
Exemple #12
0
        internal void SetRule(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args)
        {
            RubyModule currentDeclaringModule;
            string     currentMethodName;

            var scope = args.Scope;

            object target;

            scope.GetSuperCallTarget(out currentDeclaringModule, out currentMethodName, out target);

            var targetExpression = metaBuilder.GetTemporary(typeof(object), "#super-self");

            metaBuilder.AddCondition(
                Methods.IsSuperCallTarget.OpCall(
                    AstUtils.Convert(args.ScopeExpression, typeof(RubyScope)),
                    Ast.Constant(currentDeclaringModule),
                    AstUtils.Constant(currentMethodName),
                    targetExpression
                    )
                );

            args.SetTarget(targetExpression, target);

            Debug.Assert(currentDeclaringModule != null);

            // target is stored in a local, therefore it cannot be part of the restrictions:
            metaBuilder.TreatRestrictionsAsConditions = true;
            metaBuilder.AddTargetTypeTest(target, targetExpression, scope.RubyContext, args.ContextExpression);
            metaBuilder.TreatRestrictionsAsConditions = false;

            RubyMemberInfo method = scope.RubyContext.ResolveSuperMethod(target, currentMethodName, currentDeclaringModule);

            // super calls don't go to method_missing
            if (method == null)
            {
                metaBuilder.SetError(Methods.MakeMissingSuperException.OpCall(Ast.Constant(currentMethodName)));
            }
            else
            {
                method.InvalidateSitesOnOverride = true;
                method.BuildSuperCall(metaBuilder, args, currentMethodName, currentDeclaringModule);
            }
        }
Exemple #13
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);
            }
        }
Exemple #14
0
        private bool TryGetClrField(Type/*!*/ type, BindingFlags bindingFlags, bool inherited, bool isWrite, string/*!*/ name, out RubyMemberInfo method) {
            FieldInfo fieldInfo;
            if (inherited) {
                fieldInfo = type.GetInheritedFields(name).WithBindingFlags(bindingFlags).FirstOrDefault();
            } else {
                fieldInfo = type.GetDeclaredField(name).WithBindingFlags(bindingFlags);
            }
            
            if (fieldInfo != null && IsVisible(fieldInfo) && (!isWrite || IsWriteable(fieldInfo))) {
                // creates detached info if only declared members are requested (used by Kernel#clr_member):
                method = new RubyFieldInfo(fieldInfo, RubyMemberFlags.Public, this, isWrite, isDetached: inherited);
                return true;
            }

            method = null;
            return false;
        }
Exemple #15
0
        private static void SetClassMethodsVisibility(RubyContext/*!*/ context, RubyModule/*!*/ module, string[]/*!*/ methodNames, RubyMethodVisibility visibility) {
            var methods = new RubyMemberInfo[methodNames.Length];
            for (int i = 0; i < methods.Length; i++) {
                RubyMemberInfo method = module.SingletonClass.ResolveMethod(methodNames[i], true);
                if (method == null) {
                    throw RubyExceptions.CreateUndefinedMethodError(module, methodNames[i]);
                }
                methods[i] = method;
            }

            for (int i = 0; i < methods.Length; i++) {
                module.SingletonClass.SetMethodVisibility(context, methodNames[i], methods[i], visibility);
            }
        }
        /// <summary>
        /// There are basically 4 cases:
        /// 1) CLR method of the given name is not defined in the specified type.
        ///    Do nothing, the method will be found as we traverse the hierarhy towards the Kernel module.
        /// 2) Otherwise
        ///    1) There is no RubyMemberInfo of given <c>name</c> present in the (type..Kernel] ancestors.
        ///       We need to search all types in (type..Object] for CLR method overloads.
        ///    2) There is a RubyMemberInfo in a class, say C, in (type..Kernel].
        ///       We need to get CLR methods from (type..C) in addition to the members in the type.
        ///        1) C.HidesInheritedOverloads == true
        ///           All overloads of the method we look for are in [type..C).
        ///        2) C.HidesInheritedOverloads == false
        ///           All overloads of the method we look for are in [type..C) and in the RubyMemberInfo.
        /// </summary>
        internal bool TryGetClrMethod(RubyClass /*!*/ cls, Type /*!*/ type, BindingFlags bindingFlags, string /*!*/ name, string /*!*/ clrName, out RubyMemberInfo method)
        {
            // declared only:
            MemberInfo[] initialMembers            = GetDeclaredClrMethods(type, bindingFlags, clrName);
            int          initialVisibleMemberCount = GetVisibleMethodCount(initialMembers);

            if (initialVisibleMemberCount == 0)
            {
                // case [1]
                method = null;
                return(false);
            }

            // inherited overloads:
            List <RubyClass> ancestors           = new List <RubyClass>();
            RubyMemberInfo   inheritedRubyMember = null;
            bool             skipHidden          = false;

            cls.ForEachAncestor((module) => {
                if (module != cls)
                {
                    if (module.TryGetDefinedMethod(name, ref skipHidden, out inheritedRubyMember) && !inheritedRubyMember.IsSuperForwarder)
                    {
                        return(true);
                    }

                    // Skip classes that have no tracker, e.g. Fixnum(tracker) <: Integer(null) <: Numeric(null) <: Object(tracker).
                    // Skip interfaces, their methods are not callable => do not include them into a method group.
                    // Skip all classes once hidden sentinel is encountered (no CLR overloads are visible since then).
                    if (!skipHidden && module.TypeTracker != null && module.IsClass)
                    {
                        ancestors.Add((RubyClass)module);
                    }
                }

                // continue:
                return(false);
            });

            _allMethods = null;
            if (inheritedRubyMember != null)
            {
                // case [2.2.2]: add CLR methods from the Ruby member:
                var inheritedGroup = inheritedRubyMember as RubyMethodGroupInfo;
                if (inheritedGroup != null)
                {
                    AddMethodsOverwriteExisting(inheritedGroup.MethodBases, inheritedGroup.OverloadOwners);
                }
            }

            // populate classes in (type..Kernel] or (type..C) with method groups:
            for (int i = ancestors.Count - 1; i >= 0; i--)
            {
                var declared = GetDeclaredClrMethods(ancestors[i].TypeTracker.Type, bindingFlags, clrName);
                if (declared.Length != 0 && AddMethodsOverwriteExisting(declared, null))
                {
                    // There is no cached method that needs to be invalidated.
                    //
                    // Proof:
                    // Suppose the group being created here overridden an existing method that is cached in a dynamic site invoked on some target class.
                    // Then either the target class is above all ancestors[i] or below some. If it is above then the new group doesn't
                    // invalidate validity of the site. If it is below then the method resolution for the cached method would create
                    // and store to method tables all method groups in between the target class and the owner of the cached method, including the
                    // one that contain overloads of ancestors[i]. But no module below inheritedRubyMember contains a method group of the name
                    // being currently resolved.
                    ancestors[i].AddMethodNoCacheInvalidation(name, MakeAllMethodsGroup(ancestors[i]));
                }
            }

            if (_allMethods != null)
            {
                // add members declared in self:
                AddMethodsOverwriteExisting(initialMembers, null);

                // return the group, it will be stored in the method table by the caller:
                method = MakeAllMethodsGroup(cls);
            }
            else
            {
                method = MakeSingleOwnerGroup(cls, initialMembers, initialVisibleMemberCount);
            }

            return(true);
        }
Exemple #17
0
        // thread safe: doesn't need any lock since it only accesses immutable state
        public bool TryGetClrConstructor(out RubyMemberInfo method) {
            OverloadInfo[] ctors;
            if (TypeTracker != null && (ctors = GetConstructors(TypeTracker.Type).ToArray()).Length > 0) {
                method = new RubyMethodGroupInfo(ctors, this, true);
                return true;
            }

            method = null;
            return false;
        }
Exemple #18
0
        /// <summary>
        /// Returns a fresh instance of RubyMemberInfo each time it is called. The caller needs to cache it if appropriate.
        /// May add or use method groups to/from super-clases if BindingFlags.DeclaredOnly is used.
        /// </summary>
        private bool TryGetClrMember(Type/*!*/ type, string/*!*/ name, bool mapNames, bool unmangleNames, bool inherited, out RubyMemberInfo method) {
            Context.RequiresClassHierarchyLock();

            BindingFlags basicBindingFlags = BindingFlags.Public | BindingFlags.NonPublic;

            // We look only for members directly declared on the type and handle method overloads inheritance manually.  
            BindingFlags bindingFlags = basicBindingFlags | ((_isSingletonClass) ? BindingFlags.Static : BindingFlags.Instance);

            // instance methods on Object are also available in static context:
            if (type == typeof(Object)) {
                bindingFlags |= BindingFlags.Instance;
            }

            string operatorName;
            if (mapNames && !_isSingletonClass && (operatorName = RubyUtils.ToClrOperatorName(name)) != null) {
                // instance invocation of an operator:
                if (TryGetClrMethod(type, basicBindingFlags | BindingFlags.Static, inherited, true, name, null, operatorName, null, out method)) {
                    return true;
                }
            } else if (mapNames && (name == "[]" || name == "[]=")) {
                if (type.IsArray && !_isSingletonClass) {
                    bool isSetter = name.Length == 3;
                    TryGetClrMethod(type, bindingFlags, inherited, false, name, null, isSetter ? "Set" : "Get", null, out method);
                    Debug.Assert(method != null);
                    return true;
                } else {
                    var defaultMember = type.GetTypeInfo().GetCustomAttributes<DefaultMemberAttribute>(false).SingleOrDefault();
                    if (defaultMember != null) {
                        // default indexer accessor:
                        bool isSetter = name.Length == 3;
                        if (TryGetClrProperty(type, bindingFlags, inherited, isSetter, name, defaultMember.MemberName, null, out method)) {
                            return true;
                        }
                    }
                }
            } else if (name.LastCharacter() == '=') {
                string propertyName = name.Substring(0, name.Length - 1);
                string altName = unmangleNames ? RubyUtils.TryUnmangleMethodName(propertyName) : null;
                
                // property setter:
                if (TryGetClrProperty(type, bindingFlags, inherited, true, name, propertyName, altName, out method)) return true;

                // writeable field:
                if (TryGetClrField(type, bindingFlags, inherited, true, propertyName, altName, out method)) return true;
            } else {
                string altName = unmangleNames ? RubyUtils.TryUnmangleMethodName(name) : null;

                // method:
                if (TryGetClrMethod(type, bindingFlags, inherited, false, name, null, name, altName, out method)) return true;
                
                // getter:
                if (TryGetClrProperty(type, bindingFlags, inherited, false, name, name, altName, out method)) return true;

                // event:
                if (TryGetClrEvent(type, bindingFlags, inherited, name, altName, out method)) return true;

                // field:
                if (TryGetClrField(type, bindingFlags, inherited, false, name, altName, out method)) return true;
            }

            method = null;
            return false;
        }
Exemple #19
0
        private static void BuildOverriddenInitializerCall(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, RubyMemberInfo/*!*/ initializer) {
            var instanceExpr = metaBuilder.Result;
            metaBuilder.Result = null;

            var instanceVariable = metaBuilder.GetTemporary(instanceExpr.Type, "#instance");

            // We know an exact type of the new instance and that there is no singleton for that instance.
            // We also have the exact method we need to call ("initialize" is a RubyMethodInfo/RubyLambdaMethodInfo).
            // => no tests are necessary:
            args.SetTarget(instanceVariable, null);

            if (initializer is RubyMethodInfo || initializer is RubyLambdaMethodInfo) {
                initializer.BuildCallNoFlow(metaBuilder, args, Symbols.Initialize);
            } else {
                // TODO: we need more refactoring of RubyMethodGroupInfo.BuildCall to be able to inline this:
                metaBuilder.Result = AstUtils.LightDynamic(
                    RubyCallAction.Make(args.RubyContext, "initialize",
                        new RubyCallSignature(
                            args.Signature.ArgumentCount, 
                            (args.Signature.Flags & ~RubyCallFlags.IsInteropCall) | RubyCallFlags.HasImplicitSelf
                        )
                    ),
                    args.GetCallSiteArguments(instanceVariable)
                );
            }

            if (!metaBuilder.Error) {
                // PropagateRetrySingleton(instance = new <type>(), instance.initialize(<args>))
                metaBuilder.Result = Methods.PropagateRetrySingleton.OpCall(
                    Ast.Assign(instanceVariable, instanceExpr),
                    metaBuilder.Result
                );

                // we need to handle break, which unwinds to a proc-converter that could be this method's frame:
                if (args.Signature.HasBlock) {
                    metaBuilder.ControlFlowBuilder = RubyMethodInfo.RuleControlFlowBuilder;
                }
            }
        }
Exemple #20
0
        internal void PrepareMethodUpdate(string/*!*/ methodName, RubyMemberInfo/*!*/ method, int mixinsToSkip) {
            Context.RequiresClassHierarchyLock();

            bool superClassUpdated = false;

            // A singleton subclass of a CLR type that doesn't implement IRubyObject:
            if (_isSingletonClass && !_superClass.IsSingletonClass && !_superClass.IsRubyClass && !(_singletonClassOf is IRubyObject)) {
                var ssm = _superClass.ClrSingletonMethods;
                if (ssm != null) {
                    if (!ssm.ContainsKey(methodName)) {
                        ssm[methodName] = true;
                        _superClass.MethodsUpdated("SetSingletonMethod: " + methodName);
                        superClassUpdated = true;
                    }
                } else {
                    _superClass.ClrSingletonMethods = ssm = new Dictionary<string, bool>();
                    ssm[methodName] = true;
                    _superClass.MethodsUpdated("SetSingletonMethod: " + methodName);
                    superClassUpdated = true;
                }
            }

            // Method table doesn't need to be initialized here. No site could be bound to this class or its subclass
            // without initializing this class. So there would be nothing to invalidate.
            if (MethodInitializationNeeded) {
                return;
            }

            Debug.Assert(mixinsToSkip <= Mixins.Length);
            int modulesToSkip, updatedLevel;
            if (mixinsToSkip > 0) {
                // skip this class when looking for overridden method:
                modulesToSkip = mixinsToSkip + 1;

                // update groups in this class as well:
                updatedLevel = _level - 1;
            } else {
                modulesToSkip = 0;
                updatedLevel = _level;
            }

            RubyMemberInfo overriddenMethod = ResolveOverriddenMethod(methodName, modulesToSkip);

            if (overriddenMethod == null) {
                // super class and this class have already been updated, no need to update again:
                if (!superClassUpdated) {
                    var missingMethods = Context.MissingMethodsCachedInSites;
                    if (missingMethods != null && missingMethods.Contains(methodName)) {
                        MethodsUpdated("SetMethod: " + methodName);
                    }
                }
            } else {
                if (overriddenMethod.InvalidateSitesOnOverride && !superClassUpdated) {
                    MethodsUpdated("SetMethod: " + methodName);
                }

                // If the overridden method is not a group the groups below don't need to be updated since they don't include any overloads
                // from above the current method definition.
                RubyOverloadGroupInfo overriddenGroup = overriddenMethod as RubyOverloadGroupInfo;
                if (overriddenGroup != null) {
                    // It suffice to compare the level of the overridden group.
                    // Reason: If there was any overload visible to a group below updatedLevel it would be visible to the overriddenGroup as well. 
                    if (overriddenGroup.MaxCachedOverloadLevel > updatedLevel) {
                        // Search the subtree of this class and remove all method groups that cache an overload owned by a super class.
                        if (mixinsToSkip > 0) {
                            InvalidateGroupsInSubClasses(methodName, overriddenGroup.MaxCachedOverloadLevel);
                        } else {
                            InvalidateGroupsInDependentClasses(methodName, overriddenGroup.MaxCachedOverloadLevel);
                        }

                        if (method.IsRemovable) {
                            method.InvalidateGroupsOnRemoval = true;
                        }
                    }
                } else {
                    // if the overridden method requires group invalidation on removal then the overridding method requires it too:
                    if (method.IsRemovable && overriddenMethod.InvalidateGroupsOnRemoval) {
                        method.InvalidateGroupsOnRemoval = true;
                    }
                }
            }
        }
Exemple #21
0
 internal static void SetInvalidateSitesOnOverride(RubyMemberInfo /*!*/ member)
 {
     member._invalidateSitesOnOverride = true;
 }
Exemple #22
0
        private bool TryGetClrField(Type/*!*/ type, BindingFlags bindingFlags, bool isWrite, string/*!*/ name, out RubyMemberInfo method) {
            FieldInfo fieldInfo = type.GetField(name, bindingFlags);
            if (fieldInfo != null && IsVisible(fieldInfo) && (!isWrite || IsWriteable(fieldInfo))) {
                // creates detached info if only declared members are requested (used by Kernel#clr_member):
                bool createDetached = (bindingFlags & BindingFlags.DeclaredOnly) != 0;
                method = new RubyFieldInfo(fieldInfo, RubyMemberFlags.Public, this, isWrite, createDetached);
                return true;
            }

            method = null;
            return false;
        }
Exemple #23
0
 internal Curried(object target, RubyMemberInfo/*!*/ info, string/*!*/ methodNameArg)
     : base(target, info, "method_missing") {
     _methodNameArg = methodNameArg;
 }
Exemple #24
0
        protected override bool Build(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, bool defaultFallback)
        {
            RubyModule currentDeclaringModule;
            string     currentMethodName;

            var scope     = args.Scope;
            var scopeExpr = AstUtils.Convert(args.MetaScope.Expression, typeof(RubyScope));

            RubyScope targetScope;
            int       scopeNesting = scope.GetSuperCallTarget(out currentDeclaringModule, out currentMethodName, out targetScope);

            if (scopeNesting == -1)
            {
                metaBuilder.AddCondition(Methods.IsSuperOutOfMethodScope.OpCall(scopeExpr));
                metaBuilder.SetError(Methods.MakeTopLevelSuperException.OpCall());
                return(true);
            }

            object target           = targetScope.SelfObject;
            var    targetExpression = metaBuilder.GetTemporary(typeof(object), "#super-self");
            var    assignTarget     = Ast.Assign(
                targetExpression,
                Methods.GetSuperCallTarget.OpCall(scopeExpr, AstUtils.Constant(scopeNesting))
                );

            if (_signature.HasImplicitArguments && targetScope.Kind == ScopeKind.BlockMethod)
            {
                metaBuilder.AddCondition(Ast.NotEqual(assignTarget, Ast.Field(null, Fields.NeedsUpdate)));
                metaBuilder.SetError(Methods.MakeImplicitSuperInBlockMethodError.OpCall());
                return(true);
            }

            // If we need to update we return RubyOps.NeedsUpdate instance that will cause the subsequent conditions to fail:
            metaBuilder.AddInitialization(assignTarget);

            args.SetTarget(targetExpression, target);

            Debug.Assert(currentDeclaringModule != null);

            RubyMemberInfo method;
            RubyMemberInfo methodMissing = null;

            // MRI bug: Uses currentDeclaringModule for method look-up so we can end up with an instance method of class C
            // called on a target of another class. See http://redmine.ruby-lang.org/issues/show/2419.

            // we need to lock the hierarchy of the target class:
            var targetClass = scope.RubyContext.GetImmediateClassOf(target);

            using (targetClass.Context.ClassHierarchyLocker()) {
                // initialize all methods in ancestors:
                targetClass.InitializeMethodsNoLock();

                // target is stored in a local, therefore it cannot be part of the restrictions:
                metaBuilder.TreatRestrictionsAsConditions = true;
                metaBuilder.AddTargetTypeTest(target, targetClass, targetExpression, args.MetaContext,
                                              new[] { Symbols.MethodMissing } // currentMethodName is resolved for super, which cannot be an instance singleton
                                              );
                metaBuilder.TreatRestrictionsAsConditions = false;

                method = targetClass.ResolveSuperMethodNoLock(currentMethodName, currentDeclaringModule).InvalidateSitesOnOverride().Info;

                if (_signature.ResolveOnly)
                {
                    metaBuilder.Result = AstUtils.Constant(method != null);
                    return(true);
                }

                if (method == null)
                {
                    // MRI: method_missing is called for the targetClass, not for the super:
                    methodMissing = targetClass.ResolveMethodMissingForSite(currentMethodName, RubyMethodVisibility.None);
                }
            }

            if (method != null)
            {
                method.BuildSuperCall(metaBuilder, args, currentMethodName, currentDeclaringModule);
            }
            else
            {
                return(RubyCallAction.BuildMethodMissingCall(metaBuilder, args, currentMethodName, methodMissing, RubyMethodVisibility.None, true, defaultFallback));
            }

            return(true);
        }
Exemple #25
0
        private static MethodMissingBinding BindToKernelMethodMissing(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ methodName,
            RubyMemberInfo methodMissing, RubyMethodVisibility incompatibleVisibility, bool isSuperCall) {

            // TODO: better specialization of method_missing methods
            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 {
                    return MethodMissingBinding.Fallback;
                }

                return MethodMissingBinding.Error;
            }

            return MethodMissingBinding.Custom;
        }
Exemple #26
0
        // Returns true if the call was bound (with success or failure), false if fallback should be performed.
        internal static bool BuildMethodMissingAccess(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ methodName,
            RubyMemberInfo methodMissing, RubyMethodVisibility incompatibleVisibility, bool isSuperCall, bool defaultFallback) {

            switch (BindToKernelMethodMissing(metaBuilder, args, methodName, methodMissing, incompatibleVisibility, isSuperCall)) {
                case MethodMissingBinding.Custom:
                    // we pretend we found the member and return a method that calls method_missing:
                    Debug.Assert(!metaBuilder.Error);
                    metaBuilder.Result = Methods.CreateBoundMissingMember.OpCall(
                        AstUtils.Convert(args.TargetExpression, typeof(object)),
                        Ast.Constant(methodMissing, typeof(RubyMemberInfo)),
                        Ast.Constant(methodName)
                    );
                    return true;

                case MethodMissingBinding.Error:
                    // method_missing is defined in Kernel, error has been reported:
                    return true;

                case MethodMissingBinding.Fallback:
                    // method_missing is defined in Kernel:
                    if (defaultFallback) {
                        metaBuilder.SetError(Methods.MakeMissingMemberError.OpCall(Ast.Constant(methodName)));
                        return true;
                    }
                    return false;
            }
            throw Assert.Unreachable;
        }
Exemple #27
0
        internal static MethodResolutionResult Resolve(MetaObjectBuilder/*!*/ metaBuilder, string/*!*/ methodName, CallArguments/*!*/ args,
            out RubyMemberInfo methodMissing) {

            MethodResolutionResult method;
            var targetClass = args.TargetClass;
            var visibilityContext = GetVisibilityContext(args.Signature, args.Scope);
            using (targetClass.Context.ClassHierarchyLocker()) {
                metaBuilder.AddTargetTypeTest(args.Target, targetClass, args.TargetExpression, args.MetaContext, 
                    new[] { methodName, Symbols.MethodMissing }
                );

                if (args.Signature.IsSuperCall) {
                    Debug.Assert(!args.Signature.IsVirtualCall && args.Signature.HasImplicitSelf);
                    method = targetClass.ResolveSuperMethodNoLock(methodName, targetClass).InvalidateSitesOnOverride();
                } else {
                    var options = args.Signature.IsVirtualCall ? MethodLookup.Virtual : MethodLookup.Default;
                    method = targetClass.ResolveMethodForSiteNoLock(methodName, visibilityContext, options);
                }

                if (!method.Found) {
                    methodMissing = targetClass.ResolveMethodMissingForSite(methodName, method.IncompatibleVisibility);
                } else {
                    methodMissing = null;
                }
            }

            // Whenever the current self's class changes we need to invalidate the rule, if a protected method is being called.
            if (method.Info != null && method.Info.IsProtected && visibilityContext.Class != null) {
                // We don't need to compare versions, just the class objects (super-class relationship cannot be changed).
                // Since we don't want to hold on a class object (to make it collectible) we compare references to the version handlers.
                metaBuilder.AddCondition(Ast.Equal(
                    Methods.GetSelfClassVersionHandle.OpCall(AstUtils.Convert(args.MetaScope.Expression, typeof(RubyScope))),
                    Ast.Constant(visibilityContext.Class.Version)
                ));
            }

            return method;
        }
Exemple #28
0
 private bool TryGetClrEvent(Type/*!*/ type, BindingFlags bindingFlags, bool inherited, string/*!*/ name, string altName, out RubyMemberInfo method) {
     return
         TryGetClrEvent(type, bindingFlags, inherited, name, out method) ? true :
         altName != null && TryGetClrEvent(type, bindingFlags, inherited, altName, out method);
 }
Exemple #29
0
        private bool TryGetClrEvent(Type/*!*/ type, BindingFlags bindingFlags, string/*!*/ name, out RubyMemberInfo method) {
            Assert.NotNull(type, name);

            EventInfo eventInfo = type.GetEvent(name, bindingFlags);
            if (eventInfo != null) {
                // creates detached info if only declared members are requested (used by Kernel#clr_member):
                bool createDetached = (bindingFlags & BindingFlags.DeclaredOnly) != 0;
                method = new RubyEventInfo((EventTracker)MemberTracker.FromMemberInfo(eventInfo), RubyMemberFlags.Public, this, createDetached);
                return true;
            }

            method = null;
            return false;
        }
Exemple #30
0
        private bool TryGetClrEvent(Type/*!*/ type, BindingFlags bindingFlags, bool inherited, string/*!*/ name, out RubyMemberInfo method) {
            Assert.NotNull(type, name);

            EventInfo eventInfo;
            if (inherited) {
                eventInfo = type.GetInheritedEvents(name).WithBindingFlags(bindingFlags).FirstOrDefault();
            } else {
                eventInfo = type.GetDeclaredEvent(name).WithBindingFlags(bindingFlags);
            }
            
            if (eventInfo != null) {
                // creates detached info if only declared members are requested (used by Kernel#clr_member):
                method = new RubyEventInfo((EventTracker)MemberTracker.FromMemberInfo(eventInfo), RubyMemberFlags.Public, this, isDetached: inherited);
                return true;
            }

            method = null;
            return false;
        }
Exemple #31
0
        // thread safe: doesn't need any lock since it only accesses immutable state
        public bool TryGetClrMember(string/*!*/ name, out RubyMemberInfo method) {
            // Get the first class in hierarchy that represents CLR type - worse case we end up with Object.
            // Ruby classes don't represent a CLR type and hence expose no CLR members.
            RubyClass cls = this;
            while (cls.TypeTracker == null) {
                cls = cls.SuperClass;
            }

            Debug.Assert(!cls.TypeTracker.Type.IsInterface);

            // Note: We don't cache failures as this API is not used so frequently (e.g. for regular method dispatch) that we would need caching.
            method = null;
            return cls.TryGetClrMember(cls.TypeTracker.Type, name, true, 0, out method);
        }
Exemple #32
0
 internal override void PrepareMethodUpdate(string/*!*/ methodName, RubyMemberInfo/*!*/ method) {
     PrepareMethodUpdate(methodName, method, 0);
 }
Exemple #33
0
        // thread safe: doesn't need any lock since it only accesses immutable state
        public bool TryGetClrConstructor(out RubyMemberInfo method) {
            ConstructorInfo[] ctors;
            if (TypeTracker != null && !TypeTracker.Type.IsInterface && (ctors = TypeTracker.Type.GetConstructors()) != null && ctors.Length > 0) {
                method = new RubyMethodGroupInfo(ctors, this, true);
                return true;
            }

            method = null;
            return false;
        }
Exemple #34
0
        // thread safe: doesn't need any lock since it only accesses immutable state
        public bool TryGetClrMember(string/*!*/ name, Type asType, out RubyMemberInfo method) {
            Debug.Assert(!_isSingletonClass);

            // Get the first class in hierarchy that represents CLR type - worse case we end up with Object.
            // Ruby classes don't represent a CLR type and hence expose no CLR members.
            RubyClass cls = this;
            while (cls.TypeTracker == null) {
                cls = cls.SuperClass;
            }

            Type type = cls.TypeTracker.Type;
            Debug.Assert(!RubyModule.IsModuleType(type));

            // Note: We don't cache results as this API is not used so frequently (e.g. for regular method dispatch).
            
            if (asType != null && !asType.IsAssignableFrom(type)) {
                throw RubyExceptions.CreateNameError(String.Format("`{0}' does not inherit from `{1}'", cls.Name, Context.GetTypeName(asType, true)));
            }

            method = null;
            using (Context.ClassHierarchyLocker()) {
                return cls.TryGetClrMember(asType ?? type, name, true, true, true, out method);
            }
        }
Exemple #35
0
        protected override bool TryGetClrMember(Type/*!*/ type, string/*!*/ name, bool tryUnmangle, out RubyMemberInfo method) {
            Context.RequiresClassHierarchyLock();

            if (IsFailureCached(type, name, _isSingletonClass)) {
                method = null;
                return false;
            }

            if (TryGetClrMember(type, name, tryUnmangle, BindingFlags.DeclaredOnly, out method)) {
                return true;
            }

            CacheFailure(type, name, _isSingletonClass);
            method = null;
            return false;
        }
Exemple #36
0
        protected override bool TryGetClrMember(Type/*!*/ type, string/*!*/ name, bool mapNames, bool unmangleNames, out RubyMemberInfo method) {
            Context.RequiresClassHierarchyLock();

            if (IsFailureCached(type, name, _isSingletonClass, _extensionVersion)) {
                method = null;
                return false;
            }

            if (TryGetClrMember(type, name, mapNames, unmangleNames, false, out method)) {
                return true;
            }

            CacheFailure(type, name, _isSingletonClass, _extensionVersion);
            method = null;
            return false;
        }
Exemple #37
0
        private bool TryGetClrMember(Type/*!*/ type, string/*!*/ name, bool tryUnmangle, BindingFlags basicBindingFlags, out RubyMemberInfo method) {
            basicBindingFlags |= BindingFlags.Public | BindingFlags.NonPublic;

            // We look only for members directly declared on the type and handle method overloads inheritance manually.  
            BindingFlags bindingFlags = basicBindingFlags | ((_isSingletonClass) ? BindingFlags.Static : BindingFlags.Instance);

            // instance methods on Object are also available in static context:
            if (type == typeof(Object)) {
                bindingFlags |= BindingFlags.Instance;
            }

            string operatorName;
            if (tryUnmangle && !_isSingletonClass && (operatorName = RubyUtils.MapOperator(name)) != null) {
                // instance invocation of an operator:
                if (TryGetClrMethod(type, basicBindingFlags | BindingFlags.Static, true, name, null, operatorName, null, out method)) {
                    return true;
                }
            } else if (tryUnmangle && (name == "[]" || name == "[]=")) {
                if (type.IsArray && !_isSingletonClass) {
                    bool isSetter = name.Length == 3;
                    TryGetClrMethod(type, bindingFlags, false, name, null, isSetter ? "Set" : "Get", null, out method);
                    Debug.Assert(method != null);
                    return true;
                } else {
                    object[] attrs = type.GetCustomAttributes(typeof(DefaultMemberAttribute), false);
                    if (attrs.Length == 1) {
                        // default indexer accessor:
                        bool isSetter = name.Length == 3;
                        if (TryGetClrProperty(type, bindingFlags, isSetter, name, ((DefaultMemberAttribute)attrs[0]).MemberName, null, out method)) {
                            return true;
                        }
                    }
                }
            } else if (name.LastCharacter() == '=') {
                string propertyName = name.Substring(0, name.Length - 1);
                string altName = tryUnmangle ? RubyUtils.TryUnmangleName(propertyName) : null;
                
                // property setter:
                if (TryGetClrProperty(type, bindingFlags, true, name, propertyName, altName, out method)) return true;

                // writeable field:
                if (TryGetClrField(type, bindingFlags, true, propertyName, altName, out method)) return true;
            } else {
                string altName = tryUnmangle ? RubyUtils.TryUnmangleName(name) : null;

                // method:
                if (TryGetClrMethod(type, bindingFlags, false, name, null, name, altName, out method)) return true;
                
                // getter:
                if (TryGetClrProperty(type, bindingFlags, false, name, name, altName, out method)) return true;

                // event:
                if (TryGetClrEvent(type, bindingFlags, name, altName, out method)) return true;

                // field:
                if (TryGetClrField(type, bindingFlags, false, name, altName, out method)) return true;
            }

            method = null;
            return false;
        }
Exemple #38
0
        /// <summary>
        /// There are basically 4 cases:
        /// 1) CLR method of the given name is not defined in the specified type.
        ///    Do nothing, the method will be found as we traverse the hierarhy towards the Kernel module.
        /// 2) Otherwise
        ///    1) There is no RubyMemberInfo of given <c>name</c> present in the (type..Kernel] ancestors.
        ///       We need to search all types in (type..Object] for CLR method overloads.
        ///    2) There is a RubyMemberInfo in a class, say C, in (type..Kernel]. 
        ///       We need to get CLR methods from (type..C) in addition to the members in the type.
        ///        1) C.HidesInheritedOverloads == true
        ///           All overloads of the method we look for are in [type..C).
        ///        2) C.HidesInheritedOverloads == false
        ///           All overloads of the method we look for are in [type..C) and in the RubyMemberInfo.
        /// </summary>
        /// <remarks>
        /// Doesn't include explicitly implemented interface methods. Including them would allow to call them directly (obj.foo) 
        /// if the overload resolution succeeds. However, the interface methods are probably implemented explicitly for a reason:
        /// 1) There is a conflict in signatures -> the overload resolution would probably fail.
        /// 2) The class was designed with an intention to not expose the implementations directly.
        /// </remarks>
        private bool TryGetClrMethod(Type/*!*/ type, BindingFlags bindingFlags, bool inherited, bool specialNameOnly, 
            string/*!*/ name, string clrNamePrefix, string/*!*/ clrName, string altClrName, out RubyMemberInfo method) {
            Context.RequiresClassHierarchyLock();

            // declared only:
            List<OverloadInfo> initialMembers = new List<OverloadInfo>(GetClrMethods(type, bindingFlags, inherited, clrNamePrefix, clrName, altClrName, specialNameOnly));
            if (initialMembers.Count == 0) {
                // case [1]
                //
                // Note: This failure might be cached (see CacheFailure) based on the type and name, 
                // therefore it must not depend on any other mutable state:
                method = null;
                return false;
            }

            // If all CLR inherited members are to be returned we are done.
            // (creates a detached info; used by Kernel#clr_member)
            if (inherited) {
                method = MakeGroup(initialMembers, initialMembers.Count, specialNameOnly, true);
                return true;
            }

            // inherited overloads:
            List<RubyClass> ancestors = new List<RubyClass>();
            RubyMemberInfo inheritedRubyMember = null;
            bool skipHidden = false;

            ForEachAncestor((module) => {
                if (module != this) {
                    if (module.TryGetDefinedMethod(name, ref skipHidden, out inheritedRubyMember) && !inheritedRubyMember.IsSuperForwarder) {
                        return true;
                    }

                    // Skip classes that have no tracker, e.g. Fixnum(tracker) <: Integer(null) <: Numeric(null) <: Object(tracker).
                    // Skip CLR modules, their methods are not callable => do not include them into a method group.
                    // Skip all classes once hidden sentinel is encountered (no CLR overloads are visible since then).
                    if (!skipHidden && module.TypeTracker != null && module.IsClass) {
                        ancestors.Add((RubyClass)module);
                    }
                }

                // continue:
                return false;
            });

            // (method clr name, parameter types) => (overload, owner)
            Dictionary<Key<string, ValueArray<Type>>, ClrOverloadInfo> allMethods = null;

            if (inheritedRubyMember != null) {
                // case [2.2.2]: add CLR methods from the Ruby member:
                var inheritedGroup = inheritedRubyMember as RubyOverloadGroupInfo;

                if (inheritedGroup != null) {
                    AddMethodsOverwriteExisting(ref allMethods, inheritedGroup.MethodBases, inheritedGroup.OverloadOwners, specialNameOnly);
                } else if (inheritedRubyMember.IsRemovable) {
                    // The groups created below won't contain overloads defined above (if there are any).
                    // If this method is removed we need to invalidate them.
                    inheritedRubyMember.InvalidateGroupsOnRemoval = true;
                }
            }

            // populate classes in (type..Kernel] or (type..C) with method groups:
            for (int i = ancestors.Count - 1; i >= 0; i--) {
                var declared = ancestors[i].GetClrMethods(ancestors[i].TypeTracker.Type, bindingFlags, false, clrNamePrefix, clrName, altClrName, specialNameOnly);
                if (AddMethodsOverwriteExisting(ref allMethods, declared, null, specialNameOnly)) {
                    // There is no cached method that needs to be invalidated.
                    //
                    // Proof:
                    // Suppose the group being created here overridden an existing method that is cached in a dynamic site invoked on some target class.
                    // Then either the target class is above all ancestors[i] or below some. If it is above then the new group doesn't 
                    // invalidate validity of the site. If it is below then the method resolution for the cached method would create
                    // and store to method tables all method groups in between the target class and the owner of the cached method, including the 
                    // one that contain overloads of ancestors[i]. But no module below inheritedRubyMember contains a method group of the name 
                    // being currently resolved.
                    ancestors[i].AddMethodNoCacheInvalidation(name, ancestors[i].MakeGroup(allMethods.Values));
                }
            }

            if (allMethods != null) {
                // add members declared in self:
                AddMethodsOverwriteExisting(ref allMethods, initialMembers, null, specialNameOnly);

                // return the group, it will be stored in the method table by the caller:
                method = MakeGroup(allMethods.Values);
            } else {
                method = MakeGroup(initialMembers, initialMembers.Count, specialNameOnly, false);
            }

            return true;
        }
Exemple #39
0
        // TODO: Indexers can be overloaded:
        //private bool TryGetClrProperty(Type/*!*/ type, BindingFlags bindingFlags, string/*!*/ name, bool isWrite, out RubyMemberInfo method) {
        //    Assert.NotNull(type, name);

        //    PropertyInfo propertyInfo = type.GetProperty(name, bindingFlags);
        //    if (propertyInfo != null) {
        //        MethodInfo accessor = isWrite ? propertyInfo.GetSetMethod(false) : propertyInfo.GetGetMethod(false);
        //        if (accessor != null) {
        //            // TODO: define RubyPropertyInfo class
        //            method = new RubyMethodGroupInfo(new MethodBase[] { accessor }, this, _isSingletonClass);
        //            return true;
        //        }
        //    }

        //    method = null;
        //    return false;
        //}

        private bool TryGetClrProperty(Type/*!*/ type, BindingFlags bindingFlags, bool inherited, bool isWrite, 
            string/*!*/ name, string/*!*/ clrName, string altClrName, out RubyMemberInfo method) {

            return TryGetClrMethod(type, bindingFlags, inherited, true, name, isWrite ? "set_" : "get_", clrName, altClrName, out method);
        }
Exemple #40
0
 private bool TryGetClrField(Type/*!*/ type, BindingFlags bindingFlags, bool isWrite, string/*!*/ name, string altName, out RubyMemberInfo method) {
     return
         TryGetClrField(type, bindingFlags, isWrite, name, out method) ? true :
         altName != null && TryGetClrField(type, bindingFlags, isWrite, altName, out method);
 }
Exemple #41
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
                    )
                );
        }