From: nagachika00@... Date: 2017-01-16T19:21:36+00:00 Subject: [ruby-dev:49954] [Ruby trunk Bug#13004] rb_get_kwargs はバグってないでしょうか? Issue #13004 has been updated by Tomoyuki Chikanaga. Backport changed from 2.1: REQUIRED, 2.2: DONE, 2.3: REQUIRED to 2.1: REQUIRED, 2.2: DONE, 2.3: DONE ruby_2_3 r57346 merged revision(s) 56980,56981. ---------------------------------------- Bug #13004: rb_get_kwargs はバグってないでしょうか? https://bugs.ruby-lang.org/issues/13004#change-62504 * Author: Makoto Kishimoto * Status: Closed * Priority: Normal * Assignee: * Target version: * ruby -v: * Backport: 2.1: REQUIRED, 2.2: DONE, 2.3: DONE ---------------------------------------- きしもとです 拡張ライブラリで `rb_get_kwargs` を使おうとしたところ、いくつか変に 感じた点がありましたので、確認をお願いしたくこちらにメイルします。 もしバグでしたらチケットにまとめます。 (1) `values`を渡すと、全て変更(初期化)される。 extension(.ja).rdoc には、 > ``` > ... If an optional key is not present in > +keyword_hash+, the corresponding element in +values+ is not changed. > ``` > ``` > ...省略可能キーワー > ドがない場合は,values中の対応する要素は変更されません. > ``` とあるが、コードでは、 ```C 1866 if (values) { 1867 for (j = 0; j < required + optional; j++) { 1868 values[j] = Qundef; 1869 } 1870 } ``` となっていて、`values`が非`NULL`であれば一律に `Qundef` で初期化している。 (2) "unknown keyword" `ArgumentError` を `raise` しそこねることがある。 該当部分のコードはこうなっていて、 ```C 1898 if (!rest && keyword_hash) { 1899 if (RHASH_SIZE(keyword_hash) > (unsigned int)j) { 1900 unknown_keyword_error(keyword_hash, table, required+optional); 1901 } 1902 } ``` ここで `j` には認識されたキーワード引数の個数が入っていて、`values`が `NULL`の時には`keyword_hash`の中身が変更されず、恐らくその意図通りの 動作になるが、`values`が非`NULL`の場合は`keyword_hash`からエントリが 除かれるため、認識できなかった引数があってもこのチェックを通り 抜けてしまうことがある(`extract_kwarg`マクロも参照)。 ```C 1855 #define extract_kwarg(keyword, val) \ 1856 (key = (st_data_t)(keyword), values ? \ 1857 st_delete(rb_hash_tbl_raw(keyword_hash), &key, (val)) : \ 1858 st_lookup(rb_hash_tbl_raw(keyword_hash), key, (val))) ``` (3) 使用されない要素は`keyword_hash`に残されるだけで、別には保存されない。 extension(.ja).rdoc には、 > ``` > If +optional+ is negative, rest of +keyword_hash+ are stored in the > next to optional +values+ as a new Hash, ... > ``` > ``` > keyword_hashに使用されない要素がある場合は,optionalが負なら > 新しいHashとして省略可能引数の次に保存されますが,... > ``` とあるが、該当するコードが見つからない。またもし、そのような動作を するのであれば、ドキュメントのこの関数に関する記述の冒頭部に、 `values`のサイズは必ず「`required+optional + 1`」以上でなければ ならないと書く必要があるように思う。 -- https://bugs.ruby-lang.org/