ruby-trunk-changes r42445 - r42473

今日は Binding にローカル変数を操作するメソッドが追加されたり、Module#singleton_method? が追加されたり、ブロックに lambda が渡されて yield で呼ばれた時の挙動の変更があったりと、新機能追加/仕様変更が多くありました。また Enumerator::Lazy#zip の不具合修正もありました。

akr:r42445 2013-08-08 21:13:04 +0900

time.c の時刻生成時にオーバーフローをチェックする time_overflow_p() で中間変数を削除して不要な代入によるオーバフローを回避しています。

akr:r42446 2013-08-08 21:34:30 +0900

r42445 で nsec が負の数の時に範囲チェックに使う定数のミスを修正。

akr:r42447 2013-08-08 21:59:21 +0900

r42445 および r42446 の続きで nsec のオーバフロー分を削って 1sec 以下にするのに余剰を使っていたのをあふれたぶんを加減算するように変更しています。余剰のでも良さそうな気はしますけどなぜでしょうか。 また rb_time_new() でアンダーフローのチェック条件が間違っていたのも修正しています。

kazu:r42448 2013-08-08 22:09:24 +0900

r42396 と r42397 の ChangeLog エントリの typo 修正。

nobu:r42449 2013-08-08 23:01:23 +0900

Module#singleton_class? というメソッドが追加されています。レシーバが特異クラスの時に true を返すというものだそうで、どう使うのかなぁと思ってみたところプロファイラなどでイベントに渡されたクラスをチェックするなどの用途を想定しているっぽいですね。 [ruby-core:51087] [Feature #7609]

nobu:r42450 2013-08-09 00:10:37 +0900

Enumerator::Lazy#zip が yield に複数の値が渡される Enumerator を lazy 化したものだった時にブロックに渡す引数から2つ目以降の値が抜け落ちてしまっていた不具合を修正しています。 [ruby-core:56383] [Bug #8735]

svn:r42451 2013-08-09 00:10:43 +0900

version.h の日付更新。

nobu:r42452 2013-08-09 00:15:44 +0900

make install-arch で と ext-arch のみでなくライブラリなどアーキテクチャ固有のファイルを全てインストールするほうに rbinstall.rb の引数を修正しています。

naruse:r42453 2013-08-09 01:27:08 +0900

r42439 で coverage のテストの Windows 環境でのパフォーマンス改善のために、スクリプトの "p" を "1" とリテラルだけ書くように変更したところ、-w つきだと警告が大量に出てしまうので __id__ メソッドの呼び出しに変更しています。 [ruby-dev:47040]

ktsj:r42455 2013-08-09 10:49:38 +0900

lambda 型の Proc オブジェクトをブロックとしてメソッドに渡した時に、その lambda 内の return の扱いについて仕様変更しています。 [ruby-core:56193] [Feature #8693]
込み入った話なので順に書きます。このチケットの議論を追ってなかったので過去の知見からの推測が入ってるので間違ってるかもしれません。ご指摘お願いします。
まず Proc オブジェクトには lambda{} や ->{} で作成した時には lambda フラグというのが立って、Proc.new で作成した Proc オブジェクトや通常のブロックをブロック引数として受け取った時のものとは少し挙動が変化します。

  1. ブロックパラメータの arity チェックがある(引数の数が一致しないと ArgumentError が発生したりする)
  2. return 文が break 相当になる(通常は return はそのメソッドを抜けるのですが、lambda のブロックを抜けるようになる)

2つ目の仕様は lambda{} をいわゆる無名関数っぽく書けるようにするために追加(変更)された仕様だったのだと思います。
ところがこの return の挙動は、lambda{} や ->{} で生成した Proc オブジェクトをさらに m(&b) のように & つきの記法でブロックとしてメソッドに渡し、さらに渡されたブロックが yield で起動された時にだけ無効になるという微妙な挙動を示していました。

def m1_yield
  p(yield)
end
def m2
  m1_yield(&lambda{ return :block })
  :m2
end

m2
# => :block

m1_yield がブロックを yield で呼び出しているので、渡しているのが lambda による Proc オブジェクトなのにかかわらずその中の return が lambda から抜けるのではなくて、 m2 メソッドから抜けています。
なおブロックの呼び出しを yield ではなくブロック引数として受け取って Proc#call メソッドで呼ぶと lambda としての動作が保存されます。

def m1_call(&b)
  p(b.call)
end
def m2
  m1_call(&lambda{ return :block })
  :m2
end

m2
:block  # <- m1_call 内の p(b.call) が実行されるので :block が表示される
# => :m2

しかしこれだとメソッド内でどのようにブロックを起動しているかに依存して渡す lambda の挙動が変化するという使いずらい仕様だったので、yield で起動した時もブロックが lambda 由来の Proc オブジェクトだった場合は return が lambda から抜ける(つまりブロックから帰ってくる)という挙動に揃えられています。 [ruby-core:56193] [Feature #8693]
ついでにこの変更で lambda の1つ目の特徴である引数の arity チェックの厳格化も引き継ぐようになったみたいです。つまり上の例で m1_yield で yield 1 で呼び出してしまうとか、m2 で渡す lambda のブロックパラメータが1以上だったりする ArgumentError が発生します。こっちはこっちで微妙なところですねー…。
[追記]ArgumentError については勘違いで、この変更の前から元々 ArgumentError が発生するようになっていたようです。なので今回変更されたのは lambda の 2. の特徴についてのみみたいです。[/追記]

ktsj:r42456 2013-08-09 10:56:51 +0900

time.c の get_timeval() および get_new_timeval() で TypeError を発生させる時に引数のオブジェクトのクラスを取得するのに CLASS_OF() マクロで取得していたのを rb_obj_class() を利用するように変更しています。 CLASS_OF() はより内部的に使われるもので特異クラスがあるとそれを返してしまうので、Object#class の挙動(特異クラスや IClass はスキップする)にあわせています。

nobu:r42457 2013-08-09 12:06:53 +0900

r42430 で file.c に切り出した UFS+ のファイルパスのエンコーディング変換処理 rb_str_normalize_ospath() を Mac OS X では CoreFoundation を利用して CFStringNormalize() を利用するようにしています。これはずいぶん思いきった変更ですね。しかし現在 Mac OS X Lion 上で miniruby のリンク時に -framework 不足のせいかビルドに失敗しているので再修正が必要そうです。

nobu:r42458 2013-08-09 13:42:44 +0900

configure.in で clock_gettime(2) の存在チェックを librt から探した結果を利用するように修正しています。

hsbt:r42459 2013-08-09 14:05:15 +0900

tool/make-snapshot で結果のアーカイブファイル名を -archname オプションの指定が優先的に利用されていなかったのを修正しています。

ko1:r42464 2013-08-09 18:51:00 +0900

Binding オブジェクトに local_variable_get, local_variable_set, local_variable_defined? というメソッドを新設して Binding オブジェクトが作られたスコープでのローカル変数を操作できるようにしています。
実装的にはローカル変数はパース時に決定されて配列が確保されるので、get や defined? はともかく set が定義されているのがちょっと不思議だったのですが、追加時にはメソッドのスコープのトップレベルのローカル変数のところではなくて、binding 内に env を追加してブロック変数に相当するところに追加しているようです。
従って以下のようにしてもメソッド内のローカル変数が本当に後から増えるわけではないので eval で遅延しても追加したローカル変数はメソッド内からは見えません。

def m1
  [lambda{ eval("a") }, binding]
end

l, b = m1
l.call # => NameError: undefined local variable or method `a' for main:Object
b.local_variable_set(:a, 72)
l.call # => NameError: undefined local variable or method `a' for main:Object

しかし Binding オブジェクトは追加した変数も持っているので、Binding#eval や eval の第2引数に渡した時はこの追加した変数も見えます

b.eval("a")  # => 72
eval("a", b) # => 72

なるほどー。vm.c に追加した rb_binding_add_dynavars() で vm_pop_frame() しているのは vm_set_eval_stack() で push したぶんが不要なのですぐに排除しているってことでしょうか。おもしろいですね。

ko1:r42465 2013-08-09 19:14:30 +0900

r42464 で追加した Binding の local_variable_set, local_variable_get, local_variable_defined? メソッドについて NEWS ファイルに追記しています。 ただ local_variable_set の引数の数が間違っていますね。

charliesome:r42466 2013-08-09 19:18:56 +0900

拡張ライブラリ io/console で rb_cloexec_open() が定義されていますが、これは本体の io.c で定義されているからということで削除しています。しかし、io/console は gem パッケージとして独立して配布もしている拡張ライブラリなので、古い Ruby 本体との組み合わせも考慮しないといけないためにこの定義が #ifndef HAVE_RB_CLOEXEC_OPEN での条件つきで定義されているものだと思いますので、消すのはまずいと思います。

nobu:r42467 2013-08-09 22:20:55 +0900

r42466 の ChangeLog エントリを io/console の 1.8, 1.9 サポートを打ち切るから消すよ、と書きかえています。 revert すると思ったのにこの仕打ちw

nobu:r42468 2013-08-09 22:20:58 +0900

require や load でスクリプトファイルを検索する時のファイルパスの操作を修正して __FILE__ の返すファイルパスのエンコーディングファイルシステムエンコーディングになっていなかった不具合を修正しています。 [Bug #8753]

nobu:r42469 2013-08-09 22:24:37 +0900

proc.c のインデントの修正のみ。

nobu:r42470 2013-08-09 22:41:23 +0900

bignum.c の条件式にかっこをつけてコンパイラの警告を除去。

nobu:r42471 2013-08-09 22:41:26 +0900

re.c で if の条件部で代入をしていたためコンパイラの警告が出ていたのを代入を前に出して修正しています。

nobu:r42472 2013-08-09 22:51:13 +0900

r42457 の変更で Mac OS X でファイルパスのエンコーディング正規化のために CoreFoundation が必要になったので、configure.in で --enable-shared が有効の時だけオプションに追加していた -framework CoreFoundation を常に追加するように修正しています。 [ruby-core:56467] [Bug #8759]

nobu:r42473 2013-08-09 22:56:04 +0900

r42466, r42467 に続いて io/console の gemspec ファイルからサポートする ruby のバージョンを 2.0.0 以降に指定するようにしています。えー本当に 1.9 を切ってしまうんですかね。