2015-05-31 4 views

Antwort

2

Da die Klasse begrenzt Methoden hat, gibt es begrenzte Dinge, die Sie versuchen können. Wahrscheinlich das einzige, was man tun kann, ist seine Instanz als String speichern:

puts RubyVM::InstructionSequence.disasm(proc{puts "foo"}) 

Ergebnis:

== disasm: <RubyVM::InstructionSequence:block in [email protected](irb)>===== 
== catch table 
| catch type: redo st: 0002 ed: 0009 sp: 0000 cont: 0002 
| catch type: next st: 0002 ed: 0009 sp: 0000 cont: 0009 
|------------------------------------------------------------------------ 
0000 trace   256            ( 1) 
0002 trace   1 
0004 putself   
0005 putstring  \"foo\" 
0007 opt_send_without_block <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE> 
0009 trace   512 
0011 leave    

und wenn Sie es deserialisieren möchten, müssen Sie diese Zeichenfolge analysieren.

11

Ja, es gibt einen Weg.

Zuerst müssen Sie zugänglich load Methode von InstructionSequence machen, die standardmäßig deaktiviert ist:

require 'fiddle' 

class RubyVM::InstructionSequence 
    # Retrieve Ruby Core's C-ext `iseq_load' function address 
    load_fn_addr = Fiddle::Handle::DEFAULT['rb_iseq_load'] 
    # Retrieve `iseq_load' C function representation 
    load_fn  = Fiddle::Function.new(load_fn_addr, 
             [Fiddle::TYPE_VOIDP] * 3, 
             Fiddle::TYPE_VOIDP) 

    # Make `iseq_load' accessible as `load' class method 
    define_singleton_method(:load) do |data, parent = nil, opt = nil| 
    load_fn.call(Fiddle.dlwrap(data), parent, opt).to_value 
    end 
end 

Da die RubyVM::InstructionSequence.load Methode kann als ein Array zusammengestellt VM-Anweisungen laden, können Sie frei diese verwenden für (de) Serialisierung Zwecke:

irb> # compile simple ruby program into its instruction sequence 
irb> seq = RubyVM::InstructionSequence.new <<-EOS 
irb: p 'Hello, world !' 
irb: EOS 
=> <RubyVM::InstructionSequence:<compiled>@<compiled> 

irb> # serialize sequence as Array instance representation 
irb> data = Marshal.dump seq.to_a 
=> "\x04\b[\x13\"-YARVInstructionSequence/SimpleDataFormat … ]" 

irb> # de-serialize previously serialized sequence 
irb> seq_loaded = Marshal.load data 
=> ["YARVInstructionSequence/SimpleDataFormat", 2, 2, 1, { … ] 

irb> # load deserialized Array back into instruction sequence 
irb> new_iseq = RubyVM::InstructionSequence.load seq_loaded 
=> <RubyVM::InstructionSequence:<compiled>@<compiled>> 

irb> # execute instruction sequence in current context 
irb> new_iseq.eval 
"Hello, world !" 
=> "Hello, world !" 

Das ist alles, Leute;)

+0

Das ist sehr nette! Ich mag es, dass alles mit FFI gemacht werden kann. Eine Frage: Diese Technik scheint nur zu funktionieren, wenn der Typ ISEQ_TYPE_TOP ist (also erhält man eine Befehlssequenz über RubyVM :: InstructionSequence.of (method (: foo))) löst eine Exception mit der Nachricht "keine Toplevel InstructionSequence" aus - definierter Weg, um dies zu erreichen, wenn die Befehlssequenz von einem Proc oder einer Methode erhalten wird? Setting Data [9] =: top scheint zu funktionieren, aber ich bin mir nicht sicher, was die Konsequenzen sein könnten. –

Verwandte Themen