[Rubycocoa-devel 1277] BUG: Direct override and inherited methods

Zurück zum Archiv-Index

Dave Vasilevsky djvas****@gmail*****
Sat Dec 15 11:02:33 JST 2007


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




More information about the Rubycocoa-devel mailing list
Zurück zum Archiv-Index