From 6008edadf1f56e01e1f83b94694031f3b0d1f397 Mon Sep 17 00:00:00 2001
From: Shugo Maeda <shugo@ruby-lang.org>
Date: Thu, 27 May 2010 15:44:27 +0900
Subject: [PATCH 01/51] added Kernel#overlay_module.

---
 class.c               |    6 +++---
 eval.c                |   27 +++++++++++++++++++++++++++
 gc.c                  |    6 ++++++
 include/ruby/intern.h |    1 +
 iseq.c                |   16 +++++++++-------
 method.h              |    2 +-
 node.h                |   11 ++++++++++-
 vm.c                  |    1 +
 vm_insnhelper.c       |    9 +++++++--
 vm_insnhelper.h       |    1 +
 vm_method.c           |   29 ++++++++++++++++++++++-------
 11 files changed, 88 insertions(+), 21 deletions(-)

diff --git a/class.c b/class.c
index 2241792..4a9471d 100644
--- a/class.c
+++ b/class.c
@@ -617,8 +617,8 @@ rb_define_module_id_under(VALUE outer, ID id)
     return module;
 }
 
-static VALUE
-include_class_new(VALUE module, VALUE super)
+VALUE
+rb_include_class_new(VALUE module, VALUE super)
 {
     VALUE klass = class_alloc(T_ICLASS, rb_cClass);
 
@@ -685,7 +685,7 @@ rb_include_module(VALUE klass, VALUE module)
 		break;
 	    }
 	}
-	c = RCLASS_SUPER(c) = include_class_new(module, RCLASS_SUPER(c));
+	c = RCLASS_SUPER(c) = rb_include_class_new(module, RCLASS_SUPER(c));
 	if (RMODULE_M_TBL(module) && RMODULE_M_TBL(module)->num_entries)
 	    changed = 1;
       skip:
diff --git a/eval.c b/eval.c
index bbf66b3..b142177 100644
--- a/eval.c
+++ b/eval.c
@@ -969,6 +969,31 @@ 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)
+{
+    NODE *cref = rb_vm_cref();
+    VALUE c;
+
+    Check_Type(module, T_MODULE);
+    if (NIL_P(cref->nd_omod)) {
+	cref->nd_omod = rb_hash_new();
+	rb_funcall(cref->nd_omod, rb_intern("compare_by_identity"), 0);
+    }
+    c = rb_include_class_new(module, klass);
+    rb_hash_aset(cref->nd_omod, klass, c);
+    rb_clear_cache_by_class(klass);
+    return Qnil;
+}
+
 VALUE rb_f_trace_var();
 VALUE rb_f_untrace_var();
 
@@ -1136,6 +1161,8 @@ Init_eval(void)
 
     rb_define_singleton_method(rb_vm_top_self(), "include", top_include, -1);
 
+    rb_define_global_function("overlay_module", f_overlay_module, 2);
+
     rb_define_method(rb_mKernel, "extend", rb_obj_extend, -1);
 
     rb_define_global_function("trace_var", rb_f_trace_var, -1);	/* in variable.c */
diff --git a/gc.c b/gc.c
index cf9107a..c3309ee 100644
--- a/gc.c
+++ b/gc.c
@@ -1684,6 +1684,12 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev)
 	    ptr = (VALUE)obj->as.node.u2.node;
 	    goto again;
 
+	  case NODE_CREF:
+	    gc_mark(objspace, obj->as.node.u0.value, lev);
+	    gc_mark(objspace, (VALUE)obj->as.node.u1.node, lev);
+	    ptr = (VALUE)obj->as.node.u3.node;
+	    goto again;
+
 	  default:		/* unlisted NODE */
 	    if (is_pointer_to_heap(objspace, obj->as.node.u1.node)) {
 		gc_mark(objspace, (VALUE)obj->as.node.u1.node, lev);
diff --git a/include/ruby/intern.h b/include/ruby/intern.h
index b18a1f5..7cc75b2 100644
--- a/include/ruby/intern.h
+++ b/include/ruby/intern.h
@@ -169,6 +169,7 @@ VALUE rb_define_class_id_under(VALUE, ID, VALUE);
 VALUE rb_module_new(void);
 VALUE rb_define_module_id(ID);
 VALUE rb_define_module_id_under(VALUE, ID);
+VALUE rb_include_class_new(VALUE, VALUE);
 VALUE rb_mod_included_modules(VALUE);
 VALUE rb_mod_include_p(VALUE, VALUE);
 VALUE rb_mod_ancestors(VALUE);
diff --git a/iseq.c b/iseq.c
index 2b82aa8..7542969 100644
--- a/iseq.c
+++ b/iseq.c
@@ -182,20 +182,20 @@ set_relation(rb_iseq_t *iseq, const VALUE parent)
     /* set class nest stack */
     if (type == ISEQ_TYPE_TOP) {
 	/* toplevel is private */
-	iseq->cref_stack = NEW_BLOCK(rb_cObject);
-	iseq->cref_stack->nd_file = 0;
+	iseq->cref_stack = NEW_CREF(rb_cObject);
+	iseq->cref_stack->nd_omod = Qnil;
 	iseq->cref_stack->nd_visi = NOEX_PRIVATE;
 	if (th->top_wrapper) {
-	    NODE *cref = NEW_BLOCK(th->top_wrapper);
-	    cref->nd_file = 0;
+	    NODE *cref = NEW_CREF(th->top_wrapper);
+	    cref->nd_omod = Qnil;
 	    cref->nd_visi = NOEX_PRIVATE;
 	    cref->nd_next = iseq->cref_stack;
 	    iseq->cref_stack = cref;
 	}
     }
     else if (type == ISEQ_TYPE_METHOD || type == ISEQ_TYPE_CLASS) {
-	iseq->cref_stack = NEW_BLOCK(0); /* place holder */
-	iseq->cref_stack->nd_file = 0;
+	iseq->cref_stack = NEW_CREF(0); /* place holder */
+	iseq->cref_stack->nd_omod = Qnil;
     }
     else if (RTEST(parent)) {
 	rb_iseq_t *piseq;
@@ -1368,7 +1368,9 @@ rb_iseq_clone(VALUE iseqval, VALUE newcbase)
 	iseq1->local_iseq = iseq1;
     }
     if (newcbase) {
-	iseq1->cref_stack = NEW_BLOCK(newcbase);
+	iseq1->cref_stack = NEW_CREF(newcbase);
+	iseq1->cref_stack->nd_omod = iseq0->cref_stack->nd_omod;
+	iseq1->cref_stack->nd_visi = iseq0->cref_stack->nd_visi;
 	if (iseq0->cref_stack->nd_next) {
 	    iseq1->cref_stack->nd_next = iseq0->cref_stack->nd_next;
 	}
diff --git a/method.h b/method.h
index 3cfe438..36a1a2d 100644
--- a/method.h
+++ b/method.h
@@ -91,7 +91,7 @@ void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc,
 rb_method_entry_t *rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_flag_t noex);
 rb_method_entry_t *rb_method_entry(VALUE klass, ID id);
 
-rb_method_entry_t *rb_method_entry_get_without_cache(VALUE klass, ID id);
+rb_method_entry_t *rb_method_entry_get_without_cache(VALUE klass, VALUE omod, ID id);
 rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_flag_t noex);
 
 int rb_method_entry_arity(const rb_method_entry_t *me);
diff --git a/node.h b/node.h
index 82f7ba0..f663bdc 100644
--- a/node.h
+++ b/node.h
@@ -188,6 +188,8 @@ enum node_type {
 #define NODE_COLON2      NODE_COLON2
     NODE_COLON3,
 #define NODE_COLON3      NODE_COLON3
+    NODE_CREF,
+#define NODE_CREF        NODE_CREF
     NODE_DOT2,
 #define NODE_DOT2        NODE_DOT2
     NODE_DOT3,
@@ -234,7 +236,10 @@ enum node_type {
 
 typedef struct RNode {
     unsigned long flags;
-    char *nd_file;
+    union {
+	char *file;
+	VALUE value;
+    } u0;
     union {
 	struct RNode *node;
 	ID id;
@@ -277,6 +282,9 @@ typedef struct RNode {
 #define nd_set_line(n,l) \
     RNODE(n)->flags=((RNODE(n)->flags&~(-1<<NODE_LSHIFT))|(((l)&NODE_LMASK)<<NODE_LSHIFT))
 
+#define nd_file  u0.file
+#define nd_omod  u0.value
+
 #define nd_head  u1.node
 #define nd_alen  u2.argc
 #define nd_next  u3.node
@@ -433,6 +441,7 @@ typedef struct RNode {
 #define NEW_MODULE(n,b) NEW_NODE(NODE_MODULE,n,NEW_SCOPE(0,b),0)
 #define NEW_COLON2(c,i) NEW_NODE(NODE_COLON2,c,i,0)
 #define NEW_COLON3(i) NEW_NODE(NODE_COLON3,0,i,0)
+#define NEW_CREF(a) NEW_NODE(NODE_CREF,a,0,0)
 #define NEW_DOT2(b,e) NEW_NODE(NODE_DOT2,b,e,0)
 #define NEW_DOT3(b,e) NEW_NODE(NODE_DOT3,b,e,0)
 #define NEW_SELF() NEW_NODE(NODE_SELF,0,0,0)
diff --git a/vm.c b/vm.c
index 980e7ea..57c9ec8 100644
--- a/vm.c
+++ b/vm.c
@@ -816,6 +816,7 @@ rb_vm_cref(void)
 {
     rb_thread_t *th = GET_THREAD();
     rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
+    if (!cfp) return NULL;
     return vm_get_cref(cfp->iseq, cfp->lfp, cfp->dfp);
 }
 
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 18c22cb..fa387ed 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -1086,8 +1086,8 @@ static NODE *
 vm_cref_push(rb_thread_t *th, VALUE klass, int noex, rb_block_t *blockptr)
 {
     rb_control_frame_t *cfp = vm_get_ruby_level_caller_cfp(th, th->cfp);
-    NODE *cref = NEW_BLOCK(klass);
-    cref->nd_file = 0;
+    NODE *cref = NEW_CREF(klass);
+    cref->nd_omod = Qnil;
     cref->nd_visi = noex;
 
     if (blockptr) {
@@ -1096,6 +1096,11 @@ vm_cref_push(rb_thread_t *th, VALUE klass, int noex, rb_block_t *blockptr)
     else if (cfp) {
 	cref->nd_next = vm_get_cref0(cfp->iseq, cfp->lfp, cfp->dfp);
     }
+#if 0
+    if (cref->nd_next) {
+	cref->nd_omod = cref->nd_next->nd_omod;
+    }
+#endif
 
     return cref;
 }
diff --git a/vm_insnhelper.h b/vm_insnhelper.h
index 095b417..6a62794 100644
--- a/vm_insnhelper.h
+++ b/vm_insnhelper.h
@@ -151,6 +151,7 @@ extern VALUE ruby_vm_const_missing_count;
 
 #define COPY_CREF(c1, c2) do {  \
   NODE *__tmp_c2 = (c2); \
+  c1->nd_omod = __tmp_c2->nd_omod; \
   c1->nd_clss = __tmp_c2->nd_clss; \
   c1->nd_visi = __tmp_c2->nd_visi;\
   c1->nd_next = __tmp_c2->nd_next; \
diff --git a/vm_method.c b/vm_method.c
index fa5738b..500f72f 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -4,7 +4,7 @@
 
 #define CACHE_SIZE 0x800
 #define CACHE_MASK 0x7ff
-#define EXPR1(c,m) ((((c)>>3)^(m))&CACHE_MASK)
+#define EXPR1(c,o,m) ((((c)>>3)^((o)>>3)^(m))&CACHE_MASK)
 
 static void rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me);
 
@@ -15,6 +15,7 @@ static ID added, singleton_added, attached;
 struct cache_entry {		/* method hash table. */
     ID mid;			/* method's id */
     VALUE klass;		/* receiver's class */
+    VALUE omod;			/* overlay modules */
     rb_method_entry_t *me;
 };
 
@@ -395,6 +396,7 @@ static rb_method_entry_t*
 search_method(VALUE klass, ID id)
 {
     st_data_t body;
+
     if (!klass) {
 	return 0;
     }
@@ -416,14 +418,21 @@ search_method(VALUE klass, ID id)
  * rb_method_entry() simply.
  */
 rb_method_entry_t *
-rb_method_entry_get_without_cache(VALUE klass, ID id)
+rb_method_entry_get_without_cache(VALUE klass, VALUE omod, ID id)
 {
-    rb_method_entry_t *me = search_method(klass, id);
+    VALUE iclass;
+    rb_method_entry_t *me;
+
+    if (!NIL_P(omod) && !NIL_P(iclass = rb_hash_lookup(omod, klass))) {
+	klass = iclass;
+    }
+    me = search_method(klass, id);
 
     if (ruby_running) {
 	struct cache_entry *ent;
-	ent = cache + EXPR1(klass, id);
+	ent = cache + EXPR1(klass, omod, id);
 	ent->klass = klass;
+	ent->omod = omod;
 
 	if (UNDEFINED_METHOD_ENTRY_P(me)) {
 	    ent->mid = id;
@@ -443,13 +452,19 @@ rb_method_entry_t *
 rb_method_entry(VALUE klass, ID id)
 {
     struct cache_entry *ent;
+    NODE *cref = rb_vm_cref();
+    VALUE omod = Qnil;
+
+    if (cref && !NIL_P(cref->nd_omod)) {
+	omod = cref->nd_omod;
+    }
 
-    ent = cache + EXPR1(klass, id);
-    if (ent->mid == id && ent->klass == klass) {
+    ent = cache + EXPR1(klass, omod, id);
+    if (ent->mid == id && ent->klass == klass && ent->omod == omod) {
 	return ent->me;
     }
 
-    return rb_method_entry_get_without_cache(klass, id);
+    return rb_method_entry_get_without_cache(klass, omod, id);
 }
 
 static void
-- 
1.7.0.4

