lib/iseq_extra.rb in rb-threadframe-0.33 vs lib/iseq_extra.rb in rb-threadframe-0.34

- old
+ new

@@ -58,10 +58,90 @@ # proc{ 5 }.iseq.sha1 => 'b361a73f9efd7dc4d2c5e86d4e94d40b36141d42' def sha1 Digest::SHA1.hexdigest(encoded) end + + ## + # Locates the instruction address offset of the first instruction on + # the specified line or nil if no match for the specified line is + # found. + # + # @return [Fixnum, NilClass] returns + # nil if nothing is found, else the first offset for the line + def locate_line(line) + offsetlines.each_pair do |offset, val| + return offset if val.member?(line) + end + nil + end + + # 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. + # + # @return [(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. + def locate_line_with_children(line) + iseq = self + offset = iseq.locate_line(line) + p ['++++1', offset, iseq] + 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 + child_iseq = iseq + iseq = iseq.parent + unless iseq + # child_iseq is the top-most scope. Search down from here. + top_iseq = child_iseq + top_iseq.child_iseqs.each do |child_iseq| + p ['++++2', offset, child_iseq, child_iseq.parent] + next if child_iseq.equal? top_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 + + def lines + offsetlines.values.flatten.uniq + end + + # 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+. + 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 end if __FILE__ == $0 # Demo it. iseq = RubyVM::ThreadFrame.current.iseq @@ -83,7 +163,23 @@ end tf = tf.prev end end show_type + puts '-' * 40 + + line = __LINE__ + def find_line(line) # :nodoc + tf = RubyVM::ThreadFrame.current + puts "find_line has lines: #{tf.iseq.lines}" + p tf.iseq.find_iseq_with_line(line) + end + + tf = RubyVM::ThreadFrame.current + puts tf.iseq.disassemble + puts("offset %d above should be at line %d" % + [tf.iseq.locate_line(line), line]) + find_line(line+2) + find_line(line) + p tf.iseq.find_iseq_with_line(line+2) end