File: System\Reflection\Metadata\TypeSystem\TypeDefinition.cs
Web Access
Project: src\src\libraries\System.Reflection.Metadata\src\System.Reflection.Metadata.csproj (System.Reflection.Metadata)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection.Metadata.Ecma335;
 
namespace System.Reflection.Metadata
{
    public readonly struct TypeDefinition
    {
        private readonly MetadataReader _reader;
 
        // Workaround: JIT doesn't generate good code for nested structures, so use RowId.
        private readonly uint _treatmentAndRowId;
 
        internal TypeDefinition(MetadataReader reader, uint treatmentAndRowId)
        {
            Debug.Assert(reader != null);
            Debug.Assert(treatmentAndRowId != 0);
 
            _reader = reader;
            _treatmentAndRowId = treatmentAndRowId;
        }
 
        private int RowId
        {
            get { return (int)(_treatmentAndRowId & TokenTypeIds.RIDMask); }
        }
 
        private TypeDefTreatment Treatment
        {
            get { return (TypeDefTreatment)(_treatmentAndRowId >> TokenTypeIds.RowIdBitCount); }
        }
 
        private TypeDefinitionHandle Handle
        {
            get { return TypeDefinitionHandle.FromRowId(RowId); }
        }
 
        public TypeAttributes Attributes
        {
            get
            {
                if (Treatment == 0)
                {
                    return _reader.TypeDefTable.GetFlags(Handle);
                }
 
                return GetProjectedFlags();
            }
        }
 
        /// <summary>
        /// Indicates whether this is a nested type.
        /// </summary>
        public bool IsNested => Attributes.IsNested();
 
        /// <summary>
        /// Name of the type.
        /// </summary>
        public StringHandle Name
        {
            get
            {
                if (Treatment == 0)
                {
                    return _reader.TypeDefTable.GetName(Handle);
                }
 
                return GetProjectedName();
            }
        }
 
        /// <summary>
        /// Full name of the namespace where the type is defined, or nil if the type is nested or defined in a root namespace.
        /// </summary>
        public StringHandle Namespace
        {
            get
            {
                if (Treatment == 0)
                {
                    return _reader.TypeDefTable.GetNamespace(Handle);
                }
 
                return GetProjectedNamespaceString();
            }
        }
 
        /// <summary>
        /// The definition handle of the namespace where the type is defined, or nil if the type is nested or defined in a root namespace.
        /// </summary>
        public NamespaceDefinitionHandle NamespaceDefinition
        {
            get
            {
                if (Treatment == 0)
                {
                    return _reader.TypeDefTable.GetNamespaceDefinition(Handle);
                }
 
                return GetProjectedNamespace();
            }
        }
 
        /// <summary>
        /// The base type of the type definition: either
        /// <see cref="TypeSpecificationHandle"/>, <see cref="TypeReferenceHandle"/> or <see cref="TypeDefinitionHandle"/>.
        /// </summary>
        public EntityHandle BaseType
        {
            get
            {
                if (Treatment == 0)
                {
                    return _reader.TypeDefTable.GetExtends(Handle);
                }
 
                return GetProjectedBaseType();
            }
        }
 
        public TypeLayout GetLayout()
        {
            int classLayoutRowId = _reader.ClassLayoutTable.FindRow(Handle);
            if (classLayoutRowId == 0)
            {
                // NOTE: We don't need a bool/TryGetLayout because zero also means use default:
                //
                // Spec:
                //  ClassSize of zero does not mean the class has zero size. It means that no .size directive was specified
                //  at definition time, in which case, the actual size is calculated from the field types, taking account of
                //  packing size (default or specified) and natural alignment on the target, runtime platform.
                //
                // PackingSize shall be one of {0, 1, 2, 4, 8, 16, 32, 64, 128}. (0 means use
                // the default pack size for the platform on which the application is
                // running.)
 
                return default(TypeLayout);
            }
 
            uint size = _reader.ClassLayoutTable.GetClassSize(classLayoutRowId);
 
            // The spec doesn't limit the size to 31bit. It only limits the size to 1MB if Parent is a value type.
            // It however doesn't make much sense to define classes with >2GB size. So in order to keep the API
            // clean of unsigned ints we impose the limit.
            if (unchecked((int)size) != size)
            {
                throw new BadImageFormatException(SR.InvalidTypeSize);
            }
 
            int packingSize = _reader.ClassLayoutTable.GetPackingSize(classLayoutRowId);
            return new TypeLayout((int)size, packingSize);
        }
 
        /// <summary>
        /// Returns the enclosing type of a specified nested type or nil handle if the type is not nested.
        /// </summary>
        public TypeDefinitionHandle GetDeclaringType()
        {
            return _reader.NestedClassTable.FindEnclosingType(Handle);
        }
 
        public GenericParameterHandleCollection GetGenericParameters()
        {
            return _reader.GenericParamTable.FindGenericParametersForType(Handle);
        }
 
        public MethodDefinitionHandleCollection GetMethods()
        {
            return new MethodDefinitionHandleCollection(_reader, Handle);
        }
 
        public FieldDefinitionHandleCollection GetFields()
        {
            return new FieldDefinitionHandleCollection(_reader, Handle);
        }
 
        public PropertyDefinitionHandleCollection GetProperties()
        {
            return new PropertyDefinitionHandleCollection(_reader, Handle);
        }
 
        public EventDefinitionHandleCollection GetEvents()
        {
            return new EventDefinitionHandleCollection(_reader, Handle);
        }
 
        /// <summary>
        /// Returns an array of types nested in the specified type.
        /// </summary>
        public ImmutableArray<TypeDefinitionHandle> GetNestedTypes()
        {
            return _reader.GetNestedTypes(Handle);
        }
 
        public MethodImplementationHandleCollection GetMethodImplementations()
        {
            return new MethodImplementationHandleCollection(_reader, Handle);
        }
 
        public InterfaceImplementationHandleCollection GetInterfaceImplementations()
        {
            return new InterfaceImplementationHandleCollection(_reader, Handle);
        }
 
        public CustomAttributeHandleCollection GetCustomAttributes()
        {
            return new CustomAttributeHandleCollection(_reader, Handle);
        }
 
        public DeclarativeSecurityAttributeHandleCollection GetDeclarativeSecurityAttributes()
        {
            return new DeclarativeSecurityAttributeHandleCollection(_reader, Handle);
        }
 
        #region Projections
 
        private TypeAttributes GetProjectedFlags()
        {
            var flags = _reader.TypeDefTable.GetFlags(Handle);
            var treatment = Treatment;
 
            switch (treatment & TypeDefTreatment.KindMask)
            {
                case TypeDefTreatment.NormalNonAttribute:
                    flags |= TypeAttributes.WindowsRuntime | TypeAttributes.Import;
                    break;
 
                case TypeDefTreatment.NormalAttribute:
                    flags |= TypeAttributes.WindowsRuntime | TypeAttributes.Sealed;
                    break;
 
                case TypeDefTreatment.UnmangleWinRTName:
                    flags = flags & ~TypeAttributes.SpecialName | TypeAttributes.Public;
                    break;
 
                case TypeDefTreatment.PrefixWinRTName:
                    flags = flags & ~TypeAttributes.Public | TypeAttributes.Import;
                    break;
 
                case TypeDefTreatment.RedirectedToClrType:
                    flags = flags & ~TypeAttributes.Public | TypeAttributes.Import;
                    break;
 
                case TypeDefTreatment.RedirectedToClrAttribute:
                    flags &= ~TypeAttributes.Public;
                    break;
            }
 
            if ((treatment & TypeDefTreatment.MarkAbstractFlag) != 0)
            {
                flags |= TypeAttributes.Abstract;
            }
 
            if ((treatment & TypeDefTreatment.MarkInternalFlag) != 0)
            {
                flags &= ~TypeAttributes.Public;
            }
 
            return flags;
        }
 
        private StringHandle GetProjectedName()
        {
            var name = _reader.TypeDefTable.GetName(Handle);
 
            return (Treatment & TypeDefTreatment.KindMask) switch
            {
                TypeDefTreatment.UnmangleWinRTName => name.SuffixRaw(MetadataReader.ClrPrefix.Length),
                TypeDefTreatment.PrefixWinRTName => name.WithWinRTPrefix(),
                _ => name,
            };
        }
 
        private NamespaceDefinitionHandle GetProjectedNamespace()
        {
            // NOTE: NamespaceDefinitionHandle currently relies on never having virtual values. If this ever gets projected
            //       to a virtual namespace name, then that assumption will need to be removed.
 
            // no change:
            return _reader.TypeDefTable.GetNamespaceDefinition(Handle);
        }
 
        private StringHandle GetProjectedNamespaceString()
        {
            // no change:
            return _reader.TypeDefTable.GetNamespace(Handle);
        }
 
        private EntityHandle GetProjectedBaseType()
        {
            // no change:
            return _reader.TypeDefTable.GetExtends(Handle);
        }
 
        #endregion
    }
}