diff options
-rw-r--r-- | DebugUtils/GTMMethodCheck.h | 69 | ||||
-rw-r--r-- | DebugUtils/GTMMethodCheck.m | 102 | ||||
-rw-r--r-- | DebugUtils/GTMMethodCheckTest.m | 39 | ||||
-rw-r--r-- | GoogleToolboxForMac.podspec | 7 |
4 files changed, 30 insertions, 187 deletions
diff --git a/DebugUtils/GTMMethodCheck.h b/DebugUtils/GTMMethodCheck.h index 7b0919b..9fad81d 100644 --- a/DebugUtils/GTMMethodCheck.h +++ b/DebugUtils/GTMMethodCheck.h @@ -1,14 +1,14 @@ // // GTMMethodCheck.h -// -// Copyright 2006-2008 Google Inc. +// +// Copyright 2006-2016 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy // of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -21,66 +21,47 @@ #import <sysexits.h> /// A macro for enforcing debug time checks to make sure all required methods are linked in -// +// // When using categories, it can be very easy to forget to include the -// implementation of a category. +// implementation of a category. // Let's say you had a class foo that depended on method bar of class baz, and // method bar was implemented as a member of a category. // You could add the following code: -// @implementation foo +// // GTM_METHOD_CHECK(baz, bar) -// @end +// // and the code would check to make sure baz was implemented just before main // was called. This works for both dynamic libraries, and executables. // -// Classes (or one of their superclasses) being checked must conform to the -// NSObject protocol. We will check this, and spit out a warning if a class does -// not conform to NSObject. // // This is not compiled into release builds. #ifdef DEBUG -#ifdef __cplusplus -extern "C" { -#endif - -// If you get an error for GTMMethodCheckMethodChecker not being defined, -// you need to link in GTMMethodCheck.m. We keep it hidden so that we can have -// it living in several separate images without conflict. -// Functions with the ((constructor)) attribute are called after all +loads -// have been called. See "Initializing Objective-C Classes" in -// http://developer.apple.com/documentation/DeveloperTools/Conceptual/DynamicLibraries/Articles/DynamicLibraryDesignGuidelines.html#//apple_ref/doc/uid/TP40002013-DontLinkElementID_20 - -__attribute__ ((constructor, visibility("hidden"))) void GTMMethodCheckMethodChecker(void); - -#ifdef __cplusplus -}; -#endif - // This is the "magic". -// A) we need a multi layer define here so that the stupid preprocessor -// expands __LINE__ out the way we want it. We need LINE so that each of -// out GTM_METHOD_CHECKs generates a unique class method for the class. +// A) we need a multi layer define here so that the preprocessor expands +// __LINE__ the way we want it. We need __LINE__ so that each of our +// GTM_METHOD_CHECKs generates a unique function name. #define GTM_METHOD_CHECK(class, method) GTM_METHOD_CHECK_INNER(class, method, __LINE__) -#define GTM_METHOD_CHECK_INNER(class, method, line) GTM_METHOD_CHECK_INNER_INNER(class, method, line) +#define GTM_METHOD_CHECK_INNER(class, method, line) \ + GTM_METHOD_CHECK_INNER_INNER(class, method, line) -// B) Create up a class method called xxGMethodCheckMethod+class+line that the -// GTMMethodCheckMethodChecker function can look for and call. We -// look for GTMMethodCheckMethodChecker to enforce linkage of -// GTMMethodCheck.m. +// B) define a function that is called at startup to check that |class| has an +// implementation for |method| (either a class method or an instance method). #define GTM_METHOD_CHECK_INNER_INNER(class, method, line) \ -+ (void)xxGTMMethodCheckMethod ## class ## line { \ - void (*addr)() = GTMMethodCheckMethodChecker; \ - if (addr && ![class instancesRespondToSelector:@selector(method)] \ - && ![class respondsToSelector:@selector(method)]) { \ - fprintf(stderr, "%s:%d: error: We need method '%s' to be linked in for class '%s'\n", \ - __FILE__, line, #method, #class); \ - exit(EX_SOFTWARE); \ +__attribute__ ((constructor, visibility("hidden"))) \ + static void xxGTMMethodCheckMethod ## class ## line () { \ + @autoreleasepool { \ + if (![class instancesRespondToSelector:@selector(method)] \ + && ![class respondsToSelector:@selector(method)]) { \ + fprintf(stderr, "%s:%d: error: We need method '%s' to be linked in for class '%s'\n", \ + __FILE__, line, #method, #class); \ + exit(EX_SOFTWARE); \ + } \ } \ } -#else // !DEBUG +#else // DEBUG // Do nothing in release. #define GTM_METHOD_CHECK(class, method) diff --git a/DebugUtils/GTMMethodCheck.m b/DebugUtils/GTMMethodCheck.m index 0ff795e..5a9a363 100644 --- a/DebugUtils/GTMMethodCheck.m +++ b/DebugUtils/GTMMethodCheck.m @@ -16,103 +16,5 @@ // the License. // -// Don't want any of this in release builds -#ifdef DEBUG -#import "GTMDefines.h" -#import "GTMMethodCheck.h" -#import "GTMObjC2Runtime.h" -#import <dlfcn.h> - -void GTMMethodCheckMethodChecker(void) { - // Run through all the classes looking for class methods that are - // prefixed with xxGMMethodCheckMethod. If it finds one, it calls it. - // See GTMMethodCheck.h to see what it does. -#if !defined(__has_feature) || !__has_feature(objc_arc) - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; -#else - @autoreleasepool { -#endif - // Since GTMMethodCheckMethodChecker is not exported, we should always find - // the copy in our local image. This will give us access to our local image - // in the methodCheckerInfo structure. - Dl_info methodCheckerInfo; - int foundMethodChecker = dladdr(GTMMethodCheckMethodChecker, - &methodCheckerInfo); - _GTMDevAssert(foundMethodChecker, @"GTMMethodCheckMethodChecker: Unable to " - @"get dladdr for GTMMethodCheckMethodChecker"); - int numClasses = 0; - int newNumClasses = objc_getClassList(NULL, 0); - int i; - Class *classes = NULL; - while (numClasses < newNumClasses) { - numClasses = newNumClasses; - classes = (Class *)realloc(classes, sizeof(Class) * numClasses); - _GTMDevAssert(classes, @"Unable to allocate memory for classes"); - newNumClasses = objc_getClassList(classes, numClasses); - } - for (i = 0; i < numClasses && classes; ++i) { - Class cls = classes[i]; - const char *className = class_getName(cls); - _GTMDevAssert(className, @"GTMMethodCheckMethodChecker: Unable to " - @"get className for class %d", i); - // Since we are looking for a class method (+xxGMMethodCheckMethod...) - // we need to query the isa pointer to see what methods it support, but - // send the method (if it's supported) to the class itself. - if (strcmp(className, "__ARCLite__") == 0) { - // __ARCLite__ is "magic" and does not have a metaClass. - continue; - } - Class metaClass = objc_getMetaClass(className); - _GTMDevAssert(metaClass, @"GTMMethodCheckMethodChecker: Unable to " - @"get metaClass for %s", className); - unsigned int count; - Method *methods = class_copyMethodList(metaClass, &count); - if (count == 0) { - continue; - } - _GTMDevAssert(methods, @"GTMMethodCheckMethodChecker: Unable to " - @"get methods for class %s", className); - - unsigned int j; - for (j = 0; j < count; ++j) { - SEL selector = method_getName(methods[j]); - _GTMDevAssert(selector, @"GTMMethodCheckMethodChecker: Unable to " - @"get selector for method %d of %s", j, className); - const char *name = sel_getName(selector); - _GTMDevAssert(selector, @"GTMMethodCheckMethodChecker: Unable to " - @"get name for method %d of %s", j, className); - if (strstr(name, "xxGTMMethodCheckMethod") == name) { - Dl_info methodInfo; - IMP imp = method_getImplementation(methods[j]); - _GTMDevAssert(selector, @"GTMMethodCheckMethodChecker: Unable to " - @"get IMP for method %s of %s", name, className); - int foundMethod = dladdr(imp, &methodInfo); - _GTMDevAssert(foundMethod, @"GTMMethodCheckMethodChecker: Unable to " - @"get dladdr for method %s of %s", name, className); - - // Check to make sure that the method we are checking comes from the - // same image that we are in. We compare the address of the local - // image (stored in |methodCheckerInfo| as noted above) with the - // address of the image which implements the method we want to - // check. If they match we continue. This does two things: - // a) minimizes the amount of calls we make to the xxxGTMMethodCheck - // methods. They should only be called once. - // b) prevents initializers for various classes being called too - // early - if (methodCheckerInfo.dli_fbase == methodInfo.dli_fbase) { - void (*func)(id, SEL) = (void *)imp; - func(cls, selector); - } - } - } - free(methods); - } - free(classes); -#if !defined(__has_feature) || !__has_feature(objc_arc) - [pool drain]; -#else - } // @autoreleasepool -#endif -} - -#endif // DEBUG +// TODO(dmaclach): This file is no longer needed. Delete once we have cleaned +// up GTM projects. diff --git a/DebugUtils/GTMMethodCheckTest.m b/DebugUtils/GTMMethodCheckTest.m index 1fba238..9d90563 100644 --- a/DebugUtils/GTMMethodCheckTest.m +++ b/DebugUtils/GTMMethodCheckTest.m @@ -15,40 +15,5 @@ // the License. // -#import "GTMSenTestCase.h" -#import "GTMMethodCheck.h" - -static BOOL gTestCheckVar = NO; - -@interface GTMMethodCheckTest : GTMTestCase -+ (void)GTMMethodCheckTestClassMethod; -- (void)GTMMethodCheckTestMethod; -@end - -@implementation GTMMethodCheckTest -GTM_METHOD_CHECK(GTMMethodCheckTest, GTMMethodCheckTestMethod); -GTM_METHOD_CHECK(GTMMethodCheckTest, GTMMethodCheckTestClassMethod); - -- (void)GTMMethodCheckTestMethod { -} - -+ (void)GTMMethodCheckTestClassMethod { -} - -+ (void)xxGTMMethodCheckMethodTestCheck { - // This gets called because of its special name by GMMethodCheck - // Look at the Macros in GMMethodCheck.h for details. - gTestCheckVar = YES; -} - -- (void)testGTMMethodCheck { -#ifdef DEBUG - // GTMMethodCheck only runs in debug - STAssertTrue(gTestCheckVar, @"Should be true"); -#endif - - // Next two calls just verify our code coverage - [self GTMMethodCheckTestMethod]; - [[self class] GTMMethodCheckTestClassMethod]; -} -@end +// TODO(dmaclach): This file no longer needed. Delete once we have cleaned up +// GTM projects. diff --git a/GoogleToolboxForMac.podspec b/GoogleToolboxForMac.podspec index a1bd450..863c387 100644 --- a/GoogleToolboxForMac.podspec +++ b/GoogleToolboxForMac.podspec @@ -41,8 +41,7 @@ Pod::Spec.new do |s| sp.source_files = 'DebugUtils/GTMDebugSelectorValidation.{h,m}', 'DebugUtils/GTMDebugThreadValidation.h', - 'DebugUtils/GTMMethodCheck.{h,m}' - sp.requires_arc = 'DebugUtils/GTMMethodCheck.m' + 'DebugUtils/GTMMethodCheck.h' sp.dependency 'GoogleToolboxForMac/Defines', "#{s.version}" sp.dependency 'GoogleToolboxForMac/Runtime', "#{s.version}" end @@ -56,10 +55,6 @@ Pod::Spec.new do |s| s.subspec 'KVO' do |sp| sp.source_files = 'Foundation/GTMNSObject+KeyValueObserving.{h,m}', - # The symbol in this file is hidden by default, and so - # must be directly included here where it's needed, - # even though it's already included in DebugUtils - 'DebugUtils/GTMMethodCheck.m' sp.dependency 'GoogleToolboxForMac/Core', "#{s.version}" sp.dependency 'GoogleToolboxForMac/DebugUtils', "#{s.version}" sp.dependency 'GoogleToolboxForMac/Runtime', "#{s.version}" |