typeprof 0.15.3 → 0.20.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (366) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +1 -1
  3. data/Gemfile.lock +5 -5
  4. data/doc/ide.md +81 -0
  5. data/doc/typeprof-for-ide-log.png +0 -0
  6. data/doc/typeprof-for-ide.png +0 -0
  7. data/exe/typeprof +5 -1
  8. data/lib/typeprof/analyzer.rb +235 -56
  9. data/lib/typeprof/arguments.rb +1 -0
  10. data/lib/typeprof/builtin.rb +23 -23
  11. data/lib/typeprof/cli.rb +23 -5
  12. data/lib/typeprof/code-range.rb +177 -0
  13. data/lib/typeprof/config.rb +41 -20
  14. data/lib/typeprof/container-type.rb +3 -0
  15. data/lib/typeprof/export.rb +191 -15
  16. data/lib/typeprof/import.rb +33 -9
  17. data/lib/typeprof/insns-def.rb +1 -0
  18. data/lib/typeprof/iseq.rb +224 -17
  19. data/lib/typeprof/lsp.rb +884 -0
  20. data/lib/typeprof/method.rb +15 -11
  21. data/lib/typeprof/type.rb +50 -42
  22. data/lib/typeprof/utils.rb +18 -1
  23. data/lib/typeprof/version.rb +1 -1
  24. data/lib/typeprof.rb +3 -5
  25. data/typeprof-lsp +3 -0
  26. data/typeprof.gemspec +2 -2
  27. data/vscode/.gitignore +5 -0
  28. data/vscode/.vscode/launch.json +16 -0
  29. data/vscode/.vscodeignore +7 -0
  30. data/vscode/README.md +22 -0
  31. data/vscode/development.md +31 -0
  32. data/vscode/package-lock.json +3249 -0
  33. data/vscode/package.json +71 -0
  34. data/vscode/sandbox/test.rb +24 -0
  35. data/vscode/src/extension.ts +299 -0
  36. data/vscode/tsconfig.json +15 -0
  37. metadata +21 -334
  38. data/smoke/alias.rb +0 -31
  39. data/smoke/alias2.rb +0 -21
  40. data/smoke/any-cbase.rb +0 -5
  41. data/smoke/any1.rb +0 -16
  42. data/smoke/any2.rb +0 -18
  43. data/smoke/arguments.rb +0 -17
  44. data/smoke/arguments2.rb +0 -56
  45. data/smoke/array-each.rb +0 -15
  46. data/smoke/array-each2.rb +0 -16
  47. data/smoke/array-each3.rb +0 -13
  48. data/smoke/array-ltlt.rb +0 -14
  49. data/smoke/array-ltlt2.rb +0 -17
  50. data/smoke/array-map.rb +0 -12
  51. data/smoke/array-map2.rb +0 -11
  52. data/smoke/array-map3.rb +0 -23
  53. data/smoke/array-mul.rb +0 -18
  54. data/smoke/array-plus1.rb +0 -11
  55. data/smoke/array-plus2.rb +0 -16
  56. data/smoke/array-pop.rb +0 -12
  57. data/smoke/array-range-aref.rb +0 -71
  58. data/smoke/array-replace.rb +0 -13
  59. data/smoke/array-s-aref.rb +0 -12
  60. data/smoke/array1.rb +0 -27
  61. data/smoke/array10.rb +0 -15
  62. data/smoke/array11.rb +0 -14
  63. data/smoke/array12.rb +0 -25
  64. data/smoke/array13.rb +0 -31
  65. data/smoke/array14.rb +0 -14
  66. data/smoke/array15.rb +0 -16
  67. data/smoke/array2.rb +0 -28
  68. data/smoke/array3.rb +0 -26
  69. data/smoke/array4.rb +0 -15
  70. data/smoke/array5.rb +0 -14
  71. data/smoke/array6.rb +0 -17
  72. data/smoke/array7.rb +0 -14
  73. data/smoke/array8.rb +0 -13
  74. data/smoke/array9.rb +0 -13
  75. data/smoke/attr-module.rb +0 -24
  76. data/smoke/attr-vis.rb +0 -43
  77. data/smoke/attr-vis.rbs +0 -4
  78. data/smoke/attr.rb +0 -28
  79. data/smoke/autoload.rb +0 -14
  80. data/smoke/backtrace.rb +0 -33
  81. data/smoke/block-ambiguous.rb +0 -37
  82. data/smoke/block-args1-rest.rb +0 -64
  83. data/smoke/block-args1.rb +0 -60
  84. data/smoke/block-args2-rest.rb +0 -64
  85. data/smoke/block-args2.rb +0 -60
  86. data/smoke/block-args3-rest.rb +0 -75
  87. data/smoke/block-args3.rb +0 -71
  88. data/smoke/block-blockarg.rb +0 -28
  89. data/smoke/block-kwarg.rb +0 -53
  90. data/smoke/block1.rb +0 -23
  91. data/smoke/block10.rb +0 -15
  92. data/smoke/block11.rb +0 -40
  93. data/smoke/block12.rb +0 -23
  94. data/smoke/block13.rb +0 -9
  95. data/smoke/block13.rbs +0 -3
  96. data/smoke/block14.rb +0 -18
  97. data/smoke/block2.rb +0 -15
  98. data/smoke/block3.rb +0 -38
  99. data/smoke/block4.rb +0 -19
  100. data/smoke/block5.rb +0 -21
  101. data/smoke/block6.rb +0 -21
  102. data/smoke/block7.rb +0 -19
  103. data/smoke/block8.rb +0 -28
  104. data/smoke/block9.rb +0 -13
  105. data/smoke/block_given.rb +0 -37
  106. data/smoke/blown.rb +0 -13
  107. data/smoke/break1.rb +0 -19
  108. data/smoke/break2.rb +0 -16
  109. data/smoke/break3.rb +0 -13
  110. data/smoke/break4.rb +0 -17
  111. data/smoke/case.rb +0 -17
  112. data/smoke/case2.rb +0 -18
  113. data/smoke/case3.rb +0 -17
  114. data/smoke/class-hierarchy.rb +0 -54
  115. data/smoke/class-hierarchy2.rb +0 -27
  116. data/smoke/class-new.rb +0 -15
  117. data/smoke/class.rb +0 -7
  118. data/smoke/class_eval.rb +0 -22
  119. data/smoke/class_instance_var.rb +0 -9
  120. data/smoke/class_method.rb +0 -25
  121. data/smoke/class_method2.rb +0 -21
  122. data/smoke/class_method3.rb +0 -29
  123. data/smoke/constant1.rb +0 -46
  124. data/smoke/constant2.rb +0 -36
  125. data/smoke/constant3.rb +0 -10
  126. data/smoke/constant4.rb +0 -12
  127. data/smoke/context-sensitive1.rb +0 -13
  128. data/smoke/cvar.rb +0 -31
  129. data/smoke/cvar2.rb +0 -17
  130. data/smoke/define_method.rb +0 -16
  131. data/smoke/define_method2.rb +0 -18
  132. data/smoke/define_method3.rb +0 -14
  133. data/smoke/define_method3.rbs +0 -3
  134. data/smoke/define_method4.rb +0 -15
  135. data/smoke/define_method4.rbs +0 -3
  136. data/smoke/define_method5.rb +0 -12
  137. data/smoke/define_method6.rb +0 -19
  138. data/smoke/define_method7.rb +0 -18
  139. data/smoke/demo.rb +0 -81
  140. data/smoke/demo1.rb +0 -17
  141. data/smoke/demo10.rb +0 -21
  142. data/smoke/demo11.rb +0 -12
  143. data/smoke/demo2.rb +0 -15
  144. data/smoke/demo3.rb +0 -17
  145. data/smoke/demo4.rb +0 -27
  146. data/smoke/demo5.rb +0 -16
  147. data/smoke/demo6.rb +0 -22
  148. data/smoke/demo7.rb +0 -15
  149. data/smoke/demo8.rb +0 -19
  150. data/smoke/demo9.rb +0 -18
  151. data/smoke/dummy-execution1.rb +0 -15
  152. data/smoke/dummy-execution2.rb +0 -16
  153. data/smoke/dummy_element.rb +0 -14
  154. data/smoke/ensure1.rb +0 -21
  155. data/smoke/enum_for.rb +0 -15
  156. data/smoke/enum_for2.rb +0 -17
  157. data/smoke/enumerator.rb +0 -16
  158. data/smoke/expandarray1.rb +0 -23
  159. data/smoke/expandarray2.rb +0 -24
  160. data/smoke/extended.rb +0 -38
  161. data/smoke/fib.rb +0 -28
  162. data/smoke/flip-flop.rb +0 -28
  163. data/smoke/flow1.rb +0 -17
  164. data/smoke/flow10.rb +0 -17
  165. data/smoke/flow11.rb +0 -17
  166. data/smoke/flow2.rb +0 -15
  167. data/smoke/flow3.rb +0 -15
  168. data/smoke/flow4.rb +0 -5
  169. data/smoke/flow5.rb +0 -20
  170. data/smoke/flow6.rb +0 -20
  171. data/smoke/flow7.rb +0 -21
  172. data/smoke/flow8.rb +0 -14
  173. data/smoke/flow9.rb +0 -12
  174. data/smoke/for.rb +0 -9
  175. data/smoke/freeze.rb +0 -12
  176. data/smoke/function.rb +0 -17
  177. data/smoke/gvar.rb +0 -14
  178. data/smoke/gvar2.rb +0 -15
  179. data/smoke/gvar2.rbs +0 -1
  180. data/smoke/hash-bot.rb +0 -12
  181. data/smoke/hash-fetch.rb +0 -28
  182. data/smoke/hash-merge-bang.rb +0 -12
  183. data/smoke/hash1.rb +0 -20
  184. data/smoke/hash2.rb +0 -13
  185. data/smoke/hash3.rb +0 -14
  186. data/smoke/hash4.rb +0 -11
  187. data/smoke/hash5.rb +0 -14
  188. data/smoke/huge_union.rb +0 -86
  189. data/smoke/identifier_keywords.rb +0 -17
  190. data/smoke/included.rb +0 -38
  191. data/smoke/inheritance.rb +0 -34
  192. data/smoke/inheritance2.rb +0 -35
  193. data/smoke/inherited.rb +0 -26
  194. data/smoke/initialize.rb +0 -28
  195. data/smoke/instance_eval.rb +0 -18
  196. data/smoke/instance_eval2.rb +0 -10
  197. data/smoke/instance_eval3.rb +0 -25
  198. data/smoke/instance_eval4.rb +0 -12
  199. data/smoke/int_times.rb +0 -15
  200. data/smoke/integer.rb +0 -11
  201. data/smoke/ivar.rb +0 -31
  202. data/smoke/ivar2.rb +0 -30
  203. data/smoke/ivar3.rb +0 -17
  204. data/smoke/ivar3.rbs +0 -3
  205. data/smoke/ivar4.rb +0 -21
  206. data/smoke/kernel-class.rb +0 -13
  207. data/smoke/keyword1.rb +0 -12
  208. data/smoke/keyword2.rb +0 -12
  209. data/smoke/keyword3.rb +0 -12
  210. data/smoke/keyword4.rb +0 -12
  211. data/smoke/keyword5.rb +0 -16
  212. data/smoke/kwrest.rb +0 -13
  213. data/smoke/kwrest.rbs +0 -3
  214. data/smoke/kwsplat1.rb +0 -43
  215. data/smoke/kwsplat2.rb +0 -13
  216. data/smoke/lit-complex.rb +0 -10
  217. data/smoke/lit-encoding.rb +0 -10
  218. data/smoke/manual-rbs.rb +0 -29
  219. data/smoke/manual-rbs.rbs +0 -3
  220. data/smoke/manual-rbs2.rb +0 -21
  221. data/smoke/manual-rbs2.rbs +0 -8
  222. data/smoke/manual-rbs3.rb +0 -13
  223. data/smoke/manual-rbs3.rbs +0 -3
  224. data/smoke/masgn1.rb +0 -14
  225. data/smoke/masgn2.rb +0 -18
  226. data/smoke/masgn3.rb +0 -13
  227. data/smoke/method_in_branch.rb +0 -23
  228. data/smoke/method_missing.rb +0 -29
  229. data/smoke/module1.rb +0 -29
  230. data/smoke/module2.rb +0 -28
  231. data/smoke/module3.rb +0 -33
  232. data/smoke/module4.rb +0 -35
  233. data/smoke/module5.rb +0 -17
  234. data/smoke/module6.rb +0 -40
  235. data/smoke/module_function1.rb +0 -29
  236. data/smoke/module_function2.rb +0 -29
  237. data/smoke/multiple-include.rb +0 -15
  238. data/smoke/multiple-superclass.rb +0 -28
  239. data/smoke/next1.rb +0 -21
  240. data/smoke/next2.rb +0 -17
  241. data/smoke/noname.rb +0 -9
  242. data/smoke/object-send1.rb +0 -23
  243. data/smoke/object-send2.rb +0 -10
  244. data/smoke/object-send3.rb +0 -18
  245. data/smoke/once.rb +0 -13
  246. data/smoke/optional1.rb +0 -14
  247. data/smoke/optional2.rb +0 -16
  248. data/smoke/optional3.rb +0 -11
  249. data/smoke/or_raise.rb +0 -18
  250. data/smoke/parameterizedd-self.rb +0 -20
  251. data/smoke/parameterizedd-self2.rb +0 -15
  252. data/smoke/pathname1.rb +0 -14
  253. data/smoke/pathname2.rb +0 -14
  254. data/smoke/pattern-match1.rb +0 -19
  255. data/smoke/pattern-match2.rb +0 -16
  256. data/smoke/prepend1.rb +0 -33
  257. data/smoke/prepend2.rb +0 -10
  258. data/smoke/prepend2.rbs +0 -9
  259. data/smoke/primitive_method.rb +0 -19
  260. data/smoke/printf.rb +0 -20
  261. data/smoke/proc.rb +0 -20
  262. data/smoke/proc2.rb +0 -17
  263. data/smoke/proc3.rb +0 -15
  264. data/smoke/proc4.rb +0 -12
  265. data/smoke/proc5.rb +0 -19
  266. data/smoke/proc6.rb +0 -13
  267. data/smoke/proc7.rb +0 -32
  268. data/smoke/public.rb +0 -38
  269. data/smoke/range.rb +0 -14
  270. data/smoke/rbs-alias.rb +0 -10
  271. data/smoke/rbs-alias.rbs +0 -4
  272. data/smoke/rbs-attr.rb +0 -27
  273. data/smoke/rbs-attr.rbs +0 -5
  274. data/smoke/rbs-attr2.rb +0 -11
  275. data/smoke/rbs-attr2.rbs +0 -3
  276. data/smoke/rbs-extend.rb +0 -10
  277. data/smoke/rbs-extend.rbs +0 -7
  278. data/smoke/rbs-interface.rb +0 -25
  279. data/smoke/rbs-interface.rbs +0 -12
  280. data/smoke/rbs-module.rb +0 -26
  281. data/smoke/rbs-module.rbs +0 -4
  282. data/smoke/rbs-opt-and-rest.rb +0 -10
  283. data/smoke/rbs-opt-and-rest.rbs +0 -3
  284. data/smoke/rbs-proc1.rb +0 -10
  285. data/smoke/rbs-proc1.rbs +0 -3
  286. data/smoke/rbs-proc2.rb +0 -21
  287. data/smoke/rbs-proc2.rbs +0 -3
  288. data/smoke/rbs-proc3.rb +0 -14
  289. data/smoke/rbs-proc3.rbs +0 -4
  290. data/smoke/rbs-record.rb +0 -18
  291. data/smoke/rbs-record.rbs +0 -4
  292. data/smoke/rbs-tyvar.rb +0 -19
  293. data/smoke/rbs-tyvar.rbs +0 -5
  294. data/smoke/rbs-tyvar2.rb +0 -21
  295. data/smoke/rbs-tyvar2.rbs +0 -9
  296. data/smoke/rbs-tyvar3.rb +0 -18
  297. data/smoke/rbs-tyvar3.rbs +0 -5
  298. data/smoke/rbs-tyvar4.rb +0 -37
  299. data/smoke/rbs-tyvar5.rb +0 -13
  300. data/smoke/rbs-tyvar5.rbs +0 -8
  301. data/smoke/rbs-tyvar6.rb +0 -18
  302. data/smoke/rbs-tyvar6.rbs +0 -12
  303. data/smoke/rbs-tyvar7.rb +0 -12
  304. data/smoke/rbs-tyvar7.rbs +0 -7
  305. data/smoke/rbs-vars.rb +0 -35
  306. data/smoke/rbs-vars.rbs +0 -7
  307. data/smoke/redo1.rb +0 -22
  308. data/smoke/redo2.rb +0 -23
  309. data/smoke/req-keyword.rb +0 -13
  310. data/smoke/require1.rb +0 -13
  311. data/smoke/require2.rb +0 -13
  312. data/smoke/rescue1.rb +0 -21
  313. data/smoke/rescue2.rb +0 -23
  314. data/smoke/rescue3.rb +0 -20
  315. data/smoke/rescue4.rb +0 -17
  316. data/smoke/respond_to.rb +0 -23
  317. data/smoke/rest-farg.rb +0 -11
  318. data/smoke/rest1.rb +0 -26
  319. data/smoke/rest2.rb +0 -31
  320. data/smoke/rest3.rb +0 -37
  321. data/smoke/rest4.rb +0 -19
  322. data/smoke/rest5.rb +0 -11
  323. data/smoke/rest6.rb +0 -12
  324. data/smoke/retry1.rb +0 -21
  325. data/smoke/return.rb +0 -14
  326. data/smoke/reveal.rb +0 -13
  327. data/smoke/simple.rb +0 -12
  328. data/smoke/singleton_class.rb +0 -8
  329. data/smoke/singleton_method.rb +0 -12
  330. data/smoke/step.rb +0 -18
  331. data/smoke/string-split.rb +0 -12
  332. data/smoke/struct-keyword_init.rb +0 -10
  333. data/smoke/struct.rb +0 -13
  334. data/smoke/struct2.rb +0 -25
  335. data/smoke/struct3.rb +0 -14
  336. data/smoke/struct4.rb +0 -7
  337. data/smoke/struct5.rb +0 -16
  338. data/smoke/struct6.rb +0 -15
  339. data/smoke/struct7.rb +0 -17
  340. data/smoke/stub-keyword.rb +0 -10
  341. data/smoke/super1.rb +0 -69
  342. data/smoke/super2.rb +0 -16
  343. data/smoke/super3.rb +0 -20
  344. data/smoke/super4.rb +0 -45
  345. data/smoke/super5.rb +0 -38
  346. data/smoke/svar1.rb +0 -13
  347. data/smoke/symbol-proc-attr.rb +0 -22
  348. data/smoke/symbol-proc-attr2.rb +0 -15
  349. data/smoke/symbol-proc-bot.rb +0 -13
  350. data/smoke/symbol-proc.rb +0 -25
  351. data/smoke/tap1.rb +0 -18
  352. data/smoke/toplevel.rb +0 -13
  353. data/smoke/two-map.rb +0 -18
  354. data/smoke/type_var.rb +0 -11
  355. data/smoke/typed_method.rb +0 -16
  356. data/smoke/uninitialize-var.rb +0 -13
  357. data/smoke/union-recv.rb +0 -35
  358. data/smoke/user-demo.rb +0 -15
  359. data/smoke/wrong-extend.rb +0 -27
  360. data/smoke/wrong-include.rb +0 -27
  361. data/smoke/wrong-include2.rb +0 -17
  362. data/smoke/wrong-rbs.rb +0 -15
  363. data/smoke/wrong-rbs.rbs +0 -7
  364. data/testbed/ao.rb +0 -297
  365. data/testbed/diff-lcs-entrypoint.rb +0 -4
  366. data/testbed/goodcheck-Gemfile.lock +0 -51
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,67 @@ 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
+
37
+ unless defined?(RubyVM::InstructionSequence)
38
+ puts "Currently, TypeProf can work on a Ruby implementation that supports RubyVM::InstructionSequence, such as CRuby."
39
+ exit 1
40
+ end
41
+
23
42
  if str
43
+ node = RubyVM::AbstractSyntaxTree.parse(str, **parse_opts)
24
44
  iseq = RubyVM::InstructionSequence.compile(str, path, **opt)
25
45
  else
46
+ node = RubyVM::AbstractSyntaxTree.parse_file(path, **parse_opts)
26
47
  iseq = RubyVM::InstructionSequence.compile_file(path, **opt)
27
48
  end
28
49
 
29
- return new(iseq.to_a)
50
+ node_id2node = {}
51
+ build_ast_node_id_table(node, node_id2node) if RICH_AST
52
+
53
+ file_info = FileInfo.new(node_id2node, CodeRangeTable.new, CodeRangeTable.new, [])
54
+ iseq_rb = new(iseq.to_a, file_info)
55
+ iseq_rb.collect_local_variable_info(file_info) if RICH_AST
56
+ file_info.created_iseqs.each do |iseq|
57
+ iseq.unify_instructions
58
+ end
59
+
60
+ return iseq_rb, file_info.definition_table, file_info.caller_table
61
+ end
62
+
63
+ private def build_ast_node_id_table(node, tbl = {})
64
+ tbl[node.node_id] = node
65
+ node.children.each do |child|
66
+ build_ast_node_id_table(child, tbl) if child.is_a?(RubyVM::AbstractSyntaxTree::Node)
67
+ end
68
+ tbl
69
+ end
70
+
71
+ def code_range_from_node(node)
72
+ CodeRange.new(
73
+ CodeLocation.new(node.first_lineno, node.first_column),
74
+ CodeLocation.new(node.last_lineno, node.last_column),
75
+ )
76
+ end
77
+
78
+ def find_node_by_id(node, id)
79
+ node = RubyVM::AbstractSyntaxTree.parse(node) if node.is_a?(String)
80
+
81
+ return node if id == node.node_id
82
+
83
+ node.children.each do |child|
84
+ if child.is_a?(RubyVM::AbstractSyntaxTree::Node)
85
+ ret = find_node_by_id(child, id)
86
+ return ret if ret
87
+ end
88
+ end
89
+
90
+ nil
30
91
  end
31
92
  end
32
93
 
33
- Insn = Struct.new(:insn, :operands, :lineno)
94
+ Insn = Struct.new(:insn, :operands, :lineno, :code_range, :definitions)
34
95
  class Insn
35
96
  def check?(insn_cmp, operands_cmp = nil)
36
97
  return insn == insn_cmp && (!operands_cmp || operands == operands_cmp)
@@ -39,14 +100,19 @@ module TypeProf
39
100
 
40
101
  ISEQ_FRESH_ID = [0]
41
102
 
42
- def initialize(iseq)
103
+ def initialize(iseq, file_info)
104
+ file_info.created_iseqs << self
105
+
43
106
  @id = (ISEQ_FRESH_ID[0] += 1)
44
107
 
45
- _magic, _major_version, _minor_version, _format_type, _misc,
108
+ _magic, _major_version, _minor_version, _format_type, misc,
46
109
  @name, @path, @absolute_path, @start_lineno, @type,
47
110
  @locals, @fargs_format, catch_table, insns = *iseq
48
111
 
49
- convert_insns(insns)
112
+ fl, fc, ll, lc = misc[:code_location]
113
+ @iseq_code_range = CodeRange.new(CodeLocation.new(fl, fc), CodeLocation.new(ll, lc))
114
+
115
+ convert_insns(insns, misc[:node_ids] || [], file_info)
50
116
 
51
117
  add_body_start_marker(insns)
52
118
 
@@ -54,13 +120,13 @@ module TypeProf
54
120
 
55
121
  labels = create_label_table(insns)
56
122
 
57
- @insns = setup_insns(insns, labels)
123
+ @insns = setup_insns(insns, labels, file_info)
58
124
 
59
125
  @fargs_format[:opt] = @fargs_format[:opt].map {|l| labels[l] } if @fargs_format[:opt]
60
126
 
61
127
  @catch_table = []
62
128
  catch_table.map do |type, iseq, first, last, cont, stack_depth|
63
- iseq = iseq ? ISeq.new(iseq) : nil
129
+ iseq = iseq ? ISeq.new(iseq, file_info) : nil
64
130
  target = labels[cont]
65
131
  entry = [type, iseq, target, stack_depth]
66
132
  labels[first].upto(labels[last]) do |i|
@@ -69,17 +135,78 @@ module TypeProf
69
135
  end
70
136
  end
71
137
 
138
+ def_node_id = misc[:def_node_id]
139
+ if def_node_id && file_info.node_id2node[def_node_id] && (@type == :method || @type == :block)
140
+ def_node = file_info.node_id2node[def_node_id]
141
+ method_name_token_range = extract_method_name_token_range(def_node)
142
+ if method_name_token_range
143
+ @callers = Utils::MutableSet.new
144
+ file_info.caller_table[method_name_token_range] = @callers
145
+ end
146
+ end
147
+
72
148
  rename_insn_types
149
+ end
73
150
 
74
- unify_instructions
151
+ def extract_method_name_token_range(node)
152
+ case @type
153
+ when :method
154
+ regex = if node.type == :DEFS
155
+ /^def\s+(?:\w+)\s*\.\s*(\w+)/
156
+ else
157
+ /^def\s+(\w+)/
158
+ end
159
+ return nil unless node.source =~ regex
160
+ zero_loc = CodeLocation.new(1, 0)
161
+ name_start = $~.begin(1)
162
+ name_length = $~.end(1) - name_start
163
+ name_head_loc = zero_loc.advance_cursor(name_start, node.source)
164
+ name_tail_loc = name_head_loc.advance_cursor(name_length, node.source)
165
+ return CodeRange.new(
166
+ CodeLocation.new(
167
+ node.first_lineno + (name_head_loc.lineno - 1),
168
+ name_head_loc.lineno == 1 ? node.first_column + name_head_loc.column : name_head_loc.column
169
+ ),
170
+ CodeLocation.new(
171
+ node.first_lineno + (name_tail_loc.lineno - 1),
172
+ name_tail_loc.lineno == 1 ? node.first_column + name_tail_loc.column : name_tail_loc.column
173
+ ),
174
+ )
175
+ when :block
176
+ return ISeq.code_range_from_node(node)
177
+ end
75
178
  end
76
179
 
77
180
  def source_location(pc)
78
181
  "#{ @path }:#{ @insns[pc].lineno }"
79
182
  end
80
183
 
184
+ def detailed_source_location(pc)
185
+ code_range = @insns[pc].code_range
186
+ if code_range
187
+ [@path, code_range]
188
+ else
189
+ [@path]
190
+ end
191
+ end
192
+
193
+ def add_called_iseq(pc, callee_iseq)
194
+ if callee_iseq && @insns[pc].definitions
195
+ @insns[pc].definitions << [callee_iseq.path, callee_iseq.iseq_code_range]
196
+ end
197
+ if callee_iseq.callers
198
+ callee_iseq.callers << [@path, @insns[pc].code_range]
199
+ end
200
+ end
201
+
202
+ def add_def_loc(pc, detailed_loc)
203
+ if detailed_loc && @insns[pc].definitions
204
+ @insns[pc].definitions << detailed_loc
205
+ end
206
+ end
207
+
81
208
  attr_reader :name, :path, :absolute_path, :start_lineno, :type, :locals, :fargs_format, :catch_table, :insns
82
- attr_reader :id
209
+ attr_reader :id, :iseq_code_range, :callers
83
210
 
84
211
  def pretty_print(q)
85
212
  q.text "ISeq["
@@ -118,7 +245,7 @@ module TypeProf
118
245
  end
119
246
 
120
247
  # Remove lineno entry and convert instructions to Insn instances
121
- def convert_insns(insns)
248
+ def convert_insns(insns, node_ids, file_info)
122
249
  ninsns = []
123
250
  lineno = 0
124
251
  insns.each do |e|
@@ -129,7 +256,25 @@ module TypeProf
129
256
  ninsns << e
130
257
  when Array
131
258
  insn, *operands = e
132
- ninsns << Insn.new(insn, operands, lineno)
259
+ node_id = node_ids.shift
260
+ node = file_info.node_id2node[node_id]
261
+ if node
262
+ code_range = ISeq.code_range_from_node(node)
263
+ case insn
264
+ when :send, :invokesuper
265
+ opt, blk_iseq = operands
266
+ opt[:node_id] = node_id
267
+ if blk_iseq
268
+ misc = blk_iseq[4] # iseq's "misc" field
269
+ misc[:def_node_id] = node_id
270
+ end
271
+ when :definemethod, :definesmethod
272
+ iseq = operands[1]
273
+ misc = iseq[4] # iseq's "misc" field
274
+ misc[:def_node_id] = node_id
275
+ end
276
+ end
277
+ ninsns << Insn.new(insn, operands, lineno, code_range, nil)
133
278
  else
134
279
  raise "unknown iseq entry: #{ e }"
135
280
  end
@@ -156,7 +301,7 @@ module TypeProf
156
301
  i = insns.index(label) + 1
157
302
  end
158
303
 
159
- insns.insert(i, Insn.new(:_iseq_body_start, [], @start_lineno))
304
+ insns.insert(i, Insn.new(:_iseq_body_start, [], @start_lineno, nil, nil))
160
305
  end
161
306
  end
162
307
 
@@ -198,7 +343,7 @@ module TypeProf
198
343
  labels
199
344
  end
200
345
 
201
- def setup_insns(insns, labels)
346
+ def setup_insns(insns, labels, file_info)
202
347
  ninsns = []
203
348
  insns.each do |e|
204
349
  case e
@@ -208,7 +353,7 @@ module TypeProf
208
353
  operands = (INSN_TABLE[e.insn] || []).zip(e.operands).map do |type, operand|
209
354
  case type
210
355
  when "ISEQ"
211
- operand && ISeq.new(operand)
356
+ operand && ISeq.new(operand, file_info)
212
357
  when "lindex_t", "rb_num_t", "VALUE", "ID", "GENTRY", "CALL_DATA"
213
358
  operand
214
359
  when "OFFSET"
@@ -221,7 +366,12 @@ module TypeProf
221
366
  end
222
367
  end
223
368
 
224
- ninsns << Insn.new(e.insn, operands, e.lineno)
369
+ if e.code_range && should_collect_defs(e.insn)
370
+ definition = Utils::MutableSet.new
371
+ file_info.definition_table[e.code_range] = definition
372
+ end
373
+
374
+ ninsns << Insn.new(e.insn, operands, e.lineno, e.code_range, definition)
225
375
  else
226
376
  raise "unknown iseq entry: #{ e }"
227
377
  end
@@ -229,6 +379,63 @@ module TypeProf
229
379
  ninsns
230
380
  end
231
381
 
382
+ def should_collect_defs(insn_kind)
383
+ case insn_kind
384
+ when :send, :getinstancevariable, :getconstant
385
+ return true
386
+ else
387
+ return false
388
+ end
389
+ end
390
+
391
+ # Collect local variable use and definition info recursively
392
+ def collect_local_variable_info(file_info, absolute_level = 0, parent_variable_tables = {})
393
+ # e.g.
394
+ # variable_tables[abs_level][idx] = [[path, code_range]]
395
+ current_variables = []
396
+ variable_tables = parent_variable_tables.merge({
397
+ absolute_level => current_variables
398
+ })
399
+
400
+ dummy_def_range = CodeRange.new(
401
+ CodeLocation.new(@start_lineno, 0),
402
+ CodeLocation.new(@start_lineno, 1),
403
+ )
404
+ # Fill tail elements with parameters
405
+ (@fargs_format[:lead_num] || 0).times do |offset|
406
+ current_variables[VM_ENV_DATA_SIZE + @locals.length - offset - 1] ||= Utils::MutableSet.new
407
+ current_variables[VM_ENV_DATA_SIZE + @locals.length - offset - 1] << [@path, dummy_def_range]
408
+ end
409
+
410
+ @insns.each do |insn|
411
+ next unless insn.insn == :getlocal || insn.insn == :setlocal
412
+
413
+ idx = insn.operands[0]
414
+ # note: level is relative value to the current level
415
+ level = insn.operands[1]
416
+ target_abs_level = absolute_level - level
417
+ variable_tables[target_abs_level] ||= {}
418
+ variable_tables[target_abs_level][idx] ||= Utils::MutableSet.new
419
+
420
+ case insn.insn
421
+ when :setlocal
422
+ variable_tables[target_abs_level][idx] << [path, insn.code_range]
423
+ when :getlocal
424
+ file_info.definition_table[insn.code_range] = variable_tables[target_abs_level][idx]
425
+ end
426
+ end
427
+
428
+ @insns.each do |insn|
429
+ insn.operands.each do |operand|
430
+ next unless operand.is_a?(ISeq)
431
+ operand.collect_local_variable_info(
432
+ file_info, absolute_level + 1,
433
+ variable_tables
434
+ )
435
+ end
436
+ end
437
+ end
438
+
232
439
  def rename_insn_types
233
440
  @insns.each do |insn|
234
441
  case insn.insn
@@ -331,7 +538,7 @@ module TypeProf
331
538
  break unless @insns[j + 1].check?(:putobject, [true])
332
539
  break unless @insns[j + 2].check?(:getconstant) # TODO: support A::B::C
333
540
  break unless @insns[j + 3].check?(:topn, [1])
334
- break unless @insns[j + 4].check?(:send, [{:mid=>:===, :flag=>20, :orig_argc=>1}, nil])
541
+ break unless @insns[j + 4].check?(:send) && @insns[j + 4].operands[0].slice(:mid, :flag, :orig_argc) == {:mid=>:===, :flag=>20, :orig_argc=>1}
335
542
  break unless @insns[j + 5].check?(:branch)
336
543
  target_pc = @insns[j + 5].operands[1]
337
544
  break unless @insns[target_pc].check?(:pop, [])
@@ -497,7 +704,7 @@ module TypeProf
497
704
  sp += 1
498
705
  when :newhashfromarray
499
706
  raise NotImplementedError, "newhashfromarray"
500
- when :newrange, :tostring
707
+ when :newrange, :tostring, :anytostring
501
708
  sp -= 2
502
709
  return nil if sp <= 0
503
710
  sp += 1