/core/externals/update-engine/externals/google-toolbox-for-mac/DebugUtils/GTMMethodCheck.m

http://macfuse.googlecode.com/ · Objective C · 174 lines · 101 code · 8 blank · 65 comment · 42 complexity · 163eb294952c590969a271bd1b6bc3dd MD5 · raw file

  1. //
  2. // GTMMethodCheck.m
  3. //
  4. // Copyright 2006-2008 Google Inc.
  5. //
  6. // Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7. // use this file except in compliance with the License. You may obtain a copy
  8. // of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  14. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  15. // License for the specific language governing permissions and limitations under
  16. // the License.
  17. //
  18. // Don't want any of this in release builds
  19. #ifdef DEBUG
  20. #import "GTMDefines.h"
  21. #import "GTMMethodCheck.h"
  22. #import "GTMObjC2Runtime.h"
  23. #import <dlfcn.h>
  24. // Checks to see if the cls passed in (or one of it's superclasses) conforms
  25. // to NSObject protocol. Inheriting from NSObject is the easiest way to do this
  26. // but not all classes (i.e. NSProxy) inherit from NSObject. Also, some classes
  27. // inherit from Object instead of NSObject which is fine, and we'll count as
  28. // conforming to NSObject for our needs.
  29. static BOOL ConformsToNSObjectProtocol(Class cls) {
  30. // If we get nil, obviously doesn't conform.
  31. if (!cls) return NO;
  32. const char *className = class_getName(cls);
  33. if (!className) return NO;
  34. // We're going to assume that all Apple classes will work
  35. // (and aren't being checked)
  36. // Note to apple: why doesn't obj-c have real namespaces instead of two
  37. // letter hacks? If you name your own classes starting with NS this won't
  38. // work for you.
  39. // Some classes (like _NSZombie) start with _NS.
  40. // On Leopard we have to look for CFObject as well.
  41. // On iPhone we check Object as well
  42. if ((strncmp(className, "NS", 2) == 0)
  43. || (strncmp(className, "_NS", 3) == 0)
  44. || (strncmp(className, "__NS", 4) == 0)
  45. || (strcmp(className, "CFObject") == 0)
  46. || (strcmp(className, "__IncompleteProtocol") == 0)
  47. || (strcmp(className, "__ARCLite__") == 0)
  48. || (strcmp(className, "WebMIMETypeRegistry") == 0)
  49. #if GTM_IPHONE_SDK
  50. || (strcmp(className, "Object") == 0)
  51. || (strcmp(className, "UIKeyboardCandidateUtilities") == 0)
  52. #endif
  53. ) {
  54. return YES;
  55. }
  56. // iPhone and Mac OS X 10.8 with Obj-C 2 SDKs do not define the |Object|
  57. // class, so we instead test for the |NSObject| class.
  58. #if GTM_IPHONE_SDK || \
  59. (__OBJC2__ && defined(MAC_OS_X_VERSION_10_8) && \
  60. MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8)
  61. // Iterate through all the protocols |cls| supports looking for NSObject.
  62. if (cls == [NSObject class]
  63. || class_conformsToProtocol(cls, @protocol(NSObject))) {
  64. return YES;
  65. }
  66. #else
  67. // Iterate through all the protocols |cls| supports looking for NSObject.
  68. if (cls == [Object class]
  69. || class_conformsToProtocol(cls, @protocol(NSObject))) {
  70. return YES;
  71. }
  72. #endif
  73. // Recursively check the superclasses.
  74. return ConformsToNSObjectProtocol(class_getSuperclass(cls));
  75. }
  76. void GTMMethodCheckMethodChecker(void) {
  77. // Run through all the classes looking for class methods that are
  78. // prefixed with xxGMMethodCheckMethod. If it finds one, it calls it.
  79. // See GTMMethodCheck.h to see what it does.
  80. #if !defined(__has_feature) || !__has_feature(objc_arc)
  81. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  82. #else
  83. @autoreleasepool {
  84. #endif
  85. int numClasses = 0;
  86. int newNumClasses = objc_getClassList(NULL, 0);
  87. int i;
  88. Class *classes = NULL;
  89. while (numClasses < newNumClasses) {
  90. numClasses = newNumClasses;
  91. classes = (Class *)realloc(classes, sizeof(Class) * numClasses);
  92. _GTMDevAssert(classes, @"Unable to allocate memory for classes");
  93. newNumClasses = objc_getClassList(classes, numClasses);
  94. }
  95. for (i = 0; i < numClasses && classes; ++i) {
  96. Class cls = classes[i];
  97. // Since we are directly calling objc_msgSend, we need to conform to
  98. // @protocol(NSObject), or else we will tumble into a _objc_msgForward
  99. // recursive loop when we try and call a function by name.
  100. if (!ConformsToNSObjectProtocol(cls)) {
  101. // COV_NF_START
  102. _GTMDevLog(@"GTMMethodCheckMethodChecker: Class %s does not conform to "
  103. "@protocol(NSObject), so won't be checked",
  104. class_getName(cls));
  105. continue;
  106. // COV_NF_END
  107. }
  108. // Since we are looking for a class method (+xxGMMethodCheckMethod...)
  109. // we need to query the isa pointer to see what methods it support, but
  110. // send the method (if it's supported) to the class itself.
  111. unsigned int count;
  112. Class metaClass = objc_getMetaClass(class_getName(cls));
  113. Method *methods = class_copyMethodList(metaClass, &count);
  114. unsigned int j;
  115. for (j = 0; j < count; ++j) {
  116. SEL selector = method_getName(methods[j]);
  117. const char *name = sel_getName(selector);
  118. if (strstr(name, "xxGTMMethodCheckMethod") == name) {
  119. // Check to make sure that the method we are checking comes
  120. // from the same image that we are in. Since GTMMethodCheckMethodChecker
  121. // is not exported, we should always find the copy in our local
  122. // image. We compare the address of it's image with the address of
  123. // the image which implements the method we want to check. If
  124. // they match we continue. This does two things:
  125. // a) minimizes the amount of calls we make to the xxxGTMMethodCheck
  126. // methods. They should only be called once.
  127. // b) prevents initializers for various classes being called too early
  128. Dl_info methodCheckerInfo;
  129. if (!dladdr(GTMMethodCheckMethodChecker,
  130. &methodCheckerInfo)) {
  131. // COV_NF_START
  132. // Don't know how to force this case in a unittest.
  133. // Certainly hope we never see it.
  134. _GTMDevLog(@"GTMMethodCheckMethodChecker: Unable to get dladdr info "
  135. "for GTMMethodCheckMethodChecker while introspecting +[%s %s]]",
  136. class_getName(cls), name);
  137. continue;
  138. // COV_NF_END
  139. }
  140. Dl_info methodInfo;
  141. if (!dladdr(method_getImplementation(methods[j]),
  142. &methodInfo)) {
  143. // COV_NF_START
  144. // Don't know how to force this case in a unittest
  145. // Certainly hope we never see it.
  146. _GTMDevLog(@"GTMMethodCheckMethodChecker: Unable to get dladdr info "
  147. "for %s while introspecting +[%s %s]]", name,
  148. class_getName(cls), name);
  149. continue;
  150. // COV_NF_END
  151. }
  152. if (methodCheckerInfo.dli_fbase == methodInfo.dli_fbase) {
  153. objc_msgSend(cls, selector);
  154. }
  155. }
  156. }
  157. free(methods);
  158. }
  159. free(classes);
  160. #if !defined(__has_feature) || !__has_feature(objc_arc)
  161. [pool drain];
  162. #else
  163. } // @autoreleasepool
  164. #endif
  165. }
  166. #endif // DEBUG