* class.c (rb_mix_module): implement Module#mix.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@31873 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
fcc86b0f4c
commit
604fe33b44
@ -1,3 +1,7 @@
|
|||||||
|
Wed Jun 1 01:16:02 2011 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
|
* class.c (rb_mix_module): implement Module#mix.
|
||||||
|
|
||||||
Wed Jun 1 01:15:12 2011 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
Wed Jun 1 01:15:12 2011 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
* io.c (io_encoding_set): should honor already set ecflags since it
|
* io.c (io_encoding_set): should honor already set ecflags since it
|
||||||
|
169
class.c
169
class.c
@ -120,27 +120,28 @@ rb_class_new(VALUE super)
|
|||||||
return rb_class_boot(super);
|
return rb_class_boot(super);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct clone_method_data {
|
|
||||||
st_table *tbl;
|
|
||||||
VALUE klass;
|
|
||||||
};
|
|
||||||
|
|
||||||
VALUE rb_iseq_clone(VALUE iseqval, VALUE newcbase);
|
VALUE rb_iseq_clone(VALUE iseqval, VALUE newcbase);
|
||||||
|
|
||||||
static int
|
static void
|
||||||
clone_method(ID mid, const rb_method_entry_t *me, struct clone_method_data *data)
|
rb_mod_clone_method(VALUE klass, ID mid, const rb_method_entry_t *me)
|
||||||
{
|
{
|
||||||
VALUE newiseqval;
|
VALUE newiseqval;
|
||||||
if (me->def && me->def->type == VM_METHOD_TYPE_ISEQ) {
|
if (me->def && me->def->type == VM_METHOD_TYPE_ISEQ) {
|
||||||
rb_iseq_t *iseq;
|
rb_iseq_t *iseq;
|
||||||
newiseqval = rb_iseq_clone(me->def->body.iseq->self, data->klass);
|
newiseqval = rb_iseq_clone(me->def->body.iseq->self, klass);
|
||||||
GetISeqPtr(newiseqval, iseq);
|
GetISeqPtr(newiseqval, iseq);
|
||||||
rb_add_method(data->klass, mid, VM_METHOD_TYPE_ISEQ, iseq, me->flag);
|
rb_add_method(klass, mid, VM_METHOD_TYPE_ISEQ, iseq, me->flag);
|
||||||
RB_GC_GUARD(newiseqval);
|
RB_GC_GUARD(newiseqval);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rb_method_entry_set(data->klass, mid, me, me->flag);
|
rb_method_entry_set(klass, mid, me, me->flag);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
clone_method_i(st_data_t key, st_data_t value, st_data_t data)
|
||||||
|
{
|
||||||
|
rb_mod_clone_method((VALUE)data, (ID)key, (const rb_method_entry_t *)value);
|
||||||
return ST_CONTINUE;
|
return ST_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,15 +184,11 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
|
|||||||
st_foreach(RCLASS_CONST_TBL(orig), clone_const, (st_data_t)RCLASS_CONST_TBL(clone));
|
st_foreach(RCLASS_CONST_TBL(orig), clone_const, (st_data_t)RCLASS_CONST_TBL(clone));
|
||||||
}
|
}
|
||||||
if (RCLASS_M_TBL(orig)) {
|
if (RCLASS_M_TBL(orig)) {
|
||||||
struct clone_method_data data;
|
|
||||||
|
|
||||||
if (RCLASS_M_TBL(clone)) {
|
if (RCLASS_M_TBL(clone)) {
|
||||||
rb_free_m_table(RCLASS_M_TBL(clone));
|
rb_free_m_table(RCLASS_M_TBL(clone));
|
||||||
}
|
}
|
||||||
data.tbl = RCLASS_M_TBL(clone) = st_init_numtable();
|
RCLASS_M_TBL(clone) = st_init_numtable();
|
||||||
data.klass = clone;
|
st_foreach(RCLASS_M_TBL(orig), clone_method_i, (st_data_t)clone);
|
||||||
st_foreach(RCLASS_M_TBL(orig), clone_method,
|
|
||||||
(st_data_t)&data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return clone;
|
return clone;
|
||||||
@ -221,12 +218,11 @@ rb_singleton_class_clone(VALUE obj)
|
|||||||
if (!FL_TEST(klass, FL_SINGLETON))
|
if (!FL_TEST(klass, FL_SINGLETON))
|
||||||
return klass;
|
return klass;
|
||||||
else {
|
else {
|
||||||
struct clone_method_data data;
|
|
||||||
/* copy singleton(unnamed) class */
|
/* copy singleton(unnamed) class */
|
||||||
VALUE clone = class_alloc((RBASIC(klass)->flags & ~(FL_MARK)), 0);
|
VALUE clone = class_alloc((RBASIC(klass)->flags & ~(FL_MARK)), 0);
|
||||||
|
|
||||||
if (BUILTIN_TYPE(obj) == T_CLASS) {
|
if (BUILTIN_TYPE(obj) == T_CLASS) {
|
||||||
RBASIC(clone)->klass = (VALUE)clone;
|
RBASIC(clone)->klass = clone;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
RBASIC(clone)->klass = rb_singleton_class_clone(klass);
|
RBASIC(clone)->klass = rb_singleton_class_clone(klass);
|
||||||
@ -241,13 +237,10 @@ rb_singleton_class_clone(VALUE obj)
|
|||||||
st_foreach(RCLASS_CONST_TBL(klass), clone_const, (st_data_t)RCLASS_CONST_TBL(clone));
|
st_foreach(RCLASS_CONST_TBL(klass), clone_const, (st_data_t)RCLASS_CONST_TBL(clone));
|
||||||
}
|
}
|
||||||
RCLASS_M_TBL(clone) = st_init_numtable();
|
RCLASS_M_TBL(clone) = st_init_numtable();
|
||||||
data.tbl = RCLASS_M_TBL(clone);
|
st_foreach(RCLASS_M_TBL(klass), clone_method_i, (st_data_t)clone);
|
||||||
data.klass = (VALUE)clone;
|
rb_singleton_class_attached(RBASIC(clone)->klass, clone);
|
||||||
st_foreach(RCLASS_M_TBL(klass), clone_method,
|
|
||||||
(st_data_t)&data);
|
|
||||||
rb_singleton_class_attached(RBASIC(clone)->klass, (VALUE)clone);
|
|
||||||
FL_SET(clone, FL_SINGLETON);
|
FL_SET(clone, FL_SINGLETON);
|
||||||
return (VALUE)clone;
|
return clone;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -697,6 +690,134 @@ rb_include_module(VALUE klass, VALUE module)
|
|||||||
if (changed) rb_clear_cache();
|
if (changed) rb_clear_cache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct mixing_arg {
|
||||||
|
st_table *mtbl;
|
||||||
|
ID id;
|
||||||
|
st_table *aliasing;
|
||||||
|
VALUE klass;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
check_mix_const_i(st_data_t key, st_data_t value, st_data_t arg)
|
||||||
|
{
|
||||||
|
struct mixing_arg *argp = (struct mixing_arg *)arg;
|
||||||
|
ID id = (ID)key;
|
||||||
|
st_table *aliasing = argp->aliasing;
|
||||||
|
st_data_t alias;
|
||||||
|
|
||||||
|
if (!rb_is_const_id(id)) return ST_CONTINUE;
|
||||||
|
if (aliasing && st_lookup(aliasing, ID2SYM(id), &alias)) {
|
||||||
|
id = rb_to_id(alias);
|
||||||
|
}
|
||||||
|
if (st_lookup(argp->mtbl, id, NULL)) {
|
||||||
|
argp->id = id;
|
||||||
|
return ST_STOP;
|
||||||
|
}
|
||||||
|
return ST_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_mix_const_i(st_data_t key, st_data_t value, st_data_t arg)
|
||||||
|
{
|
||||||
|
struct mixing_arg *argp = (struct mixing_arg *)arg;
|
||||||
|
ID id = (ID)key;
|
||||||
|
st_table *aliasing = argp->aliasing;
|
||||||
|
st_data_t old, alias;
|
||||||
|
|
||||||
|
if (!rb_is_const_id(id)) return ST_CONTINUE;
|
||||||
|
if (aliasing && st_lookup(aliasing, ID2SYM(id), &alias)) {
|
||||||
|
id = rb_to_id(alias);
|
||||||
|
}
|
||||||
|
if (st_lookup(argp->mtbl, id, &old)) {
|
||||||
|
argp->id = id;
|
||||||
|
return ST_STOP;
|
||||||
|
}
|
||||||
|
st_insert(argp->mtbl, id, value);
|
||||||
|
return ST_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
check_mix_method_i(st_data_t key, st_data_t value, st_data_t arg)
|
||||||
|
{
|
||||||
|
struct mixing_arg *argp = (struct mixing_arg *)arg;
|
||||||
|
ID id = (ID)key;
|
||||||
|
st_table *aliasing = argp->aliasing;
|
||||||
|
st_data_t alias;
|
||||||
|
|
||||||
|
if (aliasing && st_lookup(aliasing, ID2SYM(id), &alias)) {
|
||||||
|
id = rb_to_id(alias);
|
||||||
|
}
|
||||||
|
if (st_lookup(argp->mtbl, id, NULL)) {
|
||||||
|
argp->id = id;
|
||||||
|
return ST_STOP;
|
||||||
|
}
|
||||||
|
return ST_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_mix_method_i(st_data_t key, st_data_t value, st_data_t arg)
|
||||||
|
{
|
||||||
|
struct mixing_arg *argp = (struct mixing_arg *)arg;
|
||||||
|
ID id = (ID)key;
|
||||||
|
st_table *aliasing = argp->aliasing;
|
||||||
|
st_data_t old, alias;
|
||||||
|
|
||||||
|
if (aliasing && st_lookup(aliasing, ID2SYM(id), &alias)) {
|
||||||
|
id = rb_to_id(alias);
|
||||||
|
}
|
||||||
|
if (st_lookup(argp->mtbl, id, &old)) {
|
||||||
|
argp->id = id;
|
||||||
|
return ST_STOP;
|
||||||
|
}
|
||||||
|
rb_mod_clone_method(argp->klass, id, (rb_method_entry_t *)value);
|
||||||
|
return ST_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rb_mix_module(VALUE klass, VALUE module, st_table *constants, st_table *methods)
|
||||||
|
{
|
||||||
|
st_table *mtbl_from;
|
||||||
|
struct mixing_arg methodarg, constarg;
|
||||||
|
|
||||||
|
rb_frozen_class_p(klass);
|
||||||
|
if (!OBJ_UNTRUSTED(klass)) {
|
||||||
|
rb_secure(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TYPE(module) != T_MODULE) {
|
||||||
|
Check_Type(module, T_MODULE);
|
||||||
|
}
|
||||||
|
|
||||||
|
OBJ_INFECT(klass, module);
|
||||||
|
|
||||||
|
mtbl_from = RMODULE_M_TBL(module);
|
||||||
|
methodarg.mtbl = RMODULE_M_TBL(klass);
|
||||||
|
methodarg.id = 0;
|
||||||
|
methodarg.aliasing = methods;
|
||||||
|
methodarg.klass = klass;
|
||||||
|
constarg.mtbl = RMODULE_IV_TBL(klass);
|
||||||
|
constarg.id = 0;
|
||||||
|
constarg.aliasing = constants;
|
||||||
|
|
||||||
|
st_foreach(mtbl_from, check_mix_method_i, (st_data_t)&methodarg);
|
||||||
|
if (methodarg.id) {
|
||||||
|
rb_raise(rb_eArgError, "method would conflict - %s", rb_id2name(methodarg.id));
|
||||||
|
}
|
||||||
|
st_foreach(mtbl_from, check_mix_const_i, (st_data_t)&constarg);
|
||||||
|
if (constarg.id) {
|
||||||
|
rb_raise(rb_eArgError, "constant would conflict - %s", rb_id2name(constarg.id));
|
||||||
|
}
|
||||||
|
st_foreach(mtbl_from, do_mix_method_i, (st_data_t)&methodarg);
|
||||||
|
if (methodarg.id) {
|
||||||
|
rb_raise(rb_eArgError, "method would conflict - %s", rb_id2name(methodarg.id));
|
||||||
|
}
|
||||||
|
st_foreach(mtbl_from, do_mix_const_i, (st_data_t)&constarg);
|
||||||
|
if (constarg.id) {
|
||||||
|
rb_raise(rb_eArgError, "constant would conflict - %s", rb_id2name(constarg.id));
|
||||||
|
}
|
||||||
|
rb_vm_inc_const_missing_count();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* mod.included_modules -> array
|
* mod.included_modules -> array
|
||||||
|
64
eval.c
64
eval.c
@ -880,6 +880,69 @@ rb_mod_include(int argc, VALUE *argv, VALUE module)
|
|||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* mix(module, ...) -> module
|
||||||
|
*
|
||||||
|
* Mix +Module+> into self.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
rb_mod_mix_into(int argc, VALUE *argv, VALUE klass)
|
||||||
|
{
|
||||||
|
VALUE module, tmp, constants = Qnil, methods = Qnil;
|
||||||
|
st_table *const_tbl = 0, *method_tbl = 0;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (argc < 1 || argc > 3) {
|
||||||
|
wrong_args:
|
||||||
|
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
|
||||||
|
}
|
||||||
|
module = argv[i++];
|
||||||
|
|
||||||
|
switch (TYPE(module)) {
|
||||||
|
case T_CLASS:
|
||||||
|
case T_MODULE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Check_Type(module, T_CLASS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i < argc) {
|
||||||
|
constants = argv[i++];
|
||||||
|
if (!NIL_P(tmp = rb_check_array_type(constants))) {
|
||||||
|
constants = tmp;
|
||||||
|
}
|
||||||
|
else if (!NIL_P(methods = rb_check_hash_type(constants))) {
|
||||||
|
constants = Qnil;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Check_Type(constants, T_HASH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i < argc && NIL_P(methods)) {
|
||||||
|
methods = argv[i++];
|
||||||
|
if (NIL_P(tmp = rb_check_hash_type(methods))) {
|
||||||
|
Check_Type(methods, T_HASH);
|
||||||
|
}
|
||||||
|
methods = tmp;
|
||||||
|
}
|
||||||
|
if (i < argc) goto wrong_args;
|
||||||
|
if (!NIL_P(constants)) {
|
||||||
|
VALUE hash = rb_hash_new();
|
||||||
|
for (i = 0; i < RARRAY_LEN(constants); ++i) {
|
||||||
|
rb_hash_update_by(hash, RARRAY_PTR(constants)[i], NULL);
|
||||||
|
}
|
||||||
|
const_tbl = RHASH_TBL(RB_GC_GUARD(constants) = hash);
|
||||||
|
}
|
||||||
|
if (!NIL_P(methods)) {
|
||||||
|
method_tbl = RHASH_TBL(RB_GC_GUARD(methods));
|
||||||
|
}
|
||||||
|
|
||||||
|
rb_mix_module(klass, module, const_tbl, method_tbl);
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_obj_call_init(VALUE obj, int argc, VALUE *argv)
|
rb_obj_call_init(VALUE obj, int argc, VALUE *argv)
|
||||||
{
|
{
|
||||||
@ -1144,6 +1207,7 @@ 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, "mix", rb_mod_mix_into, -1);
|
||||||
|
|
||||||
rb_undef_method(rb_cClass, "module_function");
|
rb_undef_method(rb_cClass, "module_function");
|
||||||
|
|
||||||
|
1
method.h
1
method.h
@ -100,5 +100,6 @@ void rb_mark_method_entry(const rb_method_entry_t *me);
|
|||||||
void rb_free_method_entry(rb_method_entry_t *me);
|
void rb_free_method_entry(rb_method_entry_t *me);
|
||||||
void rb_sweep_method_entry(void *vm);
|
void rb_sweep_method_entry(void *vm);
|
||||||
void rb_free_m_table(st_table *tbl);
|
void rb_free_m_table(st_table *tbl);
|
||||||
|
void rb_mix_module(VALUE klass, VALUE module, struct st_table *constants, struct st_table *methods);
|
||||||
|
|
||||||
#endif /* METHOD_H */
|
#endif /* METHOD_H */
|
||||||
|
@ -1068,4 +1068,33 @@ class TestModule < Test::Unit::TestCase
|
|||||||
INPUT
|
INPUT
|
||||||
assert_in_out_err([], src, ["NameError"], [])
|
assert_in_out_err([], src, ["NameError"], [])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_mix
|
||||||
|
american = Module.new do
|
||||||
|
attr_accessor :address
|
||||||
|
end
|
||||||
|
japanese = Module.new do
|
||||||
|
attr_accessor :address
|
||||||
|
end
|
||||||
|
|
||||||
|
japanese_american = Class.new
|
||||||
|
assert_nothing_raised(ArgumentError) {
|
||||||
|
japanese_american.class_eval {mix american}
|
||||||
|
}
|
||||||
|
assert_raise(ArgumentError) {
|
||||||
|
japanese_american.class_eval {mix japanese}
|
||||||
|
}
|
||||||
|
|
||||||
|
japanese_american = Class.new
|
||||||
|
assert_nothing_raised(ArgumentError) {
|
||||||
|
japanese_american.class_eval {
|
||||||
|
mix american, :address => :us_address, :address= => :us_address=
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_nothing_raised(ArgumentError) {
|
||||||
|
japanese_american.class_eval {
|
||||||
|
mix japanese, :address => :jp_address, :address= => :jp_address=
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user