-
Notifications
You must be signed in to change notification settings - Fork 124
Expand file tree
/
Copy pathTypeExtensions.cs
More file actions
365 lines (327 loc) · 16.4 KB
/
TypeExtensions.cs
File metadata and controls
365 lines (327 loc) · 16.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace WinRT
{
#if EMBED
internal
#else
public
#endif
static class TypeExtensions
{
internal static readonly ConcurrentDictionary<Type, Type> HelperTypeCache = new ConcurrentDictionary<Type, Type>();
#if NET
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.PublicFields)]
[UnconditionalSuppressMessage("Trimming", "IL2073", Justification = "Matching trimming annotations are used at all callsites registering helper types present in the cache.")]
#endif
public static Type FindHelperType(this Type type)
{
return type.FindHelperType(true);
}
#if NET
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.PublicFields)]
[UnconditionalSuppressMessage("Trimming", "IL2073", Justification = "Matching trimming annotations are used at all callsites registering helper types present in the cache.")]
#endif
internal static Type FindHelperType(this Type type, bool throwIfNotAotSupported)
{
#if NET
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.PublicFields)]
#endif
static Type FindHelperTypeNoCache(Type type)
{
if (typeof(Exception).IsAssignableFrom(type))
{
type = typeof(Exception);
}
Type customMapping = Projections.FindCustomHelperTypeMapping(type, false, true);
if (customMapping is not null)
{
return customMapping;
}
var helperTypeAtribute = type.GetCustomAttribute<WindowsRuntimeHelperTypeAttribute>();
if (helperTypeAtribute is not null)
{
return GetHelperTypeFromAttribute(helperTypeAtribute, type);
}
var authoringMetadaType = type.GetAuthoringMetadataType();
if (authoringMetadaType is not null)
{
helperTypeAtribute = authoringMetadaType.GetCustomAttribute<WindowsRuntimeHelperTypeAttribute>();
if (helperTypeAtribute is not null)
{
return GetHelperTypeFromAttribute(helperTypeAtribute, type);
}
}
#if NET
// Using AOT requires using updated projections, which would never let the code below
// be reached (as it's just a fallback path for legacy projections). So we can trim it.
if (!RuntimeFeature.IsDynamicCodeCompiled)
{
return null;
}
#endif
return FindHelperTypeFallback(type);
}
var helperType = HelperTypeCache.GetOrAdd(type, FindHelperTypeNoCache);
#if NET
if (!RuntimeFeature.IsDynamicCodeCompiled)
{
if (helperType == typeof(HelperTypeMetadataNotAvailableOnAot))
{
return throwIfNotAotSupported ? throw new NotSupportedException($"Cannot retrieve a helper type for generic public type '{type}'.") : null;
}
}
#endif
return helperType;
#if NET
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "No members of the generic type are dynamically accessed other than for the attributes on it.")]
[UnconditionalSuppressMessage("Trimming", "IL2055", Justification = "The type arguments are guaranteed to be valid for the generic ABI types.")]
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicFields)]
#endif
static Type GetHelperTypeFromAttribute(WindowsRuntimeHelperTypeAttribute helperTypeAtribute, Type type)
{
if (type.IsGenericType && !type.IsGenericTypeDefinition)
{
#if NET
if (!RuntimeFeature.IsDynamicCodeCompiled)
{
return typeof(HelperTypeMetadataNotAvailableOnAot);
}
#endif
#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273
return helperTypeAtribute.HelperType.MakeGenericType(type.GetGenericArguments());
#pragma warning restore IL3050
}
else
{
return helperTypeAtribute.HelperType;
}
}
#if NET
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification =
"This is a fallback for compat purposes with existing projections. " +
"Applications which make use of trimming will make use of updated projections that won't hit this code path.")]
[UnconditionalSuppressMessage("Trimming", "IL2057", Justification =
"This is a fallback for compat purposes with existing projections. " +
"Applications which make use of trimming will make use of updated projections that won't hit this code path.")]
#endif
static Type FindHelperTypeFallback(Type type)
{
string fullTypeName = type.FullName;
string ccwTypePrefix = "ABI.Impl.";
if (fullTypeName.StartsWith(ccwTypePrefix, StringComparison.Ordinal))
{
fullTypeName = fullTypeName.Substring(ccwTypePrefix.Length);
}
var helper = $"ABI.{fullTypeName}";
return type.Assembly.GetType(helper) ?? Type.GetType(helper);
}
}
#if NET
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.PublicFields)]
#endif
public static Type GetHelperType(this Type type)
{
var helperType = type.FindHelperType();
if (helperType is object)
return helperType;
throw new InvalidOperationException($"Target type is not a projected type: {type.FullName}.");
}
#if NET
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)]
#endif
public static Type GetGuidType(
#if NET
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)]
#endif
this Type type)
{
return type.IsDelegate() ? type.GetHelperType() : type;
}
#if NET
[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "The fallback path is not AOT-safe by design (to avoid annotations).")]
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The fallback path is not trim-safe by design (to avoid annotations).")]
#endif
public static Type FindVftblType(this Type helperType)
{
#if NET
if (!RuntimeFeature.IsDynamicCodeCompiled)
{
return null;
}
#endif
#if NET8_0_OR_GREATER
[RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)]
#endif
#if NET
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.Windows.Foundation.IAsyncActionWithProgress`1+Vftbl", "Microsoft.Windows.SDK.NET")]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.Windows.Foundation.IAsyncOperationWithProgress`2+Vftbl", "Microsoft.Windows.SDK.NET")]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.Windows.Foundation.IAsyncOperation`1+Vftbl", "Microsoft.Windows.SDK.NET")]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.Windows.Foundation.Collections.IMapChangedEventArgs`1+Vftbl", "Microsoft.Windows.SDK.NET")]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.Windows.Foundation.Collections.IObservableMap`2+Vftbl", "Microsoft.Windows.SDK.NET")]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.Windows.Foundation.Collections.IObservableVector`1+Vftbl", "Microsoft.Windows.SDK.NET")]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.System.EventHandler`1+Vftbl", "WinRT.Runtime")]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.System.Collections.Generic.KeyValuePair`2+Vftbl", "WinRT.Runtime")]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.System.Collections.Generic.IEnumerable`1+Vftbl", "WinRT.Runtime")]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.System.Collections.Generic.IEnumerator`1+Vftbl", "WinRT.Runtime")]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.System.Collections.Generic.IList`1+Vftbl", "WinRT.Runtime")]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.System.Collections.Generic.IReadOnlyList`1+Vftbl", "WinRT.Runtime")]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.System.Collections.Generic.IDictionary`2+Vftbl", "WinRT.Runtime")]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.System.Collections.Generic.IReadOnlyDictionary`2+Vftbl", "WinRT.Runtime")]
[RequiresUnreferencedCode(AttributeMessages.GenericRequiresUnreferencedCodeMessage)]
#endif
static Type FindVftblTypeFallback(Type helperType)
{
Type vftblType = helperType.GetNestedType("Vftbl");
if (vftblType is null)
{
return null;
}
if (helperType.IsGenericType)
{
vftblType = vftblType.MakeGenericType(helperType.GetGenericArguments());
}
return vftblType;
}
return FindVftblTypeFallback(helperType);
}
#if NET
[UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "The path using vtable types is a fallback and is not trim-safe by design.")]
#endif
internal static IntPtr GetAbiToProjectionVftblPtr(
#if NET
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)]
#endif
this Type helperType)
{
return (IntPtr)(helperType.FindVftblType() ?? helperType).GetField("AbiToProjectionVftablePtr", BindingFlags.Public | BindingFlags.Static).GetValue(null);
}
public static Type GetAbiType(this Type type)
{
return type.GetHelperType().GetMethod("GetAbi", BindingFlags.Public | BindingFlags.Static).ReturnType;
}
public static Type GetMarshalerType(this Type type)
{
return type.GetHelperType().GetMethod("CreateMarshaler", BindingFlags.Public | BindingFlags.Static).ReturnType;
}
internal static Type GetMarshaler2Type(this Type type)
{
var helperType = type.GetHelperType();
var createMarshaler =
helperType.GetMethod("CreateMarshaler2", BindingFlags.Public | BindingFlags.Static) ??
helperType.GetMethod("CreateMarshaler", BindingFlags.Public | BindingFlags.Static);
return createMarshaler.ReturnType;
}
internal static Type GetMarshalerArrayType(this Type type)
{
return type.GetHelperType().GetMethod("CreateMarshalerArray", BindingFlags.Public | BindingFlags.Static)?.ReturnType;
}
public static bool IsDelegate(this Type type)
{
return typeof(Delegate).IsAssignableFrom(type);
}
internal static bool IsNullableT(this Type type)
{
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Nullable<>);
}
internal static bool IsAbiNullableDelegate(this Type type)
{
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ABI.System.Nullable_Delegate<>);
}
internal static bool IsIReferenceArray(this Type type)
{
// If support for 'IReference<T>' is disabled, we'll never instantiate any types implementing this interface. We
// can guard this check behind the feature switch to avoid making 'IReferenceArray<T>' reflectable, which will
// otherwise root some unnecessary code and metadata from the ABI implementation type.
if (!FeatureSwitches.EnableIReferenceSupport)
{
return false;
}
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Windows.Foundation.IReferenceArray<>);
}
internal static bool ShouldProvideIReference(this Type type)
{
if (!FeatureSwitches.EnableIReferenceSupport)
{
return false;
}
return type.IsPrimitive ||
type == typeof(string) ||
type == typeof(Guid) ||
type == typeof(DateTimeOffset) ||
type == typeof(TimeSpan) ||
type.IsTypeOfType() ||
type.IsTypeOfException() ||
((type.IsValueType || type.IsDelegate()) && Projections.IsTypeWindowsRuntimeType(type));
}
internal static bool IsTypeOfType(this Type type)
{
return typeof(Type).IsAssignableFrom(type);
}
internal static bool IsTypeOfException(this Type type)
{
return typeof(Exception).IsAssignableFrom(type);
}
public static Type GetRuntimeClassCCWType(this Type type)
{
return type.IsClass && !type.IsArray ? type.GetAuthoringMetadataType() : null;
}
internal static Type GetCCWType(this Type type)
{
return !type.IsArray ? type.GetAuthoringMetadataType() : null;
}
private static readonly ConcurrentDictionary<Type, Type> AuthoringMetadataTypeCache = new();
private static readonly List<Func<Type, Type>> AuthoringMetadaTypeLookup = new();
internal static void RegisterAuthoringMetadataTypeLookup(Func<Type, Type> authoringMetadataTypeLookup)
{
AuthoringMetadaTypeLookup.Add(authoringMetadataTypeLookup);
}
internal static Type GetAuthoringMetadataType(this Type type)
{
return AuthoringMetadataTypeCache.GetOrAdd(type,
static (type) =>
{
// Using for loop to avoid exception from list changing when using for each.
// List is only added to and if any are added while looping, we can ignore those.
int count = AuthoringMetadaTypeLookup.Count;
for (int i = 0; i < count; i++)
{
Type metadataType = AuthoringMetadaTypeLookup[i](type);
if (metadataType is not null)
{
return metadataType;
}
}
#if NET
if (!RuntimeFeature.IsDynamicCodeCompiled)
{
return null;
}
#endif
// Fallback code path for back compat with previously generated projections
// running without AOT.
return GetAuthoringMetadataTypeFallback(type);
});
}
#if NET
[UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
Justification = "This is a fallback for compat purposes with existing projections. " +
"Applications making use of updated projections won't hit this code path.")]
#endif
private static Type GetAuthoringMetadataTypeFallback(Type type)
{
var ccwTypeName = $"ABI.Impl.{type.FullName}";
return type.Assembly.GetType(ccwTypeName, false);
}
}
}