block-blocks-test-8.c   [plain text]


/* APPLE LOCAL file radar 5932809 */
/* Test the __byreg runtime features. */
/* { dg-options "-mmacosx-version-min=10.5 -ObjC -framework Foundation" { target i?86-*-darwin*  } } */
/* { dg-do run { target i?86-*-darwin* } } */
/* { dg-require-effective-target ilp32 } */

#import <Foundation/Foundation.h>

void * _NSConcreteStackBlock;

int CalledRetain = 0;

id objc_msgSend(id target, SEL cmd) {
    printf("[%p %s] called\n", target, sel_getName(cmd));
    if (!strcmp(sel_getName(cmd), "retain")) {
        CalledRetain = 1;
    }
    return target;
}

@interface DumbObject {
@public
    id isa;
}
@end
id newDumbObject() {
    DumbObject *result = (DumbObject *)calloc(sizeof(DumbObject), 1);
    result->isa = objc_getClass("DumbObject");
    return result;
}

@implementation DumbObject
+ new {
    return newDumbObject();
}
+ (void) initialize { }
- forward:(SEL)msg :(long *)params {
    printf("forward:: with %s called\n", sel_getName(msg));
    return self;
}
@end

//#include <Block_private.h>

enum {
    BLOCK_NEEDS_FREE =        (1 << 24),
    BLOCK_HAS_COPY_DISPOSE =  (1 << 25),
    BLOCK_NO_COPY =           (1 << 26), // interim byref: no copies allowed
    BLOCK_IS_GC =             (1 << 27),
};

/* APPLE LOCAL begin radar 5847213 - radar 6329245 */
struct Block_basic {
    void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
    int Block_flags;
    int reserved;
    void (*Block_invoke)(void *);

    struct Block_descriptor_1 {
        unsigned long int reserved;     // NULL
        unsigned long int Block_size;  // sizeof(struct Block_literal_1)

        // optional helper functions
        void (*Block_copy)(void *dst, void *src);
        void (*Block_dispose)(void *src);
    } *descriptor;

    // imported variables
};
/* APPLE LOCAL end radar 5847213 - radar 6329245 */

struct Block_byref {
    void* isa;
    struct Block_byref *forwarding;
    int flags;//refcount;
    int size;
    void (*byref_keep)(struct Block_byref *dst, struct Block_byref *src);
    void (*byref_destroy)(struct Block_byref *);
    // long shared[0];
};


int ByrefAssignCopy = 0;

void _Block_byref_assign_copy(void *destp, void *source) {
    struct Block_byref *src = (struct Block_byref *)source;
    ByrefAssignCopy = 1;
    // lets look at the Block_byref
    if ((src->flags & BLOCK_HAS_COPY_DISPOSE) == 0) {
        printf("byref data block does not have bit saying that copy/dispose helper routines are present\n");
        return;
    }
    printf("calling out to byref copy helper at %p\n", src->byref_keep);
    int junk[100];
    src->byref_keep((struct Block_byref *)junk, src);
}

void _Block_byref_release(void *source) {
    struct Block_byref *src = source;
    src->byref_destroy(src);
}

int main(int argc, char *argv[]) {
    id __block dumbo = newDumbObject(); //[DumbObject new];
    void (^dummy)(void) = ^{ 
        [dumbo self];
    };

    struct Block_basic *aBlock = (struct Block_basic *)(void *)dummy;
    if ((aBlock->Block_flags & BLOCK_HAS_COPY_DISPOSE) == 0) {
        printf("Block should have a NON_POD copy/destroy helpers and flags to say so, but doesn't\n");
        return 1;
    }

    char result[200];
    printf("calling out to copy support helper at %p\n", aBlock->descriptor->Block_copy);
    (*aBlock->descriptor->Block_copy)(result, aBlock); // do fixup

    // The copy/destroy helper should have had a callout  to _Block_byref_assign_copy for its byref block
    if (! ByrefAssignCopy) {
        printf("_Block_copy_assign not called\n");
        return 1;
    }

    // the copy/destroy helper of the byref should have done dst->object = [src->object retain]
    if (! CalledRetain) {
        printf("byref block support helper did not call retain\n");
        return 1;
    }

    (*aBlock->descriptor->Block_dispose)(aBlock);

    return 0;
}