blocksAsImps.m 7.8 KB

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