Laurent Sansonetti
lsans****@apple*****
Thu Dec 20 07:38:00 JST 2007
Applied to trunk. Thanks a lot for the patch :-) Laurent On Dec 15, 2007, at 3:02 AM, Dave Vasilevsky wrote: > Hi, > > I've been overriding methods of Objective-C classes with ruby methods, > and encountered some weird behavior. It turns out that RubyCocoa isn't > checking whether the method being overridden belongs to the class at > hand. If the method actually is inherited, RubyCocoa ends up changing > not only this class, but some of its ancestors as well. > > The solution is to detect whether or not the method is inherited. If > it is, don't do a direct override, instead add a method with the new > behavior. Patch with test case follows. > > Cheers, > Dave > > > Index: framework/src/objc/OverrideMixin.m > =================================================================== > --- framework/src/objc/OverrideMixin.m (revision 2158) > +++ framework/src/objc/OverrideMixin.m (working copy) > @@ -390,8 +390,28 @@ > OVMIX_LOG("Already registered Ruby method by selector '%s' types > '%s', skipping...", (char *)method, me_types); > return; > } > + > +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4 > + if (direct_override) { > + // It's only ok to use setImplementation if this method is in > our own > + // class--otherwise it will change the behavior of our ancestors. > + Method *meth_list, *iter; > + BOOL ok = NO; > + unsigned int count = 0; > + > + // Search our class' methods > + iter = meth_list = class_copyMethodList(klass, &count); > + for (; iter && count; ++iter, --count) { > + if (sel_isEqual(method_getName(*iter), me_name)) { > + ok = YES; > + break; > + } > + } > + if (!ok) > + direct_override = NO; > + free(meth_list); > + } > > -#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4 > if (direct_override) > method_setImplementation(me, imp); > else > Index: tests/tc_ovmix.rb > =================================================================== > --- tests/tc_ovmix.rb (revision 2158) > +++ tests/tc_ovmix.rb (working copy) > @@ -101,6 +101,12 @@ > end > end > > +class OSX::DirectOverrideChild > + def overrideMe > + 'bar' > + end > +end > + > class OSX::NSObject > def self.mySuperClassMethod > 'bar' > @@ -145,6 +151,22 @@ > OSX::DirectOverride.checkOverridenMethods > end > > + def test_direct_inheritance > + assert(OSX::DirectOverrideParent.ancestors.include? > (OSX::NSObject)) > + p = OSX::DirectOverrideParent.alloc.init > + assert_kind_of(OSX::NSString, p.performSelector('overrideMe')) > + assert_equal('foo', p.performSelector('overrideMe').to_s) > + p.checkOverride('foo') > + > + assert(OSX::DirectOverrideChild.ancestors.include? > (OSX::NSObject)) > + assert(OSX::DirectOverrideChild.ancestors.include?( > + OSX::DirectOverrideParent)) > + c = OSX::DirectOverrideChild.alloc.init > + assert_kind_of(OSX::NSString, c.performSelector('overrideMe')) > + assert_equal('bar', c.performSelector('overrideMe').to_s) > + c.checkOverride('bar') > + end > + > def test_super_method > o = OSX::NSString.stringWithCString('blah') > assert_equal('foo', o.mySuperMethod.to_s) > Index: tests/objc_test.m > =================================================================== > --- tests/objc_test.m (revision 2158) > +++ tests/objc_test.m (working copy) > @@ -393,6 +393,37 @@ > > @end > > + > +// This needs to be separate from DirectOverride since the test > might damage > +// this class. > + at interface DirectOverrideParent : NSObject > + at end > + > + at implementation DirectOverrideParent > + > +- (id)overrideMe > +{ > + return @"foo"; > +} > + > +- (void)checkOverride:(NSString *)want > +{ > + id obj = [self overrideMe]; > + > + if (![obj isEqualToString:want]) > + [NSException raise:@"DirectOverrideInheritance" > + format:@"assertion overrideMe failed, got %@", obj]; > +} > + > + at end > + > + at interface DirectOverrideChild : DirectOverrideParent > + at end > + > + at implementation DirectOverrideChild > + at end > + > + > #import <AddressBook/ABPeoplePickerC.h> > > @interface TestFourCharCode : NSObject > > _______________________________________________ > Rubycocoa-devel mailing list > Rubyc****@lists***** > http://lists.sourceforge.jp/mailman/listinfo/rubycocoa-devel