|
1 | 1 | #import "TNSReturnsRetained.h" |
2 | 2 |
|
3 | | -id functionReturnsNSRetained() { |
4 | | - return [[NSObject alloc] init]; |
5 | | -} |
6 | | -id functionReturnsCFRetained() { |
7 | | - return [[NSObject alloc] init]; |
8 | | -} |
9 | | -CFTypeRef functionImplicitCreate() { |
10 | | - return [[NSObject alloc] init]; |
11 | | -} |
12 | | -id functionExplicitCreateNSObject() { |
13 | | - return [[NSObject alloc] init]; |
14 | | -} |
| 3 | +// Identity passthrough: hides the stack-block-ness from the compiler's |
| 4 | +// return-stack-address diagnostic without copying the block to the heap. |
| 5 | +static TNSIntBlock TNSPassthroughIntBlock(TNSIntBlock block) { return block; } |
| 6 | + |
| 7 | +id functionReturnsNSRetained() { return [[NSObject alloc] init]; } |
| 8 | +id functionReturnsCFRetained() { return [[NSObject alloc] init]; } |
| 9 | +CFTypeRef functionImplicitCreate() { return [[NSObject alloc] init]; } |
| 10 | +id functionExplicitCreateNSObject() { return [[NSObject alloc] init]; } |
15 | 11 |
|
16 | 12 | @implementation TNSReturnsRetained |
17 | 13 | + (id)methodReturnsNSRetained { |
18 | | - return [[NSObject alloc] init]; |
| 14 | + return [[NSObject alloc] init]; |
19 | 15 | } |
20 | 16 | + (id)methodReturnsCFRetained { |
21 | | - return [[NSObject alloc] init]; |
| 17 | + return [[NSObject alloc] init]; |
22 | 18 | } |
23 | 19 | + (id)newNSObjectMethod { |
24 | | - return [[TNSReturnsRetained alloc] init]; |
| 20 | + return [[TNSReturnsRetained alloc] init]; |
| 21 | +} |
| 22 | ++ (TNSIntBlock)blockCapturing:(int)value { |
| 23 | + // This file is compiled with -fno-objc-arc. Capturing a non-constant value |
| 24 | + // (the parameter) forces a __NSStackBlock__ - capturing only a compile-time |
| 25 | + // constant would let clang promote it to a global block, which CFRetain |
| 26 | + // handles fine and would not reproduce the bug. The block is routed through |
| 27 | + // TNSPassthroughIntBlock so the compiler's "returning a stack block" check |
| 28 | + // can't see through the call boundary; it is still a +0 stack block living in |
| 29 | + // this frame at runtime. A correct runtime Block_copy's it to take ownership |
| 30 | + // (heap-promoting it). CFRetain does not promote a stack block, so the wrapper |
| 31 | + // is left pointing at this (about to be dead) stack frame. |
| 32 | + return TNSPassthroughIntBlock(^{ |
| 33 | + return value; |
| 34 | + }); |
25 | 35 | } |
26 | 36 | @end |
0 commit comments