Revision: 3140 http://trac.macosforge.org/projects/ruby/changeset/3140 Author: joshua.ballanco@apple.com Date: 2009-12-21 04:34:46 -0800 (Mon, 21 Dec 2009) Log Message: ----------- Enable aliasing of Objective-C methods Modified Paths: -------------- MacRuby/trunk/spec/macruby/core/array_spec.rb MacRuby/trunk/spec/macruby/core/hash_spec.rb MacRuby/trunk/spec/macruby/core/string_spec.rb MacRuby/trunk/spec/macruby/language/objc_method_spec.rb MacRuby/trunk/vm.cpp MacRuby/trunk/vm.h Modified: MacRuby/trunk/spec/macruby/core/array_spec.rb =================================================================== --- MacRuby/trunk/spec/macruby/core/array_spec.rb 2009-12-19 01:50:51 UTC (rev 3139) +++ MacRuby/trunk/spec/macruby/core/array_spec.rb 2009-12-21 12:34:46 UTC (rev 3140) @@ -79,3 +79,49 @@ end =end end + +describe "Objective-C Array methods" do + before :each do + @a = [1,2,3,4] + end + + it "should be able to be aliased to other selectors" do + class << @a + alias :foo :count + end + + @a.foo.should == @a.count + end + + it "should be able to be aliased by pure Ruby methods" do + class << @a + def foo + return 42 + end + alias :count :foo + end + + @a.count.should == 42 + end + + it "should be commutative when aliased" do + class << @a + def foo + return 42 + end + def do_alias + alias :old_count :count + alias :count :foo + end + def undo_alias + alias :count :old_count + end + end + + @a.count.should == 4 + @a.do_alias + @a.count.should == 42 + @a.undo_alias + @a.count.should == 4 + end +end Modified: MacRuby/trunk/spec/macruby/core/hash_spec.rb =================================================================== --- MacRuby/trunk/spec/macruby/core/hash_spec.rb 2009-12-19 01:50:51 UTC (rev 3139) +++ MacRuby/trunk/spec/macruby/core/hash_spec.rb 2009-12-21 12:34:46 UTC (rev 3140) @@ -77,3 +77,49 @@ lambda { a[42] = 123 }.should raise_error(RuntimeError) end end + +describe "Objective-C Hash methods" do + before :each do + @a = {:a => 1, :b => 2, :c => 3} + end + + it "should be able to be aliased to other selectors" do + class << @a + alias :foo :count + end + + @a.foo.should == @a.count + end + + it "should be able to be aliased by pure Ruby methods" do + class << @a + def foo + return 42 + end + alias :count :foo + end + + @a.count.should == 42 + end + + it "should be commutative when aliased" do + class << @a + def foo + return 42 + end + def do_alias + alias :old_count :count + alias :count :foo + end + def undo_alias + alias :count :old_count + end + end + + @a.count.should == 4 + @a.do_alias + @a.count.should == 42 + @a.undo_alias + @a.count.should == 4 + end +end Modified: MacRuby/trunk/spec/macruby/core/string_spec.rb =================================================================== --- MacRuby/trunk/spec/macruby/core/string_spec.rb 2009-12-19 01:50:51 UTC (rev 3139) +++ MacRuby/trunk/spec/macruby/core/string_spec.rb 2009-12-21 12:34:46 UTC (rev 3140) @@ -81,3 +81,49 @@ #lambda { a << 'foo' }.should raise_error(RuntimeError) end end + +describe "Objective-C String methods" do + before :each do + @a = "test" + end + + it "should be able to be aliased to other selectors" do + class << @a + alias :foo :length + end + + @a.foo.should == @a.length + end + + it "should be able to be aliased by pure Ruby methods" do + class << @a + def foo + return 42 + end + alias :length :foo + end + + @a.length.should == 42 + end + + it "should be commutative when aliased" do + class << @a + def foo + return 42 + end + def do_alias + alias :old_length :length + alias :length :foo + end + def undo_alias + alias :length :old_length + end + end + + @a.length.should == 4 + @a.do_alias + @a.length.should == 42 + @a.undo_alias + @a.length.should == 4 + end +end Modified: MacRuby/trunk/spec/macruby/language/objc_method_spec.rb =================================================================== --- MacRuby/trunk/spec/macruby/language/objc_method_spec.rb 2009-12-19 01:50:51 UTC (rev 3139) +++ MacRuby/trunk/spec/macruby/language/objc_method_spec.rb 2009-12-21 12:34:46 UTC (rev 3140) @@ -408,6 +408,49 @@ end @o.methodAcceptingSEL('foo:arg1:arg2:', target:o) end + + it "can have another method aliased to it" do + class << @o + alias :test_method :methodReturningSelf + end + + @o.should.respond_to(:test_method) + @o.test_method.should == @o + @o.test_method.object_id.should == @o.object_id + end + + it "can be aliased to a pure Ruby method" do + class << @o + def foo + return 42 + end + alias :methodReturningSelf :foo + end + + @o.should.respond_to(:methodReturningSelf) + @o.methodReturningSelf.should == 42 + end + + it "should be commutative when aliased" do + class << @o + def foo + return 42 + end + def do_alias + alias :old_methodReturningSelf :methodReturningSelf + alias :methodReturningSelf :foo + end + def undo_alias + alias :methodReturningSelf :old_methodReturningSelf + end + end + + @o.methodReturningSelf.should == @o + @o.do_alias + @o.methodReturningSelf.should == 42 + @o.undo_alias + @o.methodReturningSelf.should == @o + end end describe "A pure MacRuby method" do Modified: MacRuby/trunk/vm.cpp =================================================================== --- MacRuby/trunk/vm.cpp 2009-12-19 01:50:51 UTC (rev 3139) +++ MacRuby/trunk/vm.cpp 2009-12-21 12:34:46 UTC (rev 3140) @@ -1464,11 +1464,6 @@ const char *types = method_getTypeEncoding(method); rb_vm_method_node_t *node = GET_CORE()->method_node_get(method); - if (node == NULL) { - rb_raise(rb_eArgError, - "only pure Ruby methods can be aliased (`%s' is not)", - sel_getName(method_getName(method))); - } const char *name_str = rb_id2name(name); SEL sel; @@ -1481,8 +1476,27 @@ sel = sel_registerName(tmp); } - GET_CORE()->add_method(klass, sel, imp, node->ruby_imp, - node->arity, node->flags, types); + if (node == NULL) { + Method method_to_replace = class_getInstanceMethod(klass, sel); + rb_vm_method_node_t *node_to_replace = GET_CORE()->method_node_get(method_to_replace); + if ((klass == (Class)rb_cCFString || klass == (Class)rb_cCFHash) + && node_to_replace != NULL) { + //Doesn't work: + //GET_CORE()->remove_method(klass, sel); + // + //This, apparently, doesn't work either... + //GET_CORE()->add_method(klass, sel, imp, imp, + // arity, 0, types); + // + //This...sorta...works... + GET_CORE()->nuke_method(method_to_replace); + } + class_replaceMethod(klass, sel, imp, types); + } + else { + GET_CORE()->add_method(klass, sel, imp, node->ruby_imp, + node->arity, node->flags, types); + } } extern "C" @@ -1503,6 +1517,15 @@ const char *def_str = rb_id2name(def); SEL sel = sel_registerName(def_str); + + // String and Hash aliases should be defined on the Core Foundation classes + if (klass == (Class)rb_cNSMutableString) { + klass = (Class)rb_cCFString; + } + if (klass == (Class)rb_cNSMutableHash) { + klass = (Class)rb_cCFHash; + } + Method def_method1 = class_getInstanceMethod(klass, sel); Method def_method2 = NULL; if (def_str[strlen(def_str) - 1] != ':') { @@ -2535,6 +2558,16 @@ } } +void +RoxorCore::nuke_method(Method m) +{ + invalidate_method_cache(method_getName(m)); + std::map<Method, rb_vm_method_node_t *>::iterator iter = ruby_methods.find(m); + assert(iter != ruby_methods.end()); + free(iter->second); + ruby_methods.erase(iter); +} + extern "C" void rb_vm_undef_method(Class klass, ID name, bool must_exist) Modified: MacRuby/trunk/vm.h =================================================================== --- MacRuby/trunk/vm.h 2009-12-19 01:50:51 UTC (rev 3139) +++ MacRuby/trunk/vm.h 2009-12-21 12:34:46 UTC (rev 3140) @@ -757,6 +757,7 @@ rb_vm_method_node_t *node, const char *types); void undef_method(Class klass, SEL sel); void remove_method(Class klass, SEL sel); + void nuke_method(Method m); bool resolve_methods(std::map<Class, rb_vm_method_source_t *> *map, Class klass, SEL sel); bool copy_method(Class klass, Method m);