From b2fa9805eb63d5daa1dd8fab1edf0c85fb7ebfc0 Mon Sep 17 00:00:00 2001 From: "gtm.daemon" Date: Fri, 8 Jan 2010 04:36:12 +0000 Subject: [Author: dmaclach] Added some basic casting debugging goodness to GTM. Opinions? added to help catch a bug in QSB. R=thomasvl DELTA=55 (55 added, 0 deleted, 0 changed) --- AddressBook/GTMABAddressBook.m | 5 ++- AppKit/GTMCarbonEvent.m | 5 ++- AppKit/GTMHotKeyTextField.m | 4 +- AppKit/GTMLargeTypeWindow.m | 6 ++- AppKit/GTMWindowSheetController.m | 4 +- DebugUtils/GTMTypeCasting.h | 81 +++++++++++++++++++++++++++++++++++++ Foundation/GTMFileSystemKQueue.m | 5 ++- Foundation/GTMHTTPServer.m | 4 +- Foundation/GTMObjC2RuntimeTest.m | 7 ++-- Foundation/GTMSignalHandler.m | 3 +- GTM.xcodeproj/project.pbxproj | 4 ++ GTMiPhone.xcodeproj/project.pbxproj | 2 + ReleaseNotes.txt | 3 ++ 13 files changed, 119 insertions(+), 14 deletions(-) create mode 100644 DebugUtils/GTMTypeCasting.h diff --git a/AddressBook/GTMABAddressBook.m b/AddressBook/GTMABAddressBook.m index 77642db..637494b 100644 --- a/AddressBook/GTMABAddressBook.m +++ b/AddressBook/GTMABAddressBook.m @@ -18,6 +18,7 @@ #import "GTMABAddressBook.h" #import "GTMGarbageCollection.h" +#import "GTMTypeCasting.h" #if GTM_IPHONE_SDK #import @@ -811,7 +812,9 @@ typedef struct { isEqual = [label isEqual:objLabel]; if (isEqual) { id value = [self valueAtIndex:i]; - id objValue = [(GTMABMultiValue*)object valueAtIndex:i]; + GTMABMultiValue *multiValueObject + = GTM_STATIC_CAST(GTMABMultiValue, object); + id objValue = [multiValueObject valueAtIndex:i]; isEqual = [value isEqual:objValue]; } } diff --git a/AppKit/GTMCarbonEvent.m b/AppKit/GTMCarbonEvent.m index 16c46c8..2166331 100644 --- a/AppKit/GTMCarbonEvent.m +++ b/AppKit/GTMCarbonEvent.m @@ -19,6 +19,7 @@ #import "GTMCarbonEvent.h" #import "GTMObjectSingleton.h" #import "GTMDebugSelectorValidation.h" +#import "GTMTypeCasting.h" // Wrapper for all the info we need about a hotkey that we can store in a // Foundation storage class. We expecct selector to have this signature: @@ -432,8 +433,8 @@ static OSStatus EventHandler(EventHandlerCallRef inHandler, EventRef inEvent, void *inUserData) { GTMCarbonEvent *event = [GTMCarbonEvent eventWithEvent:inEvent]; - GTMCarbonEventHandler *handler= (GTMCarbonEventHandler *)inUserData; - check([handler isKindOfClass:[GTMCarbonEventHandler class]]); + GTMCarbonEventHandler *handler + = GTM_STATIC_CAST(GTMCarbonEventHandler, inUserData); // First check to see if our delegate cares about this event. If the delegate // handles it (i.e responds to it and does not return eventNotHandledErr) we diff --git a/AppKit/GTMHotKeyTextField.m b/AppKit/GTMHotKeyTextField.m index 864ba54..4524340 100644 --- a/AppKit/GTMHotKeyTextField.m +++ b/AppKit/GTMHotKeyTextField.m @@ -21,6 +21,7 @@ #import "GTMSystemVersion.h" #import "GTMObjectSingleton.h" #import "GTMNSObject+KeyValueObserving.h" +#import "GTMTypeCasting.h" #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 typedef struct __TISInputSource* TISInputSourceRef; @@ -370,7 +371,8 @@ static CFStringRef kGTM_TISPropertyUnicodeKeyLayoutData = NULL; - (void)textDidChange:(NSNotification *)notification { // Sanity - GTMHotKeyFieldEditor *fieldEditor = [notification object]; + GTMHotKeyFieldEditor *fieldEditor = GTM_STATIC_CAST(GTMHotKeyFieldEditor, + [notification object]); if (![fieldEditor isKindOfClass:[GTMHotKeyFieldEditor class]]) { _GTMDevLog(@"Field editor not appropriate for field, check window delegate"); return; diff --git a/AppKit/GTMLargeTypeWindow.m b/AppKit/GTMLargeTypeWindow.m index dfef15f..de85ec8 100644 --- a/AppKit/GTMLargeTypeWindow.m +++ b/AppKit/GTMLargeTypeWindow.m @@ -22,7 +22,7 @@ #import "GTMGeometryUtils.h" #import "GTMNSBezierPath+RoundRect.h" #import "GTMMethodCheck.h" - +#import "GTMTypeCasting.h" // How far to inset the text from the edge of the window static const CGFloat kEdgeInset = 16.0; @@ -221,7 +221,9 @@ static NSTimeInterval gGTMLargeTypeWindowFadeAnimationDuration = 0.333; // Give the user some feedback that a copy has occurred NSTimeInterval dur = [[self class] copyAnimationDuration]; - [(GTMLargeTypeBackgroundView*)[self contentView] animateCopyWithDuration:dur]; + GTMLargeTypeBackgroundView *view + = GTM_STATIC_CAST(GTMLargeTypeBackgroundView, [self contentView]); + [view animateCopyWithDuration:dur]; } - (BOOL)canBecomeKeyWindow { diff --git a/AppKit/GTMWindowSheetController.m b/AppKit/GTMWindowSheetController.m index ae14685..59a866d 100644 --- a/AppKit/GTMWindowSheetController.m +++ b/AppKit/GTMWindowSheetController.m @@ -19,6 +19,7 @@ #import "GTMWindowSheetController.h" #import "GTMDefines.h" +#import "GTMTypeCasting.h" @interface GTMWSCSheetInfo : NSObject { @public @@ -462,7 +463,8 @@ willPositionSheet:(NSWindow*)sheet } - (void)notificationHappened:(NSNotification*)notification { - [self viewDidChangeSize:[notification object]]; + NSView *view = GTM_STATIC_CAST(NSView, [notification object]); + [self viewDidChangeSize:view]; } - (void)viewDidChangeSize:(NSView*)view { diff --git a/DebugUtils/GTMTypeCasting.h b/DebugUtils/GTMTypeCasting.h new file mode 100644 index 0000000..1e9ebb9 --- /dev/null +++ b/DebugUtils/GTMTypeCasting.h @@ -0,0 +1,81 @@ +// +// GTMTypeCasting.h +// +// Copyright 2010 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 +#import "GTMDefines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// These are some basic macros for making down-casting safer in Objective C. +// They are loosely based on the same cast types with similar names in C++. +// A typical usage would look like this: +// +// Bar* b = [[Bar alloc] init]; +// Foo* a = GTM_STATIC_CAST(Foo, b); +// +// Note that it's GTM_STATIC_CAST(Foo, b) and not GTM_STATIC_CAST(Foo*, b). +// +// GTM_STATIC_CAST runs only in debug mode, and will assert if and only if: +// - object is non nil +// - [object isKindOfClass:[cls class]] returns nil +// +// otherwise it returns object. +// +// GTM_DYNAMIC_CAST runs in both debug and release and will return nil if +// - object is nil +// - [object isKindOfClass:[cls class]] returns nil +// +// otherwise it returns object. +// + +// Support functions for dealing with casting. +GTM_INLINE id GTMDynamicCastSupport(Class cls, id object) { + id value = nil; + if (object) { + _GTMDevAssert(cls, @"Nil Class"); + if ([object isKindOfClass:cls]) { + value = object; + } + } + return value; +} + +GTM_INLINE id GTMStaticCastSupport(Class cls, id object) { + id value = nil; + if (object) { + value = GTMDynamicCastSupport(cls, object); + _GTMDevAssert(value, @"Could not cast %@ to class %@", object, cls); + } + return value; +} + +#ifndef GTM_STATIC_CAST + #ifdef DEBUG + #define GTM_STATIC_CAST(type, object) GTMStaticCastSupport([type class], \ + object) + #else + #define GTM_STATIC_CAST(type, object) ((type *) (object)) + #endif +#endif + +#ifndef GTM_DYNAMIC_CAST + #define GTM_DYNAMIC_CAST(type, object) GTMDynamicCastSupport([type class], \ + object) +#endif diff --git a/Foundation/GTMFileSystemKQueue.m b/Foundation/GTMFileSystemKQueue.m index 1db71c1..e423de2 100644 --- a/Foundation/GTMFileSystemKQueue.m +++ b/Foundation/GTMFileSystemKQueue.m @@ -20,7 +20,7 @@ #import #import "GTMDefines.h" #import "GTMDebugSelectorValidation.h" - +#import "GTMTypeCasting.h" // File descriptor for the kqueue that will hold all of our file system events. static int gFileSystemKQueueFileDescriptor = 0; @@ -118,7 +118,8 @@ static void SocketCallBack(CFSocketRef socketref, CFSocketCallBackType type, if (kevent(gFileSystemKQueueFileDescriptor, NULL, 0, &event, 1, NULL) == -1) { _GTMDevLog(@"could not pick up kqueue event. Errno %d", errno); // COV_NF_LINE } else { - GTMFileSystemKQueue *fskq = (GTMFileSystemKQueue *)event.udata; + GTMFileSystemKQueue *fskq = GTM_STATIC_CAST(GTMFileSystemKQueue, + event.udata); [fskq notify:event.fflags]; } diff --git a/Foundation/GTMHTTPServer.m b/Foundation/GTMHTTPServer.m index 7887ade..a514842 100644 --- a/Foundation/GTMHTTPServer.m +++ b/Foundation/GTMHTTPServer.m @@ -28,6 +28,7 @@ #import "GTMDebugSelectorValidation.h" #import "GTMGarbageCollection.h" #import "GTMDefines.h" +#import "GTMTypeCasting.h" @interface GTMHTTPServer (PrivateMethods) - (void)acceptedConnectionNotification:(NSNotification *)notification; @@ -283,7 +284,8 @@ startFailed: } - (void)dataAvailableNotification:(NSNotification *)notification { - NSFileHandle *connectionHandle = [notification object]; + NSFileHandle *connectionHandle = GTM_STATIC_CAST(NSFileHandle, + [notification object]); NSMutableDictionary *connDict = [self lookupConnection:connectionHandle]; if (connDict == nil) return; // we are no longer tracking this one diff --git a/Foundation/GTMObjC2RuntimeTest.m b/Foundation/GTMObjC2RuntimeTest.m index 626a625..aa57388 100644 --- a/Foundation/GTMObjC2RuntimeTest.m +++ b/Foundation/GTMObjC2RuntimeTest.m @@ -19,8 +19,7 @@ #import "GTMObjC2Runtime.h" #import "GTMSenTestCase.h" #import "GTMSystemVersion.h" - - +#import "GTMTypeCasting.h" #import @@ -94,7 +93,9 @@ AT_REQUIRED - (void)startedTest:(NSNotification *)notification { // Logs if we are testing on Tiger or Leopard runtime. - NSString *testName = [(SenTest*)[[notification object] test] name]; + SenTestSuiteRun *suiteRun = GTM_STATIC_CAST(SenTestSuiteRun, + [notification object]); + NSString *testName = [[suiteRun test] name]; NSString *className = NSStringFromClass([GTMObjC2RuntimeTest class]); if ([testName isEqualToString:className]) { NSString *runtimeString; diff --git a/Foundation/GTMSignalHandler.m b/Foundation/GTMSignalHandler.m index b6cfb70..dd67269 100644 --- a/Foundation/GTMSignalHandler.m +++ b/Foundation/GTMSignalHandler.m @@ -18,6 +18,7 @@ #import "GTMSignalHandler.h" #import "GTMDefines.h" +#import "GTMTypeCasting.h" #import // for kqueue() and kevent #import "GTMDebugSelectorValidation.h" @@ -116,7 +117,7 @@ static void SocketCallBack(CFSocketRef socketref, CFSocketCallBackType type, if (kevent(gSignalKQueueFileDescriptor, NULL, 0, &event, 1, NULL) == -1) { _GTMDevLog(@"could not pick up kqueue event. Errno %d", errno); // COV_NF_LINE } else { - GTMSignalHandler *handler = (GTMSignalHandler *)event.udata; + GTMSignalHandler *handler = GTM_STATIC_CAST(GTMSignalHandler, event.udata); [handler notify]; } diff --git a/GTM.xcodeproj/project.pbxproj b/GTM.xcodeproj/project.pbxproj index 947bda3..2ff56ac 100644 --- a/GTM.xcodeproj/project.pbxproj +++ b/GTM.xcodeproj/project.pbxproj @@ -213,6 +213,7 @@ 8BEEA90D0DA7446300894774 /* GTMUnitTestingImage.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8BEEA90A0DA7446300894774 /* GTMUnitTestingImage.tiff */; }; 8BEEA90E0DA7446300894774 /* GTMUnitTestingWindow.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8BEEA90B0DA7446300894774 /* GTMUnitTestingWindow.tiff */; }; 8BEEA90F0DA7446300894774 /* GTMUnitTestingView.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8BEEA90C0DA7446300894774 /* GTMUnitTestingView.tiff */; }; + 8BF2555310F65B56000490C8 /* GTMTypeCasting.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BF2555110F65B56000490C8 /* GTMTypeCasting.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8BF4D2E60FC7073A009ABC3F /* GTMGoogleSearch.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BF4D2E30FC7073A009ABC3F /* GTMGoogleSearch.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8BF4D2E70FC7073A009ABC3F /* GTMGoogleSearch.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BF4D2E40FC7073A009ABC3F /* GTMGoogleSearch.m */; }; 8BF4D2E80FC70751009ABC3F /* GTMGoogleSearchTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BF4D2E20FC7073A009ABC3F /* GTMGoogleSearchTest.m */; }; @@ -627,6 +628,7 @@ 8BEEA90A0DA7446300894774 /* GTMUnitTestingImage.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = GTMUnitTestingImage.tiff; sourceTree = ""; }; 8BEEA90B0DA7446300894774 /* GTMUnitTestingWindow.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = GTMUnitTestingWindow.tiff; sourceTree = ""; }; 8BEEA90C0DA7446300894774 /* GTMUnitTestingView.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = GTMUnitTestingView.tiff; sourceTree = ""; }; + 8BF2555110F65B56000490C8 /* GTMTypeCasting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMTypeCasting.h; sourceTree = ""; }; 8BF4D2E20FC7073A009ABC3F /* GTMGoogleSearchTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMGoogleSearchTest.m; sourceTree = ""; }; 8BF4D2E30FC7073A009ABC3F /* GTMGoogleSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMGoogleSearch.h; sourceTree = ""; }; 8BF4D2E40FC7073A009ABC3F /* GTMGoogleSearch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMGoogleSearch.m; sourceTree = ""; }; @@ -1358,6 +1360,7 @@ F4FF22760D9D47FB003880AC /* DebugUtils */ = { isa = PBXGroup; children = ( + 8BF2555110F65B56000490C8 /* GTMTypeCasting.h */, F4FF22770D9D4835003880AC /* GTMDebugSelectorValidation.h */, 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */, 8B6F31F40DA3489B0052CA40 /* GTMMethodCheck.h */, @@ -1448,6 +1451,7 @@ 8B307FF91056B773006C4C7A /* GTMNSNumber+64Bit.h in Headers */, F4C6248B109753960069CADD /* GTMIBArray.h in Headers */, 8B158ADE10A8C42000C93125 /* GTMNSAnimation+Duration.h in Headers */, + 8BF2555310F65B56000490C8 /* GTMTypeCasting.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/GTMiPhone.xcodeproj/project.pbxproj b/GTMiPhone.xcodeproj/project.pbxproj index 039b578..a3929a7 100644 --- a/GTMiPhone.xcodeproj/project.pbxproj +++ b/GTMiPhone.xcodeproj/project.pbxproj @@ -257,6 +257,7 @@ 8BE839870E89C74A00C611B0 /* GTMDebugThreadValidation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMDebugThreadValidation.m; sourceTree = ""; }; 8BE839880E89C74A00C611B0 /* GTMDebugThreadValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDebugThreadValidation.h; sourceTree = ""; }; 8BE83A650E8B059A00C611B0 /* GTMDebugThreadValidationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMDebugThreadValidationTest.m; sourceTree = ""; }; + 8BF2568E10F673D1000490C8 /* GTMTypeCasting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMTypeCasting.h; sourceTree = ""; }; 8BF4D3E00FC72A46009ABC3F /* GTMGoogleSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMGoogleSearch.h; sourceTree = ""; }; 8BF4D3E10FC72A46009ABC3F /* GTMGoogleSearch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMGoogleSearch.m; sourceTree = ""; }; 8BF4D3E20FC72A46009ABC3F /* GTMGoogleSearchTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMGoogleSearchTest.m; sourceTree = ""; }; @@ -506,6 +507,7 @@ 8BC0479A0DAE928A00C2D1CA /* DebugUtils */ = { isa = PBXGroup; children = ( + 8BF2568E10F673D1000490C8 /* GTMTypeCasting.h */, 8BC0479B0DAE928A00C2D1CA /* GTMDebugSelectorValidation.h */, 8BE83A650E8B059A00C611B0 /* GTMDebugThreadValidationTest.m */, 8BE839870E89C74A00C611B0 /* GTMDebugThreadValidation.m */, diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index ef9dc49..a61862e 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -382,6 +382,9 @@ Changes since 1.5.1 - iPhone unit tests now print "Test Case '-[TEST SELECTOR]' started." before each test. +- Added GTMTypeCasting.h which gives you safer objective-c casts based on + C++ static_cast and dynamic_cast. + Release 1.5.1 Changes since 1.5.0 -- cgit v1.2.3