Class: RubyVM::InstructionSequence

Inherits:
Object
  • Object
show all
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

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_argsObject

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

#lineoffsetsObject

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

#linesObject



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.

Returns:

  • (Fixnum, NilClass)

    returns nil if nothing is found, else the first offset for the line



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.

Returns:

  • ((RubyVM::InstructionSequence, Fixnum), NilClass)

    returns nil if nothing is found, else an array of size 2 containing the method the line was found in and the offset pointing there.



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

#sha1Object

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