dyld-832.7.1.tar.gz
[apple/dyld.git] / testing / test-cases / _dyld_for_each_objc_class-duplicates.dtest / main.m
1
2 // BUILD:  $CC linked1.m -dynamiclib -o $BUILD_DIR/liblinked1.dylib -install_name $RUN_DIR/liblinked1.dylib -lobjc
3 // BUILD:  $CC linked2.m -dynamiclib -o $BUILD_DIR/liblinked2.dylib -install_name $RUN_DIR/liblinked2.dylib -lobjc
4 // BUILD:  $CC main.m -o $BUILD_DIR/_dyld_for_each_objc_class-duplicates.exe $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib -lobjc -framework Foundation
5
6 // RUN:  ./_dyld_for_each_objc_class-duplicates.exe
7
8 #include <mach-o/dyld_priv.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #import <Foundation/NSObject.h>
13
14 #include "test_support.h"
15
16
17 static bool objcOptimizedByDyld() {
18     extern const uint32_t objcInfo[]  __asm("section$start$__DATA_CONST$__objc_imageinfo");
19     return (objcInfo[1] & 0x80);
20 }
21
22 static bool haveDyldCache() {
23     size_t unusedCacheLen;
24     return (_dyld_get_shared_cache_range(&unusedCacheLen) != NULL);
25 }
26
27 // All the libraries have a copy of NSString
28 @interface NSString : NSObject
29 @end
30
31 @implementation NSString
32 @end
33
34 extern Class OBJC_CLASS_$_NSString;
35
36 // The main executable also has versions of these Foundation classes
37 @interface NSDictionary : NSObject
38 @end
39
40 @implementation NSDictionary
41 @end
42
43 extern Class OBJC_CLASS_$_NSDictionary;
44
45 @interface NSError : NSObject
46 @end
47
48 @implementation NSError
49 @end
50
51 extern Class OBJC_CLASS_$_NSError;
52
53 @interface NSSet : NSObject
54 @end
55
56 @implementation NSSet
57 @end
58
59 extern Class OBJC_CLASS_$_NSSet;
60
61 @interface NSArray : NSObject
62 @end
63
64 @implementation NSArray
65 @end
66
67 extern Class OBJC_CLASS_$_NSArray;
68
69 Class getMainNSString() {
70   return (Class)&OBJC_CLASS_$_NSString;
71 }
72
73 extern id objc_getClass(const char *name);
74
75 // Get the NSString from liblinked1.dylib
76 extern Class getLinked1NSString();
77
78 // Get the NSString from liblinked2.dylib
79 extern Class getLinked2NSString();
80
81 static bool gotNSStringMain = false;
82 static bool gotNSStringLinked = false;
83 static bool gotNSStringLinked2 = false;
84 static bool gotNSStringFoundation = false;
85
86 void testDuplicate(const char* className, Class nonCacheClass) {
87   // Walk all the implementations of the class.  There should be 2.  One in the executable and one in the shared cache
88   // The shared cache one should be returned first.
89
90   // The objc runtime should have chosen the Foundation one as the canonical definition.
91   Class objcRuntimeClassImpl = (Class)objc_getClass(className);
92   if (objcRuntimeClassImpl == nil) {
93     FAIL("class %s not found via runtime", className);
94   }
95
96   if (objcRuntimeClassImpl == nonCacheClass) {
97     FAIL("class %s from runtime should not match main exexutable", className);
98   }
99
100   __block bool foundSharedCacheImpl = false;
101   __block bool foundMainExecutableImpl = false;
102   __block bool foundAnyClass = false;
103   __block bool foundTooManyClasses = false;
104   _dyld_for_each_objc_class(className, ^(void* classPtr, bool isLoaded, bool* stop) {
105     foundAnyClass = true;
106
107     // We should walk these in the order Foundation, main exe
108     if (!foundSharedCacheImpl) {
109       if (classPtr != objcRuntimeClassImpl) {
110         FAIL("Optimized class %s should have come from Foundation", className);
111       }
112       if (!isLoaded) {
113         FAIL("Optimized class %s isLoaded should have been set on Foundation", className);
114       }
115       foundSharedCacheImpl = true;
116       return;
117     }
118     if (!foundMainExecutableImpl) {
119       if (classPtr != nonCacheClass) {
120         FAIL("Optimized class %s should have come from main executable", className);
121       }
122       if (!isLoaded) {
123         FAIL("Optimized class %s isLoaded should have been set on main executable", className);
124       }
125       foundMainExecutableImpl = true;
126       return;
127     }
128
129     foundTooManyClasses = true;
130     FAIL("class %s found somewhere other than main executable and Foundation", className);
131   });
132
133   if (!foundAnyClass) {
134     FAIL("class %s not found", className);
135   }
136
137   if (foundTooManyClasses) {
138     FAIL("class %s found too many times", className);
139   }
140
141   if (!foundSharedCacheImpl || !foundMainExecutableImpl) {
142     FAIL("class %s not found for shared cache or main executable", className);
143   }
144 }
145
146 int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
147     size_t sharedCacheLen = 0;
148     const void* sharedCacheStart = _dyld_get_shared_cache_range(&sharedCacheLen);
149     if (!objcOptimizedByDyld() || (sharedCacheStart==NULL)) {
150     __block bool sawClass = false;
151     _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) {
152       sawClass = true;
153     });
154     if (sawClass) {
155       FAIL("dyld2 shouldn't see any classes");
156     }
157     PASS("no shared cache or no dyld optimized objc");
158   }
159
160   // Check that NSString comes from Foundation as the shared cache should win here.
161   id runtimeNSString = objc_getClass("NSString");
162   if ( (uint64_t)runtimeNSString < (uint64_t)sharedCacheStart ) {
163     FAIL("NSString should have come from Foundation but instead was %p", runtimeNSString);
164   }
165   if ( (uint64_t)runtimeNSString >= ((uint64_t)sharedCacheStart + sharedCacheLen) ) {
166     FAIL("NSString should have come from Foundation but instead was %p", runtimeNSString);
167   }
168
169   // Walk all the implementations of "NSString"
170   _dyld_for_each_objc_class("NSString", ^(void* classPtr, bool isLoaded, bool* stop) {
171     // We should walk these in the order Foundation, liblinked2, liblinked, main exe
172     if (!gotNSStringFoundation) {
173       if (classPtr != runtimeNSString) {
174         FAIL("Optimized NSString should have come from Foundation");
175       }
176       if (!isLoaded) {
177         FAIL("Optimized NSString isLoaded should have been set on Foundation");
178       }
179       gotNSStringFoundation = true;
180       return;
181     }
182     if (!gotNSStringLinked2) {
183       if (classPtr != getLinked2NSString()) {
184         FAIL("Optimized NSString should have come from liblinked2");
185       }
186       if (!isLoaded) {
187         FAIL("Optimized NSString isLoaded should have been set on liblinked2");
188       }
189       gotNSStringLinked2 = true;
190       return;
191     }
192     if (!gotNSStringLinked) {
193       if (classPtr != getLinked1NSString()) {
194         FAIL("Optimized NSString should have come from liblinked");
195       }
196       if (!isLoaded) {
197         FAIL("Optimized NSString isLoaded should have been set on liblinked");
198       }
199       gotNSStringLinked = true;
200       return;
201     }
202     if (!gotNSStringMain) {
203       if (classPtr != getMainNSString()) {
204         FAIL("Optimized NSString should have come from main exe");
205       }
206       if (!isLoaded) {
207         FAIL("Optimized NSString isLoaded should have been set on main exe");
208       }
209       gotNSStringMain = true;
210       return;
211     }
212     FAIL("Unexpected Optimized NSString");
213   });
214
215   if ( !gotNSStringFoundation || !gotNSStringLinked2 || !gotNSStringLinked || !gotNSStringMain) {
216     FAIL("Failed to find all duplicates of 'NSString'");
217   }
218
219   // Visit again, and return Foundation's NSString
220   __block void* NSStringImpl = nil;
221   _dyld_for_each_objc_class("NSString", ^(void* classPtr, bool isLoaded, bool* stop) {
222     NSStringImpl = classPtr;
223     *stop = true;
224   });
225   if (NSStringImpl != runtimeNSString) {
226     FAIL("_dyld_for_each_objc_class should have returned NSString from Foundation");
227   }
228
229   testDuplicate("NSDictionary", (Class)&OBJC_CLASS_$_NSDictionary);
230   testDuplicate("NSError", (Class)&OBJC_CLASS_$_NSError);
231   testDuplicate("NSSet", (Class)&OBJC_CLASS_$_NSSet);
232   testDuplicate("NSArray", (Class)&OBJC_CLASS_$_NSArray);
233
234   PASS("Success");
235 }