1/*
2TEST_BUILD
3    $C{COMPILE}   $DIR/unload4.m -o unload4.dylib -dynamiclib
4    $C{COMPILE_C} $DIR/unload3.c -o unload3.dylib -dynamiclib
5    $C{COMPILE}   $DIR/unload2.m -o unload2.bundle -bundle
6    $C{COMPILE}   $DIR/unload.m -o unload.out
7END
8 */
9
10#include "test.h"
11#include <objc/runtime.h>
12#include <dlfcn.h>
13#include <unistd.h>
14
15#include "unload.h"
16
17#if __has_feature(objc_arc)
18
19int main()
20{
21    testwarn("rdar://11368528 confused by Foundation");
22    succeed(__FILE__);
23}
24
25#else
26
27static id forward_handler(void)
28{
29    return 0;
30}
31
32static BOOL hasName(const char * const *names, const char *query)
33{
34    const char *name;
35    while ((name = *names++)) {
36        if (strstr(name, query)) return YES;
37    }
38
39    return NO;
40}
41
42void cycle(void)
43{
44    int i;
45    char buf[100];
46    unsigned int imageCount, imageCount0;
47    const char **names;
48    const char *name;
49
50    names = objc_copyImageNames(&imageCount0);
51    testassert(names);
52    free(names);
53
54    void *bundle = dlopen("unload2.bundle", RTLD_LAZY);
55    testassert(bundle);
56
57    names = objc_copyImageNames(&imageCount);
58    testassert(names);
59    testassert(imageCount == imageCount0 + 1);
60    testassert(hasName(names, "unload2.bundle"));
61    free(names);
62
63    Class small = objc_getClass("SmallClass");
64    Class big = objc_getClass("BigClass");
65    testassert(small);
66    testassert(big);
67
68    name = class_getImageName(small);
69    testassert(name);
70    testassert(strstr(name, "unload2.bundle"));
71    name = class_getImageName(big);
72    testassert(name);
73    testassert(strstr(name, "unload2.bundle"));
74
75    id o1 = [small new];
76    id o2 = [big new];
77    testassert(o1);
78    testassert(o2);
79
80    // give BigClass and BigClass->isa large method caches (4692641)
81    for (i = 0; i < 10000; i++) {
82        sprintf(buf, "method_%d", i);
83        SEL sel = sel_registerName(buf);
84        ((void(*)(id, SEL))objc_msgSend)(o2, sel);
85        ((void(*)(id, SEL))objc_msgSend)(object_getClass(o2), sel);
86    }
87
88    RELEASE_VAR(o1);
89    RELEASE_VAR(o2);
90
91    testcollect();
92
93    int err = dlclose(bundle);
94    testassert(err == 0);
95    err = dlclose(bundle);
96    testassert(err == -1);  // already closed
97
98    testassert(objc_getClass("SmallClass") == NULL);
99    testassert(objc_getClass("BigClass") == NULL);
100
101    names = objc_copyImageNames(&imageCount);
102    testassert(names);
103    testassert(imageCount == imageCount0);
104    testassert(! hasName(names, "unload2.bundle"));
105    free(names);
106
107    // these selectors came from the bundle
108    testassert(0 == strcmp("unload2_instance_method", sel_getName(sel_registerName("unload2_instance_method"))));
109    testassert(0 == strcmp("unload2_category_method", sel_getName(sel_registerName("unload2_category_method"))));
110}
111
112int main()
113{
114    // fixme object_dispose() not aggressive enough?
115    if (objc_collectingEnabled()) succeed(__FILE__);
116
117    objc_setForwardHandler((void*)&forward_handler, (void*)&forward_handler);
118
119#if defined(__arm__)
120    int count = 10;
121#else
122    int count = is_guardmalloc() ? 10 : 100;
123#endif
124
125    cycle();
126#if __LP64__
127    // fixme heap use goes up 512 bytes after the 2nd cycle only - bad or not?
128    cycle();
129#endif
130
131    leak_mark();
132    while (count--) {
133        cycle();
134    }
135    leak_check(0);
136
137    // 5359412 Make sure dylibs with nothing other than image_info can close
138    void *dylib = dlopen("unload3.dylib", RTLD_LAZY);
139    testassert(dylib);
140    int err = dlclose(dylib);
141    testassert(err == 0);
142    err = dlclose(dylib);
143    testassert(err == -1);  // already closed
144
145    // Make sure dylibs with real objc content cannot close
146    dylib = dlopen("unload4.dylib", RTLD_LAZY);
147    testassert(dylib);
148    err = dlclose(dylib);
149    testassert(err == 0);
150    err = dlclose(dylib);
151    testassert(err == 0);   // dlopen from libobjc itself
152    err = dlclose(dylib);
153    testassert(err == -1);  // already closed
154
155    succeed(__FILE__);
156}
157
158#endif
159