aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Foundation/GTMGarbageCollection.h2
-rw-r--r--Foundation/GTMLogger.h2
-rw-r--r--Foundation/GTMLoggerTest.m4
-rw-r--r--Foundation/GTMNSAppleEvent+HandlerTest.applescript6
-rw-r--r--Foundation/GTMNSAppleScript+Handler.h4
-rw-r--r--Foundation/GTMNSAppleScript+Handler.m15
-rw-r--r--Foundation/GTMNSAppleScript+HandlerTest.m10
-rw-r--r--Foundation/GTMNSFileManager+Path.h4
-rw-r--r--Foundation/GTMNSFileManager+Path.m4
-rw-r--r--Foundation/GTMNSFileManager+PathTest.m6
-rw-r--r--Foundation/GTMNSString+Replace.h4
-rw-r--r--Foundation/GTMNSString+Replace.m4
-rw-r--r--Foundation/GTMNSString+ReplaceTest.m4
-rw-r--r--Foundation/GTMObjC2Runtime.h2
-rw-r--r--Foundation/GTMObjC2Runtime.m2
-rw-r--r--Foundation/GTMObjC2RuntimeTest.m2
-rw-r--r--Foundation/GTMPathTest.m2
-rw-r--r--Foundation/GTMStackTrace.h15
-rw-r--r--Foundation/GTMStackTrace.m178
-rw-r--r--Foundation/GTMStackTraceTest.m41
-rw-r--r--GTM.xcodeproj/project.pbxproj18
-rw-r--r--GTMDefines.h7
-rw-r--r--GTMiPhone.xcodeproj/project.pbxproj32
-rw-r--r--ReleaseNotes.txt7
-rw-r--r--XcodeConfig/subconfig/General.xcconfig11
-rw-r--r--iPhone/GTMUIFont+LineHeight.h24
-rw-r--r--iPhone/GTMUIFont+LineHeight.m25
-rw-r--r--iPhone/GTMUIFont+LineHeightTest.m40
28 files changed, 384 insertions, 91 deletions
diff --git a/Foundation/GTMGarbageCollection.h b/Foundation/GTMGarbageCollection.h
index b29a13b..58c8140 100644
--- a/Foundation/GTMGarbageCollection.h
+++ b/Foundation/GTMGarbageCollection.h
@@ -28,7 +28,7 @@
// http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection/Articles/gcCoreFoundation.html#//apple_ref/doc/uid/TP40006687-SW1
// for details.
-#if (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) && !GTM_IPHONE_SDK
+#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) && !GTM_IPHONE_SDK
// General use would be to call this through GTMCFAutorelease
// but there may be a reason the you want to make something collectable
// but not autoreleased, especially in pure GC code where you don't
diff --git a/Foundation/GTMLogger.h b/Foundation/GTMLogger.h
index 8fdb0fa..1626b1b 100644
--- a/Foundation/GTMLogger.h
+++ b/Foundation/GTMLogger.h
@@ -54,7 +54,7 @@
// Predeclaration of used protocols that are declared later in this file.
@protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter;
-#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
#define CHECK_FORMAT_NSSTRING(a, b) __attribute__((format(__NSString__, a, b)))
#else
#define CHECK_FORMAT_NSSTRING(a, b)
diff --git a/Foundation/GTMLoggerTest.m b/Foundation/GTMLoggerTest.m
index 043c73d..86ced5e 100644
--- a/Foundation/GTMLoggerTest.m
+++ b/Foundation/GTMLoggerTest.m
@@ -94,7 +94,7 @@
@"GTMLoggerUnitTest.log"] retain];
STAssertNotNil(path_, nil);
// Make sure we're cleaned up from the last run
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
[[NSFileManager defaultManager] removeFileAtPath:path_ handler:nil];
#else
[[NSFileManager defaultManager] removeItemAtPath:path_ error:NULL];
@@ -103,7 +103,7 @@
- (void)tearDown {
STAssertNotNil(path_, nil);
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
[[NSFileManager defaultManager] removeFileAtPath:path_ handler:nil];
#else
[[NSFileManager defaultManager] removeItemAtPath:path_ error:NULL];
diff --git a/Foundation/GTMNSAppleEvent+HandlerTest.applescript b/Foundation/GTMNSAppleEvent+HandlerTest.applescript
index a0822f9..377d733 100644
--- a/Foundation/GTMNSAppleEvent+HandlerTest.applescript
+++ b/Foundation/GTMNSAppleEvent+HandlerTest.applescript
@@ -27,6 +27,8 @@ script testScript
on testScriptFunc()
return "child"
end testScriptFunc
+ on open foo
+ end open
end script
property foo : 1
@@ -54,5 +56,5 @@ on testGetScript()
return testScript
end testGetScript
-on open
-end open
+on print
+end print
diff --git a/Foundation/GTMNSAppleScript+Handler.h b/Foundation/GTMNSAppleScript+Handler.h
index e63eaee..6e3af50 100644
--- a/Foundation/GTMNSAppleScript+Handler.h
+++ b/Foundation/GTMNSAppleScript+Handler.h
@@ -119,4 +119,8 @@ enum {
- (BOOL)gtm_setValue:(id)value
forPropertyEnum:(DescType)property
addingDefinition:(BOOL)adding;
+
+// Return YES if the script has an open documents (odoc) handler
+// Does not require script compilation, so it's a fast check.
+- (BOOL)gtm_hasOpenDocumentsHandler;
@end
diff --git a/Foundation/GTMNSAppleScript+Handler.m b/Foundation/GTMNSAppleScript+Handler.m
index 592aca6..be4a2dc 100644
--- a/Foundation/GTMNSAppleScript+Handler.m
+++ b/Foundation/GTMNSAppleScript+Handler.m
@@ -232,6 +232,21 @@ GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_registerSelector:forTypes:count:);
return desc;
}
+- (BOOL)gtm_hasOpenDocumentsHandler {
+ ComponentInstance component;
+ OSAID osaID = [self gtm_realIDAndComponent:&component];
+ long value = 0;
+ OSAError error = OSAGetScriptInfo(component,
+ osaID,
+ kASHasOpenHandler,
+ &value);
+ if (error) {
+ _GTMDevLog(@"Unable to get script info about open handler %d", error);
+ value = 0;
+ }
+ return value != 0;
+}
+
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
if (!signature) {
diff --git a/Foundation/GTMNSAppleScript+HandlerTest.m b/Foundation/GTMNSAppleScript+HandlerTest.m
index 102332e..02f62e0 100644
--- a/Foundation/GTMNSAppleScript+HandlerTest.m
+++ b/Foundation/GTMNSAppleScript+HandlerTest.m
@@ -278,7 +278,7 @@
- (void)testHandlers {
NSSet *handlers = [script_ gtm_handlers];
NSSet *expected = [NSSet setWithObjects:
- @"aevtodoc",
+ @"aevtpdoc",
@"test",
@"testreturnone",
@"testreturnparam",
@@ -475,6 +475,14 @@
STAssertNotNil(handlers, @"Couldn't get handlers");
}
+- (void)testOpenHandler {
+ STAssertFalse([script_ gtm_hasOpenDocumentsHandler], nil);
+ id script = [script_ gtm_valueForProperty:@"testscript"];
+ STAssertNotNil(script, nil);
+ STAssertTrue([script gtm_hasOpenDocumentsHandler], nil);
+}
+
+
@protocol ScriptInterface
- (id)test;
- (id)testReturnParam:(id)param;
diff --git a/Foundation/GTMNSFileManager+Path.h b/Foundation/GTMNSFileManager+Path.h
index 2ed6888..f377215 100644
--- a/Foundation/GTMNSFileManager+Path.h
+++ b/Foundation/GTMNSFileManager+Path.h
@@ -22,7 +22,7 @@
/// A few useful methods for dealing with paths.
@interface NSFileManager (GMFileManagerPathAdditions)
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
/// For the Unix-y at heart, this is "mkdir -p". It tries to create
/// the directory specified by |path|, and any intervening directories that
@@ -47,7 +47,7 @@
- (BOOL)gtm_createFullPathToDirectory:(NSString *)path
attributes:(NSDictionary *)attributes;
-#endif // MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
/// Return an the paths for all resources in |directoryPath| that have the
/// |extension| file extension.
diff --git a/Foundation/GTMNSFileManager+Path.m b/Foundation/GTMNSFileManager+Path.m
index 6dec24b..2d71729 100644
--- a/Foundation/GTMNSFileManager+Path.m
+++ b/Foundation/GTMNSFileManager+Path.m
@@ -20,7 +20,7 @@
@implementation NSFileManager (GMFileManagerPathAdditions)
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
- (BOOL)gtm_createFullPathToDirectory:(NSString *)path
attributes:(NSDictionary *)attributes {
@@ -52,7 +52,7 @@
return YES;
}
-#endif // MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
- (NSArray *)gtm_filePathsWithExtension:(NSString *)extension
inDirectory:(NSString *)directoryPath {
diff --git a/Foundation/GTMNSFileManager+PathTest.m b/Foundation/GTMNSFileManager+PathTest.m
index ac36cac..db40c68 100644
--- a/Foundation/GTMNSFileManager+PathTest.m
+++ b/Foundation/GTMNSFileManager+PathTest.m
@@ -48,7 +48,7 @@
if (baseDir_) {
// clean up our directory
NSFileManager *fm = [NSFileManager defaultManager];
-#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
NSError *error = nil;
[fm removeItemAtPath:baseDir_ error:&error];
STAssertNil(error,
@@ -62,7 +62,7 @@
}
}
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
- (void)testCreateFullPathToDirectoryAttributes {
STAssertNotNil(baseDir_, @"setUp failed");
@@ -91,7 +91,7 @@
@"Should have failed when passed (nil)");
}
-#endif // MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
- (void)testfilePathsWithExtensionsInDirectory {
STAssertNotNil(baseDir_, @"setUp failed");
diff --git a/Foundation/GTMNSString+Replace.h b/Foundation/GTMNSString+Replace.h
index edc8478..71a98c5 100644
--- a/Foundation/GTMNSString+Replace.h
+++ b/Foundation/GTMNSString+Replace.h
@@ -21,7 +21,7 @@
/// Give easy search-n-replace functionality to NSString.
@interface NSString (GTMStringReplaceAdditions)
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
// 10.5 has stringByReplacingOccurrencesOfString:withString:, use that directly.
/// Returns a new autoreleased string by replacing all occurrences of
@@ -40,6 +40,6 @@
- (NSString *)gtm_stringByReplacingString:(NSString *)target
withString:(NSString *)replacement;
-#endif // MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
@end
diff --git a/Foundation/GTMNSString+Replace.m b/Foundation/GTMNSString+Replace.m
index f617945..ddaad5e 100644
--- a/Foundation/GTMNSString+Replace.m
+++ b/Foundation/GTMNSString+Replace.m
@@ -21,7 +21,7 @@
@implementation NSString (GTMStringReplaceAdditions)
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
// 10.5 has stringByReplacingOccurrencesOfString:withString:, use that directly.
- (NSString *)gtm_stringByReplacingString:(NSString *)target
@@ -48,6 +48,6 @@
return result;
}
-#endif // MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
@end
diff --git a/Foundation/GTMNSString+ReplaceTest.m b/Foundation/GTMNSString+ReplaceTest.m
index 805136c..4561af6 100644
--- a/Foundation/GTMNSString+ReplaceTest.m
+++ b/Foundation/GTMNSString+ReplaceTest.m
@@ -24,7 +24,7 @@
@implementation GTMNSString_ReplaceTest
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
- (void)testStringByReplacingStringWithString {
NSString *testString = @"a bc debc gh";
@@ -54,6 +54,6 @@
@"replacing '' with anything should yield the original string");
}
-#endif // MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
@end
diff --git a/Foundation/GTMObjC2Runtime.h b/Foundation/GTMObjC2Runtime.h
index f94e680..f901d1e 100644
--- a/Foundation/GTMObjC2Runtime.h
+++ b/Foundation/GTMObjC2Runtime.h
@@ -47,7 +47,7 @@
#import <objc/Object.h>
#endif
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
#import "objc/Protocol.h"
#ifdef __cplusplus
diff --git a/Foundation/GTMObjC2Runtime.m b/Foundation/GTMObjC2Runtime.m
index ba0cb74..9835654 100644
--- a/Foundation/GTMObjC2Runtime.m
+++ b/Foundation/GTMObjC2Runtime.m
@@ -18,7 +18,7 @@
#import "GTMObjC2Runtime.h"
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
#import <stdlib.h>
#import <string.h>
diff --git a/Foundation/GTMObjC2RuntimeTest.m b/Foundation/GTMObjC2RuntimeTest.m
index 1bc0ee3..d0ce4f7 100644
--- a/Foundation/GTMObjC2RuntimeTest.m
+++ b/Foundation/GTMObjC2RuntimeTest.m
@@ -299,7 +299,7 @@ AT_REQUIRED
// Apparently it was a bug that we could call setImplementation with a nil
// so we now test to make sure that setting to nil works as expected on
// all systems.
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
// Built for less then leopard gives us the behaviors we defined...
// (doesn't take nil)
IMP nullImp = method_setImplementation(list[0], nil);
diff --git a/Foundation/GTMPathTest.m b/Foundation/GTMPathTest.m
index 3a60ea7..211206c 100644
--- a/Foundation/GTMPathTest.m
+++ b/Foundation/GTMPathTest.m
@@ -46,7 +46,7 @@
// Make sure it's safe to remove this directory before nuking it.
STAssertNotNil(testDirectory_, nil);
STAssertNotEqualObjects(testDirectory_, @"/", nil);
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
[[NSFileManager defaultManager] removeFileAtPath:testDirectory_ handler:nil];
#else
[[NSFileManager defaultManager] removeItemAtPath:testDirectory_ error:NULL];
diff --git a/Foundation/GTMStackTrace.h b/Foundation/GTMStackTrace.h
index 3b6965d..9726da5 100644
--- a/Foundation/GTMStackTrace.h
+++ b/Foundation/GTMStackTrace.h
@@ -51,7 +51,17 @@ struct GTMAddressDescriptor {
// #6 0x000025b9 tart () [/Users/me/./StackLog]
//
+#ifdef GTM_MACOS_SDK // currently not supported on iPhone
NSString *GTMStackTrace(void);
+#endif
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
+// Returns a string containing a nicely formatted stack trace from the
+// exception. Only available on 10.5 or later, uses
+// -[NSException callStackReturnAddresses].
+//
+NSString *GTMStackTraceFromException(NSException *e);
+#endif
// Returns an array of program counters from the current thread's stack.
// *** You should probably use GTMStackTrace() instead of this function ***
@@ -67,7 +77,9 @@ NSString *GTMStackTrace(void);
// Returns:
// The number of program counters actually added to outPcs.
//
+#ifdef GTM_MACOS_SDK // currently not supported on iPhone
NSUInteger GTMGetStackProgramCounters(void *outPcs[], NSUInteger count);
+#endif
// Returns an array of GTMAddressDescriptors from the current thread's stack.
// *** You should probably use GTMStackTrace() instead of this function ***
@@ -85,8 +97,11 @@ NSUInteger GTMGetStackProgramCounters(void *outPcs[], NSUInteger count);
// Returns:
// The number of program counters actually added to outPcs.
//
+#ifdef GTM_MACOS_SDK // currently not supported on iPhone
NSUInteger GTMGetStackAddressDescriptors(struct GTMAddressDescriptor outDescs[],
NSUInteger count);
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/Foundation/GTMStackTrace.m b/Foundation/GTMStackTrace.m
index 8a14bcf..c22c153 100644
--- a/Foundation/GTMStackTrace.m
+++ b/Foundation/GTMStackTrace.m
@@ -87,49 +87,11 @@ static void GTMFreeClassDescriptions(struct GTMClassDescription *class_descs,
free(class_descs);
}
-#pragma mark Public functions
-
-// __builtin_frame_address(0) is a gcc builtin that returns a pointer to the
-// current frame pointer. We then use the frame pointer to walk the stack
-// picking off program counters and other saved frame pointers. This works
-// great on i386, but PPC requires a little more work because the PC (or link
-// register) isn't always stored on the stack.
-//
-NSUInteger GTMGetStackProgramCounters(void *outPcs[], NSUInteger count) {
- if (!outPcs || (count < 1)) return 0;
-
- struct GTMStackFrame *fp;
-#if defined (__ppc__) || defined(__ppc64__)
- outPcs[0] = __builtin_return_address(0);
- fp = (struct GTMStackFrame *)__builtin_frame_address(1);
-#elif defined (__i386__) || defined(__x86_64__)
- fp = (struct GTMStackFrame *)__builtin_frame_address(0);
-#else
-#error architecture not supported
-#endif
-
- NSUInteger level = 0;
- while (level < count) {
- if (fp == NULL) {
- level--;
- break;
- }
- outPcs[level] = fp->saved_pc;
- level++;
- fp = (struct GTMStackFrame *)fp->saved_fp;
- }
-
- return level;
-}
-
-NSUInteger GTMGetStackAddressDescriptors(struct GTMAddressDescriptor outDescs[],
- NSUInteger count) {
- if (count < 1 || !outDescs) return 0;
+static NSUInteger GTMGetStackAddressDescriptorsForAddresses(void *pcs[],
+ struct GTMAddressDescriptor outDescs[],
+ NSUInteger count) {
+ if (count < 1 || !pcs || !outDescs) return 0;
- void **pcs = calloc(count, sizeof(void*));
- if (!pcs) return 0;
-
- NSUInteger newSize = GTMGetStackProgramCounters(pcs, count);
NSUInteger class_desc_count;
// Get our obj-c class descriptions. This is expensive, so we do it once
@@ -139,7 +101,7 @@ NSUInteger GTMGetStackAddressDescriptors(struct GTMAddressDescriptor outDescs[],
= GTMClassDescriptions(&class_desc_count);
// Iterate through the stack.
- for (NSUInteger i = 0; i < newSize; ++i) {
+ for (NSUInteger i = 0; i < count; ++i) {
const char *class_name = NULL;
Boolean is_class_method = FALSE;
size_t smallest_diff = SIZE_MAX;
@@ -194,8 +156,84 @@ NSUInteger GTMGetStackAddressDescriptors(struct GTMAddressDescriptor outDescs[],
currDesc->filename = info.dli_fname;
}
GTMFreeClassDescriptions(class_descs, class_desc_count);
+ return count;
+}
+
+static NSString *GTMStackTraceFromAddressDescriptors(struct GTMAddressDescriptor descs[],
+ NSUInteger count) {
+ NSMutableString *trace = [NSMutableString string];
+
+ for (NSUInteger i = 0; i < count; i++) {
+ // Newline between all the lines
+ if (i) {
+ [trace appendString:@"\n"];
+ }
+ if (descs[i].class_name) {
+ [trace appendFormat:@"#%-2u %#08lx %s[%s %s] (%s)",
+ i, descs[i].address,
+ (descs[i].is_class_method ? "+" : "-"),
+ descs[i].class_name,
+ (descs[i].symbol ? descs[i].symbol : "??"),
+ (descs[i].filename ? descs[i].filename : "??")];
+ } else {
+ [trace appendFormat:@"#%-2u %#08lx %s() (%s)",
+ i, descs[i].address,
+ (descs[i].symbol ? descs[i].symbol : "??"),
+ (descs[i].filename ? descs[i].filename : "??")];
+ }
+ }
+ return trace;
+}
+
+#pragma mark Public functions
+
+// __builtin_frame_address(0) is a gcc builtin that returns a pointer to the
+// current frame pointer. We then use the frame pointer to walk the stack
+// picking off program counters and other saved frame pointers. This works
+// great on i386, but PPC requires a little more work because the PC (or link
+// register) isn't always stored on the stack.
+//
+#ifdef GTM_MACOS_SDK // currently not supported on iPhone
+NSUInteger GTMGetStackProgramCounters(void *outPcs[], NSUInteger count) {
+ if (!outPcs || (count < 1)) return 0;
+
+ struct GTMStackFrame *fp;
+#if defined (__ppc__) || defined(__ppc64__)
+ outPcs[0] = __builtin_return_address(0);
+ fp = (struct GTMStackFrame *)__builtin_frame_address(1);
+#elif defined (__i386__) || defined(__x86_64__)
+ fp = (struct GTMStackFrame *)__builtin_frame_address(0);
+#else
+#error architecture not supported
+#endif
+
+ NSUInteger level = 0;
+ while (level < count) {
+ if (fp == NULL) {
+ level--;
+ break;
+ }
+ outPcs[level] = fp->saved_pc;
+ level++;
+ fp = (struct GTMStackFrame *)fp->saved_fp;
+ }
+
+ return level;
+}
+
+NSUInteger GTMGetStackAddressDescriptors(struct GTMAddressDescriptor outDescs[],
+ NSUInteger count) {
+ if (count < 1 || !outDescs) return 0;
+
+ void **pcs = calloc(count, sizeof(void*));
+ if (!pcs) return 0;
+
+ NSUInteger newSize = GTMGetStackProgramCounters(pcs, count);
+
+ NSUInteger result
+ = GTMGetStackAddressDescriptorsForAddresses(pcs, outDescs, newSize);
free(pcs);
- return newSize;
+ return result;
}
NSString *GTMStackTrace(void) {
@@ -206,25 +244,47 @@ NSString *GTMStackTrace(void) {
size_t depth = sizeof(descs) / sizeof(struct GTMAddressDescriptor);
depth = GTMGetStackAddressDescriptors(descs, depth);
- NSMutableString *trace = [NSMutableString string];
-
// Start at the second item so that GTMStackTrace and it's utility calls (of
// which there is currently 1) is not included in the output.
const size_t kTracesToStrip = 2;
- for (size_t i = kTracesToStrip; i < depth; i++) {
- if (descs[i].class_name) {
- [trace appendFormat:@"#%-2d 0x%08lx %s[%s %s] (%s)\n",
- i - kTracesToStrip, descs[i].address,
- (descs[i].is_class_method ? "+" : "-"),
- descs[i].class_name,
- (descs[i].symbol ? descs[i].symbol : "??"),
- (descs[i].filename ? descs[i].filename : "??")];
- } else {
- [trace appendFormat:@"#%-2d 0x%08lx %s() (%s)\n",
- i - kTracesToStrip, descs[i].address,
- (descs[i].symbol ? descs[i].symbol : "??"),
- (descs[i].filename ? descs[i].filename : "??")];
+ if (depth > kTracesToStrip) {
+ return GTMStackTraceFromAddressDescriptors(&descs[kTracesToStrip],
+ (depth - kTracesToStrip));
+ }
+ // If we didn't have enough frames, return an empty string
+ return @"";
+}
+#endif // GTM_MACOS_SDK
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
+
+NSString *GTMStackTraceFromException(NSException *e) {
+ NSString *trace = @"";
+
+ // collect the addresses
+ NSArray *addresses = [e callStackReturnAddresses];
+ NSUInteger count = [addresses count];
+ if (count) {
+ void **pcs = calloc(count, sizeof(void*));
+ struct GTMAddressDescriptor *descs
+ = calloc(count, sizeof(struct GTMAddressDescriptor));
+ if (pcs && descs) {
+ void **pcsScanner = pcs;
+ for (NSNumber *address in addresses) {
+ NSUInteger addr = [address unsignedIntegerValue];
+ *pcsScanner = (void *)addr;
+ ++pcsScanner;
+ }
+ // Fill in the desc structures
+ count = GTMGetStackAddressDescriptorsForAddresses(pcs, descs, count);
+ // Build the trace
+ trace = GTMStackTraceFromAddressDescriptors(descs, count);
}
+ if (pcs) free(pcs);
+ if (descs) free(descs);
}
+
return trace;
}
+
+#endif // MAC_OS_X_VERSION_MIN_REQUIRED
diff --git a/Foundation/GTMStackTraceTest.m b/Foundation/GTMStackTraceTest.m
index dc5ea57..01b02a3 100644
--- a/Foundation/GTMStackTraceTest.m
+++ b/Foundation/GTMStackTraceTest.m
@@ -25,6 +25,7 @@
@implementation GTMStackTraceTest
+#ifdef GTM_MACOS_SDK // currently not supported on iPhone
- (void)testStackTraceBasic {
NSString *stacktrace = GTMStackTrace();
NSArray *stacklines = [stacktrace componentsSeparatedByString:@"\n"];
@@ -44,7 +45,46 @@
@"First frame should contain #0, stack trace: %@",
stacktrace);
}
+#endif // GTM_MACOS_SDK
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
+
+- (void)helperThatThrows {
+ [NSException raise:@"TestException" format:@"TestExceptionDescription"];
+}
+
+- (void)testStackExceptionTrace {
+ NSException *exception = nil;
+ @try {
+ [self helperThatThrows];
+ }
+ @catch (NSException * e) {
+ exception = e;
+ }
+ STAssertNotNil(exception, nil);
+ NSString *stacktrace = GTMStackTraceFromException(exception);
+ NSArray *stacklines = [stacktrace componentsSeparatedByString:@"\n"];
+
+ STAssertGreaterThan([stacklines count], (NSUInteger)4,
+ @"stack trace must have > 4 lines");
+ STAssertLessThan([stacklines count], (NSUInteger)25,
+ @"stack trace must have < 25 lines");
+ STAssertEquals([stacklines count],
+ [[exception callStackReturnAddresses] count],
+ @"stack trace should have the same number of lines as the "
+ @" array of return addresses. stack trace: %@", stacktrace);
+
+ // we can't look for it on a specific frame because NSException doesn't
+ // really document how deep the stack will be
+ NSRange range = [stacktrace rangeOfString:@"testStackExceptionTrace"];
+ STAssertNotEquals(range.location, (NSUInteger)NSNotFound,
+ @"Stack trace should contain testStackExceptionTrace,"
+ " stack trace: %@", stacktrace);
+}
+
+#endif
+
+#ifdef GTM_MACOS_SDK // currently not supported on iPhone
- (void)testProgramCountersBasic {
void *pcs[10];
NSUInteger depth = 10;
@@ -80,5 +120,6 @@
void *current_pc = __builtin_return_address(0);
STAssertEquals(pcs2[1], current_pc, @"pcs[1] should equal the current PC");
}
+#endif // GTM_MACOS_SDK
@end
diff --git a/GTM.xcodeproj/project.pbxproj b/GTM.xcodeproj/project.pbxproj
index 017d952..6d7c854 100644
--- a/GTM.xcodeproj/project.pbxproj
+++ b/GTM.xcodeproj/project.pbxproj
@@ -1429,6 +1429,8 @@
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = GTM_Prefix.pch;
+ GCC_WARN_SHADOW = YES;
+ GTM_EXTRA_WARNING_OVERRIDE_CFLAGS = "-Wno-unused-parameter";
};
name = "TigerOrLater-Debug";
};
@@ -1438,6 +1440,8 @@
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = GTM_Prefix.pch;
+ GCC_WARN_SHADOW = YES;
+ GTM_EXTRA_WARNING_OVERRIDE_CFLAGS = "-Wno-unused-parameter";
};
name = "TigerOrLater-Release";
};
@@ -1447,6 +1451,8 @@
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = GTM_Prefix.pch;
+ GCC_WARN_SHADOW = YES;
+ GTM_EXTRA_WARNING_OVERRIDE_CFLAGS = "-Wno-unused-parameter";
};
name = "SnowLeopardOrLater-Debug";
};
@@ -1545,6 +1551,8 @@
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = GTM_Prefix.pch;
+ GCC_WARN_SHADOW = YES;
+ GTM_EXTRA_WARNING_OVERRIDE_CFLAGS = "-Wno-unused-parameter";
OTHER_LDFLAGS = "-lgcov";
};
name = "SnowLeopardOrLater-Debug-gcov";
@@ -1555,6 +1563,8 @@
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = GTM_Prefix.pch;
+ GCC_WARN_SHADOW = YES;
+ GTM_EXTRA_WARNING_OVERRIDE_CFLAGS = "-Wno-unused-parameter";
};
name = "SnowLeopardOrLater-Release";
};
@@ -1958,6 +1968,8 @@
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = GTM_Prefix.pch;
+ GCC_WARN_SHADOW = YES;
+ GTM_EXTRA_WARNING_OVERRIDE_CFLAGS = "-Wno-unused-parameter";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/TigerGcov\"",
@@ -2050,6 +2062,8 @@
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = GTM_Prefix.pch;
+ GCC_WARN_SHADOW = YES;
+ GTM_EXTRA_WARNING_OVERRIDE_CFLAGS = "-Wno-unused-parameter";
};
name = "LeopardOrLater-Debug";
};
@@ -2139,6 +2153,8 @@
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = GTM_Prefix.pch;
+ GCC_WARN_SHADOW = YES;
+ GTM_EXTRA_WARNING_OVERRIDE_CFLAGS = "-Wno-unused-parameter";
OTHER_LDFLAGS = "-lgcov";
};
name = "LeopardOrLater-Debug-gcov";
@@ -2227,6 +2243,8 @@
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = GTM_Prefix.pch;
+ GCC_WARN_SHADOW = YES;
+ GTM_EXTRA_WARNING_OVERRIDE_CFLAGS = "-Wno-unused-parameter";
};
name = "LeopardOrLater-Release";
};
diff --git a/GTMDefines.h b/GTMDefines.h
index 1d1c908..bd37b3e 100644
--- a/GTMDefines.h
+++ b/GTMDefines.h
@@ -20,9 +20,12 @@
#include <AvailabilityMacros.h>
+// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs
+#ifndef MAC_OS_X_VERSION_10_5
+# define MAC_OS_X_VERSION_10_5 1050
+#endif
#ifndef MAC_OS_X_VERSION_10_6
-// MAC_OS_X_VERSION_10_6 not defined in some earlier SDKs
-#define MAC_OS_X_VERSION_10_6 1060
+# define MAC_OS_X_VERSION_10_6 1060
#endif
// ----------------------------------------------------------------------------
diff --git a/GTMiPhone.xcodeproj/project.pbxproj b/GTMiPhone.xcodeproj/project.pbxproj
index aa2bb78..1d573cd 100644
--- a/GTMiPhone.xcodeproj/project.pbxproj
+++ b/GTMiPhone.xcodeproj/project.pbxproj
@@ -89,6 +89,10 @@
F439ADEC0DBD3C0000BE9B91 /* GTMBase64Test.m in Sources */ = {isa = PBXBuildFile; fileRef = F439ADEA0DBD3C0000BE9B91 /* GTMBase64Test.m */; };
F439ADF00DBD3C4000BE9B91 /* GTMGeometryUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = F439ADEE0DBD3C4000BE9B91 /* GTMGeometryUtils.m */; };
F439ADF10DBD3C4000BE9B91 /* GTMGeometryUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F439ADEF0DBD3C4000BE9B91 /* GTMGeometryUtilsTest.m */; };
+ F4E3B3D80EB5EF2400CB713D /* GTMUIFont+LineHeight.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E3B3D70EB5EF2400CB713D /* GTMUIFont+LineHeight.m */; };
+ F4E3B3E20EB5EF9A00CB713D /* GTMUIFont+LineHeightTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E3B3E10EB5EF9A00CB713D /* GTMUIFont+LineHeightTest.m */; };
+ F4EF8AD70EBFF814008DD6DA /* GTMStackTrace.m in Sources */ = {isa = PBXBuildFile; fileRef = F4EF8AD50EBFF814008DD6DA /* GTMStackTrace.m */; };
+ F4EF8AD80EBFF814008DD6DA /* GTMStackTraceTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F4EF8AD60EBFF814008DD6DA /* GTMStackTraceTest.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -213,6 +217,12 @@
F439ADED0DBD3C4000BE9B91 /* GTMGeometryUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMGeometryUtils.h; sourceTree = "<group>"; };
F439ADEE0DBD3C4000BE9B91 /* GTMGeometryUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMGeometryUtils.m; sourceTree = "<group>"; };
F439ADEF0DBD3C4000BE9B91 /* GTMGeometryUtilsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMGeometryUtilsTest.m; sourceTree = "<group>"; };
+ F4E3B3D60EB5EF2400CB713D /* GTMUIFont+LineHeight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMUIFont+LineHeight.h"; sourceTree = "<group>"; };
+ F4E3B3D70EB5EF2400CB713D /* GTMUIFont+LineHeight.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMUIFont+LineHeight.m"; sourceTree = "<group>"; };
+ F4E3B3E10EB5EF9A00CB713D /* GTMUIFont+LineHeightTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMUIFont+LineHeightTest.m"; sourceTree = "<group>"; };
+ F4EF8AD40EBFF814008DD6DA /* GTMStackTrace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMStackTrace.h; sourceTree = "<group>"; };
+ F4EF8AD50EBFF814008DD6DA /* GTMStackTrace.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMStackTrace.m; sourceTree = "<group>"; };
+ F4EF8AD60EBFF814008DD6DA /* GTMStackTraceTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMStackTraceTest.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -287,6 +297,9 @@
8BA5F4070E75669000798036 /* GTMABAddressBook.h */,
8BA5F4080E75669000798036 /* GTMABAddressBook.m */,
8BA5F52B0E7567AB00798036 /* GTMABAddressBookTest.m */,
+ F4E3B3D60EB5EF2400CB713D /* GTMUIFont+LineHeight.h */,
+ F4E3B3D70EB5EF2400CB713D /* GTMUIFont+LineHeight.m */,
+ F4E3B3E10EB5EF9A00CB713D /* GTMUIFont+LineHeightTest.m */,
8BA5F4090E75669000798036 /* TestData */,
);
path = iPhone;
@@ -303,9 +316,6 @@
8BC047760DAE928A00C2D1CA /* Foundation */ = {
isa = PBXGroup;
children = (
- F418AFD40E755D44004FB565 /* GTMPath.h */,
- F418AFD50E755D44004FB565 /* GTMPath.m */,
- F418AFD60E755D44004FB565 /* GTMPathTest.m */,
F439ADE80DBD3C0000BE9B91 /* GTMBase64.h */,
F439ADE90DBD3C0000BE9B91 /* GTMBase64.m */,
F439ADEA0DBD3C0000BE9B91 /* GTMBase64Test.m */,
@@ -354,12 +364,18 @@
8BC0478C0DAE928A00C2D1CA /* GTMNSString+XMLTest.m */,
8BC0478D0DAE928A00C2D1CA /* GTMObjC2Runtime.h */,
8BC047900DAE928A00C2D1CA /* GTMObjectSingleton.h */,
+ F418AFD40E755D44004FB565 /* GTMPath.h */,
+ F418AFD50E755D44004FB565 /* GTMPath.m */,
+ F418AFD60E755D44004FB565 /* GTMPathTest.m */,
F418AFE80E755F21004FB565 /* GTMProgressMonitorInputStream.h */,
F418AFE90E755F21004FB565 /* GTMProgressMonitorInputStream.m */,
F418AFEA0E755F21004FB565 /* GTMProgressMonitorInputStreamTest.m */,
8BC047910DAE928A00C2D1CA /* GTMRegex.h */,
8BC047920DAE928A00C2D1CA /* GTMRegex.m */,
8BC047930DAE928A00C2D1CA /* GTMRegexTest.m */,
+ F4EF8AD40EBFF814008DD6DA /* GTMStackTrace.h */,
+ F4EF8AD50EBFF814008DD6DA /* GTMStackTrace.m */,
+ F4EF8AD60EBFF814008DD6DA /* GTMStackTraceTest.m */,
8BC04A740DAF145200C2D1CA /* GTMSystemVersion.m */,
8BC04A6F0DAF144200C2D1CA /* GTMSystemVersion.h */,
8BC04A710DAF144700C2D1CA /* GTMSystemVersionTest.m */,
@@ -596,6 +612,10 @@
8BDA25140E759A6500C9769D /* GTMNSData+zlibTest.m in Sources */,
8BE839890E89C74B00C611B0 /* GTMDebugThreadValidation.m in Sources */,
8BE83A660E8B059A00C611B0 /* GTMDebugThreadValidationTest.m in Sources */,
+ F4E3B3D80EB5EF2400CB713D /* GTMUIFont+LineHeight.m in Sources */,
+ F4E3B3E20EB5EF9A00CB713D /* GTMUIFont+LineHeightTest.m in Sources */,
+ F4EF8AD70EBFF814008DD6DA /* GTMStackTrace.m in Sources */,
+ F4EF8AD80EBFF814008DD6DA /* GTMStackTraceTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -634,6 +654,8 @@
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = GTM_Prefix.pch;
+ GCC_WARN_SHADOW = YES;
+ GTM_EXTRA_WARNING_OVERRIDE_CFLAGS = "-Wno-unused-parameter";
};
name = "Debug-gcov";
};
@@ -653,6 +675,8 @@
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = GTM_Prefix.pch;
+ GCC_WARN_SHADOW = YES;
+ GTM_EXTRA_WARNING_OVERRIDE_CFLAGS = "-Wno-unused-parameter";
};
name = Debug;
};
@@ -662,6 +686,8 @@
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = GTM_Prefix.pch;
+ GCC_WARN_SHADOW = YES;
+ GTM_EXTRA_WARNING_OVERRIDE_CFLAGS = "-Wno-unused-parameter";
};
name = Release;
};
diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt
index 1ffac73..1fdd464 100644
--- a/ReleaseNotes.txt
+++ b/ReleaseNotes.txt
@@ -155,6 +155,13 @@ Changes since 1.5.1
make sure to turn off zombies by defining the GTM_DISABLE_ZOMBIES variable,
otherwise every memory allocation you do will look like a leak.
+- Added has ability to check if a script has an open handler to
+ GTMNSAppleScript+Handler.
+
+- GTMStackTrace support for building a trace from the call stack in an
+ NSException (for 10.5+ and iPhone).
+
+
Release 1.5.1
Changes since 1.5.0
16-June-2008
diff --git a/XcodeConfig/subconfig/General.xcconfig b/XcodeConfig/subconfig/General.xcconfig
index 89304b9..2aea15f 100644
--- a/XcodeConfig/subconfig/General.xcconfig
+++ b/XcodeConfig/subconfig/General.xcconfig
@@ -128,7 +128,8 @@ GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES
// GTM should always compile with full warnings.
GTM_GENERAL_WARNING_CFLAGS1 = -Wall -Wendif-labels -Winvalid-pch -Wformat=2 -Wmissing-format-attribute -Wwrite-strings -Wstack-protector -Wstrict-aliasing=2
GTM_GENERAL_WARNING_CFLAGS2 = -Wpacked -Wmissing-field-initializers
-GTM_GENERAL_WARNING_CFLAGS3 = -Wextra -Wno-unused-parameter -Wpointer-arith -Wdisabled-optimization -Wfloat-equal
+GTM_EXTRA_WARNING_OVERRIDE_CFLAGS = -Wno-unused-parameter -Wno-sign-compare
+GTM_GENERAL_WARNING_CFLAGS3 = -Wextra $(GTM_EXTRA_WARNING_OVERRIDE_CFLAGS) -Wpointer-arith -Wdisabled-optimization -Wfloat-equal
GTM_GENERAL_WARNING_CFLAGS = $(GTM_GENERAL_WARNING_CFLAGS1) $(GTM_GENERAL_WARNING_CFLAGS2) $(GTM_GENERAL_WARNING_CFLAGS3)
// GCC_WARN_UNINITIALIZED_AUTOS is defined in the release/debug xcconfigs.
@@ -140,7 +141,6 @@ GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES
GCC_WARN_ABOUT_RETURN_TYPE = YES
GCC_WARN_MISSING_PARENTHESES = YES
GCC_WARN_ABOUT_POINTER_SIGNEDNESS = YES
-GCC_WARN_SIGN_COMPARE = YES
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES
GCC_WARN_UNKNOWN_PRAGMAS = YES
GCC_WARN_UNUSED_LABEL = YES
@@ -148,7 +148,12 @@ GCC_WARN_UNUSED_FUNCTION = YES
GCC_WARN_UNUSED_VALUE = YES
GCC_WARN_UNUSED_VARIABLE = YES
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES
-GCC_WARN_SHADOW = YES
+// We don't turn on shadow and sign comparisons because too many 3rd party
+// libaries don't compile with them turned on (sign compare rarely catches
+// errors, but shadow is very useful).
+// NOTE: sign compare is also controlled by -Wextra, we we override it above.
+// GCC_WARN_SHADOW = YES
+// GCC_WARN_SIGN_COMPARE = YES
GCC_WARN_PEDANTIC = NO
GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO
diff --git a/iPhone/GTMUIFont+LineHeight.h b/iPhone/GTMUIFont+LineHeight.h
new file mode 100644
index 0000000..cb2d206
--- /dev/null
+++ b/iPhone/GTMUIFont+LineHeight.h
@@ -0,0 +1,24 @@
+//
+// GTMUIFont+LineHeight.h
+//
+// Copyright 2008 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
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+@interface UIFont (GTMLineHeight)
+- (CGFloat)gtm_lineHeight;
+@end
diff --git a/iPhone/GTMUIFont+LineHeight.m b/iPhone/GTMUIFont+LineHeight.m
new file mode 100644
index 0000000..74f8a8f
--- /dev/null
+++ b/iPhone/GTMUIFont+LineHeight.m
@@ -0,0 +1,25 @@
+//
+// GTMUIFont+LineHeight.m
+//
+// Copyright 2008 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
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMUIFont+LineHeight.h"
+
+@implementation UIFont (GTMLineHeight)
+- (CGFloat)gtm_lineHeight {
+ return [@"Fake line with gjy" sizeWithFont:self].height;
+}
+@end
diff --git a/iPhone/GTMUIFont+LineHeightTest.m b/iPhone/GTMUIFont+LineHeightTest.m
new file mode 100644
index 0000000..8da28eb
--- /dev/null
+++ b/iPhone/GTMUIFont+LineHeightTest.m
@@ -0,0 +1,40 @@
+//
+// GTMUIFont+LineHeightTest.m
+//
+// Copyright 2008 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
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMSenTestCase.h"
+#import "GTMUIFont+LineHeight.h"
+
+@interface GTMUIFontLineHeightTest : GTMTestCase
+@end
+
+
+@implementation GTMUIFontLineHeightTest
+
+- (void)testLineHeight {
+ UIFont *font = [UIFont systemFontOfSize:[UIFont systemFontSize]];
+ STAssertNotNil(font, nil);
+ STAssertGreaterThanOrEqual([font gtm_lineHeight], (CGFloat)5.0, nil);
+
+ UIFont *fontSmall = [UIFont systemFontOfSize:[UIFont smallSystemFontSize]];
+ STAssertNotNil(fontSmall, nil);
+ STAssertGreaterThanOrEqual([fontSmall gtm_lineHeight], (CGFloat)5.0, nil);
+
+ STAssertGreaterThan([font gtm_lineHeight], [fontSmall gtm_lineHeight], nil);
+}
+
+@end