前々回 [http://www.koooge.com/entry/2016/12/22/013733]、 前回 [http://www.koooge.com/entry/2016/12/26/005958]でlen[0]、len[1]が明らかになったので、 select_bang_ensureをみていきます。
修正前コード static VALUE select_bang_ensure(VALUE a) { volatile struct select_bang_arg *arg = (void *)a; VALUE ary = arg->ary; long len = RARRAY_LEN(ary); long i1 = arg->len[0], i2 = arg->len[1];
if (i2 < i1) {
if (i1 < len) {
RARRAY_PTR_USE(ary, ptr, {
MEMMOVE(ptr + i2, ptr + i1, VALUE, len - i1);
});
}
ARY_SET_LEN(ary, len - i1 + i2);
}
return ary;
}
さてlen, i1, i2について考えます。i1 = arg->len[0]は元々のArrayの長さで、i2 = arg->len[1] がselect!後のArrayの長さとわかっています。 最後にlenですが、引数として受け取ったarg->aryの長さとなります。しかしこれ、呼び出し元のrb_ary_select_bang を軽く読んでみても何が入るか良くわからず。。
#13503のケースを考える arg->aryに何が渡されているかパッとはわからないので、 立ち返って[https://bugs.ruby-lang.org/issues/13053]のスニペットを考えます。
ary = [1,2,3,4,5] ary.select! { |i| ary.clear if i==5; false } p ary #=> [] p ary.size #=> -5
今までの結果から、i1=5、 i2=0。これに、ARRAY_SET_LEN(ary, len - i1 + i2) #=> -5 なので、lenは0です。多分どこかでselect_bang_ensureに渡されるargs->lenの値が狂っていそうです。
修正後コード ここで#13053の修正方法を見てみます。
static VALUE select_bang_ensure(VALUE a) { volatile struct select_bang_arg *arg = (void *)a; VALUE ary = arg->ary; long len = RARRAY_LEN(ary); long i1 = arg->len[0], i2 = arg->len[1];
if (i2 < len && i2 < i1) {
long tail = 0;
if (i1 < len) {
tail = len - i1;
RARRAY_PTR_USE(ary, ptr, {
MEMMOVE(ptr + i2, ptr + i1, VALUE, tail);
});
}
ARY_SET_LEN(ary, i2 + tail);
}
return ary;
}
このコードだとlen=0のときif (i2 < len && i2 < i1) { で偽となるので、p ary.size #=> 0となりそうですね。
チケットのコメントを見ると下記の記載があります。
Shrinking the Array from the block invoked by Array#select! or Array#reject! causes the Array to be a negative number size. Ensure that the resulting Array won’t be smaller than 0.
超訳:“lenを0未満にはしないようにした”
なんだか本対処じゃなくってフェールセーフ処理っぽいですね。 つまりargs->lenが0となっている根本原因は他にありそうです。
まとめ
- rubyのissue #13053と、共にArray#select!の処理を追ってきた * rb_ary_select_bangがArray#select!の入口で、そこからselect_bang_iとselect_bang_ensure
を呼ぶ
- select_bang_iは、select!後のArrayを生成
- select_bang_ensureは、select!後のArray.lengthを設定(#13053はここにフェールセーフ処理を追加した)
今後 rubyのissueの雰囲気はつかめたので、 args->aryに0が渡されている元を追ってみたいと思います。
スポンサードリンク [asin:4873113679:detail]