[ruby-dev:50020] Re: メソッド呼び出し時の引数の値を VM 側で取得する方法について
From:
Tetsuo Handa <penguin-kernel@...>
Date:
2017-03-16 02:13:43 UTC
List:
ruby-dev #50020
> lib/rdoc/token_stream.rb は lib/rdoc/parser/c.rb から呼ばれているとのことなので、
> 以下のように add_token に渡す値を tk ではなく nil に差し替えてみたところ、最後の
> ファイルまで処理した後でエラーが出るようになりました。
>
> 下記の出力は上記の出力よりは先の段階ですので、 tk を渡したことで NULL pointer
> dereference になっているものと推測されます。下記の結果が出るまでは、パッチが
> 行っている処理の中で何かの手続きを忘れているものと考えていたのですが、下記の
> 結果からは、何らかの状態の引数を渡すとエラーになるようにも思えます。
> 何か手がかりになりそうでしょうか?
VM 側に修正を加えていない ruby で以下のプログラムを実行した場合に
can't convert nil into Integer (TypeError) というエラーが発生することを
確認できました。
---------- test.rb ----------
require '/usr/share/rubygems/rubygems/rdoc.rb'
tk = RDoc::RubyToken::Token.new nil, 1, 1
p tk
---------- test.rb ----------
---------- 実行結果 ----------
/usr/share/gems/gems/rdoc-4.0.0/lib/rdoc/ruby_token.rb:65:in `%': can't convert nil into Integer (TypeError)
from /usr/share/gems/gems/rdoc-4.0.0/lib/rdoc/ruby_token.rb:65:in `inspect'
from test.rb:3:in `p'
from test.rb:3:in `<main>'
---------- 実行結果 ----------
当該箇所は以下の部分で、 @seek の値が nil になっていることが原因でした。
---------- ruby_token.rb ----------
def inspect # :nodoc:
klass = self.class.name.split('::').last
"{%s %d, %d:%d %p}" % [klass, @seek, @line_no, @char_no, @text]
end
---------- ruby_token.rb ----------
以下のように nil の代わりに値を渡してやると、正常に実行できました。
---------- test.rb ----------
require '/usr/share/rubygems/rubygems/rdoc.rb'
tk = RDoc::RubyToken::Token.new 0, 1, 1
p tk
---------- test.rb ----------
---------- 実行結果 ----------
{Token 0, 1:1 nil}
---------- 実行結果 ----------
これは、 VM 側のバグなのでしょうか?それとも、 inspect 処理の中で想定外の値が
入っていると実行時エラーになるため、想定外の値が入っていないことを保証できない
オブジェクトに対して rb_inspect() を呼ぶのは危険であるということでしょうか?
とりあえず、以下のように修正をすることで、
./miniruby -I./lib -I. -I.ext/common ./tool/runruby.rb --extout=.ext -- --disable-gems "./bin/rdoc" --root "." --page-dir "./doc" --encoding=UTF-8 --all --ri --op ".ext/rdoc" "." --debug
というコマンドラインが最後のファイルまで処理した後でエラーが出るという段階まで
進めることを確認できました。
----------
diff --git a/lib/rdoc/parser/c.rb b/lib/rdoc/parser/c.rb
index 087d56f..d42bf0a 100644
--- a/lib/rdoc/parser/c.rb
+++ b/lib/rdoc/parser/c.rb
@@ -666,7 +666,7 @@ def find_body class_name, meth_name, meth_obj, file_content, quiet = false
#meth_obj.params = params
meth_obj.start_collecting_tokens
- tk = RDoc::RubyToken::Token.new nil, 1, 1
+ tk = RDoc::RubyToken::Token.new 0, 1, 1
tk.set_text body
meth_obj.add_token tk
meth_obj.comment = comment
@@ -685,7 +685,7 @@ def find_body class_name, meth_name, meth_obj, file_content, quiet = false
find_modifiers comment, meth_obj
meth_obj.start_collecting_tokens
- tk = RDoc::RubyToken::Token.new nil, 1, 1
+ tk = RDoc::RubyToken::Token.new 0, 1, 1
tk.set_text body
meth_obj.add_token tk
meth_obj.comment = comment
----------
----------
Parsing sources...
0% [ 1/895] /root/ruby/doc/ChangeLog-0.06_to_0.52
0% [ 2/895] /root/ruby/doc/ChangeLog-0.50_to_0.60
0% [ 3/895] /root/ruby/doc/ChangeLog-0.60_to_1.1
(中略)
99% [893/895] vm_method.c
99% [894/895] vm_trace.c
100% [895/895] vsnprintf.c
stack level too deep
/root/ruby/lib/rdoc/class_module.rb:700:in `superclass'
/root/ruby/lib/rdoc/normal_class.rb:13:in `ancestors'
/root/ruby/lib/rdoc/normal_class.rb:18:in `ancestors' (という行の 10915 回繰り返し)
/root/ruby/lib/rdoc/store.rb:307:in `block in complete'
/root/ruby/lib/rdoc/store.rb:307:in `each'
/root/ruby/lib/rdoc/store.rb:307:in `complete'
/root/ruby/lib/rdoc/rdoc.rb:492:in `document'
./bin/rdoc:20:in `<main>'
----------
キャパシティオーバーのように見えますが、何故 10915 回も再帰呼出しが
発生しているのかは不明です。
---------- normal_class.rb ----------
class RDoc::NormalClass < RDoc::ClassModule
##
# The ancestors of this class including modules. Unlike Module#ancestors,
# this class is not included in the result. The result will contain both
# RDoc::ClassModules and Strings.
def ancestors
if String === superclass then (←ここが13行目の呼出しです。)
super << superclass
elsif superclass then
ancestors = super
ancestors << superclass
ancestors.concat superclass.ancestors (←ここが18行目の呼出しです。)
else
super
end
end
---------- normal_class.rb ----------