[macruby-changes] [1516] MacRuby/branches/experimental

source_changes at macosforge.org source_changes at macosforge.org
Sat May 2 13:07:26 PDT 2009


Revision: 1516
          http://trac.macosforge.org/projects/ruby/changeset/1516
Author:   lsansonetti at apple.com
Date:     2009-05-02 13:07:25 -0700 (Sat, 02 May 2009)
Log Message:
-----------
compile specialized objc stubs for pure ruby methods and insert them into the runtime

Modified Paths:
--------------
    MacRuby/branches/experimental/roxor.cpp
    MacRuby/branches/experimental/spec/macruby/fixtures/method.m
    MacRuby/branches/experimental/spec/macruby/method_spec.rb
    MacRuby/branches/experimental/spec/macruby/objc_method_spec.rb
    MacRuby/branches/experimental/spec/macruby/spec_helper.rb

Added Paths:
-----------
    MacRuby/branches/experimental/spec/macruby/fixtures/method.rb

Modified: MacRuby/branches/experimental/roxor.cpp
===================================================================
--- MacRuby/branches/experimental/roxor.cpp	2009-05-02 16:47:43 UTC (rev 1515)
+++ MacRuby/branches/experimental/roxor.cpp	2009-05-02 20:07:25 UTC (rev 1516)
@@ -68,14 +68,12 @@
     unsigned char *start;
     unsigned char *end;
     void *imp;
-    ID mid;
 
     RoxorFunction (Function *_f, unsigned char *_start, unsigned char *_end) {
 	f = _f;
 	start = _start;
 	end = _end;
 	imp = NULL; 	// lazy
-	mid = 0;	// lazy
     }
 };
 
@@ -205,6 +203,7 @@
 	Function *compile_ffi_function(void *stub, void *imp, int argc);
 	Function *compile_to_rval_convertor(const char *type);
 	Function *compile_to_ocval_convertor(const char *type);
+	Function *compile_objc_stub(Function *ruby_func, const char *types);
 
 	const Type *convert_type(const char *type);
 
@@ -451,11 +450,15 @@
 struct RoxorFunctionIMP
 {
     NODE *node;
-    SEL sel; 
+    SEL sel;
+    IMP objc_imp;
+    IMP ruby_imp;
 
-    RoxorFunctionIMP(NODE *_node, SEL _sel) { 
+    RoxorFunctionIMP(NODE *_node, SEL _sel, IMP _objc_imp, IMP _ruby_imp) { 
 	node = _node;
 	sel = _sel;
+	objc_imp = _objc_imp;
+	ruby_imp = _ruby_imp;
     }
 };
 
@@ -566,6 +569,8 @@
 	}
 #endif
 
+	std::map<Function *, IMP> objc_to_ruby_stubs;
+
 #if ROXOR_VM_DEBUG
 	long functions_compiled;
 #endif
@@ -582,9 +587,10 @@
 	void set_running(bool flag) { running = flag; }
 
 	struct mcache *method_cache_get(SEL sel, bool super);
+	struct RoxorFunctionIMP *method_func_imp_get(IMP imp);
 	NODE *method_node_get(IMP imp);
-	void add_method(Class klass, SEL sel, IMP imp, NODE *node,
-		const char *types);
+	void add_method(Class klass, SEL sel, IMP imp, IMP ruby_imp,
+		NODE *node, const char *types);
 
 	GlobalVariable *redefined_op_gvar(SEL sel, bool create);
 	bool should_invalidate_inline_op(SEL sel, Class klass);
@@ -2684,17 +2690,24 @@
     return iter->second;
 }
 
-NODE *
-RoxorVM::method_node_get(IMP imp)
+inline struct RoxorFunctionIMP *
+RoxorVM::method_func_imp_get(IMP imp)
 {
     std::map<IMP, struct RoxorFunctionIMP *>::iterator iter =
 	ruby_imps.find(imp);
     if (iter == ruby_imps.end()) {
 	return NULL;
     }
-    return iter->second->node;
+    return iter->second;
 }
 
+inline NODE *
+RoxorVM::method_node_get(IMP imp)
+{
+    struct RoxorFunctionIMP *func_imp = method_func_imp_get(imp);
+    return func_imp == NULL ? NULL : func_imp->node;
+}
+
 extern "C"
 NODE *
 rb_vm_get_method_node(IMP imp)
@@ -2755,7 +2768,7 @@
 }
 
 void
-RoxorVM::add_method(Class klass, SEL sel, IMP imp, NODE *node,
+RoxorVM::add_method(Class klass, SEL sel, IMP imp, IMP ruby_imp, NODE *node,
 		    const char *types)
 {
 #if ROXOR_VM_DEBUG
@@ -2772,18 +2785,29 @@
     class_replaceMethod(klass, sel, imp, types);
 
     // Cache the method node.
-    NODE *old_node = method_node_get(imp);
-    if (old_node == NULL && old_node != node) {
-	ruby_imps[imp] = new RoxorFunctionIMP(node, sel);
+    std::map<IMP, struct RoxorFunctionIMP *>::iterator iter =
+	ruby_imps.find(imp);
+    RoxorFunctionIMP *func_imp;
+    if (iter == ruby_imps.end()) {
+	ruby_imps[imp] = func_imp =
+	    new RoxorFunctionIMP(node, sel, imp, ruby_imp);
     }
-//    else {
-//	assert(old_node == node);
-//    }
+    else {
+	func_imp = iter->second;
+	func_imp->node = node;
+	func_imp->sel = sel;
+	func_imp->ruby_imp = ruby_imp;
+    }
+#if 1
+    if (imp != ruby_imp) {
+	ruby_imps[ruby_imp] = func_imp;
+    }
+#endif
 
     // Invalidate dispatch cache.
-    std::map<SEL, struct mcache *>::iterator iter = mcache.find(sel);
-    if (iter != mcache.end()) {
-	iter->second->flag = 0;
+    std::map<SEL, struct mcache *>::iterator iter2 = mcache.find(sel);
+    if (iter2 != mcache.end()) {
+	iter2->second->flag = 0;
     }
 
     // Invalidate inline operations.
@@ -6210,8 +6234,9 @@
 {
     IMP imp = method_getImplementation(method);
     const char *types = method_getTypeEncoding(method);
-    NODE *node = GET_VM()->method_node_get(imp);
-    if (node == NULL) {
+
+    struct RoxorFunctionIMP *func_imp = GET_VM()->method_func_imp_get(imp);
+    if (func_imp == NULL) {
 	rb_raise(rb_eArgError, "cannot alias non-Ruby method `%s'",
 		sel_getName(method_getName(method)));
     }
@@ -6227,7 +6252,8 @@
 	sel = sel_registerName(tmp);
     }
 
-    GET_VM()->add_method(klass, sel, imp, node, types);
+    GET_VM()->add_method(klass, sel, imp, func_imp->ruby_imp,
+	    func_imp->node, types);
 }
 
 extern "C"
@@ -6367,26 +6393,112 @@
     return -1;
 }
 
+Function *
+RoxorCompiler::compile_objc_stub(Function *ruby_func, const char *types)
+{
+    char buf[100];
+    const char *p = types;
+
+    p = GetFirstType(p, buf, sizeof buf);
+    std::string ret_type(buf);
+    const Type *f_ret_type = convert_type(buf);
+
+    std::vector<const Type *> f_types;
+    // self
+    f_types.push_back(RubyObjTy);
+    p = SkipFirstType(p);
+    // sel
+    f_types.push_back(PtrTy);
+    p = SkipFirstType(p);
+    std::vector<std::string> arg_types;
+    for (unsigned int i = 0; i < ruby_func->arg_size() - 2; i++) {
+	p = GetFirstType(p, buf, sizeof buf);
+	f_types.push_back(convert_type(buf));
+	arg_types.push_back(buf);
+    }
+
+    FunctionType *ft = FunctionType::get(f_ret_type, f_types, false);
+    Function *f = cast<Function>(module->getOrInsertFunction("", ft));
+    Function::arg_iterator arg = f->arg_begin();
+
+    bb = BasicBlock::Create("EntryBlock", f);
+
+    std::vector<Value *> params;
+    params.push_back(arg++); // self
+    params.push_back(arg++); // sel
+
+    for (unsigned int i = 0; i < ruby_func->arg_size() - 2; i++) {
+	Value *ruby_arg = compile_conversion_to_ruby(arg_types[i].c_str(),
+		f_types[i + 2], arg++);
+	params.push_back(ruby_arg);
+    }
+
+    Value *ret_val = CallInst::Create(ruby_func, params.begin(),
+	    params.end(), "", bb);
+
+    if (f_ret_type != Type::VoidTy) {
+	ret_val = compile_conversion_to_c(ret_type.c_str(), ret_val,
+		new AllocaInst(f_ret_type, "", bb));
+
+	ReturnInst::Create(ret_val, bb);
+    }
+    else {
+	ReturnInst::Create(bb);
+    }
+
+    return f;
+}
+
 #if ROXOR_ULTRA_LAZY_JIT
 static void
-resolve_method(Class klass, SEL sel, rb_vm_method_source_t *m)
+resolve_method(Class klass, SEL sel, Function *func, NODE *node, IMP imp,
+	       Method m)
 {
-    IMP imp = GET_VM()->compile(m->func);
+    const int oc_arity = rb_vm_node_arity(node).real + 3;
 
-    const int oc_arity = rb_vm_node_arity(m->node).real;
+    char types[100];
+    bs_element_method_t *bs_method = GET_VM()->find_bs_method(klass, sel);
 
-    char *types = (char *)alloca(oc_arity + 4);
-    types[0] = '@';
-    types[1] = '@';
-    types[2] = ':';
-    for (int i = 0; i < oc_arity; i++) {
-	types[3 + i] = '@';
+    if (m == NULL || !rb_objc_get_types(Qnil, klass, sel, bs_method, types,
+					sizeof types)) {
+	assert((unsigned int)oc_arity < sizeof(types));
+	types[0] = '@';
+	types[1] = '@';
+	types[2] = ':';
+	for (int i = 3; i < oc_arity; i++) {
+	    types[i] = '@';
+	}
+	types[oc_arity] = '\0';
     }
-    types[3 + oc_arity] = '\0';
+    else {
+	const int m_argc = method_getNumberOfArguments(m);
+	if (m_argc < oc_arity) {
+	    for (int i = m_argc; i < oc_arity; i++) {
+		strcat(types, "@");
+	    }
+	}
+    }
 
-    GET_VM()->add_method(klass, sel, imp, m->node, types);
-} 
+    std::map<Function *, IMP>::iterator iter =
+	GET_VM()->objc_to_ruby_stubs.find(func);
+    IMP objc_imp;
+    if (iter == GET_VM()->objc_to_ruby_stubs.end()) {
+	Function *objc_func = RoxorCompiler::shared->compile_objc_stub(func,
+		types);
+	objc_imp = GET_VM()->compile(objc_func);
+	GET_VM()->objc_to_ruby_stubs[func] = objc_imp;
+    }
+    else {
+	objc_imp = iter->second;
+    }
 
+    if (imp == NULL) {
+	imp = GET_VM()->compile(func);
+    }
+
+    GET_VM()->add_method(klass, sel, objc_imp, imp, node, types);
+}
+
 static bool
 rb_vm_resolve_method(Class klass, SEL sel)
 {
@@ -6427,7 +6539,7 @@
 
 	if (k != NULL) {
 	    rb_vm_method_source_t *m = iter->second;
-	    resolve_method(iter->first, sel, m);
+	    resolve_method(iter->first, sel, m->func, m->node, NULL, NULL);
 	    map->erase(iter++);
 	    free(m);
 	    did_something = true;
@@ -6456,16 +6568,18 @@
     const bool genuine_selector = sel_name[strlen(sel_name) - 1] == ':';
     bool redefined = false;
     SEL orig_sel = sel;
+    Method m;
     IMP imp = NULL;
 
 prepare_method:
 
-    if (class_getInstanceMethod(klass, sel) != NULL) {
+    m = class_getInstanceMethod(klass, sel);
+    if (m != NULL) {
 	// The method already exists - we need to JIT it.
 	if (imp == NULL) {
 	    imp = GET_VM()->compile(func);
 	}
-	rb_vm_define_method(klass, sel, imp, node, true);
+	resolve_method(klass, sel, func, node, imp, m);
     }
     else {
 	// Let's keep the method and JIT it later on demand.
@@ -6798,6 +6912,8 @@
 
     int oc_arity = genuine_selector ? arity.real : 0;
     bool redefined = direct;
+    RoxorFunctionIMP *func_imp = GET_VM()->method_func_imp_get(imp);
+    IMP ruby_imp = func_imp == NULL ? imp : func_imp->ruby_imp;
 
 define_method:
     char *types;
@@ -6817,7 +6933,7 @@
 	types[3 + oc_arity] = '\0';
     }
 
-    GET_VM()->add_method(klass, sel, imp, node, types);
+    GET_VM()->add_method(klass, sel, imp, ruby_imp, node, types);
 
     if (!redefined) {
 	if (!genuine_selector && arity.max != arity.min) {
@@ -7047,7 +7163,7 @@
     abort();
 }
 
-static inline Method
+static /*inline*/ Method
 rb_vm_super_lookup(VALUE klass, SEL sel, VALUE *klassp)
 {
     VALUE k, ary;
@@ -7059,13 +7175,29 @@
     void *callstack[128];
     int callstack_n = backtrace(callstack, 128);
 
+    std::vector<void *> callstack_funcs;
+    for (int i = 0; i < callstack_n; i++) {
+	void *start = NULL;
+	if (GET_VM()->symbolize_call_address(callstack[i],
+		    &start, NULL, NULL, 0)) {
+	    struct RoxorFunctionIMP *func_imp =
+		GET_VM()->method_func_imp_get((IMP)start);
+	    if (func_imp != NULL && func_imp->ruby_imp == start) {
+		start = (void *)func_imp->objc_imp;
+	    }
+	    callstack_funcs.push_back(start);
+	}
+    }
+
 #if ROXOR_VM_DEBUG
     printf("locating super method %s of class %s in ancestor chain %s\n", 
 	    sel_getName(sel), rb_class2name(klass),
 	    RSTRING_PTR(rb_inspect(ary)));
-    printf("callstack: ");
-    for (i = callstack_n - 1; i >= 0; i--) {
-	printf("%p ", callstack[i]);
+    printf("callstack functions: ");
+    for (std::vector<void *>::iterator iter = callstack_funcs.begin();
+	 iter != callstack_funcs.end();
+	 ++iter) {
+	printf("%p ", *iter);
     }
     printf("\n");
 #endif
@@ -7092,21 +7224,12 @@
 
 		IMP imp = method_getImplementation(method);
 
-		bool on_stack = false;
-		for (int j = callstack_n - 1; j >= 0; j--) {
-		    void *start = NULL;
-		    if (GET_VM()->symbolize_call_address(callstack[j],
-				&start, NULL, NULL, 0)) {
-			if (start == (void *)imp) {
-			    on_stack = true;
-			    break;
-			}
-		    }
-		}
-
-		if (!on_stack) {
+		if (std::find(callstack_funcs.begin(), callstack_funcs.end(), 
+			    (void *)imp) == callstack_funcs.end()) {
+		    // Method is not on stack.
 #if ROXOR_VM_DEBUG
-		    printf("returning method implementation from class/module %s\n", rb_class2name(k));
+		    printf("returning method implementation %p " \
+		    	   "from class/module %s\n", imp, rb_class2name(k));
 #endif
 		    return method;
 		}
@@ -7359,15 +7482,16 @@
 
 	if (method != NULL) {
 	    IMP imp = method_getImplementation(method);
-	    NODE *node = GET_VM()->method_node_get(imp);
+	    struct RoxorFunctionIMP *func_imp =
+		GET_VM()->method_func_imp_get(imp);
 
-	    if (node != NULL) {
+	    if (func_imp != NULL) {
 		// ruby call
 		cache->flag = MCACHE_RCALL;
 		rcache.klass = klass;
-		rcache.imp = imp;
-		rcache.node = node;
-		rcache.arity = rb_vm_node_arity(node);
+		rcache.imp = func_imp->ruby_imp;
+		rcache.node = func_imp->node;
+		rcache.arity = rb_vm_node_arity(func_imp->node);
 	    }
 	    else {
 		// objc call

Modified: MacRuby/branches/experimental/spec/macruby/fixtures/method.m
===================================================================
--- MacRuby/branches/experimental/spec/macruby/fixtures/method.m	2009-05-02 16:47:43 UTC (rev 1515)
+++ MacRuby/branches/experimental/spec/macruby/fixtures/method.m	2009-05-02 20:07:25 UTC (rev 1516)
@@ -32,26 +32,52 @@
 {
 }
 
++ (BOOL)testMethodReturningVoid:(TestMethod *)o
+{
+    [o methodReturningVoid];
+    return YES;
+}
+
 - (id)methodReturningSelf
 {
     return self;
 }
 
++ (BOOL)testMethodReturningSelf:(TestMethod *)o
+{
+    return [o methodReturningSelf] == o;
+}
+
 - (id)methodReturningNil
 {
     return nil;
 }
 
++ (BOOL)testMethodReturningNil:(TestMethod *)o
+{
+    return [o methodReturningNil] == nil;
+}
+
 - (id)methodReturningCFTrue
 {
     return (id)kCFBooleanTrue;
 }
 
++ (BOOL)testMethodReturningCFTrue:(TestMethod *)o
+{
+    return [o methodReturningCFTrue] == (id)kCFBooleanTrue;
+}
+
 - (id)methodReturningCFFalse
 {
     return (id)kCFBooleanFalse;
 }
 
++ (BOOL)testMethodReturningCFFalse:(TestMethod *)o
+{
+    return [o methodReturningCFFalse] == (id)kCFBooleanFalse;
+}
+
 - (id)methodReturningCFNull
 {
     return (id)kCFNull;
@@ -62,66 +88,131 @@
     return YES;
 }
 
++ (BOOL)testMethodReturningYES:(TestMethod *)o
+{
+    return [o methodReturningYES] == YES;
+}
+
 - (BOOL)methodReturningNO
 {
     return NO;
 }
 
++ (BOOL)testMethodReturningNO:(TestMethod *)o
+{
+    return [o methodReturningNO] == NO;
+}
+
 - (char)methodReturningChar
 {
     return (char)42;
 }
 
++ (BOOL)testMethodReturningChar:(TestMethod *)o
+{
+    return [o methodReturningChar] == (char)42;
+}
+
 - (char)methodReturningChar2
 {
     return (char)-42;
 }
 
++ (BOOL)testMethodReturningChar2:(TestMethod *)o
+{
+    return [o methodReturningChar2] == (char)-42;
+}
+
 - (unsigned char)methodReturningUnsignedChar
 {
     return (unsigned char)42;
 }
 
++ (BOOL)testMethodReturningUnsignedChar:(TestMethod *)o
+{
+    return [o methodReturningUnsignedChar] == (unsigned char)42;
+}
+
 - (short)methodReturningShort
 {
     return (short)42;
 }
 
++ (BOOL)testMethodReturningShort:(TestMethod *)o
+{
+    return [o methodReturningShort] == (short)42;
+}
+
 - (short)methodReturningShort2
 {
     return (short)-42;
 }
 
++ (BOOL)testMethodReturningShort2:(TestMethod *)o
+{
+    return [o methodReturningShort2] == (short)-42;
+}
+
 - (unsigned short)methodReturningUnsignedShort
 {
     return (unsigned short)42;
 }
 
++ (BOOL)testMethodReturningUnsignedShort:(TestMethod *)o
+{
+    return [o methodReturningUnsignedShort] == (unsigned short)42;
+}
+
 - (int)methodReturningInt
 {
     return 42;
 }
 
++ (BOOL)testMethodReturningInt:(TestMethod *)o
+{
+    return [o methodReturningInt] == (int)42;
+}
+
 - (int)methodReturningInt2
 {
     return -42;
 }
 
++ (BOOL)testMethodReturningInt2:(TestMethod *)o
+{
+    return [o methodReturningInt2] == (int)-42;
+}
+
 - (unsigned int)methodReturningUnsignedInt
 {
     return 42;
 }
 
++ (BOOL)testMethodReturningUnsignedInt:(TestMethod *)o
+{
+    return [o methodReturningUnsignedInt] == (int)42;
+}
+
 - (long)methodReturningLong
 {
     return 42;
 }
+
++ (BOOL)testMethodReturningLong:(TestMethod *)o
+{
+    return [o methodReturningLong] == (long)42;
+}
  
 - (long)methodReturningLong2
 {
     return -42;
 }
 
++ (BOOL)testMethodReturningLong2:(TestMethod *)o
+{
+    return [o methodReturningLong2] == (long)-42;
+}
+
 - (long)methodReturningLong3
 {
 #if __LP64__
@@ -145,6 +236,11 @@
     return 42;
 }
 
++ (BOOL)testMethodReturningUnsignedLong:(TestMethod *)o
+{
+    return [o methodReturningUnsignedLong] == (unsigned long)42;
+}
+
 - (unsigned long)methodReturningUnsignedLong2
 {
 #if __LP64__

Added: MacRuby/branches/experimental/spec/macruby/fixtures/method.rb
===================================================================
--- MacRuby/branches/experimental/spec/macruby/fixtures/method.rb	                        (rev 0)
+++ MacRuby/branches/experimental/spec/macruby/fixtures/method.rb	2009-05-02 20:07:25 UTC (rev 1516)
@@ -0,0 +1,21 @@
+class TestMethodOverride < TestMethod
+  def methodReturningVoid; 42; end
+  def methodReturningSelf; self; end
+  def methodReturningNil; nil; end
+  def methodReturningCFTrue; true; end
+  def methodReturningCFFalse; false; end
+  def methodReturningYES; true; end
+  def methodReturningNO; false; end
+  def methodReturningChar; 42; end
+  def methodReturningChar2; -42; end
+  def methodReturningUnsignedChar; 42; end
+  def methodReturningShort; 42; end
+  def methodReturningShort2; -42; end
+  def methodReturningUnsignedShort; 42; end
+  def methodReturningInt; 42; end
+  def methodReturningInt2; -42; end
+  def methodReturningUnsignedInt; 42; end
+  def methodReturningLong; 42; end
+  def methodReturningLong2; -42; end
+  def methodReturningUnsignedLong; 42; end
+end

Modified: MacRuby/branches/experimental/spec/macruby/method_spec.rb
===================================================================
--- MacRuby/branches/experimental/spec/macruby/method_spec.rb	2009-05-02 16:47:43 UTC (rev 1515)
+++ MacRuby/branches/experimental/spec/macruby/method_spec.rb	2009-05-02 20:07:25 UTC (rev 1516)
@@ -55,7 +55,6 @@
     @o.send(:'doSomething:withObject:withObject:', 30, 10, 2).should == 42
   end
 
-=begin # TODO
   it "can be called using -[NSObject performSelector:]" do
     def @o.doSomething; 42; end
     @o.performSelector(:'doSomething').should == 42
@@ -71,7 +70,6 @@
     @o.performSelector(:'doSomething:withObject:',
                        withObject:40, withObject:2).should == 42
   end
-=end
 
   it "cannot be called with #foo=, even if it matches the Objective-C #setFoo pattern" do
     def @o.setFoo(x); end
@@ -82,6 +80,4 @@
     def @o.isFoo; end
     @o.should_not have_method(:'foo?')
   end
-
-  # TODO add overloading specs
 end

Modified: MacRuby/branches/experimental/spec/macruby/objc_method_spec.rb
===================================================================
--- MacRuby/branches/experimental/spec/macruby/objc_method_spec.rb	2009-05-02 16:47:43 UTC (rev 1515)
+++ MacRuby/branches/experimental/spec/macruby/objc_method_spec.rb	2009-05-02 20:07:25 UTC (rev 1516)
@@ -1,6 +1,8 @@
 require File.dirname(__FILE__) + "/spec_helper"
 FixtureCompiler.require! "method"
 
+require File.dirname(__FILE__) + '/fixtures/method'
+
 describe "A pure Objective-C method" do
   before :each do
     @o = TestMethod.new
@@ -397,3 +399,80 @@
     lambda { @o.methodAcceptingNSRectPtr2(Pointer.new(NSPoint.type)) }.should raise_error(TypeError)
   end
 end
+
+describe "A pure MacRuby method" do
+  before :each do
+    @o = TestMethodOverride.new
+  end
+
+  it "can overwrite an Objective-C method returning void" do
+    @o.methodReturningVoid.should == 42
+    TestMethodOverride.testMethodReturningVoid(@o).should == 1
+  end
+
+  it "can overwrite an Objective-C method returning self" do
+    @o.methodReturningSelf.should == @o
+    TestMethodOverride.testMethodReturningSelf(@o).should == 1
+  end
+
+  it "can overwrite an Objective-C method returning nil as 'id'" do
+    @o.methodReturningNil.should == nil
+    TestMethodOverride.testMethodReturningNil(@o).should == 1
+  end
+
+  it "can overwrite an Objective-C method returning kCFBooleanTrue as 'id'" do
+    @o.methodReturningCFTrue.should == true
+    TestMethodOverride.testMethodReturningCFTrue(@o).should == 1
+  end
+
+  it "can overwrite an Objective-C method returning kCFBooleanFalse as 'id'" do
+    @o.methodReturningCFFalse.should == false
+    TestMethodOverride.testMethodReturningCFFalse(@o).should == 1
+  end
+
+  it "can overwrite an Objective-C method returning YES as 'BOOL'" do
+    @o.methodReturningYES.should == true
+    TestMethodOverride.testMethodReturningYES(@o).should == 1
+  end
+
+  it "can overwrite an Objective-C method returning NO as 'BOOL'" do
+    @o.methodReturningNO.should == false
+    TestMethodOverride.testMethodReturningNO(@o).should == 1
+  end
+
+  it "can overwrite an Objective-C method returning 'unsigned char' or 'char'" do
+    @o.methodReturningChar.should == 42
+    @o.methodReturningChar2.should == -42
+    @o.methodReturningUnsignedChar.should == 42
+    TestMethodOverride.testMethodReturningChar(@o).should == 1
+    TestMethodOverride.testMethodReturningChar2(@o).should == 1
+    TestMethodOverride.testMethodReturningUnsignedChar(@o).should == 1
+  end
+
+  it "can overwrite an Objective-C method returning 'unsigned short' or 'short'" do
+    @o.methodReturningShort.should == 42
+    @o.methodReturningShort2.should == -42
+    @o.methodReturningUnsignedShort.should == 42
+    TestMethodOverride.testMethodReturningShort(@o).should == 1
+    TestMethodOverride.testMethodReturningShort2(@o).should == 1
+    TestMethodOverride.testMethodReturningUnsignedShort(@o).should == 1
+  end
+
+  it "can overwrite an Objective-C method returning 'unsigned int' or 'int'" do
+    @o.methodReturningInt.should == 42
+    @o.methodReturningInt2.should == -42
+    @o.methodReturningUnsignedInt.should == 42
+    TestMethodOverride.testMethodReturningInt(@o).should == 1
+    TestMethodOverride.testMethodReturningInt2(@o).should == 1
+    TestMethodOverride.testMethodReturningUnsignedInt(@o).should == 1
+  end
+
+  it "can overwrite an Objective-C method returning 'unsigned long' or 'long'" do
+    @o.methodReturningLong.should == 42
+    @o.methodReturningLong2.should == -42
+    @o.methodReturningUnsignedLong.should == 42
+    TestMethodOverride.testMethodReturningLong(@o).should == 1
+    TestMethodOverride.testMethodReturningLong2(@o).should == 1
+    TestMethodOverride.testMethodReturningUnsignedLong(@o).should == 1
+  end
+end

Modified: MacRuby/branches/experimental/spec/macruby/spec_helper.rb
===================================================================
--- MacRuby/branches/experimental/spec/macruby/spec_helper.rb	2009-05-02 16:47:43 UTC (rev 1515)
+++ MacRuby/branches/experimental/spec/macruby/spec_helper.rb	2009-05-02 20:07:25 UTC (rev 1516)
@@ -32,7 +32,7 @@
   private
   
   def needs_update?
-    !File.exist?(bundle) or File.mtime(fixture) > File.mtime(fixture)
+    !File.exist?(bundle) or File.mtime(fixture) > File.mtime(bundle)
   end
   
   def compile!
@@ -51,4 +51,4 @@
     require bundle[0..-8]
     load_bridge_support_file bridge_support
   end
-end
\ No newline at end of file
+end
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20090502/fb0e9ea4/attachment-0001.html>


More information about the macruby-changes mailing list