[前][次][番号順一覧][スレッド一覧]

ruby-changes:24183

From: nobu <ko1@a...>
Date: Wed, 27 Jun 2012 16:50:09 +0900 (JST)
Subject: [ruby-changes:24183] nobu:r36234 (trunk): Module#prepend

nobu	2012-06-27 16:48:50 +0900 (Wed, 27 Jun 2012)

  New Revision: 36234

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=36234

  Log:
    Module#prepend
    
    * class.c (rb_prepend_module): prepend module into another module.
    * eval.c (rb_mod_prepend): new method Module#prepend.  [Feature #1102]

  Modified files:
    trunk/ChangeLog
    trunk/class.c
    trunk/eval.c
    trunk/include/ruby/ruby.h
    trunk/internal.h
    trunk/object.c
    trunk/test/ruby/test_module.rb
    trunk/vm_insnhelper.c
    trunk/vm_method.c

Index: include/ruby/ruby.h
===================================================================
--- include/ruby/ruby.h	(revision 36233)
+++ include/ruby/ruby.h	(revision 36234)
@@ -1080,6 +1080,7 @@
 
 void rb_include_module(VALUE,VALUE);
 void rb_extend_object(VALUE,VALUE);
+void rb_prepend_module(VALUE,VALUE);
 
 struct rb_global_variable;
 
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 36233)
+++ ChangeLog	(revision 36234)
@@ -1,3 +1,9 @@
+Wed Jun 27 16:48:48 2012  Nobuyoshi Nakada  <nobu@r...>
+
+	* class.c (rb_prepend_module): prepend module into another module.
+
+	* eval.c (rb_mod_prepend): new method Module#prepend.  [Feature #1102]
+
 Wed Jun 27 09:15:46 2012  Nobuyoshi Nakada  <nobu@r...>
 
 	* io.c (is_popen_fork): check if fork and raise NotImplementedError if
Index: object.c
===================================================================
--- object.c	(revision 36233)
+++ object.c	(revision 36234)
@@ -2862,6 +2862,7 @@
     rb_define_private_method(rb_cClass, "inherited", rb_obj_dummy, 1);
     rb_define_private_method(rb_cModule, "included", rb_obj_dummy, 1);
     rb_define_private_method(rb_cModule, "extended", rb_obj_dummy, 1);
+    rb_define_private_method(rb_cModule, "prepended", rb_obj_dummy, 1);
     rb_define_private_method(rb_cModule, "method_added", rb_obj_dummy, 1);
     rb_define_private_method(rb_cModule, "method_removed", rb_obj_dummy, 1);
     rb_define_private_method(rb_cModule, "method_undefined", rb_obj_dummy, 1);
Index: vm_method.c
===================================================================
--- vm_method.c	(revision 36233)
+++ vm_method.c	(revision 36234)
@@ -169,6 +169,9 @@
 		     rb_method_definition_t *def, rb_method_flag_t noex)
 {
     rb_method_entry_t *me;
+#if NOEX_NOREDEF
+    VALUE rklass;
+#endif
     st_table *mtbl;
     st_data_t data;
 
@@ -194,6 +197,10 @@
     }
 
     rb_check_frozen(klass);
+#if NOEX_NOREDEF
+    rklass = klass;
+#endif
+    klass = RCLASS_ORIGIN(klass);
     mtbl = RCLASS_M_TBL(klass);
 
     /* check re-definition */
@@ -205,7 +212,7 @@
 #if NOEX_NOREDEF
 	if (old_me->flag & NOEX_NOREDEF) {
 	    rb_raise(rb_eTypeError, "cannot redefine %"PRIsVALUE"#%"PRIsVALUE,
-		     rb_class_name(klass), rb_id2str(mid));
+		     rb_class_name(rklass), rb_id2str(mid));
 	}
 #endif
 	rb_vm_check_redefinition_opt_method(old_me, klass);
@@ -384,7 +391,7 @@
 	return 0;
     }
 
-    while (!st_lookup(RCLASS_M_TBL(klass), id, &body)) {
+    while (!RCLASS_M_TBL(klass) || !st_lookup(RCLASS_M_TBL(klass), id, &body)) {
 	klass = RCLASS_SUPER(klass);
 	if (!klass) {
 	    return 0;
Index: eval.c
===================================================================
--- eval.c	(revision 36233)
+++ eval.c	(revision 36234)
@@ -954,6 +954,58 @@
     return module;
 }
 
+/*
+ *  call-seq:
+ *     prepend_features(mod)   -> mod
+ *
+ *  When this module is prepended in another, Ruby calls
+ *  <code>prepend_features</code> in this module, passing it the
+ *  receiving module in _mod_. Ruby's default implementation is
+ *  to overlay the constants, methods, and module variables of this module
+ *  to _mod_ if this module has not already been added to
+ *  _mod_ or one of its ancestors. See also <code>Module#prepend</code>.
+ */
+
+static VALUE
+rb_mod_prepend_features(VALUE module, VALUE prepend)
+{
+    switch (TYPE(prepend)) {
+      case T_CLASS:
+      case T_MODULE:
+	break;
+      default:
+	Check_Type(prepend, T_CLASS);
+	break;
+    }
+    rb_prepend_module(prepend, module);
+
+    return module;
+}
+
+/*
+ *  call-seq:
+ *     prepend(module, ...)    -> self
+ *
+ *  Invokes <code>Module.prepend_features</code> on each parameter in reverse order.
+ */
+
+static VALUE
+rb_mod_prepend(int argc, VALUE *argv, VALUE module)
+{
+    int i;
+    ID id_prepend_features, id_prepended;
+
+    CONST_ID(id_prepend_features, "prepend_features");
+    CONST_ID(id_prepended, "prepended");
+    for (i = 0; i < argc; i++)
+	Check_Type(argv[i], T_MODULE);
+    while (argc--) {
+	rb_funcall(argv[argc], id_prepend_features, 1, module);
+	rb_funcall(argv[argc], id_prepended, 1, module);
+    }
+    return module;
+}
+
 void
 rb_obj_call_init(VALUE obj, int argc, VALUE *argv)
 {
@@ -1213,6 +1265,8 @@
     rb_define_private_method(rb_cModule, "append_features", rb_mod_append_features, 1);
     rb_define_private_method(rb_cModule, "extend_object", rb_mod_extend_object, 1);
     rb_define_private_method(rb_cModule, "include", rb_mod_include, -1);
+    rb_define_private_method(rb_cModule, "prepend_features", rb_mod_prepend_features, 1);
+    rb_define_private_method(rb_cModule, "prepend", rb_mod_prepend, -1);
 
     rb_undef_method(rb_cClass, "module_function");
 
Index: class.c
===================================================================
--- class.c	(revision 36233)
+++ class.c	(revision 36234)
@@ -56,6 +56,7 @@
     RCLASS_CONST_TBL(obj) = 0;
     RCLASS_M_TBL(obj) = 0;
     RCLASS_SUPER(obj) = 0;
+    RCLASS_ORIGIN(obj) = (VALUE)obj;
     RCLASS_IV_INDEX_TBL(obj) = 0;
     return (VALUE)obj;
 }
@@ -687,6 +688,8 @@
 		break;
 	    }
 	}
+	if (c == klass)
+	    c = RCLASS_ORIGIN(klass);
 	c = RCLASS_SUPER(c) = include_class_new(module, RCLASS_SUPER(c));
 	if (RMODULE_M_TBL(module) && RMODULE_M_TBL(module)->num_entries)
 	    changed = 1;
@@ -696,6 +699,45 @@
     if (changed) rb_clear_cache();
 }
 
+void
+rb_prepend_module(VALUE klass, VALUE module)
+{
+    VALUE p, c, origin;
+
+    rb_frozen_class_p(klass);
+    if (!OBJ_UNTRUSTED(klass)) {
+	rb_secure(4);
+    }
+
+    Check_Type(module, T_MODULE);
+
+    OBJ_INFECT(klass, module);
+    c = RCLASS_SUPER(klass);
+    if (RCLASS_M_TBL(klass) == RCLASS_M_TBL(module))
+	rb_raise(rb_eArgError, "cyclic include detected");
+    for (p = c; p; p = RCLASS_SUPER(p)) {
+	if (BUILTIN_TYPE(p) == T_ICLASS) {
+	    if (RCLASS_M_TBL(p) == RCLASS_M_TBL(module)) {
+		rb_raise(rb_eArgError, "already prepended module");
+	    }
+	}
+    }
+    origin = RCLASS_ORIGIN(klass);
+    if (origin == klass) {
+	origin = class_alloc(T_ICLASS, rb_cClass);
+	RCLASS_SUPER(origin) = RCLASS_SUPER(klass);
+	RCLASS_SUPER(klass) = origin;
+	RCLASS_ORIGIN(klass) = origin;
+	RCLASS_M_TBL(origin) = RCLASS_M_TBL(klass);
+	RCLASS_M_TBL(klass) = 0;
+	c = origin;
+    }
+    RCLASS_SUPER(klass) = include_class_new(module, c);
+    if (RMODULE_M_TBL(module) && RMODULE_M_TBL(module)->num_entries) {
+	rb_clear_cache_by_class(klass);
+    }
+}
+
 /*
  *  call-seq:
  *     mod.included_modules -> array
Index: internal.h
===================================================================
--- internal.h	(revision 36233)
+++ internal.h	(revision 36234)
@@ -27,6 +27,7 @@
     VALUE super;
     struct st_table *iv_tbl;
     struct st_table *const_tbl;
+    VALUE origin;
 };
 
 #undef RCLASS_SUPER
@@ -36,6 +37,7 @@
 #define RCLASS_CONST_TBL(c) (RCLASS_EXT(c)->const_tbl)
 #define RCLASS_M_TBL(c) (RCLASS(c)->m_tbl)
 #define RCLASS_IV_INDEX_TBL(c) (RCLASS(c)->iv_index_tbl)
+#define RCLASS_ORIGIN(c) (RCLASS_EXT(c)->origin)
 
 struct vtm; /* defined by timev.h */
 
Index: vm_insnhelper.c
===================================================================
--- vm_insnhelper.c	(revision 36233)
+++ vm_insnhelper.c	(revision 36234)
@@ -1425,6 +1425,7 @@
 vm_search_normal_superclass(VALUE klass, VALUE recv)
 {
     if (BUILTIN_TYPE(klass) == T_CLASS) {
+	klass = RCLASS_ORIGIN(klass);
 	return RCLASS_SUPER(klass);
     }
     else if (BUILTIN_TYPE(klass) == T_MODULE) {
Index: test/ruby/test_module.rb
===================================================================
--- test/ruby/test_module.rb	(revision 36233)
+++ test/ruby/test_module.rb	(revision 36234)
@@ -1238,4 +1238,36 @@
     INPUT
     assert_in_out_err([], src, ["NameError"], [])
   end
+
+  module M0
+    def m1; [:M0] end
+  end
+  module M1
+    def m1; [:M1, super, :M1] end
+  end
+  module M2
+    def m1; [:M2, super, :M2] end
+  end
+  M3 = Module.new do
+    def m1; [:M3, super, :M3] end
+  end
+  module M4
+    def m1; [:M4, super, :M4] end
+  end
+  class C0
+    include M0
+    prepend M1
+    def m1; [:C0, super, :C0] end
+  end
+  class C1 < C0
+    prepend M2, M3
+    include M4
+    def m1; [:C1, super, :C1] end
+  end
+
+  def test_prepend
+    obj = C1.new
+    expected = [:M2,[:M3,[:C1,[:M4,[:M1,[:C0,[:M0],:C0],:M1],:M4],:C1],:M3],:M2]
+    assert_equal(expected, obj.m1)
+  end
 end

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]