mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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
This commit is contained in:
parent
8634544fa7
commit
8ddbbb3324
9 changed files with 148 additions and 2 deletions
|
@ -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
|
||||
|
|
42
class.c
42
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
|
||||
|
|
54
eval.c
54
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");
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
1
object.c
1
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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
11
vm_method.c
11
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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue