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