# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011 Rocky Bernstein # Things related to file/module status require 'thread_frame' require_relative 'iseq' SCRIPT_ISEQS__ = {} unless defined?(SCRIPT_ISEQS__) && SCRIPT_ISEQS__.is_a?(Hash) ISEQS__ = {} unless defined?(ISEQS__) && ISEQS__.is_a?(Hash) module Trepanning def file_match_pat(filename) prefix = if filename[0..0] == File::SEPARATOR # An absolute filename has to match at the beginning and # the end. '^' else # An nonabsolute filename has to match either at the # beginning of the file name or have a path separator before # the supplied part, e.g. "file.rb" does not match "myfile.rb" # but matches "my/file.rb" '(?:^|[/])' end "#{prefix}#{Regexp.escape(filename)}$" end def filter_scripts(dirname) match_block = Proc.new{|filename, iseq| filename =~ /^#{dirname}/} scripts = SCRIPT_ISEQS__.select(&match_block) SCRIPT_ISEQS__.delete_if(&match_block) match_block = Proc.new{|iseq| iseq.source_container[1] =~ /^#{dirname}/ } rejected = {} # SCRIPT_ISEQS__ is updated automatically. Dup copy is to make # sure we we aren't iterating over something that some other # process, thread or hook is filling. script_iseqs = SCRIPT_ISEQS__.dup script_iseqs.each do |name, iseqs| ary = iseqs.select(&match_block) rejected[name] = ary unless ary.empty? iseqs.delete_if(&match_block) end return [scripts, rejected] end def find_iseqs(iseqs_hash, name) iseq_name, filename = name.split(/@/) return [] unless iseqs_hash.member?(iseq_name) iseqs = iseqs_hash[iseq_name] # FIXME: filter out debugger iseqs if filename filename_pat = file_match_pat(filename) iseqs.select{|iseq| iseq.source_container[1] =~ /#{filename_pat}/} else return iseqs end end def find_iseqs_with_lineno(filename, lineno) files = find_scripts(filename) files.each do |file| SCRIPT_ISEQS__[file].each do |iseq| found_iseq = find_iseq_with_line_from_iseq(iseq, lineno, true) return found_iseq if found_iseq end end return nil end def find_scripts(filename) filename_pat = file_match_pat(filename) return SCRIPT_LINES__.keys.grep(/#{filename_pat}/) end end # Demo it if __FILE__ == $0 include Trepanning if !(ARGV.size == 1 && ARGV[0] == 'noload') ISEQS__ = {} SCRIPT_ISEQS__ = {} SCRIPT_LINES__ = {} ARGV[0..-1] = ['noload'] load(__FILE__) else load 'tmpdir.rb' tmpdir_dir = File.dirname(find_scripts('tmpdir.rb')[0]) p tmpdir_dir %w(tmpdir.rb /tmpdir.rb sometmpdir.rb).each do |filename| p find_scripts(filename) end p find_scripts(__FILE__) def tmpdir 'to conflict with the other tmpdir' end p find_iseqs(ISEQS__, "tmpdir@#{__FILE__}") puts '-' * 20 p SCRIPT_ISEQS__.keys puts '-' * 20 scripts, rejected = filter_scripts(tmpdir_dir) p scripts.keys p rejected.keys puts '-' * 20 p SCRIPT_ISEQS__.keys # require_relative '../lib/trepanning'; debugger p find_iseqs_with_lineno(__FILE__, __LINE__) p find_iseqs(ISEQS__, 'find_scripts') end end