typeprof 0.2.0 → 0.5.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.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +1 -2
  3. data/.gitignore +1 -0
  4. data/Gemfile +2 -2
  5. data/Gemfile.lock +10 -21
  6. data/LICENSE +21 -0
  7. data/README.md +6 -0
  8. data/doc/demo.md +398 -0
  9. data/doc/doc.ja.md +6 -1
  10. data/doc/doc.md +7 -2
  11. data/exe/typeprof +2 -1
  12. data/lib/typeprof.rb +9 -0
  13. data/lib/typeprof/analyzer.rb +427 -325
  14. data/lib/typeprof/arguments.rb +405 -0
  15. data/lib/typeprof/block.rb +136 -0
  16. data/lib/typeprof/builtin.rb +36 -25
  17. data/lib/typeprof/cli.rb +51 -98
  18. data/lib/typeprof/config.rb +114 -0
  19. data/lib/typeprof/container-type.rb +280 -92
  20. data/lib/typeprof/export.rb +197 -108
  21. data/lib/typeprof/import.rb +134 -80
  22. data/lib/typeprof/iseq.rb +42 -1
  23. data/lib/typeprof/method.rb +178 -82
  24. data/lib/typeprof/type.rb +228 -369
  25. data/lib/typeprof/utils.rb +4 -18
  26. data/lib/typeprof/version.rb +3 -0
  27. data/smoke/arguments2.rb +55 -0
  28. data/smoke/array-each3.rb +1 -4
  29. data/smoke/array12.rb +1 -1
  30. data/smoke/array6.rb +1 -0
  31. data/smoke/block-ambiguous.rb +36 -0
  32. data/smoke/block-args1-rest.rb +62 -0
  33. data/smoke/block-args1.rb +59 -0
  34. data/smoke/block-args2-rest.rb +62 -0
  35. data/smoke/block-args2.rb +59 -0
  36. data/smoke/block-args3-rest.rb +73 -0
  37. data/smoke/block-args3.rb +70 -0
  38. data/smoke/block-blockarg.rb +27 -0
  39. data/smoke/block-kwarg.rb +52 -0
  40. data/smoke/block10.rb +1 -1
  41. data/smoke/block11.rb +1 -1
  42. data/smoke/block14.rb +17 -0
  43. data/smoke/block4.rb +2 -2
  44. data/smoke/block5.rb +1 -0
  45. data/smoke/block6.rb +1 -1
  46. data/smoke/block7.rb +0 -2
  47. data/smoke/block8.rb +2 -2
  48. data/smoke/block9.rb +1 -1
  49. data/smoke/blown.rb +1 -1
  50. data/smoke/class-hierarchy.rb +54 -0
  51. data/smoke/class-hierarchy2.rb +27 -0
  52. data/smoke/constant1.rb +11 -6
  53. data/smoke/constant2.rb +2 -0
  54. data/smoke/cvar.rb +1 -0
  55. data/smoke/demo10.rb +1 -1
  56. data/smoke/demo8.rb +2 -2
  57. data/smoke/demo9.rb +1 -3
  58. data/smoke/flow7.rb +1 -7
  59. data/smoke/flow8.rb +13 -0
  60. data/smoke/hash-fetch.rb +3 -3
  61. data/smoke/hash-merge-bang.rb +11 -0
  62. data/smoke/hash1.rb +1 -1
  63. data/smoke/hash3.rb +1 -1
  64. data/smoke/hash4.rb +1 -1
  65. data/smoke/instance_eval.rb +1 -1
  66. data/smoke/int_times.rb +1 -1
  67. data/smoke/ivar2.rb +1 -1
  68. data/smoke/keyword3.rb +1 -2
  69. data/smoke/keyword4.rb +1 -1
  70. data/smoke/kwsplat1.rb +1 -1
  71. data/smoke/kwsplat2.rb +1 -1
  72. data/smoke/module4.rb +2 -0
  73. data/smoke/multiple-superclass.rb +4 -0
  74. data/smoke/next2.rb +1 -1
  75. data/smoke/optional1.rb +1 -1
  76. data/smoke/optional2.rb +1 -1
  77. data/smoke/optional3.rb +10 -0
  78. data/smoke/pattern-match1.rb +23 -0
  79. data/smoke/pattern-match2.rb +15 -0
  80. data/smoke/proc4.rb +1 -1
  81. data/smoke/rbs-extend.rb +9 -0
  82. data/smoke/rbs-extend.rbs +7 -0
  83. data/smoke/rbs-interface.rb +24 -0
  84. data/smoke/rbs-interface.rbs +12 -0
  85. data/smoke/rbs-proc1.rb +9 -0
  86. data/smoke/rbs-proc1.rbs +3 -0
  87. data/smoke/rbs-proc2.rb +20 -0
  88. data/smoke/rbs-proc2.rbs +3 -0
  89. data/smoke/rbs-proc3.rb +13 -0
  90. data/smoke/rbs-proc3.rbs +4 -0
  91. data/smoke/rbs-record.rb +17 -0
  92. data/smoke/rbs-record.rbs +4 -0
  93. data/smoke/rbs-tyvar.rb +18 -0
  94. data/smoke/rbs-tyvar.rbs +5 -0
  95. data/smoke/rbs-tyvar2.rb +20 -0
  96. data/smoke/rbs-tyvar2.rbs +9 -0
  97. data/smoke/rbs-tyvar3.rb +17 -0
  98. data/smoke/rbs-tyvar3.rbs +5 -0
  99. data/smoke/rbs-tyvar4.rb +36 -0
  100. data/smoke/rbs-tyvar5.rb +12 -0
  101. data/smoke/rbs-tyvar5.rbs +8 -0
  102. data/smoke/rest1.rb +1 -1
  103. data/smoke/rest2.rb +1 -1
  104. data/smoke/rest3.rb +1 -1
  105. data/smoke/rest5.rb +1 -1
  106. data/smoke/rest6.rb +1 -1
  107. data/smoke/retry1.rb +1 -1
  108. data/smoke/return.rb +1 -1
  109. data/smoke/singleton_method.rb +3 -0
  110. data/smoke/step.rb +1 -1
  111. data/smoke/struct.rb +4 -3
  112. data/smoke/struct3.rb +14 -0
  113. data/smoke/symbol-proc.rb +24 -0
  114. data/smoke/uninitialize-var.rb +12 -0
  115. data/smoke/user-demo.rb +15 -0
  116. data/smoke/wrong-extend.rb +1 -0
  117. data/typeprof.gemspec +4 -2
  118. metadata +53 -6
  119. data/run.sh +0 -3
  120. data/smoke/variadic1.rb.notyet +0 -5
@@ -54,21 +54,29 @@ module TypeProf
54
54
  ctn[ret_ty, ep, env]
55
55
  end
56
56
 
57
+ def vmcore_raise(recv, mid, aargs, ep, env, scratch, &ctn)
58
+ # no-op
59
+ end
60
+
57
61
  def lambda(recv, mid, aargs, ep, env, scratch, &ctn)
58
62
  ctn[aargs.blk_ty, ep, env]
59
63
  end
60
64
 
61
65
  def proc_call(recv, mid, aargs, ep, env, scratch, &ctn)
62
- given_block = env.static_env.blk_ty == recv
63
- scratch.do_invoke_block(given_block, recv, aargs, ep, env, &ctn)
66
+ scratch.do_invoke_block(recv, aargs, ep, env, &ctn)
64
67
  end
65
68
 
66
69
  def object_s_new(recv, mid, aargs, ep, env, scratch, &ctn)
67
- ty = Type::Instance.new(recv)
70
+ if recv.type_params.size >= 1
71
+ ty = Type::ContainerType.create_empty_instance(recv)
72
+ env, ty = scratch.localize_type(ty, env, ep, AllocationSite.new(ep).add_id(:object_s_new))
73
+ else
74
+ ty = Type::Instance.new(recv)
75
+ end
68
76
  meths = scratch.get_method(recv, false, :initialize)
69
77
  meths.flat_map do |meth|
70
78
  meth.do_send(ty, :initialize, aargs, ep, env, scratch) do |ret_ty, ep, env|
71
- ctn[Type::Instance.new(recv), ep, env]
79
+ ctn[ty, ep, env]
72
80
  end
73
81
  end
74
82
  end
@@ -118,7 +126,7 @@ module TypeProf
118
126
  else
119
127
  mid_ty = aargs.rest_ty
120
128
  end
121
- aargs = ActualArguments.new(aargs.lead_tys[1..-1], aargs.rest_ty, aargs.kw_ty, aargs.blk_ty)
129
+ aargs = ActualArguments.new(aargs.lead_tys[1..-1], aargs.rest_ty, aargs.kw_tys, aargs.blk_ty)
122
130
  found = false
123
131
  mid_ty.each_child do |mid|
124
132
  if mid.is_a?(Type::Symbol)
@@ -138,9 +146,8 @@ module TypeProf
138
146
  ctn[type.any, ep, env]
139
147
  return
140
148
  end
141
- naargs = ActualArguments.new([recv], Type.nil, nil, Type.nil)
142
- given_block = env.static_env.blk_ty == recv
143
- scratch.do_invoke_block(given_block, aargs.blk_ty, naargs, ep, env, replace_recv_ty: recv) do |_ret_ty, ep|
149
+ naargs = ActualArguments.new([recv], nil, {}, Type.nil)
150
+ scratch.do_invoke_block(aargs.blk_ty, naargs, ep, env, replace_recv_ty: recv) do |_ret_ty, ep|
144
151
  ctn[recv, ep, scratch.return_envs[ep]]
145
152
  end
146
153
  end
@@ -244,7 +251,10 @@ module TypeProf
244
251
  def array_aset(recv, mid, aargs, ep, env, scratch, &ctn)
245
252
  return ctn[Type.any, ep, env] unless recv.is_a?(Type::LocalArray)
246
253
 
247
- raise NotImplementedError if aargs.lead_tys.size != 2
254
+ if aargs.lead_tys.size != 2
255
+ #raise NotImplementedError # XXX
256
+ ctn[Type.any, ep, env]
257
+ end
248
258
 
249
259
  idx = aargs.lead_tys.first
250
260
  if idx.is_a?(Type::Literal)
@@ -256,7 +266,7 @@ module TypeProf
256
266
 
257
267
  ty = aargs.lead_tys.last
258
268
 
259
- env = scratch.update_container_elem_types(env, ep, recv.id) do |elems|
269
+ env = scratch.update_container_elem_types(env, ep, recv.id, recv.base_type) do |elems|
260
270
  elems.update(idx, ty)
261
271
  end
262
272
 
@@ -307,7 +317,7 @@ module TypeProf
307
317
  return ctn[ty, ep, env]
308
318
  end
309
319
 
310
- env = scratch.update_container_elem_types(env, ep, recv.id) do |elems|
320
+ env = scratch.update_container_elem_types(env, ep, recv.id, recv.base_type) do |elems|
311
321
  elems.update(idx, ty)
312
322
  end
313
323
 
@@ -316,14 +326,12 @@ module TypeProf
316
326
 
317
327
  def struct_initialize(recv, mid, aargs, ep, env, scratch, &ctn)
318
328
  recv = Type::Instance.new(recv)
319
- scratch.get_instance_variable(recv, :_members, ep, env) do |ty, nenv|
320
- ty = scratch.globalize_type(ty, nenv, ep)
321
- ty.elems.lead_tys.zip(aargs.lead_tys) do |sym, ty|
329
+ scratch.add_ivar_read!(recv, :_members, ep) do |member_ary_ty, ep|
330
+ member_ary_ty.elems.lead_tys.zip(aargs.lead_tys) do |sym, ty|
322
331
  ty ||= Type.nil
323
332
  scratch.set_instance_variable(recv, sym.sym, ty, ep, env)
324
333
  end
325
334
  end
326
- #scratch.set_instance_variable(recv, , ty, ep, env)
327
335
  ctn[recv, ep, env]
328
336
  end
329
337
 
@@ -337,9 +345,8 @@ module TypeProf
337
345
  return
338
346
  end
339
347
  if struct_klass != recv
340
- scratch.get_instance_variable(Type::Instance.new(struct_klass), :_members, ep, env) do |ty, nenv|
341
- ty = scratch.globalize_type(ty, nenv, ep)
342
- scratch.set_instance_variable(Type::Instance.new(recv), :_members, ty, ep, env)
348
+ scratch.add_ivar_read!(Type::Instance.new(struct_klass), :_members, ep) do |ty, ep|
349
+ scratch.add_ivar_write!(Type::Instance.new(recv), :_members, ty, ep)
343
350
  end
344
351
  end
345
352
  meths = scratch.get_method(recv, false, :initialize)
@@ -364,7 +371,7 @@ module TypeProf
364
371
  fields = fields.map {|field| Type::Symbol.new(field, Type::Instance.new(Type::Builtin[:sym])) }
365
372
  base_ty = Type::Instance.new(Type::Builtin[:ary])
366
373
  fields = Type::Array.new(Type::Array::Elements.new(fields), base_ty)
367
- scratch.set_instance_variable(Type::Instance.new(struct_klass), :_members, fields, ep, env)
374
+ scratch.add_ivar_write!(Type::Instance.new(struct_klass), :_members, fields, ep)
368
375
  #set_singleton_custom_method(struct_klass, :members, Builtin.method(:...))
369
376
 
370
377
  ctn[struct_klass, ep, env]
@@ -372,10 +379,10 @@ module TypeProf
372
379
 
373
380
  def self.file_load(path, ep, env, scratch, &ctn)
374
381
  iseq = ISeq.compile(path)
375
- callee_ep, callee_env = CLI.starting_state(iseq)
382
+ callee_ep, callee_env = TypeProf.starting_state(iseq)
376
383
  scratch.merge_env(callee_ep, callee_env)
377
384
 
378
- scratch.add_callsite!(callee_ep.ctx, nil, ep, env) do |_ret_ty, ep|
385
+ scratch.add_callsite!(callee_ep.ctx, ep, env) do |_ret_ty, ep|
379
386
  ret_ty = Type::Instance.new(Type::Builtin[:true])
380
387
  ctn[ret_ty, ep, env]
381
388
  end
@@ -482,8 +489,8 @@ module TypeProf
482
489
  def self.setup_initial_global_env(scratch)
483
490
  klass_basic_obj = scratch.new_class(nil, :BasicObject, [], :__root__, nil) # cbase, name, superclass
484
491
  klass_obj = scratch.new_class(nil, :Object, [], klass_basic_obj, nil)
485
- scratch.add_constant(klass_obj, "Object", klass_obj)
486
- scratch.add_constant(klass_obj, "BasicObject", klass_basic_obj)
492
+ scratch.add_constant(klass_obj, :Object, klass_obj, nil)
493
+ scratch.add_constant(klass_obj, :BasicObject, klass_basic_obj, nil)
487
494
 
488
495
  Type::Builtin[:basic_obj] = klass_basic_obj
489
496
  Type::Builtin[:obj] = klass_obj
@@ -518,6 +525,7 @@ module TypeProf
518
525
  scratch.set_custom_method(klass_vmcore, :"core#set_method_alias", Builtin.method(:vmcore_set_method_alias))
519
526
  scratch.set_custom_method(klass_vmcore, :"core#undef_method", Builtin.method(:vmcore_undef_method))
520
527
  scratch.set_custom_method(klass_vmcore, :"core#hash_merge_kwd", Builtin.method(:vmcore_hash_merge_kwd))
528
+ scratch.set_custom_method(klass_vmcore, :"core#raise", Builtin.method(:vmcore_raise))
521
529
  scratch.set_custom_method(klass_vmcore, :lambda, Builtin.method(:lambda))
522
530
  scratch.set_singleton_custom_method(klass_obj, :"new", Builtin.method(:object_s_new))
523
531
  scratch.set_singleton_custom_method(klass_obj, :"attr_accessor", Builtin.method(:module_attr_accessor))
@@ -529,6 +537,7 @@ module TypeProf
529
537
  scratch.set_custom_method(klass_obj, :class, Builtin.method(:object_class))
530
538
  scratch.set_custom_method(klass_obj, :send, Builtin.method(:object_send))
531
539
  scratch.set_custom_method(klass_obj, :instance_eval, Builtin.method(:object_instance_eval))
540
+ scratch.set_custom_method(klass_obj, :proc, Builtin.method(:lambda))
532
541
 
533
542
  scratch.set_custom_method(klass_module, :include, Builtin.method(:module_include))
534
543
  scratch.set_custom_method(klass_module, :extend, Builtin.method(:module_extend))
@@ -551,8 +560,10 @@ module TypeProf
551
560
  scratch.set_custom_method(klass_obj, :require_relative, Builtin.method(:kernel_require_relative))
552
561
  scratch.set_custom_method(klass_obj, :Array, Builtin.method(:kernel_Array))
553
562
 
554
- fargs, ret_ty = FormalArguments.new([], [], nil, [], nil, nil, Type.any), Type.any
555
- scratch.add_method(klass_obj, :initialize, false, TypedMethodDef.new([[fargs, ret_ty]], nil))
563
+ # ENV: Hash[String, String]
564
+ str_ty = Type::Instance.new(Type::Builtin[:str])
565
+ env_ty = Type.gen_hash {|h| h[str_ty] = Type.optional(str_ty) }
566
+ scratch.add_constant(klass_obj, :ENV, env_ty, false)
556
567
  end
557
568
  end
558
569
  end
@@ -1,64 +1,62 @@
1
1
  require "optparse"
2
- require "rbconfig"
3
2
 
4
3
  module TypeProf
5
- class CLI
6
- DEFAULT_DIR_FILTER = [
7
- [:include],
8
- [:exclude, RbConfig::CONFIG["prefix"]],
9
- [:exclude, Gem.dir],
10
- [:exclude, Gem.user_dir],
11
- ]
4
+ module CLI
5
+ module_function
12
6
 
13
- def initialize(argv)
7
+ def parse(argv)
14
8
  opt = OptionParser.new
15
9
 
16
- @output = nil
10
+ output = nil
17
11
 
18
12
  # Verbose level:
19
13
  # * 0: no output
20
14
  # * 1: show indicator
21
15
  # * 2: debug print
22
- @verbose = 1
23
-
24
- @options = {
25
- type_depth_limit: 5,
26
- pedantic_output: false,
27
- show_errors: false,
28
- }
29
- @dir_filter = nil
30
- @rbs_features_to_load = []
31
-
32
- opt.on("-o OUTFILE") {|v| @output = v }
33
- opt.on("-q", "--quiet") { @verbose = 0 }
34
- opt.on("-v", "--verbose") { @verbose = 2 }
16
+ verbose = 1
17
+
18
+ options = {}
19
+ dir_filter = nil
20
+ gem_rbs_features = []
21
+ version = false
22
+ max_sec = max_iter = nil
23
+
24
+ opt.on("-o OUTFILE") {|v| output = v }
25
+ opt.on("-q", "--quiet") { verbose = 0 }
26
+ opt.on("-v", "--verbose") { options[:show_errors] = true }
27
+ opt.on("--version") { version = true }
28
+ opt.on("-d", "--debug") { verbose = 2 }
35
29
  opt.on("-I DIR") {|v| $LOAD_PATH << v }
36
- opt.on("-r FEATURE") {|v| @rbs_features_to_load << v }
30
+ opt.on("-r FEATURE") {|v| gem_rbs_features << v }
31
+ opt.on("--max-second SECOND", Float) {|v| max_sec = v }
32
+ opt.on("--max-iteration TIMES", Integer) {|v| max_iter = v }
37
33
 
38
34
  opt.on("--include-dir DIR") do |dir|
39
35
  # When `--include-dir` option is specified as the first directory option,
40
36
  # typeprof will exclude any files by default unless a file path matches the explicit option
41
- @dir_filter ||= [[:exclude]]
42
- @dir_filter << [:include, File.expand_path(dir)]
37
+ dir_filter ||= [[:exclude]]
38
+ dir_filter << [:include, File.expand_path(dir)]
43
39
  end
44
40
  opt.on("--exclude-dir DIR") do |dir|
45
41
  # When `--exclude-dir` option is specified as the first directory option,
46
42
  # typeprof will include any files by default, except Ruby's install directory and Gem directories
47
- @dir_filter ||= DEFAULT_DIR_FILTER
48
- @dir_filter << [:exclude, File.expand_path(dir)]
43
+ dir_filter ||= ConfigData::DEFAULT_DIR_FILTER
44
+ dir_filter << [:exclude, File.expand_path(dir)]
49
45
  end
50
46
 
51
47
  opt.on("-f OPTION") do |v|
52
48
  key, args = v.split("=", 2)
53
49
  case key
54
50
  when "type-depth-limit"
55
- @options[:type_depth_limit] = Integer(args)
51
+ options[:type_depth_limit] = Integer(args)
56
52
  when "pedantic-output"
57
- @options[:pedantic_output] = true
53
+ options[:pedantic_output] = true
58
54
  when "show-errors"
59
- @options[:show_errors] = true
55
+ options[:show_errors] = true
60
56
  when "show-container-raw-elements"
61
- @options[:show_container_raw_elements] = true
57
+ options[:show_container_raw_elements] = true
58
+ when "stackprof"
59
+ options[:stackprof] = args ? args.to_sym : :cpu
62
60
  else
63
61
  raise OptionParser::InvalidOption.new("unknown option: #{ key }")
64
62
  end
@@ -66,83 +64,38 @@ module TypeProf
66
64
 
67
65
  opt.parse!(argv)
68
66
 
69
- @dir_filter ||= DEFAULT_DIR_FILTER
70
- @rb_files = []
71
- @rbs_files = []
67
+ dir_filter ||= ConfigData::DEFAULT_DIR_FILTER
68
+ rb_files = []
69
+ rbs_files = []
72
70
  argv.each do |path|
73
71
  if File.extname(path) == ".rbs"
74
- @rbs_files << path
72
+ rbs_files << path
75
73
  else
76
- @rb_files << path
74
+ rb_files << path
77
75
  end
78
76
  end
79
77
 
80
- raise OptionParser::InvalidOption.new("no input files") if @rb_files.empty?
78
+ puts "typeprof #{ VERSION }" if version
79
+ if rb_files.empty?
80
+ exit if version
81
+ raise OptionParser::InvalidOption.new("no input files")
82
+ end
81
83
 
82
- TypeProf.const_set(:Config, self)
84
+ ConfigData.new(
85
+ rb_files: rb_files,
86
+ rbs_files: rbs_files,
87
+ output: output,
88
+ gem_rbs_features: gem_rbs_features,
89
+ verbose: verbose,
90
+ dir_filter: dir_filter,
91
+ max_sec: max_sec,
92
+ max_iter: max_iter,
93
+ options: options,
94
+ )
83
95
 
84
96
  rescue OptionParser::InvalidOption
85
97
  puts $!
86
98
  exit
87
99
  end
88
-
89
- attr_reader :verbose, :options, :dir_filter
90
- attr_accessor :output
91
-
92
- def check_dir_filter(path)
93
- @dir_filter.reverse_each do |cond, dir|
94
- return cond unless dir
95
- return cond if path.start_with?(dir)
96
- end
97
- end
98
-
99
- def run
100
- scratch = Scratch.new
101
- Builtin.setup_initial_global_env(scratch)
102
-
103
- @rbs_features_to_load.each do |feature|
104
- Import.import_library(scratch, feature)
105
- end
106
-
107
- prologue_ctx = Context.new(nil, nil, nil)
108
- prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
109
- prologue_env = Env.new(StaticEnv.new(:top, Type.nil, false), [], [], Utils::HashWrapper.new({}))
110
-
111
- @rb_files.each do |path|
112
- if path == "-"
113
- iseq = ISeq.compile_str($<.read)
114
- else
115
- iseq = ISeq.compile(path)
116
- end
117
- ep, env = CLI.starting_state(iseq)
118
- scratch.merge_env(ep, env)
119
- scratch.add_callsite!(ep.ctx, nil, prologue_ep, prologue_env) {|ty, ep| }
120
- end
121
-
122
- @rbs_files.each do |path|
123
- Import.import_rbs_file(scratch, path)
124
- end
125
-
126
- result = scratch.type_profile
127
-
128
- if @output
129
- open(@output, "w") do |output|
130
- scratch.report(result, output)
131
- end
132
- else
133
- scratch.report(result, $stdout)
134
- end
135
- end
136
-
137
- def self.starting_state(iseq)
138
- cref = CRef.new(:bottom, Type::Builtin[:obj], false) # object
139
- recv = Type::Instance.new(Type::Builtin[:obj])
140
- ctx = Context.new(iseq, cref, nil)
141
- ep = ExecutionPoint.new(ctx, 0, nil)
142
- locals = [Type.nil] * iseq.locals.size
143
- env = Env.new(StaticEnv.new(recv, Type.nil, false), locals, [], Utils::HashWrapper.new({}))
144
-
145
- return ep, env
146
- end
147
100
  end
148
101
  end
@@ -0,0 +1,114 @@
1
+ require "rbconfig"
2
+
3
+ module TypeProf
4
+ ConfigData = Struct.new(
5
+ :rb_files,
6
+ :rbs_files,
7
+ :output,
8
+ :gem_rbs_features,
9
+ :verbose,
10
+ :dir_filter,
11
+ :max_iter,
12
+ :max_sec,
13
+ :options,
14
+ keyword_init: true
15
+ )
16
+
17
+ class ConfigData
18
+ def initialize(**opt)
19
+ opt[:output] ||= $stdout
20
+ opt[:gem_rbs_features] ||= []
21
+ opt[:dir_filter] ||= DEFAULT_DIR_FILTER
22
+ opt[:verbose] ||= 0
23
+ opt[:options] ||= {}
24
+ opt[:options] = {
25
+ type_depth_limit: 5,
26
+ pedantic_output: false,
27
+ show_errors: false,
28
+ stackprof: nil,
29
+ }.merge(opt[:options])
30
+ super(**opt)
31
+ end
32
+
33
+ def check_dir_filter(path)
34
+ dir_filter.reverse_each do |cond, dir|
35
+ return cond unless dir
36
+ return cond if path.start_with?(dir)
37
+ end
38
+ end
39
+
40
+ DEFAULT_DIR_FILTER = [
41
+ [:include],
42
+ [:exclude, RbConfig::CONFIG["prefix"]],
43
+ [:exclude, Gem.dir],
44
+ [:exclude, Gem.user_dir],
45
+ ]
46
+ end
47
+
48
+ def self.analyze(config)
49
+ # Deploy the config to the TypeProf::Config (Note: This is thread unsafe)
50
+ if TypeProf.const_defined?(:Config)
51
+ TypeProf.send(:remove_const, :Config)
52
+ end
53
+ TypeProf.const_set(:Config, config)
54
+
55
+ if Config.options[:stackprof]
56
+ require "stackprof"
57
+ out = "typeprof-stackprof-#{ Config.options[:stackprof] }.dump"
58
+ StackProf.start(mode: Config.options[:stackprof], out: out, raw: true)
59
+ end
60
+
61
+ scratch = Scratch.new
62
+ Builtin.setup_initial_global_env(scratch)
63
+
64
+ Config.gem_rbs_features.each do |feature|
65
+ Import.import_library(scratch, feature)
66
+ end
67
+
68
+ prologue_ctx = Context.new(nil, nil, nil)
69
+ prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
70
+ prologue_env = Env.new(StaticEnv.new(:top, Type.nil, false), [], [], Utils::HashWrapper.new({}))
71
+
72
+ Config.rb_files.each do |file|
73
+ if file.respond_to?(:read)
74
+ iseq = ISeq.compile_str(file.read, file.to_s)
75
+ else
76
+ iseq = ISeq.compile(file)
77
+ end
78
+ ep, env = TypeProf.starting_state(iseq)
79
+ scratch.merge_env(ep, env)
80
+ scratch.add_callsite!(ep.ctx, prologue_ep, prologue_env) {|ty, ep| }
81
+ end
82
+
83
+ Config.rbs_files.each do |path|
84
+ Import.import_rbs_file(scratch, path)
85
+ end
86
+
87
+ result = scratch.type_profile
88
+
89
+ if Config.output.respond_to?(:write)
90
+ scratch.report(result, Config.output)
91
+ else
92
+ open(Config.output, "w") do |output|
93
+ scratch.report(result, output)
94
+ end
95
+ end
96
+
97
+ ensure
98
+ if Config.options[:stackprof] && defined?(StackProf)
99
+ StackProf.stop
100
+ StackProf.results
101
+ end
102
+ end
103
+
104
+ def self.starting_state(iseq)
105
+ cref = CRef.new(:bottom, Type::Builtin[:obj], false) # object
106
+ recv = Type::Instance.new(Type::Builtin[:obj])
107
+ ctx = Context.new(iseq, cref, nil)
108
+ ep = ExecutionPoint.new(ctx, 0, nil)
109
+ locals = [Type.nil] * iseq.locals.size
110
+ env = Env.new(StaticEnv.new(recv, Type.nil, false), locals, [], Utils::HashWrapper.new({}))
111
+
112
+ return ep, env
113
+ end
114
+ end