[macruby-changes] [4930] MacRuby/trunk

source_changes at macosforge.org source_changes at macosforge.org
Mon Nov 22 14:53:02 PST 2010


Revision: 4930
          http://trac.macosforge.org/projects/ruby/changeset/4930
Author:   eloy.de.enige at gmail.com
Date:     2010-11-22 14:53:00 -0800 (Mon, 22 Nov 2010)
Log Message:
-----------
Check if a class, or an instance of that class, conforms to a protocol.

The conformsToProtocol: implementation checks if the class implements:
* the required class methods
* the required instance methods
* the methods required by protocols adopted by the protocol

Modified Paths:
--------------
    MacRuby/trunk/object.c
    MacRuby/trunk/spec/macruby/core/object_spec.rb
    MacRuby/trunk/spec/macruby/fixtures/object.m
    MacRuby/trunk/spec/macruby/fixtures/object.rb

Modified: MacRuby/trunk/object.c
===================================================================
--- MacRuby/trunk/object.c	2010-11-22 14:14:11 UTC (rev 4929)
+++ MacRuby/trunk/object.c	2010-11-22 22:53:00 UTC (rev 4930)
@@ -2853,6 +2853,91 @@
     return RCLASS_META(klass) ? Qtrue : Qfalse;
 }
 
+static bool
+implementsProtocolMethods(Protocol *p, Class klass, bool instanceMethods)
+{
+    unsigned int count = 0;
+    struct objc_method_description *list =
+	protocol_copyMethodDescriptionList(p, true, instanceMethods,
+	    &count);
+    if (list != NULL) {
+	bool success = true;
+	for (unsigned int i = 0; i < count; i++) {
+	    SEL msel = list[i].name;
+	    Method m;
+	    if (instanceMethods) {
+		m = class_getInstanceMethod(klass, msel);
+	    }
+	    else {
+		m = class_getClassMethod(klass, msel);
+	    }
+	    if (m == NULL) {
+		success = false;
+		break;
+	    }
+	}
+	free(list);
+	if (success) {
+		return true;
+	}
+    }
+    return false;
+}
+
+static bool
+conformsToProtocol(Class klass, Protocol *p)
+{
+    if (implementsProtocolMethods(p, klass, true)
+	&& implementsProtocolMethods(p, klass, false)) {
+	unsigned int count = 0;
+	Protocol **list = protocol_copyProtocolList(p, &count);
+	bool success = true;
+	for (unsigned int i = 0; i < count; i++) {
+	    if (!conformsToProtocol(klass, list[i])) {
+		success = false;
+		break;
+	    }
+	}
+	free(list);
+	return success;
+    }
+    return false;
+}
+
+static bool
+conformsToProtocolAndAncestors(void *self, SEL sel, Class klass,
+				void *protocol, IMP super)
+{
+    if (protocol != NULL) {
+	Protocol *p = (Protocol *)protocol;
+	if (conformsToProtocol(klass, p)) {
+	    return true;
+	}
+    }
+    if (super != NULL) {
+	return ((bool(*)(void *, SEL, Protocol *))super)(self, sel, protocol);
+    }
+    return false;
+}
+
+static IMP old_conformsToProtocol_imp = NULL;
+
+static bool
+robj_conformsToProtocol(void *self, SEL sel, void *protocol)
+{
+    return conformsToProtocolAndAncestors(self, sel, object_getClass(self),
+	protocol, old_conformsToProtocol_imp);
+}
+
+static IMP old_conformsToProtocol_mimp = NULL;
+
+static bool
+robj_conformsToProtocol_m(void *self, SEL sel, void *protocol)
+{
+    return conformsToProtocolAndAncestors(self, sel, (Class)self, protocol,
+	old_conformsToProtocol_mimp);
+}
+
 /*
  *  Document-class: Class
  *
@@ -2951,6 +3036,12 @@
     RCLASS_SET_VERSION_FLAG(rb_cRubyObject, RCLASS_IS_OBJECT_SUBCLASS);
     rb_define_object_special_methods(rb_cRubyObject);
 
+    SEL ctp = sel_registerName("conformsToProtocol:");
+    old_conformsToProtocol_imp = rb_objc_install_method((Class)rb_cRubyObject,
+	    ctp, (IMP)robj_conformsToProtocol);
+    old_conformsToProtocol_mimp = rb_objc_install_method(
+	    *(Class *)rb_cRubyObject, ctp, (IMP)robj_conformsToProtocol_m);
+
     eqlSel = sel_registerName("eql?:");
 
     rb_objc_define_method(*(VALUE *)rb_cModule, "alloc", rb_module_s_alloc, 0);

Modified: MacRuby/trunk/spec/macruby/core/object_spec.rb
===================================================================
--- MacRuby/trunk/spec/macruby/core/object_spec.rb	2010-11-22 14:14:11 UTC (rev 4929)
+++ MacRuby/trunk/spec/macruby/core/object_spec.rb	2010-11-22 22:53:00 UTC (rev 4930)
@@ -3,6 +3,7 @@
 
 FixtureCompiler.require! "object"
 TestObject # force dynamic load
+TestProtocolConformance
 require File.join(FIXTURES, 'object')
 
 describe "A pure MacRuby Class" do
@@ -25,6 +26,24 @@
     o.class.should == TestObjectPureMacRuby
     o.initialized?.should == true
   end
+
+  it "returns whether or not it conforms to the given protocol" do
+    o = TestObjectThatDoesNotCompletelyConformToProtocol
+    conforms = TestProtocolConformance.checkIfObjectConformsToTestProtocol(o)
+    conforms.should == 0
+
+    o = TestObjectThatDoesNotCompletelyConformToProtocol.new
+    conforms = TestProtocolConformance.checkIfObjectConformsToTestProtocol(o)
+    conforms.should == 0
+
+    o = TestObjectThatConformsToProtocol
+    conforms = TestProtocolConformance.checkIfObjectConformsToTestProtocol(o)
+    conforms.should == 1
+
+    o = TestObjectThatConformsToProtocol.new
+    conforms = TestProtocolConformance.checkIfObjectConformsToTestProtocol(o)
+    conforms.should == 1
+  end
 end
 
 # Atm we don't actually do anything with the given zone, so the current

Modified: MacRuby/trunk/spec/macruby/fixtures/object.m
===================================================================
--- MacRuby/trunk/spec/macruby/fixtures/object.m	2010-11-22 14:14:11 UTC (rev 4929)
+++ MacRuby/trunk/spec/macruby/fixtures/object.m	2010-11-22 22:53:00 UTC (rev 4930)
@@ -60,4 +60,45 @@
 
 @end
 
+ at protocol AnotherTestProtocol
+
++ (int)anotherClassMethod;
+- (int)anotherInstanceMethod;
+
+ at optional
+
++ (int)anotherOptionalClassMethod;
+- (int)anotherOptionalInstanceMethod;
+
+ at end
+
+ at protocol TestProtocol <AnotherTestProtocol>
+
++ (int)aClassMethod;
++ (int)aClassMethodWithArg:(int)arg;
++ (int)aClassMethodWithArg:(int)arg1 anotherArg:(int)arg2;
+
+- (int)anInstanceMethod;
+- (int)anInstanceMethodWithArg:(int)arg;
+- (int)anInstanceMethodWithArg:(int)arg1 anotherArg:(int)arg2;
+
+ at optional
+
++ (int)anOptionalClassMethod;
+- (int)anOptionalInstanceMethod;
+
+ at end
+
+ at interface TestProtocolConformance : NSObject
+ at end
+
+ at implementation TestProtocolConformance
+
++ (BOOL)checkIfObjectConformsToTestProtocol:(id)object
+{
+  return [object conformsToProtocol:@protocol(TestProtocol)];
+}
+
+ at end
+
 void Init_object(void) {}

Modified: MacRuby/trunk/spec/macruby/fixtures/object.rb
===================================================================
--- MacRuby/trunk/spec/macruby/fixtures/object.rb	2010-11-22 14:14:11 UTC (rev 4929)
+++ MacRuby/trunk/spec/macruby/fixtures/object.rb	2010-11-22 22:53:00 UTC (rev 4930)
@@ -4,3 +4,31 @@
   end
   def initialized?; @initialized; end
 end
+
+class TestObjectThatDoesNotCompletelyConformToProtocol
+  def anInstanceMethod
+  end
+
+  def anInstanceMethodWithArg(arg)
+  end
+
+  def anInstanceMethodWithArg(arg1, anotherArg: arg2)
+  end
+
+  def self.aClassMethod
+  end
+
+  def self.aClassMethodWithArg(arg)
+  end
+
+  def self.aClassMethodWithArg(arg1, anotherArg: arg2)
+  end
+end
+
+class TestObjectThatConformsToProtocol < TestObjectThatDoesNotCompletelyConformToProtocol
+  def anotherInstanceMethod
+  end
+
+  def self.anotherClassMethod
+  end
+end
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20101122/5ce60d30/attachment.html>


More information about the macruby-changes mailing list