1// TEST_CONFIG
2
3#include "test.h"
4#include <objc/runtime.h>
5#include <objc/objc-internal.h>
6#include <objc/objc-gdb.h>
7#include <dlfcn.h>
8#import <Foundation/NSObject.h>
9
10#if OBJC_HAVE_TAGGED_POINTERS
11
12#if !__OBJC2__  ||  !__x86_64__
13#error wrong architecture for tagged pointers
14#endif
15
16static BOOL didIt;
17
18@interface WeakContainer : NSObject
19{
20  @public
21    __weak id weaks[10000];
22}
23@end
24@implementation WeakContainer
25-(void) dealloc {
26    for (unsigned int i = 0; i < sizeof(weaks)/sizeof(weaks[0]); i++) {
27        testassert(weaks[i] == nil);
28    }
29    SUPER_DEALLOC();
30}
31-(void) finalize {
32    for (unsigned int i = 0; i < sizeof(weaks)/sizeof(weaks[0]); i++) {
33        testassert(weaks[i] == nil);
34    }
35    [super finalize];
36}
37@end
38
39OBJC_ROOT_CLASS
40@interface TaggedBaseClass
41@end
42
43@implementation TaggedBaseClass
44+ (void) initialize {
45}
46
47- (void) instanceMethod {
48    didIt = YES;
49}
50
51- (uintptr_t) taggedValue {
52    return _objc_getTaggedPointerValue(objc_unretainedPointer(self));
53}
54
55- (struct stret) stret: (struct stret) aStruct {
56    return aStruct;
57}
58
59- (long double) fpret: (long double) aValue {
60    return aValue;
61}
62
63
64-(void) dealloc {
65    fail("TaggedBaseClass dealloc called!");
66}
67
68static void *
69retain_fn(void *self, SEL _cmd __unused) {
70    void * (*fn)(void *) = (typeof(fn))_objc_rootRetain;
71    return fn(self); 
72}
73
74static void 
75release_fn(void *self, SEL _cmd __unused) {
76    void (*fn)(void *) = (typeof(fn))_objc_rootRelease;
77    fn(self); 
78}
79
80static void *
81autorelease_fn(void *self, SEL _cmd __unused) { 
82    void * (*fn)(void *) = (typeof(fn))_objc_rootAutorelease;
83    return fn(self); 
84}
85
86static unsigned long 
87retaincount_fn(void *self, SEL _cmd __unused) { 
88    unsigned long (*fn)(void *) = (typeof(fn))_objc_rootRetainCount;
89    return fn(self); 
90}
91
92+(void) load {
93    class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
94    class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
95    class_addMethod(self, sel_registerName("autorelease"), (IMP)autorelease_fn, "");
96    class_addMethod(self, sel_registerName("retainCount"), (IMP)retaincount_fn, "");    
97}
98
99@end
100
101@interface TaggedSubclass: TaggedBaseClass
102@end
103
104@implementation TaggedSubclass
105
106- (void) instanceMethod {
107    return [super instanceMethod];
108}
109
110- (uintptr_t) taggedValue {
111    return [super taggedValue];
112}
113
114- (struct stret) stret: (struct stret) aStruct {
115    return [super stret: aStruct];
116}
117
118- (long double) fpret: (long double) aValue {
119    return [super fpret: aValue];
120}
121@end
122
123@interface TaggedNSObjectSubclass : NSObject
124@end
125
126@implementation TaggedNSObjectSubclass
127
128- (void) instanceMethod {
129    didIt = YES;
130}
131
132- (uintptr_t) taggedValue {
133    return _objc_getTaggedPointerValue(objc_unretainedPointer(self));
134}
135
136- (struct stret) stret: (struct stret) aStruct {
137    return aStruct;
138}
139
140- (long double) fpret: (long double) aValue {
141    return aValue;
142}
143@end
144
145void testGenericTaggedPointer(objc_tag_index_t tag, const char *classname)
146{
147    testprintf("%s\n", classname);
148
149    Class cls = objc_getClass(classname);
150    testassert(cls);
151
152    void *taggedAddress = _objc_makeTaggedPointer(tag, 1234);
153    testassert(_objc_isTaggedPointer(taggedAddress));
154    testassert(_objc_getTaggedPointerTag(taggedAddress) == tag);
155    testassert(_objc_getTaggedPointerValue(taggedAddress) == 1234);
156
157    testassert((uintptr_t)taggedAddress & objc_debug_taggedpointer_mask);
158    uintptr_t slot = ((uintptr_t)taggedAddress >> objc_debug_taggedpointer_slot_shift) & objc_debug_taggedpointer_slot_mask;
159    testassert(objc_debug_taggedpointer_classes[slot] == cls);
160    testassert((((uintptr_t)taggedAddress << objc_debug_taggedpointer_payload_lshift) >> objc_debug_taggedpointer_payload_rshift) == 1234);
161    
162    id taggedPointer = objc_unretainedObject(taggedAddress);
163    testassert(object_getClass(taggedPointer) == cls);
164    testassert([taggedPointer taggedValue] == 1234);
165
166    didIt = NO;
167    [taggedPointer instanceMethod];
168    testassert(didIt);    
169    
170    struct stret orig = STRET_RESULT;
171    testassert(stret_equal(orig, [taggedPointer stret: orig]));
172    
173    long double value = 3.14156789;
174    testassert(value == [taggedPointer fpret: value]);
175
176    // Tagged pointers should bypass refcount tables and autorelease pools
177    // and weak reference tables
178    WeakContainer *w = [WeakContainer new];
179#if !__has_feature(objc_arc)
180    // prime method caches before leak checking
181    [taggedPointer retain];
182    [taggedPointer release];
183    [taggedPointer autorelease];
184#endif
185    leak_mark();
186    for (uintptr_t i = 0; i < sizeof(w->weaks)/sizeof(w->weaks[0]); i++) {
187        id o = objc_unretainedObject(_objc_makeTaggedPointer(tag, i));
188        testassert(object_getClass(o) == cls);
189        
190        id result = WEAK_STORE(w->weaks[i], o);
191        testassert(result == o);
192        testassert(w->weaks[i] == o);
193        
194        result = WEAK_LOAD(w->weaks[i]);
195        testassert(result == o);
196        
197        if (!objc_collectingEnabled()) {
198            uintptr_t rc = _objc_rootRetainCount(o);
199            testassert(rc != 0);
200            _objc_rootRelease(o);  testassert(_objc_rootRetainCount(o) == rc);
201            _objc_rootRelease(o);  testassert(_objc_rootRetainCount(o) == rc);
202            _objc_rootRetain(o);   testassert(_objc_rootRetainCount(o) == rc);
203            _objc_rootRetain(o);   testassert(_objc_rootRetainCount(o) == rc);
204            _objc_rootRetain(o);   testassert(_objc_rootRetainCount(o) == rc);
205#if !__has_feature(objc_arc)
206            [o release];  testassert(_objc_rootRetainCount(o) == rc);
207            [o release];  testassert(_objc_rootRetainCount(o) == rc);
208            [o retain];   testassert(_objc_rootRetainCount(o) == rc);
209            [o retain];   testassert(_objc_rootRetainCount(o) == rc);
210            [o retain];   testassert(_objc_rootRetainCount(o) == rc);
211            objc_release(o);  testassert(_objc_rootRetainCount(o) == rc);
212            objc_release(o);  testassert(_objc_rootRetainCount(o) == rc);
213            objc_retain(o);   testassert(_objc_rootRetainCount(o) == rc);
214            objc_retain(o);   testassert(_objc_rootRetainCount(o) == rc);
215            objc_retain(o);   testassert(_objc_rootRetainCount(o) == rc);
216#endif
217            PUSH_POOL {
218                testassert(_objc_rootRetainCount(o) == rc);
219                _objc_rootAutorelease(o);
220                testassert(_objc_rootRetainCount(o) == rc);
221#if !__has_feature(objc_arc)
222                [o autorelease];
223                testassert(_objc_rootRetainCount(o) == rc);
224                objc_autorelease(o);
225                testassert(_objc_rootRetainCount(o) == rc);
226                objc_retainAutorelease(o);
227                testassert(_objc_rootRetainCount(o) == rc);
228                objc_autoreleaseReturnValue(o);
229                testassert(_objc_rootRetainCount(o) == rc);
230                objc_retainAutoreleaseReturnValue(o);
231                testassert(_objc_rootRetainCount(o) == rc);
232                objc_retainAutoreleasedReturnValue(o);
233                testassert(_objc_rootRetainCount(o) == rc);
234#endif
235            } POP_POOL;
236            testassert(_objc_rootRetainCount(o) == rc);
237        }
238    }
239    leak_check(0);
240    for (uintptr_t i = 0; i < 10000; i++) {
241        testassert(w->weaks[i] != NULL);
242        WEAK_STORE(w->weaks[i], NULL);
243        testassert(w->weaks[i] == NULL);
244        testassert(WEAK_LOAD(w->weaks[i]) == NULL);
245    }
246    RELEASE_VAR(w);
247}
248
249int main()
250{
251    testassert(objc_debug_taggedpointer_mask != 0);
252    testassert(_objc_taggedPointersEnabled());
253
254    PUSH_POOL {
255        // Avoid CF's tagged pointer tags because of rdar://11368528
256
257        _objc_registerTaggedPointerClass(OBJC_TAG_1, 
258                                         objc_getClass("TaggedBaseClass"));
259        testGenericTaggedPointer(OBJC_TAG_1, 
260                                 "TaggedBaseClass");
261        
262        _objc_registerTaggedPointerClass(OBJC_TAG_7, 
263                                         objc_getClass("TaggedSubclass"));
264        testGenericTaggedPointer(OBJC_TAG_7, 
265                                 "TaggedSubclass");
266        
267        _objc_registerTaggedPointerClass(OBJC_TAG_NSManagedObjectID, 
268                                         objc_getClass("TaggedNSObjectSubclass"));
269        testGenericTaggedPointer(OBJC_TAG_NSManagedObjectID, 
270                                 "TaggedNSObjectSubclass");
271    } POP_POOL;
272
273    succeed(__FILE__);
274}
275
276// OBJC_HAVE_TAGGED_POINTERS
277#else
278// not OBJC_HAVE_TAGGED_POINTERS
279
280// Tagged pointers not supported.
281
282int main() 
283{
284#if __OBJC2__
285    testassert(objc_debug_taggedpointer_mask == 0);
286    testassert(!_objc_taggedPointersEnabled());
287#else
288    testassert(!dlsym(RTLD_DEFAULT, "objc_debug_taggedpointer_mask"));
289#endif
290
291    succeed(__FILE__);
292}
293
294#endif
295