File: Microsoft\CSharp\RuntimeBinder\SymbolTable.cs
Web Access
Project: src\src\libraries\Microsoft.CSharp\src\Microsoft.CSharp.csproj (Microsoft.CSharp)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.CSharp.RuntimeBinder.Semantics;
using Microsoft.CSharp.RuntimeBinder.Syntax;
 
namespace Microsoft.CSharp.RuntimeBinder
{
    internal static class SymbolTable
    {
        private const BindingFlags EverythingBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
        private static readonly HashSet<Type> s_typesWithConversionsLoaded = new HashSet<Type>();
        private static readonly HashSet<NameHashKey> s_namesLoadedForEachType = new HashSet<NameHashKey>();
 
        private readonly struct NameHashKey : IEquatable<NameHashKey>
        {
            internal Type Type { get; }
            internal string Name { get; }
 
            public NameHashKey(Type type, string name)
            {
                Type = type;
                Name = name;
            }
 
            public bool Equals(NameHashKey other) => Type.Equals(other.Type) && Name.Equals(other.Name);
 
#if  DEBUG
            [ExcludeFromCodeCoverage(Justification = "Typed overload should always be the method called")]
#endif
            public override bool Equals(object obj)
            {
                Debug.Fail("Sub-optimal overload called. Check if this can be avoided.");
                return obj is NameHashKey key && Equals(key);
            }
 
            public override int GetHashCode() => Type.GetHashCode() ^ Name.GetHashCode();
        }
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        internal static void PopulateSymbolTableWithName(
            string name,
            IEnumerable<Type> typeArguments,
            Type callingType)
        {
            // The first argument is the object that we're calling off of.
            if (callingType.IsGenericType)
            {
                callingType = callingType.GetGenericTypeDefinition();
            }
 
            if (name == SpecialNames.Indexer)
            {
                // If we don't find an indexer name for this type, use SpecialNames.Indexer as a key on the
                // empty results we'll get, so that those empty results gets cached.
                name = callingType.GetIndexerName() ?? SpecialNames.Indexer;
            }
 
            NameHashKey key = new NameHashKey(callingType, name);
 
            // If we've already populated this name/type pair, then just leave.
            if (s_namesLoadedForEachType.Contains(key))
            {
                return;
            }
 
            // Add the names.
            AddNamesOnType(key);
 
            // Take each type argument and load its conversions into the symbol table.
            if (typeArguments != null)
            {
                foreach (Type o in typeArguments)
                {
                    AddConversionsForType(o);
                }
            }
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        internal static SymWithType LookupMember(
            string name,
            Expr callingObject,
            ParentSymbol context,
            int arity,
            MemberLookup mem,
            bool allowSpecialNames,
            bool requireInvocable)
        {
            CType type = callingObject.Type;
 
            if (type is ArrayType)
            {
                type = SymbolLoader.GetPredefindType(PredefinedType.PT_ARRAY);
            }
            if (type is NullableType nub)
            {
                type = nub.GetAts();
            }
 
            if (!mem.Lookup(
                type,
                callingObject,
                context,
                GetName(name),
                arity,
                (allowSpecialNames ? 0 : MemLookFlags.UserCallable) |
                    (name == SpecialNames.Indexer ? MemLookFlags.Indexer : 0) |
                    (name == SpecialNames.Constructor ? MemLookFlags.Ctor : 0) |
                    (requireInvocable ? MemLookFlags.MustBeInvocable : 0)))
            {
                return null;
            }
            return mem.SwtFirst();
        }
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static void AddParameterConversions(MethodBase method)
        {
            foreach (ParameterInfo param in method.GetParameters())
            {
                AddConversionsForType(param.ParameterType);
            }
        }
 
        #region InheritanceHierarchy
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static void AddNamesOnType(NameHashKey key)
        {
            Debug.Assert(!s_namesLoadedForEachType.Contains(key));
 
            // We need to declare all of its inheritance hierarchy.
            List<Type> inheritance = CreateInheritanceHierarchyList(key.Type);
 
            // Now add every method as it appears in the inheritance hierarchy.
            AddNamesInInheritanceHierarchy(key.Name, inheritance);
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static void AddNamesInInheritanceHierarchy(string name, List<Type> inheritance)
        {
            for (int i = inheritance.Count - 1; i >= 0; --i)
            {
                Type type = inheritance[i];
                if (type.IsGenericType)
                {
                    type = type.GetGenericTypeDefinition();
                }
 
                if (!s_namesLoadedForEachType.Add(new NameHashKey(type, name)))
                {
                    continue;
                }
 
                // Now loop over all methods and add them.
                IEnumerator<MemberInfo> memberEn = type
                    .GetMembers(EverythingBindingFlags)
                    .Where(member => member.DeclaringType == type && member.Name == name).GetEnumerator();
                if (memberEn.MoveNext())
                {
                    List<EventInfo> events = null;
                    CType cType = GetCTypeFromType(type);
                    if (!(cType is AggregateType aggType))
                        continue;
                    AggregateSymbol aggregate = aggType.OwningAggregate;
                    FieldSymbol addedField = null;
 
                    // We need to add fields before the actual events, so do the first iteration
                    // excluding events.
                    do
                    {
                        MemberInfo member = memberEn.Current;
                        if (member is MethodInfo method)
                        {
                            MethodKindEnum kind = member.Name switch
                            {
                                SpecialNames.Invoke => MethodKindEnum.Invoke,
                                SpecialNames.ImplicitConversion => MethodKindEnum.ImplicitConv,
                                SpecialNames.ExplicitConversion => MethodKindEnum.ExplicitConv,
                                _ => MethodKindEnum.Actual,
                            };
                            AddMethodToSymbolTable(method, aggregate, kind);
                            AddParameterConversions(method);
                        }
                        else if (member is ConstructorInfo ctor)
                        {
                            AddMethodToSymbolTable(ctor, aggregate, MethodKindEnum.Constructor);
                            AddParameterConversions(ctor);
                        }
                        else if (member is PropertyInfo prop)
                        {
                            AddPropertyToSymbolTable(prop, aggregate);
                        }
                        else if (member is FieldInfo field)
                        {
                            // Store this field so that if we also find an event, we can
                            // mark it as the backing field of the event.
                            Debug.Assert(addedField == null);
                            addedField = AddFieldToSymbolTable(field, aggregate);
                        }
                        else if (member is EventInfo e)
                        {
                            // Store events until after all fields
                            (events ??= new List<EventInfo>()).Add(e);
                        }
                    } while (memberEn.MoveNext());
 
                    if (events != null)
                    {
                        foreach (EventInfo e in events)
                        {
                            AddEventToSymbolTable(e, aggregate, addedField);
                        }
                    }
                }
            }
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static List<Type> CreateInheritanceHierarchyList(Type type)
        {
            List<Type> list;
            if (type.IsInterface)
            {
                Type[] ifaces = type.GetInterfaces();
 
                list = new List<Type>(ifaces.Length + 2)
                {
                    type
                };
                foreach (Type iface in type.GetInterfaces())
                {
                    LoadSymbolsFromType(iface);
                    list.Add(iface);
                }
 
                Type obj = typeof(object);
                LoadSymbolsFromType(obj);
                list.Add(obj);
            }
            else
            {
                list = new List<Type> { type };
                for (Type parent = type.BaseType; parent != null; parent = parent.BaseType)
                {
                    // Load it in the symbol table.
                    LoadSymbolsFromType(parent);
 
                    // Insert into our list of Types.
                    list.Add(parent);
                }
            }
            return list;
        }
        #endregion
 
        #region GetName
        /////////////////////////////////////////////////////////////////////////////////
 
        private static Name GetName(string p) => NameManager.Add(p ?? "");
 
        /////////////////////////////////////////////////////////////////////////////////
 
        private static Name GetName(Type type)
        {
            string name = type.Name;
            if (type.IsGenericType)
            {
                int idx = name.IndexOf('`');
                if (idx >= 0)
                {
                    return NameManager.Add(name, idx);
                }
            }
 
            return NameManager.Add(name);
        }
 
        #endregion
 
        #region TypeParameters
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static TypeArray GetMethodTypeParameters(MethodInfo method, MethodSymbol parent)
        {
            if (method.IsGenericMethod)
            {
                Type[] genericArguments = method.GetGenericArguments();
                CType[] ctypes = new CType[genericArguments.Length];
                for (int i = 0; i < genericArguments.Length; i++)
                {
                    Type t = genericArguments[i];
                    ctypes[i] = LoadMethodTypeParameter(parent, t);
                }
 
                // After we load the type parameters, we need to resolve their bounds.
                for (int i = 0; i < genericArguments.Length; i++)
                {
                    Type t = genericArguments[i];
                    ((TypeParameterType)ctypes[i]).Symbol.SetBounds(TypeArray.Allocate(GetCTypeArrayFromTypes(t.GetGenericParameterConstraints())));
                }
 
                return TypeArray.Allocate(ctypes);
            }
 
            return TypeArray.Empty;
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static TypeArray GetAggregateTypeParameters(Type type, AggregateSymbol agg)
        {
            if (type.IsGenericType)
            {
                Type genericDefinition = type.GetGenericTypeDefinition();
                Type[] genericArguments = genericDefinition.GetGenericArguments();
                List<CType> ctypes = new List<CType>();
                int outerParameters = agg.isNested() ? agg.GetOuterAgg().GetTypeVarsAll().Count : 0;
 
                for (int i = 0; i < genericArguments.Length; i++)
                {
                    // Suppose we have the following:
                    //
                    // class A<A1, A2, ..., An>
                    // {
                    //     class B<B1, B2, ..., Bm>
                    //     {
                    //     }
                    // }
                    //
                    // B will have m+n generic arguments - { A1, A2, ..., An, B1, B2, ..., Bn }.
                    // As we enumerate these, we need to skip type parameters whose GenericParameterPosition
                    // is less than n, since the first n type parameters are { A1, A2, ..., An }.
 
                    Type t = genericArguments[i];
 
                    if (t.GenericParameterPosition < outerParameters)
                    {
                        continue;
                    }
 
                    CType ctype;
                    if (t.IsGenericParameter && t.DeclaringType == genericDefinition)
                    {
                        ctype = LoadClassTypeParameter(agg, t);
                    }
                    else
                    {
                        ctype = GetCTypeFromType(t);
                    }
 
                    // We check to make sure we own the type parameter - this is because we're
                    // currently calculating TypeArgsThis, NOT TypeArgsAll.
                    if (((TypeParameterType)ctype).OwningSymbol == agg)
                    {
                        ctypes.Add(ctype);
                    }
                }
 
                return TypeArray.Allocate(ctypes.ToArray());
            }
 
            return TypeArray.Empty;
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static TypeParameterType LoadClassTypeParameter(AggregateSymbol parent, Type t)
        {
            for (AggregateSymbol p = parent; p != null; p = p.parent as AggregateSymbol)
            {
                for (TypeParameterSymbol typeParam = SymbolStore.LookupSym(
                        GetName(t), p, symbmask_t.MASK_TypeParameterSymbol) as TypeParameterSymbol;
                    typeParam != null;
                    typeParam = typeParam.LookupNext(symbmask_t.MASK_TypeParameterSymbol) as TypeParameterSymbol)
                {
                    if (AreTypeParametersEquivalent(typeParam.GetTypeParameterType().AssociatedSystemType, t))
                    {
                        return typeParam.GetTypeParameterType();
                    }
                }
            }
            return AddTypeParameterToSymbolTable(parent, null, t, true);
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        private static bool AreTypeParametersEquivalent(Type t1, Type t2)
        {
            Debug.Assert(t1.IsGenericParameter && t2.IsGenericParameter);
 
            if (t1 == t2)
            {
                return true;
            }
 
            Type t1Original = GetOriginalTypeParameterType(t1);
            Type t2Original = GetOriginalTypeParameterType(t2);
 
            return t1Original == t2Original;
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        // GetOriginalTypeParameterType
        // This was added so that LoadClassTypeParameter would not fail to find outer
        // type parameters when given a System.Type from an outer class and a matching
        // type parameter from in an inner class. In Reflection type parameters are
        // always declared by their inner most declaring class. For example, given:
        //
        //   class A<T> {
        //     class B<U> { }
        //   }
        //
        // in Reflection there are two Ts, A<T>'s T, and B<U>'s T. In our world there is
        // only A<T>'s T.
        //
        // So this method here drills down and finds the type parameter type corresponding
        // to the position of the given type parameter, from the outer most containing
        // type. So in the above example, given B<U>'s T from reflection, this will return
        // A<T>'s T, so that you can make a reference comparison of type parameters coming
        // from different nesting levels.
        //
        // There is an exception, we don't handle the case where you have type parameters
        // coming from different partially constructed methods. E.g.
        //
        //   class A<T,S> {
        //     public void M<U> { }
        //   }
        //
        //   A<T,int>.M<U>
        //   A<T,string>.M<U>
        //
        // In the above two methods, the two U's are different in Reflection. Here we just
        // return the type parameter type given if it is in a method, we do not try to
        // generalize these occurrences for reference equality.
        //
        private static Type GetOriginalTypeParameterType(Type t)
        {
            Debug.Assert(t.IsGenericParameter);
 
            int pos = t.GenericParameterPosition;
 
            Type parentType = t.DeclaringType;
            if (parentType != null && parentType.IsGenericType)
            {
                parentType = parentType.GetGenericTypeDefinition();
            }
 
            if (t.DeclaringMethod != null)
            {
                if (parentType.GetGenericArguments() == null || pos >= parentType.GetGenericArguments().Length)
                {
                    return t;
                }
            }
 
            while (parentType.GetGenericArguments().Length > pos)
            {
                Type nextParent = parentType.DeclaringType;
                if (nextParent != null && nextParent.IsGenericType)
                {
                    nextParent = nextParent.GetGenericTypeDefinition();
                }
 
                if (nextParent?.GetGenericArguments()?.Length > pos)
                {
                    parentType = nextParent;
                }
                else
                {
                    break;
                }
            }
 
            return parentType.GetGenericArguments()[pos];
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static TypeParameterType LoadMethodTypeParameter(MethodSymbol parent, Type t)
        {
            for (Symbol sym = parent.firstChild; sym != null; sym = sym.nextChild)
            {
                if (sym is TypeParameterSymbol parSym)
                {
                    TypeParameterType type = parSym.GetTypeParameterType();
                    if (AreTypeParametersEquivalent(type.AssociatedSystemType, t))
                    {
                        return type;
                    }
                }
            }
 
            return AddTypeParameterToSymbolTable(null, parent, t, false);
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        private static TypeParameterType AddTypeParameterToSymbolTable(
                AggregateSymbol agg,
                MethodSymbol meth,
                Type t,
                bool bIsAggregate)
        {
            Debug.Assert((agg != null && bIsAggregate) || (meth != null && !bIsAggregate));
 
            TypeParameterSymbol typeParam;
            if (bIsAggregate)
            {
                typeParam = SymFactory.CreateClassTypeParameter(
                    GetName(t),
                    agg,
                    t.GenericParameterPosition,
                    t.GenericParameterPosition);
            }
            else
            {
                typeParam = SymFactory.CreateMethodTypeParameter(
                    GetName(t),
                    meth,
                    t.GenericParameterPosition,
                    t.GenericParameterPosition);
            }
 
            if ((t.GenericParameterAttributes & GenericParameterAttributes.Covariant) != 0)
            {
                typeParam.Covariant = true;
            }
            if ((t.GenericParameterAttributes & GenericParameterAttributes.Contravariant) != 0)
            {
                typeParam.Contravariant = true;
            }
 
            SpecCons cons = SpecCons.None;
 
            if ((t.GenericParameterAttributes & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
            {
                cons |= SpecCons.New;
            }
            if ((t.GenericParameterAttributes & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
            {
                cons |= SpecCons.Ref;
            }
            if ((t.GenericParameterAttributes & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
            {
                cons |= SpecCons.Val;
            }
 
            typeParam.SetConstraints(cons);
            typeParam.SetAccess(ACCESS.ACC_PUBLIC);
            TypeParameterType typeParamType = TypeManager.GetTypeParameter(typeParam);
 
            return typeParamType;
        }
 
        #endregion
 
        #region LoadTypeChain
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static CType LoadSymbolsFromType(Type type)
        {
            List<object> declarationChain = BuildDeclarationChain(type);
 
            NamespaceOrAggregateSymbol current = NamespaceSymbol.Root;
 
            // Go through the declaration chain and add namespaces and types for
            // each element in the chain.
            for (int i = 0; i < declarationChain.Count; i++)
            {
                object o = declarationChain[i];
                if (o is Type t)
                {
                    if (t.IsNullableType())
                    {
                        return TypeManager.GetNullable(GetCTypeFromType(t.GetGenericArguments()[0]));
                    }
 
                    AggregateSymbol next = FindSymForType(
                        SymbolStore.LookupSym(GetName(t), current, symbmask_t.MASK_AggregateSymbol), t);
 
                    // If we haven't found this type yet, then add it to our symbol table.
                    if (next == null)
                    {
                        // Note that if we have anything other than an AggregateSymbol,
                        // we must be at the end of the line - that is, nothing else can
                        // have children.
                        CType ctype = ProcessSpecialTypeInChain(current, t);
                        if (ctype != null)
                        {
                            Debug.Assert(!(ctype is AggregateType));
                            return ctype;
                        }
 
                        // This is a regular class.
                        next = AddAggregateToSymbolTable(current, t);
                    }
 
                    if (t == type)
                    {
                        return GetConstructedType(type, next);
                    }
 
                    current = next;
                }
                else if (o is MethodInfo m)
                {
                    // We cant be at the end.
                    Debug.Assert(i + 1 < declarationChain.Count);
                    return ProcessMethodTypeParameter(m, declarationChain[++i] as Type, current as AggregateSymbol);
                }
                else
                {
                    Debug.Assert(o is string);
                    current = AddNamespaceToSymbolTable(current, o as string);
                }
            }
 
            Debug.Fail("Should be unreachable");
            return null;
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static TypeParameterType ProcessMethodTypeParameter(MethodInfo methinfo, Type t, AggregateSymbol parent)
        {
            MethodSymbol meth = FindMatchingMethod(methinfo, parent);
            if (meth == null)
            {
                meth = AddMethodToSymbolTable(methinfo, parent, MethodKindEnum.Actual);
 
                // Because we return null from AddMethodToSymbolTable when we have a MethodKindEnum.Actual
                // and the method that we're trying to add is a special name, we need to assert that
                // we indeed have added a method. This is because no special name should have a method
                // type parameter on it.
                Debug.Assert(meth != null);
            }
 
            return LoadMethodTypeParameter(meth, t);
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static CType GetConstructedType(Type type, AggregateSymbol agg)
        {
            // We've found the one we want, so return it.
            if (type.IsGenericType)
            {
                // If we're a generic type, then we need to add the type arguments.
                List<CType> types = new List<CType>();
 
                foreach (Type argument in type.GetGenericArguments())
                {
                    types.Add(GetCTypeFromType(argument));
                }
 
                TypeArray typeArray = TypeArray.Allocate(types.ToArray());
                AggregateType aggType = TypeManager.GetAggregate(agg, typeArray);
                return aggType;
            }
            CType ctype = agg.getThisType();
            return ctype;
        }
 
        /////////////////////////////////////////////////////////////////////////////////
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static CType ProcessSpecialTypeInChain(NamespaceOrAggregateSymbol parent, Type t)
        {
            if (t.IsGenericParameter)
            {
                AggregateSymbol agg = parent as AggregateSymbol;
                Debug.Assert(agg != null);
                return LoadClassTypeParameter(agg, t);
            }
 
            if (t.IsArray)
            {
                // Now we return an array of nesting level corresponding to the rank.
                return TypeManager.GetArray(
                    GetCTypeFromType(t.GetElementType()),
                    t.GetArrayRank(),
                    t.GetElementType().MakeArrayType() == t
                    );
            }
 
            if (t.IsPointer)
            {
                // Now we return the pointer type that we want.
                return TypeManager.GetPointer(GetCTypeFromType(t.GetElementType()));
            }
 
            return null;
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static List<object> BuildDeclarationChain(Type callingType)
        {
            // We need to build the parent chain of the calling type. Since we only
            // have the type itself, first we need to build the chain up from the
            // type down to the root namespace, then we need to ensure that
            // the chain exists in our symbol table by searching from the root namespace
            // back down to the calling type. Also note that if we have a method type
            // parameter, then we'll also add the MethodBase to the chain.
            //
            // Note that we'll populate this list in a hybrid way - we'll add the
            // types for the type part of the chain, and we'll just add the string names
            // of the namespaces.
 
            // Strip off the ref-ness.
            if (callingType.IsByRef)
            {
                callingType = callingType.GetElementType();
            }
 
            List<object> callChain = new List<object>();
            for (Type t = callingType; t != null; t = t.DeclaringType)
            {
                callChain.Add(t);
 
                if (t.IsGenericParameter && t.DeclaringMethod != null)
                {
                    MethodBase methodBase = t.DeclaringMethod;
                    bool bAdded = false;
                    foreach (MethodInfo methinfo in t.DeclaringType.GetMethods(EverythingBindingFlags))
                    {
                        if (!methinfo.HasSameMetadataDefinitionAs(methodBase) || !methinfo.IsGenericMethod)
                        {
                            continue;
                        }
 
                        Debug.Assert(!bAdded);
                        callChain.Add(methinfo);
                        bAdded = true;
                    }
                    Debug.Assert(bAdded);
                }
            }
 
            callChain.Reverse();
 
            // Now take out the namespaces and add them to the end of the chain.
            if (callingType.Namespace != null)
            {
                callChain.InsertRange(0, callingType.Namespace.Split('.'));
            }
            return callChain;
        }
 
        // We have an aggregate symbol of the correct parent and full name, but it may have the wrong arity, or, due to
        // dynamic loading or creation, two different types can exist that have the same name.
 
        // In the static compiler, this would have been an error and name lookup would be ambiguous, but here we never have
        // to lookup names of types for real (only names of members).
 
        // For either case, move onto the next symbol in the chain, and check again for appropriate type.
        private static AggregateSymbol FindSymForType(Symbol sym, Type t)
        {
            while (sym != null)
            {
                // We use "IsEquivalentTo" so that unified local types match.
                if (sym is AggregateSymbol agg)
                    if (agg.AssociatedSystemType.IsEquivalentTo(t.IsGenericType ? t.GetGenericTypeDefinition() : t))
                    {
                        return agg;
                    }
 
                sym = sym.nextSameName;
            }
 
            return null;
        }
 
        private static NamespaceSymbol AddNamespaceToSymbolTable(NamespaceOrAggregateSymbol parent, string sz)
        {
            Name name = GetName(sz);
            return SymbolStore.LookupSym(name, parent, symbmask_t.MASK_NamespaceSymbol) as NamespaceSymbol
                ?? SymFactory.CreateNamespace(name, parent as NamespaceSymbol);
        }
        #endregion
 
        #region CTypeFromType
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        internal static CType[] GetCTypeArrayFromTypes(Type[] types)
        {
            Debug.Assert(types != null);
 
            int length = types.Length;
            if (length == 0)
            {
                return Array.Empty<CType>();
            }
 
            CType[] ctypes = new CType[length];
            for (int i = 0; i < types.Length; i++)
            {
                Type t = types[i];
                Debug.Assert(t != null);
                ctypes[i] = GetCTypeFromType(t);
            }
 
            return ctypes;
        }
 
        /////////////////////////////////////////////////////////////////////////////////
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        internal static CType GetCTypeFromType(Type type) => type.IsByRef
            ? TypeManager.GetParameterModifier(LoadSymbolsFromType(type.GetElementType()), false)
            : LoadSymbolsFromType(type);
 
        #endregion
 
        #region Aggregates
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static AggregateSymbol AddAggregateToSymbolTable(
            NamespaceOrAggregateSymbol parent,
            Type type)
        {
            AggregateSymbol agg = SymFactory.CreateAggregate(GetName(type), parent);
            agg.AssociatedSystemType = type.IsGenericType ? type.GetGenericTypeDefinition() : type;
            agg.AssociatedAssembly = type.Assembly;
 
            // We have to set the TypeVars, access, and the AggKind before we can set the aggState
            // because of the assertion checking the compiler does.
            AggKindEnum kind;
            if (type.IsInterface)
            {
                kind = AggKindEnum.Interface;
            }
            else if (type.IsEnum)
            {
                kind = AggKindEnum.Enum;
                agg.SetUnderlyingType((AggregateType)GetCTypeFromType(Enum.GetUnderlyingType(type)));
            }
            else if (type.IsValueType)
            {
                kind = AggKindEnum.Struct;
            }
            else
            {
                // If it derives from Delegate or MulticastDelegate, then its
                // a delegate type. However, MuticastDelegate itself is not a
                // delegate type.
                if (type.BaseType != null &&
                    (type.BaseType.FullName == "System.MulticastDelegate" ||
                    type.BaseType.FullName == "System.Delegate") &&
                    type.FullName != "System.MulticastDelegate")
                {
                    kind = AggKindEnum.Delegate;
                }
                else
                {
                    kind = AggKindEnum.Class;
                }
            }
            agg.SetAggKind(kind);
            agg.SetTypeVars(TypeArray.Empty);
 
            ACCESS access;
            if (type.IsPublic)
            {
                access = ACCESS.ACC_PUBLIC;
            }
            else if (type.IsNested)
            {
                // If its nested, we may have other accessibility options.
                if (type.IsNestedAssembly)
                {
                    access = ACCESS.ACC_INTERNAL;
                }
                else if (type.IsNestedFamORAssem)
                {
                    access = ACCESS.ACC_INTERNALPROTECTED;
                }
                else if (type.IsNestedPrivate)
                {
                    access = ACCESS.ACC_PRIVATE;
                }
                else if (type.IsNestedFamily)
                {
                    access = ACCESS.ACC_PROTECTED;
                }
                else if (type.IsNestedFamANDAssem)
                {
                    access = ACCESS.ACC_INTERNAL_AND_PROTECTED;
                }
                else
                {
                    Debug.Assert(type.IsPublic || type.IsNestedPublic);
                    access = ACCESS.ACC_PUBLIC;
                }
            }
            else
            {
                // We're not public and we're not nested - we must be internal.
                access = ACCESS.ACC_INTERNAL;
            }
            agg.SetAccess(access);
 
            if (!type.IsGenericParameter)
            {
                agg.SetTypeVars(GetAggregateTypeParameters(type, agg));
            }
 
            if (type.IsGenericType)
            {
                Type genericDefinition = type.GetGenericTypeDefinition();
                Type[] genericArguments = genericDefinition.GetGenericArguments();
 
                // After we load the type parameters, we need to resolve their bounds.
                for (int i = 0; i < agg.GetTypeVars().Count; i++)
                {
                    Type t = genericArguments[i];
                    if (agg.GetTypeVars()[i] is TypeParameterType typeVar)
                    {
                        typeVar.Symbol.SetBounds(TypeArray.Allocate(GetCTypeArrayFromTypes(t.GetGenericParameterConstraints())));
                    }
                }
            }
 
            agg.SetAbstract(type.IsAbstract);
 
            {
                string typeName = type.FullName;
                if (type.IsGenericType)
                {
                    typeName = type.GetGenericTypeDefinition().FullName;
                }
                if (typeName != null)
                {
                    PredefinedType predefinedType = PredefinedTypeFacts.TryGetPredefTypeIndex(typeName);
                    if (predefinedType != PredefinedType.PT_UNDEFINEDINDEX)
                    {
                        PredefinedTypes.InitializePredefinedType(agg, predefinedType);
                    }
                }
            }
 
            agg.SetSealed(type.IsSealed);
            if (type.BaseType != null)
            {
                // type.BaseType can be null for Object or for interface types.
                Type t = type.BaseType;
                if (t.IsGenericType)
                {
                    t = t.GetGenericTypeDefinition();
                }
                agg.SetBaseClass((AggregateType)GetCTypeFromType(t));
            }
 
            agg.SetFirstUDConversion(null);
            SetInterfacesOnAggregate(agg, type);
            agg.SetHasPubNoArgCtor(type.GetConstructor(Type.EmptyTypes) != null);
 
            // If we have a delegate, get its invoke and constructor methods as well.
            if (agg.IsDelegate())
            {
                PopulateSymbolTableWithName(SpecialNames.Constructor, null, type);
                PopulateSymbolTableWithName(SpecialNames.Invoke, null, type);
            }
 
            return agg;
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static void SetInterfacesOnAggregate(AggregateSymbol aggregate, Type type)
        {
            if (type.IsGenericType)
            {
                type = type.GetGenericTypeDefinition();
            }
            Type[] interfaces = type.GetInterfaces();
 
            // We won't be able to find the difference between Ifaces and
            // IfacesAll anymore - at runtime, the class implements all of its
            // Ifaces and IfacesAll, so theres no way to differentiate.
            //
            // This actually doesn't matter though - for conversions and methodcalls,
            // we don't really care where they've come from as long as we know the overall
            // set of IfacesAll.
 
            aggregate.SetIfaces(TypeArray.Allocate(GetCTypeArrayFromTypes(interfaces)));
            aggregate.SetIfacesAll(aggregate.GetIfaces());
        }
        #endregion
 
        #region Field
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static FieldSymbol AddFieldToSymbolTable(FieldInfo fieldInfo, AggregateSymbol aggregate)
        {
            FieldSymbol field = SymbolStore.LookupSym(
                GetName(fieldInfo.Name),
                aggregate,
                symbmask_t.MASK_FieldSymbol) as FieldSymbol;
            if (field != null)
            {
                return field;
            }
 
            field = SymFactory.CreateMemberVar(GetName(fieldInfo.Name), aggregate);
            field.AssociatedFieldInfo = fieldInfo;
 
            field.isStatic = fieldInfo.IsStatic;
            ACCESS access;
            if (fieldInfo.IsPublic)
            {
                access = ACCESS.ACC_PUBLIC;
            }
            else if (fieldInfo.IsPrivate)
            {
                access = ACCESS.ACC_PRIVATE;
            }
            else if (fieldInfo.IsFamily)
            {
                access = ACCESS.ACC_PROTECTED;
            }
            else if (fieldInfo.IsAssembly)
            {
                access = ACCESS.ACC_INTERNAL;
            }
            else if (fieldInfo.IsFamilyOrAssembly)
            {
                access = ACCESS.ACC_INTERNALPROTECTED;
            }
            else
            {
                Debug.Assert(fieldInfo.IsFamilyAndAssembly);
                access = ACCESS.ACC_INTERNAL_AND_PROTECTED;
            }
            field.SetAccess(access);
            field.isReadOnly = fieldInfo.IsInitOnly;
            field.isEvent = false;
            field.SetType(GetCTypeFromType(fieldInfo.FieldType));
 
            return field;
        }
        #endregion
 
        #region Events
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static void AddEventToSymbolTable(EventInfo eventInfo, AggregateSymbol aggregate, FieldSymbol addedField)
        {
            EventSymbol ev = SymbolStore.LookupSym(
                GetName(eventInfo.Name),
                aggregate,
                symbmask_t.MASK_EventSymbol) as EventSymbol;
            if (ev != null)
            {
                Debug.Assert(ev.AssociatedEventInfo == eventInfo);
                return;
            }
 
            ev = SymFactory.CreateEvent(GetName(eventInfo.Name), aggregate);
            ev.AssociatedEventInfo = eventInfo;
 
            // EventSymbol
            ACCESS access = ACCESS.ACC_PRIVATE;
            if (eventInfo.AddMethod != null)
            {
                ev.methAdd = AddMethodToSymbolTable(eventInfo.AddMethod, aggregate, MethodKindEnum.EventAccessor);
                ev.methAdd.SetEvent(ev);
                ev.isOverride = ev.methAdd.IsOverride();
 
                access = ev.methAdd.GetAccess();
            }
            if (eventInfo.RemoveMethod != null)
            {
                ev.methRemove = AddMethodToSymbolTable(eventInfo.RemoveMethod, aggregate, MethodKindEnum.EventAccessor);
                ev.methRemove.SetEvent(ev);
                ev.isOverride = ev.methRemove.IsOverride();
 
                access = ev.methRemove.GetAccess();
            }
            Debug.Assert(ev.methAdd != null || ev.methRemove != null);
            ev.isStatic = false;
            ev.type = GetCTypeFromType(eventInfo.EventHandlerType);
 
            // Symbol
            ev.SetAccess(access);
 
            // If we imported a field on the same aggregate, with the same name, and it also
            // has the same type, then that field is the backing field for this event, and
            // we mark it as such. This is used for the CSharpIsEventBinder.
            CType addedFieldType = addedField?.GetType();
            if (addedFieldType != null)
            {
                if (addedFieldType == ev.type)
                {
                    addedField.isEvent = true;
                }
            }
        }
        #endregion
 
        #region Properties
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        internal static void AddPredefinedPropertyToSymbolTable(AggregateSymbol type, Name property)
        {
            AggregateType aggtype = type.getThisType();
            Type t = aggtype.AssociatedSystemType;
 
            foreach (PropertyInfo pi in t.GetProperties(EverythingBindingFlags))
            {
                if (pi.Name == property.Text)
                {
                    AddPropertyToSymbolTable(pi, type);
                }
            }
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static void AddPropertyToSymbolTable(PropertyInfo property, AggregateSymbol aggregate)
        {
            Name name;
            bool isIndexer = property.GetIndexParameters().Length != 0
                             && property.DeclaringType?.GetCustomAttribute<DefaultMemberAttribute>()
                             ?.MemberName == property.Name;
 
            if (isIndexer)
            {
                name = GetName(SpecialNames.Indexer);
            }
            else
            {
                name = GetName(property.Name);
            }
            PropertySymbol prop = SymbolStore.LookupSym(
                name,
                aggregate,
                symbmask_t.MASK_PropertySymbol) as PropertySymbol;
 
            // If we already had one, see if it matches.
            if (prop != null)
            {
                PropertySymbol prevProp = null;
 
                // We'll have multiple properties with the same name if we have indexers.
                // In that case, we need to look at every indexer to see if we find one with
                // the matching associated sym that we want.
                while (prop != null)
                {
                    if (prop.AssociatedPropertyInfo.IsEquivalentTo(property))
                    {
                        return;
                    }
 
                    prevProp = prop;
                    prop = prop.LookupNext(symbmask_t.MASK_PropertySymbol) as PropertySymbol;
                }
 
                prop = prevProp;
                if (isIndexer)
                {
                    // We have an indexer for a different property info, so
                    // create a new symbol for it.
                    prop = null;
                }
            }
 
            // If we already had a property but its associated info doesn't match,
            // then we repurpose the property that we've found. This can happen
            // in the case of generic instantiations.
            //
            // Note that this is a bit of a hack - the best way to fix this is
            // by not depending on the instantiated properties at all, but rather depending
            // on their non-instantiated generic form, which can be gotten from the
            // parent's generic type definition's member. From there, we'll also need to
            // keep track of the instantiation as we move along, so that when we need the
            // associated property, we can instantiate it correctly.
            //
            // This seems far too heavyweight - since we know we will never bind to more
            // than one property per payload, lets just blast it each time.
            if (prop == null)
            {
                if (isIndexer)
                {
                    prop = SymFactory.CreateIndexer(name, aggregate);
                    prop.Params = CreateParameterArray(null, property.GetIndexParameters());
                }
                else
                {
                    prop = SymFactory.CreateProperty(GetName(property.Name), aggregate);
                    prop.Params = TypeArray.Empty;
                }
            }
            prop.AssociatedPropertyInfo = property;
 
            prop.isStatic = property.GetGetMethod(true) != null ? property.GetGetMethod(true).IsStatic : property.GetSetMethod(true).IsStatic;
            prop.isParamArray = DoesMethodHaveParameterArray(property.GetIndexParameters());
            prop.swtSlot = null;
            prop.RetType = GetCTypeFromType(property.PropertyType);
            prop.isOperator = isIndexer;
 
            // Determine if its an override. We should always have an accessor, unless
            // the metadata was bogus.
            if (property.GetMethod != null || property.SetMethod != null)
            {
                MethodInfo accessor = property.GetMethod ?? property.SetMethod; // Must have at least one.
                prop.isOverride = accessor.IsVirtual && accessor.IsHideBySig && accessor.GetBaseDefinition() != accessor;
                prop.isHideByName = !accessor.IsHideBySig;
            }
 
            SetParameterDataForMethProp(prop, property.GetIndexParameters());
 
            // Get and set.
            MethodInfo methGet = property.GetMethod;
            MethodInfo methSet = property.SetMethod;
            ACCESS access = ACCESS.ACC_PRIVATE;
            if (methGet != null)
            {
                prop.GetterMethod = AddMethodToSymbolTable(methGet, aggregate, MethodKindEnum.PropAccessor);
 
                // If we have an indexed property, leave the method as a method we can call,
                // and mark the property as bogus.
                if (isIndexer || prop.GetterMethod.Params.Count == 0)
                {
                    prop.GetterMethod.SetProperty(prop);
                }
                else
                {
                    prop.Bogus = true;
                    prop.GetterMethod.SetMethKind(MethodKindEnum.Actual);
                }
 
                if (prop.GetterMethod.GetAccess() > access)
                {
                    access = prop.GetterMethod.GetAccess();
                }
            }
            if (methSet != null)
            {
                prop.SetterMethod = AddMethodToSymbolTable(methSet, aggregate, MethodKindEnum.PropAccessor);
 
                // If we have an indexed property, leave the method as a method we can call,
                // and mark the property as bogus.
                if (isIndexer || prop.SetterMethod.Params.Count == 1)
                {
                    prop.SetterMethod.SetProperty(prop);
                }
                else
                {
                    prop.Bogus = true;
                    prop.SetterMethod.SetMethKind(MethodKindEnum.Actual);
                }
 
                if (prop.SetterMethod.GetAccess() > access)
                {
                    access = prop.SetterMethod.GetAccess();
                }
            }
 
            // The access of the property is the least restrictive access of its getter/setter.
            prop.SetAccess(access);
        }
 
        #endregion
 
        #region Methods
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        internal static void AddPredefinedMethodToSymbolTable(AggregateSymbol type, Name methodName)
        {
            Type t = type.getThisType().AssociatedSystemType;
 
            // If we got here, it means we couldn't find it in our initial lookup. Means we haven't loaded it from reflection yet.
            // Lets go and do that now.
            // Check if we have constructors or not.
            if (methodName == NameManager.GetPredefinedName(PredefinedName.PN_CTOR))
            {
                foreach (ConstructorInfo c in t.GetConstructors())
                {
                    AddMethodToSymbolTable(
                        c,
                        type,
                        MethodKindEnum.Constructor);
                }
            }
            else
            {
                foreach (MethodInfo m in t.GetMethods(EverythingBindingFlags))
                {
                    if (m.Name == methodName.Text && m.DeclaringType == t)
                    {
                        AddMethodToSymbolTable(
                            m,
                            type,
                            m.Name == SpecialNames.Invoke ? MethodKindEnum.Invoke : MethodKindEnum.Actual);
                    }
                }
            }
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static MethodSymbol AddMethodToSymbolTable(MethodBase member, AggregateSymbol callingAggregate, MethodKindEnum kind)
        {
            MethodInfo method = member as MethodInfo;
 
            Debug.Assert(method != null || member is ConstructorInfo);
 
            // If we are trying to add an actual method via MethodKindEnum.Actual, and
            // the memberinfo is a special name, and its not static, then return null.
            // We'll re-add the thing later with some other method kind.
            //
            // This will happen for things like indexers and properties. The ones that have
            // special names that we DO want to allow adding are things like operators, which
            // are static and will not be added again later.
 
            if (kind == MethodKindEnum.Actual && // MethKindEnum.Actual
                (method == null || // Not a ConstructorInfo
                    (!method.IsStatic && method.IsSpecialName))) // Not static and is a special name
            {
                return null;
            }
 
            MethodSymbol methodSymbol = FindMatchingMethod(member, callingAggregate);
            if (methodSymbol != null)
            {
                return methodSymbol;
            }
 
            ParameterInfo[] parameters = member.GetParameters();
            // First create the method.
            methodSymbol = SymFactory.CreateMethod(GetName(member.Name), callingAggregate);
            methodSymbol.AssociatedMemberInfo = member;
            methodSymbol.SetMethKind(kind);
            if (kind == MethodKindEnum.ExplicitConv || kind == MethodKindEnum.ImplicitConv)
            {
                callingAggregate.SetHasConversion();
                methodSymbol.SetConvNext(callingAggregate.GetFirstUDConversion());
                callingAggregate.SetFirstUDConversion(methodSymbol);
            }
 
            ACCESS access;
            if (member.IsPublic)
            {
                access = ACCESS.ACC_PUBLIC;
            }
            else if (member.IsPrivate)
            {
                access = ACCESS.ACC_PRIVATE;
            }
            else if (member.IsFamily)
            {
                access = ACCESS.ACC_PROTECTED;
            }
            else if (member.IsFamilyOrAssembly)
            {
                access = ACCESS.ACC_INTERNALPROTECTED;
            }
            else if (member.IsAssembly)
            {
                access = ACCESS.ACC_INTERNAL;
            }
            else
            {
                Debug.Assert(member.IsFamilyAndAssembly);
                access = ACCESS.ACC_INTERNAL_AND_PROTECTED;
            }
 
            methodSymbol.SetAccess(access);
            methodSymbol.isVirtual = member.IsVirtual;
            methodSymbol.isStatic = member.IsStatic;
 
            if (method != null)
            {
                methodSymbol.typeVars = GetMethodTypeParameters(method, methodSymbol);
                methodSymbol.isOverride = method.IsVirtual && method.IsHideBySig && method.GetBaseDefinition() != method;
                methodSymbol.isOperator = IsOperator(method);
                methodSymbol.swtSlot = GetSlotForOverride(method);
                methodSymbol.RetType = GetCTypeFromType(method.ReturnType);
            }
            else
            {
                methodSymbol.typeVars = TypeArray.Empty;
                methodSymbol.isOverride = false;
                methodSymbol.isOperator = false;
                methodSymbol.swtSlot = null;
                methodSymbol.RetType = VoidType.Instance;
            }
 
            methodSymbol.modOptCount = 0;
 
            methodSymbol.isParamArray = DoesMethodHaveParameterArray(parameters);
            methodSymbol.isHideByName = false;
 
            methodSymbol.Params = CreateParameterArray(methodSymbol.AssociatedMemberInfo, parameters);
 
            SetParameterDataForMethProp(methodSymbol, parameters);
 
            return methodSymbol;
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static void SetParameterDataForMethProp(MethodOrPropertySymbol methProp, ParameterInfo[] parameters)
        {
            if (parameters.Length > 0)
            {
                // See if we have a param array.
                if (parameters[parameters.Length - 1].GetCustomAttribute<ParamArrayAttribute>(false) != null)
                {
                    methProp.isParamArray = true;
                }
 
                // Mark the names of the parameters, and their default values.
                for (int i = 0; i < parameters.Length; i++)
                {
                    SetParameterAttributes(methProp, parameters, i);
 
                    // Insert the name.
                    methProp.ParameterNames.Add(GetName(parameters[i].Name));
                }
            }
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static void SetParameterAttributes(MethodOrPropertySymbol methProp, ParameterInfo[] parameters, int i)
        {
            ParameterInfo parameter = parameters[i];
            if ((parameter.Attributes & ParameterAttributes.Optional) != 0 && !parameter.ParameterType.IsByRef)
            {
                methProp.SetOptionalParameter(i);
                PopulateSymbolTableWithName("Value", new Type[] { typeof(Missing) }, typeof(Missing)); // We might need this later
            }
 
            // Get MarshalAsAttribute
            if ((parameter.Attributes & ParameterAttributes.HasFieldMarshal) != 0)
            {
                MarshalAsAttribute attr = parameter.GetCustomAttribute<MarshalAsAttribute>(false);
                if (attr != null)
                {
                    methProp.SetMarshalAsParameter(i, attr.Value);
                }
            }
 
            DateTimeConstantAttribute dateAttr = parameter.GetCustomAttribute<DateTimeConstantAttribute>(false);
            // Get the various kinds of default values
            if (dateAttr != null)
            {
                // Get DateTimeConstant
                ConstVal cv = ConstVal.Get(((DateTime)dateAttr.Value).Ticks);
                CType cvType = SymbolLoader.GetPredefindType(PredefinedType.PT_DATETIME);
                methProp.SetDefaultParameterValue(i, cvType, cv);
            }
            else
            {
                DecimalConstantAttribute decAttr = parameter.GetCustomAttribute<DecimalConstantAttribute>();
                if (decAttr != null)
                {
                    // Get DecimalConstant
                    ConstVal cv = ConstVal.Get(decAttr.Value);
                    CType cvType = SymbolLoader.GetPredefindType(PredefinedType.PT_DECIMAL);
                    methProp.SetDefaultParameterValue(i, cvType, cv);
                }
                else if ((parameter.Attributes & ParameterAttributes.HasDefault) != 0 && !parameter.ParameterType.IsByRef)
                {
                    // Only set a default value if we have one, and the type that we're
                    // looking at isn't a by ref type or a type parameter.
 
                    ConstVal cv = default(ConstVal);
                    CType cvType = SymbolLoader.GetPredefindType(PredefinedType.PT_OBJECT);
 
                    // We need to use RawDefaultValue, because DefaultValue is too clever.
                    if (parameter.DefaultValue != null)
                    {
                        object defValue = parameter.DefaultValue;
                        Debug.Assert(Type.GetTypeCode(defValue.GetType()) != TypeCode.Decimal); // Handled above
                        switch (Type.GetTypeCode(defValue.GetType()))
                        {
 
                            case TypeCode.Byte:
                                cv = ConstVal.Get((byte)defValue);
                                cvType = SymbolLoader.GetPredefindType(PredefinedType.PT_BYTE);
                                break;
 
                            case TypeCode.Int16:
                                cv = ConstVal.Get((short)defValue);
                                cvType = SymbolLoader.GetPredefindType(PredefinedType.PT_SHORT);
                                break;
 
                            case TypeCode.Int32:
                                cv = ConstVal.Get((int)defValue);
                                cvType = SymbolLoader.GetPredefindType(PredefinedType.PT_INT);
                                break;
 
                            case TypeCode.Int64:
                                cv = ConstVal.Get((long)defValue);
                                cvType = SymbolLoader.GetPredefindType(PredefinedType.PT_LONG);
                                break;
 
                            case TypeCode.Single:
                                cv = ConstVal.Get((float)defValue);
                                cvType = SymbolLoader.GetPredefindType(PredefinedType.PT_FLOAT);
                                break;
 
                            case TypeCode.Double:
                                cv = ConstVal.Get((double)defValue);
                                cvType = SymbolLoader.GetPredefindType(PredefinedType.PT_DOUBLE);
                                break;
 
                            case TypeCode.Char:
                                cv = ConstVal.Get((char)defValue);
                                cvType = SymbolLoader.GetPredefindType(PredefinedType.PT_CHAR);
                                break;
 
                            case TypeCode.Boolean:
                                cv = ConstVal.Get((bool)defValue);
                                cvType = SymbolLoader.GetPredefindType(PredefinedType.PT_BOOL);
                                break;
 
                            case TypeCode.SByte:
                                cv = ConstVal.Get((sbyte)defValue);
                                cvType = SymbolLoader.GetPredefindType(PredefinedType.PT_SBYTE);
                                break;
 
                            case TypeCode.UInt16:
                                cv = ConstVal.Get((ushort)defValue);
                                cvType = SymbolLoader.GetPredefindType(PredefinedType.PT_USHORT);
                                break;
 
                            case TypeCode.UInt32:
                                cv = ConstVal.Get((uint)defValue);
                                cvType = SymbolLoader.GetPredefindType(PredefinedType.PT_UINT);
                                break;
 
                            case TypeCode.UInt64:
                                cv = ConstVal.Get((ulong)defValue);
                                cvType = SymbolLoader.GetPredefindType(PredefinedType.PT_ULONG);
                                break;
 
                            case TypeCode.String:
                                cv = ConstVal.Get((string)defValue);
                                cvType = SymbolLoader.GetPredefindType(PredefinedType.PT_STRING);
                                break;
                        }
 
                        // if we hit no case in the switch, we get object/null
                        // because that's how we initialized the constval.
                    }
 
                    methProp.SetDefaultParameterValue(i, cvType, cv);
                }
            }
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        private static MethodSymbol FindMatchingMethod(MemberInfo method, AggregateSymbol callingAggregate)
        {
            MethodSymbol meth = SymbolStore.LookupSym(GetName(method.Name), callingAggregate, symbmask_t.MASK_MethodSymbol) as MethodSymbol;
            while (meth != null)
            {
                if (meth.AssociatedMemberInfo.IsEquivalentTo(method))
                {
                    return meth;
                }
 
                meth = meth.LookupNext(symbmask_t.MASK_MethodSymbol) as MethodSymbol;
            }
 
            return null;
        }
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static TypeArray CreateParameterArray(MemberInfo associatedInfo, ParameterInfo[] parameters)
        {
            bool isVarArg = associatedInfo is MethodBase mb && (mb.CallingConvention & CallingConventions.VarArgs) != 0;
            CType[] types = new CType[isVarArg ? parameters.Length + 1 : parameters.Length];
 
            for (int i = 0; i < parameters.Length; i++)
            {
                types[i] = GetTypeOfParameter(parameters[i], associatedInfo);
            }
 
            if (isVarArg)
            {
                types[types.Length - 1] = ArgumentListType.Instance;
            }
 
            return TypeArray.Allocate(types);
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static CType GetTypeOfParameter(ParameterInfo p, MemberInfo m)
        {
            Type t = p.ParameterType;
            CType ctype;
            if (t.IsGenericParameter && t.DeclaringMethod != null && t.DeclaringMethod == m)
            {
                // If its a method type parameter from ourselves, just find it.
                ctype = LoadMethodTypeParameter(FindMethodFromMemberInfo(m), t);
            }
            else
            {
                ctype = GetCTypeFromType(t);
            }
 
            // Check if we have an out parameter.
            if (ctype is ParameterModifierType mod && p.IsOut && !p.IsIn)
            {
                CType parameterType = mod.ParameterType;
                ctype = TypeManager.GetParameterModifier(parameterType, true);
            }
 
            return ctype;
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        private static bool DoesMethodHaveParameterArray(ParameterInfo[] parameters)
        {
            if (parameters.Length == 0)
            {
                return false;
            }
 
            ParameterInfo p = parameters[parameters.Length - 1];
            var attributes = p.GetCustomAttributes(false);
 
            foreach (object o in attributes)
            {
                if (o is ParamArrayAttribute)
                {
                    return true;
                }
            }
            return false;
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static SymWithType GetSlotForOverride(MethodInfo method)
        {
            if (method.IsVirtual && method.IsHideBySig)
            {
                MethodInfo baseMethodInfo = method.GetBaseDefinition();
                if (baseMethodInfo == method)
                {
                    // We just found ourselves, so we don't care here.
                    return null;
                }
 
                // We have the base class method that we're overriding. We can assume
                // that all the parent aggregate symbols were added, and that we added
                // the methods in order. As such, our parent methods should be in the
                // symbol table at this point.
 
                AggregateSymbol aggregate = ((AggregateType)GetCTypeFromType(baseMethodInfo.DeclaringType)).OwningAggregate;
                MethodSymbol baseMethod = FindMethodFromMemberInfo(baseMethodInfo);
                Debug.Assert(baseMethod != null);
                return new SymWithType(baseMethod, aggregate.getThisType());
            }
 
            return null;
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static MethodSymbol FindMethodFromMemberInfo(MemberInfo baseMemberInfo)
        {
            CType t = GetCTypeFromType(baseMemberInfo.DeclaringType);
            Debug.Assert(t is AggregateType);
            AggregateSymbol aggregate = ((AggregateType)t).OwningAggregate;
            Debug.Assert(aggregate != null);
 
            MethodSymbol meth = SymbolLoader.LookupAggMember(
                GetName(baseMemberInfo.Name),
                aggregate,
                symbmask_t.MASK_MethodSymbol) as MethodSymbol;
            for (;
                    meth != null && !meth.AssociatedMemberInfo.IsEquivalentTo(baseMemberInfo);
                    meth = meth.LookupNext(symbmask_t.MASK_MethodSymbol) as MethodSymbol)
                ;
 
            return meth;
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        internal static bool AggregateContainsMethod(AggregateSymbol agg, string szName, symbmask_t mask) =>
            SymbolLoader.LookupAggMember(GetName(szName), agg, mask) != null;
 
        #endregion
 
        #region Conversions
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        internal static void AddConversionsForType(Type type)
        {
            if (type.IsInterface)
            {
                AddConversionsForOneType(type);
            }
            for (Type t = type; t.BaseType != null; t = t.BaseType)
            {
                AddConversionsForOneType(t);
            }
        }
 
        /////////////////////////////////////////////////////////////////////////////////
 
        [RequiresUnreferencedCode(Binder.TrimmerWarning)]
        private static void AddConversionsForOneType(Type type)
        {
            if (type.IsGenericType)
            {
                type = type.GetGenericTypeDefinition();
            }
 
            if (!s_typesWithConversionsLoaded.Add(type))
            {
                return;
            }
 
            // Always make the aggregate for the type, regardless of whether or not
            // there are any conversions.
            CType t = GetCTypeFromType(type);
 
            if (!(t is AggregateType))
            {
                CType endT;
                while ((endT = t.BaseOrParameterOrElementType) != null)
                {
                    t = endT;
                }
            }
 
            if (t is TypeParameterType paramType)
            {
                // Add conversions for the bounds.
                foreach (CType bound in paramType.Bounds.Items)
                {
                    AddConversionsForType(bound.AssociatedSystemType);
                }
                return;
            }
 
            Debug.Assert(t is AggregateType);
            AggregateSymbol aggregate = ((AggregateType)t).OwningAggregate;
 
            // Now find all the conversions and make them.
            foreach (MethodInfo conversion in type.GetMethods(BindingFlags.Public | BindingFlags.Static))
            {
                if (conversion.DeclaringType == type && conversion.IsSpecialName && !conversion.IsGenericMethod)
                {
                    MethodKindEnum methodKind;
                    switch (conversion.Name)
                    {
                        case SpecialNames.ImplicitConversion:
                            methodKind = MethodKindEnum.ImplicitConv;
                            break;
                        case SpecialNames.ExplicitConversion:
                            methodKind = MethodKindEnum.ExplicitConv;
                            break;
                        default:
                            continue;
                    }
 
                    AddMethodToSymbolTable(conversion, aggregate, methodKind);
                }
            }
        }
        #endregion
 
        #region Operators
        /////////////////////////////////////////////////////////////////////////////////
 
        private static bool IsOperator(MethodInfo method)
        {
            if (method.IsSpecialName && method.IsStatic)
            {
                switch (method.Name)
                {
                    case SpecialNames.ImplicitConversion:
                    case SpecialNames.ExplicitConversion:
                    case SpecialNames.CLR_Add:
                    case SpecialNames.CLR_Subtract:
                    case SpecialNames.CLR_Multiply:
                    case SpecialNames.CLR_Division:
                    case SpecialNames.CLR_Modulus:
                    case SpecialNames.CLR_LShift:
                    case SpecialNames.CLR_RShift:
                    case SpecialNames.CLR_LT:
                    case SpecialNames.CLR_GT:
                    case SpecialNames.CLR_LTE:
                    case SpecialNames.CLR_GTE:
                    case SpecialNames.CLR_Equality:
                    case SpecialNames.CLR_Inequality:
                    case SpecialNames.CLR_BitwiseAnd:
                    case SpecialNames.CLR_ExclusiveOr:
                    case SpecialNames.CLR_BitwiseOr:
                    case SpecialNames.CLR_LogicalNot:
                    case SpecialNames.CLR_UnaryNegation:
                    case SpecialNames.CLR_UnaryPlus:
                    case SpecialNames.CLR_OnesComplement:
                    case SpecialNames.CLR_True:
                    case SpecialNames.CLR_False:
                    case SpecialNames.CLR_Increment:
                    case SpecialNames.CLR_Decrement:
                        return true;
                }
            }
 
            return false;
        }
 
        #endregion
    }
}