1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System.Collections.Generic;
6 using System.Collections.ObjectModel;
7 using System.Runtime.InteropServices;
9 namespace System.Reflection.TypeLoading
11 internal static class CustomAttributeHelpers
14 /// Helper for creating a CustomAttributeNamedArgument.
16 public static CustomAttributeNamedArgument ToCustomAttributeNamedArgument(this Type attributeType, string name, Type argumentType, object value)
18 MemberInfo[] members = attributeType.GetMember(name, MemberTypes.Field | MemberTypes.Property, BindingFlags.Public | BindingFlags.Instance);
19 if (members.Length == 0)
20 throw new MissingMemberException(attributeType.FullName, name);
21 if (members.Length > 1)
22 throw new AmbiguousMatchException();
24 return new CustomAttributeNamedArgument(members[0], new CustomAttributeTypedArgument(argumentType, value));
28 /// Clones a cached CustomAttributeTypedArgument list into a freshly allocated one suitable for direct return through an api.
30 public static ReadOnlyCollection<CustomAttributeTypedArgument> CloneForApiReturn(this IList<CustomAttributeTypedArgument> cats)
32 int count = cats.Count;
33 CustomAttributeTypedArgument[] clones = new CustomAttributeTypedArgument[count];
34 for (int i = 0; i < count; i++)
36 clones[i] = cats[i].CloneForApiReturn();
38 return clones.ToReadOnlyCollection();
42 /// Clones a cached CustomAttributeNamedArgument list into a freshly allocated one suitable for direct return through an api.
44 public static ReadOnlyCollection<CustomAttributeNamedArgument> CloneForApiReturn(this IList<CustomAttributeNamedArgument> cans)
46 int count = cans.Count;
47 CustomAttributeNamedArgument[] clones = new CustomAttributeNamedArgument[count];
48 for (int i = 0; i < count; i++)
50 clones[i] = cans[i].CloneForApiReturn();
52 return clones.ToReadOnlyCollection();
56 /// Clones a cached CustomAttributeTypedArgument into a freshly allocated one suitable for direct return through an api.
58 private static CustomAttributeTypedArgument CloneForApiReturn(this CustomAttributeTypedArgument cat)
60 Type type = cat.ArgumentType;
61 object value = cat.Value;
63 if (!(value is IList<CustomAttributeTypedArgument> cats))
66 int count = cats.Count;
67 CustomAttributeTypedArgument[] cads = new CustomAttributeTypedArgument[count];
68 for (int i = 0; i < count; i++)
70 cads[i] = cats[i].CloneForApiReturn();
72 return new CustomAttributeTypedArgument(type, cads.ToReadOnlyCollection());
76 /// Clones a cached CustomAttributeNamedArgument into a freshly allocated one suitable for direct return through an api.
78 private static CustomAttributeNamedArgument CloneForApiReturn(this CustomAttributeNamedArgument can)
80 return new CustomAttributeNamedArgument(can.MemberInfo, can.TypedValue.CloneForApiReturn());
84 /// Convert MarshalAsAttribute data into CustomAttributeData form. Returns null if the core assembly cannot be loaded or if the necessary
85 /// types aren't in the core assembly.
87 public static CustomAttributeData TryComputeMarshalAsCustomAttributeData(Func<MarshalAsAttribute> marshalAsAttributeComputer, MetadataLoadContext loader)
89 // Make sure all the necessary framework types exist in this MetadataLoadContext's core assembly. If one doesn't, skip.
90 CoreTypes ct = loader.GetAllFoundCoreTypes();
91 if (ct[CoreType.String] == null ||
92 ct[CoreType.Boolean] == null ||
93 ct[CoreType.UnmanagedType] == null ||
94 ct[CoreType.VarEnum] == null ||
95 ct[CoreType.Type] == null ||
96 ct[CoreType.Int16] == null ||
97 ct[CoreType.Int32] == null)
99 ConstructorInfo ci = loader.TryGetMarshalAsCtor();
103 Func<CustomAttributeArguments> argumentsPromise =
106 // The expensive work goes in here. It will not execute unless someone invokes the Constructor/NamedArguments properties on
107 // the CustomAttributeData.
109 MarshalAsAttribute ma = marshalAsAttributeComputer();
111 Type attributeType = ci.DeclaringType;
113 CustomAttributeTypedArgument[] cats = { new CustomAttributeTypedArgument(ct[CoreType.UnmanagedType], (int)(ma.Value)) };
114 List<CustomAttributeNamedArgument> cans = new List<CustomAttributeNamedArgument>();
115 cans.AddRange(new CustomAttributeNamedArgument[]
117 attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.ArraySubType), ct[CoreType.UnmanagedType], (int)ma.ArraySubType),
118 attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.IidParameterIndex), ct[CoreType.Int32], ma.IidParameterIndex),
119 attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.SafeArraySubType), ct[CoreType.VarEnum], (int)ma.SafeArraySubType),
120 attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.SizeConst), ct[CoreType.Int32], ma.SizeConst),
121 attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.SizeParamIndex), ct[CoreType.Int16], ma.SizeParamIndex),
124 if (ma.SafeArrayUserDefinedSubType != null)
126 cans.Add(attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.SafeArrayUserDefinedSubType), ct[CoreType.Type], ma.SafeArrayUserDefinedSubType));
129 if (ma.MarshalType != null)
131 cans.Add(attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.MarshalType), ct[CoreType.String], ma.MarshalType));
134 if (ma.MarshalTypeRef != null)
136 cans.Add(attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.MarshalTypeRef), ct[CoreType.Type], ma.MarshalTypeRef));
139 if (ma.MarshalCookie != null)
141 cans.Add(attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.MarshalCookie), ct[CoreType.String], ma.MarshalCookie));
144 return new CustomAttributeArguments(cats, cans);
147 return new RoPseudoCustomAttributeData(ci, argumentsPromise);