-
Notifications
You must be signed in to change notification settings - Fork 519
/
Copy pathBlocks.cs
721 lines (629 loc) · 34.4 KB
/
Blocks.cs
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
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
//
// Block support
//
// Copyright 2010, Novell, Inc.
// Copyright 2011 - 2013 Xamarin Inc
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;
using Foundation;
using ObjCRuntime;
#if !COREBUILD
using Xamarin.Bundler;
#endif
// Disable until we get around to enable + fix any issues.
#nullable disable
// http://clang.llvm.org/docs/Block-ABI-Apple.html
namespace ObjCRuntime {
#pragma warning disable 649 // Field 'XamarinBlockDescriptor.ref_count' is never assigned to, and will always have its default value 0
[StructLayout (LayoutKind.Sequential)]
struct BlockDescriptor {
public IntPtr reserved;
public IntPtr size;
public IntPtr copy_helper;
public IntPtr dispose;
public IntPtr signature;
}
#pragma warning restore 649
struct XamarinBlockDescriptor {
#pragma warning disable 649 // Field 'XamarinBlockDescriptor.descriptor' is never assigned to, and will always have its default value
public BlockDescriptor descriptor;
public volatile int ref_count;
#pragma warning restore 649
// followed by variable-length string (the signature)
}
[StructLayout (LayoutKind.Sequential)]
#if XAMCORE_5_0
// Let's try to make this a ref struct in XAMCORE_5_0, that will mean blocks can't be boxed (which is good, because it would most likely result in broken code).
// Note that the presence of a Dispose method is enough to be able to do a 'using var block = new BlockLiteral ()' in C# due to pattern-based using for 'ref structs':
// Ref: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using#pattern-based-using
public unsafe ref struct BlockLiteral
#elif COREBUILD
public unsafe struct BlockLiteral {
#else
public unsafe struct BlockLiteral : IDisposable {
#endif
#pragma warning disable 169
IntPtr isa;
BlockFlags flags;
int reserved;
IntPtr invoke;
IntPtr block_descriptor;
IntPtr local_handle;
IntPtr global_handle;
#pragma warning restore 169
#if !COREBUILD
static IntPtr block_class;
static IntPtr NSConcreteStackBlock {
get {
if (block_class == IntPtr.Zero)
block_class = Dlfcn.dlsym (Libraries.System.Handle, "_NSConcreteStackBlock");
return block_class;
}
}
[DllImport ("__Internal")]
static extern IntPtr xamarin_get_block_descriptor ();
#if NET
/// <summary>
/// Creates a block literal.
/// </summary>
/// <param name="trampoline">A function pointer that will be called when the block is called. This function must have an [UnmanagedCallersOnly] attribute.</param>
/// <param name="context">A context object that can be retrieved from the trampoline. This is typically a delegate to the managed function to call.</param>
/// <param name="trampolineType">The type where the trampoline is located.</param>
/// <param name="trampolineMethod">The name of the trampoline method.</param>
/// <remarks>
/// The 'trampolineType' and 'trampolineMethod' must uniquely define the trampoline method (it will be looked up using reflection).
/// If there are multiple methods with the same name, use the overload that takes a MethodInfo instead.
/// </remarks>
public BlockLiteral (void* trampoline, object context, Type trampolineType, string trampolineMethod)
: this (trampoline, context, FindTrampoline (trampolineType, trampolineMethod))
{
}
/// <summary>
/// Creates a block literal.
/// </summary>
/// <param name="trampoline">A function pointer that will be called when the block is called. This function must have an [UnmanagedCallersOnly] attribute.</param>
/// <param name="context">A context object that can be retrieved from the trampoline. This is typically a delegate to the managed function to call.</param>
/// <param name="trampolineMethod">The MethodInfo instance corresponding with the trampoline method.</param>
public BlockLiteral (void* trampoline, object context, MethodInfo trampolineMethod)
: this (trampoline, context, GetBlockSignature (trampoline, trampolineMethod))
{
}
/// <summary>
/// Creates a block literal.
/// </summary>
/// <param name="trampoline">A function pointer that will be called when the block is called. This function must have an [UnmanagedCallersOnly] attribute.</param>
/// <param name="context">A context object that can be retrieved from the trampoline. This is typically a delegate to the managed function to call.</param>
/// <param name="trampolineSignature">The Objective-C signature of the trampoline method.</param>
public BlockLiteral (void* trampoline, object context, string trampolineSignature)
{
isa = IntPtr.Zero;
flags = (BlockFlags) 0;
reserved = 0;
invoke = IntPtr.Zero;
block_descriptor = IntPtr.Zero;
local_handle = IntPtr.Zero;
global_handle = IntPtr.Zero;
SetupFunctionPointerBlock ((IntPtr) trampoline, context, System.Text.Encoding.UTF8.GetBytes (trampolineSignature));
}
#if NET
// Note that the code in this method shouldn't be called when using NativeAOT, so throw an exception in that case.
// IL2070: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods', 'DynamicallyAccessedMemberTypes.NonPublicMethods' in call to 'System.Type.GetMethod(String, BindingFlags)'. The parameter 'trampolineType' of method 'ObjCRuntime.BlockLiteral.FindTrampoline(Type, String)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.
[UnconditionalSuppressMessage ("", "IL2070", Justification = "The APIs this method tries to access are marked by other means, so this is linker-safe.")]
#endif
static MethodInfo FindTrampoline (Type trampolineType, string trampolineMethod)
{
#if NET
// Note that the code in this method shouldn't be called when using NativeAOT, so throw an exception in that case.
if (Runtime.IsNativeAOT)
throw Runtime.CreateNativeAOTNotSupportedException ();
#endif
var rv = trampolineType.GetMethod (trampolineMethod, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
if (rv is null)
throw ErrorHelper.CreateError (8046, Errors.MX8046 /* Unable to find the method '{0}' in the type '{1}' */, trampolineMethod, trampolineType.FullName);
return rv;
}
[BindingImpl (BindingImplOptions.Optimizable)]
static string GetBlockSignature (void* trampoline, MethodInfo trampolineMethod)
{
if (!Runtime.DynamicRegistrationSupported)
throw ErrorHelper.CreateError (8050, Errors.MX8050 /* BlockLiteral.GetBlockSignature is not supported when the dynamic registrar has been linked away. */);
// Verify that there's at least one parameter, and it must be System.IntPtr, void* or ObjCRuntime.BlockLiteral*.
var parameters = trampolineMethod.GetParameters ();
if (parameters.Length < 1)
throw ErrorHelper.CreateError (8048, Errors.MX8048 /* The trampoline method {0} must have at least one parameter. */, trampolineMethod.DeclaringType.FullName + "." + trampolineMethod.Name);
var firstParameterType = parameters [0].ParameterType;
if (firstParameterType != typeof (IntPtr) &&
firstParameterType != typeof (void*) &&
firstParameterType != typeof (BlockLiteral*)) {
throw ErrorHelper.CreateError (8049, Errors.MX8049 /* The first parameter in the trampoline method {0} must be either 'System.IntPtr', 'void*' or 'ObjCRuntime.BlockLiteral*'. */, trampolineMethod.DeclaringType.FullName + "." + trampolineMethod.Name);
}
// Verify that the method as an [UnmanagedCallersOnly] attribute
if (!trampolineMethod.IsDefined (typeof (UnmanagedCallersOnlyAttribute), false))
throw ErrorHelper.CreateError (8051, Errors.MX8051 /* The trampoline method {0} must have an [UnmanagedCallersOnly] attribute. */, trampolineMethod.DeclaringType.FullName + "." + trampolineMethod.Name);
// We need to get the signature of the target method, so that we can compute
// the ObjC signature correctly (the generated method that's actually
// invoked by native code does not have enough type information to compute
// the correct signature).
// This attribute might not exist for third-party libraries created
// with earlier versions of our SDK, so make sure to cope with
// the attribute not being available.
// This logic is mirrored in CoreOptimizeGeneratedCode.ProcessSetupBlock and must be
// updated if anything changes here.
TryGetUserDelegateType (trampolineMethod, trampolineMethod, out var userMethod);
// We're good to go!
return Runtime.ComputeSignature (userMethod, true);
}
#endif // NET
[BindingImpl (BindingImplOptions.Optimizable)]
void SetupBlock (Delegate trampoline, Delegate target, bool safe)
{
// Note that the code in this method shouldn't be called when using any static registrar, so throw an exception in that case.
if (!Runtime.DynamicRegistrationSupported)
throw ErrorHelper.CreateError (8026, "BlockLiteral.SetupBlock is not supported when the dynamic registrar has been linked away.");
// We need to get the signature of the target method, so that we can compute
// the ObjC signature correctly (the generated method that's actually
// invoked by native code does not have enough type information to compute
// the correct signature).
// This attribute might not exist for third-party libraries created
// with earlier versions of Xamarin.iOS, so make sure to cope with
// the attribute not being available.
// This logic is mirrored in CoreOptimizeGeneratedCode.ProcessSetupBlock and must be
// updated if anything changes here.
var blockSignature = TryGetUserDelegateType (trampoline.GetType (), trampoline.Method, out var userMethod);
var signature = Runtime.ComputeSignature (userMethod, blockSignature);
SetupBlockImpl (trampoline, target, safe, System.Text.Encoding.UTF8.GetBytes (signature));
}
#if NET
// Note that the code in this method shouldn't be called when using any static registrar, so throw an exception in that case.
// IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethod(String)'. The return value of method 'ObjCRuntime.UserDelegateTypeAttribute.UserDelegateType.get' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.
[UnconditionalSuppressMessage ("", "IL2075", Justification = "The APIs this method tries to access are marked by other means, so this is linker-safe.")]
#endif
static bool TryGetUserDelegateType (MemberInfo provider, MethodInfo noUserDelegateTypeMethod, out MethodInfo userMethod)
{
var userDelegateType = provider.GetCustomAttribute<UserDelegateTypeAttribute> ()?.UserDelegateType;
if (userDelegateType is not null) {
userMethod = userDelegateType.GetMethod ("Invoke");
return true;
} else {
userMethod = noUserDelegateTypeMethod;
return false;
}
}
void SetupBlockImpl (Delegate trampoline, Delegate target, bool safe, string signature)
{
SetupBlockImpl (trampoline, target, safe, System.Text.Encoding.UTF8.GetBytes (signature));
}
void SetupBlockImpl (Delegate trampoline, Delegate target, bool safe, byte [] utf8Signature)
{
var invoke = Marshal.GetFunctionPointerForDelegate (trampoline);
SetupFunctionPointerBlock (invoke, GetContext (trampoline, target, safe), utf8Signature);
}
static object GetContext (Delegate trampoline, Delegate target, bool safe)
{
if (safe) {
return new Tuple<Delegate, Delegate> (trampoline, target);
} else {
return target;
}
}
void SetupFunctionPointerBlock (IntPtr invokeMethod, object context, byte [] utf8Signature)
{
if (utf8Signature is null)
ThrowHelper.ThrowArgumentNullException (nameof (utf8Signature));
if (utf8Signature.Length == 0)
ThrowHelper.ThrowArgumentException (nameof (utf8Signature), Errors.MX8052 /* The signature must be a non-empty string. */);
isa = NSConcreteStackBlock;
invoke = invokeMethod;
local_handle = (IntPtr) GCHandle.Alloc (context);
global_handle = IntPtr.Zero;
flags = BlockFlags.BLOCK_HAS_COPY_DISPOSE | BlockFlags.BLOCK_HAS_SIGNATURE;
/* FIXME: support stret blocks */
// we allocate one big block of memory, the first part is the BlockDescriptor,
// the second part is the signature string (no need to allocate a second time
// for the signature if we can avoid it). One descriptor is allocated for every
// Block; this is potentially something the static registrar can fix, since it
// should know every possible trampoline signature.
var bytes = utf8Signature;
var hasNull = utf8Signature [utf8Signature.Length - 1] == 0;
var desclen = sizeof (XamarinBlockDescriptor) + bytes.Length + (hasNull ? 0 : 1 /* null character */);
var descptr = Marshal.AllocHGlobal (desclen);
block_descriptor = descptr;
var xblock_descriptor = (XamarinBlockDescriptor*) block_descriptor;
xblock_descriptor->descriptor = *(BlockDescriptor*) xamarin_get_block_descriptor ();
xblock_descriptor->descriptor.signature = descptr + sizeof (BlockDescriptor) + 4 /* signature_length */;
xblock_descriptor->ref_count = 1;
Marshal.Copy (bytes, 0, xblock_descriptor->descriptor.signature, bytes.Length);
if (!hasNull)
Marshal.WriteByte (xblock_descriptor->descriptor.signature + bytes.Length, 0); // null terminate string
}
// trampoline must be static, and someone else needs to keep a ref to it
[EditorBrowsable (EditorBrowsableState.Never)]
public void SetupBlockUnsafe (Delegate trampoline, Delegate userDelegate)
{
SetupBlock (trampoline, userDelegate, safe: false);
}
// trampoline must be static, but it's not necessary to keep a ref to it
[EditorBrowsable (EditorBrowsableState.Never)]
public void SetupBlock (Delegate trampoline, Delegate userDelegate)
{
if (trampoline is null)
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (trampoline));
VerifyBlockDelegates (trampoline, userDelegate);
SetupBlock (trampoline, userDelegate, safe: true);
}
#if NET
// IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethod(String)'. The return value of method 'ObjCRuntime.MonoPInvokeCallbackAttribute.DelegateType.get' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.
[UnconditionalSuppressMessage ("", "IL2075", Justification = "Calling GetMethod('Invoke') on a delegate type will always find something, because the invoke method can't be linked away for a delegate.")]
#endif
void VerifyBlockDelegates (Delegate trampoline, Delegate userDelegate)
{
#if !MONOMAC && !__MACCATALYST__
// Check that:
// * The trampoline is static
// * The trampoline's method has a [MonoPInvokeCallback] attribute
// * The delegate in the [MonoPInvokeCallback] has the right signature
//
// WARNING: the XAMARIN_IOS_SKIP_BLOCK_CHECK will be removed in a future version,
// if you find you need it, please file a bug with a test case and we'll
// make sure your scenario works without the environment variable before removing it.
if (Runtime.Arch == Arch.SIMULATOR && string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("XAMARIN_IOS_SKIP_BLOCK_CHECK"))) {
// It should be enough to run this check in the simulator
var method = trampoline.Method;
if (!method.IsStatic)
ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {method.DeclaringType.FullName}.{method.Name} is not static.");
var attrib = method.GetCustomAttribute<MonoPInvokeCallbackAttribute> (false);
if (attrib is null)
ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {method.DeclaringType.FullName}.{method.Name} does not have a [MonoPInvokeCallback] attribute.");
Type delegateType = attrib.DelegateType;
var signatureMethod = delegateType.GetMethod ("Invoke");
if (method.ReturnType != signatureMethod.ReturnType)
ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {method.DeclaringType.FullName}.{method.Name}'s return type ({method.ReturnType.FullName}) does not match the return type of the delegate in its [MonoPInvokeCallback] attribute ({signatureMethod.ReturnType.FullName}).");
var parameters = method.GetParameters ();
var signatureParameters = signatureMethod.GetParameters ();
if (parameters.Length != signatureParameters.Length)
ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {method.DeclaringType.FullName}.{method.Name}'s parameter count ({parameters.Length}) does not match the parameter count of the delegate in its [MonoPInvokeCallback] attribute ({signatureParameters.Length}).");
for (int i = 0; i < parameters.Length; i++) {
if (parameters [i].ParameterType != signatureParameters [i].ParameterType)
ObjCRuntime.ThrowHelper.ThrowArgumentException (nameof (trampoline), $"The method {method.DeclaringType.FullName}.{method.Name}'s parameter #{i + 1}'s type ({parameters [i].ParameterType.FullName}) does not match the corresponding parameter type of the delegate in its [MonoPInvokeCallback] attribute ({signatureParameters [i].ParameterType.FullName}).");
}
}
#endif
}
public void CleanupBlock ()
{
Dispose ();
}
public void Dispose ()
{
if (local_handle != IntPtr.Zero) {
GCHandle.FromIntPtr (local_handle).Free ();
local_handle = IntPtr.Zero;
}
if (block_descriptor != IntPtr.Zero) {
var xblock_descriptor = (XamarinBlockDescriptor*) block_descriptor;
#pragma warning disable 420
// CS0420: A volatile field references will not be treated as volatile
// Documentation says: "A volatile field should not normally be passed using a ref or out parameter, since it will not be treated as volatile within the scope of the function. There are exceptions to this, such as when calling an interlocked API."
// So ignoring the warning, since it's a documented exception.
// Interlocked.Decrement returns the new value after the subtraction
var rc = Interlocked.Decrement (ref xblock_descriptor->ref_count);
#pragma warning restore 420
if (rc == 0)
Marshal.FreeHGlobal (block_descriptor);
block_descriptor = IntPtr.Zero;
}
}
/// <summary>
/// This is the 'context' value that was specified when creating the BlockLiteral.
/// </summary>
public object Context {
get {
var handle = global_handle != IntPtr.Zero ? global_handle : local_handle;
return GCHandle.FromIntPtr (handle).Target;
}
}
/// <summary>Returns the target object for the block.</summary>
/// <value>
/// </value>
/// <remarks>
/// </remarks>
public object Target {
get {
var target = Context;
var tuple = target as Tuple<Delegate, Delegate>;
if (tuple is not null)
return tuple.Item2;
return target;
}
}
#if NET
public T GetDelegateForBlock<T> () where T : System.MulticastDelegate
#else
public T GetDelegateForBlock<T> () where T : class
#endif
{
return Runtime.GetDelegateForBlock<T> (invoke);
}
#if NET
public unsafe static T GetTarget<T> (IntPtr block) where T : System.MulticastDelegate
#else
public unsafe static T GetTarget<T> (IntPtr block) where T : class /* /* requires C# 7.3+: System.MulticastDelegate */
#endif
{
return (T) ((BlockLiteral*) block)->Target;
}
[EditorBrowsable (EditorBrowsableState.Never)]
public static bool IsManagedBlock (IntPtr block)
{
if (block == IntPtr.Zero)
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (block));
BlockLiteral* literal = (BlockLiteral*) block;
BlockDescriptor* descriptor = (BlockDescriptor*) xamarin_get_block_descriptor ();
return descriptor->copy_helper == ((BlockDescriptor*) literal->block_descriptor)->copy_helper;
}
#if NET
// This method should never be called when using the managed static registrar, so assert that never happens by throwing an exception in that case.
// This method doesn't necessarily work with NativeAOT, but this is covered by the exception, because the managed static registrar is required for NativeAOT.
//
// IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.Interfaces' in call to 'System.Type.GetInterfaces()'. The return value of method 'System.Reflection.MemberInfo.DeclaringType.get' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.
[UnconditionalSuppressMessage ("", "IL2075", Justification = "The APIs this method tries to access are marked by other means, so this is linker-safe.")]
// IL2062: Value passed to parameter 'interfaceType' of method 'System.Type.GetInterfaceMap(Type)' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements.
[UnconditionalSuppressMessage ("", "IL2062", Justification = "The APIs this method tries to access are marked by other means, so this is linker-safe.")]
#endif
static Type GetDelegateProxyType (MethodInfo minfo, uint token_ref, out MethodInfo baseMethod)
{
#if NET
// Note that the code in this method doesn't necessarily work with NativeAOT, so assert that never happens by throwing an exception if using the managed static registrar (which is required for NativeAOT)
if (Runtime.IsManagedStaticRegistrar)
throw new System.Diagnostics.UnreachableException ();
#endif
// A mirror of this method is also implemented in StaticRegistrar:GetDelegateProxyType
// If this method is changed, that method will probably have to be updated too (tests!!!)
baseMethod = null;
if (token_ref != Runtime.INVALID_TOKEN_REF)
return Class.ResolveTypeTokenReference (token_ref);
baseMethod = minfo.GetBaseDefinition ();
var delegateProxies = baseMethod.ReturnTypeCustomAttributes.GetCustomAttributes (typeof (DelegateProxyAttribute), false);
if (delegateProxies.Length > 0)
return ((DelegateProxyAttribute) delegateProxies [0]).DelegateType;
// We might be implementing a protocol, find any DelegateProxy attributes on the corresponding interface as well.
string selector = null;
foreach (var iface in minfo.DeclaringType.GetInterfaces ()) {
if (!iface.IsDefined (typeof (ProtocolAttribute), false))
continue;
var map = minfo.DeclaringType.GetInterfaceMap (iface);
for (int i = 0; i < map.TargetMethods.Length; i++) {
if (map.TargetMethods [i] == minfo) {
delegateProxies = map.InterfaceMethods [i].ReturnTypeCustomAttributes.GetCustomAttributes (typeof (DelegateProxyAttribute), false);
if (delegateProxies.Length > 0)
return ((DelegateProxyAttribute) delegateProxies [0]).DelegateType;
}
}
// It might be an optional method/property, in which case we need to check any ProtocolMember attributes
if (selector is null)
selector = Runtime.GetExportAttribute (minfo)?.Selector ?? string.Empty;
if (!string.IsNullOrEmpty (selector)) {
var attrib = Runtime.GetProtocolMemberAttribute (iface, selector, minfo);
if (attrib?.ReturnTypeDelegateProxy is not null)
return attrib.ReturnTypeDelegateProxy;
}
}
throw ErrorHelper.CreateError (8011, $"Unable to locate the delegate to block conversion attribute ([DelegateProxy]) for the return value for the method {baseMethod.DeclaringType.FullName}.{baseMethod.Name}. {Constants.PleaseFileBugReport}");
}
#if NET
[BindingImpl (BindingImplOptions.Optimizable)]
unsafe static IntPtr GetBlockForFunctionPointer (MethodInfo delegateInvokeMethod, object @delegate, string signature)
{
void* invokeFunctionPointer = (void*) delegateInvokeMethod.MethodHandle.GetFunctionPointer ();
if (signature is null) {
if (!Runtime.DynamicRegistrationSupported)
throw ErrorHelper.CreateError (8026, $"BlockLiteral.GetBlockForDelegate with a null signature is not supported when the dynamic registrar has been linked away (delegate type: {@delegate.GetType ().FullName}).");
using (var block = new BlockLiteral (invokeFunctionPointer, @delegate, delegateInvokeMethod))
return _Block_copy (&block);
} else {
using (var block = new BlockLiteral (invokeFunctionPointer, @delegate, signature))
return _Block_copy (&block);
}
}
#endif // NET
[EditorBrowsable (EditorBrowsableState.Never)]
[BindingImpl (BindingImplOptions.Optimizable)]
static IntPtr CreateBlockForDelegate (Delegate @delegate, Delegate delegateProxyFieldValue, string /*?*/ signature)
{
if (@delegate is null)
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (@delegate));
if (delegateProxyFieldValue is null)
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (delegateProxyFieldValue));
// Note that we must create a heap-allocated block, so we
// start off by creating a stack-allocated block, and then
// call _Block_copy, which will create a heap-allocated block
// with the proper reference count.
using var block = new BlockLiteral ();
if (signature is null) {
if (Runtime.DynamicRegistrationSupported) {
block.SetupBlock (delegateProxyFieldValue, @delegate);
} else {
throw ErrorHelper.CreateError (8026, $"BlockLiteral.GetBlockForDelegate with a null signature is not supported when the dynamic registrar has been linked away (delegate type: {@delegate.GetType ().FullName}).");
}
} else {
block.SetupBlockImpl (delegateProxyFieldValue, @delegate, true, signature);
}
return _Block_copy (&block);
}
[BindingImpl (BindingImplOptions.Optimizable)]
#if NET
// This method should never be called when using the managed static registrar, so assert that never happens by throwing an exception in that case.
// This method doesn't necessarily work with NativeAOT, but this is covered by the exception, because the managed static registrar is required for NativeAOT.
//
// IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.NonPublicFields' in call to 'System.Type.GetField(String, BindingFlags)'. The return value of method 'ObjCRuntime.BlockLiteral.GetDelegateProxyType(MethodInfo, UInt32, MethodInfo&)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.
// IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.NonPublicMethods' in call to 'System.Type.GetMethod(String, BindingFlags)'. The return value of method 'ObjCRuntime.BlockLiteral.GetDelegateProxyType(MethodInfo, UInt32, MethodInfo&)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."
[UnconditionalSuppressMessage ("", "IL2075", Justification = "The APIs this method tries to access are marked by other means, so this is linker-safe.")]
#endif
internal static IntPtr GetBlockForDelegate (MethodInfo minfo, object @delegate, uint token_ref, string signature)
{
#if NET
// Note that the code in this method doesn't necessarily work with NativeAOT, so assert that never happens by throwing an exception if using the managed static registrar (which is required for NativeAOT)
if (Runtime.IsManagedStaticRegistrar)
throw new System.Diagnostics.UnreachableException ();
#endif
if (@delegate is null)
return IntPtr.Zero;
if (!(@delegate is Delegate))
throw ErrorHelper.CreateError (8016, $"Unable to convert delegate to block for the return value for the method {minfo.DeclaringType.FullName}.{minfo.Name}, because the input isn't a delegate, it's a {@delegate.GetType ().FullName}. {Constants.PleaseFileBugReport}");
#if NET
if (Runtime.IsNativeAOT)
throw Runtime.CreateNativeAOTNotSupportedException ();
#endif
Type delegateProxyType = GetDelegateProxyType (minfo, token_ref, out var baseMethod);
if (baseMethod is null)
baseMethod = minfo; // 'baseMethod' is only used in error messages, and if it's null, we just use the closest alternative we have (minfo).
if (delegateProxyType is null)
throw ErrorHelper.CreateError (8012, $"Invalid DelegateProxyAttribute for the return value for the method {baseMethod.DeclaringType.FullName}.{baseMethod.Name}: DelegateType is null. {Constants.PleaseFileBugReport}");
#if NET
var delegateInvokeMethod = delegateProxyType.GetMethod ("Invoke", BindingFlags.NonPublic | BindingFlags.Static);
if (delegateInvokeMethod is not null && delegateInvokeMethod.IsDefined (typeof (UnmanagedCallersOnlyAttribute), false))
return GetBlockForFunctionPointer (delegateInvokeMethod, @delegate, signature);
#endif
var delegateProxyField = delegateProxyType.GetField ("Handler", BindingFlags.NonPublic | BindingFlags.Static);
if (delegateProxyField is null)
throw ErrorHelper.CreateError (8013, $"Invalid DelegateProxyAttribute for the return value for the method {baseMethod.DeclaringType.FullName}.{baseMethod.Name}: DelegateType ({delegateProxyType.FullName}) specifies a type without a 'Handler' field. {Constants.PleaseFileBugReport}");
var handlerDelegate = delegateProxyField.GetValue (null);
if (handlerDelegate is null)
throw ErrorHelper.CreateError (8014, $"Invalid DelegateProxyAttribute for the return value for the method {baseMethod.DeclaringType.FullName}.{baseMethod.Name}: The DelegateType's ({delegateProxyType.FullName}) 'Handler' field is null. {Constants.PleaseFileBugReport}");
if (!(handlerDelegate is Delegate))
throw ErrorHelper.CreateError (8015, $"Invalid DelegateProxyAttribute for the return value for the method {baseMethod.DeclaringType.FullName}.{baseMethod.Name}: The DelegateType's ({delegateProxyType.FullName}) 'Handler' field is not a delegate, it's a {handlerDelegate.GetType ().FullName}. {Constants.PleaseFileBugReport}");
// We now have the information we need to create the block.
// Note that we must create a heap-allocated block, so we
// start off by creating a stack-allocated block, and then
// call _Block_copy, which will create a heap-allocated block
// with the proper reference count.
using var block = new BlockLiteral ();
if (signature is null) {
if (Runtime.DynamicRegistrationSupported) {
block.SetupBlock ((Delegate) handlerDelegate, (Delegate) @delegate);
} else {
throw ErrorHelper.CreateError (8026, $"BlockLiteral.GetBlockForDelegate with a null signature is not supported when the dynamic registrar has been linked away (delegate type: {@delegate.GetType ().FullName}).");
}
} else {
block.SetupBlockImpl ((Delegate) handlerDelegate, (Delegate) @delegate, true, signature);
}
unsafe {
return _Block_copy (&block);
}
}
[DllImport (Messaging.LIBOBJC_DYLIB)]
internal static extern IntPtr _Block_copy (BlockLiteral* block);
[DllImport (Messaging.LIBOBJC_DYLIB)]
internal static extern IntPtr _Block_copy (IntPtr block);
[DllImport (Messaging.LIBOBJC_DYLIB)]
internal static extern void _Block_release (IntPtr block);
internal static IntPtr Copy (IntPtr block)
{
return _Block_copy (block);
}
#endif
}
#if !COREBUILD
// This class sole purpose is to keep a static field that is initialized on
// first use of the class
internal class BlockStaticDispatchClass {
#if !NET
internal delegate void dispatch_block_t (IntPtr block);
[MonoPInvokeCallback (typeof (dispatch_block_t))]
#else
[UnmanagedCallersOnly]
#endif
internal static unsafe void TrampolineDispatchBlock (IntPtr block)
{
var del = BlockLiteral.GetTarget<Action> (block);
if (del is not null) {
del ();
}
}
[BindingImpl (BindingImplOptions.Optimizable)]
unsafe internal static BlockLiteral CreateBlock (Action action)
{
#if NET
delegate* unmanaged<IntPtr, void> trampoline = &BlockStaticDispatchClass.TrampolineDispatchBlock;
return new BlockLiteral (trampoline, action, typeof (BlockStaticDispatchClass), nameof (TrampolineDispatchBlock));
#else
var block = new BlockLiteral ();
block.SetupBlockUnsafe (static_dispatch_block, action);
return block;
#endif
}
#if !NET
internal static dispatch_block_t static_dispatch_block = TrampolineDispatchBlock;
#endif
}
// This class will free the specified block when it's collected by the GC.
internal class BlockCollector {
IntPtr block;
int count;
public BlockCollector (IntPtr block)
{
this.block = block;
count = 1;
}
public void Add (IntPtr block)
{
if (block != this.block)
throw new InvalidOperationException (string.Format ("Can't release the block 0x{0} because this BlockCollector instance is already tracking 0x{1}.", block.ToString ("x"), this.block.ToString ("x")));
Interlocked.Increment (ref count);
}
~BlockCollector ()
{
for (var i = 0; i < count; i++)
Runtime.ReleaseBlockOnMainThread (block);
count = 0;
}
}
#endif
[Flags]
internal enum BlockFlags : int {
/// <summary>Objective-C Block ABI Flags.</summary>
BLOCK_REFCOUNT_MASK = (0xffff),
/// <summary>Objective-C Block ABI Flags.</summary>
BLOCK_NEEDS_FREE = (1 << 24),
/// <summary>Objective-C Block ABI Flags</summary>
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
/// <summary>Objective-C Block ABI Flags</summary>
BLOCK_HAS_CTOR = (1 << 26), /* Helpers have C++ code. */
/// <summary>Objective-C Block ABI Flags.</summary>
BLOCK_IS_GC = (1 << 27),
/// <summary>Objective-C Block ABI Flags.</summary>
BLOCK_IS_GLOBAL = (1 << 28),
/// <summary>Whether the block_descriptor field is filled in.</summary>
BLOCK_HAS_DESCRIPTOR = (1 << 29), // This meaning was deprecated
/// <summary>Objective-C Block ABI Flags.</summary>
BLOCK_HAS_STRET = (1 << 29),
/// <summary>Objective-C Block ABI Flags</summary>
BLOCK_HAS_SIGNATURE = (1 << 30),
}
}