getClassHook.m 4.0 KB

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