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

ruby-changes:19827

From: nobu <ko1@a...>
Date: Wed, 1 Jun 2011 01:16:13 +0900 (JST)
Subject: [ruby-changes:19827] nobu:r31873 (trunk): * class.c (rb_mix_module): implement Module#mix.

nobu	2011-06-01 01:16:06 +0900 (Wed, 01 Jun 2011)

  New Revision: 31873

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

  Log:
    * class.c (rb_mix_module): implement Module#mix.

  Modified files:
    trunk/ChangeLog
    trunk/class.c
    trunk/eval.c
    trunk/method.h
    trunk/test/ruby/test_module.rb

Index: method.h
===================================================================
--- method.h	(revision 31872)
+++ method.h	(revision 31873)
@@ -100,5 +100,6 @@
 void rb_free_method_entry(rb_method_entry_t *me);
 void rb_sweep_method_entry(void *vm);
 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 */
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 31872)
+++ ChangeLog	(revision 31873)
@@ -1,3 +1,7 @@
+Wed Jun  1 01:16:02 2011  Nobuyoshi Nakada  <nobu@r...>
+
+	* class.c (rb_mix_module): implement Module#mix.
+
 Wed Jun  1 01:15:12 2011  Nobuyoshi Nakada  <nobu@r...>
 
 	* io.c (io_encoding_set): should honor already set ecflags since it
Index: eval.c
===================================================================
--- eval.c	(revision 31872)
+++ eval.c	(revision 31873)
@@ -880,6 +880,69 @@
     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
 rb_obj_call_init(VALUE obj, int argc, VALUE *argv)
 {
@@ -1144,6 +1207,7 @@
     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, "mix", rb_mod_mix_into, -1);
 
     rb_undef_method(rb_cClass, "module_function");
 
Index: class.c
===================================================================
--- class.c	(revision 31872)
+++ class.c	(revision 31873)
@@ -120,27 +120,28 @@
     return rb_class_boot(super);
 }
 
-struct clone_method_data {
-    st_table *tbl;
-    VALUE klass;
-};
-
 VALUE rb_iseq_clone(VALUE iseqval, VALUE newcbase);
 
-static int
-clone_method(ID mid, const rb_method_entry_t *me, struct clone_method_data *data)
+static void
+rb_mod_clone_method(VALUE klass, ID mid, const rb_method_entry_t *me)
 {
     VALUE newiseqval;
     if (me->def && me->def->type == VM_METHOD_TYPE_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);
-	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);
     }
     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;
 }
 
@@ -183,15 +184,11 @@
 	st_foreach(RCLASS_CONST_TBL(orig), clone_const, (st_data_t)RCLASS_CONST_TBL(clone));
     }
     if (RCLASS_M_TBL(orig)) {
-	struct clone_method_data data;
-
 	if (RCLASS_M_TBL(clone)) {
 	    rb_free_m_table(RCLASS_M_TBL(clone));
 	}
-	data.tbl = RCLASS_M_TBL(clone) = st_init_numtable();
-	data.klass = clone;
-	st_foreach(RCLASS_M_TBL(orig), clone_method,
-		   (st_data_t)&data);
+	RCLASS_M_TBL(clone) = st_init_numtable();
+	st_foreach(RCLASS_M_TBL(orig), clone_method_i, (st_data_t)clone);
     }
 
     return clone;
@@ -221,12 +218,11 @@
     if (!FL_TEST(klass, FL_SINGLETON))
 	return klass;
     else {
-	struct clone_method_data data;
 	/* copy singleton(unnamed) class */
 	VALUE clone = class_alloc((RBASIC(klass)->flags & ~(FL_MARK)), 0);
 
 	if (BUILTIN_TYPE(obj) == T_CLASS) {
-	    RBASIC(clone)->klass = (VALUE)clone;
+	    RBASIC(clone)->klass = clone;
 	}
 	else {
 	    RBASIC(clone)->klass = rb_singleton_class_clone(klass);
@@ -241,13 +237,10 @@
 	    st_foreach(RCLASS_CONST_TBL(klass), clone_const, (st_data_t)RCLASS_CONST_TBL(clone));
 	}
 	RCLASS_M_TBL(clone) = st_init_numtable();
-	data.tbl = RCLASS_M_TBL(clone);
-	data.klass = (VALUE)clone;
-	st_foreach(RCLASS_M_TBL(klass), clone_method,
-		   (st_data_t)&data);
-	rb_singleton_class_attached(RBASIC(clone)->klass, (VALUE)clone);
+	st_foreach(RCLASS_M_TBL(klass), clone_method_i, (st_data_t)clone);
+	rb_singleton_class_attached(RBASIC(clone)->klass, clone);
 	FL_SET(clone, FL_SINGLETON);
-	return (VALUE)clone;
+	return clone;
     }
 }
 
@@ -697,6 +690,134 @@
     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:
  *     mod.included_modules -> array
Index: test/ruby/test_module.rb
===================================================================
--- test/ruby/test_module.rb	(revision 31872)
+++ test/ruby/test_module.rb	(revision 31873)
@@ -1068,4 +1068,33 @@
     INPUT
     assert_in_out_err([], src, ["NameError"], [])
   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

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

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