diff --git a/NEWS b/NEWS index d96c5d88498f8b..17526a30580aeb 100644 --- a/NEWS +++ b/NEWS @@ -62,6 +62,12 @@ with all sufficient information, see the ChangeLog file. * extended method: * Module#define_method accepts a UnboundMethod from a Module. + * Class + * added method: + * added Class#singleton_instance returning the attached instance from a + singleton Class. This is the reversed version of the + Kernel#singleton_class getter method. + * NilClass * added method: * added nil.to_h which returns {} diff --git a/class.c b/class.c index 8d9889ec3be699..ba839c2ac8b8dc 100644 --- a/class.c +++ b/class.c @@ -1294,12 +1294,27 @@ special_singleton_class_of(VALUE obj) return Qnil; } +static inline VALUE +special_singleton_instance_of(VALUE obj) +{ + SPECIAL_SINGLETON(rb_cNilClass, Qnil); + SPECIAL_SINGLETON(rb_cFalseClass, Qfalse); + SPECIAL_SINGLETON(rb_cTrueClass, Qtrue); + rb_raise(rb_eTypeError, "not singleton class"); +} + VALUE rb_special_singleton_class(VALUE obj) { return special_singleton_class_of(obj); } +VALUE +rb_special_singleton_instance(VALUE obj) +{ + return special_singleton_instance_of(obj); +} + /*! * \internal * Returns the singleton class of \a obj. Creates it if necessary. diff --git a/internal.h b/internal.h index 3a2331a867ae10..08f202cc72b7c9 100644 --- a/internal.h +++ b/internal.h @@ -62,6 +62,7 @@ VALUE rb_obj_private_methods(int argc, VALUE *argv, VALUE obj); VALUE rb_obj_public_methods(int argc, VALUE *argv, VALUE obj); int rb_obj_basic_to_s_p(VALUE); VALUE rb_special_singleton_class(VALUE); +VALUE rb_special_singleton_instance(VALUE); void Init_class_hierarchy(void); /* compile.c */ diff --git a/object.c b/object.c index 8b6334020f3608..a25b84bcd47b4e 100644 --- a/object.c +++ b/object.c @@ -1744,6 +1744,29 @@ rb_class_superclass(VALUE klass) return super; } +/* + * call-seq: + * class.singleton_instance -> an_object + * + * Returns the attached singleton instance of class. + * + * klass = "Hello".singleton_class + * klass.singleton_instance => "Hello" + * + * If the given class isn't a singleton class, it raises a TypeError. + * + */ + +VALUE +rb_class_singleton_instance(VALUE klass) +{ + if(FL_TEST(klass, FL_SINGLETON)) { + return rb_iv_get(klass, "__attached__"); + } else { + return rb_special_singleton_instance(klass); + } +} + VALUE rb_class_get_superclass(VALUE klass) { @@ -2989,6 +3012,7 @@ Init_Object(void) rb_define_method(rb_cClass, "initialize", rb_class_initialize, -1); rb_define_method(rb_cClass, "initialize_copy", rb_class_init_copy, 1); /* in class.c */ rb_define_method(rb_cClass, "superclass", rb_class_superclass, 0); + rb_define_method(rb_cClass, "singleton_instance", rb_class_singleton_instance, 0); rb_define_alloc_func(rb_cClass, rb_class_s_alloc); rb_undef_method(rb_cClass, "extend_object"); rb_undef_method(rb_cClass, "append_features"); diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index 8b2328e289518d..599d30d18e14b3 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -206,6 +206,27 @@ module Foo; def foo; :foo; end; end INPUT end + def test_singleton_instance + o = Object.new + c = o.singleton_class + assert_equal(o, c.singleton_instance) + end + + def test_no_singleton_instance + assert_raise(TypeError) { String.singleton_instance } + end + + def test_class_singleton_instance + c = Object.singleton_class + assert_equal(Object, c.singleton_instance) + end + + def test_immediate_singleton_instance + assert_equal(true, TrueClass.singleton_instance) + assert_equal(false, FalseClass.singleton_instance) + assert_equal(nil, NilClass.singleton_instance) + end + def test_uninitialized assert_raise(TypeError) { Class.allocate.new } assert_raise(TypeError) { Class.allocate.superclass }