Revision: 2941 http://trac.macosforge.org/projects/ruby/changeset/2941 Author: lsansonetti@apple.com Date: 2009-11-02 17:22:54 -0800 (Mon, 02 Nov 2009) Log Message: ----------- when registering a new method, if the given class also responds to the arity-1+1 selector let's make it unavailable, to fully conform to the way Ruby behaves Modified Paths: -------------- MacRuby/trunk/dispatcher.cpp MacRuby/trunk/vm.cpp MacRuby/trunk/vm.h Modified: MacRuby/trunk/dispatcher.cpp =================================================================== --- MacRuby/trunk/dispatcher.cpp 2009-11-02 23:00:03 UTC (rev 2940) +++ MacRuby/trunk/dispatcher.cpp 2009-11-03 01:22:54 UTC (rev 2941) @@ -221,9 +221,20 @@ rb_vm_super_lookup(VALUE klass, SEL sel) { // Locate the current method implementation. - Method m = class_getInstanceMethod((Class)klass, sel); - assert(m != NULL); - IMP self = objc_imp(method_getImplementation(m)); + Class k = (Class)klass; + IMP self; + while (true) { + Method m = class_getInstanceMethod(k, sel); + assert(m != NULL); + self = method_getImplementation(m); + if (UNAVAILABLE_IMP(self)) { + k = class_getSuperclass(k); + assert(k != NULL); + } + else { + break; + } + } // Compute the stack call implementations right after our current method. void *callstack[128]; @@ -248,7 +259,6 @@ // the stack. VALUE ary = rb_mod_ancestors_nocopy(klass); const int count = RARRAY_LEN(ary); - VALUE k = klass; bool klass_located = false; #if ROXOR_VM_DEBUG @@ -272,18 +282,21 @@ } if (klass_located) { if (i < count - 1) { - k = RARRAY_AT(ary, i + 1); + VALUE k = RARRAY_AT(ary, i + 1); Method method = class_getInstanceMethod((Class)k, sel); VALUE super = RCLASS_SUPER(k); - if (method == NULL || REMOVED_IMP(method_getImplementation(method)) - || (super != 0 - && class_getInstanceMethod((Class)super, sel) == method)) { + if (method == NULL) { continue; } IMP imp = method_getImplementation(method); + if (UNAVAILABLE_IMP(imp) + || (super != 0 && class_getInstanceMethod((Class)super, + sel) == method)) { + continue; + } if (std::find(callstack_funcs.begin(), callstack_funcs.end(), (void *)imp) == callstack_funcs.end()) { @@ -556,9 +569,7 @@ recache2: IMP imp = method_getImplementation(method); - if (UNDEFINED_IMP(imp) - || (REMOVED_IMP(imp) - && rb_vm_super_lookup((VALUE)klass, sel) == NULL)) { + if (UNAVAILABLE_IMP(imp)) { // Method was undefined. goto call_method_missing; } @@ -1769,9 +1780,7 @@ } IMP imp = method_getImplementation(m); - if (UNDEFINED_IMP(imp) - || (REMOVED_IMP(imp) - && rb_vm_super_lookup((VALUE)klass, sel) == NULL)) { + if (UNAVAILABLE_IMP(imp)) { return false; } Modified: MacRuby/trunk/vm.cpp =================================================================== --- MacRuby/trunk/vm.cpp 2009-11-02 23:00:03 UTC (rev 2940) +++ MacRuby/trunk/vm.cpp 2009-11-03 01:22:54 UTC (rev 2941) @@ -1379,6 +1379,9 @@ rb_vm_alias_method(Class klass, Method method, ID name, bool noargs) { IMP imp = method_getImplementation(method); + if (UNAVAILABLE_IMP(imp)) { + return; + } const char *types = method_getTypeEncoding(method); rb_vm_method_node_t *node = GET_CORE()->method_node_get(method); @@ -1806,7 +1809,9 @@ m = class_getInstanceMethod(klass, sel); if (precompiled) { - imp = (IMP)data; + if (imp == NULL) { + imp = (IMP)data; + } GET_CORE()->resolve_method(klass, sel, NULL, arity, flags, imp, m); } else { @@ -1825,23 +1830,34 @@ } if (!redefined) { - if (!genuine_selector && arity.max != arity.min) { - char buf[100]; + char buf[100]; + SEL new_sel = 0; + if (!genuine_selector) { snprintf(buf, sizeof buf, "%s:", sel_name); - sel = sel_registerName(buf); - redefined = true; - - goto prepare_method; + new_sel = sel_registerName(buf); + if (arity.max != arity.min) { + sel = new_sel; + redefined = true; + goto prepare_method; + } } - else if (genuine_selector && arity.min == 0) { - char buf[100]; + else { strlcpy(buf, sel_name, sizeof buf); buf[strlen(buf) - 1] = 0; // remove the ending ':' - sel = sel_registerName(buf); - redefined = true; - - goto prepare_method; + new_sel = sel_registerName(buf); + if (arity.min == 0) { + sel = new_sel; + redefined = true; + goto prepare_method; + } } + Method tmp_m = class_getInstanceMethod(klass, new_sel); + if (tmp_m != NULL) { + // If we add -[foo:] and the class responds to -[foo], we need + // to disable it (and vice-versa). + class_replaceMethod(klass, new_sel, + (IMP)rb_vm_undefined_imp, method_getTypeEncoding(tmp_m)); + } } if (RCLASS_VERSION(klass) & RCLASS_IS_INCLUDED) { @@ -2131,6 +2147,9 @@ return false; } IMP imp = method_getImplementation(m); + if (UNAVAILABLE_IMP(imp)) { + return false; + } if (pimp != NULL) { *pimp = imp; } @@ -2259,8 +2278,6 @@ rb_vm_define_method(klass, sel, imp, body, false); } -#define UNDEFINED_IMP(imp) (imp == NULL || imp == (IMP)rb_vm_undefined_imp) - void RoxorCore::undef_method(Class klass, SEL sel) { Modified: MacRuby/trunk/vm.h =================================================================== --- MacRuby/trunk/vm.h 2009-11-02 23:00:03 UTC (rev 2940) +++ MacRuby/trunk/vm.h 2009-11-03 01:22:54 UTC (rev 2941) @@ -283,9 +283,10 @@ void rb_vm_define_method3(Class klass, SEL sel, rb_vm_block_t *node); bool rb_vm_resolve_method(Class klass, SEL sel); void *rb_vm_undefined_imp(void *rcv, SEL sel); -#define UNDEFINED_IMP(imp) (imp == NULL || imp == (IMP)rb_vm_undefined_imp) void *rb_vm_removed_imp(void *rcv, SEL sel); -#define REMOVED_IMP(imp) (imp == (IMP)rb_vm_removed_imp) +#define UNAVAILABLE_IMP(imp) \ + (imp == NULL || imp == (IMP)rb_vm_undefined_imp \ + || imp == (IMP)rb_vm_removed_imp) void rb_vm_define_attr(Class klass, const char *name, bool read, bool write); void rb_vm_undef_method(Class klass, ID name, bool must_exist); void rb_vm_alias(VALUE klass, ID name, ID def);