ruby-trunk-changes r29599 - r29612

今日は private 定数という機能の追加という ruby の利用者にとっても関心がありそうな変更があります。また T_CLASS のオブジェクト (struct RClass型、正確にはそこからポインタで保持されている rb_classext_t)にメンバが追加されて内部表現に変化がありました。ruby の実装に興味がある人も要注目です。

[追記]Matz が [ruby-dev:42469] で private constant の導入について「ちょっと待って」と言っているので、一旦差し戻されるかもしれません。そうですかー。[/追記]

naruse:r29599 2010-10-26 21:39:33 +0900

Scanf#extract_float で "2.e+2" のように小数点のあとの小数部の数値が省略された形式をうけつけるように変更されています。[Bug #3978]

mame:r29600 2010-10-27 02:27:21 +0900

mame さんの久しぶりのコミットです。燃え尽きてなくて良かった良かった。
ここから r29602 までは、[ruby-dev:39685][ruby-core:32698] で提案されている、private constant の導入のための一連の修正になっています。
r29600 では、まず T_CLASS (Class や Module の MRI での内部表現の種類)のメンバに const_tbl を追加して、これまで iv_tbl にインスタンス変数と一緒に格納されていた定数を別のテーブルで管理するようにしています。

svn:r29601 2010-10-27 02:27:24 +0900

version.h の日付変更。

mame:r29602 2010-10-27 02:27:32 +0900

const_tbl のエントリが定数の内容を指す VALUE を直接持っていたものを、rb_const_entry_t 型を定義してそれを格納するようにしています。
rb_const_entry_t は新たに追加されたヘッダファイル constant.h で以下のように定義されています。

typedef enum {
    CONST_PUBLIC    = 0x00,
    CONST_PRIVATE   = 0x01,
} rb_const_flag_t;

typedef struct rb_const_entry_struct {
    rb_const_flag_t flag;
    VALUE value;            /* should be mark */
} rb_const_entry_t;

この段階で T_CLASS 型のオブジェクトが持つ定数のテーブルには可視性のフラグを持てるようになっています。しかしまだこれだけでは可視性は public に初期化されて変更できないようになっています。

mame:r29603 2010-10-27 02:27:44 +0900

Module#public_constant, Module#private_constant メソッドを追加して、定数の可視性を変更できるようにしています。また、定数を参照しようとした時に、それが private に設定されていたら NameError 例外を発生するようにしています。test_module.rb に追加されたテストをみると一目瞭然ですが、"::" を使って内部のクラスにアクセスするのが禁止されていて、"::" を使わずに直接定数名だけでアクセスするのは OK です。よって定数が定義されているクラスのスコープからでないとアクセスできないということになります。private メソッドが recv.meth の形式で呼ぶのを禁止することでレシーバのメソッド内からしか呼べなくするのと似たような制限のしかたですね。
内部的には rb_const_get_??? の関数経由で定数を取得しようとすると private 定数は例外になります。 "::" がない時の定数アクセスは insns.def や vm_insnhelper.c を見ると別に vm_get_ev_const() で定数探索するコードがあるので rb_const_get_??? は利用しないというわけです。

module M1
  class C
  end
  private_constant :C

  module_function
  def create_C
    C.new
  end
end

M1.create_C.class # => M1::C

M1::C.new # => private constant M1::C referenced (NameError)

ところで public_constant/private_constant で発生する SecurityError 例外のメッセージにちょっとした typo をみつけたので報告しておきました。
また [Feature #3908] にコメントが追加されているように、const_get でも例外になる(private メソッドが send を使えば呼べるのとは対照的)、Module#constants で private 定数も含まれる(Object#methods が private メソッドを含まないのと対照的)、set_const で定数を上書きすると可視性が public に戻る(これはいいような。定数の上書き自体警告が出る操作ですし)といった指摘があるので、もう少しメソッドの追加や修正があるかもしれません。

ライブラリ作者は今後 private constant を使って内部的なクラスやモジュールを非公開であることを明示的に宣言することができることになります。この仕様が受け入れられたら、他の処理系もこの仕様に追随することになるでしょうか?

naruse:r29604 2010-10-27 09:26:29 +0900

Continuation と Fiber のドキュメントが修正されています。[ruby-core:32915]

usa:r29605 2010-10-27 12:00:26 +0900

gcc の警告除去のために NUM2LONG_internal マクロに long へのキャストを追加しています。
[追記] gcc 以外のコンパイラでの警告除去、でした。not を見落し orz [/追記]

usa:r29606 2010-10-27 12:06:27 +0900

r29602 の clone_const で値の return をしていなかった不具合を修正しています。st_foreach へ渡すコールバック関数なので適切な値を返さないといけません(が、省略されると多分 0 を return したことになるので ST_CONTINUE とたまたま一致していたので動作はしていたというわけですね)。

usa:r29607 2010-10-27 13:53:34 +0900

r29541 の test_io.rb, test_io_m17n.rb で pipe の読み書きを別々の Thread でするようにした修正で、Thread 起動などをまとめた pipe メソッドで、各 Thread での例外を捕捉して親 Thread であらためて raise するようにしています。Tread#join したら再発生するから……と思ってたんですけど、よく考えたら ensure の前に join しているのでそこで例外が発生して、ensure 節に入ってまた join して……とよろしくない感じになっていました。反省。

usa:r29608 2010-10-27 14:56:01 +0900

r29607 で test_io_m17n.rb のほうの pipe メソッドは可変長の引数を受け付けるようになっていたのが消えていたので戻しています。

usa:r29609 2010-10-27 15:08:37 +0900

w32_error 関数で GetLastError() を2度呼んでいたのを、一度呼んで結果を変数に格納するようにしています。2度目の呼び出しでは意図したのとは違うものを取得してしまうからということのようです。errno の退避と同じようなものでしょう。

usa:r29610 2010-10-27 18:15:02 +0900

rb_w32_getppid で 64bit 版 Windows の対応だそうです。構造体のメンバの型を ULONG から uintptr_t という(多分移植性のある)型に変更しています。

usa:r29611 2010-10-27 18:28:08 +0900

test_io.rb, test_io_m17n.rb の pipe メソッドについて、まず書き込み側のパイプから close するようにしています。エラー時等に発生する例外がタイミングで変化してしまったりするからだと思います。うーむ、なかなか一筋縄ではいかないところでした。
[追記]じゃなくてエラー時に書き込み側を close しないと読み込み側が eof にならずブロックすることになるという問題でした。いやーまったくそのとうりです。ブロックを回避するために変更したつもりなのに、自分のうかつさにがっかりです。[/追記]

usa:r29612 2010-10-27 18:54:27 +0900

[Bug #3490]
rb_big2long, rb_big2ulong でキャストの変更されています。VALUE 型が long とバイト幅が違うような環境のことをケアしているものです。
コミットメッセージをそのまま読むと、rb2ulong は VALUE を返すけど、その実際の範囲は ulong なので、VALUE 型が ulong よりもバイト幅が大きい時は、負の数を返しているつもりでも上位ビットは常に0になるから long にキャストする、ということだそうです。ulong の範囲なのに負の数? とちょっと疑問ですが、要は Bignum が ulong におさまりきらない範囲の数だった時にオーバフローを検出するためのようです。難しいですね。

今日は読み応えのある修正がいっぱいありました。