I have the following setup in Ruby 2.6.3:
def some_method(&block)
@block = block
end
some_method do
puts "Hello There!"
puts "General Kenobi"
end
I need to take the proc that comes out of some_method
and extract the literal code that was passed in the given block, in this example I would ideally get:
"puts \"Hello There!\"\n puts \"General Kenobi\""
as a String, so I can evaluate the passed code at a later date. However, it seems that every way I could find to do this went out the window after Ruby 1.9 (parse_tree and ruby2ruby being the primary methods). In previous iterations of this project where I've had to do similarly weird things with Procs, I've been able to use the source_location
of the passed proc to figure out the content well enough to achieve this. Unfortunately I can no longer rely on the proc having a legitimate source location for this to work.
My question is this: Is there any way in modern Ruby that I would be able to simply get the interior of this proc, the source of which does not reliably exist?
While looking for some way to figure this out I've come across the RubyVM::InstructionSequence
class and have been able to use this to infer the following about the proc in question:
# Continuing from the first example
obj = RubyVM::InstructionSequence.of(@block)
puts obj.disasm
=> == disasm: #<ISeq:block in __pry__@(pry):7 (7,12)-(10,3)> (catch: FALSE)
== catch table
| catch type: redo st: 0001 ed: 0014 sp: 0000 cont: 0001
| catch type: next st: 0001 ed: 0014 sp: 0000 cont: 0014
|------------------------------------------------------------------------
0000 nop ( 7)[Bc]
0001 putself ( 8)[Li]
0002 putstring "Hello There!"
0004 opt_send_without_block <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0007 pop
0008 putself ( 9)[Li]
0009 putstring "General Kenobi"
0011 opt_send_without_block <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0014 nop
0015 leave ( 10)[Br]
Looking at this I can clearly see the instructions to load and run the given code, but, particularly with more complicated process, it is not something I can, nor want to, reliably deserialize. So a follow up question would be: Is there any way to cleanly deserialize these RubyVM::InstructionSequence instructions?