Skip to content

Instantly share code, notes, and snippets.

@jballanc
Created November 17, 2009 20:34
Show Gist options
  • Save jballanc/237245 to your computer and use it in GitHub Desktop.
Save jballanc/237245 to your computer and use it in GitHub Desktop.
diff --git a/spec/macruby/core/array_spec.rb b/spec/macruby/core/array_spec.rb
index 9bc6b7c..4dbbf69 100644
--- a/spec/macruby/core/array_spec.rb
+++ b/spec/macruby/core/array_spec.rb
@@ -79,3 +79,49 @@ describe "An NSArray object" do
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
diff --git a/spec/macruby/core/hash_spec.rb b/spec/macruby/core/hash_spec.rb
index 7e5a27b..c6d3c9a 100644
--- a/spec/macruby/core/hash_spec.rb
+++ b/spec/macruby/core/hash_spec.rb
@@ -77,3 +77,49 @@ describe "An NSDictionary object" do
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
diff --git a/spec/macruby/core/string_spec.rb b/spec/macruby/core/string_spec.rb
index 3703b3d..436842a 100644
--- a/spec/macruby/core/string_spec.rb
+++ b/spec/macruby/core/string_spec.rb
@@ -81,3 +81,49 @@ describe "An NSString object" do
#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
diff --git a/spec/macruby/language/objc_method_spec.rb b/spec/macruby/language/objc_method_spec.rb
index b4dc337..e0dd066 100644
--- a/spec/macruby/language/objc_method_spec.rb
+++ b/spec/macruby/language/objc_method_spec.rb
@@ -408,6 +408,49 @@ describe "A pure Objective-C method" do
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
diff --git a/vm.cpp b/vm.cpp
index 7610df8..e5a53dd 100644
--- a/vm.cpp
+++ b/vm.cpp
@@ -1419,11 +1419,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;
@@ -1436,8 +1431,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"
@@ -1458,6 +1472,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] != ':') {
@@ -2402,6 +2425,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)
diff --git a/vm.h b/vm.h
index cb5abd0..614cbd1 100644
--- a/vm.h
+++ b/vm.h
@@ -750,6 +750,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);
> macirb ⚡(alias_fix)
irb(main):001:0> class String
irb(main):002:1> def foo
irb(main):003:2> puts "Hi"
irb(main):004:2> end
irb(main):005:1> def do_alias
irb(main):006:2> alias :old :length
irb(main):007:2> alias :length :foo
irb(main):008:2> end
irb(main):009:1> def undo_alias
irb(main):010:2> alias :length :old
irb(main):011:2> end
irb(main):012:1> end
=> nil
irb(main):013:0> a = "test"
=> "test"
irb(main):014:0> a.foo
Hi
=> nil
irb(main):015:0> a.length
=> 4
irb(main):016:0> a.do_alias
=> nil
irb(main):017:0> a.foo
Hi
=> nil
irb(main):018:0> a.length
Hi
=> nil
irb(main):019:0> a.undo_alias
=> nil
irb(main):020:0> a.foo
Hi
=> nil
irb(main):021:0> a.length
=> 4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment