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
@ -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>
|
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
|
* 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_CONST_TBL(obj) = 0;
|
||||||
RCLASS_M_TBL(obj) = 0;
|
RCLASS_M_TBL(obj) = 0;
|
||||||
RCLASS_SUPER(obj) = 0;
|
RCLASS_SUPER(obj) = 0;
|
||||||
|
RCLASS_ORIGIN(obj) = (VALUE)obj;
|
||||||
RCLASS_IV_INDEX_TBL(obj) = 0;
|
RCLASS_IV_INDEX_TBL(obj) = 0;
|
||||||
return (VALUE)obj;
|
return (VALUE)obj;
|
||||||
}
|
}
|
||||||
@ -687,6 +688,8 @@ rb_include_module(VALUE klass, VALUE module)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (c == klass)
|
||||||
|
c = RCLASS_ORIGIN(klass);
|
||||||
c = RCLASS_SUPER(c) = include_class_new(module, RCLASS_SUPER(c));
|
c = RCLASS_SUPER(c) = include_class_new(module, RCLASS_SUPER(c));
|
||||||
if (RMODULE_M_TBL(module) && RMODULE_M_TBL(module)->num_entries)
|
if (RMODULE_M_TBL(module) && RMODULE_M_TBL(module)->num_entries)
|
||||||
changed = 1;
|
changed = 1;
|
||||||
@ -696,6 +699,45 @@ rb_include_module(VALUE klass, VALUE module)
|
|||||||
if (changed) rb_clear_cache();
|
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:
|
* call-seq:
|
||||||
* mod.included_modules -> array
|
* 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;
|
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
|
void
|
||||||
rb_obj_call_init(VALUE obj, int argc, VALUE *argv)
|
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, "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, "extend_object", rb_mod_extend_object, 1);
|
||||||
rb_define_private_method(rb_cModule, "include", rb_mod_include, -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");
|
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_include_module(VALUE,VALUE);
|
||||||
void rb_extend_object(VALUE,VALUE);
|
void rb_extend_object(VALUE,VALUE);
|
||||||
|
void rb_prepend_module(VALUE,VALUE);
|
||||||
|
|
||||||
struct rb_global_variable;
|
struct rb_global_variable;
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ struct rb_classext_struct {
|
|||||||
VALUE super;
|
VALUE super;
|
||||||
struct st_table *iv_tbl;
|
struct st_table *iv_tbl;
|
||||||
struct st_table *const_tbl;
|
struct st_table *const_tbl;
|
||||||
|
VALUE origin;
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef RCLASS_SUPER
|
#undef RCLASS_SUPER
|
||||||
@ -36,6 +37,7 @@ struct rb_classext_struct {
|
|||||||
#define RCLASS_CONST_TBL(c) (RCLASS_EXT(c)->const_tbl)
|
#define RCLASS_CONST_TBL(c) (RCLASS_EXT(c)->const_tbl)
|
||||||
#define RCLASS_M_TBL(c) (RCLASS(c)->m_tbl)
|
#define RCLASS_M_TBL(c) (RCLASS(c)->m_tbl)
|
||||||
#define RCLASS_IV_INDEX_TBL(c) (RCLASS(c)->iv_index_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 */
|
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_cClass, "inherited", rb_obj_dummy, 1);
|
||||||
rb_define_private_method(rb_cModule, "included", 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, "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_added", rb_obj_dummy, 1);
|
||||||
rb_define_private_method(rb_cModule, "method_removed", 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);
|
rb_define_private_method(rb_cModule, "method_undefined", rb_obj_dummy, 1);
|
||||||
|
@ -1238,4 +1238,36 @@ class TestModule < Test::Unit::TestCase
|
|||||||
INPUT
|
INPUT
|
||||||
assert_in_out_err([], src, ["NameError"], [])
|
assert_in_out_err([], src, ["NameError"], [])
|
||||||
end
|
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
|
end
|
||||||
|
@ -1425,6 +1425,7 @@ static inline VALUE
|
|||||||
vm_search_normal_superclass(VALUE klass, VALUE recv)
|
vm_search_normal_superclass(VALUE klass, VALUE recv)
|
||||||
{
|
{
|
||||||
if (BUILTIN_TYPE(klass) == T_CLASS) {
|
if (BUILTIN_TYPE(klass) == T_CLASS) {
|
||||||
|
klass = RCLASS_ORIGIN(klass);
|
||||||
return RCLASS_SUPER(klass);
|
return RCLASS_SUPER(klass);
|
||||||
}
|
}
|
||||||
else if (BUILTIN_TYPE(klass) == T_MODULE) {
|
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_definition_t *def, rb_method_flag_t noex)
|
||||||
{
|
{
|
||||||
rb_method_entry_t *me;
|
rb_method_entry_t *me;
|
||||||
|
#if NOEX_NOREDEF
|
||||||
|
VALUE rklass;
|
||||||
|
#endif
|
||||||
st_table *mtbl;
|
st_table *mtbl;
|
||||||
st_data_t data;
|
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);
|
rb_check_frozen(klass);
|
||||||
|
#if NOEX_NOREDEF
|
||||||
|
rklass = klass;
|
||||||
|
#endif
|
||||||
|
klass = RCLASS_ORIGIN(klass);
|
||||||
mtbl = RCLASS_M_TBL(klass);
|
mtbl = RCLASS_M_TBL(klass);
|
||||||
|
|
||||||
/* check re-definition */
|
/* check re-definition */
|
||||||
@ -205,7 +212,7 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
|
|||||||
#if NOEX_NOREDEF
|
#if NOEX_NOREDEF
|
||||||
if (old_me->flag & NOEX_NOREDEF) {
|
if (old_me->flag & NOEX_NOREDEF) {
|
||||||
rb_raise(rb_eTypeError, "cannot redefine %"PRIsVALUE"#%"PRIsVALUE,
|
rb_raise(rb_eTypeError, "cannot redefine %"PRIsVALUE"#%"PRIsVALUE,
|
||||||
rb_class_name(klass), rb_id2str(mid));
|
rb_class_name(rklass), rb_id2str(mid));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
rb_vm_check_redefinition_opt_method(old_me, klass);
|
rb_vm_check_redefinition_opt_method(old_me, klass);
|
||||||
@ -384,7 +391,7 @@ search_method(VALUE klass, ID id)
|
|||||||
return 0;
|
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);
|
klass = RCLASS_SUPER(klass);
|
||||||
if (!klass) {
|
if (!klass) {
|
||||||
return 0;
|
return 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user