PageRenderTime 37ms CodeModel.GetById 2ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 0ms

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