summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-06-27 07:48:50 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-06-27 07:48:50 +0000
commit8ddbbb33242f413845079b0274c28331cb4aa0f5 (patch)
tree522b12dada77b09073a58130c99c77d62c895b87
parent8634544fa74be0995582bc8056763fcfd38a0702 (diff)
Module#prepend
* class.c (rb_prepend_module): prepend module into another module. * eval.c (rb_mod_prepend): new method Module#prepend. [Feature #1102] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36234 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog6
-rw-r--r--class.c42
-rw-r--r--eval.c54
-rw-r--r--include/ruby/ruby.h1
-rw-r--r--internal.h2
-rw-r--r--object.c1
-rw-r--r--test/ruby/test_module.rb32
-rw-r--r--vm_insnhelper.c1
-rw-r--r--vm_method.c11
9 files changed, 148 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index 8145a70a17..310f6250a3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+Wed Jun 27 16:48:48 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * 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@ruby-lang.org>
* io.c (is_popen_fork): check if fork and raise NotImplementedError if
diff --git a/class.c b/class.c
index daaf401497..fc3b483735 100644
--- a/class.c
+++ b/class.c
@@ -56,6 +56,7 @@ class_alloc(VALUE flags, VALUE klass)
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 @@ rb_include_module(VALUE klass, VALUE module)
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 @@ rb_include_module(VALUE klass, VALUE module)
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
diff --git a/eval.c b/eval.c
index 1203a20ecc..37bb35a148 100644
--- a/eval.c
+++ b/eval.c
@@ -954,6 +954,58 @@ rb_mod_include(int argc, VALUE *argv, VALUE module)
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 @@ Init_eval(void)
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");
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
index 626f283ed2..23ba7535f2 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -1080,6 +1080,7 @@ VALUE rb_define_module_under(VALUE, const char*);
void rb_include_module(VALUE,VALUE);
void rb_extend_object(VALUE,VALUE);
+void rb_prepend_module(VALUE,VALUE);
struct rb_global_variable;
diff --git a/internal.h b/internal.h
index 51d71d7cab..77e6c2e191 100644
--- a/internal.h
+++ b/internal.h
@@ -27,6 +27,7 @@ struct rb_classext_struct {
VALUE super;
struct st_table *iv_tbl;
struct st_table *const_tbl;
+ VALUE origin;
};
#undef RCLASS_SUPER
@@ -36,6 +37,7 @@ struct rb_classext_struct {
#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 */
diff --git a/object.c b/object.c
index ac8fa53104..6e42043a55 100644
--- a/object.c
+++ b/object.c
@@ -2862,6 +2862,7 @@ Init_Object(void)
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);
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb
index b24fa3222d..decb217289 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -1238,4 +1238,36 @@ class TestModule < Test::Unit::TestCase
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
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 2e00c0e1de..6544ed89fc 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -1425,6 +1425,7 @@ static inline VALUE
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) {
diff --git a/vm_method.c b/vm_method.c
index c9f0130512..5a7722cd67 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -169,6 +169,9 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
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_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
}
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 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
#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 @@ search_method(VALUE klass, ID id)
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;