123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- // TEST_CFLAGS -framework Foundation
- #include "test.h"
- #include <objc/runtime.h>
- #import <Foundation/Foundation.h>
- #include <Block_private.h>
- #if !__has_feature(objc_arc)
- # define __bridge
- #endif
- #if __arm64__
- // stret supported, but is identical to non-stret
- # define STRET_SPECIAL 0
- #else
- // stret supported and distinct from non-stret
- # define STRET_SPECIAL 1
- #endif
- typedef struct BigStruct {
- uintptr_t datums[200];
- } BigStruct;
- @interface Foo:NSObject
- @end
- @implementation Foo
- - (BigStruct) methodThatReturnsBigStruct: (BigStruct) b
- {
- return b;
- }
- @end
- @interface Foo(bar)
- - (int) boo: (int) a;
- - (BigStruct) structThatIsBig: (BigStruct) b;
- - (BigStruct) methodThatReturnsBigStruct: (BigStruct) b;
- - (float) methodThatReturnsFloat: (float) aFloat;
- @end
- // This is void* instead of id to prevent interference from ARC.
- typedef uintptr_t (*FuncPtr)(void *, SEL);
- typedef BigStruct (*BigStructFuncPtr)(id, SEL, BigStruct);
- typedef float (*FloatFuncPtr)(id, SEL, float);
- BigStruct bigfunc(BigStruct a) {
- return a;
- }
- @interface Deallocator : NSObject @end
- @implementation Deallocator
- -(void) methodThatNobodyElseCalls1 { }
- -(void) methodThatNobodyElseCalls2 { }
- id retain_imp(Deallocator *self, SEL _cmd) {
- _objc_flush_caches([Deallocator class]);
- [self methodThatNobodyElseCalls1];
- struct objc_super sup = { self, [[Deallocator class] superclass] };
- return ((id(*)(struct objc_super *, SEL))objc_msgSendSuper)(&sup, _cmd);
- }
- void dealloc_imp(Deallocator *self, SEL _cmd) {
- _objc_flush_caches([Deallocator class]);
- [self methodThatNobodyElseCalls2];
- struct objc_super sup = { self, [[Deallocator class] superclass] };
- ((void(*)(struct objc_super *, SEL))objc_msgSendSuper)(&sup, _cmd);
- }
- +(void) load {
- class_addMethod(self, sel_registerName("retain"), (IMP)retain_imp, "");
- class_addMethod(self, sel_registerName("dealloc"), (IMP)dealloc_imp, "");
- }
- @end
- /* Code copied from objc-block-trampolines.m to test Block innards */
- typedef enum {
- ReturnValueInRegisterArgumentMode,
- #if STRET_SPECIAL
- ReturnValueOnStackArgumentMode,
- #endif
-
- ArgumentModeMax
- } ArgumentMode;
- static ArgumentMode _argumentModeForBlock(id block) {
- ArgumentMode aMode = ReturnValueInRegisterArgumentMode;
- #if STRET_SPECIAL
- if ( _Block_use_stret((__bridge void *)block) )
- aMode = ReturnValueOnStackArgumentMode;
- #else
- testassert(!_Block_use_stret((__bridge void *)block));
- #endif
-
- return aMode;
- }
- /* End copied code */
- int main () {
- // make sure the bits are in place
- int (^registerReturn)() = ^(){ return 42; };
- ArgumentMode aMode;
-
- aMode = _argumentModeForBlock(registerReturn);
- testassert(aMode == ReturnValueInRegisterArgumentMode);
- BigStruct (^stackReturn)() = ^() { BigStruct k = {{0}}; return k; };
- aMode = _argumentModeForBlock(stackReturn);
- #if STRET_SPECIAL
- testassert(aMode == ReturnValueOnStackArgumentMode);
- #else
- testassert(aMode == ReturnValueInRegisterArgumentMode);
- #endif
- uintptr_t TEST_QUANTITY = is_guardmalloc() ? 1000 : 100000;
- FuncPtr funcArray[TEST_QUANTITY];
- for(uintptr_t i = 0; i < TEST_QUANTITY; i++) {
- uintptr_t (^block)(void *self) = ^uintptr_t(void *self) {
- testassert(i == (uintptr_t)self);
- return i;
- };
- block = (__bridge id)_Block_copy((__bridge void *)block);
-
- funcArray[i] = (FuncPtr) imp_implementationWithBlock(block);
-
- testassert(block((void *)i) == i);
-
- id blockFromIMPResult = imp_getBlock((IMP)funcArray[i]);
- testassert(blockFromIMPResult == (id)block);
-
- _Block_release((__bridge void *)block);
- }
-
- for(uintptr_t i = 0; i < TEST_QUANTITY; i++) {
- uintptr_t result = funcArray[i]((void *)i, 0);
- testassert(i == result);
- }
-
- for(uintptr_t i = 0; i < TEST_QUANTITY; i = i + 3) {
- imp_removeBlock((IMP)funcArray[i]);
- id shouldBeNull = imp_getBlock((IMP)funcArray[i]);
- testassert(shouldBeNull == NULL);
- }
-
- for(uintptr_t i = 0; i < TEST_QUANTITY; i = i + 3) {
- uintptr_t j = i * i;
-
- uintptr_t (^block)(void *) = ^uintptr_t(void *self) {
- testassert(j == (uintptr_t)self);
- return j;
- };
- funcArray[i] = (FuncPtr) imp_implementationWithBlock(block);
-
- testassert(block((void *)j) == j);
- testassert(funcArray[i]((void *)j, 0) == j);
- }
-
- for(uintptr_t i = 0; i < TEST_QUANTITY; i = i + 3) {
- uintptr_t j = i * i;
- uintptr_t result = funcArray[i]((void *)j, 0);
- testassert(j == result);
- }
-
- int (^implBlock)(id, int);
-
- implBlock = ^(id self __attribute__((unused)), int a){
- return -1 * a;
- };
-
- PUSH_POOL {
-
- IMP methodImp = imp_implementationWithBlock(implBlock);
-
- BOOL success = class_addMethod([Foo class], @selector(boo:), methodImp, "i@:i");
- testassert(success);
- Foo *f = [Foo new];
- int (*impF)(id self, SEL _cmd, int x) = (int(*)(id, SEL, int)) [Foo instanceMethodForSelector: @selector(boo:)];
-
- int x = impF(f, @selector(boo:), -42);
-
- testassert(x == 42);
- testassert([f boo: -42] == 42);
-
- BigStruct a;
- for (uintptr_t i = 0; i < 200; i++) {
- a.datums[i] = i;
- }
-
- // slightly more straightforward here
- __block unsigned int state = 0;
- BigStruct (^structBlock)(id, BigStruct) = ^BigStruct(id self __attribute__((unused)), BigStruct c) {
- state++;
- return c;
- };
- BigStruct blockDirect = structBlock(nil, a);
- testassert(!memcmp(&a, &blockDirect, sizeof(BigStruct)));
- testassert(state==1);
-
- IMP bigStructIMP = imp_implementationWithBlock(structBlock);
-
- class_addMethod([Foo class], @selector(structThatIsBig:), bigStructIMP, "oh, type strings, how I hate thee. Fortunately, the runtime doesn't generally care.");
-
- BigStruct b;
-
- BigStructFuncPtr bFunc;
-
- b = bigfunc(a);
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
- b = bigfunc(a);
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
-
- bFunc = (BigStructFuncPtr) [Foo instanceMethodForSelector: @selector(methodThatReturnsBigStruct:)];
-
- b = bFunc(f, @selector(methodThatReturnsBigStruct:), a);
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
-
- b = [f methodThatReturnsBigStruct: a];
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
-
- bFunc = (BigStructFuncPtr) [Foo instanceMethodForSelector: @selector(structThatIsBig:)];
-
- b = bFunc(f, @selector(structThatIsBig:), a);
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
- testassert(state==2);
-
- b = [f structThatIsBig: a];
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
- testassert(state==3);
-
-
- IMP floatIMP = imp_implementationWithBlock(^float (id self __attribute__((unused)), float aFloat ) {
- return aFloat;
- });
- class_addMethod([Foo class], @selector(methodThatReturnsFloat:), floatIMP, "ooh.. type string unspecified again... oh noe... runtime might punish. not.");
-
- float e = (float)0.001;
- float retF = (float)[f methodThatReturnsFloat: 37.1212f];
- testassert( ((retF - e) < 37.1212) && ((retF + e) > 37.1212) );
-
- #if !__has_feature(objc_arc)
- // Make sure imp_implementationWithBlock() and imp_removeBlock()
- // don't deadlock while calling Block_copy() and Block_release()
- Deallocator *dead = [[Deallocator alloc] init];
- IMP deadlockImp = imp_implementationWithBlock(^{ [dead self]; });
- [dead release];
- imp_removeBlock(deadlockImp);
- #endif
- } POP_POOL;
- succeed(__FILE__);
- }
|