aboutsummaryrefslogtreecommitdiff
path: root/DebugUtils
diff options
context:
space:
mode:
authorGravatar Thomas Van Lenten <thomasvl@google.com>2016-09-16 16:12:53 -0400
committerGravatar Thomas Van Lenten <thomasvl@google.com>2016-09-16 16:12:53 -0400
commit088532e8367dd681639e288665a3f0c9619c0cb5 (patch)
treeee4b552ae18484db6993eae459f209ee704b75a8 /DebugUtils
parent3da7e0c8de4adf4f923d5c6dee2fb3c9835a39f0 (diff)
Fix up GTM_METHOD_CHECK so that it doesn't need the objectivec runtime calls.
Diffstat (limited to 'DebugUtils')
-rw-r--r--DebugUtils/GTMMethodCheck.h69
-rw-r--r--DebugUtils/GTMMethodCheck.m102
-rw-r--r--DebugUtils/GTMMethodCheckTest.m39
3 files changed, 29 insertions, 181 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.