40fa9406621ed3aa63a11f0fc9fc8e4399d41bf1
[platform/upstream/dotnet/runtime.git] /
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.
4
5 using System.Collections.Generic;
6 using System.Collections.ObjectModel;
7 using System.Runtime.InteropServices;
8
9 namespace System.Reflection.TypeLoading
10 {
11     internal static class CustomAttributeHelpers
12     {
13         /// <summary>
14         /// Helper for creating a CustomAttributeNamedArgument.
15         /// </summary>
16         public static CustomAttributeNamedArgument ToCustomAttributeNamedArgument(this Type attributeType, string name, Type argumentType, object value)
17         {
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();
23
24             return new CustomAttributeNamedArgument(members[0], new CustomAttributeTypedArgument(argumentType, value));
25         }
26
27         /// <summary>
28         /// Clones a cached CustomAttributeTypedArgument list into a freshly allocated one suitable for direct return through an api.
29         /// </summary>
30         public static ReadOnlyCollection<CustomAttributeTypedArgument> CloneForApiReturn(this IList<CustomAttributeTypedArgument> cats)
31         {
32             int count = cats.Count;
33             CustomAttributeTypedArgument[] clones = new CustomAttributeTypedArgument[count];
34             for (int i = 0; i < count; i++)
35             {
36                 clones[i] = cats[i].CloneForApiReturn();
37             }
38             return clones.ToReadOnlyCollection();
39         }
40
41         /// <summary>
42         /// Clones a cached CustomAttributeNamedArgument list into a freshly allocated one suitable for direct return through an api.
43         /// </summary>
44         public static ReadOnlyCollection<CustomAttributeNamedArgument> CloneForApiReturn(this IList<CustomAttributeNamedArgument> cans)
45         {
46             int count = cans.Count;
47             CustomAttributeNamedArgument[] clones = new CustomAttributeNamedArgument[count];
48             for (int i = 0; i < count; i++)
49             {
50                 clones[i] = cans[i].CloneForApiReturn();
51             }
52             return clones.ToReadOnlyCollection();
53         }
54
55         /// <summary>
56         /// Clones a cached CustomAttributeTypedArgument into a freshly allocated one suitable for direct return through an api.
57         /// </summary>
58         private static CustomAttributeTypedArgument CloneForApiReturn(this CustomAttributeTypedArgument cat)
59         {
60             Type type = cat.ArgumentType;
61             object value = cat.Value;
62
63             if (!(value is IList<CustomAttributeTypedArgument> cats))
64                 return cat;
65
66             int count = cats.Count;
67             CustomAttributeTypedArgument[] cads = new CustomAttributeTypedArgument[count];
68             for (int i = 0; i < count; i++)
69             {
70                 cads[i] = cats[i].CloneForApiReturn();
71             }
72             return new CustomAttributeTypedArgument(type, cads.ToReadOnlyCollection());
73         }
74
75         /// <summary>
76         /// Clones a cached CustomAttributeNamedArgument into a freshly allocated one suitable for direct return through an api.
77         /// </summary>
78         private static CustomAttributeNamedArgument CloneForApiReturn(this CustomAttributeNamedArgument can)
79         {
80             return new CustomAttributeNamedArgument(can.MemberInfo, can.TypedValue.CloneForApiReturn());
81         }
82
83         /// <summary>
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.
86         /// </summary>
87         public static CustomAttributeData TryComputeMarshalAsCustomAttributeData(Func<MarshalAsAttribute> marshalAsAttributeComputer, MetadataLoadContext loader)
88         {
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)
98                 return null;
99             ConstructorInfo ci = loader.TryGetMarshalAsCtor();
100             if (ci == null)
101                 return null;
102
103             Func<CustomAttributeArguments> argumentsPromise =
104                 () =>
105                 {
106                     // The expensive work goes in here. It will not execute unless someone invokes the Constructor/NamedArguments properties on
107                     // the CustomAttributeData.
108
109                     MarshalAsAttribute ma = marshalAsAttributeComputer();
110
111                     Type attributeType = ci.DeclaringType;
112
113                     CustomAttributeTypedArgument[] cats = { new CustomAttributeTypedArgument(ct[CoreType.UnmanagedType], (int)(ma.Value)) };
114                     List<CustomAttributeNamedArgument> cans = new List<CustomAttributeNamedArgument>();
115                     cans.AddRange(new CustomAttributeNamedArgument[]
116                     {
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),
122                     });
123
124                     if (ma.SafeArrayUserDefinedSubType != null)
125                     {
126                         cans.Add(attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.SafeArrayUserDefinedSubType), ct[CoreType.Type], ma.SafeArrayUserDefinedSubType));
127                     }
128
129                     if (ma.MarshalType != null)
130                     {
131                         cans.Add(attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.MarshalType), ct[CoreType.String], ma.MarshalType));
132                     }
133
134                     if (ma.MarshalTypeRef != null)
135                     {
136                         cans.Add(attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.MarshalTypeRef), ct[CoreType.Type], ma.MarshalTypeRef));
137                     }
138
139                     if (ma.MarshalCookie != null)
140                     {
141                         cans.Add(attributeType.ToCustomAttributeNamedArgument(nameof(MarshalAsAttribute.MarshalCookie), ct[CoreType.String], ma.MarshalCookie));
142                     }
143
144                     return new CustomAttributeArguments(cats, cans);
145                 };
146
147             return new RoPseudoCustomAttributeData(ci, argumentsPromise);
148         }
149     }
150 }