Пример #1
0
        void TypeRedeclared(Reflection.PhpTypeInfo type)
        {
            Debug.Assert(type != null);

            // TODO: ErrCode & throw
            throw new InvalidOperationException($"Type {type.Name} redeclared!");
        }
Пример #2
0
        /// <summary>
        /// Resolves whether given instance <paramref name="obj"/> is of given type <paramref name="tinfo"/>.
        /// </summary>
        /// <param name="obj">Value to be checked.</param>
        /// <param name="tinfo">Type descriptor.</param>
        /// <returns>Whether <paramref name="obj"/> is of type <paramref name="tinfo"/>.</returns>
        public static bool IsInstanceOf(object obj, Reflection.PhpTypeInfo tinfo)
        {
            // note: if tinfo is null =>
            // type was not declared =>
            // value cannot be its instance because there is no way how to instantiate it
            // ignoring the case when object is passed from CLR

            return(obj != null && tinfo != null && tinfo.Type.IsInstanceOfType(obj));
        }
Пример #3
0
        /// <summary>
        /// Resolves whether given instance <paramref name="value"/> is of given type <paramref name="tinfo"/>.
        /// </summary>
        /// <param name="value">Value to be checked.</param>
        /// <param name="tinfo">Type descriptor.</param>
        /// <returns>Whether <paramref name="value"/> is of type <paramref name="tinfo"/>.</returns>
        public static bool IsInstanceOf(object value, Reflection.PhpTypeInfo tinfo)
        {
            if (tinfo == null)
            {
                throw new ArgumentNullException(nameof(tinfo));
            }

            return(tinfo.Type.GetTypeInfo().IsInstanceOfType(value));
        }
Пример #4
0
        /// <summary>
        /// Gets runtime instance fields of given object.
        /// </summary>
        /// <param name="tinfo">Type of <paramref name="instance"/>.</param>
        /// <param name="instance">Instance of type <paramref name="tinfo"/>.</param>
        /// <returns>Array representing internal runtime fields or <c>null</c> if no values are available.</returns>
        public static PhpArray GetRuntimeFields(this PhpTypeInfo tinfo, object instance)
        {
            Debug.Assert(instance != null);
            Debug.Assert(instance.GetType() == tinfo.Type.AsType());

            // PhpArray __runtime_fields
            if (tinfo.RuntimeFieldsHolder != null)
            {
                // all runtime fields are considered public,
                // no visibility check needed

                return((PhpArray)tinfo.RuntimeFieldsHolder.GetValue(instance));
            }
            else
            {
                return(null);
            }
        }
Пример #5
0
        /// <summary>
        /// Gets a collection of the trait types implemented by the current type.
        /// </summary>
        public static IEnumerable <PhpTypeInfo> GetImplementedTraits(this PhpTypeInfo phptype)
        {
            if (ReferenceEquals(phptype, null))
            {
                throw new ArgumentNullException(nameof(phptype));
            }

            foreach (var f in phptype.Type.DeclaredFields)
            {
                // traits instance is stored in a fields:
                // private readonly TraitType<phptype> <>trait_TraitType;

                if (f.IsPrivate && f.IsInitOnly && f.Name.StartsWith("<>trait_") && f.FieldType.IsConstructedGenericType)
                {
                    yield return(GetPhpTypeInfo(f.FieldType.GetGenericTypeDefinition()));
                }
            }
        }
Пример #6
0
            public ClrProperty(PhpTypeInfo tinfo, PropertyInfo property)
                : base(tinfo)
            {
                Property = property ?? throw new ArgumentNullException(nameof(property));

                _lazyGetter = new Lazy <Func <object, PhpValue> >(() =>
                {
                    var pinstance = Expression.Parameter(typeof(object));

                    var expr = Bind(null !, Expression.Convert(pinstance, Property.DeclaringType));
                    expr     = ConvertExpression.BindToValue(expr);

                    //
                    return((Func <object, PhpValue>)Expression.Lambda(expr, true, pinstance).Compile());
                });

                // SetValue(instance, PhpValue): void
                _lazySetValue = new Lazy <Action <Context, object, PhpValue> >(() =>
                {
                    if (IsReadOnly)
                    {
                        // error
                        return((_, _instance, _value) =>
                        {
                            PhpException.ErrorException(Resources.ErrResources.readonly_property_written, ContainingType.Name, PropertyName);
                        });
                    }

                    var pctx      = Expression.Parameter(typeof(Context));
                    var pinstance = Expression.Parameter(typeof(object));
                    var pvalue    = Expression.Parameter(typeof(PhpValue));

                    // expr: <instance>.<field>
                    var expr = Bind(pctx, Expression.Convert(pinstance, Property.DeclaringType));

                    // expr: <property> := <value>
                    expr = Expression.Assign(expr, ConvertExpression.Bind(pvalue, expr.Type, pctx));    // TODO: PHP semantic (Operators.SetValue)

                    // {expr}: void
                    var lambda = Expression.Lambda(Expression.Block(expr, Expression.Empty()), pctx, pinstance, pvalue);

                    return((Action <Context, object, PhpValue>)lambda.Compile());
                });
            }
Пример #7
0
        internal TypeMethods(PhpTypeInfo type)
        {
            IEnumerable <MethodInfo> methods = type.Type
                                               .GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);

            // skip members of {System.Object} if we are in a PHP type
            if (type.Type.AsType() != typeof(object))
            {
                methods = methods.Where(s_notObjectMember);
            }

            // skip [PhpHidden] methods
            methods = methods.Where(s_notPhpHidden);

            // collect available methods (including methods on base classes)
            foreach (var m in methods.ToLookup(_MethodName, StringComparer.OrdinalIgnoreCase))
            {
                if (!ReflectionUtils.IsAllowedPhpName(m.Key))   // .ctor, .phpnew, implicit interface implementation
                {
                    continue;
                }

                if (_methods == null)
                {
                    _methods = new Dictionary <string, PhpMethodInfo>(StringComparer.OrdinalIgnoreCase);
                }

                var info = PhpMethodInfo.Create(_methods.Count + 1, m.Key, m.ToArray(), type);

                _methods[info.Name] = info;

                // resolve special methods
                var magic = MagicMethodByName(info.Name);
                if (magic != MagicMethods.undefined)
                {
                    if (_magicMethods == null)
                    {
                        _magicMethods = new Dictionary <MagicMethods, PhpMethodInfo>();
                    }

                    _magicMethods[magic] = info;
                }
            }
        }
Пример #8
0
        /// <summary>
        /// Gets <see cref="PhpTypeInfo"/> of given <paramref name="type"/>.
        /// </summary>
        public static PhpTypeInfo GetPhpTypeInfo(this Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            PhpTypeInfo result = null;
            var         handle = type.TypeHandle;

            // lookup cache first
            lock (s_cache)    // TODO: RW lock
            {
                s_cache.TryGetValue(handle, out result);
            }

            // invoke GetPhpTypeInfo<TType>() dynamically and cache the result
            if (result == null)
            {
                if (type.GetTypeInfo().IsGenericTypeDefinition)
                {
                    // generic type definition cannot be used as a type parameter for GetPhpTypeInfo<T>
                    // just instantiate the type info and cache the result
                    result = new PhpTypeInfo(type);
                }
                else
                {
                    // TypeInfoHolder<TType>.TypeInfo;
                    result = (PhpTypeInfo)s_getPhpTypeInfo_T
                             .MakeGenericMethod(type)
                             .Invoke(null, Utilities.ArrayUtils.EmptyObjects);
                }

                lock (s_cache)
                {
                    s_cache[handle] = result;
                }
            }

            //
            return(result);
        }
Пример #9
0
        /// <summary>
        /// Builds delegate that creates uninitialized class instance for purposes of deserialization.
        /// </summary>
        internal static Func <Context, object> BuildCreateEmptyObjectFunc(PhpTypeInfo tinfo)
        {
            Debug.Assert(tinfo != null);

            Func <Context, object> candidate = null;

            if (!tinfo.IsInterface && !tinfo.IsTrait)
            {
                var ctors = tinfo.Type.DeclaredConstructors;

                foreach (var c in ctors)
                {
                    if (c.IsStatic || c.IsPhpHidden())
                    {
                        continue;
                    }

                    if (c.IsPhpFieldsOnlyCtor())
                    {
                        // we want this one:
                        return((_ctx) => c.Invoke(new object[] { _ctx }));
                    }

                    var ps = c.GetParameters();
                    if (ps.Length == 0)
                    {
                        candidate = (_ctx) => c.Invoke(Array.Empty <object>());
                    }
                    if (ps.Length == 1 && ps[0].ParameterType == typeof(Context))
                    {
                        candidate = (_ctx) => c.Invoke(new object[] { _ctx });
                    }
                }
            }

            return(candidate
                   ?? ((_ctx) =>
            {
                Debug.Fail(string.Format(Resources.ErrResources.construct_not_supported, tinfo.Name));
                return null;
            }));
        }
Пример #10
0
        static void DeclareType(PhpTypeInfo info)
        {
            if (info.Index == 0)
            {
                var name = info.Name;
                int index;
                if (NameToIndex.TryGetValue(name, out index))
                {
                    throw new ArgumentException();  // redeclaration
                }
                else
                {
                    index = -AppTypes.Count - 1;
                    AppTypes.Add(info);
                    NameToIndex[name] = index;
                }

                info.Index = index;
            }
        }
        public static void DeclareAppType(PhpTypeInfo info)
        {
            if (info.Index == 0)
            {
                var name = info.Name;
                int index;
                if (_nameToIndex.TryGetValue(name, out index))
                {
                    throw new ArgumentException($"Type '{name}' already declared!");  // redeclaration
                }
                else
                {
                    index = -_appTypes.Count - 1;
                    _appTypes.Add(info);
                    _nameToIndex[name] = index;
                }

                info.Index = index;
            }
        }
Пример #12
0
        int EnsureTypeIndex(PhpTypeInfo info)
        {
            if (info.Index == 0)
            {
                TypesAppContext.RwLock.EnterWriteLock();
                try
                {
                    // double checked lock
                    if (info.Index == 0)
                    {
                        int index;
                        var name = info.Name;
                        if (_nameToIndex.TryGetValue(name, out index))
                        {
                            if (index < 0)  // redeclaring over an app context type
                            {
                                _redeclarationCallback(info);
                            }

                            info.Index = index;
                        }
                        else
                        {
                            index = _contextTypesCounter.GetNewIndex() + 1;
                            _nameToIndex[name] = index;
                        }

                        info.Index = index;
                    }
                }
                finally
                {
                    TypesAppContext.RwLock.ExitWriteLock();
                }
            }

            Debug.Assert(info.Index != 0);

            return(info.Index);
        }
Пример #13
0
        /// <summary>
        /// Gets descriptor representing a runtime field.
        /// Can be <c>null</c> if type does not support runtime fields.
        /// </summary>
        public static PhpPropertyInfo GetRuntimeProperty(this PhpTypeInfo tinfo, string propertyName, object instance)
        {
            if (tinfo.RuntimeFieldsHolder != null)
            {
                var key = new IntStringKey(propertyName);

                if (instance != null)
                {
                    var runtimefields = tinfo.GetRuntimeFields(instance);
                    if (runtimefields == null || runtimefields.Count == 0 || !runtimefields.ContainsKey(key))
                    {
                        return(null);
                    }
                }

                return(new PhpPropertyInfo.RuntimeProperty(tinfo, key));
            }
            else
            {
                return(null);
            }
        }
Пример #14
0
        /// <summary>
        /// Gets field name to be used as array key when casting object to array.
        /// </summary>
        static string FieldAsArrayKey(FieldInfo f, PhpTypeInfo declaringType)
        {
            Debug.Assert(f != null);
            Debug.Assert(declaringType != null);

            if (f.IsPublic)
            {
                return(f.Name);
            }
            if (f.IsFamily)
            {
                return(" * " + f.Name);
            }
            if (f.IsPrivate)
            {
                return(" " + declaringType.Name + " " + f.Name);
            }

            Debug.Fail($"Unexpected field attributes {f.Attributes}");

            return(f.Name);
        }
Пример #15
0
        /// <summary>
        /// Copies fields from <paramref name="source"/> object to <paramref name="target"/> object.
        /// Types of both objects must be the same.
        /// </summary>
        /// <param name="tinfo">Type of both objects.</param>
        /// <param name="source">Source instance.</param>
        /// <param name="target">Target instance.</param>
        public static void MemberwiseClone(PhpTypeInfo tinfo, object source, object target)
        {
            Debug.Assert(tinfo != null);
            Debug.Assert(source != null);
            Debug.Assert(target != null);
            Debug.Assert(source.GetType() == target.GetType());
            Debug.Assert(source.GetType() == tinfo.Type.AsType());

            // copy CLR fields, skipping runtime fields
            foreach (var fldvalue in TypeMembersUtils.EnumerateInstanceFields(source, (f, d) => f, null, null, true))
            {
                fldvalue.Key.SetValue(target, fldvalue.Value.DeepCopy());
            }

            // fast copy of runtime fields
            var runtime_fields = tinfo.GetRuntimeFields(source);

            if (runtime_fields != null && runtime_fields.Count != 0)
            {
                tinfo.RuntimeFieldsHolder.SetValue(target, runtime_fields.DeepCopy());
            }
        }
Пример #16
0
            public ClrFieldProperty(PhpTypeInfo tinfo, FieldInfo field)
                : base(tinfo)
            {
                _field = field ?? throw new ArgumentNullException(nameof(field));

                //
                _lazyGetter       = new Lazy <Func <object, PhpValue> >(() => (Func <object, PhpValue>)CompileAccess(AccessMask.Read));
                _lazyEnsureAlias  = new Lazy <Func <object, PhpAlias> >(() => (Func <object, PhpAlias>)CompileAccess(AccessMask.ReadRef));
                _lazyEnsureObject = new Lazy <Func <object, object> >(() => (Func <object, object>)CompileAccess(AccessMask.EnsureObject));
                _lazyEnsureArray  = new Lazy <Func <object, IPhpArray> >(() => (Func <object, IPhpArray>)CompileAccess(AccessMask.EnsureArray));

                // SetValue(instance, PhpValue): void
                _lazySetValue = new Lazy <Action <Context, object, PhpValue> >(() =>
                {
                    if (IsReadOnly)
                    {
                        // error
                        return(new Action <Context, object, PhpValue>((_, _instance, _value) =>
                        {
                            PhpException.ErrorException(Resources.ErrResources.readonly_property_written, ContainingType.Name, PropertyName);
                        }));
                    }

                    var pctx      = Expression.Parameter(typeof(Context));
                    var pinstance = Expression.Parameter(typeof(object));
                    var pvalue    = Expression.Parameter(typeof(PhpValue));

                    // expr: <instance>.<field>
                    var expr = Bind(pctx, Expression.Convert(pinstance, _field.DeclaringType));

                    // expr: <field> := <value>
                    expr = BinderHelpers.BindAccess(expr, pctx, AccessMask.Write, pvalue);

                    //
                    var lambda = Expression.Lambda(Expression.Block(expr, Expression.Empty()), pctx, pinstance, pvalue);

                    return((Action <Context, object, PhpValue>)lambda.Compile());
                });
            }
Пример #17
0
        /// <summary>
        /// Gets <see cref="PhpTypeInfo"/> of given <paramref name="type"/>.
        /// </summary>
        public static PhpTypeInfo GetPhpTypeInfo(this Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            PhpTypeInfo result = null;
            var         handle = type.TypeHandle;

            // lookup cache first
            lock (_cache)    // TODO: RW lock
            {
                _cache.TryGetValue(handle, out result);
            }

            // invoke GetPhpTypeInfo<TType>() dynamically and cache the result
            if (result == null)
            {
                if (_lazyGetPhpTypeInfo_T == null)
                {
                    _lazyGetPhpTypeInfo_T = typeof(PhpTypeInfoExtension).GetRuntimeMethod("GetPhpTypeInfo", Dynamic.Cache.Types.Empty);
                }

                // TypeInfoHolder<TType>.TypeInfo;
                result = (PhpTypeInfo)_lazyGetPhpTypeInfo_T
                         .MakeGenericMethod(type)
                         .Invoke(null, Utilities.ArrayUtils.EmptyObjects);

                lock (_cache)
                {
                    _cache[handle] = result;
                }
            }

            //
            return(result);
        }
Пример #18
0
        int EnsureTypeIndex(PhpTypeInfo info)
        {
            if (info.Index == 0)
            {
                s_rwLock.EnterWriteLock();
                try
                {
                    // double checked lock
                    if (info.Index == 0)
                    {
                        if (s_nameToIndex.TryGetValue(info.Name, out var index))
                        {
                            if (index < 0)  // redeclaring over an app context type
                            {
                                RedeclarationError(info);
                            }

                            info.Index = index;
                        }
                        else
                        {
                            index = s_contextTypesCounter.GetNewIndex();
                            s_nameToIndex[info.Name] = index;
                        }

                        info.Index = index;
                    }
                }
                finally
                {
                    s_rwLock.ExitWriteLock();
                }
            }

            Debug.Assert(info.Index != 0);

            return(info.Index);
        }
Пример #19
0
        /// <summary>
        /// Copies fields from <paramref name="source"/> object to <paramref name="target"/> object.
        /// Types of both objects must be the same.
        /// </summary>
        /// <param name="tinfo">Type of both objects.</param>
        /// <param name="source">Source instance.</param>
        /// <param name="target">Target instance.</param>
        public static void MemberwiseClone(PhpTypeInfo tinfo, object source, object target)
        {
            Debug.Assert(tinfo != null);
            Debug.Assert(source != null);
            Debug.Assert(target != null);
            Debug.Assert(source.GetType() == target.GetType());
            Debug.Assert(source.GetType() == tinfo.Type.AsType());

            // copy CLR fields, skipping runtime fields
            foreach (var prop in TypeMembersUtils.EnumerateInstanceFields(source, Utilities.FuncExtensions.Identity <PhpPropertyInfo>(), null, null, ignoreRuntimeFields: true))
            {
                var value = prop.Value.DeepCopy();
                prop.Key.SetValue(null, target, value);
            }

            // fast copy of runtime fields
            var runtime_fields = tinfo.GetRuntimeFields(source);

            if (runtime_fields != null && runtime_fields.Count != 0)
            {
                tinfo.RuntimeFieldsHolder.SetValue(target, runtime_fields.DeepCopy());
            }
        }
Пример #20
0
        public void DeclareTypeAlias(PhpTypeInfo info, string name)
        {
            if (s_nameToIndex.TryGetValue(name, out var index))
            {
                if (index < 0)  // redeclaring over an app context type
                {
                    RedeclarationError(info);
                }
            }
            else
            {
                index = s_contextTypesCounter.GetNewIndex();
                s_nameToIndex[name] = index;
            }

            //
            if (_contextTypes.Length < index)
            {
                Array.Resize(ref _contextTypes, index * 2);
            }

            DeclareType(ref _contextTypes[index - 1], info);
        }
Пример #21
0
 protected PhpPropertyInfo(PhpTypeInfo tinfo)
 {
     ContainingType = tinfo ?? throw new ArgumentNullException(nameof(tinfo));
 }
Пример #22
0
 public RuntimeProperty(PhpTypeInfo tinfo, IntStringKey name)
     : base(tinfo)
 {
     _name = name;
 }
Пример #23
0
        internal TypeMethods(PhpTypeInfo type)
        {
            // note: GetMethods() ignores "private" members on subclasses
            IEnumerable <MethodInfo> methods = type.Type
                                               .GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);

            int index = 0;

            // skip members of {System.Object} if we are in a PHP type
            if (type.Type != typeof(object))
            {
                methods = methods.Where(s_notObjectMember);
            }

            // skip [PhpHidden] methods and hidden methods (internal, private protected)
            methods = methods.Where(s_phpvisible);

            // collect available methods (including methods on base classes)
            foreach (var m in methods.ToLookup(_MethodName, StringComparer.OrdinalIgnoreCase))
            {
                if (!ReflectionUtils.IsAllowedPhpName(m.Key))   // .ctor, implicit interface implementation
                {
                    continue;
                }

                var overrides = m.ToArray();

                // ignore methods in base classes that has been "overriden" in current class
                // in PHP we do override even if signature does not match (e.g. __construct)
                SelectVisibleOverrides(ref overrides);

                var magic = MagicMethods.undefined;

                if (IsSpecialName(overrides))
                {
                    // 'specialname' methods,
                    // get_Item, set_Item
                    if (!Enum.TryParse <MagicMethods>(m.Key.ToLowerInvariant(), out magic))
                    {
                        continue;
                    }
                }

                // TODO: negative {index} in case of non-user method

                var info = PhpMethodInfo.Create(++index, m.Key, overrides, type);

                if (magic == MagicMethods.undefined)
                {
                    _methods ??= new Dictionary <string, PhpMethodInfo>(StringComparer.OrdinalIgnoreCase);
                    _methods[info.Name] = info;

                    // resolve magic methods
                    magic = MagicMethodByName(info.Name);
                }

                if (magic != MagicMethods.undefined)
                {
                    _magicMethods ??= new Dictionary <MagicMethods, PhpMethodInfo>();
                    _magicMethods[magic] = info;
                }
            }
        }
Пример #24
0
 /// <summary>
 /// Enumerates self, all base types and all inherited interfaces.
 /// </summary>
 public static IEnumerable <PhpTypeInfo> EnumerateTypeHierarchy(this PhpTypeInfo phptype)
 {
     return(EnumerateClassHierarchy(phptype).Concat(                   // phptype + base types
                phptype.Type.GetInterfaces().Select(GetPhpTypeInfo))); // inherited interfaces
 }
Пример #25
0
 /// <summary>
 /// Returns names and properties of all instance properties or only PHP fields (including runtime fields).
 /// </summary>
 /// <param name="instance">The instance being serialized.</param>
 /// <param name="tinfo"><paramref name="instance"/> type info.</param>
 /// <returns>Name-value pairs. Names are properly formatted for serialization.</returns>
 public static IEnumerable <KeyValuePair <string, PhpValue> > EnumerateSerializableProperties(object /*!*/ instance, PhpTypeInfo tinfo)
 {
     return(TypeMembersUtils.EnumerateInstanceFields(instance,
                                                     FormatSerializedPropertyName,
                                                     TypeMembersUtils.s_keyToString,
                                                     (m) => !m.IsReadOnly));
 }
Пример #26
0
 /// <summary>
 /// Invoked when a type is redeclared.
 /// </summary>
 /// <param name="type">The type being declared, but another with the same name is already declared in context.</param>
 static void RedeclarationError(PhpTypeInfo type)
 {
     // TODO: ErrCode & throw, Log
     throw new InvalidOperationException($"Type {type.Name} redeclared!");
 }
Пример #27
0
 /// <summary>
 /// Resolves a method by its name, visible in given <paramref name="context"/>.
 /// </summary>
 /// <param name="tinfo">Type info.</param>
 /// <param name="name">Method name.</param>
 /// <param name="context">Current class context.</param>
 /// <returns>Resolved method or <c>null</c>.</returns>
 public static RoutineInfo GetVisibleMethod(this PhpTypeInfo /*!*/ tinfo, string /*!*/ name, RuntimeTypeHandle context = default)
 {
     var tcontext = context.Equals(default) ? null : Type.GetTypeFromHandle(context);
Пример #28
0
 public PhpMethodInfoWithBoundType(int index, string name, MethodInfo[] methods, PhpTypeInfo lateStaticType)
     : base(index, name, methods)
 {
     Debug.Assert(lateStaticType != null);
     _lateStaticType = lateStaticType;
 }
Пример #29
0
 internal bool IsDeclared(PhpTypeInfo type)
 {
     return(type.Index > 0 && type.Index <= _contextTypes.Length && _contextTypes[type.Index - 1] == type);
 }
Пример #30
0
        /// <summary>
        /// Creates instance of <see cref="PhpMethodInfo"/>.
        /// </summary>
        public static PhpMethodInfo Create(int index, string name, MethodInfo[] methods, PhpTypeInfo callertype = null)
        {
            if (callertype != null)
            {
                if (callertype.Type.IsClass && methods.Any(Dynamic.BinderHelpers.HasLateStaticParameter))
                {
                    // if method requires late static bound type, remember it:
                    return(new PhpMethodInfoWithBoundType(index, name, methods, lateStaticType: callertype));
                }

                // reuse PhpMethodInfo from base type if possible
                if (AllDeclaredInBase(methods, callertype.Type) &&
                    callertype.BaseType != null &&
                    callertype.BaseType.RuntimeMethods[name] is PhpMethodInfo frombase)
                {
                    return(frombase);
                }
            }

            return(new PhpMethodInfo(index, name, methods));
        }