From 75d3c5f8fdb49f94dcbe516d11601f4bc7e7cf27 Mon Sep 17 00:00:00 2001
From: Ryo Onodera <ryoqun@gmail.com>
Date: Mon, 9 Jul 2012 07:09:09 +0900
Subject: [PATCH] Define Class#singleton_instance

---
 NEWS                    |    6 ++++++
 class.c                 |   15 +++++++++++++++
 internal.h              |    1 +
 object.c                |   24 ++++++++++++++++++++++++
 test/ruby/test_class.rb |   21 +++++++++++++++++++++
 5 files changed, 67 insertions(+)

diff --git a/NEWS b/NEWS
index d96c5d8..17526a3 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 8d9889e..ba839c2 100644
--- a/class.c
+++ b/class.c
@@ -1294,12 +1294,27 @@ class_instance_method_list(int argc, VALUE *argv, VALUE mod, int obj, int (*func
     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 3a2331a..08f202c 100644
--- a/internal.h
+++ b/internal.h
@@ -62,6 +62,7 @@ struct rb_classext_struct {
 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 8b63340..a25b84b 100644
--- a/object.c
+++ b/object.c
@@ -1744,6 +1744,29 @@ class_or_module_required(VALUE c)
     return super;
 }
 
+/*
+ *  call-seq:
+ *     class.singleton_instance -> an_object
+ *
+ *  Returns the attached singleton instance of <i>class</i>.
+ *
+ *     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 @@ class_or_module_required(VALUE c)
     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 8b2328e..599d30d 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 }
-- 
1.7.10

