1 /*
2 * runtime.c
3 * libclosure
4 *
5 * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
6 *
7 * @APPLE_LLVM_LICENSE_HEADER@
8 */
9
10
11 #ifndef KERNEL
12
13 #include "Block_private.h"
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <dlfcn.h>
17 #include <os/assumes.h>
18 #include <TargetConditionals.h>
19
20 #else /* !KERNEL */
21 #define TARGET_OS_WIN32 0
22
23 #include <libkern/Block_private.h>
24 __BEGIN_DECLS
25 #include <kern/kalloc.h>
26 __END_DECLS
27
28 static inline void *
malloc(size_t size)29 malloc(size_t size)
30 {
31 if (size == 0) {
32 return NULL;
33 }
34 return kheap_alloc_tag_bt(KHEAP_DEFAULT, size,
35 (zalloc_flags_t) (Z_WAITOK | Z_ZERO), VM_KERN_MEMORY_LIBKERN);
36 }
37
38 static inline void
free(void * addr)39 free(void *addr)
40 {
41 kheap_free_addr(KHEAP_DEFAULT, addr);
42 }
43
44 #endif /* KERNEL */
45
46 #include <machine/atomic.h>
47 #include <string.h>
48 #include <stdint.h>
49 #ifndef os_assumes
50 #define os_assumes(_x) (_x)
51 #endif
52 #ifndef os_assert
53 #define os_assert(_x) assert(_x)
54 #endif
55
56 #if TARGET_OS_WIN32
57 #define _CRT_SECURE_NO_WARNINGS 1
58 #include <windows.h>
59 static __inline bool
OSAtomicCompareAndSwapLong(long oldl,long newl,long volatile * dst)60 OSAtomicCompareAndSwapLong(long oldl, long newl, long volatile *dst)
61 {
62 // fixme barrier is overkill -- see objc-os.h
63 long original = InterlockedCompareExchange(dst, newl, oldl);
64 return original == oldl;
65 }
66
67 static __inline bool
OSAtomicCompareAndSwapInt(int oldi,int newi,int volatile * dst)68 OSAtomicCompareAndSwapInt(int oldi, int newi, int volatile *dst)
69 {
70 // fixme barrier is overkill -- see objc-os.h
71 int original = InterlockedCompareExchange(dst, newi, oldi);
72 return original == oldi;
73 }
74 #else
75 #define OSAtomicCompareAndSwapLong(_Old, _New, _Ptr) os_atomic_cmpxchg(_Ptr, _Old, _New, relaxed)
76 #define OSAtomicCompareAndSwapInt(_Old, _New, _Ptr) os_atomic_cmpxchg(_Ptr, _Old, _New, relaxed)
77 #endif
78
79
80 /*******************************************************************************
81 * Internal Utilities
82 ********************************************************************************/
83
84 static int32_t
latching_incr_int(volatile int32_t * where)85 latching_incr_int(volatile int32_t *where)
86 {
87 while (1) {
88 int32_t old_value = *where;
89 if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
90 return BLOCK_REFCOUNT_MASK;
91 }
92 if (OSAtomicCompareAndSwapInt(old_value, old_value + 2, where)) {
93 return old_value + 2;
94 }
95 }
96 }
97
98 static bool
latching_incr_int_not_deallocating(volatile int32_t * where)99 latching_incr_int_not_deallocating(volatile int32_t *where)
100 {
101 while (1) {
102 int32_t old_value = *where;
103 if (old_value & BLOCK_DEALLOCATING) {
104 // if deallocating we can't do this
105 return false;
106 }
107 if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
108 // if latched, we're leaking this block, and we succeed
109 return true;
110 }
111 if (OSAtomicCompareAndSwapInt(old_value, old_value + 2, where)) {
112 // otherwise, we must store a new retained value without the deallocating bit set
113 return true;
114 }
115 }
116 }
117
118
119 // return should_deallocate?
120 static bool
latching_decr_int_should_deallocate(volatile int32_t * where)121 latching_decr_int_should_deallocate(volatile int32_t *where)
122 {
123 while (1) {
124 int32_t old_value = *where;
125 if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
126 return false; // latched high
127 }
128 if ((old_value & BLOCK_REFCOUNT_MASK) == 0) {
129 return false; // underflow, latch low
130 }
131 int32_t new_value = old_value - 2;
132 bool result = false;
133 if ((old_value & (BLOCK_REFCOUNT_MASK | BLOCK_DEALLOCATING)) == 2) {
134 new_value = old_value - 1;
135 result = true;
136 }
137 if (OSAtomicCompareAndSwapInt(old_value, new_value, where)) {
138 return result;
139 }
140 }
141 }
142
143
144 /**************************************************************************
145 * Framework callback functions and their default implementations.
146 ***************************************************************************/
147 #if !TARGET_OS_WIN32
148 #pragma mark Framework Callback Routines
149 #endif
150 #if KERNEL
151 static inline void
_Block_retain_object(const void * ptr __unused)152 _Block_retain_object(const void *ptr __unused)
153 {
154 }
155
156 static inline void
_Block_release_object(const void * ptr __unused)157 _Block_release_object(const void *ptr __unused)
158 {
159 }
160
161 static inline void
_Block_destructInstance(const void * aBlock __unused)162 _Block_destructInstance(const void *aBlock __unused)
163 {
164 }
165
166 #else
167
168 static void
_Block_retain_object_default(const void * ptr __unused)169 _Block_retain_object_default(const void *ptr __unused)
170 {
171 }
172
173 static void
_Block_release_object_default(const void * ptr __unused)174 _Block_release_object_default(const void *ptr __unused)
175 {
176 }
177
178 static void
_Block_destructInstance_default(const void * aBlock __unused)179 _Block_destructInstance_default(const void *aBlock __unused)
180 {
181 }
182
183 static void (*_Block_retain_object)(const void *ptr) = _Block_retain_object_default;
184 static void (*_Block_release_object)(const void *ptr) = _Block_release_object_default;
185 static void (*_Block_destructInstance) (const void *aBlock) = _Block_destructInstance_default;
186
187
188 /**************************************************************************
189 * Callback registration from ObjC runtime and CoreFoundation
190 ***************************************************************************/
191
192 void
_Block_use_RR2(const Block_callbacks_RR * callbacks)193 _Block_use_RR2(const Block_callbacks_RR *callbacks)
194 {
195 _Block_retain_object = callbacks->retain;
196 _Block_release_object = callbacks->release;
197 _Block_destructInstance = callbacks->destructInstance;
198 }
199 #endif // !KERNEL
200
201 /****************************************************************************
202 * Accessors for block descriptor fields
203 *****************************************************************************/
204
205 template <class T>
206 static T *
unwrap_relative_pointer(int32_t & offset)207 unwrap_relative_pointer(int32_t &offset)
208 {
209 if (offset == 0) {
210 return nullptr;
211 }
212
213 uintptr_t base = (uintptr_t)&offset;
214 uintptr_t extendedOffset = (uintptr_t)(intptr_t)offset;
215 uintptr_t pointer = base + extendedOffset;
216 return (T *)pointer;
217 }
218
219 #if 0
220 static struct Block_descriptor_2 *
221 _Block_descriptor_2(struct Block_layout *aBlock)
222 {
223 uint8_t *desc = (uint8_t *)_Block_get_descriptor(aBlock);
224 desc += sizeof(struct Block_descriptor_1);
225 return __IGNORE_WCASTALIGN((struct Block_descriptor_2 *)desc);
226 }
227 #endif
228
229 static struct Block_descriptor_3 *
_Block_descriptor_3(struct Block_layout * aBlock)230 _Block_descriptor_3(struct Block_layout *aBlock)
231 {
232 uint8_t *desc = (uint8_t *)_Block_get_descriptor(aBlock);
233 desc += sizeof(struct Block_descriptor_1);
234 if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
235 desc += sizeof(struct Block_descriptor_2);
236 }
237 return __IGNORE_WCASTALIGN((struct Block_descriptor_3 *)desc);
238 }
239
240 static void
_Block_call_copy_helper(void * result,struct Block_layout * aBlock)241 _Block_call_copy_helper(void *result, struct Block_layout *aBlock)
242 {
243 if (auto *pFn = _Block_get_copy_function(aBlock)) {
244 pFn(result, aBlock);
245 }
246 }
247
248 static void
_Block_call_dispose_helper(struct Block_layout * aBlock)249 _Block_call_dispose_helper(struct Block_layout *aBlock)
250 {
251 if (auto *pFn = _Block_get_dispose_function(aBlock)) {
252 pFn(aBlock);
253 }
254 }
255
256 /*******************************************************************************
257 * Internal Support routines for copying
258 ********************************************************************************/
259
260 #if !TARGET_OS_WIN32
261 #pragma mark Copy/Release support
262 #endif
263
264 // Copy, or bump refcount, of a block. If really copying, call the copy helper if present.
265 void *
_Block_copy(const void * arg)266 _Block_copy(const void *arg)
267 {
268 struct Block_layout *aBlock;
269
270 if (!arg) {
271 return NULL;
272 }
273
274 // The following would be better done as a switch statement
275 aBlock = (struct Block_layout *)arg;
276 if (aBlock->flags & BLOCK_NEEDS_FREE) {
277 // latches on high
278 latching_incr_int(&aBlock->flags);
279 return aBlock;
280 } else if (aBlock->flags & BLOCK_IS_GLOBAL) {
281 return aBlock;
282 } else {
283 // Its a stack block. Make a copy.
284 size_t size = Block_size(aBlock);
285 struct Block_layout *result = (struct Block_layout *)malloc(size);
286 if (!result) {
287 return NULL;
288 }
289 memmove(result, aBlock, size); // bitcopy first
290 #if __has_feature(ptrauth_calls)
291 // Resign the invoke pointer as it uses address authentication.
292 result->invoke = aBlock->invoke;
293
294 #if __has_feature(ptrauth_signed_block_descriptors)
295 uintptr_t oldDesc =
296 ptrauth_blend_discriminator(
297 &aBlock->descriptor, _Block_descriptor_ptrauth_discriminator);
298 uintptr_t newDesc =
299 ptrauth_blend_discriminator(
300 &result->descriptor, _Block_descriptor_ptrauth_discriminator);
301
302 result->descriptor =
303 ptrauth_auth_and_resign(aBlock->descriptor, ptrauth_key_asda, oldDesc,
304 ptrauth_key_asda, newDesc);
305 #endif
306 #endif
307
308 // reset refcount
309 result->flags &= ~(BLOCK_REFCOUNT_MASK | BLOCK_DEALLOCATING); // XXX not needed
310 result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1
311 _Block_call_copy_helper(result, aBlock);
312 // Set isa last so memory analysis tools see a fully-initialized object.
313 result->isa = _NSConcreteMallocBlock;
314 return result;
315 }
316 }
317
318
319 // Runtime entry points for maintaining the sharing knowledge of byref data blocks.
320
321 // A closure has been copied and its fixup routine is asking us to fix up the reference to the shared byref data
322 // Closures that aren't copied must still work, so everyone always accesses variables after dereferencing the forwarding ptr.
323 // We ask if the byref pointer that we know about has already been copied to the heap, and if so, increment and return it.
324 // Otherwise we need to copy it and update the stack forwarding pointer
325 static struct Block_byref *
_Block_byref_copy(const void * arg)326 _Block_byref_copy(const void *arg)
327 {
328 struct Block_byref *src = (struct Block_byref *)arg;
329
330 if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
331 // src points to stack
332 struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
333 copy->isa = NULL;
334 // byref value 4 is logical refcount of 2: one for caller, one for stack
335 copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
336 copy->forwarding = copy; // patch heap copy to point to itself
337 src->forwarding = copy; // patch stack to point to heap copy
338 copy->size = src->size;
339
340 if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
341 // Trust copy helper to copy everything of interest
342 // If more than one field shows up in a byref block this is wrong XXX
343 struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src + 1);
344 struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy + 1);
345 copy2->byref_keep = src2->byref_keep;
346 copy2->byref_destroy = src2->byref_destroy;
347
348 if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
349 struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2 + 1);
350 struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2 + 1);
351 copy3->layout = src3->layout;
352 }
353
354 (*src2->byref_keep)(copy, src);
355 } else {
356 // Bitwise copy.
357 // This copy includes Block_byref_3, if any.
358 memmove(copy + 1, src + 1, src->size - sizeof(*src));
359 }
360 }
361 // already copied to heap
362 else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
363 latching_incr_int(&src->forwarding->flags);
364 }
365
366 return src->forwarding;
367 }
368
369 static void
_Block_byref_release(const void * arg)370 _Block_byref_release(const void *arg)
371 {
372 struct Block_byref *byref = (struct Block_byref *)arg;
373
374 // dereference the forwarding pointer since the compiler isn't doing this anymore (ever?)
375 byref = byref->forwarding;
376
377 if (byref->flags & BLOCK_BYREF_NEEDS_FREE) {
378 __assert_only int32_t refcount = byref->flags & BLOCK_REFCOUNT_MASK;
379 os_assert(refcount);
380 if (latching_decr_int_should_deallocate(&byref->flags)) {
381 if (byref->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
382 struct Block_byref_2 *byref2 = (struct Block_byref_2 *)(byref + 1);
383 (*byref2->byref_destroy)(byref);
384 }
385 free(byref);
386 }
387 }
388 }
389
390
391 /************************************************************
392 *
393 * API supporting SPI
394 * _Block_copy, _Block_release, and (old) _Block_destroy
395 *
396 ***********************************************************/
397
398 #if !TARGET_OS_WIN32
399 #pragma mark SPI/API
400 #endif
401
402
403 // API entry point to release a copied Block
404 void
_Block_release(const void * arg)405 _Block_release(const void *arg)
406 {
407 struct Block_layout *aBlock = (struct Block_layout *)arg;
408 if (!aBlock) {
409 return;
410 }
411 if (aBlock->flags & BLOCK_IS_GLOBAL) {
412 return;
413 }
414 if (!(aBlock->flags & BLOCK_NEEDS_FREE)) {
415 return;
416 }
417
418 if (latching_decr_int_should_deallocate(&aBlock->flags)) {
419 _Block_call_dispose_helper(aBlock);
420 _Block_destructInstance(aBlock);
421 free(aBlock);
422 }
423 }
424
425 bool
_Block_tryRetain(const void * arg)426 _Block_tryRetain(const void *arg)
427 {
428 struct Block_layout *aBlock = (struct Block_layout *)arg;
429 return latching_incr_int_not_deallocating(&aBlock->flags);
430 }
431
432 bool
_Block_isDeallocating(const void * arg)433 _Block_isDeallocating(const void *arg)
434 {
435 struct Block_layout *aBlock = (struct Block_layout *)arg;
436 return (aBlock->flags & BLOCK_DEALLOCATING) != 0;
437 }
438
439
440 /************************************************************
441 *
442 * SPI used by other layers
443 *
444 ***********************************************************/
445
446 size_t
Block_size(void * aBlock)447 Block_size(void *aBlock)
448 {
449 auto *layout = (Block_layout *)aBlock;
450 void *desc = _Block_get_descriptor(layout);
451 if (layout->flags & BLOCK_SMALL_DESCRIPTOR) {
452 return ((Block_descriptor_small *)desc)->size;
453 }
454 return ((Block_descriptor_1 *)desc)->size;
455 }
456
457 bool
_Block_use_stret(void * aBlock)458 _Block_use_stret(void *aBlock)
459 {
460 struct Block_layout *layout = (struct Block_layout *)aBlock;
461
462 int requiredFlags = BLOCK_HAS_SIGNATURE | BLOCK_USE_STRET;
463 return (layout->flags & requiredFlags) == requiredFlags;
464 }
465
466 // Checks for a valid signature, not merely the BLOCK_HAS_SIGNATURE bit.
467 bool
_Block_has_signature(void * aBlock)468 _Block_has_signature(void *aBlock)
469 {
470 return _Block_signature(aBlock) ? true : false;
471 }
472
473 const char *
_Block_signature(void * aBlock)474 _Block_signature(void *aBlock)
475 {
476 struct Block_layout *layout = (struct Block_layout *)aBlock;
477 if (!(layout->flags & BLOCK_HAS_SIGNATURE)) {
478 return nullptr;
479 }
480
481 if (layout->flags & BLOCK_SMALL_DESCRIPTOR) {
482 auto *bds = (Block_descriptor_small *)_Block_get_descriptor(layout);
483 return unwrap_relative_pointer<const char>(bds->signature);
484 }
485
486 struct Block_descriptor_3 *desc3 = _Block_descriptor_3(layout);
487 return desc3->signature;
488 }
489
490 const char *
_Block_layout(void * aBlock)491 _Block_layout(void *aBlock)
492 {
493 // Don't return extended layout to callers expecting old GC layout
494 Block_layout *layout = (Block_layout *)aBlock;
495 if ((layout->flags & BLOCK_HAS_EXTENDED_LAYOUT) ||
496 !(layout->flags & BLOCK_HAS_SIGNATURE)) {
497 return nullptr;
498 }
499
500 if (layout->flags & BLOCK_SMALL_DESCRIPTOR) {
501 auto *bds = (Block_descriptor_small *)_Block_get_descriptor(layout);
502 return unwrap_relative_pointer<const char>(bds->layout);
503 }
504
505 Block_descriptor_3 *desc = _Block_descriptor_3(layout);
506 return desc->layout;
507 }
508
509 const char *
_Block_extended_layout(void * aBlock)510 _Block_extended_layout(void *aBlock)
511 {
512 // Don't return old GC layout to callers expecting extended layout
513 Block_layout *layout = (Block_layout *)aBlock;
514 if (!(layout->flags & BLOCK_HAS_EXTENDED_LAYOUT) ||
515 !(layout->flags & BLOCK_HAS_SIGNATURE)) {
516 return nullptr;
517 }
518
519 const char *extLayout;
520 if (layout->flags & BLOCK_SMALL_DESCRIPTOR) {
521 auto *bds = (Block_descriptor_small *)_Block_get_descriptor(layout);
522 if (layout->flags & BLOCK_INLINE_LAYOUT_STRING) {
523 extLayout = (const char *)(uintptr_t)bds->layout;
524 } else {
525 extLayout = unwrap_relative_pointer<const char>(bds->layout);
526 }
527 } else {
528 Block_descriptor_3 *desc3 = _Block_descriptor_3(layout);
529 extLayout = desc3->layout;
530 }
531
532 // Return empty string (all non-object bytes) instead of NULL
533 // so callers can distinguish "empty layout" from "no layout".
534 if (!extLayout) {
535 extLayout = "";
536 }
537 return extLayout;
538 }
539
540 #if !TARGET_OS_WIN32
541 #pragma mark Compiler SPI entry points
542 #endif
543
544
545 /*******************************************************
546 *
547 * Entry points used by the compiler - the real API!
548 *
549 *
550 * A Block can reference four different kinds of things that require help when the Block is copied to the heap.
551 * 1) C++ stack based objects
552 * 2) References to Objective-C objects
553 * 3) Other Blocks
554 * 4) __block variables
555 *
556 * In these cases helper functions are synthesized by the compiler for use in Block_copy and Block_release, called the copy and dispose helpers. The copy helper emits a call to the C++ const copy constructor for C++ stack based objects and for the rest calls into the runtime support function _Block_object_assign. The dispose helper has a call to the C++ destructor for case 1 and a call into _Block_object_dispose for the rest.
557 *
558 * The flags parameter of _Block_object_assign and _Block_object_dispose is set to
559 * BLOCK_FIELD_IS_OBJECT (3), for the case of an Objective-C Object,
560 * BLOCK_FIELD_IS_BLOCK (7), for the case of another Block, and
561 * BLOCK_FIELD_IS_BYREF (8), for the case of a __block variable.
562 * If the __block variable is marked weak the compiler also or's in BLOCK_FIELD_IS_WEAK (16)
563 *
564 * So the Block copy/dispose helpers should only ever generate the four flag values of 3, 7, 8, and 24.
565 *
566 * When a __block variable is either a C++ object, an Objective-C object, or another Block then the compiler also generates copy/dispose helper functions. Similarly to the Block copy helper, the "__block" copy helper (formerly and still a.k.a. "byref" copy helper) will do a C++ copy constructor (not a const one though!) and the dispose helper will do the destructor. And similarly the helpers will call into the same two support functions with the same values for objects and Blocks with the additional BLOCK_BYREF_CALLER (128) bit of information supplied.
567 *
568 * So the __block copy/dispose helpers will generate flag values of 3 or 7 for objects and Blocks respectively, with BLOCK_FIELD_IS_WEAK (16) or'ed as appropriate and always 128 or'd in, for the following set of possibilities:
569 * __block id 128+3 (0x83)
570 * __block (^Block) 128+7 (0x87)
571 * __weak __block id 128+3+16 (0x93)
572 * __weak __block (^Block) 128+7+16 (0x97)
573 *
574 *
575 ********************************************************/
576
577 //
578 // When Blocks or Block_byrefs hold objects then their copy routine helpers use this entry point
579 // to do the assignment.
580 //
581 void
_Block_object_assign(void * destArg,const void * object,const int flags)582 _Block_object_assign(void *destArg, const void *object, const int flags)
583 {
584 const void **dest = (const void **)destArg;
585 switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
586 case BLOCK_FIELD_IS_OBJECT:
587 /*******
588 * id object = ...;
589 * [^{ object; } copy];
590 ********/
591
592 _Block_retain_object(object);
593 *dest = object;
594 break;
595
596 case BLOCK_FIELD_IS_BLOCK:
597 /*******
598 * void (^object)(void) = ...;
599 * [^{ object; } copy];
600 ********/
601
602 *dest = _Block_copy(object);
603 break;
604
605 case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
606 case BLOCK_FIELD_IS_BYREF:
607 /*******
608 * // copy the onstack __block container to the heap
609 * // Note this __weak is old GC-weak/MRC-unretained.
610 * // ARC-style __weak is handled by the copy helper directly.
611 * __block ... x;
612 * __weak __block ... x;
613 * [^{ x; } copy];
614 ********/
615
616 *dest = _Block_byref_copy(object);
617 break;
618
619 case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
620 case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
621 /*******
622 * // copy the actual field held in the __block container
623 * // Note this is MRC unretained __block only.
624 * // ARC retained __block is handled by the copy helper directly.
625 * __block id object;
626 * __block void (^object)(void);
627 * [^{ object; } copy];
628 ********/
629
630 *dest = object;
631 break;
632
633 case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
634 case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_WEAK:
635 /*******
636 * // copy the actual field held in the __block container
637 * // Note this __weak is old GC-weak/MRC-unretained.
638 * // ARC-style __weak is handled by the copy helper directly.
639 * __weak __block id object;
640 * __weak __block void (^object)(void);
641 * [^{ object; } copy];
642 ********/
643
644 *dest = object;
645 break;
646
647 default:
648 break;
649 }
650 }
651
652 // When Blocks or Block_byrefs hold objects their destroy helper routines call this entry point
653 // to help dispose of the contents
654 void
_Block_object_dispose(const void * object,const int flags)655 _Block_object_dispose(const void *object, const int flags)
656 {
657 switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
658 case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
659 case BLOCK_FIELD_IS_BYREF:
660 // get rid of the __block data structure held in a Block
661 _Block_byref_release(object);
662 break;
663 case BLOCK_FIELD_IS_BLOCK:
664 _Block_release(object);
665 break;
666 case BLOCK_FIELD_IS_OBJECT:
667 _Block_release_object(object);
668 break;
669 case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
670 case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
671 case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
672 case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_WEAK:
673 break;
674 default:
675 break;
676 }
677 }
678
679
680 // Workaround for <rdar://26015603> dylib with no __DATA segment fails to rebase
681 __attribute__((used))
682 static int let_there_be_data = 42;
683