From 34023d9de05b4538bcd083da9af36ac75847744c Mon Sep 17 00:00:00 2001
From: Shugo Maeda <shugo@ruby-lang.org>
Date: Wed, 28 Jul 2010 18:53:07 +0900
Subject: [PATCH 10/51] supported nested methods.

---
 compile.c           |    4 ++-
 eval.c              |   28 +++++++++++++++----------
 include/ruby/ruby.h |    1 +
 node.h              |    3 +-
 parse.y             |    2 +
 vm.c                |   55 ++++++++++++++++++++++++++++++++++++++++++++------
 vm_core.h           |    2 +
 7 files changed, 75 insertions(+), 20 deletions(-)

diff --git a/compile.c b/compile.c
index b9749f4..8875e57 100644
--- a/compile.c
+++ b/compile.c
@@ -4575,7 +4575,9 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
 	ADD_INSN1(ret, nd_line(node), putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
 	ADD_INSN1(ret, nd_line(node), putobject, ID2SYM(node->nd_mid));
 	ADD_INSN1(ret, nd_line(node), putiseq, iseqval);
-	ADD_SEND (ret, nd_line(node), ID2SYM(id_core_define_method), INT2FIX(3));
+	ADD_INSN1(ret, nd_line(node), putobject,
+		  node->flags & NODE_FL_NESTED_DEF ? Qtrue : Qfalse);
+	ADD_SEND (ret, nd_line(node), ID2SYM(id_core_define_method), INT2FIX(4));
 
 	if (poped) {
 	    ADD_INSN(ret, nd_line(node), pop);
diff --git a/eval.c b/eval.c
index bee14cc..33dc10a 100644
--- a/eval.c
+++ b/eval.c
@@ -969,18 +969,9 @@ top_include(int argc, VALUE *argv, VALUE self)
     return rb_mod_include(argc, argv, rb_cObject);
 }
 
-/*
- *  call-seq:
- *     overlay_module(klass, mod)
- *
- *  Append features of <i>mod</i> to <i>klass</i> only in the scope where
- *  <code>overlay_module</code> is called.
- */
-
-static VALUE
-f_overlay_module(VALUE self, VALUE klass, VALUE module)
+void
+rb_overlay_module(NODE *cref, VALUE klass, VALUE module)
 {
-    NODE *cref = rb_vm_cref();
     VALUE iclass, c, superclass = klass;
 
     Check_Type(module, T_MODULE);
@@ -999,6 +990,21 @@ f_overlay_module(VALUE self, VALUE klass, VALUE module)
     }
     rb_hash_aset(cref->nd_omod, klass, iclass);
     rb_clear_cache_by_class(klass);
+}
+
+/*
+ *  call-seq:
+ *     overlay_module(klass, mod)
+ *
+ *  Append features of <i>mod</i> to <i>klass</i> only in the scope where
+ *  <code>overlay_module</code> is called.
+ */
+
+static VALUE
+f_overlay_module(VALUE self, VALUE klass, VALUE module)
+{
+    NODE *cref = rb_vm_cref();
+    rb_overlay_module(cref, klass, module);
     return Qnil;
 }
 
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
index 4e15517..def9d3c 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -629,6 +629,7 @@ struct RClass {
 #define RMODULE_CONST_TBL(m) RCLASS_CONST_TBL(m)
 #define RMODULE_M_TBL(m) RCLASS_M_TBL(m)
 #define RMODULE_SUPER(m) RCLASS_SUPER(m)
+#define RMODULE_HAS_NESTED_METHODS FL_USER2
 
 struct RFloat {
     struct RBasic basic;
diff --git a/node.h b/node.h
index f663bdc..c798d96 100644
--- a/node.h
+++ b/node.h
@@ -265,9 +265,10 @@ typedef struct RNode {
 
 #define RNODE(obj)  (R_CAST(RNode)(obj))
 
-/* 0..4:T_TYPES, 5:FL_MARK, 6:reserved, 7:NODE_FL_NEWLINE */
+/* 0..4:T_TYPES, 5:FL_MARK, 6:NODE_FL_NESTED_DEF, 7:NODE_FL_NEWLINE */
 #define NODE_FL_NEWLINE (((VALUE)1)<<7)
 #define NODE_FL_CREF_PUSHED_BY_EVAL NODE_FL_NEWLINE
+#define NODE_FL_NESTED_DEF (((VALUE)1)<<6)
 
 #define NODE_TYPESHIFT 8
 #define NODE_TYPEMASK  (((VALUE)0x7f)<<NODE_TYPESHIFT)
diff --git a/parse.y b/parse.y
index 13e18f2..67ca1f1 100644
--- a/parse.y
+++ b/parse.y
@@ -2967,6 +2967,8 @@ primary		: literal
 			reduce_nodes(&body);
 			$$ = NEW_DEFN($2, $4, body, NOEX_PRIVATE);
 			nd_set_line($$, $<num>1);
+			if (in_def > 1 || in_single > 0)
+			    $$->flags |= NODE_FL_NESTED_DEF;
 		    /*%
 			$$ = dispatch3(def, $2, $4, $5);
 		    %*/
diff --git a/vm.c b/vm.c
index 0968dae..8d19d4c 100644
--- a/vm.c
+++ b/vm.c
@@ -1821,14 +1821,37 @@ rb_thread_alloc(VALUE klass)
     return self;
 }
 
+static VALUE
+find_module_for_nested_methods(NODE *cref, VALUE klass)
+{
+    VALUE iclass;
+
+    if (NIL_P(cref->nd_omod))
+	return Qnil;
+    iclass = rb_hash_lookup(cref->nd_omod, klass);
+    if (NIL_P(iclass))
+	return Qnil;
+    while (iclass) {
+	VALUE module = RBASIC(iclass)->klass;
+	if (FL_TEST(module, RMODULE_HAS_NESTED_METHODS)) {
+	    return module;
+	}
+	iclass = RCLASS_SUPER(iclass);
+    }
+    return Qnil;
+}
+
 VALUE rb_iseq_clone(VALUE iseqval, VALUE newcbase);
 
 static void
-vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval,
+vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval, VALUE nested,
 		 rb_num_t is_singleton, NODE *cref)
 {
     VALUE klass = cref->nd_clss;
+    VALUE target, defined_class;
+    rb_method_entry_t *me;
     int noex = (int)cref->nd_visi;
+    int is_nested = RTEST(nested);
     rb_iseq_t *miseq;
     GetISeqPtr(iseqval, miseq);
 
@@ -1853,12 +1876,30 @@ vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval,
 	noex = NOEX_PUBLIC;
     }
 
+    if (is_nested) {
+	target = find_module_for_nested_methods(cref, klass);
+	if (NIL_P(target)) {
+	    target = rb_module_new();
+	    FL_SET(target, RMODULE_HAS_NESTED_METHODS);
+	    rb_overlay_module(cref, klass, target);
+	}
+	else {
+	    me = search_method(target, id, &defined_class);
+	    if (me && me->def->type == VM_METHOD_TYPE_ISEQ &&
+		me->def->body.iseq == miseq) {
+		return;
+	    }
+	}
+    }
+    else {
+	target = klass;
+    }
     /* dup */
     COPY_CREF(miseq->cref_stack, cref);
     miseq->cref_stack->nd_visi = NOEX_PUBLIC;
-    miseq->klass = klass;
+    miseq->klass = target;
     miseq->defined_method_id = id;
-    rb_add_method(klass, id, VM_METHOD_TYPE_ISEQ, miseq, noex);
+    rb_add_method(target, id, VM_METHOD_TYPE_ISEQ, miseq, noex);
 
     if (!is_singleton && noex == NOEX_MODFUNC) {
 	rb_add_method(rb_singleton_class(klass), id, VM_METHOD_TYPE_ISEQ, miseq, NOEX_PUBLIC);
@@ -1872,10 +1913,10 @@ vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval,
 } while (0)
 
 static VALUE
-m_core_define_method(VALUE self, VALUE cbase, VALUE sym, VALUE iseqval)
+m_core_define_method(VALUE self, VALUE cbase, VALUE sym, VALUE iseqval, VALUE nested)
 {
     REWIND_CFP({
-	vm_define_method(GET_THREAD(), cbase, SYM2ID(sym), iseqval, 0, rb_vm_cref());
+	vm_define_method(GET_THREAD(), cbase, SYM2ID(sym), iseqval, nested, 0, rb_vm_cref());
     });
     return Qnil;
 }
@@ -1884,7 +1925,7 @@ static VALUE
 m_core_define_singleton_method(VALUE self, VALUE cbase, VALUE sym, VALUE iseqval)
 {
     REWIND_CFP({
-	vm_define_method(GET_THREAD(), cbase, SYM2ID(sym), iseqval, 1, rb_vm_cref());
+	vm_define_method(GET_THREAD(), cbase, SYM2ID(sym), iseqval, Qfalse, 1, rb_vm_cref());
     });
     return Qnil;
 }
@@ -2000,7 +2041,7 @@ Init_VM(void)
     rb_define_method_id(klass, id_core_set_method_alias, m_core_set_method_alias, 3);
     rb_define_method_id(klass, id_core_set_variable_alias, m_core_set_variable_alias, 2);
     rb_define_method_id(klass, id_core_undef_method, m_core_undef_method, 2);
-    rb_define_method_id(klass, id_core_define_method, m_core_define_method, 3);
+    rb_define_method_id(klass, id_core_define_method, m_core_define_method, 4);
     rb_define_method_id(klass, id_core_define_singleton_method, m_core_define_singleton_method, 3);
     rb_define_method_id(klass, id_core_set_postexe, m_core_set_postexe, 1);
     rb_obj_freeze(fcore);
diff --git a/vm_core.h b/vm_core.h
index 811e7ff..5a5d08e 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -717,6 +717,8 @@ rb_threadptr_exec_event_hooks(rb_thread_t *th, rb_event_flag_t flag, VALUE self,
     } \
 } while (0)
 
+void rb_overlay_module(NODE*, VALUE, VALUE);
+
 #if defined __GNUC__ && __GNUC__ >= 4
 #pragma GCC visibility push(default)
 #endif
-- 
1.7.0.4

