blocksAsImps.m   [plain text]


// TEST_CFLAGS -framework Foundation

#include "test.h"
#include <objc/runtime.h>
#import <Foundation/Foundation.h>

#include <Block_private.h>

#if !__clang__  &&  !__llvm__
    // gcc will never support struct-return marking
#   define STRET_OK 0
#elif !clang
// llvm-gcc waiting for rdar://8143947
#   define STRET_OK 0
#else
#   define STRET_OK 1
#endif

typedef uint32_t (*funcptr)();

typedef struct BigStruct {
    unsigned int 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

typedef uint32_t (*FuncPtr)(id, 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 {
    _objc_flush_caches([Deallocator class]);
    [self methodThatNobodyElseCalls1];
    return [super retain];
}
-(void) dealloc {
    _objc_flush_caches([Deallocator class]);
    [self methodThatNobodyElseCalls2];
    [super dealloc];
}
@end

/* Code copied from objc-block-trampolines.m to test Block innards */
typedef enum {
    ReturnValueInRegisterArgumentMode,
    ReturnValueOnStackArgumentMode,
    
    ArgumentModeMax
} ArgumentMode;

static ArgumentMode _argumentModeForBlock(void *block) {
    ArgumentMode aMode = ReturnValueInRegisterArgumentMode;
    if ( _Block_use_stret(block) )
        aMode = ReturnValueOnStackArgumentMode;
    
    return aMode;
}
/* End copied code */

int main () {
#if __llvm__  &&  !__clang__
    // edit STRET_OK above when you remove this
    testwarn("<rdar://8143947> struct-return blocks not yet integrated in llvm-gcc");
#endif

    // make sure the bits are in place
    int (^registerReturn)() = ^(){ return 42; };
    ArgumentMode aMode;
    
    aMode = _argumentModeForBlock(registerReturn);
    testassert(aMode == ReturnValueInRegisterArgumentMode);

#if STRET_OK
    BigStruct (^stackReturn)() = ^() { BigStruct k; return k; };
    aMode = _argumentModeForBlock(stackReturn);
    testassert(aMode == ReturnValueOnStackArgumentMode);
#endif
        
#define TEST_QUANTITY 100000
    static FuncPtr funcArray[TEST_QUANTITY];

    uint32_t i;
    for(i = 0; i<TEST_QUANTITY; i++) {
        uint32_t (^block)(id self) = ^uint32_t(id self) {
            testassert((vm_address_t) self == (vm_address_t) i);
            return i;
        };
        block = Block_copy(block);
        
        funcArray[i] =  (FuncPtr) imp_implementationWithBlock(block);
        
        testassert(block((id)(uintptr_t) i) == i);
        
        void *blockFromIMPResult = imp_getBlock((IMP)funcArray[i]);
        testassert(blockFromIMPResult == block);
        
        Block_release(block);
    }
    
    for(i = 0; i<TEST_QUANTITY; i++) {
        uint32_t result = funcArray[i]((id)(uintptr_t) i, 0);
        testassert(i == result);
    }
    
    for(i = 0; i < TEST_QUANTITY; i= i + 3) {
	imp_removeBlock((IMP)funcArray[i]);
	void *shouldBeNull = imp_getBlock((IMP)funcArray[i]);
	assert(shouldBeNull == NULL);
    }
    
    for(i = 0; i < TEST_QUANTITY; i= i + 3) {
        uint32_t j = i * i;
        
        uint32_t (^block)(id self) = ^uint32_t(id self) {
            uint32_t value = (uint32_t)(uintptr_t) self;
            testassert(j == value);
            return j;
        };
        funcArray[i] =  (FuncPtr) imp_implementationWithBlock(block);
        
        testassert(block((id)(uintptr_t)j) == j);
        testassert(funcArray[i]((id)(uintptr_t)j, 0) == j);
    }
    
    for(i = 0; i < TEST_QUANTITY; i= i + 3) {
        uint32_t j = i * i;
        uint32_t result = funcArray[i]((id)(uintptr_t) j, 0);
        testassert(j == result);
    }
    
    int (^implBlock)(id, int);
    
    implBlock = ^(id self __attribute__((unused)), int a){
        return -1 * a;
    };
    
    NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
    
    IMP methodImp = imp_implementationWithBlock(implBlock);
    
    BOOL success = class_addMethod([Foo class], @selector(boo:), methodImp, "i@:i");
    if (!success) {
        fprintf(stdout, "class_addMethod failed\n");
        abort();
    }
    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);

#if STRET_OK
    BigStruct a;
    for(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);
// STRET_OK
#endif
    

    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) );


    // 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);

    [p drain];
    succeed(__FILE__);
}