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