| File: System\Reflection\TypeLoading\CustomAttributes\Ecma\EcmaCustomAttributeHelpers.cs | Web Access |
| Project: src\runtime\src\libraries\System.Reflection.MetadataLoadContext\src\System.Reflection.MetadataLoadContext.csproj (System.Reflection.MetadataLoadContext) |
// 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.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Reflection.Metadata; using System.Runtime.InteropServices; #pragma warning disable 0618 // Obsolete members namespace System.Reflection.TypeLoading.Ecma { internal static class EcmaCustomAttributeHelpers { /// <summary> /// Converts ECMA-encoded custom attributes into a freshly allocated CustomAttributeData object suitable for direct return /// from the CustomAttributes api. /// </summary> public static IEnumerable<CustomAttributeData> ToTrueCustomAttributes(this CustomAttributeHandleCollection handles, EcmaModule module) { foreach (CustomAttributeHandle handle in handles) { yield return handle.ToCustomAttributeData(module); } } public static CustomAttributeData ToCustomAttributeData(this CustomAttributeHandle handle, EcmaModule module) => new EcmaCustomAttributeData(handle, module); public static bool IsCustomAttributeDefined(this CustomAttributeHandleCollection handles, ReadOnlySpan<byte> ns, ReadOnlySpan<byte> name, EcmaModule module) { return !handles.FindCustomAttributeByName(ns, name, module).IsNil; } public static CustomAttributeData? TryFindCustomAttribute(this CustomAttributeHandleCollection handles, ReadOnlySpan<byte> ns, ReadOnlySpan<byte> name, EcmaModule module) { CustomAttributeHandle handle = handles.FindCustomAttributeByName(ns, name, module); if (handle.IsNil) return null; return handle.ToCustomAttributeData(module); } private static CustomAttributeHandle FindCustomAttributeByName(this CustomAttributeHandleCollection handles, ReadOnlySpan<byte> ns, ReadOnlySpan<byte> name, EcmaModule module) { MetadataReader reader = module.Reader; foreach (CustomAttributeHandle handle in handles) { CustomAttribute ca = handle.GetCustomAttribute(reader); EntityHandle declaringTypeHandle = ca.TryGetDeclaringTypeHandle(reader); if (declaringTypeHandle.IsNil) continue; if (declaringTypeHandle.TypeMatchesNameAndNamespace(ns, name, reader)) return handle; } return default; } public static bool TypeMatchesNameAndNamespace(this EntityHandle handle, ReadOnlySpan<byte> ns, ReadOnlySpan<byte> name, MetadataReader reader) { switch (handle.Kind) { case HandleKind.TypeDefinition: // Not clear if this can happen but as fail-safe.... { TypeDefinition td = ((TypeDefinitionHandle)handle).GetTypeDefinition(reader); return td.Name.Equals(name, reader) && td.Namespace.Equals(ns, reader); } case HandleKind.TypeReference: { TypeReference tr = ((TypeReferenceHandle)handle).GetTypeReference(reader); return tr.ResolutionScope.Kind != HandleKind.TypeReference && tr.Name.Equals(name, reader) && tr.Namespace.Equals(ns, reader); } default: return false; } } public static EntityHandle TryGetDeclaringTypeHandle(this in CustomAttribute ca, MetadataReader reader) { EntityHandle ctorHandle = ca.Constructor; switch (ctorHandle.Kind) { case HandleKind.MethodDefinition: { MethodDefinitionHandle mh = (MethodDefinitionHandle)ctorHandle; return mh.GetMethodDefinition(reader).GetDeclaringType(); } case HandleKind.MemberReference: { MemberReferenceHandle mh = (MemberReferenceHandle)ctorHandle; return mh.GetMemberReference(reader).Parent; } default: return default; } } /// <summary> /// Converts a list of System.Reflection.Metadata CustomAttributeTypedArgument<> into a freshly allocated CustomAttributeTypedArgument /// list suitable for direct return from the CustomAttributes api. /// </summary> public static IList<CustomAttributeTypedArgument> ToApiForm(this IList<CustomAttributeTypedArgument<RoType>> catgs) { int count = catgs.Count; CustomAttributeTypedArgument[] cats = count != 0 ? new CustomAttributeTypedArgument[count] : Array.Empty<CustomAttributeTypedArgument>(); for (int i = 0; i < count; i++) { cats[i] = catgs[i].ToApiForm(); } return Array.AsReadOnly(cats); } /// <summary> /// Converts a System.Reflection.Metadata CustomAttributeTypedArgument<> into a freshly allocated CustomAttributeTypedArgument /// object suitable for direct return from the CustomAttributes api. /// </summary> public static CustomAttributeTypedArgument ToApiForm(this CustomAttributeTypedArgument<RoType> catg) => ToApiForm(catg.Type, catg.Value); private static CustomAttributeTypedArgument ToApiForm(Type type, object? value) { if (!(value is IList<CustomAttributeTypedArgument<RoType>> catgs)) { return new CustomAttributeTypedArgument(type, value); } return new CustomAttributeTypedArgument(type, catgs.ToApiForm()); } /// <summary> /// Converts a list of System.Reflection.Metadata CustomAttributeNamedArgument<> into a freshly allocated CustomAttributeNamedArgument /// list suitable for direct return from the CustomAttributes api. /// </summary> public static IList<CustomAttributeNamedArgument> ToApiForm(this IList<CustomAttributeNamedArgument<RoType>> cangs, Type attributeType) { int count = cangs.Count; CustomAttributeNamedArgument[] cans = count != 0 ? new CustomAttributeNamedArgument[count] : Array.Empty<CustomAttributeNamedArgument>(); for (int i = 0; i < count; i++) { cans[i] = cangs[i].ToApiForm(attributeType); } return Array.AsReadOnly(cans); } /// <summary> /// Converts a System.Reflection.Metadata CustomAttributeNamedArgument<> into a freshly allocated CustomAttributeNamedArgument /// object suitable for direct return from the CustomAttributes api. /// </summary> public static CustomAttributeNamedArgument ToApiForm(this CustomAttributeNamedArgument<RoType> cang, Type attributeType) { MemberInfo? member; switch (cang.Kind) { case CustomAttributeNamedArgumentKind.Field: member = attributeType.GetField(cang.Name!, BindingFlags.Public | BindingFlags.Instance); break; case CustomAttributeNamedArgumentKind.Property: member = attributeType.GetProperty(cang.Name!, BindingFlags.Public | BindingFlags.Instance); break; default: Debug.Fail("Invalid CustomAttributeNamedArgumentKind value: " + cang.Kind); throw new BadImageFormatException(); } return new CustomAttributeNamedArgument(member!, ToApiForm(cang.Type, cang.Value)); } // // Logic ported from ParseNativeTypeInfo() // // https://github.com/dotnet/runtime/blob/b908ecf514f32c7ba7d59ecc28fa3fdd64a10a1a/src/coreclr/vm/mlinfo.cpp#L469 // public static MarshalAsAttribute ToMarshalAsAttribute(this BlobHandle blobHandle, EcmaModule module) { MetadataReader reader = module.Reader; BlobReader br = blobHandle.GetBlobReader(reader); UnmanagedType unmgdType = (UnmanagedType)br.ReadByte(); MarshalAsAttribute ma = new MarshalAsAttribute(unmgdType); switch (unmgdType) { case UnmanagedType.Interface: case UnmanagedType.IUnknown: case UnmanagedType.IDispatch: if (br.RemainingBytes == 0) break; ma.IidParameterIndex = br.ReadCompressedInteger(); break; case UnmanagedType.ByValArray: if (br.RemainingBytes == 0) break; ma.SizeConst = br.ReadCompressedInteger(); if (br.RemainingBytes == 0) break; ma.ArraySubType = (UnmanagedType)br.ReadCompressedInteger(); break; case UnmanagedType.ByValTStr: if (br.RemainingBytes == 0) break; ma.SizeConst = br.ReadCompressedInteger(); break; case UnmanagedType.SafeArray: if (br.RemainingBytes == 0) break; ma.SafeArraySubType = (VarEnum)br.ReadCompressedInteger(); if (br.RemainingBytes == 0) break; string? udtName = br.ReadSerializedString(); ma.SafeArrayUserDefinedSubType = Helpers.LoadTypeFromAssemblyQualifiedName(udtName!, module.GetRoAssembly(), ignoreCase: false, throwOnError: false); break; case UnmanagedType.LPArray: if (br.RemainingBytes == 0) break; ma.ArraySubType = (UnmanagedType)br.ReadCompressedInteger(); if (br.RemainingBytes == 0) break; ma.SizeParamIndex = (short)br.ReadCompressedInteger(); if (br.RemainingBytes == 0) break; ma.SizeConst = br.ReadCompressedInteger(); break; case UnmanagedType.CustomMarshaler: if (br.RemainingBytes == 0) break; br.ReadSerializedString(); // Skip the typelib guid. if (br.RemainingBytes == 0) break; br.ReadSerializedString(); // Skip name of native type. if (br.RemainingBytes == 0) break; ma.MarshalType = br.ReadSerializedString(); ma.MarshalTypeRef = Helpers.LoadTypeFromAssemblyQualifiedName(ma.MarshalType!, module.GetRoAssembly(), ignoreCase: false, throwOnError: false); if (br.RemainingBytes == 0) break; ma.MarshalCookie = br.ReadSerializedString(); break; default: break; } return ma; } } }