読者です 読者をやめる 読者になる 読者になる

YARVの気持ちに触れる方法

ふとYARVの気持にふれてみようと思ったんですが、どうやるんだったかよく忘れてしまうのでメモ。

yoshimi% ruby -e 'puts RubyVM::InstructionSequence.compile("(0..100).each {|i| }").disasm'
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
== catch table
| catch type: break  st: 0002 ed: 0012 sp: 0000 cont: 0012
|------------------------------------------------------------------------
0000 trace            1                                               (   1)
0002 trace            1
0004 putobject        0..100
0006 send             :each, 0, block in <compiled>, 0, <ic>
0012 leave
== disasm: <RubyVM::InstructionSequence:block in <compiled>@<compiled>>=
== catch table
| catch type: redo   st: 0000 ed: 0001 sp: 0000 cont: 0000
| catch type: next   st: 0000 ed: 0001 sp: 0000 cont: 0001
|------------------------------------------------------------------------
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s3)
[ 2] i<Arg>
0000 putnil                                                           (   1)
0001 leave
yoshimi% ruby -e 'puts RubyVM::InstructionSequence.compile("for i in (0..100) do; end").disasm'                                                                          
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
== catch table
| catch type: break  st: 0002 ed: 0012 sp: 0000 cont: 0012
|------------------------------------------------------------------------
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 2] i          
0000 trace            1                                               (   1)
0002 trace            1
0004 putobject        0..100
0006 send             :each, 0, block in <compiled>, 0, <ic>
0012 leave            
== disasm: <RubyVM::InstructionSequence:block in <compiled>@<compiled>>=
== catch table
| catch type: redo   st: 0005 ed: 0006 sp: 0000 cont: 0005
| catch type: next   st: 0005 ed: 0006 sp: 0000 cont: 0006
|------------------------------------------------------------------------
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s3)
[ 2] ?<Arg>     
0000 getdynamic       *, 0                                            (   1)
0003 setlocal         i
0005 putnil           
0006 leave 

VMがRubyVMに去年の6月ごろに変わっていたらしい。

追記:

twitter経由で中田さんに--dump=insnsオプションを教えてもらった。

yoshimi% ruby --dump=insns -e 'for i in 1..10 do end'                                                                                                                    [ ~/project/ruby/todoit master ]
== disasm: <RubyVM::InstructionSequence:<main>@-e>======================
== catch table
| catch type: break  st: 0002 ed: 0010 sp: 0000 cont: 0010
|------------------------------------------------------------------------
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 2] i          
0000 trace            1                                               (   1)
0002 putobject        1..10
0004 send             :each, 0, block in <main>, 0, <ic>
0010 leave            
== disasm: <RubyVM::InstructionSequence:block in <main>@-e>=============
== catch table
| catch type: redo   st: 0006 ed: 0007 sp: 0000 cont: 0006
| catch type: next   st: 0006 ed: 0007 sp: 0000 cont: 0007
|------------------------------------------------------------------------
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s3)
[ 2] ?<Arg>     
0000 getdynamic       *, 0                                            (   1)
0003 setdynamic       i, 1
0006 putnil           
0007 leave 

ちなみにinsnsって覚えられなさそうだなぁと思って何度か打ち間違えましたが、insとか短くてもマッチすればOKっぽい。(ただし将来的にその挙動が保証されるかは怪しい気もする)

1.9.1のmanを見ても載ってないということは開発者向けの隠しオプションという扱いなのかな。trunkを見てみると、dumpオプションは載っていて説明はDO NOT USEと書いてあるけど、ケースによってはこの出力は役に立つこともありそうだなぁと思ったりはする。(確かにそこまで必要なことはほとんどないけど)

他にもdumpに指定できるやつで便利そうなのはどんなのがあるんだろうか。