]> git.saurik.com Git - apple/objc4.git/blob - test/getClassHook.m
objc4-787.1.tar.gz
[apple/objc4.git] / test / getClassHook.m
1 // TEST_CONFIG
2
3 #include "test.h"
4 #include "testroot.i"
5
6 @interface OrdinaryClass : TestRoot @end
7 @implementation OrdinaryClass @end
8
9 objc_hook_getClass OnePreviousHook;
10 static int HookOneCalls = 0;
11 BOOL GetClassHookOne(const char *name, Class *outClass)
12 {
13 HookOneCalls++;
14 if (0 == strcmp(name, "TwoClass")) {
15 fail("other hook should have handled this already");
16 } else if (0 == strcmp(name, "OrdinaryClass")) {
17 fail("runtime should have handled this already");
18 } else if (0 == strcmp(name, "OneClass")) {
19 Class cls = objc_allocateClassPair([OrdinaryClass class], "OneClass", 0);
20 objc_registerClassPair(cls);
21 *outClass = cls;
22 return YES;
23 } else {
24 return OnePreviousHook(name, outClass);
25 }
26 }
27
28 objc_hook_getClass TwoPreviousHook;
29 static int HookTwoCalls = 0;
30 BOOL GetClassHookTwo(const char *name, Class *outClass)
31 {
32 HookTwoCalls++;
33 if (0 == strcmp(name, "OrdinaryClass")) {
34 fail("runtime should have handled this already");
35 } else if (0 == strcmp(name, "TwoClass")) {
36 Class cls = objc_allocateClassPair([OrdinaryClass class], "TwoClass", 0);
37 objc_registerClassPair(cls);
38 *outClass = cls;
39 return YES;
40 } else {
41 return TwoPreviousHook(name, outClass);
42 }
43 }
44
45
46 objc_hook_getClass ThreePreviousHook;
47 static int HookThreeCalls = 0;
48 #define MAXDEPTH 100
49 BOOL GetClassHookThree(const char *name, Class *outClass)
50 {
51 // Re-entrant hook test.
52 // libobjc must prevent re-entrancy when a getClass
53 // hook provokes another getClass call.
54
55 static int depth = 0;
56 static char *names[MAXDEPTH] = {0};
57
58 if (depth < MAXDEPTH) {
59 // Re-entrantly call objc_getClass() with a new class name.
60 if (!names[depth]) asprintf(&names[depth], "Reentrant%d", depth);
61 const char *reentrantName = names[depth];
62 depth++;
63 (void)objc_getClass(reentrantName);
64 depth--;
65 } else if (depth == MAXDEPTH) {
66 // We now have maxdepth getClass hooks stacked up.
67 // Call objc_getClass() on all of those names a second time.
68 // None of those lookups should call this hook again.
69 HookThreeCalls++;
70 depth = -1;
71 for (int i = 0; i < MAXDEPTH; i++) {
72 testassert(!objc_getClass(names[i]));
73 }
74 depth = MAXDEPTH;
75 } else {
76 fail("reentrancy protection failed");
77 }
78
79 // Chain to the previous hook after all of the reentrancy is unwound.
80 if (depth == 0) {
81 return ThreePreviousHook(name, outClass);
82 } else {
83 return NO;
84 }
85 }
86
87
88 void testLookup(const char *name, int expectedHookOneCalls,
89 int expectedHookTwoCalls, int expectedHookThreeCalls)
90 {
91 HookOneCalls = HookTwoCalls = HookThreeCalls = 0;
92 Class cls = objc_getClass(name);
93 testassert(HookOneCalls == expectedHookOneCalls &&
94 HookTwoCalls == expectedHookTwoCalls &&
95 HookThreeCalls == expectedHookThreeCalls);
96 testassert(cls);
97 testassert(0 == strcmp(class_getName(cls), name));
98 testassert(cls == [cls self]);
99 }
100
101 int main()
102 {
103 testassert(objc_getClass("OrdinaryClass"));
104 testassert(!objc_getClass("OneClass"));
105 testassert(!objc_getClass("TwoClass"));
106 testassert(!objc_getClass("NoSuchClass"));
107
108 objc_setHook_getClass(GetClassHookOne, &OnePreviousHook);
109 objc_setHook_getClass(GetClassHookTwo, &TwoPreviousHook);
110 objc_setHook_getClass(GetClassHookThree, &ThreePreviousHook);
111 // invocation order: HookThree -> Hook Two -> Hook One
112
113 HookOneCalls = HookTwoCalls = HookThreeCalls = 0;
114 testassert(!objc_getClass("NoSuchClass"));
115 testassert(HookOneCalls == 1 && HookTwoCalls == 1 && HookThreeCalls == 1);
116
117 testLookup("OneClass", 1, 1, 1);
118 testLookup("TwoClass", 0, 1, 1);
119 testLookup("OrdinaryClass", 0, 0, 0);
120
121 // Check again. No hooks should be needed this time.
122
123 testLookup("OneClass", 0, 0, 0);
124 testLookup("TwoClass", 0, 0, 0);
125 testLookup("OrdinaryClass", 0, 0, 0);
126
127 succeed(__FILE__);
128 }