Permalink
Browse files

Enable aliasing of Objective-C methods

git-svn-id: http://svn.macosforge.org/repository/ruby/MacRuby/trunk@3140 23306eb0-4c56-4727-a40e-e92c0eb68959
  • Loading branch information...
1 parent 08d6819 commit 2533ca1f04c22b26b8ec3d59dcc86e5649946304 @jballanc jballanc committed Dec 21, 2009
Showing with 222 additions and 7 deletions.
  1. +46 −0 spec/macruby/core/array_spec.rb
  2. +46 −0 spec/macruby/core/hash_spec.rb
  3. +46 −0 spec/macruby/core/string_spec.rb
  4. +43 −0 spec/macruby/language/objc_method_spec.rb
  5. +40 −7 vm.cpp
  6. +1 −0 vm.h
@@ -79,3 +79,49 @@ def a.foo; 42; end
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
@@ -77,3 +77,49 @@ def a.foo; 42; end
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
@@ -81,3 +81,49 @@ def a.foo; 42; end
#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
@@ -408,6 +408,49 @@ def o.foo(i, arg1: b, arg2: f)
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
View
47 vm.cpp
@@ -1464,11 +1464,6 @@ rb_vm_alias_method(Class klass, Method method, ID name, bool noargs)
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 @@ rb_vm_alias_method(Class klass, Method method, ID name, bool noargs)
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 @@ rb_vm_alias2(VALUE outer, ID name, ID def, unsigned char dynamic_class)
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 @@ RoxorCore::undef_method(Class klass, SEL sel)
}
}
+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)
View
1 vm.h
@@ -757,6 +757,7 @@ class RoxorCore {
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);

0 comments on commit 2533ca1

Please sign in to comment.