123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- // TEST_CONFIG
- #include "test.h"
- #include "testroot.i"
- @interface OrdinaryClass : TestRoot @end
- @implementation OrdinaryClass @end
- objc_hook_getClass OnePreviousHook;
- static int HookOneCalls = 0;
- BOOL GetClassHookOne(const char *name, Class *outClass)
- {
- HookOneCalls++;
- if (0 == strcmp(name, "TwoClass")) {
- fail("other hook should have handled this already");
- } else if (0 == strcmp(name, "OrdinaryClass")) {
- fail("runtime should have handled this already");
- } else if (0 == strcmp(name, "OneClass")) {
- Class cls = objc_allocateClassPair([OrdinaryClass class], "OneClass", 0);
- objc_registerClassPair(cls);
- *outClass = cls;
- return YES;
- } else {
- return OnePreviousHook(name, outClass);
- }
- }
- objc_hook_getClass TwoPreviousHook;
- static int HookTwoCalls = 0;
- BOOL GetClassHookTwo(const char *name, Class *outClass)
- {
- HookTwoCalls++;
- if (0 == strcmp(name, "OrdinaryClass")) {
- fail("runtime should have handled this already");
- } else if (0 == strcmp(name, "TwoClass")) {
- Class cls = objc_allocateClassPair([OrdinaryClass class], "TwoClass", 0);
- objc_registerClassPair(cls);
- *outClass = cls;
- return YES;
- } else {
- return TwoPreviousHook(name, outClass);
- }
- }
- objc_hook_getClass ThreePreviousHook;
- static int HookThreeCalls = 0;
- #define MAXDEPTH 100
- BOOL GetClassHookThree(const char *name, Class *outClass)
- {
- // Re-entrant hook test.
- // libobjc must prevent re-entrancy when a getClass
- // hook provokes another getClass call.
- static int depth = 0;
- static char *names[MAXDEPTH] = {0};
- if (depth < MAXDEPTH) {
- // Re-entrantly call objc_getClass() with a new class name.
- if (!names[depth]) asprintf(&names[depth], "Reentrant%d", depth);
- const char *reentrantName = names[depth];
- depth++;
- (void)objc_getClass(reentrantName);
- depth--;
- } else if (depth == MAXDEPTH) {
- // We now have maxdepth getClass hooks stacked up.
- // Call objc_getClass() on all of those names a second time.
- // None of those lookups should call this hook again.
- HookThreeCalls++;
- depth = -1;
- for (int i = 0; i < MAXDEPTH; i++) {
- testassert(!objc_getClass(names[i]));
- }
- depth = MAXDEPTH;
- } else {
- fail("reentrancy protection failed");
- }
- // Chain to the previous hook after all of the reentrancy is unwound.
- if (depth == 0) {
- return ThreePreviousHook(name, outClass);
- } else {
- return NO;
- }
- }
- void testLookup(const char *name, int expectedHookOneCalls,
- int expectedHookTwoCalls, int expectedHookThreeCalls)
- {
- HookOneCalls = HookTwoCalls = HookThreeCalls = 0;
- Class cls = objc_getClass(name);
- testassert(HookOneCalls == expectedHookOneCalls &&
- HookTwoCalls == expectedHookTwoCalls &&
- HookThreeCalls == expectedHookThreeCalls);
- testassert(cls);
- testassert(0 == strcmp(class_getName(cls), name));
- testassert(cls == [cls self]);
- }
- int main()
- {
- testassert(objc_getClass("OrdinaryClass"));
- testassert(!objc_getClass("OneClass"));
- testassert(!objc_getClass("TwoClass"));
- testassert(!objc_getClass("NoSuchClass"));
- objc_setHook_getClass(GetClassHookOne, &OnePreviousHook);
- objc_setHook_getClass(GetClassHookTwo, &TwoPreviousHook);
- objc_setHook_getClass(GetClassHookThree, &ThreePreviousHook);
- // invocation order: HookThree -> Hook Two -> Hook One
- HookOneCalls = HookTwoCalls = HookThreeCalls = 0;
- testassert(!objc_getClass("NoSuchClass"));
- testassert(HookOneCalls == 1 && HookTwoCalls == 1 && HookThreeCalls == 1);
- testLookup("OneClass", 1, 1, 1);
- testLookup("TwoClass", 0, 1, 1);
- testLookup("OrdinaryClass", 0, 0, 0);
- // Check again. No hooks should be needed this time.
- testLookup("OneClass", 0, 0, 0);
- testLookup("TwoClass", 0, 0, 0);
- testLookup("OrdinaryClass", 0, 0, 0);
- succeed(__FILE__);
- }
|