Project

General

Profile

Bug #5283

クラスオブジェクトの clone に関する質問

Added by shiba (satoshi shiba) almost 8 years ago. Updated over 6 years ago.

Status:
Closed
Priority:
Normal
Target version:
-
ruby -v:
-
Backport:
[ruby-dev:44477]

Description

=begin
芝と申します。
クラスオブジェクトを clone したときの挙動について質問させてください。

  • サンプルコード String.class_eval do def self.singleton_method_added(mid) puts("singleton_method_added: self = #{self}, mid = #{mid}") end end

puts("start clone")
StrClone = String.clone
puts("finish clone")

  • サンプルコードの実行結果 singleton_method_added: self = String, mid = singleton_method_added start clone singleton_method_added: self = String, mid = try_convert singleton_method_added: self = String, mid = singleton_method_added finish clone

上記のサンプルコードを実行すると、String クラスを clone し、メソッドテー
ブルを StrClone にコピーしているときに、String クラスに対して
singleton_method_added が呼び出され、実行結果のように出力されます。
質問なんですが、クラスオブジェクトの clone 時に clone 元のクラスオブジェ
クトの singleton_method_added が呼び出されるのは仕様なのでしょうか。

上記のサンプルコードの場合、StrClone の singleton_method_added が呼び出
されるか、もしくは singleton_method_added が呼び出されないことを期待して
singleton_method_added を使用していました。

上記のサンプルコードで String クラスの singleton_method_added が呼び出さ
れる原因は、rb_singleton_class_clone(class.c) にあります。
rb_singleton_class_clone では、以下のように特異クラスのインスタンス変数
をコピーするのですが、このとき、"attached" というインスタンス変数を
コピーしています。

VALUE
rb_singleton_class_clone(VALUE obj)
{
...
if (RCLASS_IV_TBL(klass)) {
/* インスタンス変数のコピー /
RCLASS_IV_TBL(clone) = st_copy(RCLASS_IV_TBL(klass));
}
...
RCLASS_M_TBL(clone) = st_init_numtable();
data.tbl = RCLASS_M_TBL(clone);
data.klass = (VALUE)clone;
/
メソッドテーブルのコピー、method_added の呼び出し */
st_foreach(RCLASS_M_TBL(klass), clone_method,
(st_data_t)&data);
...
}

今回の例の場合、この、"attached" は、クラスのコピー元である String
クラスを指しています。singleton_method_added を呼び出す CALL_METHOD_HOOK
では、以下のように、レシーバを特異クラスの "attached" によって決定す
るので、メソッドテーブルをコピーするときに呼び出す
singleton_method_added のレシーバが、String になっています。

#define CALL_METHOD_HOOK(klass, hook, mid) do { \
const VALUE arg = ID2SYM(mid); \
VALUE recv_class = (klass); \
ID hook_id = (hook); \
if (FL_TEST((klass), FL_SINGLETON)) { \
recv_class = rb_ivar_get((klass), attached);\
hook_id = singleton_##hook; \
} \
rb_funcall2(recv_class, hook_id, 1, &arg); \
} while (0)

このようになるのが仕様として定まっているのかどうかが分からないのですが、
singleton_method_added のレシーバをクローン先のクラスに変更するようにし
たパッチを作成したので、本メールの末尾に張っておきます。
rb_obj_clone での
RBASIC(clone)->klass = rb_singleton_class_clone_and_attach(obj, clone);
の位置をずらしていいのかどうかが不安なんですが、参考にしていただければ幸
いです。

以上、よろしくお願いいたします。

  • パッチ

Index: object.c
===================================================================
--- object.c (revision 33202)
+++ object.c (working copy)
@@ -268,6 +268,7 @@

  • the class. */

+VALUE rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach);
VALUE
rb_obj_clone(VALUE obj)
{
@@ -277,9 +278,9 @@
rb_raise(rb_eTypeError, "can't clone %s", rb_obj_classname(obj));
}
clone = rb_obj_alloc(rb_obj_class(obj));

  • RBASIC(clone)->klass = rb_singleton_class_clone(obj); RBASIC(clone)->flags = (RBASIC(obj)->flags | FL_TEST(clone, FL_TAINT) | FL_TEST(clone, FL_UNTRUSTED)) & ~(FL_FREEZE|FL_FINALIZE|FL_MARK); init_copy(clone, obj);
  • RBASIC(clone)->klass = rb_singleton_class_clone_and_attach(obj, clone); rb_funcall(clone, id_init_clone, 1, obj); RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE;

Index: class.c
===================================================================
--- class.c (revision 33202)
+++ class.c (working copy)
@@ -219,9 +219,17 @@
return rb_mod_init_copy(clone, orig);
}

+VALUE rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach);
+
VALUE
rb_singleton_class_clone(VALUE obj)
{

  • return rb_singleton_class_clone_and_attach(obj, Qundef);
    +}
    +
    +VALUE
    +rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach)
    +{
    VALUE klass = RBASIC(obj)->klass;

    if (!FL_TEST(klass, FL_SINGLETON))
    @@ -246,6 +254,10 @@
    RCLASS_CONST_TBL(clone) = st_init_numtable();
    st_foreach(RCLASS_CONST_TBL(klass), clone_const_i, (st_data_t)RCLASS_CONST_TBL(clone));
    }

  •   if (attach != Qundef) {
    
  •       rb_singleton_class_attached(clone, attach);
    
  •   }
     RCLASS_M_TBL(clone) = st_init_numtable();
     data.tbl = RCLASS_M_TBL(clone);
     data.klass = (VALUE)clone;
    

    =end

Associated revisions

Revision ad6f06ae
Added by nobu (Nobuyoshi Nakada) over 6 years ago

object.c: singleton class clone

  • object.c (rb_obj_clone): attach clone to its singleton class during cloning singleton class so that singleton_method_added will be called on it. based on the patch by shiba (satoshi shiba)[Bug #5283] in [ruby-dev:44477]. [Bug #5283]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38648 b2dd03c8-39d4-4d8f-98ff-823fe69b080e

Revision 38648
Added by nobu (Nobuyoshi Nakada) over 6 years ago

object.c: singleton class clone

  • object.c (rb_obj_clone): attach clone to its singleton class during cloning singleton class so that singleton_method_added will be called on it. based on the patch by shiba (satoshi shiba)[Bug #5283] in [ruby-dev:44477]. [Bug #5283]

Revision 38648
Added by nobu (Nobuyoshi Nakada) over 6 years ago

object.c: singleton class clone

  • object.c (rb_obj_clone): attach clone to its singleton class during cloning singleton class so that singleton_method_added will be called on it. based on the patch by shiba (satoshi shiba)[Bug #5283] in [ruby-dev:44477]. [Bug #5283]

Revision 38648
Added by nobu (Nobuyoshi Nakada) over 6 years ago

object.c: singleton class clone

  • object.c (rb_obj_clone): attach clone to its singleton class during cloning singleton class so that singleton_method_added will be called on it. based on the patch by shiba (satoshi shiba)[Bug #5283] in [ruby-dev:44477]. [Bug #5283]

Revision 38648
Added by nobu (Nobuyoshi Nakada) over 6 years ago

object.c: singleton class clone

  • object.c (rb_obj_clone): attach clone to its singleton class during cloning singleton class so that singleton_method_added will be called on it. based on the patch by shiba (satoshi shiba)[Bug #5283] in [ruby-dev:44477]. [Bug #5283]

Revision 38648
Added by nobu (Nobuyoshi Nakada) over 6 years ago

object.c: singleton class clone

  • object.c (rb_obj_clone): attach clone to its singleton class during cloning singleton class so that singleton_method_added will be called on it. based on the patch by shiba (satoshi shiba)[Bug #5283] in [ruby-dev:44477]. [Bug #5283]

Revision 38648
Added by nobu (Nobuyoshi Nakada) over 6 years ago

object.c: singleton class clone

  • object.c (rb_obj_clone): attach clone to its singleton class during cloning singleton class so that singleton_method_added will be called on it. based on the patch by shiba (satoshi shiba)[Bug #5283] in [ruby-dev:44477]. [Bug #5283]

History

Updated by ko1 (Koichi Sasada) over 7 years ago

  • Status changed from Open to Assigned
  • Assignee set to nobu (Nobuyoshi Nakada)

Updated by nobu (Nobuyoshi Nakada) over 6 years ago

  • Description updated (diff)
#3

Updated by nobu (Nobuyoshi Nakada) over 6 years ago

  • Status changed from Assigned to Closed
  • % Done changed from 0 to 100

This issue was solved with changeset r38648.
satoshi, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


object.c: singleton class clone

  • object.c (rb_obj_clone): attach clone to its singleton class during cloning singleton class so that singleton_method_added will be called on it. based on the patch by shiba (satoshi shiba)[Bug #5283] in [ruby-dev:44477]. [Bug #5283]

Also available in: Atom PDF