addMethod.m 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. // TEST_CONFIG
  2. #include "test.h"
  3. #include "testroot.i"
  4. #include <objc/runtime.h>
  5. @interface Super : TestRoot @end
  6. @implementation Super
  7. -(int)superMethod { return 0; }
  8. -(int)superMethod2 { return 0; }
  9. -(int)bothMethod { return 0; }
  10. @end
  11. @interface Sub : Super @end
  12. @implementation Sub
  13. -(int)subMethod { return 0; }
  14. -(int)bothMethod { return 0; }
  15. @end
  16. @interface Sub2 : Super @end
  17. @implementation Sub2
  18. -(int)subMethod { return 0; }
  19. -(int)bothMethod { return 0; }
  20. @end
  21. id fn(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) { return nil; }
  22. #define TWICE(x) x; x
  23. int main()
  24. {
  25. IMP superMethodFromSuper = class_getMethodImplementation([Super class], @selector(superMethod));
  26. IMP superMethod2FromSuper = class_getMethodImplementation([Super class], @selector(superMethod2));
  27. IMP bothMethodFromSuper = class_getMethodImplementation([Super class], @selector(bothMethod));
  28. IMP subMethodFromSub = class_getMethodImplementation([Sub class], @selector(subMethod));
  29. IMP bothMethodFromSub = class_getMethodImplementation([Sub class], @selector(bothMethod));
  30. IMP subMethodFromSub2 = class_getMethodImplementation([Sub2 class], @selector(subMethod));
  31. IMP bothMethodFromSub2 = class_getMethodImplementation([Sub2 class], @selector(bothMethod));
  32. testassert(superMethodFromSuper);
  33. testassert(superMethod2FromSuper);
  34. testassert(bothMethodFromSuper);
  35. testassert(subMethodFromSub);
  36. testassert(bothMethodFromSub);
  37. testassert(subMethodFromSub2);
  38. testassert(bothMethodFromSub2);
  39. BOOL ok;
  40. IMP imp;
  41. // Each method lookup below is performed twice, with the intent
  42. // that at least one of them will be a cache lookup.
  43. // class_addMethod doesn't replace existing implementations
  44. ok = class_addMethod([Super class], @selector(superMethod), (IMP)fn, NULL);
  45. testassert(!ok);
  46. TWICE(testassert(class_getMethodImplementation([Super class], @selector(superMethod)) == superMethodFromSuper));
  47. // class_addMethod does override superclass implementations
  48. ok = class_addMethod([Sub class], @selector(superMethod), (IMP)fn, NULL);
  49. testassert(ok);
  50. TWICE(testassert(class_getMethodImplementation([Sub class], @selector(superMethod)) == (IMP)fn));
  51. // class_addMethod does add root implementations
  52. ok = class_addMethod([Super class], @selector(superMethodNew2), (IMP)fn, NULL);
  53. testassert(ok);
  54. TWICE(testassert(class_getMethodImplementation([Super class], @selector(superMethodNew2)) == (IMP)fn));
  55. TWICE(testassert(class_getMethodImplementation([Sub class], @selector(superMethodNew2)) == (IMP)fn));
  56. // bincompat: some apps call class_addMethod() and class_replaceMethod() with a nil IMP
  57. // This has the effect of covering the superclass implementation.
  58. // fixme change this, possibly behind a linked-on-or-after check
  59. #pragma clang diagnostic push
  60. #pragma clang diagnostic ignored "-Wnonnull"
  61. ok = class_addMethod([Sub class], @selector(superMethod2), nil, NULL);
  62. #pragma clang diagnostic pop
  63. testassert(ok);
  64. TWICE(testassert(class_getMethodImplementation([Sub class], @selector(superMethod2)) == (IMP)_objc_msgForward));
  65. // class_replaceMethod does add new implementations,
  66. // returning NULL if super has an implementation
  67. imp = class_replaceMethod([Sub2 class], @selector(superMethod), (IMP)fn, NULL);
  68. testassert(imp == NULL);
  69. TWICE(testassert(class_getMethodImplementation([Sub2 class], @selector(superMethod)) == (IMP)fn));
  70. // class_replaceMethod does add new implementations,
  71. // returning NULL if super has no implementation
  72. imp = class_replaceMethod([Sub2 class], @selector(subMethodNew), (IMP)fn, NULL);
  73. testassert(imp == NULL);
  74. TWICE(testassert(class_getMethodImplementation([Sub2 class], @selector(subMethodNew)) == (IMP)fn));
  75. // class_replaceMethod does add new implemetations
  76. // returning NULL if there is no super class
  77. imp = class_replaceMethod([Super class], @selector(superMethodNew), (IMP)fn, NULL);
  78. testassert(imp == NULL);
  79. TWICE(testassert(class_getMethodImplementation([Super class], @selector(superMethodNew)) == (IMP)fn));
  80. // class_replaceMethod does replace existing implementations,
  81. // returning existing implementation (regardless of super)
  82. imp = class_replaceMethod([Sub2 class], @selector(subMethod), (IMP)fn, NULL);
  83. testassert(imp == subMethodFromSub2);
  84. TWICE(testassert(class_getMethodImplementation([Sub2 class], @selector(subMethod)) == (IMP)fn));
  85. // class_replaceMethod does replace existing implemetations,
  86. // returning existing implementation (regardless of super)
  87. imp = class_replaceMethod([Sub2 class], @selector(bothMethod), (IMP)fn, NULL);
  88. testassert(imp == bothMethodFromSub2);
  89. TWICE(testassert(class_getMethodImplementation([Sub2 class], @selector(bothMethod)) == (IMP)fn));
  90. // class_replaceMethod does replace existing implemetations,
  91. // returning existing implementation (regardless of super)
  92. imp = class_replaceMethod([Super class], @selector(superMethod), (IMP)fn, NULL);
  93. testassert(imp == superMethodFromSuper);
  94. TWICE(testassert(class_getMethodImplementation([Super class], @selector(superMethod)) == (IMP)fn));
  95. // fixme actually try calling them
  96. succeed(__FILE__);
  97. }