]> git.saurik.com Git - apple/objc4.git/blob - test/blocksAsImps.m
objc4-756.2.tar.gz
[apple/objc4.git] / test / blocksAsImps.m
1 // TEST_CFLAGS -framework Foundation
2
3 #include "test.h"
4 #include <objc/runtime.h>
5 #import <Foundation/Foundation.h>
6
7 #include <Block_private.h>
8
9 #if !__has_feature(objc_arc)
10 # define __bridge
11 #endif
12
13 #if __arm64__
14 // stret supported, but is identical to non-stret
15 # define STRET_SPECIAL 0
16 #else
17 // stret supported and distinct from non-stret
18 # define STRET_SPECIAL 1
19 #endif
20
21 typedef struct BigStruct {
22 uintptr_t datums[200];
23 } BigStruct;
24
25 @interface Foo:NSObject
26 @end
27 @implementation Foo
28 - (BigStruct) methodThatReturnsBigStruct: (BigStruct) b
29 {
30 return b;
31 }
32 @end
33
34 @interface Foo(bar)
35 - (int) boo: (int) a;
36 - (BigStruct) structThatIsBig: (BigStruct) b;
37 - (BigStruct) methodThatReturnsBigStruct: (BigStruct) b;
38 - (float) methodThatReturnsFloat: (float) aFloat;
39 @end
40
41 // This is void* instead of id to prevent interference from ARC.
42 typedef uintptr_t (*FuncPtr)(void *, SEL);
43 typedef BigStruct (*BigStructFuncPtr)(id, SEL, BigStruct);
44 typedef float (*FloatFuncPtr)(id, SEL, float);
45
46 BigStruct bigfunc(BigStruct a) {
47 return a;
48 }
49
50 @interface Deallocator : NSObject @end
51 @implementation Deallocator
52 -(void) methodThatNobodyElseCalls1 { }
53 -(void) methodThatNobodyElseCalls2 { }
54 id retain_imp(Deallocator *self, SEL _cmd) {
55 _objc_flush_caches([Deallocator class]);
56 [self methodThatNobodyElseCalls1];
57 struct objc_super sup = { self, [[Deallocator class] superclass] };
58 return ((id(*)(struct objc_super *, SEL))objc_msgSendSuper)(&sup, _cmd);
59 }
60 void dealloc_imp(Deallocator *self, SEL _cmd) {
61 _objc_flush_caches([Deallocator class]);
62 [self methodThatNobodyElseCalls2];
63 struct objc_super sup = { self, [[Deallocator class] superclass] };
64 ((void(*)(struct objc_super *, SEL))objc_msgSendSuper)(&sup, _cmd);
65 }
66 +(void) load {
67 class_addMethod(self, sel_registerName("retain"), (IMP)retain_imp, "");
68 class_addMethod(self, sel_registerName("dealloc"), (IMP)dealloc_imp, "");
69 }
70 @end
71
72 /* Code copied from objc-block-trampolines.m to test Block innards */
73 typedef enum {
74 ReturnValueInRegisterArgumentMode,
75 #if STRET_SPECIAL
76 ReturnValueOnStackArgumentMode,
77 #endif
78
79 ArgumentModeMax
80 } ArgumentMode;
81
82 static ArgumentMode _argumentModeForBlock(id block) {
83 ArgumentMode aMode = ReturnValueInRegisterArgumentMode;
84 #if STRET_SPECIAL
85 if ( _Block_use_stret((__bridge void *)block) )
86 aMode = ReturnValueOnStackArgumentMode;
87 #else
88 testassert(!_Block_use_stret((__bridge void *)block));
89 #endif
90
91 return aMode;
92 }
93 /* End copied code */
94
95 int main () {
96 // make sure the bits are in place
97 int (^registerReturn)() = ^(){ return 42; };
98 ArgumentMode aMode;
99
100 aMode = _argumentModeForBlock(registerReturn);
101 testassert(aMode == ReturnValueInRegisterArgumentMode);
102
103 BigStruct (^stackReturn)() = ^() { BigStruct k = {{0}}; return k; };
104 aMode = _argumentModeForBlock(stackReturn);
105 #if STRET_SPECIAL
106 testassert(aMode == ReturnValueOnStackArgumentMode);
107 #else
108 testassert(aMode == ReturnValueInRegisterArgumentMode);
109 #endif
110
111 uintptr_t TEST_QUANTITY = is_guardmalloc() ? 1000 : 100000;
112 FuncPtr funcArray[TEST_QUANTITY];
113
114 for(uintptr_t i = 0; i < TEST_QUANTITY; i++) {
115 uintptr_t (^block)(void *self) = ^uintptr_t(void *self) {
116 testassert(i == (uintptr_t)self);
117 return i;
118 };
119 block = (__bridge id)_Block_copy((__bridge void *)block);
120
121 funcArray[i] = (FuncPtr) imp_implementationWithBlock(block);
122
123 testassert(block((void *)i) == i);
124
125 id blockFromIMPResult = imp_getBlock((IMP)funcArray[i]);
126 testassert(blockFromIMPResult == (id)block);
127
128 _Block_release((__bridge void *)block);
129 }
130
131 for(uintptr_t i = 0; i < TEST_QUANTITY; i++) {
132 uintptr_t result = funcArray[i]((void *)i, 0);
133 testassert(i == result);
134 }
135
136 for(uintptr_t i = 0; i < TEST_QUANTITY; i = i + 3) {
137 imp_removeBlock((IMP)funcArray[i]);
138 id shouldBeNull = imp_getBlock((IMP)funcArray[i]);
139 testassert(shouldBeNull == NULL);
140 }
141
142 for(uintptr_t i = 0; i < TEST_QUANTITY; i = i + 3) {
143 uintptr_t j = i * i;
144
145 uintptr_t (^block)(void *) = ^uintptr_t(void *self) {
146 testassert(j == (uintptr_t)self);
147 return j;
148 };
149 funcArray[i] = (FuncPtr) imp_implementationWithBlock(block);
150
151 testassert(block((void *)j) == j);
152 testassert(funcArray[i]((void *)j, 0) == j);
153 }
154
155 for(uintptr_t i = 0; i < TEST_QUANTITY; i = i + 3) {
156 uintptr_t j = i * i;
157 uintptr_t result = funcArray[i]((void *)j, 0);
158 testassert(j == result);
159 }
160
161 int (^implBlock)(id, int);
162
163 implBlock = ^(id self __attribute__((unused)), int a){
164 return -1 * a;
165 };
166
167 PUSH_POOL {
168
169 IMP methodImp = imp_implementationWithBlock(implBlock);
170
171 BOOL success = class_addMethod([Foo class], @selector(boo:), methodImp, "i@:i");
172 testassert(success);
173
174 Foo *f = [Foo new];
175 int (*impF)(id self, SEL _cmd, int x) = (int(*)(id, SEL, int)) [Foo instanceMethodForSelector: @selector(boo:)];
176
177 int x = impF(f, @selector(boo:), -42);
178
179 testassert(x == 42);
180 testassert([f boo: -42] == 42);
181
182 BigStruct a;
183 for (uintptr_t i = 0; i < 200; i++) {
184 a.datums[i] = i;
185 }
186
187 // slightly more straightforward here
188 __block unsigned int state = 0;
189 BigStruct (^structBlock)(id, BigStruct) = ^BigStruct(id self __attribute__((unused)), BigStruct c) {
190 state++;
191 return c;
192 };
193 BigStruct blockDirect = structBlock(nil, a);
194 testassert(!memcmp(&a, &blockDirect, sizeof(BigStruct)));
195 testassert(state==1);
196
197 IMP bigStructIMP = imp_implementationWithBlock(structBlock);
198
199 class_addMethod([Foo class], @selector(structThatIsBig:), bigStructIMP, "oh, type strings, how I hate thee. Fortunately, the runtime doesn't generally care.");
200
201 BigStruct b;
202
203 BigStructFuncPtr bFunc;
204
205 b = bigfunc(a);
206 testassert(!memcmp(&a, &b, sizeof(BigStruct)));
207 b = bigfunc(a);
208 testassert(!memcmp(&a, &b, sizeof(BigStruct)));
209
210 bFunc = (BigStructFuncPtr) [Foo instanceMethodForSelector: @selector(methodThatReturnsBigStruct:)];
211
212 b = bFunc(f, @selector(methodThatReturnsBigStruct:), a);
213 testassert(!memcmp(&a, &b, sizeof(BigStruct)));
214
215 b = [f methodThatReturnsBigStruct: a];
216 testassert(!memcmp(&a, &b, sizeof(BigStruct)));
217
218 bFunc = (BigStructFuncPtr) [Foo instanceMethodForSelector: @selector(structThatIsBig:)];
219
220 b = bFunc(f, @selector(structThatIsBig:), a);
221 testassert(!memcmp(&a, &b, sizeof(BigStruct)));
222 testassert(state==2);
223
224 b = [f structThatIsBig: a];
225 testassert(!memcmp(&a, &b, sizeof(BigStruct)));
226 testassert(state==3);
227
228
229 IMP floatIMP = imp_implementationWithBlock(^float (id self __attribute__((unused)), float aFloat ) {
230 return aFloat;
231 });
232 class_addMethod([Foo class], @selector(methodThatReturnsFloat:), floatIMP, "ooh.. type string unspecified again... oh noe... runtime might punish. not.");
233
234 float e = (float)0.001;
235 float retF = (float)[f methodThatReturnsFloat: 37.1212f];
236 testassert( ((retF - e) < 37.1212) && ((retF + e) > 37.1212) );
237
238
239 #if !__has_feature(objc_arc)
240 // Make sure imp_implementationWithBlock() and imp_removeBlock()
241 // don't deadlock while calling Block_copy() and Block_release()
242 Deallocator *dead = [[Deallocator alloc] init];
243 IMP deadlockImp = imp_implementationWithBlock(^{ [dead self]; });
244 [dead release];
245 imp_removeBlock(deadlockImp);
246 #endif
247
248 } POP_POOL;
249
250 succeed(__FILE__);
251 }
252