// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Threading; using Debug = System.Diagnostics.Debug; using Internal.NativeFormat; namespace Internal.TypeSystem.Ecma { /// /// Override of MetadataType that uses actual Ecma335 metadata. /// public sealed partial class EcmaType : MetadataType, EcmaModule.IEntityHandleObject { private EcmaModule _module; private TypeDefinitionHandle _handle; private TypeDefinition _typeDefinition; // Cached values private string _typeName; private string _typeNamespace; private TypeDesc[] _genericParameters; private MetadataType _baseType; private int _hashcode; internal EcmaType(EcmaModule module, TypeDefinitionHandle handle) { _module = module; _handle = handle; _typeDefinition = module.MetadataReader.GetTypeDefinition(handle); _baseType = this; // Not yet initialized flag #if DEBUG // Initialize name eagerly in debug builds for convenience InitializeName(); InitializeNamespace(); #endif } public override int GetHashCode() { if (_hashcode != 0) return _hashcode; return InitializeHashCode(); } private int InitializeHashCode() { TypeDesc containingType = ContainingType; if (containingType == null) { string ns = Namespace; var hashCodeBuilder = new TypeHashingAlgorithms.HashCodeBuilder(ns); if (ns.Length > 0) hashCodeBuilder.Append("."); hashCodeBuilder.Append(Name); _hashcode = hashCodeBuilder.ToHashCode(); } else { _hashcode = TypeHashingAlgorithms.ComputeNestedTypeHashCode( containingType.GetHashCode(), TypeHashingAlgorithms.ComputeNameHashCode(Name)); } return _hashcode; } EntityHandle EcmaModule.IEntityHandleObject.Handle { get { return _handle; } } public override TypeSystemContext Context { get { return _module.Context; } } private void ComputeGenericParameters() { var genericParameterHandles = _typeDefinition.GetGenericParameters(); int count = genericParameterHandles.Count; if (count > 0) { TypeDesc[] genericParameters = new TypeDesc[count]; int i = 0; foreach (var genericParameterHandle in genericParameterHandles) { genericParameters[i++] = new EcmaGenericParameter(_module, genericParameterHandle); } Interlocked.CompareExchange(ref _genericParameters, genericParameters, null); } else { _genericParameters = TypeDesc.EmptyTypes; } } public override Instantiation Instantiation { get { if (_genericParameters == null) ComputeGenericParameters(); return new Instantiation(_genericParameters); } } public override ModuleDesc Module { get { return _module; } } public EcmaModule EcmaModule { get { return _module; } } public MetadataReader MetadataReader { get { return _module.MetadataReader; } } public TypeDefinitionHandle Handle { get { return _handle; } } private MetadataType InitializeBaseType() { var baseTypeHandle = _typeDefinition.BaseType; if (baseTypeHandle.IsNil) { _baseType = null; return null; } var type = _module.GetType(baseTypeHandle) as MetadataType; if (type == null) { // PREFER: "new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadBadFormat, this)" but the metadata is too broken ThrowHelper.ThrowTypeLoadException(Namespace, Name, Module); } _baseType = type; return type; } public override DefType BaseType { get { if (_baseType == this) return InitializeBaseType(); return _baseType; } } public override MetadataType MetadataBaseType { get { if (_baseType == this) return InitializeBaseType(); return _baseType; } } protected override TypeFlags ComputeTypeFlags(TypeFlags mask) { TypeFlags flags = 0; if ((mask & TypeFlags.CategoryMask) != 0) { TypeDesc baseType = this.BaseType; if (baseType != null && baseType.IsWellKnownType(WellKnownType.ValueType)) { flags |= TypeFlags.ValueType; } else if (baseType != null && baseType.IsWellKnownType(WellKnownType.Enum)) { flags |= TypeFlags.Enum; } else { if ((_typeDefinition.Attributes & TypeAttributes.Interface) != 0) flags |= TypeFlags.Interface; else flags |= TypeFlags.Class; } // All other cases are handled during TypeSystemContext intitialization } if ((mask & TypeFlags.HasGenericVarianceComputed) != 0) { flags |= TypeFlags.HasGenericVarianceComputed; foreach (GenericParameterDesc genericParam in Instantiation) { if (genericParam.Variance != GenericVariance.None) { flags |= TypeFlags.HasGenericVariance; break; } } } if ((mask & TypeFlags.HasFinalizerComputed) != 0) { flags |= TypeFlags.HasFinalizerComputed; if (GetFinalizer() != null) flags |= TypeFlags.HasFinalizer; } if ((mask & TypeFlags.AttributeCacheComputed) != 0) { MetadataReader reader = MetadataReader; MetadataStringComparer stringComparer = reader.StringComparer; bool isValueType = IsValueType; flags |= TypeFlags.AttributeCacheComputed; foreach (CustomAttributeHandle attributeHandle in _typeDefinition.GetCustomAttributes()) { if (MetadataReader.GetAttributeNamespaceAndName(attributeHandle, out StringHandle namespaceHandle, out StringHandle nameHandle)) { if (isValueType && stringComparer.Equals(nameHandle, "IsByRefLikeAttribute") && stringComparer.Equals(namespaceHandle, "System.Runtime.CompilerServices")) flags |= TypeFlags.IsByRefLike; if (stringComparer.Equals(nameHandle, "IntrinsicAttribute") && stringComparer.Equals(namespaceHandle, "System.Runtime.CompilerServices")) flags |= TypeFlags.IsIntrinsic; } } } return flags; } private string InitializeName() { var metadataReader = this.MetadataReader; _typeName = metadataReader.GetString(_typeDefinition.Name); return _typeName; } public override string Name { get { if (_typeName == null) return InitializeName(); return _typeName; } } private string InitializeNamespace() { var metadataReader = this.MetadataReader; _typeNamespace = metadataReader.GetString(_typeDefinition.Namespace); return _typeNamespace; } public override string Namespace { get { if (_typeNamespace == null) return InitializeNamespace(); return _typeNamespace; } } public override IEnumerable GetMethods() { foreach (var handle in _typeDefinition.GetMethods()) { yield return (MethodDesc)_module.GetObject(handle); } } public override MethodDesc GetMethod(string name, MethodSignature signature) { var metadataReader = this.MetadataReader; var stringComparer = metadataReader.StringComparer; foreach (var handle in _typeDefinition.GetMethods()) { if (stringComparer.Equals(metadataReader.GetMethodDefinition(handle).Name, name)) { MethodDesc method = (MethodDesc)_module.GetObject(handle); if (signature == null || signature.Equals(method.Signature)) return method; } } return null; } public override MethodDesc GetStaticConstructor() { var metadataReader = this.MetadataReader; var stringComparer = metadataReader.StringComparer; foreach (var handle in _typeDefinition.GetMethods()) { var methodDefinition = metadataReader.GetMethodDefinition(handle); if (methodDefinition.Attributes.IsRuntimeSpecialName() && stringComparer.Equals(methodDefinition.Name, ".cctor")) { MethodDesc method = (MethodDesc)_module.GetObject(handle); return method; } } return null; } public override MethodDesc GetDefaultConstructor() { if (IsAbstract) return null; MetadataReader metadataReader = this.MetadataReader; MetadataStringComparer stringComparer = metadataReader.StringComparer; foreach (var handle in _typeDefinition.GetMethods()) { var methodDefinition = metadataReader.GetMethodDefinition(handle); MethodAttributes attributes = methodDefinition.Attributes; if (attributes.IsRuntimeSpecialName() && attributes.IsPublic() && stringComparer.Equals(methodDefinition.Name, ".ctor")) { MethodDesc method = (MethodDesc)_module.GetObject(handle); if (method.Signature.Length != 0) continue; return method; } } return null; } public override MethodDesc GetFinalizer() { // System.Object defines Finalize but doesn't use it, so we can determine that a type has a Finalizer // by checking for a virtual method override that lands anywhere other than Object in the inheritance // chain. if (!HasBaseType) return null; TypeDesc objectType = Context.GetWellKnownType(WellKnownType.Object); MethodDesc decl = objectType.GetMethod("Finalize", null); if (decl != null) { MethodDesc impl = this.FindVirtualFunctionTargetMethodOnObjectType(decl); if (impl == null) { // TODO: invalid input: the type doesn't derive from our System.Object throw new TypeLoadException(this.GetFullName()); } if (impl.OwningType != objectType) { return impl; } return null; } // TODO: Better exception type. Should be: "CoreLib doesn't have a required thing in it". throw new NotImplementedException(); } public override IEnumerable GetFields() { foreach (var handle in _typeDefinition.GetFields()) { var field = (EcmaField)_module.GetObject(handle); yield return field; } } public override FieldDesc GetField(string name) { var metadataReader = this.MetadataReader; var stringComparer = metadataReader.StringComparer; foreach (var handle in _typeDefinition.GetFields()) { if (stringComparer.Equals(metadataReader.GetFieldDefinition(handle).Name, name)) { var field = (EcmaField)_module.GetObject(handle); return field; } } return null; } public override IEnumerable GetNestedTypes() { foreach (var handle in _typeDefinition.GetNestedTypes()) { yield return (MetadataType)_module.GetObject(handle); } } public override MetadataType GetNestedType(string name) { var metadataReader = this.MetadataReader; var stringComparer = metadataReader.StringComparer; foreach (var handle in _typeDefinition.GetNestedTypes()) { bool nameMatched; TypeDefinition type = metadataReader.GetTypeDefinition(handle); if (type.Namespace.IsNil) { nameMatched = stringComparer.Equals(type.Name, name); } else { string typeName = metadataReader.GetString(type.Name); typeName = metadataReader.GetString(type.Namespace) + "." + typeName; nameMatched = typeName == name; } if (nameMatched) return (MetadataType)_module.GetObject(handle); } return null; } public TypeAttributes Attributes { get { return _typeDefinition.Attributes; } } public override DefType ContainingType { get { if (!_typeDefinition.Attributes.IsNested()) return null; var handle = _typeDefinition.GetDeclaringType(); return (DefType)_module.GetType(handle); } } public override bool HasCustomAttribute(string attributeNamespace, string attributeName) { return !MetadataReader.GetCustomAttributeHandle(_typeDefinition.GetCustomAttributes(), attributeNamespace, attributeName).IsNil; } public override ClassLayoutMetadata GetClassLayout() { TypeLayout layout = _typeDefinition.GetLayout(); ClassLayoutMetadata result; result.PackingSize = layout.PackingSize; result.Size = layout.Size; // Skip reading field offsets if this is not explicit layout if (IsExplicitLayout) { var fieldDefinitionHandles = _typeDefinition.GetFields(); var numInstanceFields = 0; foreach (var handle in fieldDefinitionHandles) { var fieldDefinition = MetadataReader.GetFieldDefinition(handle); if ((fieldDefinition.Attributes & FieldAttributes.Static) != 0) continue; numInstanceFields++; } result.Offsets = new FieldAndOffset[numInstanceFields]; int index = 0; foreach (var handle in fieldDefinitionHandles) { var fieldDefinition = MetadataReader.GetFieldDefinition(handle); if ((fieldDefinition.Attributes & FieldAttributes.Static) != 0) continue; // Note: GetOffset() returns -1 when offset was not set in the metadata int specifiedOffset = fieldDefinition.GetOffset(); result.Offsets[index] = new FieldAndOffset((FieldDesc)_module.GetObject(handle), specifiedOffset == -1 ? FieldAndOffset.InvalidOffset : new LayoutInt(specifiedOffset)); index++; } } else result.Offsets = null; return result; } public override MarshalAsDescriptor[] GetFieldMarshalAsDescriptors() { var fieldDefinitionHandles = _typeDefinition.GetFields(); MarshalAsDescriptor[] marshalAsDescriptors = new MarshalAsDescriptor[fieldDefinitionHandles.Count]; int index = 0; foreach (var handle in fieldDefinitionHandles) { var fieldDefinition = MetadataReader.GetFieldDefinition(handle); if ((fieldDefinition.Attributes & FieldAttributes.Static) != 0) continue; MarshalAsDescriptor marshalAsDescriptor = GetMarshalAsDescriptor(fieldDefinition); marshalAsDescriptors[index++] = marshalAsDescriptor; } return marshalAsDescriptors; } private MarshalAsDescriptor GetMarshalAsDescriptor(FieldDefinition fieldDefinition) { if ((fieldDefinition.Attributes & FieldAttributes.HasFieldMarshal) == FieldAttributes.HasFieldMarshal) { MetadataReader metadataReader = MetadataReader; BlobReader marshalAsReader = metadataReader.GetBlobReader(fieldDefinition.GetMarshallingDescriptor()); EcmaSignatureParser parser = new EcmaSignatureParser(EcmaModule, marshalAsReader); MarshalAsDescriptor marshalAs = parser.ParseMarshalAsDescriptor(); Debug.Assert(marshalAs != null); return marshalAs; } return null; } public override bool IsExplicitLayout { get { return (_typeDefinition.Attributes & TypeAttributes.ExplicitLayout) != 0; } } public override bool IsSequentialLayout { get { return (_typeDefinition.Attributes & TypeAttributes.SequentialLayout) != 0; } } public override bool IsBeforeFieldInit { get { return (_typeDefinition.Attributes & TypeAttributes.BeforeFieldInit) != 0; } } public override bool IsModuleType { get { return _handle.Equals(MetadataTokens.TypeDefinitionHandle(0x00000001 /* COR_GLOBAL_PARENT_TOKEN */)); } } public override bool IsSealed { get { return (_typeDefinition.Attributes & TypeAttributes.Sealed) != 0; } } public override bool IsAbstract { get { return (_typeDefinition.Attributes & TypeAttributes.Abstract) != 0; } } public override PInvokeStringFormat PInvokeStringFormat { get { return (PInvokeStringFormat)(_typeDefinition.Attributes & TypeAttributes.StringFormatMask); } } } }