2007-12-29
Ruby 1.9.0のバイトコードをいじり倒す(その2)
|VM::InstructionSequenceの簡単な使い方です。
require 'pp' iseq = VM::InstructionSequence.compile("print 'Hell world\n'") PP.pp iseq.to_a VM::InstructionSequence.load(iseq.to_a).eval
出力結果は次のようになります。
["YARVInstructionSequence/SimpleDataFormat", 1, 1, 1, {:arg_size=>0, :local_size=>1, :stack_max=>2}, "<compiled>", "<compiled>", 0, :top, [], 0, [], [2, [:putnil], [:putstring, "Hell world\n"], [:send, :print, 1, nil, 8, nil], [:leave]]] Hell world
結局、VM::InstructionSequence.loadには "YARVInstructionSequence/SimpleDataFormat"で始まる配列を渡せばいいことになります。配列はRubyではバリバリにいじれますので、VM::InstructionSequence.compileで得たRubyプログラムのバイトコード表現に色々バッチを当てることが出来ます。しかし、loadに渡す配列は結構複雑な構造をしています。この配列を配列のまま扱うと結構煩雑です。
そこで、バイトコード列をオブジェクトとして扱うクラスを作りました。続きは前の日記なので注意してください
module VMLib class InstSeqTree Headers = %w(magic major_version minor_version format_type misc name filename line type locals args exception_table) def initialize(iseq = nil) @klasses = {} @methodes = {} @blockes = [] @line = {} @line[nil] = [] @line_list = [] @header = {} Headers.each do |name| @header[name] = nil end if iseq then init_from_ary(iseq.to_a) end end attr :klasses attr :methodes attr :blockes attr :line attr :line_list attr :header def init_from_ary(ary) i = 0 Headers.each do |name| @header[name] = ary[i] i = i + 1 end body = ary[i] curlinno = nil body.each do |inst| if inst.is_a? Integer then # Line number curlinno = "#{inst}" i = 1 while @line_list.include?(curlinno) do curlinno = "#{inst}-#{i}" i = i + 1 end @line_list.push curlinno @line[curlinno] = [] elsif inst.is_a? Array case inst[0] when :defineclass if inst[2] then obj = InstSeqTree.new obj.init_from_ary(inst[2]) @klasses[inst[1]] = obj end when :definemethod if inst[2] then obj = InstSeqTree.new obj.init_from_ary(inst[2]) @methodes[inst[1]] = obj end when :send if inst[3] then obj = InstSeqTree.new obj.init_from_ary(inst[3]) @blockes.push obj end when :invokesuper if inst[2] then obj = InstSeqTree.new obj.init_from_ary(inst[2]) @blockes.push obj end end @line[curlinno].push inst elsif inst.is_a? Symbol # Label if !@line_list.include?(curlinno) then @line_list.push curlinno end @line[curlinno].push inst else raise inst end end end def to_a res = [] Headers.each do |name| res.push @header[name] end body = [] blno = 0 @line_list.each do |ln| body.push ln.to_i @line[ln].each do |inst| if inst.is_a? Array then case inst[0] when :defineclass if inst[2] then inst[2] = @klasses[inst[1]].to_a end when :definemethod if inst[2] then inst[2] = @methodes[inst[1]].to_a end when :send if inst[3] then inst[3] = @blockes[blno].to_a blno = blno + 1 end when :invokesuper if inst[2] then inst[2] = @blockes[blno].to_a blno = blno + 1 end end end body.push inst end end res.push body res end end def add_code_before_block(&action) add_code([nil, nil, nil]) do |code, info| if code.line_list[0] != :before then code.line_list.unshift :before code.line[:before] = [] end curlno = code.line_list[1] insco = action.call(header['filename'], info, curlno) code.line[:before] = insco + code.line[:before] end end def add_code_after_block(&action) add_code([nil, nil, nil]) do |code, info| code.line.each do |no, cont| code.line[no] = cont.inject([]) do |res, inst| if inst.is_a? Array then if inst[0] == :leave then curlno = code.line_list.last insco = action.call(header['filename'], info, curlno) res = res + insco end end res.push inst res end end end end def add_code_before_line(&action) add_code([nil, nil, nil]) do |code, info| code.line.each do |no, cont| pre = [] if cont[0].is_a? Array and cont[0][0] == :getinlinecache then pre = [cont.shift] end code.line[no] = pre + action.call(header['filename'], info, no) + cont end end end def add_code(info, &action) action.call(self, info) klasses.each do |name, cont| cont.add_code([name, nil, nil], &action) end methodes.each do |name, cont| cont.add_code([info[0], name, nil], &action) end blockes.each_with_index do |cont, idx| cont.add_code([info[0], info[1], idx], &action) end end end
- 2 http://d.hatena.ne.jp/keyword/Ruby
- 2 http://d.hatena.ne.jp/keyworddiary/Ruby
- 2 http://reader.livedoor.com/reader/
- 1 http://atode.cc/bar.php?u=http://d.hatena.ne.jp/miura1729/20071229/1198903950&t=Ruby+1.9.0のバイトコードをいじり倒す(その1)+-+miura172
- 1 http://b.hatena.ne.jp/add?mode=confirm&title=2007-12-29 - miura1729%u306E%u65E5%u8A18&url=http://d.hatena.ne.jp/miura1729/20071229
- 1 http://b.hatena.ne.jp/add?mode=confirm&url=http://d.hatena.ne.jp/miura1729/20071229/1198903950
- 1 http://b.hatena.ne.jp/entry/http://d.hatena.ne.jp/miura1729/20071229/1198903950
- 1 http://b.hatena.ne.jp/yupo5656/favorite
- 1 http://d.hatena.ne.jp/keyworddiary/Ruby?date=20071228
- 1 http://d.hatena.ne.jp/sshi/20071226/p1