dyld-750.5.tar.gz
[apple/dyld.git] / testing / test-cases / _dyld_for_each_objc_class-missing-weak-chained.dtest / main.mm
1 // BUILD_ONLY: MacOSX
2
3 // BUILD:  $CC missing.m -dynamiclib -o $BUILD_DIR/libmissing.dylib -install_name $BUILD_DIR/libmissing.dylib -lobjc -Wl,-fixup_chains
4 // BUILD:  $CC lib1.m -dynamiclib -o $BUILD_DIR/liblinked1.dylib -install_name $RUN_DIR/liblinked1.dylib -lobjc -Wl,-fixup_chains
5 // BUILD:  $CC lib2.m -dynamiclib -o $BUILD_DIR/liblinked2.dylib -install_name $RUN_DIR/liblinked2.dylib -lobjc $BUILD_DIR/libmissing.dylib -Wl,-fixup_chains
6 // BUILD:  $CXX main.mm -o $BUILD_DIR/_dyld_for_each_objc_class-missing-weak-chained.exe -lobjc $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib $BUILD_DIR/libmissing.dylib -Wl,-fixup_chains -lc++
7
8 // BUILD: $SKIP_INSTALL $BUILD_DIR/libmissing.dylib
9
10 // RUN:  ./_dyld_for_each_objc_class-missing-weak-chained.exe
11
12 // liblinked2 weakly links libmissing and so has a missing weak superclass.
13 // This means we should not see classes from liblinked be returned from _dyld_for_each_objc_class
14 // liblinked1 itself has classes which are fine so shoud be in the map.
15 // At runtime, objc is going to walk the images in reverse load order so will see the classes in liblinked2 first
16 // which are the ones we couldn't optimize.  But objc should then check the closure class map and choose the class
17 // from liblinked
18
19 #include <mach-o/dyld_priv.h>
20
21 #import <Foundation/Foundation.h>
22
23 #include "test_support.h"
24
25 // All the libraries have a copy of DyldClass
26 @interface DyldClass : NSObject
27 @end
28
29 @implementation DyldClass
30 @end
31
32 // Only the main executable has DyldMainClass
33 @interface DyldMainClass : NSObject
34 @end
35
36 @implementation DyldMainClass
37 @end
38
39 extern Class OBJC_CLASS_$_DyldClass;
40 extern Class OBJC_CLASS_$_DyldMainClass;
41
42 Class getMainDyldClass() {
43   return (Class)&OBJC_CLASS_$_DyldClass;
44 }
45
46 Class getMainDyldMainClass() {
47   return (Class)&OBJC_CLASS_$_DyldMainClass;
48 }
49
50 extern "C" id objc_getClass(const char *name);
51
52 // Get the DyldClass from liblinked1.dylib
53 extern "C" id getLinked1DyldClass();
54
55 // Get the DyldLinkedClass from liblinked1.dylib
56 extern "C" id getLinked1DyldLinkedClass();
57
58 // Get the DyldLinkedClass from liblinked.dylib
59 extern "C" id getLinked2DyldLinkedClass();
60
61 // Get the DyldClass from libmissing.dylib
62 // Note, this is weak_import and missing so this must fail
63 __attribute__((weak_import))
64 extern "C" id getMissingDyldClass();
65
66 static bool gotDyldClassMain = false;
67 static bool gotDyldClassLinked1 = false;
68
69 int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
70   // This API is only available with dyld3 and shared caches.  If we have dyld2 then don't do anything
71   const char* testDyldMode = getenv("TEST_DYLD_MODE");
72   assert(testDyldMode);
73
74   size_t unusedCacheLen;
75   bool haveSharedCache = _dyld_get_shared_cache_range(&unusedCacheLen) != 0;
76   if (!strcmp(testDyldMode, "2") || !haveSharedCache) {
77     __block bool sawClass = false;
78     _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) {
79       sawClass = true;
80     });
81     if (sawClass) {
82       FAIL("dyld2 shouldn't see any classes");
83     }
84     PASS("Success");
85   }
86
87   // Make sure libmissing.dylib is actually missing
88   if (&getMissingDyldClass != nil) {
89     FAIL("libmissing needs to be missing");
90   }
91
92   // DyldClass in liblinked1 should exist as its superclass is just NSObject
93   if (getLinked1DyldClass() == nil) {
94     FAIL("liblinked1 DyldClass should exist");
95   }
96
97   // DyldLinkedClass in liblinked1 should exist as its superclass is just NSObject
98   if (getLinked1DyldLinkedClass() == nil) {
99     FAIL("liblinked1 DyldLinkedClass should exist");
100   }
101
102   // DyldLinkedClass in liblinked2 should exist as its superclass is just NSObject
103   if (getLinked2DyldLinkedClass() == nil) {
104     FAIL("liblinked2 DyldLinkedClass should exist");
105   }
106
107   // Check that DyldMainClass comes main.exe as that is its only definition
108   id runtimeDyldMainClass = objc_getClass("DyldMainClass");
109   if (runtimeDyldMainClass != getMainDyldMainClass()) {
110     FAIL("DyldMainClass should have come from main.exe");
111   }
112
113   // Check that DyldClass comes liblinked1 as it should be missing from liblinked2
114   id runtimeDyldClass = objc_getClass("DyldClass");
115   if (runtimeDyldClass != getLinked1DyldClass()) {
116     FAIL("DyldClass should have come from liblinked1");
117   }
118
119   // Check that DyldLinkedClass comes from liblinked2
120   // Note, this changes once the objc runtime has adopted our changes.  Don't test it for now
121 #if 0
122   id runtimeDyldLinkedClass = objc_getClass("DyldLinkedClass");
123   if (runtimeDyldLinkedClass != getLinked2DyldLinkedClass()) {
124     FAIL("DyldLinkedClass should have come from liblinked2");
125   }
126 #endif
127
128   _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) {
129     // We should walk these in the order liblinked, main exe
130     if (!gotDyldClassLinked1) {
131       if (classPtr != getLinked1DyldClass()) {
132         FAIL("DyldClass should have come from liblinked1");
133       }
134       if (!isLoaded) {
135         FAIL("DyldClass isLoaded should have been set on liblinked1");
136       }
137       gotDyldClassLinked1 = true;
138       return;
139     }
140     if (!gotDyldClassMain) {
141       if (classPtr != getMainDyldClass()) {
142         FAIL("DyldClass should have come from main exe");
143       }
144       if (!isLoaded) {
145         FAIL("DyldClass isLoaded should have been set on main exe");
146       }
147       gotDyldClassMain = true;
148       return;
149     }
150     FAIL("Unexpected DyldClass");
151   });
152
153   if (!gotDyldClassLinked1) {
154     FAIL("_dyld_for_each_objc_class should have seen DyldClass in liblinked1");
155   }
156
157   if (!gotDyldClassMain) {
158     FAIL("_dyld_for_each_objc_class should have seen DyldClass in main.exe");
159   }
160
161   // Visit again, and return liblinked1's DyldClass
162   // Visit again, and return liblinked2's DyldClass
163   __block void* dyldClassImpl = nil;
164   _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) {
165     dyldClassImpl = classPtr;
166     *stop = true;
167   });
168   if (dyldClassImpl != getLinked1DyldClass()) {
169     FAIL("_dyld_for_each_objc_class should have returned DyldClass from liblinked1");
170   }
171
172   // Visit again, and return liblinked1's DyldClass
173   dyldClassImpl = nil;
174   _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) {
175     // We should walk these in the order liblinked, main exe
176     // And return the one from main.exe
177     if (classPtr == getLinked1DyldClass())
178       return;
179     dyldClassImpl = classPtr;
180     *stop = true;
181   });
182   if (dyldClassImpl != getMainDyldClass()) {
183     FAIL("_dyld_for_each_objc_class should have returned DyldClass from main.exe");
184   }
185
186   PASS("Success");
187 }