Ruby の Virtual Machine のコンパイル済みの命令シーケンスを表すクラスです。
Method、Proc オブジェクトや Ruby のソースコードを表す文字列から VM の命令シーケンスを得る事ができます。また、 RubyVM::InstructionSequence オブジェクトを元に命令シーケンスを読みやすい文字列に変換する事もできます。Ruby の命令シーケンスコンパイラの設定を扱う必要がありますが、Ruby の VM がどのように働くかを知るのに有用です。
VM の命令シーケンスの一覧はRuby のソースコード中の insns.def から参照できます。
compile(source, file = nil, path = nil, line = 1, options = nil) -> RubyVM::InstructionSequence
[permalink][rdoc]new(source, file = nil, path = nil, line = 1, options = nil) -> RubyVM::InstructionSequence
引数 source で指定した Ruby のソースコードを元にコンパイル済みの RubyVM::InstructionSequence オブジェクトを作成して返します。
RubyVM::InstructionSequence.compile("a = 1 + 2") # => <RubyVM::InstructionSequence:<compiled>@<compiled>>
[SEE_ALSO] RubyVM::InstructionSequence.compile_file
compile_file(file, options = nil) -> RubyVM::InstructionSequence
[permalink][rdoc]引数 file で指定した Ruby のソースコードを元にコンパイル済みの RubyVM::InstructionSequence オブジェクトを作成して返します。
RubyVM::InstructionSequence.compile とは異なり、file、path などのメタデータは自動的に取得します。
# /tmp/hello.rb puts "Hello, world!" # irb RubyVM::InstructionSequence.compile_file("/tmp/hello.rb") # => <RubyVM::InstructionSequence:<main>@/tmp/hello.rb>
[SEE_ALSO] RubyVM::InstructionSequence.compile
compile_option -> Hash
[permalink][rdoc]命令シーケンスのコンパイル時のデフォルトの最適化オプションを Hash で返します。
require "pp"
pp RubyVM::InstructionSequence.compile_option
# => {:inline_const_cache=>true,
# :peephole_optimization=>true,
# :tailcall_optimization=>false,
# :specialized_instruction=>true,
# :operands_unification=>true,
# :instructions_unification=>false,
# :stack_caching=>false,
# :trace_instruction=>true,
# :frozen_string_literal=>false,
# :debug_frozen_string_literal=>false,
# :coverage_enabled=>true,
# :debug_level=>0}
[SEE_ALSO] RubyVM::InstructionSequence.compile_option=
compile_option=(options)
[permalink][rdoc]命令シーケンスのコンパイル時のデフォルトの最適化オプションを引数 options で指定します。
* :inline_const_cache * :instructions_unification * :operands_unification * :peephole_optimization * :specialized_instruction * :stack_caching * :tailcall_optimization * :trace_instruction:debug_level をキーに指定した場合は値を数値で指定します。
.new、.compile、.compile_file メソッドの実行の際に option 引数を指定した場合はその実行のみ最適化オプションを変更する事もできます。
[SEE_ALSO] RubyVM::InstructionSequence.new, RubyVM::InstructionSequence.compile, RubyVM::InstructionSequence.compile_file
disasm(body) -> String
[permalink][rdoc]disassemble(body) -> String
引数 body で指定したオブジェクトから作成した RubyVM::InstructionSequence オブジェクトを人間が読める形式の文字列に変換して返します。
例1:Proc オブジェクトを指定した場合
# /tmp/proc.rb p = proc { num = 1 + 2 } puts RubyVM::InstructionSequence.disasm(p)
出力:
== disasm: <RubyVM::InstructionSequence:block in <main>@/tmp/proc.rb>=== == catch table | catch type: redo st: 0000 ed: 0012 sp: 0000 cont: 0000 | catch type: next st: 0000 ed: 0012 sp: 0000 cont: 0012 |------------------------------------------------------------------------ local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1) [ 2] num 0000 trace 1 ( 1) 0002 putobject 1 0004 putobject 2 0006 opt_plus <ic:1> 0008 dup 0009 setlocal num, 0 0012 leave
例2:Method オブジェクトを指定した場合
# /tmp/method.rb def hello puts "hello, world" end puts RubyVM::InstructionSequence.disasm(method(:hello))
出力:
== disasm: <RubyVM::InstructionSequence:hello@/tmp/method.rb>============ 0000 trace 8 ( 1) 0002 trace 1 ( 2) 0004 putself 0005 putstring "hello, world" 0007 send :puts, 1, nil, 8, <ic:0> 0013 trace 16 ( 3) 0015 leave ( 2)
[SEE_ALSO] RubyVM::InstructionSequence#disasm
load_from_binary(binary) -> RubyVM::InstructionSequence
[permalink][rdoc]RubyVM::InstructionSequence#to_binaryにより作られたバイナリフォーマットの文字列からiseqのオブジェクトをロードします。
このローダーは検証機構をもっておらず、壊れたり改変されたバイナリを読み込むと深刻な問題を引き起こします。
他者により提供されたバイナリデータはロードすべきではありません。自分が変換したバイナリデータを使うべきです。
iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
binary = iseq.to_binary
RubyVM::InstructionSequence.load_from_binary(binary).eval # => 3
[SEE_ALSO] RubyVM::InstructionSequence#to_binary
load_from_binary_extra_data(binary) -> String
[permalink][rdoc]バイナリフォーマットの文字列から埋め込まれたextra_dataを取り出します。
iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
binary = iseq.to_binary("extra_data")
RubyVM::InstructionSequence.load_from_binary_extra_data(binary) # => extra_data
[SEE_ALSO] RubyVM::InstructionSequence#to_binary
of(body) -> RubyVM::InstructionSequence
[permalink][rdoc]引数 body で指定した Proc、Method オブジェクトを元に RubyVM::InstructionSequence オブジェクトを作成して返します。
例1:irb で実行した場合
# proc > p = proc { num = 1 + 2 } > RubyVM::InstructionSequence.of(p) > # => <RubyVM::InstructionSequence:block in irb_binding@(irb)> # method > def foo(bar); puts bar; end > RubyVM::InstructionSequence.of(method(:foo)) > # => <RubyVM::InstructionSequence:foo@(irb)>
例2: RubyVM::InstructionSequence.compile_file を使用した場合
# /tmp/iseq_of.rb def hello puts "hello, world" end $a_global_proc = proc { str = 'a' + 'b' } # irb > require '/tmp/iseq_of.rb' # hello メソッド > RubyVM::InstructionSequence.of(method(:hello)) > # => #<RubyVM::InstructionSequence:0x007fb73d7cb1d0> # グローバル proc > RubyVM::InstructionSequence.of($a_global_proc) > # => #<RubyVM::InstructionSequence:0x007fb73d7caf78>
absolute_path -> String | nil
[permalink][rdoc]self が表す命令シーケンスの絶対パスを返します。
self を文字列から作成していた場合は nil を返します。
例1:irb で実行した場合
iseq = RubyVM::InstructionSequence.compile('num = 1 + 2') # => <RubyVM::InstructionSequence:<compiled>@<compiled>> iseq.absolute_path # => nil
例2: RubyVM::InstructionSequence.compile_file を使用した場合
# /tmp/method.rb def hello puts "hello, world" end # irb > iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb') > iseq.absolute_path # => "/tmp/method.rb"
[SEE_ALSO] RubyVM::InstructionSequence#path
base_label -> String
[permalink][rdoc]self が表す命令シーケンスの基本ラベルを返します。
例1:irb で実行した場合
iseq = RubyVM::InstructionSequence.compile('num = 1 + 2') # => <RubyVM::InstructionSequence:<compiled>@<compiled>> iseq.base_label # => "<compiled>"
例2: RubyVM::InstructionSequence.compile_file を使用した場合
# /tmp/method.rb def hello puts "hello, world" end # irb > iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb') > iseq.base_label # => "<main>"
例3:
# /tmp/method2.rb def hello puts "hello, world" end RubyVM::InstructionSequence.of(method(:hello)).base_label # => "hello"
[SEE_ALSO] RubyVM::InstructionSequence#label
disasm -> String
[permalink][rdoc]disassemble -> String
self が表す命令シーケンスを人間が読める形式の文字列に変換して返します。
puts RubyVM::InstructionSequence.compile('1 + 2').disasm
出力:
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>========== 0000 trace 1 ( 1) 0002 putobject 1 0004 putobject 2 0006 opt_plus <ic:1> 0008 leave
[SEE_ALSO] RubyVM::InstructionSequence.disasm
eval -> object
[permalink][rdoc]self の命令シーケンスを評価してその結果を返します。
RubyVM::InstructionSequence.compile("1 + 2").eval # => 3
first_lineno -> Integer
[permalink][rdoc]self が表す命令シーケンスの 1 行目の行番号を返します。
例1:irb で実行した場合
RubyVM::InstructionSequence.compile('num = 1 + 2').first_lineno # => 1
例2:
# /tmp/method.rb require "foo-library" def foo p :foo end RubyVM::InstructionSequence.of(method(:foo)).first_lineno # => 2
inspect -> String
[permalink][rdoc]self の情報をラベルとパスを含んだ人間に読みやすい文字列にして返します。
iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
iseq.inspect # => "<RubyVM::InstructionSequence:<compiled>@<compiled>>"
[SEE_ALSO] RubyVM::InstructionSequence#label, RubyVM::InstructionSequence#path
label -> String
[permalink][rdoc]self が表す命令シーケンスのラベルを返します。通常、メソッド名、クラス名、モジュール名などで構成されます。
トップレベルでは "<main>" を返します。self を文字列から作成していた場合は "<compiled>" を返します。
例1:irb で実行した場合
iseq = RubyVM::InstructionSequence.compile('num = 1 + 2') # => <RubyVM::InstructionSequence:<compiled>@<compiled>> iseq.label # => "<compiled>"
例2: RubyVM::InstructionSequence.compile_file を使用した場合
# /tmp/method.rb def hello puts "hello, world" end # irb > iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb') > iseq.label # => "<main>"
例3:
# /tmp/method2.rb def hello puts "hello, world" end RubyVM::InstructionSequence.of(method(:hello)).label # => "hello"
[SEE_ALSO] RubyVM::InstructionSequence#base_label
path -> String
[permalink][rdoc]self が表す命令シーケンスの相対パスを返します。
self の作成時に指定した文字列を返します。self を文字列から作成していた場合は "<compiled>" を返します。
例1:irb で実行した場合
iseq = RubyVM::InstructionSequence.compile('num = 1 + 2') # => <RubyVM::InstructionSequence:<compiled>@<compiled>> iseq.path # => "<compiled>"
例2: RubyVM::InstructionSequence.compile_file を使用した場合
# /tmp/method.rb def hello puts "hello, world" end # irb > iseq = RubyVM::InstructionSequence.compile_file('method.rb') > iseq.path # => "method.rb"
[SEE_ALSO] RubyVM::InstructionSequence#absolute_path
to_a -> Array
[permalink][rdoc]self の情報を 14 要素の配列にして返します。
命令シーケンスを以下の情報で表します。
データフォーマットを示す文字列。常に "YARVInstructionSequence/SimpleDataFormat"。
命令シーケンスのメジャーバージョン。
命令シーケンスのマイナーバージョン。
データフォーマットを示す数値。常に 1。
以下の要素から構成される Hash オブジェクト。
:arg_size: メソッド、ブロックが取る引数の総数(1 つもない場合は 0)。
:local_size: ローカル変数の総数 + 1。
:stack_max: スタックの深さ。(SystemStackError を検出するために使用)
メソッド名、クラス名、モジュール名などで構成される命令シーケンスのラベル。トップレベルでは "<main>"。文字列から作成していた場合は "<compiled>"。
命令シーケンスの相対パス。文字列から作成していた場合は "<compiled>"。
命令シーケンスの絶対パス。文字列から作成していた場合は nil。
命令シーケンスの 1 行目の行番号。
命令シーケンスの種別。 :top、:method、:block、:class、:rescue、:ensure、:eval、:main、 :defined_guard のいずれか。
全ての引数名、ローカル変数名からなる Symbol の配列。
引数の指定が必須のメソッド、ブロックの引数の個数。あるいは以下のような配列。
[required_argc, [optional_arg_labels, ...], splat_index, post_splat_argc, post_splat_index, block_index, simple]
より詳細な情報については、vm_core.h を参照。
例外や制御構造のオペレータ(rescue、next、redo、break など)の一覧。
命令シーケンスを構成する命令とオペランドの配列の配列。
require 'pp'
iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
pp iseq.to_a
# ※ Ruby 2.5.0 での実行結果
# => ["YARVInstructionSequence/SimpleDataFormat",
# 2,
# 0,
# 1,
# {:arg_size=>0, :local_size=>2, :stack_max=>2},
# "<compiled>",
# "<compiled>",
# nil,
# 1,
# :top,
# [:num],
# 0,
# [],
# [1,
# [:trace, 1],
# [:putobject_OP_INT2FIX_O_1_C_],
# [:putobject, 2],
# [:opt_plus, {:mid=>:+, :flag=>256, :orig_argc=>1, :blockptr=>nil}],
# [:dup],
# [:setlocal_OP__WC__0, 2],
# [:leave]]]
to_binary(extra_data = nil) -> String
[permalink][rdoc]バイナリフォーマットでシリアライズされたiseqのデータを文字列として返します。 RubyVM::InstructionSequence.load_from_binary メソッドでバイナリデータに対応するiseqオブジェクトを作れます。
引数の extra_data はバイナリデータと共に保存されます。 RubyVM::InstructionSequence.load_from_binary_extra_data メソッドでこの文字列にアクセス出来ます。
注意: 変換後のバイナリデータはポータブルではありません。 to_binary で得たバイナリデータは他のマシンに移動できません。他のバージョンや他のアーキテクチャのRubyで作られたバイナリデータは使用できません。
iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
iseq.to_binary("extra_data")
# ※表示の都合上改行しているが実際は改行はない
# => "YARB\x02\x00\x00\x00\x03\x00\x00\x00\x16\x02\x00\x00\n\x00\x00\x00\x01
# \x00\x00\x00\x03\x00\x00\x00\x05\x00\x00\x00\x84\x01\x00\x00\x88\x01\x00
# \x00\x02\x02\x00\x00x86_64-darwin15\x00*\x00\x00\x00\x00\x00\x00\x00\x01
# \x00\x00\x00\x00\x00\x00\x00\\\x00\x00\x00\x00\x00\x00\x00\x0F\x00\x00\x00
# \x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x00\x00\x00
# \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1F
# \x00\x00\x00\x00\x00\x00\x00Y\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00
# \x00\x00\x00\x003\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00
# \x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x10
# \x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00<\x00\x00\x00
# \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
# \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
# \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00
# \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00
# \x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00
# \x00\x00\x9C\x00\x00\x00\x00\x00\x00\x00\xA4\x00\x00\x00\x00\x00\x00\x00
# \x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00
# \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xAC\x00\x00\x00
# \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
# \x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00
# \x01\x00\x00\x00\x02\x00\x00\x00\xBC\x00\x00\x00\x00\x00\x00\x00\x00\x00
# \x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00
# \xF1\x7F\x00\x00\b\x00\x00\x00\x00\x00\x00\x00E\x7F\x00\x00\x02\x00\x00\x00
# \x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00<compiled>\xF5\x7F\x00\x00
# \x05\x00\x00\x00\x00\x00\x00\x00E\x7F\x00\x00\x02\x00\x00\x00\x00\x00\x00
# \x00\x03\x00\x00\x00\x00\x00\x00\x00numE\x7F\x00\x00\x02\x00\x00\x00\x00
# \x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00+\xA0\x01\x00\x00\xAC\x01\x00
# \x00\xCA\x01\x00\x00\xD6\x01\x00\x00\xED\x01\x00\x00extra_data"
[SEE_ALSO] RubyVM::InstructionSequence.load_from_binary
[SEE_ALSO] RubyVM::InstructionSequence.load_from_binary_extra_data