internal static int GetHiddenParameterCount(OverloadInfo /*!*/ method, SelfCallConvention callConvention)
        {
            int i     = 0;
            var infos = method.Parameters;

            if (callConvention == SelfCallConvention.SelfIsInstance)
            {
                if (method.IsStatic)
                {
                    Debug.Assert(RubyUtils.IsOperator(method) || method.IsExtension);
                    i++;
                }
            }

            while (i < infos.Count && infos[i].ParameterType.IsSubclassOf(typeof(RubyCallSiteStorage)))
            {
                i++;
            }

            if (i < infos.Count)
            {
                var info = infos[i];

                if (info.ParameterType == typeof(RubyScope))
                {
                    i++;
                }
                else if (info.ParameterType == typeof(RubyContext))
                {
                    i++;
                }
                else if (method.IsConstructor && info.ParameterType == typeof(RubyClass))
                {
                    i++;
                }
            }

            if (i < infos.Count && infos[i].ParameterType == typeof(BlockParam))
            {
                i++;
            }

            if (callConvention == SelfCallConvention.SelfIsParameter)
            {
                Debug.Assert(i < infos.Count);
                Debug.Assert(method.IsStatic);
                i++;
            }

            return(i);
        }
        protected override BitArray MapSpecialParameters(ParameterMapping /*!*/ mapping)
        {
            var method  = mapping.Overload;
            var infos   = method.Parameters;
            var special = new BitArray(infos.Count);

            // Method signatures                                                                                  SelfCallConvention
            // RubyMethod/RubyCtor:   [(CallSiteStorage)*, (RubyContext|RubyScope)?, (BlockParam)?, self, args]  SelfIsParameter
            // static:                [(CallSiteStorage)*, (RubyContext|RubyScope)?, (BlockParam)?, args]        NoSelf
            // instance/extension/op: [self, (CallSiteStorage)*, (RubyContext|RubyScope)?, (BlockParam)?, args]  SelfIsInstace

            var i = 0;

            if (_callConvention == SelfCallConvention.SelfIsInstance)
            {
                if (method.IsStatic)
                {
                    Debug.Assert(RubyUtils.IsOperator(method) || method.IsExtension);

                    // receiver maps to the first parameter:
                    AddSimpleHiddenMapping(mapping, infos[i], true);
                    special[i++] = true;
                }
                else
                {
                    // receiver maps to the instance (no parameter info represents it):
                    mapping.AddParameter(new ParameterWrapper(null, method.DeclaringType, null, ParameterBindingFlags.ProhibitNull | ParameterBindingFlags.IsHidden));
                    mapping.AddInstanceBuilder(new InstanceBuilder(mapping.ArgIndex));
                }
            }
            else if (_callConvention == SelfCallConvention.NoSelf)
            {
                // instance methods on Object can be called with arbitrary receiver object including classes (static call):
                if (!method.IsStatic && method.DeclaringType == typeof(Object))
                {
                    // insert an InstanceBuilder that doesn't consume any arguments, only inserts the target expression as instance:
                    mapping.AddInstanceBuilder(new ImplicitInstanceBuilder());
                }
            }

            while (i < infos.Count && infos[i].ParameterType.IsSubclassOf(typeof(RubyCallSiteStorage)))
            {
                mapping.AddBuilder(new RubyCallSiteStorageBuilder(infos[i]));
                special[i++] = true;
            }

            if (i < infos.Count)
            {
                var info = infos[i];

                if (info.ParameterType == typeof(RubyScope))
                {
                    mapping.AddBuilder(new RubyScopeArgBuilder(info));
                    special[i++] = true;
                }
                else if (info.ParameterType == typeof(RubyContext))
                {
                    mapping.AddBuilder(new RubyContextArgBuilder(info));
                    special[i++] = true;
                }
                else if (method.IsConstructor && info.ParameterType == typeof(RubyClass))
                {
                    mapping.AddBuilder(new RubyClassCtorArgBuilder(info));
                    special[i++] = true;
                }
            }

            // If the method overload doesn't have a BlockParam parameter, we inject MissingBlockParam parameter and arg builder.
            // The parameter is treated as a regular explicit mandatory parameter.
            //
            // The argument builder provides no value for the actual argument expression, which makes the default binder to skip it
            // when emitting a tree for the actual method call (this is necessary since the method doesn't in fact have the parameter).
            //
            // By injecting the missing block parameter we achieve that all overloads have either BlockParam, [NotNull]BlockParam or
            // MissingBlockParam parameter. MissingBlockParam and BlockParam are convertible to each other. Default binder prefers
            // those overloads where no conversion needs to happen, which ensures the desired semantics:
            //
            //                                        conversions with desired priority (the less number the higher priority)
            // Parameters:                call w/o block      call with non-null block       call with null block
            // (implicit, MBP, ... )      MBP -> MBP (1)            BP -> MBP (3)               BP -> MBP (2)
            // (implicit, BP,  ... )      MBP -> BP  (2)            BP -> BP  (2)               BP -> BP  (1)
            // (implicit, BP!, ... )          N/A                   BP -> BP! (1)                  N/A
            //
            if (i < infos.Count && infos[i].ParameterType == typeof(BlockParam))
            {
                AddSimpleHiddenMapping(mapping, infos[i], mapping.Overload.ProhibitsNull(i));
                special[i++] = true;
            }
            else if (i >= infos.Count || infos[i].ParameterType != typeof(BlockParam))
            {
                mapping.AddBuilder(new MissingBlockArgBuilder(mapping.ArgIndex));
                mapping.AddParameter(new ParameterWrapper(null, typeof(MissingBlockParam), null, ParameterBindingFlags.IsHidden));
            }

            if (_callConvention == SelfCallConvention.SelfIsParameter)
            {
                // Ruby library methods only:
                Debug.Assert(method.IsStatic);
                Debug.Assert(i < infos.Count);

                // receiver maps to the first visible parameter:
                AddSimpleHiddenMapping(mapping, infos[i], mapping.Overload.ProhibitsNull(i));
                special[i++] = true;
            }

            return(special);
        }
Exemple #3
0
        internal OverloadInfo /*!*/[] /*!*/ SetMethodBasesNoLock(OverloadInfo /*!*/[] /*!*/ methods)
        {
            Debug.Assert(
                CollectionUtils.TrueForAll(methods, (method) => method.IsStatic || method.DeclaringType == typeof(Object)) ||
                CollectionUtils.TrueForAll(methods, (method) => !method.IsStatic || RubyUtils.IsExtension(method) || RubyUtils.IsOperator(method))
                );

            return(_methodBases = methods);
        }