6 @interface OrdinaryClass : TestRoot @end
7 @implementation OrdinaryClass @end
9 objc_hook_getClass OnePreviousHook;
10 static int HookOneCalls = 0;
11 BOOL GetClassHookOne(const char *name, Class *outClass)
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);
24 return OnePreviousHook(name, outClass);
28 objc_hook_getClass TwoPreviousHook;
29 static int HookTwoCalls = 0;
30 BOOL GetClassHookTwo(const char *name, Class *outClass)
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);
41 return TwoPreviousHook(name, outClass);
46 objc_hook_getClass ThreePreviousHook;
47 static int HookThreeCalls = 0;
49 BOOL GetClassHookThree(const char *name, Class *outClass)
51 // Re-entrant hook test.
52 // libobjc must prevent re-entrancy when a getClass
53 // hook provokes another getClass call.
56 static char *names[MAXDEPTH] = {0};
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];
63 (void)objc_getClass(reentrantName);
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.
71 for (int i = 0; i < MAXDEPTH; i++) {
72 testassert(!objc_getClass(names[i]));
76 fail("reentrancy protection failed");
79 // Chain to the previous hook after all of the reentrancy is unwound.
81 return ThreePreviousHook(name, outClass);
88 void testLookup(const char *name, int expectedHookOneCalls,
89 int expectedHookTwoCalls, int expectedHookThreeCalls)
91 HookOneCalls = HookTwoCalls = HookThreeCalls = 0;
92 Class cls = objc_getClass(name);
93 testassert(HookOneCalls == expectedHookOneCalls &&
94 HookTwoCalls == expectedHookTwoCalls &&
95 HookThreeCalls == expectedHookThreeCalls);
97 testassert(0 == strcmp(class_getName(cls), name));
98 testassert(cls == [cls self]);
103 testassert(objc_getClass("OrdinaryClass"));
104 testassert(!objc_getClass("OneClass"));
105 testassert(!objc_getClass("TwoClass"));
106 testassert(!objc_getClass("NoSuchClass"));
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
113 HookOneCalls = HookTwoCalls = HookThreeCalls = 0;
114 testassert(!objc_getClass("NoSuchClass"));
115 testassert(HookOneCalls == 1 && HookTwoCalls == 1 && HookThreeCalls == 1);
117 testLookup("OneClass", 1, 1, 1);
118 testLookup("TwoClass", 0, 1, 1);
119 testLookup("OrdinaryClass", 0, 0, 0);
121 // Check again. No hooks should be needed this time.
123 testLookup("OneClass", 0, 0, 0);
124 testLookup("TwoClass", 0, 0, 0);
125 testLookup("OrdinaryClass", 0, 0, 0);