Class: RubyVM::InstructionSequence
- Inherits:
-
Object
- Object
- RubyVM::InstructionSequence
- Defined in:
- lib/iseq_extra.rb
Overview
Some additions to RubyVM::InstructionSequence
Constant Summary collapse
- TYPE2STR =
Turns a instruction sequence type field into a string name
%w(top method block class rescue ensure eval main guard)
Instance Method Summary collapse
-
#find_iseq_with_line(line) ⇒ Object
Returns an InstructionSequence for the specified line.
-
#format_args ⇒ Object
Returns a String containing a list of arguments for the RubyVM::InstructionSequence A semicolon separates required arguments from optional ones.
-
#line2offsets(line_number) ⇒ Object
Return An array of VM instruction byte offsets (Fixnums) for a given line_number.
-
#lineoffsets ⇒ Object
Basically hash.invert but since each offset can represent many lines, we have to double loop.
- #lines ⇒ Object
-
#locate_line(line) ⇒ Fixnum, NilClass
Locates the instruction address offset of the first instruction on the specified line or nil if no match for the specified line is found.
-
#locate_line_with_children(line) ⇒ (RubyVM::InstructionSequence, Fixnum), NilClass
iseq and instruction address offset of the first instruction on the specified line.
-
#sha1 ⇒ Object
Returns a cryptographic checksum (in particluar a SHA1) for the encoded bytes of the instruction sequence.
Instance Method Details
#find_iseq_with_line(line) ⇒ Object
Returns an InstructionSequence for the specified line. We search the current method meth
and then up the parent scope. If we hit the top and we can’t find line
that way, then we reverse the search from the top and search down. This will add all siblings of ancestors of meth
.
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/iseq_extra.rb', line 123 def find_iseq_with_line(line) lines = self.lines iseq = self until lines.member?(line) do child_iseq = iseq iseq = iseq.parent unless iseq # child is the top-most scope. Search down from here. pair = child_iseq.locate_line_with_children(line) ## pair = iseq.locate_line(line) return pair ? pair[0] : nil end lines = iseq.lines end return iseq end |
#format_args ⇒ Object
Returns a String containing a list of arguments for the RubyVM::InstructionSequence A semicolon separates required arguments from optional ones. For example: for
def evaluate(context, statements, file = __FILE__, line = __LINE__)
we return:
context, statements; file, line
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/iseq_extra.rb', line 15 def format_args required_max = arity < 0 ? -arity-1 : arity args = 0.upto(required_max-1).map do |i| local_name(i) end.join(', ') last = local_table_size-1 if last >= required_max opt_args = required_max.upto(last).map do |i| local_name(i) end.join(', ') args += '; ' + opt_args else args = '?' end end |
#line2offsets(line_number) ⇒ Object
Return An array of VM instruction byte offsets (Fixnums) for a given line_number.
47 48 49 50 51 |
# File 'lib/iseq_extra.rb', line 47 def line2offsets(line_number) offsetlines.select do |offset, lines| lines.member?(line_number) end.keys end |
#lineoffsets ⇒ Object
Basically hash.invert but since each offset can represent many lines, we have to double loop. FIXME: Is there a more efficient way?
34 35 36 37 38 39 40 41 42 43 |
# File 'lib/iseq_extra.rb', line 34 def lineoffsets result = {} offsetlines.each do |offset, lines| lines.each do |line| result[line] ||= [] result[line] << offset end end result end |
#lines ⇒ Object
114 115 116 |
# File 'lib/iseq_extra.rb', line 114 def lines offsetlines.values.flatten.uniq end |
#locate_line(line) ⇒ Fixnum, NilClass
Locates the instruction address offset of the first instruction on the specified line or nil if no match for the specified line is found.
71 72 73 74 75 76 |
# File 'lib/iseq_extra.rb', line 71 def locate_line(line) offsetlines.each_pair do |offset, val| return offset if val.member?(line) end nil end |
#locate_line_with_children(line) ⇒ (RubyVM::InstructionSequence, Fixnum), NilClass
iseq and instruction address offset of the first instruction on the specified line. This method recursively examines child compiled methods until an exact match for the searched line is found. It returns both the matching CompiledMethod and the OFFSET of the first instruction on the requested line, or nil if no match for the specified line is found.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/iseq_extra.rb', line 88 def locate_line_with_children(line) iseq = self offset = iseq.locate_line(line) return iseq, offset if offset # Didn't find line in this iseq, so check if a contained # InstructionSequence encompasses the line searched for until offset current_iseq = iseq iseq = iseq.parent unless iseq # current_iseq is the top-most scope. Search down from here. top_iseq.child_iseqs.each do |child_iseq| next if child_iseq.equal? current_iseq if res = child_iseq.locate_line_with_children(line) return res end end # No child method is a match - fail return nil end offset = iseq.locate_line(line) end return parent_iseq, offset end |
#sha1 ⇒ Object
Returns a cryptographic checksum (in particluar a SHA1) for the encoded bytes of the instruction sequence.
Example
proc{ 5 }.iseq.sha1 => 'b361a73f9efd7dc4d2c5e86d4e94d40b36141d42'
59 60 61 |
# File 'lib/iseq_extra.rb', line 59 def sha1 Digest::SHA1.hexdigest(encoded) end |