1/*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LLVM_LICENSE_HEADER@
5 */
6
7//
8//  objc.m
9//  libclosure
10//
11//  Created by Blaine Garst on 3/10/08.
12//  Copyright 2008 __MyCompanyName__. All rights reserved.
13//
14
15#import "driver.h"
16#import <Foundation/Foundation.h>
17//#import <Foundation/NSBlock.h>
18
19@interface TestObject : NSObject {
20@public
21    int refcount;
22    id aSlot;
23}
24- (int) testVerbosely:(int)verbose;
25- (void)doesSomethingWithClosure:(void (^)(void))aClosure;
26- (void)doesSomethingElseWithClosure:(void (^)(NSObject *))aClosure;
27
28- (void)updateIvar;
29
30@end
31
32@implementation TestObject
33- (void)doesSomethingWithClosure:(void (^)(void))aClosure { }
34- (void)doesSomethingElseWithClosure:(void (^)(NSObject *))aClosure { }
35
36- (id) retain {
37    ++refcount;
38    return self;
39}
40
41- (int)retainCounter {
42    return refcount + 1;
43}
44
45- (void) release {
46   if (refcount == 0) [self dealloc];
47   else --refcount;
48}
49
50
51- (int) testVerbosely:(int)verbose
52 {
53    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
54    int errors = 0;
55    aSlot = [[NSObject alloc] init];
56
57    int initialRetainCounter = [self retainCounter];
58
59    void (^myClosure)(void) = ^{
60        printf("[aSlot retainCount] == %d\n", [aSlot retainCount]);
61    };
62
63
64    int afterClosureRetainCounter = [self retainCounter];
65
66    //printf("%s", _Block_dump(myClosure));
67
68    void (^myClosureCopy)(void) = Block_copy(myClosure);
69
70    int afterClosureCopyRetainCounter = [self retainCounter];
71
72    if (afterClosureRetainCounter > initialRetainCounter) {
73        printf("testVerbosely: after closure, retain count is %d vs before %d\n", afterClosureRetainCounter, initialRetainCounter);
74        ++errors;
75    }
76
77    if (afterClosureCopyRetainCounter <= afterClosureRetainCounter) {
78        printf("testVerbosely: closure copy did not retain interior object\n");
79        ++errors;
80    }
81
82    [aSlot release];
83    aSlot = nil;
84
85    if (errors == 0 && verbose) printf("testVerbosely: objc import object test success\n");
86    [pool drain];
87    return errors;
88
89}
90
91- (void)updateIvar {
92    void (^myClosure)(void) = ^{ id tmp = aSlot; aSlot = nil; aSlot = tmp; };
93    //void (^myClosure2)(void) = ^{ |aSlot| id tmp = aSlot; aSlot = nil; aSlot = tmp; };
94}
95
96@end
97
98// can a Block update an ivar
99int test_objc3(int verbose) {
100    int errors = 0;
101    TestObject *to = [[TestObject alloc] init];
102    return 0;
103}
104
105int test_objc2(int verbose) {
106    int errors = 0;
107    TestObject *to = [[TestObject alloc] init];
108
109    errors += [to testVerbosely:verbose];
110    [to release];
111    return errors;
112}
113
114// byref object
115int test_objc1_1(int verbose) {
116    int errors = 0;
117#if FULL_CLOSURES
118    TestObject *to = [[TestObject alloc] init];
119
120    // make sure a closure with to gets it retained on a copy
121
122    int initialRetainCounter = [to retainCounter];
123
124    void (^myClosure)(void) = ^{ | to |
125        printf("[to retainCounter] == %d\n", [to retainCounter]);
126    };
127
128    int afterClosureRetainCounter = [to retainCounter];
129
130    void (^myClosureCopy)(void) = Block_copy(myClosure);
131
132    int afterClosureCopyRetainCounter = [to retainCounter];
133
134    if (afterClosureRetainCounter > initialRetainCounter) {
135        printf("after closure, retain count is %d vs before %d\n", afterClosureRetainCounter, initialRetainCounter);
136        ++errors;
137    }
138
139    if (afterClosureCopyRetainCounter <= afterClosureRetainCounter) {
140        printf("closure copy did not retain interior object\n");
141        ++errors;
142    }
143
144    [to release];
145
146#endif
147    return errors;
148}
149
150int test_objc1(int verbose) {
151    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
152    int errors = 0;
153    TestObject *to = [[TestObject alloc] init];
154
155    // make sure a closure with to gets it retained on a copy
156
157    int initialRetainCounter = [to retainCounter];
158
159    void (^myClosure)(void) = ^{
160        printf("[to retainCounter] == %d\n", [to retainCounter]);
161    };
162
163    int afterClosureRetainCounter = [to retainCounter];
164
165    void (^myClosureCopy)(void) = Block_copy(myClosure);
166
167    int afterClosureCopyRetainCounter = [to retainCounter];
168
169    if (afterClosureRetainCounter > initialRetainCounter) {
170        printf("after closure, retain count is %d vs before %d\n", afterClosureRetainCounter, initialRetainCounter);
171        ++errors;
172    }
173
174    if (afterClosureCopyRetainCounter <= afterClosureRetainCounter) {
175        printf("closure copy did not retain interior object\n");
176        ++errors;
177    }
178
179    [to release];
180    [pool drain];
181
182    return errors;
183}
184
185#if 0
186const char *_Block_dump(const void *block) {
187    struct Block_basic *closure = (struct Block_basic *)block;
188    static char buffer[256], *cp = buffer;
189    if (closure == NULL) {
190        sprintf(cp, "NULL passed to _Block_dump\n");
191        return buffer;
192    }
193    if (closure->isa == NULL) {
194        cp += sprintf(cp, "isa: NULL\n");
195    }
196    else if (closure->isa == _NSConcreteStackBlock) {
197        cp += sprintf(cp, "isa: stack Block (%p)\n", _NSConcreteStackBlock);
198    }
199    else if (closure->isa == _NSConcreteMallocBlock) {
200        cp += sprintf(cp, "isa: malloc heap Block\n");
201    }
202    else if (closure->isa == _NSConcreteAutoBlock) {
203        cp += sprintf(cp, "isa: GC heap Block\n");
204    }
205    else {
206        cp += sprintf(cp, "isa: %p\n", closure->isa);
207    }
208    cp += sprintf(cp, "flags:");
209    if (closure->Block_flags & BLOCK_NEEDS_FREE) {
210        cp += sprintf(cp, " FREEME");
211    }
212    if (closure->Block_flags & BLOCK_HAS_COPY_DISPOSE) {
213        cp += sprintf(cp, " HASHELP");
214    }
215    if (closure->Block_flags & BLOCK_NO_COPY) {
216        cp += sprintf(cp, " HASBYREF");
217    }
218    if (closure->Block_flags & BLOCK_IS_GC) {
219        cp += sprintf(cp, " ISGC");
220    }
221    cp += sprintf(cp, "\nrefcount: %d\nsize: %d\n", closure->Block_flags & 0xfff, closure->Block_size);
222    cp += sprintf(cp, "invoke: %p\n", closure->Block_invoke);
223    return buffer;
224}
225#endif
226
227int test_stackObject(int verbose) {
228printf("testing stack object\n");
229    //makesubclasses();
230    void (^voidvoid)(void) = ^{ printf("hellow world\n"); };
231    //_NSMakeBlockObject((const void *)voidvoid);
232    printf("before voidvoid copy: %s", _Block_dump(voidvoid));
233    void (^voidvoidCopy)(void) = (void (^)(void))(void *)[(id)(void *)voidvoid copy];
234    printf("after voidvoid copy, voidvoidCopy is %s", _Block_dump(voidvoidCopy));
235    [(id)(void *)voidvoidCopy release];
236printf("done testing stack object\n");
237}
238
239
240int test_objc(int verbose) {
241    int errors = 0;
242
243    errors += test_objc1_1(verbose);
244    errors += test_objc1(verbose);
245    errors += test_objc2(verbose);
246    errors += test_objc3(verbose);
247#if NEWER_OBJC
248    errors += test_stackObject(verbose);
249#endif
250    if (errors == 0 && verbose) printf("objc import object test success\n");
251    return errors;
252}
253