]> git.saurik.com Git - apple/objc4.git/commitdiff
objc4-437.3.tar.gz mac-os-x-1068 v437.3
authorApple <opensource@apple.com>
Fri, 24 Jun 2011 13:59:06 +0000 (13:59 +0000)
committerApple <opensource@apple.com>
Fri, 24 Jun 2011 13:59:06 +0000 (13:59 +0000)
libobjc.order
runtime/objc-cache.m
runtime/objc-class-old.m
runtime/objc-private.h
runtime/objc-runtime-new.m
runtime/objc-runtime-old.m
test/Makefile
test/weak.h [new file with mode: 0644]
test/weak.m [new file with mode: 0644]
test/weak2.m [new file with mode: 0644]

index d23b20131c30c4674f2b29b57cd41cd70c478d75..c7e85ca361d771edab972d0f0d79e0033e4dbf2f 100644 (file)
@@ -120,7 +120,6 @@ _NXNextHashState
 _objc_msgSend
 __class_lookupMethodAndLoadCache
 __class_getFreedObjectClass
-__class_getNonexistentObjectClass
 __class_isInitialized
 __class_initialize
 __class_isMetaClass
index 0908c4225f82e95c4aaf03495f176c9149ed4617..0da21a391701e1ca81686ce1d1b180dbac6d5d7a 100644 (file)
@@ -831,7 +831,7 @@ static int _collecting_in_critical(void)
     }
 
     // Deallocate the thread list
-    vm_deallocate (mach_task_self (), (vm_address_t) threads, sizeof(threads) * number);
+    vm_deallocate (mach_task_self (), (vm_address_t) threads, sizeof(threads[0]) * number);
 
     // Return our finding
     return result;
index fecccf2d388e5a9de9b95f9042c02df8223bab94..65a85af156785adaf4a9be66a488f0714b0826d3 100644 (file)
@@ -47,20 +47,6 @@ static const struct old_class freedObjectClass =
     NULL                               // protocols
 };
 
-static const struct old_class nonexistentObjectClass =
-{
-    Nil,                               // isa
-    Nil,                               // super_class
-    "NONEXISTENT(id)",         // name
-    0,                         // version
-    0,                         // info
-    0,                         // instance_size
-    NULL,                              // ivars
-    NULL,                              // methodLists
-    (Cache) &_objc_empty_cache,                // cache
-    NULL                               // protocols
-};
-
 
 /***********************************************************************
 * _class_getFreedObjectClass.  Return a pointer to the dummy freed
@@ -74,18 +60,6 @@ static Class _class_getFreedObjectClass(void)
 }
 
 
-/***********************************************************************
-* _class_getNonexistentClass.  Return a pointer to the dummy nonexistent
-* object class.  This is used when, for example, mapping the class
-* refs for an image, and the class can not be found, so that we can
-* catch later uses of the non-existent class.
-**********************************************************************/
-__private_extern__ Class _class_getNonexistentObjectClass(void)
-{
-    return (Class)&nonexistentObjectClass;
-}
-
-
 /***********************************************************************
 * _objc_getFreedObjectClass.  Return a pointer to the dummy freed
 * object class.  Freed objects get their isa pointers replaced with
@@ -349,15 +323,6 @@ static void _freedHandler(id obj, SEL sel)
                   sel_getName(sel), obj);
 }
 
-/***********************************************************************
-* _nonexistentHandler.
-**********************************************************************/
-static void _nonexistentHandler(id obj, SEL sel)
-{
-    __objc_error (obj, "message %s sent to non-existent object=%p", 
-                  sel_getName(sel), obj);
-}
-
 
 /***********************************************************************
 * ABI-specific lookUpMethod helpers.
@@ -378,10 +343,6 @@ __private_extern__ IMP prepareForMethodLookup(Class cls, SEL sel, BOOL init)
     if (cls == _class_getFreedObjectClass())
         return (IMP) _freedHandler;
 
-    // Check for nonexistent class
-    if (cls == _class_getNonexistentObjectClass())
-        return (IMP) _nonexistentHandler;
-
     if (init  &&  !_class_isInitialized(cls)) {
         _class_initialize (cls);
         // If sel == initialize, _class_initialize will send +initialize and 
index b147f1a9adee5d4c95d49a1c8548cfe68a1151df..fd63f76c318b2c8eb4ed4eaf251083e0324835f5 100644 (file)
@@ -824,7 +824,6 @@ static inline struct old_class *_class_asOld(Class cls) { return (struct old_cla
 static inline struct old_category *_category_asOld(Category cat) { return (struct old_category *)cat; }
 
 extern void unload_class(struct old_class *cls);
-Class _class_getNonexistentObjectClass(void);
 #endif
 
 extern BOOL object_cxxConstruct(id obj);
index a1c4d9c8e6a3278487034ea604dba56509322190..de8d4fc6a31e798a68e8f9774e28dd40380d5ea9 100644 (file)
@@ -1759,6 +1759,7 @@ static void removeFutureClass(const char *name)
 /***********************************************************************
 * remappedClasses
 * Returns the oldClass => newClass map for realized future classes.
+* Returns the oldClass => NULL map for ignored weak-linked classes.
 * Locking: runtimeLock must be read- or write-locked by the caller
 **********************************************************************/
 static NXMapTable *remappedClasses(BOOL create)
@@ -1797,6 +1798,7 @@ static BOOL noClassesRemapped(void)
 /***********************************************************************
 * addRemappedClass
 * newcls is a realized future class, replacing oldcls.
+* OR newcls is NULL, replacing ignored weak-linked class oldcls.
 * Locking: runtimeLock must be write-locked by the caller
 **********************************************************************/
 static void addRemappedClass(class_t *oldcls, class_t *newcls)
@@ -1818,20 +1820,29 @@ static void addRemappedClass(class_t *oldcls, class_t *newcls)
 * remapClass
 * Returns the live class pointer for cls, which may be pointing to 
 * a class struct that has been reallocated.
+* Returns NULL if cls is ignored because of weak linking.
 * Locking: runtimeLock must be read- or write-locked by the caller
 **********************************************************************/
 static class_t *remapClass(class_t *cls)
 {
     rwlock_assert_locked(&runtimeLock);
 
-    class_t *newcls = NXMapGet(remappedClasses(YES), cls);
-    return newcls ? newcls : cls;
+    class_t *c2;
+
+    if (!cls) return NULL;
+
+    if (NXMapMember(remappedClasses(YES), cls, (void**)&c2) == NX_MAPNOTAKEY) {
+        return cls;
+    } else {
+        return c2;
+    }
 }
 
 
 /***********************************************************************
 * remapClassRef
-* Fix up a class ref, in case the class referenced has been reallocated.
+* Fix up a class ref, in case the class referenced has been reallocated 
+* or is an ignored weak-linked class.
 * Locking: runtimeLock must be read- or write-locked by the caller
 **********************************************************************/
 static void remapClassRef(class_t **clsref)
@@ -2234,6 +2245,28 @@ static class_t *getClass(const char *name)
 }
 
 
+/***********************************************************************
+* missingWeakSuperclass
+* Return YES if some superclass of cls was weak-linked and is missing.
+**********************************************************************/
+static BOOL 
+missingWeakSuperclass(class_t *cls)
+{
+    assert(!isRealized(cls));
+
+    if (!cls->superclass) {
+        // superclass NULL. This is normal for root classes only.
+        return (!(cls->data->flags & RO_ROOT));
+    } else {
+        // superclass not NULL. Check if a higher superclass is missing.
+        class_t *supercls = remapClass(cls->superclass);
+        if (!supercls) return YES;
+        if (isRealized(supercls)) return NO;
+        return missingWeakSuperclass(supercls);
+    }
+}
+
+
 /***********************************************************************
 * realizeAllClassesInImage
 * Non-lazily realizes all unrealized classes in the given image.
@@ -2541,6 +2574,20 @@ __private_extern__ void _read_images(header_info **hList, uint32_t hCount)
         class_t **classlist = _getObjc2ClassList(hi, &count);
         for (i = 0; i < count; i++) {
             const char *name = getName(classlist[i]);
+            
+            if (missingWeakSuperclass(classlist[i])) {
+                // No superclass (probably weak-linked). 
+                // Disavow any knowledge of this subclass.
+                if (PrintConnecting) {
+                    _objc_inform("CLASS: IGNORING class '%s' with "
+                                 "missing weak-linked superclass", name);
+                }
+                addRemappedClass(classlist[i], NULL);
+                classlist[i]->superclass = NULL;
+                classlist[i] = NULL;
+                continue;
+            }
+
             if (NXCountMapTable(future_class_map) > 0) {
                 class_t *newCls = NXMapGet(future_class_map, name);
                 if (newCls) {
@@ -2672,6 +2719,18 @@ __private_extern__ void _read_images(header_info **hList, uint32_t hCount)
             // Do NOT use cat->cls! It may have been remapped.
             class_t *cls = remapClass(cat->cls);
 
+            if (!cls) {
+                // Category's target class is missing (probably weak-linked).
+                // Disavow any knowledge of this category.
+                catlist[i] = NULL;
+                if (PrintConnecting) {
+                    _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
+                                 "missing weak-linked target class", 
+                                 cat->name, cat);
+                }
+                continue;
+            }
+
             // Process this category. 
             // First, register the category with its target class. 
             // Then, rebuild the class's method lists (etc) if 
@@ -2726,12 +2785,13 @@ __private_extern__ void _read_images(header_info **hList, uint32_t hCount)
 // cls must already be connected.
 static void schedule_class_load(class_t *cls)
 {
+    if (!cls) return;
     assert(isRealized(cls));  // _read_images should realize
 
     if (cls->data->flags & RW_LOADED) return;
 
-    class_t *supercls = getSuperclass(cls);
-    if (supercls) schedule_class_load(supercls);
+    // Ensure superclass-first ordering
+    schedule_class_load(getSuperclass(cls));
 
     add_class_to_loadable_list((Class)cls);
     changeInfo(cls, RW_LOADED, 0); 
@@ -2746,8 +2806,7 @@ __private_extern__ void prepare_load_methods(header_info *hi)
     class_t **classlist = 
         _getObjc2NonlazyClassList(hi, &count);
     for (i = 0; i < count; i++) {
-        class_t *cls = remapClass(classlist[i]);
-        schedule_class_load(cls);
+        schedule_class_load(remapClass(classlist[i]));
     }
 
     category_t **categorylist = _getObjc2NonlazyCategoryList(hi, &count);
@@ -2755,6 +2814,7 @@ __private_extern__ void prepare_load_methods(header_info *hi)
         category_t *cat = categorylist[i];
         // Do NOT use cat->cls! It may have been remapped.
         class_t *cls = remapClass(cat->cls);
+        if (!cls) continue;  // category for ignored weak-linked class
         realizeClass(cls);
         assert(isRealized(cls->isa));
         add_category_to_loadable_list((Category)cat);
@@ -2779,7 +2839,10 @@ __private_extern__ void _unload_image(header_info *hi)
     category_t **catlist = _getObjc2CategoryList(hi, &count);
     for (i = 0; i < count; i++) {
         category_t *cat = catlist[i];
+        if (!cat) continue;  // category for ignored weak-linked class
         class_t *cls = remapClass(cat->cls);
+        assert(cls);  // shouldn't have live category for dead class
+
         // fixme for MH_DYLIB cat's class may have been unloaded already
 
         // unattached list
@@ -2795,9 +2858,12 @@ __private_extern__ void _unload_image(header_info *hi)
     for (i = 0; i < count; i++) {
         class_t *cls = classlist[i];
         // fixme remapped classes?
-        remove_class_from_loadable_list((Class)cls);
-        unload_class(cls->isa, YES);
-        unload_class(cls, NO);
+        // fixme ignored weak-linked classes
+        if (cls) {
+            remove_class_from_loadable_list((Class)cls);
+            unload_class(cls->isa, YES);
+            unload_class(cls, NO);
+        }
     }
     
     // Clean up protocols.
@@ -3728,7 +3794,7 @@ class_copyProtocolList(Class cls_gen, unsigned int *outCount)
 __private_extern__ const char **
 _objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount)
 {
-    size_t count, i;
+    size_t count, i, shift;
     class_t **classlist;
     const char **names;
     
@@ -3737,9 +3803,16 @@ _objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount)
     classlist = _getObjc2ClassList(hi, &count);
     names = malloc((count+1) * sizeof(const char *));
     
+    shift = 0;
     for (i = 0; i < count; i++) {
-        names[i] = getName(classlist[i]);
+        class_t *cls = remapClass(classlist[i]);
+        if (cls) {
+            names[i-shift] = getName(classlist[i]);
+        } else {
+            shift++;  // ignored weak-linked class
+        }
     }
+    count -= shift;
     names[count] = NULL;
 
     rwlock_unlock_read(&runtimeLock);
index 954bc7a50997969b54223223fa959dc3a151943d..537456145b90b5e37885b70b6253e8cfa2ea4e63 100644 (file)
@@ -73,7 +73,7 @@
  * (5) is not yet eligible for +load.
  * 
  * Image mapping is NOT CURRENTLY THREAD-SAFE with respect to just about 
- *  *  * anything. Image mapping IS RE-ENTRANT in several places: superclass 
+ * anything. Image mapping IS RE-ENTRANT in several places: superclass 
  * lookup may cause ZeroLink to load another image, and +load calls may 
  * cause dyld to load another image.
  * 
@@ -82,7 +82,6 @@
  * Read all classes in all new images. 
  *   Add them all to unconnected_class_hash. 
  *   Note any +load implementations before categories are attached.
- *   Fix up any pended classrefs referring to them.
  *   Attach any pending categories.
  * Read all categories in all new images. 
  *   Attach categories whose parent class exists (connected or not), 
@@ -95,6 +94,7 @@
  *   If the superclass is connected:
  *     connect the class
  *     mark the class eligible for +load, if implemented
+ *     fix up any pended classrefs referring to the class
  *     connect any pended subclasses of the class
  * Resolve selector refs and class refs in all new images.
  *   Class refs whose classes still do not exist are pended.
  * are attached. Otherwise, if a category implements +load and its class 
  * has no class methods, the class's +load scan would find the category's 
  * +load method, which would then be called twice.
+ *
+ * Correctness: pended class refs are not fixed up until the class is 
+ * connected. Classes with missing weak superclasses remain unconnected. 
+ * Class refs to classes with missing weak superclasses must be NULL. 
+ * Therefore class refs to unconnected classes must remain un-fixed.
  * 
  **********************************************************************/
 
@@ -933,6 +938,9 @@ static void really_connect_class(struct old_class *cls,
 
     mutex_unlock(&classLock);
  
+    // Fix up pended class refs to this class, if any
+    resolve_references_to_class(cls);
+
     // Connect newly-connectable subclasses
     resolve_subclasses_of_class(cls);
 
@@ -1226,9 +1234,6 @@ static void _objc_read_classes_from_image(header_info *hi)
             mutex_unlock(&classLock);
 
             if (!rejected) {
-                // Fix up pended class refs to this class, if any
-                resolve_references_to_class(newCls);
-
                 // Attach pended categories for this class, if any
                 resolve_categories_for_class(newCls);
             }
@@ -1308,16 +1313,17 @@ static void fix_class_ref(struct old_class **ref, const char *name, BOOL isMeta)
     struct old_class *cls;
 
     // Get pointer to class of this name
-    // YES unconnected, YES class loader
-    cls = _class_asOld((Class)look_up_class(name, YES, YES));
+    // NO unconnected, YES class loader
+    // (real class with weak-missing superclass is unconnected now)
+    cls = _class_asOld((Class)look_up_class(name, NO, YES));
     if (cls) {
         // Referenced class exists. Fix up the reference.
         *ref = isMeta ? cls->isa : cls;
     } else {
-        // Referenced class does not exist yet. Insert a placeholder 
-        // class and fix up the reference later.
+        // Referenced class does not exist yet. Insert NULL for now 
+        // (weak-linking) and fix up the reference if the class arrives later.
         pendClassReference (ref, name, isMeta);
-        *ref = (struct old_class *)_class_getNonexistentObjectClass();
+        *ref = NULL;
     }
 }
 
@@ -1354,7 +1360,7 @@ static void removePendingReferences(struct old_class **refs, size_t count)
     if (!pendingClassRefsMap) return;
 
     // Search the pending class ref table for class refs in this range.
-    // The class refs may have already been stomped with nonexistentClass
+    // The class refs may have already been stomped with NULL
     // so there's no way to recover the original class name.
 
     {    
@@ -2115,7 +2121,7 @@ static void rependClassReferences(struct old_class **refs, size_t count,
         if ((uintptr_t)(refs[i]) >= start  &&  (uintptr_t)(refs[i]) < end) {
             pendClassReference(&refs[i], refs[i]->name, 
                                (refs[i]->info & CLS_META) ? YES : NO);
-            refs[i] = (struct old_class *)_class_getNonexistentObjectClass();
+            refs[i] = NULL;
         }
     }
 }
index 3c6e5fd718a86d6358877021dc10077d7a6022b6..f2fa04a2c56f4d837fd617561b3085b973db40f7 100644 (file)
@@ -24,7 +24,7 @@ COMPLEX_BUILD_TESTS = cacheflush future unload ivarSlide ivarSlidexx \
 
 # a "complex test" has a dedicated testname run rule below
 COMPLEX_RUN_TESTS = gcenforcer_noobjc gcenforcer_nogc gcenforcer_supportsgc \
-       gcenforcer_requiresgc
+       gcenforcer_requiresgc weak-missing weak-not-missing
 
 # `make fail` just fails
 FAIL_TESTS = fail
@@ -172,6 +172,15 @@ future0.out: future0.m future.h test.h Makefile
 future2.out: future2.m future.h future0.out test.h Makefile
        @ $(CC) -dynamiclib future2.m future0.out -o future2.out $(LIBS)
 
+weak.out: weak.m weak2.m weak.h test.h Makefile
+       @ $(CC) weak2.m -dynamiclib -o libweak.dylib $(LIBS)
+       @ $(CC) weak2.m -DEMPTY= -dynamiclib -o libweak_empty.dylib $(LIBS)
+       @ $(CC) weak.m -L. -weak-lweak -o weak.out $(LIBS)
+
+weak-not-missing: weak.out
+       @ $(RUN) ./weak.out $(ERR_CHECK) weak-not-missing $(EAT_ERR)
+weak-missing: weak.out
+       @ DYLD_IMAGE_SUFFIX=_empty $(RUN) ./weak.out $(ERR_CHECK) weak-missing $(EAT_ERR)
 
 CONCURRENT_IN=cc1 cc2 cc3 cc4 cc5 cc6 cc7 cc8 cc9 cc10 cc11 cc12 cc13 cc14 cc15
 CONCURRENT_DYLIBS=$(addsuffix .out,$(CONCURRENT_IN))
diff --git a/test/weak.h b/test/weak.h
new file mode 100644 (file)
index 0000000..a7bb2e5
--- /dev/null
@@ -0,0 +1,38 @@
+#include "test.h"
+#include <objc/runtime.h>
+
+extern int state;
+
+@interface MissingRoot {
+    id isa;
+}
++(void) initialize;
++(Class) class;
++(id) alloc;
+-(id) init;
++(int) method;
+@end
+
+@interface MissingSuper : MissingRoot {
+  @public
+    int ivar;
+}
+@end
+
+
+@interface NotMissingRoot {
+    id isa;
+}
++(void) initialize;
++(Class) class;
++(id) alloc;
+-(id) init;
++(int) method;
+@end
+
+@interface NotMissingSuper : NotMissingRoot {
+  @public
+    int unused[100];
+    int ivar;
+}
+@end
diff --git a/test/weak.m b/test/weak.m
new file mode 100644 (file)
index 0000000..379f364
--- /dev/null
@@ -0,0 +1,294 @@
+#include "test.h"
+#include "weak.h"
+
+// Subclass of superclass that isn't there
+@interface MyMissingSuper : MissingSuper
++(int) method;
+@end
+@implementation MyMissingSuper
++(int) method { return 1+[super method]; }
++(void) load { state++; }
+@end
+
+// Subclass of subclass of superclass that isn't there
+@interface MyMissingSub : MyMissingSuper
++(int) method;
+@end
+@implementation MyMissingSub
++(int) method { return 1+[super method]; }
++(void) load { state++; }
+@end
+
+// Subclass of real superclass 
+@interface MyNotMissingSuper : NotMissingSuper
++(int) method;
+@end
+@implementation MyNotMissingSuper
++(int) method { return 1+[super method]; }
++(void) load { state++; }
+@end
+
+// Subclass of subclass of superclass that isn't there
+@interface MyNotMissingSub : MyNotMissingSuper
++(int) method;
+@end
+@implementation MyNotMissingSub
++(int) method { return 1+[super method]; }
++(void) load { state++; }
+@end
+
+// Categories on all of the above
+@interface MissingRoot (MissingRootExtras)
++(void)load;
++(int) cat_method;
+@end
+@implementation MissingRoot (MissingRootExtras)
++(void)load { state++; }
++(int) cat_method { return 40; }
+@end
+
+@interface MissingSuper (MissingSuperExtras)
++(void)load;
++(int) cat_method;
+@end
+@implementation MissingSuper (MissingSuperExtras)
++(void)load { state++; }
++(int) cat_method { return 1+[super cat_method]; }
+@end
+
+@interface MyMissingSuper (MyMissingSuperExtras)
++(void)load;
++(int) cat_method;
+@end
+@implementation MyMissingSuper (MyMissingSuperExtras)
++(void)load { state++; }
++(int) cat_method { return 1+[super cat_method]; }
+@end
+
+@interface MyMissingSub (MyMissingSubExtras)
++(void)load;
++(int) cat_method;
+@end
+@implementation MyMissingSub (MyMissingSubExtras)
++(void)load { state++; }
++(int) cat_method { return 1+[super cat_method]; }
+@end
+
+
+@interface NotMissingRoot (NotMissingRootExtras)
++(void)load;
++(int) cat_method;
+@end
+@implementation NotMissingRoot (NotMissingRootExtras)
++(void)load { state++; }
++(int) cat_method { return 30; }
+@end
+
+@interface NotMissingSuper (NotMissingSuperExtras)
++(void)load;
++(int) cat_method;
+@end
+@implementation NotMissingSuper (NotMissingSuperExtras)
++(void)load { state++; }
++(int) cat_method { return 1+[super cat_method]; }
+@end
+
+@interface MyNotMissingSuper (MyNotMissingSuperExtras)
++(void)load;
++(int) cat_method;
+@end
+@implementation MyNotMissingSuper (MyNotMissingSuperExtras)
++(void)load { state++; }
++(int) cat_method { return 1+[super cat_method]; }
+@end
+
+@interface MyNotMissingSub (MyNotMissingSubExtras)
++(void)load;
++(int) cat_method;
+@end
+@implementation MyNotMissingSub (MyNotMissingSubExtras)
++(void)load { state++; }
++(int) cat_method { return 1+[super cat_method]; }
+@end
+
+
+static BOOL classInList(Class *classes, const char *name)
+{
+    Class *cp;
+    for (cp = classes; *cp; cp++) {
+        if (0 == strcmp(class_getName(*cp), name)) return YES;
+    }
+    return NO;
+}
+
+static BOOL classInNameList(const char **names, const char *name)
+{
+    const char **cp;
+    for (cp = names; *cp; cp++) {
+        if (0 == strcmp(*cp, name)) return YES;
+    }
+    return NO;
+}
+
+int main()
+{
+    // DYLD_IMAGE_SUFFIX=_empty loads the weak-missing version
+    BOOL weakMissing = NO;
+    if (getenv("DYLD_IMAGE_SUFFIX")) weakMissing = YES;
+
+    // class and category +load methods
+    if (weakMissing) testassert(state == 8);
+    else testassert(state == 16);
+    state = 0;
+
+    // classes
+    testassert([NotMissingRoot class]);
+    testassert([NotMissingSuper class]);
+    testassert([MyNotMissingSuper class]);
+    testassert([MyNotMissingSub class]);
+    if (weakMissing) {
+        testassert(! [MissingRoot class]);
+        testassert(! [MissingSuper class]);
+        testassert(! [MyMissingSuper class]);
+        testassert(! [MyMissingSub class]);
+    } else {
+        testassert([MissingRoot class]);
+        testassert([MissingSuper class]);
+        testassert([MyMissingSuper class]);
+        testassert([MyMissingSub class]);
+    }
+    
+    // objc_getClass
+    testassert(objc_getClass("NotMissingRoot"));
+    testassert(objc_getClass("NotMissingSuper"));
+    testassert(objc_getClass("MyNotMissingSuper"));
+    testassert(objc_getClass("MyNotMissingSub"));
+    if (weakMissing) {
+        testassert(! objc_getClass("MissingRoot"));
+        testassert(! objc_getClass("MissingSuper"));
+        testassert(! objc_getClass("MyMissingSuper"));
+        testassert(! objc_getClass("MyMissingSub"));
+    } else {
+        testassert(objc_getClass("MissingRoot"));
+        testassert(objc_getClass("MissingSuper"));
+        testassert(objc_getClass("MyMissingSuper"));
+        testassert(objc_getClass("MyMissingSub"));
+    }
+
+    // class list
+    Class classes[100];
+    int count = objc_getClassList(classes, 99);
+    classes[count] = NULL;
+    testassert(classInList(classes, "NotMissingRoot"));
+    testassert(classInList(classes, "NotMissingSuper"));
+    testassert(classInList(classes, "MyNotMissingSuper"));
+    testassert(classInList(classes, "MyNotMissingSub"));
+    if (weakMissing) {
+        testassert(! classInList(classes, "MissingRoot"));
+        testassert(! classInList(classes, "MissingSuper"));
+        testassert(! classInList(classes, "MyMissingSuper"));
+        testassert(! classInList(classes, "MyMissingSub"));
+    } else {
+        testassert(classInList(classes, "MissingRoot"));
+        testassert(classInList(classes, "MissingSuper"));
+        testassert(classInList(classes, "MyMissingSuper"));
+        testassert(classInList(classes, "MyMissingSub"));
+    }
+
+    // class name list
+    const char *image = class_getImageName(objc_getClass("NotMissingRoot"));
+    testassert(image);
+    const char **names = objc_copyClassNamesForImage(image, NULL);
+    testassert(names);
+    testassert(classInNameList(names, "NotMissingRoot"));
+    testassert(classInNameList(names, "NotMissingSuper"));
+    if (weakMissing) {
+        testassert(! classInNameList(names, "MissingRoot"));
+        testassert(! classInNameList(names, "MissingSuper"));
+    } else {
+        testassert(classInNameList(names, "MissingRoot"));
+        testassert(classInNameList(names, "MissingSuper"));
+    }
+    free(names);
+
+    image = class_getImageName(objc_getClass("MyNotMissingSub"));
+    testassert(image);
+    names = objc_copyClassNamesForImage(image, NULL);
+    testassert(names);
+    testassert(classInNameList(names, "MyNotMissingSuper"));
+    testassert(classInNameList(names, "MyNotMissingSub"));
+    if (weakMissing) {
+        testassert(! classInNameList(names, "MyMissingSuper"));
+        testassert(! classInNameList(names, "MyMissingSub"));
+    } else {
+        testassert(classInNameList(names, "MyMissingSuper"));
+        testassert(classInNameList(names, "MyMissingSub"));
+    }
+    free(names);
+    
+    // methods
+    testassert(20 == [NotMissingRoot method]);
+    testassert(21 == [NotMissingSuper method]);
+    testassert(22 == [MyNotMissingSuper method]);
+    testassert(23 == [MyNotMissingSub method]);
+    if (weakMissing) {
+        testassert(0 == [MissingRoot method]);
+        testassert(0 == [MissingSuper method]);
+        testassert(0 == [MyMissingSuper method]);
+        testassert(0 == [MyMissingSub method]);
+    } else {
+        testassert(10 == [MissingRoot method]);
+        testassert(11 == [MissingSuper method]);
+        testassert(12 == [MyMissingSuper method]);
+        testassert(13 == [MyMissingSub method]);
+    }
+    
+    // category methods
+    testassert(30 == [NotMissingRoot cat_method]);
+    testassert(31 == [NotMissingSuper cat_method]);
+    testassert(32 == [MyNotMissingSuper cat_method]);
+    testassert(33 == [MyNotMissingSub cat_method]);
+    if (weakMissing) {
+        testassert(0 == [MissingRoot cat_method]);
+        testassert(0 == [MissingSuper cat_method]);
+        testassert(0 == [MyMissingSuper cat_method]);
+        testassert(0 == [MyMissingSub cat_method]);
+    } else {
+        testassert(40 == [MissingRoot cat_method]);
+        testassert(41 == [MissingSuper cat_method]);
+        testassert(42 == [MyMissingSuper cat_method]);
+        testassert(43 == [MyMissingSub cat_method]);
+    }
+
+    // allocations and ivars
+    id obj;
+    NotMissingSuper *obj2;
+    MissingSuper *obj3;
+    testassert((obj = [[NotMissingRoot alloc] init])); 
+    free(obj);
+    testassert((obj2 = [[NotMissingSuper alloc] init]));
+    testassert(obj2->ivar == 200); free(obj2);
+    testassert((obj2 = [[MyNotMissingSuper alloc] init]));
+    testassert(obj2->ivar == 200); free(obj2);
+    testassert((obj2 = [[MyNotMissingSub alloc] init]));
+    testassert(obj2->ivar == 200); free(obj2);
+    if (weakMissing) {
+        testassert(! [[MissingRoot alloc] init]);
+        testassert(! [[MissingSuper alloc] init]);
+        testassert(! [[MyMissingSuper alloc] init]);
+        testassert(! [[MyMissingSub alloc] init]);
+    } else {
+        testassert((obj = [[MissingRoot alloc] init])); 
+        free(obj);
+        testassert((obj3 = [[MissingSuper alloc] init])); 
+        testassert(obj3->ivar == 100); free(obj3);
+        testassert((obj3 = [[MyMissingSuper alloc] init])); 
+        testassert(obj3->ivar == 100); free(obj3);
+        testassert((obj3 = [[MyMissingSub alloc] init])); 
+        testassert(obj3->ivar == 100); free(obj3);
+    }
+
+    if (weakMissing) succeed("weak-missing");
+    else succeed("weak-not-missing");
+    return 0;
+}
diff --git a/test/weak2.m b/test/weak2.m
new file mode 100644 (file)
index 0000000..ab17752
--- /dev/null
@@ -0,0 +1,39 @@
+#include "test.h"
+#include "weak.h"
+
+int state = 0;
+
+#if !defined(EMPTY)
+
+@implementation MissingRoot
++(void) initialize { } 
++(Class) class { return self; }
++(id) alloc { return class_createInstance(self, 0); }
+-(id) init { return self; }
++(int) method { return 10; }
++(void) load { state++; }
+@end
+
+@implementation MissingSuper
++(int) method { return 1+[super method]; }
+-(id) init { self = [super init]; ivar = 100; return self; }
++(void) load { state++; }
+@end
+
+#endif
+
+@implementation NotMissingRoot
++(void) initialize { } 
++(Class) class { return self; }
++(id) alloc { return class_createInstance(self, 0); }
+-(id) init { return self; }
++(int) method { return 20; }
++(void) load { state++; }
+@end
+
+@implementation NotMissingSuper
++(int) method { return 1+[super method]; }
+-(id) init { self = [super init]; ivar = 200; return self; }
++(void) load { state++; }
+@end
+