typeprof 0.15.3 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.github/workflows/main.yml +1 -1
 - data/Gemfile.lock +4 -4
 - data/exe/typeprof +5 -1
 - data/lib/typeprof/analyzer.rb +228 -54
 - data/lib/typeprof/arguments.rb +1 -0
 - data/lib/typeprof/builtin.rb +23 -23
 - data/lib/typeprof/cli.rb +22 -2
 - data/lib/typeprof/code-range.rb +177 -0
 - data/lib/typeprof/config.rb +43 -18
 - data/lib/typeprof/container-type.rb +3 -0
 - data/lib/typeprof/export.rb +191 -15
 - data/lib/typeprof/import.rb +25 -4
 - data/lib/typeprof/iseq.rb +218 -16
 - data/lib/typeprof/lsp.rb +865 -0
 - data/lib/typeprof/method.rb +15 -11
 - data/lib/typeprof/type.rb +46 -38
 - data/lib/typeprof/utils.rb +18 -1
 - data/lib/typeprof/version.rb +1 -1
 - data/lib/typeprof.rb +3 -0
 - data/typeprof-lsp +3 -0
 - data/typeprof.gemspec +1 -1
 - data/vscode/.gitignore +5 -0
 - data/vscode/.vscode/launch.json +16 -0
 - data/vscode/.vscodeignore +7 -0
 - data/vscode/README.md +22 -0
 - data/vscode/development.md +31 -0
 - data/vscode/package-lock.json +2211 -0
 - data/vscode/package.json +71 -0
 - data/vscode/sandbox/test.rb +24 -0
 - data/vscode/src/extension.ts +285 -0
 - data/vscode/tsconfig.json +15 -0
 - metadata +18 -5
 
    
        data/lib/typeprof/iseq.rb
    CHANGED
    
    | 
         @@ -2,6 +2,17 @@ module TypeProf 
     | 
|
| 
       2 
2 
     | 
    
         
             
              class ISeq
         
     | 
| 
       3 
3 
     | 
    
         
             
                # https://github.com/ruby/ruby/pull/4468
         
     | 
| 
       4 
4 
     | 
    
         
             
                CASE_WHEN_CHECKMATCH = RubyVM::InstructionSequence.compile("case 1; when Integer; end").to_a.last.any? {|insn,| insn == :checkmatch }
         
     | 
| 
      
 5 
     | 
    
         
            +
                # https://github.com/ruby/ruby/blob/v3_0_2/vm_core.h#L1206
         
     | 
| 
      
 6 
     | 
    
         
            +
                VM_ENV_DATA_SIZE = 3
         
     | 
| 
      
 7 
     | 
    
         
            +
                # Check if Ruby 3.1 or later
         
     | 
| 
      
 8 
     | 
    
         
            +
                RICH_AST = begin RubyVM::AbstractSyntaxTree.parse("1", keep_script_lines: true).node_id; true; rescue; false; end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                FileInfo = Struct.new(
         
     | 
| 
      
 11 
     | 
    
         
            +
                  :node_id2node,
         
     | 
| 
      
 12 
     | 
    
         
            +
                  :definition_table,
         
     | 
| 
      
 13 
     | 
    
         
            +
                  :caller_table,
         
     | 
| 
      
 14 
     | 
    
         
            +
                  :created_iseqs,
         
     | 
| 
      
 15 
     | 
    
         
            +
                )
         
     | 
| 
       5 
16 
     | 
    
         | 
| 
       6 
17 
     | 
    
         
             
                class << self
         
     | 
| 
       7 
18 
     | 
    
         
             
                  def compile(file)
         
     | 
| 
         @@ -20,17 +31,62 @@ module TypeProf 
     | 
|
| 
       20 
31 
     | 
    
         
             
                    opt[:operands_unification] = false
         
     | 
| 
       21 
32 
     | 
    
         
             
                    opt[:coverage_enabled] = false
         
     | 
| 
       22 
33 
     | 
    
         | 
| 
      
 34 
     | 
    
         
            +
                    parse_opts = {}
         
     | 
| 
      
 35 
     | 
    
         
            +
                    parse_opts[:keep_script_lines] = true if RICH_AST
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
       23 
37 
     | 
    
         
             
                    if str
         
     | 
| 
      
 38 
     | 
    
         
            +
                      node = RubyVM::AbstractSyntaxTree.parse(str, **parse_opts)
         
     | 
| 
       24 
39 
     | 
    
         
             
                      iseq = RubyVM::InstructionSequence.compile(str, path, **opt)
         
     | 
| 
       25 
40 
     | 
    
         
             
                    else
         
     | 
| 
      
 41 
     | 
    
         
            +
                      node = RubyVM::AbstractSyntaxTree.parse_file(path, **parse_opts)
         
     | 
| 
       26 
42 
     | 
    
         
             
                      iseq = RubyVM::InstructionSequence.compile_file(path, **opt)
         
     | 
| 
       27 
43 
     | 
    
         
             
                    end
         
     | 
| 
       28 
44 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
                     
     | 
| 
      
 45 
     | 
    
         
            +
                    node_id2node = {}
         
     | 
| 
      
 46 
     | 
    
         
            +
                    build_ast_node_id_table(node, node_id2node) if RICH_AST
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    file_info = FileInfo.new(node_id2node, CodeRangeTable.new, CodeRangeTable.new, [])
         
     | 
| 
      
 49 
     | 
    
         
            +
                    iseq_rb = new(iseq.to_a, file_info)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    iseq_rb.collect_local_variable_info(file_info) if RICH_AST
         
     | 
| 
      
 51 
     | 
    
         
            +
                    file_info.created_iseqs.each do |iseq|
         
     | 
| 
      
 52 
     | 
    
         
            +
                      iseq.unify_instructions
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                    return iseq_rb, file_info.definition_table, file_info.caller_table
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  private def build_ast_node_id_table(node, tbl = {})
         
     | 
| 
      
 59 
     | 
    
         
            +
                    tbl[node.node_id] = node
         
     | 
| 
      
 60 
     | 
    
         
            +
                    node.children.each do |child|
         
     | 
| 
      
 61 
     | 
    
         
            +
                      build_ast_node_id_table(child, tbl) if child.is_a?(RubyVM::AbstractSyntaxTree::Node)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    end
         
     | 
| 
      
 63 
     | 
    
         
            +
                    tbl
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                  def code_range_from_node(node)
         
     | 
| 
      
 67 
     | 
    
         
            +
                    CodeRange.new(
         
     | 
| 
      
 68 
     | 
    
         
            +
                      CodeLocation.new(node.first_lineno, node.first_column),
         
     | 
| 
      
 69 
     | 
    
         
            +
                      CodeLocation.new(node.last_lineno, node.last_column),
         
     | 
| 
      
 70 
     | 
    
         
            +
                    )
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  def find_node_by_id(node, id)
         
     | 
| 
      
 74 
     | 
    
         
            +
                    node = RubyVM::AbstractSyntaxTree.parse(node) if node.is_a?(String)
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                    return node if id == node.node_id
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                    node.children.each do |child|
         
     | 
| 
      
 79 
     | 
    
         
            +
                      if child.is_a?(RubyVM::AbstractSyntaxTree::Node)
         
     | 
| 
      
 80 
     | 
    
         
            +
                        ret = find_node_by_id(child, id)
         
     | 
| 
      
 81 
     | 
    
         
            +
                        return ret if ret
         
     | 
| 
      
 82 
     | 
    
         
            +
                      end
         
     | 
| 
      
 83 
     | 
    
         
            +
                    end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    nil
         
     | 
| 
       30 
86 
     | 
    
         
             
                  end
         
     | 
| 
       31 
87 
     | 
    
         
             
                end
         
     | 
| 
       32 
88 
     | 
    
         | 
| 
       33 
     | 
    
         
            -
                Insn = Struct.new(:insn, :operands, :lineno)
         
     | 
| 
      
 89 
     | 
    
         
            +
                Insn = Struct.new(:insn, :operands, :lineno, :code_range, :definitions)
         
     | 
| 
       34 
90 
     | 
    
         
             
                class Insn
         
     | 
| 
       35 
91 
     | 
    
         
             
                  def check?(insn_cmp, operands_cmp = nil)
         
     | 
| 
       36 
92 
     | 
    
         
             
                    return insn == insn_cmp && (!operands_cmp || operands == operands_cmp)
         
     | 
| 
         @@ -39,14 +95,19 @@ module TypeProf 
     | 
|
| 
       39 
95 
     | 
    
         | 
| 
       40 
96 
     | 
    
         
             
                ISEQ_FRESH_ID = [0]
         
     | 
| 
       41 
97 
     | 
    
         | 
| 
       42 
     | 
    
         
            -
                def initialize(iseq)
         
     | 
| 
      
 98 
     | 
    
         
            +
                def initialize(iseq, file_info)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  file_info.created_iseqs << self
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
       43 
101 
     | 
    
         
             
                  @id = (ISEQ_FRESH_ID[0] += 1)
         
     | 
| 
       44 
102 
     | 
    
         | 
| 
       45 
     | 
    
         
            -
                  _magic, _major_version, _minor_version, _format_type,  
     | 
| 
      
 103 
     | 
    
         
            +
                  _magic, _major_version, _minor_version, _format_type, misc,
         
     | 
| 
       46 
104 
     | 
    
         
             
                    @name, @path, @absolute_path, @start_lineno, @type,
         
     | 
| 
       47 
105 
     | 
    
         
             
                    @locals, @fargs_format, catch_table, insns = *iseq
         
     | 
| 
       48 
106 
     | 
    
         | 
| 
       49 
     | 
    
         
            -
                   
     | 
| 
      
 107 
     | 
    
         
            +
                  fl, fc, ll, lc = misc[:code_location]
         
     | 
| 
      
 108 
     | 
    
         
            +
                  @iseq_code_range = CodeRange.new(CodeLocation.new(fl, fc), CodeLocation.new(ll, lc))
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                  convert_insns(insns, misc[:node_ids] || [], file_info)
         
     | 
| 
       50 
111 
     | 
    
         | 
| 
       51 
112 
     | 
    
         
             
                  add_body_start_marker(insns)
         
     | 
| 
       52 
113 
     | 
    
         | 
| 
         @@ -54,13 +115,13 @@ module TypeProf 
     | 
|
| 
       54 
115 
     | 
    
         | 
| 
       55 
116 
     | 
    
         
             
                  labels = create_label_table(insns)
         
     | 
| 
       56 
117 
     | 
    
         | 
| 
       57 
     | 
    
         
            -
                  @insns = setup_insns(insns, labels)
         
     | 
| 
      
 118 
     | 
    
         
            +
                  @insns = setup_insns(insns, labels, file_info)
         
     | 
| 
       58 
119 
     | 
    
         | 
| 
       59 
120 
     | 
    
         
             
                  @fargs_format[:opt] = @fargs_format[:opt].map {|l| labels[l] } if @fargs_format[:opt]
         
     | 
| 
       60 
121 
     | 
    
         | 
| 
       61 
122 
     | 
    
         
             
                  @catch_table = []
         
     | 
| 
       62 
123 
     | 
    
         
             
                  catch_table.map do |type, iseq, first, last, cont, stack_depth|
         
     | 
| 
       63 
     | 
    
         
            -
                    iseq = iseq ? ISeq.new(iseq) : nil
         
     | 
| 
      
 124 
     | 
    
         
            +
                    iseq = iseq ? ISeq.new(iseq, file_info) : nil
         
     | 
| 
       64 
125 
     | 
    
         
             
                    target = labels[cont]
         
     | 
| 
       65 
126 
     | 
    
         
             
                    entry = [type, iseq, target, stack_depth]
         
     | 
| 
       66 
127 
     | 
    
         
             
                    labels[first].upto(labels[last]) do |i|
         
     | 
| 
         @@ -69,17 +130,78 @@ module TypeProf 
     | 
|
| 
       69 
130 
     | 
    
         
             
                    end
         
     | 
| 
       70 
131 
     | 
    
         
             
                  end
         
     | 
| 
       71 
132 
     | 
    
         | 
| 
      
 133 
     | 
    
         
            +
                  def_node_id = misc[:def_node_id]
         
     | 
| 
      
 134 
     | 
    
         
            +
                  if def_node_id && file_info.node_id2node[def_node_id] && (@type == :method || @type == :block)
         
     | 
| 
      
 135 
     | 
    
         
            +
                    def_node = file_info.node_id2node[def_node_id]
         
     | 
| 
      
 136 
     | 
    
         
            +
                    method_name_token_range = extract_method_name_token_range(def_node)
         
     | 
| 
      
 137 
     | 
    
         
            +
                    if method_name_token_range
         
     | 
| 
      
 138 
     | 
    
         
            +
                      @callers = Utils::MutableSet.new
         
     | 
| 
      
 139 
     | 
    
         
            +
                      file_info.caller_table[method_name_token_range] = @callers
         
     | 
| 
      
 140 
     | 
    
         
            +
                    end
         
     | 
| 
      
 141 
     | 
    
         
            +
                  end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
       72 
143 
     | 
    
         
             
                  rename_insn_types
         
     | 
| 
      
 144 
     | 
    
         
            +
                end
         
     | 
| 
       73 
145 
     | 
    
         | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
      
 146 
     | 
    
         
            +
                def extract_method_name_token_range(node)
         
     | 
| 
      
 147 
     | 
    
         
            +
                  case @type
         
     | 
| 
      
 148 
     | 
    
         
            +
                  when :method
         
     | 
| 
      
 149 
     | 
    
         
            +
                    regex = if node.type == :DEFS
         
     | 
| 
      
 150 
     | 
    
         
            +
                       /^def\s+(?:\w+)\s*\.\s*(\w+)/
         
     | 
| 
      
 151 
     | 
    
         
            +
                    else
         
     | 
| 
      
 152 
     | 
    
         
            +
                      /^def\s+(\w+)/
         
     | 
| 
      
 153 
     | 
    
         
            +
                    end
         
     | 
| 
      
 154 
     | 
    
         
            +
                    return nil unless node.source =~ regex
         
     | 
| 
      
 155 
     | 
    
         
            +
                    zero_loc = CodeLocation.new(1, 0)
         
     | 
| 
      
 156 
     | 
    
         
            +
                    name_start = $~.begin(1)
         
     | 
| 
      
 157 
     | 
    
         
            +
                    name_length = $~.end(1) - name_start
         
     | 
| 
      
 158 
     | 
    
         
            +
                    name_head_loc = zero_loc.advance_cursor(name_start, node.source)
         
     | 
| 
      
 159 
     | 
    
         
            +
                    name_tail_loc = name_head_loc.advance_cursor(name_length, node.source)
         
     | 
| 
      
 160 
     | 
    
         
            +
                    return CodeRange.new(
         
     | 
| 
      
 161 
     | 
    
         
            +
                      CodeLocation.new(
         
     | 
| 
      
 162 
     | 
    
         
            +
                        node.first_lineno + (name_head_loc.lineno - 1),
         
     | 
| 
      
 163 
     | 
    
         
            +
                        name_head_loc.lineno == 1 ? node.first_column + name_head_loc.column : name_head_loc.column
         
     | 
| 
      
 164 
     | 
    
         
            +
                      ),
         
     | 
| 
      
 165 
     | 
    
         
            +
                      CodeLocation.new(
         
     | 
| 
      
 166 
     | 
    
         
            +
                        node.first_lineno + (name_tail_loc.lineno - 1),
         
     | 
| 
      
 167 
     | 
    
         
            +
                        name_tail_loc.lineno == 1 ? node.first_column + name_tail_loc.column : name_tail_loc.column
         
     | 
| 
      
 168 
     | 
    
         
            +
                      ),
         
     | 
| 
      
 169 
     | 
    
         
            +
                    )
         
     | 
| 
      
 170 
     | 
    
         
            +
                  when :block
         
     | 
| 
      
 171 
     | 
    
         
            +
                    return ISeq.code_range_from_node(node)
         
     | 
| 
      
 172 
     | 
    
         
            +
                  end
         
     | 
| 
       75 
173 
     | 
    
         
             
                end
         
     | 
| 
       76 
174 
     | 
    
         | 
| 
       77 
175 
     | 
    
         
             
                def source_location(pc)
         
     | 
| 
       78 
176 
     | 
    
         
             
                  "#{ @path }:#{ @insns[pc].lineno }"
         
     | 
| 
       79 
177 
     | 
    
         
             
                end
         
     | 
| 
       80 
178 
     | 
    
         | 
| 
      
 179 
     | 
    
         
            +
                def detailed_source_location(pc)
         
     | 
| 
      
 180 
     | 
    
         
            +
                  code_range = @insns[pc].code_range
         
     | 
| 
      
 181 
     | 
    
         
            +
                  if code_range
         
     | 
| 
      
 182 
     | 
    
         
            +
                    [@path, code_range]
         
     | 
| 
      
 183 
     | 
    
         
            +
                  else
         
     | 
| 
      
 184 
     | 
    
         
            +
                    [@path]
         
     | 
| 
      
 185 
     | 
    
         
            +
                  end
         
     | 
| 
      
 186 
     | 
    
         
            +
                end
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
                def add_called_iseq(pc, callee_iseq)
         
     | 
| 
      
 189 
     | 
    
         
            +
                  if callee_iseq && @insns[pc].definitions
         
     | 
| 
      
 190 
     | 
    
         
            +
                    @insns[pc].definitions << [callee_iseq.path, callee_iseq.iseq_code_range]
         
     | 
| 
      
 191 
     | 
    
         
            +
                  end
         
     | 
| 
      
 192 
     | 
    
         
            +
                  if callee_iseq.callers
         
     | 
| 
      
 193 
     | 
    
         
            +
                    callee_iseq.callers << [@path, @insns[pc].code_range]
         
     | 
| 
      
 194 
     | 
    
         
            +
                  end
         
     | 
| 
      
 195 
     | 
    
         
            +
                end
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                def add_def_loc(pc, detailed_loc)
         
     | 
| 
      
 198 
     | 
    
         
            +
                  if detailed_loc && @insns[pc].definitions
         
     | 
| 
      
 199 
     | 
    
         
            +
                    @insns[pc].definitions << detailed_loc
         
     | 
| 
      
 200 
     | 
    
         
            +
                  end
         
     | 
| 
      
 201 
     | 
    
         
            +
                end
         
     | 
| 
      
 202 
     | 
    
         
            +
             
     | 
| 
       81 
203 
     | 
    
         
             
                attr_reader :name, :path, :absolute_path, :start_lineno, :type, :locals, :fargs_format, :catch_table, :insns
         
     | 
| 
       82 
     | 
    
         
            -
                attr_reader :id
         
     | 
| 
      
 204 
     | 
    
         
            +
                attr_reader :id, :iseq_code_range, :callers
         
     | 
| 
       83 
205 
     | 
    
         | 
| 
       84 
206 
     | 
    
         
             
                def pretty_print(q)
         
     | 
| 
       85 
207 
     | 
    
         
             
                  q.text "ISeq["
         
     | 
| 
         @@ -118,7 +240,7 @@ module TypeProf 
     | 
|
| 
       118 
240 
     | 
    
         
             
                end
         
     | 
| 
       119 
241 
     | 
    
         | 
| 
       120 
242 
     | 
    
         
             
                # Remove lineno entry and convert instructions to Insn instances
         
     | 
| 
       121 
     | 
    
         
            -
                def convert_insns(insns)
         
     | 
| 
      
 243 
     | 
    
         
            +
                def convert_insns(insns, node_ids, file_info)
         
     | 
| 
       122 
244 
     | 
    
         
             
                  ninsns = []
         
     | 
| 
       123 
245 
     | 
    
         
             
                  lineno = 0
         
     | 
| 
       124 
246 
     | 
    
         
             
                  insns.each do |e|
         
     | 
| 
         @@ -129,7 +251,25 @@ module TypeProf 
     | 
|
| 
       129 
251 
     | 
    
         
             
                      ninsns << e
         
     | 
| 
       130 
252 
     | 
    
         
             
                    when Array
         
     | 
| 
       131 
253 
     | 
    
         
             
                      insn, *operands = e
         
     | 
| 
       132 
     | 
    
         
            -
                       
     | 
| 
      
 254 
     | 
    
         
            +
                      node_id = node_ids.shift
         
     | 
| 
      
 255 
     | 
    
         
            +
                      node = file_info.node_id2node[node_id]
         
     | 
| 
      
 256 
     | 
    
         
            +
                      if node
         
     | 
| 
      
 257 
     | 
    
         
            +
                        code_range = ISeq.code_range_from_node(node)
         
     | 
| 
      
 258 
     | 
    
         
            +
                        case insn
         
     | 
| 
      
 259 
     | 
    
         
            +
                        when :send, :invokesuper
         
     | 
| 
      
 260 
     | 
    
         
            +
                          opt, blk_iseq = operands
         
     | 
| 
      
 261 
     | 
    
         
            +
                          opt[:node_id] = node_id
         
     | 
| 
      
 262 
     | 
    
         
            +
                          if blk_iseq
         
     | 
| 
      
 263 
     | 
    
         
            +
                            misc = blk_iseq[4] # iseq's "misc" field
         
     | 
| 
      
 264 
     | 
    
         
            +
                            misc[:def_node_id] = node_id
         
     | 
| 
      
 265 
     | 
    
         
            +
                          end
         
     | 
| 
      
 266 
     | 
    
         
            +
                        when :definemethod, :definesmethod
         
     | 
| 
      
 267 
     | 
    
         
            +
                          iseq = operands[1]
         
     | 
| 
      
 268 
     | 
    
         
            +
                          misc = iseq[4] # iseq's "misc" field
         
     | 
| 
      
 269 
     | 
    
         
            +
                          misc[:def_node_id] = node_id
         
     | 
| 
      
 270 
     | 
    
         
            +
                        end
         
     | 
| 
      
 271 
     | 
    
         
            +
                      end
         
     | 
| 
      
 272 
     | 
    
         
            +
                      ninsns << Insn.new(insn, operands, lineno, code_range, nil)
         
     | 
| 
       133 
273 
     | 
    
         
             
                    else
         
     | 
| 
       134 
274 
     | 
    
         
             
                      raise "unknown iseq entry: #{ e }"
         
     | 
| 
       135 
275 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -156,7 +296,7 @@ module TypeProf 
     | 
|
| 
       156 
296 
     | 
    
         
             
                      i = insns.index(label) + 1
         
     | 
| 
       157 
297 
     | 
    
         
             
                    end
         
     | 
| 
       158 
298 
     | 
    
         | 
| 
       159 
     | 
    
         
            -
                    insns.insert(i, Insn.new(:_iseq_body_start, [], @start_lineno))
         
     | 
| 
      
 299 
     | 
    
         
            +
                    insns.insert(i, Insn.new(:_iseq_body_start, [], @start_lineno, nil, nil))
         
     | 
| 
       160 
300 
     | 
    
         
             
                  end
         
     | 
| 
       161 
301 
     | 
    
         
             
                end
         
     | 
| 
       162 
302 
     | 
    
         | 
| 
         @@ -198,7 +338,7 @@ module TypeProf 
     | 
|
| 
       198 
338 
     | 
    
         
             
                  labels
         
     | 
| 
       199 
339 
     | 
    
         
             
                end
         
     | 
| 
       200 
340 
     | 
    
         | 
| 
       201 
     | 
    
         
            -
                def setup_insns(insns, labels)
         
     | 
| 
      
 341 
     | 
    
         
            +
                def setup_insns(insns, labels, file_info)
         
     | 
| 
       202 
342 
     | 
    
         
             
                  ninsns = []
         
     | 
| 
       203 
343 
     | 
    
         
             
                  insns.each do |e|
         
     | 
| 
       204 
344 
     | 
    
         
             
                    case e
         
     | 
| 
         @@ -208,7 +348,7 @@ module TypeProf 
     | 
|
| 
       208 
348 
     | 
    
         
             
                      operands = (INSN_TABLE[e.insn] || []).zip(e.operands).map do |type, operand|
         
     | 
| 
       209 
349 
     | 
    
         
             
                        case type
         
     | 
| 
       210 
350 
     | 
    
         
             
                        when "ISEQ"
         
     | 
| 
       211 
     | 
    
         
            -
                          operand && ISeq.new(operand)
         
     | 
| 
      
 351 
     | 
    
         
            +
                          operand && ISeq.new(operand, file_info)
         
     | 
| 
       212 
352 
     | 
    
         
             
                        when "lindex_t", "rb_num_t", "VALUE", "ID", "GENTRY", "CALL_DATA"
         
     | 
| 
       213 
353 
     | 
    
         
             
                          operand
         
     | 
| 
       214 
354 
     | 
    
         
             
                        when "OFFSET"
         
     | 
| 
         @@ -221,7 +361,12 @@ module TypeProf 
     | 
|
| 
       221 
361 
     | 
    
         
             
                        end
         
     | 
| 
       222 
362 
     | 
    
         
             
                      end
         
     | 
| 
       223 
363 
     | 
    
         | 
| 
       224 
     | 
    
         
            -
                       
     | 
| 
      
 364 
     | 
    
         
            +
                      if e.code_range && should_collect_defs(e.insn)
         
     | 
| 
      
 365 
     | 
    
         
            +
                        definition = Utils::MutableSet.new
         
     | 
| 
      
 366 
     | 
    
         
            +
                        file_info.definition_table[e.code_range] = definition
         
     | 
| 
      
 367 
     | 
    
         
            +
                      end
         
     | 
| 
      
 368 
     | 
    
         
            +
             
     | 
| 
      
 369 
     | 
    
         
            +
                      ninsns << Insn.new(e.insn, operands, e.lineno, e.code_range, definition)
         
     | 
| 
       225 
370 
     | 
    
         
             
                    else
         
     | 
| 
       226 
371 
     | 
    
         
             
                      raise "unknown iseq entry: #{ e }"
         
     | 
| 
       227 
372 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -229,6 +374,63 @@ module TypeProf 
     | 
|
| 
       229 
374 
     | 
    
         
             
                  ninsns
         
     | 
| 
       230 
375 
     | 
    
         
             
                end
         
     | 
| 
       231 
376 
     | 
    
         | 
| 
      
 377 
     | 
    
         
            +
                def should_collect_defs(insn_kind)
         
     | 
| 
      
 378 
     | 
    
         
            +
                  case insn_kind
         
     | 
| 
      
 379 
     | 
    
         
            +
                  when :send, :getinstancevariable, :getconstant
         
     | 
| 
      
 380 
     | 
    
         
            +
                    return true
         
     | 
| 
      
 381 
     | 
    
         
            +
                  else
         
     | 
| 
      
 382 
     | 
    
         
            +
                    return false
         
     | 
| 
      
 383 
     | 
    
         
            +
                  end
         
     | 
| 
      
 384 
     | 
    
         
            +
                end
         
     | 
| 
      
 385 
     | 
    
         
            +
             
     | 
| 
      
 386 
     | 
    
         
            +
                # Collect local variable use and definition info recursively
         
     | 
| 
      
 387 
     | 
    
         
            +
                def collect_local_variable_info(file_info, absolute_level = 0, parent_variable_tables = {})
         
     | 
| 
      
 388 
     | 
    
         
            +
                  # e.g.
         
     | 
| 
      
 389 
     | 
    
         
            +
                  # variable_tables[abs_level][idx] = [[path, code_range]]
         
     | 
| 
      
 390 
     | 
    
         
            +
                  current_variables = []
         
     | 
| 
      
 391 
     | 
    
         
            +
                  variable_tables = parent_variable_tables.merge({
         
     | 
| 
      
 392 
     | 
    
         
            +
                    absolute_level => current_variables
         
     | 
| 
      
 393 
     | 
    
         
            +
                  })
         
     | 
| 
      
 394 
     | 
    
         
            +
             
     | 
| 
      
 395 
     | 
    
         
            +
                  dummy_def_range = CodeRange.new(
         
     | 
| 
      
 396 
     | 
    
         
            +
                    CodeLocation.new(@start_lineno, 0),
         
     | 
| 
      
 397 
     | 
    
         
            +
                    CodeLocation.new(@start_lineno, 1),
         
     | 
| 
      
 398 
     | 
    
         
            +
                  )
         
     | 
| 
      
 399 
     | 
    
         
            +
                  # Fill tail elements with parameters
         
     | 
| 
      
 400 
     | 
    
         
            +
                  (@fargs_format[:lead_num] || 0).times do |offset|
         
     | 
| 
      
 401 
     | 
    
         
            +
                    current_variables[VM_ENV_DATA_SIZE + @locals.length - offset - 1] ||= Utils::MutableSet.new
         
     | 
| 
      
 402 
     | 
    
         
            +
                    current_variables[VM_ENV_DATA_SIZE + @locals.length - offset - 1] << [@path, dummy_def_range]
         
     | 
| 
      
 403 
     | 
    
         
            +
                  end
         
     | 
| 
      
 404 
     | 
    
         
            +
             
     | 
| 
      
 405 
     | 
    
         
            +
                  @insns.each do |insn|
         
     | 
| 
      
 406 
     | 
    
         
            +
                    next unless insn.insn == :getlocal || insn.insn == :setlocal
         
     | 
| 
      
 407 
     | 
    
         
            +
             
     | 
| 
      
 408 
     | 
    
         
            +
                    idx = insn.operands[0]
         
     | 
| 
      
 409 
     | 
    
         
            +
                    # note: level is relative value to the current level
         
     | 
| 
      
 410 
     | 
    
         
            +
                    level = insn.operands[1]
         
     | 
| 
      
 411 
     | 
    
         
            +
                    target_abs_level = absolute_level - level
         
     | 
| 
      
 412 
     | 
    
         
            +
                    variable_tables[target_abs_level] ||= {}
         
     | 
| 
      
 413 
     | 
    
         
            +
                    variable_tables[target_abs_level][idx] ||= Utils::MutableSet.new
         
     | 
| 
      
 414 
     | 
    
         
            +
             
     | 
| 
      
 415 
     | 
    
         
            +
                    case insn.insn
         
     | 
| 
      
 416 
     | 
    
         
            +
                    when :setlocal
         
     | 
| 
      
 417 
     | 
    
         
            +
                      variable_tables[target_abs_level][idx] << [path, insn.code_range]
         
     | 
| 
      
 418 
     | 
    
         
            +
                    when :getlocal
         
     | 
| 
      
 419 
     | 
    
         
            +
                      file_info.definition_table[insn.code_range] = variable_tables[target_abs_level][idx]
         
     | 
| 
      
 420 
     | 
    
         
            +
                    end
         
     | 
| 
      
 421 
     | 
    
         
            +
                  end
         
     | 
| 
      
 422 
     | 
    
         
            +
             
     | 
| 
      
 423 
     | 
    
         
            +
                  @insns.each do |insn|
         
     | 
| 
      
 424 
     | 
    
         
            +
                    insn.operands.each do |operand|
         
     | 
| 
      
 425 
     | 
    
         
            +
                      next unless operand.is_a?(ISeq)
         
     | 
| 
      
 426 
     | 
    
         
            +
                      operand.collect_local_variable_info(
         
     | 
| 
      
 427 
     | 
    
         
            +
                        file_info, absolute_level + 1,
         
     | 
| 
      
 428 
     | 
    
         
            +
                        variable_tables
         
     | 
| 
      
 429 
     | 
    
         
            +
                      )
         
     | 
| 
      
 430 
     | 
    
         
            +
                    end
         
     | 
| 
      
 431 
     | 
    
         
            +
                  end
         
     | 
| 
      
 432 
     | 
    
         
            +
                end
         
     | 
| 
      
 433 
     | 
    
         
            +
             
     | 
| 
       232 
434 
     | 
    
         
             
                def rename_insn_types
         
     | 
| 
       233 
435 
     | 
    
         
             
                  @insns.each do |insn|
         
     | 
| 
       234 
436 
     | 
    
         
             
                    case insn.insn
         
     | 
| 
         @@ -331,7 +533,7 @@ module TypeProf 
     | 
|
| 
       331 
533 
     | 
    
         
             
                          break unless @insns[j + 1].check?(:putobject, [true])
         
     | 
| 
       332 
534 
     | 
    
         
             
                          break unless @insns[j + 2].check?(:getconstant) # TODO: support A::B::C
         
     | 
| 
       333 
535 
     | 
    
         
             
                          break unless @insns[j + 3].check?(:topn, [1])
         
     | 
| 
       334 
     | 
    
         
            -
                          break unless @insns[j + 4].check?(:send,  
     | 
| 
      
 536 
     | 
    
         
            +
                          break unless @insns[j + 4].check?(:send) && @insns[j + 4].operands[0].slice(:mid, :flag, :orig_argc) == {:mid=>:===, :flag=>20, :orig_argc=>1}
         
     | 
| 
       335 
537 
     | 
    
         
             
                          break unless @insns[j + 5].check?(:branch)
         
     | 
| 
       336 
538 
     | 
    
         
             
                          target_pc = @insns[j + 5].operands[1]
         
     | 
| 
       337 
539 
     | 
    
         
             
                          break unless @insns[target_pc].check?(:pop, [])
         
     |