typeprof 0.9.2 → 0.14.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 (189) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +1 -1
  3. data/Gemfile.lock +6 -5
  4. data/doc/demo.md +2 -2
  5. data/doc/todo.md +133 -0
  6. data/lib/typeprof/analyzer.rb +201 -104
  7. data/lib/typeprof/block.rb +39 -4
  8. data/lib/typeprof/builtin.rb +217 -70
  9. data/lib/typeprof/cli.rb +7 -0
  10. data/lib/typeprof/config.rb +30 -14
  11. data/lib/typeprof/container-type.rb +24 -0
  12. data/lib/typeprof/export.rb +134 -74
  13. data/lib/typeprof/import.rb +87 -39
  14. data/lib/typeprof/iseq.rb +116 -41
  15. data/lib/typeprof/method.rb +29 -7
  16. data/lib/typeprof/type.rb +75 -13
  17. data/lib/typeprof/version.rb +1 -1
  18. data/smoke/alias.rb +4 -4
  19. data/smoke/alias2.rb +3 -1
  20. data/smoke/arguments.rb +2 -2
  21. data/smoke/arguments2.rb +5 -5
  22. data/smoke/array-each.rb +1 -1
  23. data/smoke/array-each3.rb +1 -1
  24. data/smoke/array-map.rb +1 -1
  25. data/smoke/array-map2.rb +1 -1
  26. data/smoke/array-map3.rb +3 -3
  27. data/smoke/array-mul.rb +2 -2
  28. data/smoke/array-plus1.rb +1 -1
  29. data/smoke/array-plus2.rb +1 -0
  30. data/smoke/array-range-aref.rb +11 -11
  31. data/smoke/array-replace.rb +1 -1
  32. data/smoke/array1.rb +5 -5
  33. data/smoke/array10.rb +1 -1
  34. data/smoke/array11.rb +1 -1
  35. data/smoke/array12.rb +1 -1
  36. data/smoke/array14.rb +1 -1
  37. data/smoke/array15.rb +1 -1
  38. data/smoke/array2.rb +2 -2
  39. data/smoke/array3.rb +1 -0
  40. data/smoke/array6.rb +2 -1
  41. data/smoke/array8.rb +1 -1
  42. data/smoke/array9.rb +1 -1
  43. data/smoke/attr-module.rb +1 -0
  44. data/smoke/attr-vis.rb +43 -0
  45. data/smoke/attr-vis.rbs +4 -0
  46. data/smoke/attr.rb +2 -2
  47. data/smoke/block-ambiguous.rb +4 -4
  48. data/smoke/block-args1-rest.rb +6 -5
  49. data/smoke/block-args1.rb +5 -5
  50. data/smoke/block-args2-rest.rb +6 -5
  51. data/smoke/block-args2.rb +5 -5
  52. data/smoke/block-args3-rest.rb +7 -6
  53. data/smoke/block-args3.rb +6 -6
  54. data/smoke/block-blockarg.rb +3 -3
  55. data/smoke/block-kwarg.rb +4 -4
  56. data/smoke/block1.rb +1 -1
  57. data/smoke/block10.rb +1 -1
  58. data/smoke/block11.rb +2 -2
  59. data/smoke/block2.rb +1 -1
  60. data/smoke/block3.rb +1 -1
  61. data/smoke/block5.rb +1 -0
  62. data/smoke/block_given.rb +37 -0
  63. data/smoke/break4.rb +17 -0
  64. data/smoke/class_eval.rb +22 -0
  65. data/smoke/class_method.rb +2 -2
  66. data/smoke/class_method2.rb +2 -2
  67. data/smoke/constant2.rb +3 -2
  68. data/smoke/context-sensitive1.rb +1 -1
  69. data/smoke/cvar.rb +3 -2
  70. data/smoke/define_method.rb +2 -2
  71. data/smoke/define_method3.rb +1 -0
  72. data/smoke/define_method4.rb +1 -1
  73. data/smoke/define_method6.rb +19 -0
  74. data/smoke/define_method7.rb +18 -0
  75. data/smoke/demo.rb +6 -6
  76. data/smoke/demo1.rb +1 -1
  77. data/smoke/demo11.rb +1 -1
  78. data/smoke/demo2.rb +1 -1
  79. data/smoke/demo3.rb +1 -1
  80. data/smoke/demo4.rb +3 -3
  81. data/smoke/demo5.rb +1 -1
  82. data/smoke/demo6.rb +2 -1
  83. data/smoke/demo7.rb +1 -1
  84. data/smoke/demo9.rb +1 -0
  85. data/smoke/dummy-execution1.rb +1 -1
  86. data/smoke/dummy-execution2.rb +1 -1
  87. data/smoke/dummy_element.rb +1 -1
  88. data/smoke/ensure1.rb +1 -1
  89. data/smoke/enum_for.rb +15 -0
  90. data/smoke/enum_for2.rb +17 -0
  91. data/smoke/extended.rb +38 -0
  92. data/smoke/fib.rb +2 -2
  93. data/smoke/flow1.rb +1 -1
  94. data/smoke/flow10.rb +17 -0
  95. data/smoke/flow11.rb +17 -0
  96. data/smoke/flow2.rb +1 -1
  97. data/smoke/flow3.rb +1 -1
  98. data/smoke/flow5.rb +1 -1
  99. data/smoke/flow6.rb +1 -1
  100. data/smoke/flow7.rb +1 -1
  101. data/smoke/flow8.rb +1 -1
  102. data/smoke/flow9.rb +1 -1
  103. data/smoke/function.rb +1 -1
  104. data/smoke/gvar.rb +1 -1
  105. data/smoke/gvar2.rb +1 -1
  106. data/smoke/hash-fetch.rb +3 -3
  107. data/smoke/included.rb +38 -0
  108. data/smoke/inheritance.rb +4 -4
  109. data/smoke/inherited.rb +26 -0
  110. data/smoke/initialize.rb +3 -2
  111. data/smoke/instance_eval.rb +2 -2
  112. data/smoke/instance_eval4.rb +12 -0
  113. data/smoke/int_times.rb +1 -1
  114. data/smoke/integer.rb +1 -1
  115. data/smoke/ivar.rb +3 -2
  116. data/smoke/ivar2.rb +2 -2
  117. data/smoke/ivar3.rb +2 -1
  118. data/smoke/ivar4.rb +1 -0
  119. data/smoke/kernel-class.rb +1 -1
  120. data/smoke/keyword4.rb +1 -1
  121. data/smoke/kwrest.rb +1 -0
  122. data/smoke/kwsplat1.rb +2 -2
  123. data/smoke/kwsplat2.rb +1 -1
  124. data/smoke/manual-rbs.rb +1 -0
  125. data/smoke/manual-rbs3.rb +1 -0
  126. data/smoke/method_missing.rb +4 -3
  127. data/smoke/module3.rb +1 -1
  128. data/smoke/module4.rb +1 -0
  129. data/smoke/module5.rb +1 -1
  130. data/smoke/module_function1.rb +3 -2
  131. data/smoke/module_function2.rb +3 -2
  132. data/smoke/multiple-include.rb +1 -0
  133. data/smoke/next1.rb +1 -1
  134. data/smoke/object-send1.rb +3 -3
  135. data/smoke/optional1.rb +1 -1
  136. data/smoke/optional2.rb +1 -1
  137. data/smoke/optional3.rb +1 -1
  138. data/smoke/parameterizedd-self.rb +2 -1
  139. data/smoke/prepend1.rb +33 -0
  140. data/smoke/prepend2.rb +10 -0
  141. data/smoke/prepend2.rbs +9 -0
  142. data/smoke/primitive_method.rb +19 -0
  143. data/smoke/proc4.rb +1 -1
  144. data/smoke/public.rb +4 -0
  145. data/smoke/range.rb +1 -1
  146. data/smoke/rbs-attr.rb +2 -2
  147. data/smoke/rbs-proc2.rb +1 -1
  148. data/smoke/rbs-proc3.rb +1 -1
  149. data/smoke/rbs-tyvar4.rb +3 -2
  150. data/smoke/rbs-tyvar6.rb +3 -3
  151. data/smoke/redo1.rb +1 -1
  152. data/smoke/redo2.rb +1 -1
  153. data/smoke/rescue1.rb +1 -1
  154. data/smoke/rescue2.rb +1 -1
  155. data/smoke/rescue3.rb +1 -0
  156. data/smoke/rescue4.rb +1 -1
  157. data/smoke/respond_to.rb +1 -1
  158. data/smoke/rest1.rb +2 -2
  159. data/smoke/rest2.rb +1 -1
  160. data/smoke/rest3.rb +6 -6
  161. data/smoke/rest4.rb +2 -2
  162. data/smoke/rest5.rb +1 -1
  163. data/smoke/rest6.rb +1 -1
  164. data/smoke/retry1.rb +2 -2
  165. data/smoke/simple.rb +1 -1
  166. data/smoke/step.rb +3 -3
  167. data/smoke/struct-keyword_init.rb +6 -16
  168. data/smoke/struct.rb +1 -1
  169. data/smoke/struct2.rb +1 -1
  170. data/smoke/struct3.rb +1 -1
  171. data/smoke/struct4.rb +1 -1
  172. data/smoke/struct5.rb +2 -2
  173. data/smoke/struct6.rb +2 -2
  174. data/smoke/struct7.rb +1 -1
  175. data/smoke/super1.rb +4 -4
  176. data/smoke/super3.rb +3 -2
  177. data/smoke/super4.rb +7 -5
  178. data/smoke/super5.rb +6 -4
  179. data/smoke/symbol-proc-attr.rb +1 -1
  180. data/smoke/tap1.rb +2 -2
  181. data/smoke/toplevel.rb +1 -1
  182. data/smoke/type_var.rb +3 -3
  183. data/smoke/user-demo.rb +1 -1
  184. data/smoke/wrong-extend.rb +1 -0
  185. data/smoke/wrong-include.rb +1 -0
  186. data/smoke/wrong-include2.rb +1 -1
  187. data/testbed/goodcheck-Gemfile.lock +1 -1
  188. data/typeprof.gemspec +1 -1
  189. metadata +25 -5
@@ -3,6 +3,10 @@ require "rbs"
3
3
  module TypeProf
4
4
  class RBSReader
5
5
  def initialize
6
+ @repo = RBS::Repository.new
7
+ Config.gem_repo_dirs.each do |dir|
8
+ @repo.add(Pathname(dir))
9
+ end
6
10
  @env, @builtin_env_json = RBSReader.get_builtin_env
7
11
  end
8
12
 
@@ -11,7 +15,7 @@ module TypeProf
11
15
  unless @builtin_env
12
16
  @builtin_env = RBS::Environment.new
13
17
 
14
- loader = RBS::EnvironmentLoader.new
18
+ loader = RBS::EnvironmentLoader.new(repository: @repo)
15
19
  new_decls = loader.load(env: @builtin_env).map {|decl,| decl }
16
20
  @builtin_env_json = load_rbs(@builtin_env, new_decls)
17
21
  end
@@ -24,22 +28,30 @@ module TypeProf
24
28
  end
25
29
 
26
30
  def load_library(lib)
27
- loader = RBS::EnvironmentLoader.new(core_root: nil)
31
+ loader = RBS::EnvironmentLoader.new(core_root: nil, repository: @repo)
28
32
  loader.add(library: lib)
29
33
 
30
34
  case lib
35
+ when 'bigdecimal-math'
36
+ loader.add(library: 'bigdecimal')
31
37
  when "yaml"
32
38
  loader.add(library: "pstore")
33
39
  loader.add(library: "dbm")
40
+ when "logger"
41
+ loader.add(library: "monitor")
42
+ when "csv"
43
+ loader.add(library: "forwardable")
44
+ when "prime"
45
+ loader.add(library: "singleton")
34
46
  end
35
47
 
36
48
  new_decls = loader.load(env: @env).map {|decl,| decl }
37
49
  RBSReader.load_rbs(@env, new_decls)
38
50
  end
39
51
 
40
- def load_path(path)
41
- loader = RBS::EnvironmentLoader.new(core_root: nil)
42
- loader.add(path: path)
52
+ def load_paths(paths)
53
+ loader = RBS::EnvironmentLoader.new(core_root: nil, repository: @repo)
54
+ paths.each {|path| loader.add(path: path) }
43
55
  new_decls = loader.load(env: @env).map {|decl,| decl }
44
56
  RBSReader.load_rbs(@env, new_decls)
45
57
  end
@@ -69,6 +81,7 @@ module TypeProf
69
81
  class RBS2JSON
70
82
  def initialize(all_env, cur_env)
71
83
  @all_env, @cur_env = all_env, cur_env
84
+ @alias_resolution_stack = {}
72
85
  end
73
86
 
74
87
  def dump_json
@@ -116,13 +129,13 @@ module TypeProf
116
129
  end
117
130
 
118
131
  type_params = nil
119
- included_modules = []
120
- extended_modules = []
132
+ modules = { include: [], extend: [], prepend: [] }
121
133
  methods = {}
122
134
  attr_methods = {}
123
135
  ivars = {}
124
136
  cvars = {}
125
137
  rbs_sources = {}
138
+ visibility = true
126
139
 
127
140
  decls.each do |decl|
128
141
  decl = decl.decl
@@ -143,7 +156,7 @@ module TypeProf
143
156
  end
144
157
  end
145
158
 
146
- method_def = conv_method_def(method_types)
159
+ method_def = conv_method_def(method_types, visibility)
147
160
  rbs_source = [(member.kind == :singleton ? "self." : "") + member.name.to_s, member.types.map {|type| type.location.source }]
148
161
  if member.instance?
149
162
  methods[[false, name]] = method_def
@@ -155,13 +168,13 @@ module TypeProf
155
168
  end
156
169
  when RBS::AST::Members::AttrReader
157
170
  ty = conv_type(member.type)
158
- attr_methods[[false, member.name]] = attr_method_def(:reader, member.name, ty)
171
+ attr_methods[[false, member.name]] = attr_method_def(:reader, member.name, ty, visibility)
159
172
  when RBS::AST::Members::AttrWriter
160
173
  ty = conv_type(member.type)
161
- attr_methods[[false, member.name]] = attr_method_def(:writer, member.name, ty)
174
+ attr_methods[[false, member.name]] = attr_method_def(:writer, member.name, ty, visibility)
162
175
  when RBS::AST::Members::AttrAccessor
163
176
  ty = conv_type(member.type)
164
- attr_methods[[false, member.name]] = attr_method_def(:accessor, member.name, ty)
177
+ attr_methods[[false, member.name]] = attr_method_def(:accessor, member.name, ty, visibility)
165
178
  when RBS::AST::Members::Alias
166
179
  # XXX: an alias to attr methods?
167
180
  if member.instance?
@@ -176,11 +189,15 @@ module TypeProf
176
189
  when RBS::AST::Members::Include
177
190
  name = member.name
178
191
  if name.kind == :class
192
+ # including a module
179
193
  mod = conv_type_name(name)
180
194
  type_args = member.args.map {|type| conv_type(type) }
181
- included_modules << [mod, type_args]
195
+ modules[:include] << [mod, type_args]
182
196
  else
183
- # including an interface is not supported yet
197
+ # including an interface
198
+ mod = conv_type_name(name)
199
+ type_args = member.args.map {|type| conv_type(type) }
200
+ modules[:include] << [mod, type_args]
184
201
  end
185
202
 
186
203
  when RBS::AST::Members::Extend
@@ -188,7 +205,17 @@ module TypeProf
188
205
  if name.kind == :class
189
206
  mod = conv_type_name(name)
190
207
  type_args = member.args.map {|type| conv_type(type) }
191
- extended_modules << [mod, type_args]
208
+ modules[:extend] << [mod, type_args]
209
+ else
210
+ # extending a module with an interface is not supported yet
211
+ end
212
+
213
+ when RBS::AST::Members::Prepend
214
+ name = member.name
215
+ if name.kind == :class
216
+ mod = conv_type_name(name)
217
+ type_args = member.args.map {|type| conv_type(type) }
218
+ modules[:prepend] << [mod, type_args]
192
219
  else
193
220
  # extending a module with an interface is not supported yet
194
221
  end
@@ -198,7 +225,10 @@ module TypeProf
198
225
  when RBS::AST::Members::ClassVariable
199
226
  cvars[member.name] = conv_type(member.type)
200
227
 
201
- when RBS::AST::Members::Public, RBS::AST::Members::Private # XXX
228
+ when RBS::AST::Members::Public
229
+ visibility = true
230
+ when RBS::AST::Members::Private
231
+ visibility = false
202
232
 
203
233
  # The following declarations are ignoreable because they are handled in other level
204
234
  when RBS::AST::Declarations::Constant
@@ -216,8 +246,7 @@ module TypeProf
216
246
  type_params: type_params,
217
247
  superclass: superclass,
218
248
  members: {
219
- included_modules: included_modules,
220
- extended_modules: extended_modules,
249
+ modules: modules,
221
250
  methods: methods,
222
251
  attr_methods: attr_methods,
223
252
  ivars: ivars,
@@ -297,10 +326,14 @@ module TypeProf
297
326
  return RBS::BuiltinNames::Object.name, []
298
327
  end
299
328
 
300
- def conv_method_def(rbs_method_types)
301
- rbs_method_types.map do |method_type|
329
+ def conv_method_def(rbs_method_types, visibility)
330
+ sig_rets = rbs_method_types.map do |method_type|
302
331
  conv_func(method_type.type_params, method_type.type, method_type.block)
303
332
  end
333
+ {
334
+ sig_rets: sig_rets,
335
+ visibility: visibility,
336
+ }
304
337
  end
305
338
 
306
339
  def conv_func(type_params, func, block)
@@ -330,11 +363,12 @@ module TypeProf
330
363
  }
331
364
  end
332
365
 
333
- def attr_method_def(kind, name, ty)
366
+ def attr_method_def(kind, name, ty, visibility)
334
367
  {
335
368
  kind: kind,
336
369
  ivar: name,
337
370
  ty: ty,
371
+ visibility: visibility,
338
372
  }
339
373
  end
340
374
 
@@ -407,8 +441,17 @@ module TypeProf
407
441
  raise NotImplementedError
408
442
  end
409
443
  when RBS::Types::Alias
410
- alias_decl = @all_env.alias_decls[ty.name]
411
- alias_decl ? conv_type(alias_decl.decl.type) : [:any]
444
+ if @alias_resolution_stack[ty.name]
445
+ [:any]
446
+ else
447
+ begin
448
+ @alias_resolution_stack[ty.name] = true
449
+ alias_decl = @all_env.alias_decls[ty.name]
450
+ alias_decl ? conv_type(alias_decl.decl.type) : [:any]
451
+ ensure
452
+ @alias_resolution_stack.delete(ty.name)
453
+ end
454
+ end
412
455
  when RBS::Types::Union
413
456
  [:union, ty.types.map {|ty2| conv_type(ty2) }.compact]
414
457
  when RBS::Types::Optional
@@ -455,9 +498,9 @@ module TypeProf
455
498
  Import.new(scratch, json).import
456
499
  end
457
500
 
458
- def self.import_rbs_file(scratch, rbs_path)
459
- rbs_path = Pathname(rbs_path) unless rbs_path.is_a?(Pathname)
460
- Import.new(scratch, scratch.rbs_reader.load_path(rbs_path)).import(true)
501
+ def self.import_rbs_files(scratch, rbs_paths)
502
+ rbs_paths = rbs_paths.map {|rbs_path| Pathname(rbs_path) }
503
+ Import.new(scratch, scratch.rbs_reader.load_paths(rbs_paths)).import(true)
461
504
  end
462
505
 
463
506
  def self.import_rbs_code(scratch, rbs_name, rbs_code)
@@ -502,22 +545,25 @@ module TypeProf
502
545
 
503
546
  classes.each do |klass, superclass_type_args, members|
504
547
  @scratch.add_superclass_type_args!(klass, superclass_type_args&.map {|ty| conv_type(ty) })
505
- included_modules = members[:included_modules]
506
- extended_modules = members[:extended_modules]
548
+ modules = members[:modules]
507
549
  methods = members[:methods]
508
550
  attr_methods = members[:attr_methods]
509
551
  ivars = members[:ivars]
510
552
  cvars = members[:cvars]
511
553
  rbs_sources = members[:rbs_sources]
512
554
 
513
- included_modules.each do |mod, type_args|
514
- type_args = type_args&.map {|ty| conv_type(ty) }
515
- @scratch.include_module(klass, path_to_klass(mod), type_args, false, nil)
516
- end
517
-
518
- extended_modules.each do |mod, type_args|
519
- type_args = type_args&.map {|ty| conv_type(ty) }
520
- @scratch.include_module(klass, path_to_klass(mod), type_args, true, nil)
555
+ modules.each do |kind, mods|
556
+ mods.each do |mod, type_args|
557
+ type_args = type_args&.map {|ty| conv_type(ty) }
558
+ case kind
559
+ when :include
560
+ @scratch.mix_module(:after, klass, path_to_klass(mod), type_args, false, nil)
561
+ when :extend
562
+ @scratch.mix_module(:after, klass, path_to_klass(mod), type_args, true, nil)
563
+ when :prepend
564
+ @scratch.mix_module(:before, klass, path_to_klass(mod), type_args, false, nil)
565
+ end
566
+ end
521
567
  end
522
568
 
523
569
  methods.each do |(singleton, method_name), mdef|
@@ -530,7 +576,7 @@ module TypeProf
530
576
  kind = mdef[:kind]
531
577
  ivar = mdef[:ivar]
532
578
  ty = conv_type(mdef[:ty]).remove_type_vars
533
- @scratch.add_attr_method(klass, nil, ivar, :"@#{ ivar }", kind)
579
+ @scratch.add_attr_method(klass, ivar, :"@#{ ivar }", kind, mdef[:visibility], nil)
534
580
  @scratch.add_ivar_write!(Type::Instance.new(klass), :"@#{ ivar }", ty, nil)
535
581
  end
536
582
 
@@ -560,11 +606,11 @@ module TypeProf
560
606
  end
561
607
 
562
608
  def conv_method_def(method_name, mdef, rbs_source)
563
- sig_rets = mdef.flat_map do |sig_ret|
609
+ sig_rets = mdef[:sig_rets].flat_map do |sig_ret|
564
610
  conv_func(sig_ret)
565
611
  end
566
612
 
567
- TypedMethodDef.new(sig_rets, rbs_source)
613
+ TypedMethodDef.new(sig_rets, rbs_source, mdef[:visibility])
568
614
  end
569
615
 
570
616
  def conv_func(sig_ret)
@@ -664,7 +710,9 @@ module TypeProf
664
710
  klass = Type::Builtin[:obj]
665
711
  path.each do |name|
666
712
  klass = @scratch.get_constant(klass, name)
667
- raise path.inspect if klass == Type.any
713
+ if klass == Type.any
714
+ raise TypeProfError.new("A constant `#{ path.join("::") }' is used but not defined in RBS")
715
+ end
668
716
  end
669
717
  klass
670
718
  end
data/lib/typeprof/iseq.rb CHANGED
@@ -206,8 +206,76 @@ module TypeProf
206
206
  end
207
207
  end
208
208
 
209
- # find a pattern: getlocal, ..., send (is_a?, respond_to?), branch
210
- send_branch_list = []
209
+ # flow-sensitive analysis for `case var; when A; when B; when C; end`
210
+ # find a pattern: getlocal, (dup, putobject(true), getconstant(class name), checkmatch, branch)* for ..Ruby 3.0
211
+ # find a pattern: getlocal, (putobject(true), getconstant(class name), top(1), send(===), branch)* for Ruby 3.1..
212
+ case_branch_list = []
213
+ if (RUBY_VERSION.split(".") <=> %w(3 1 0)) < 0
214
+ (@insns.size - 1).times do |i|
215
+ insn0, getlocal_operands = @insns[i]
216
+ next unless [:getlocal, :getblockparam, :getblockparamproxy].include?(insn0) && getlocal_operands[1] == 0
217
+ nops = [i]
218
+ new_insns = []
219
+ j = i + 1
220
+ while true
221
+ case @insns[j]
222
+ when [:dup, []]
223
+ break unless @insns[j + 1] == [:putnil, []]
224
+ break unless @insns[j + 2] == [:putobject, [true]]
225
+ break unless @insns[j + 3][0] == :getconstant # TODO: support A::B::C
226
+ break unless @insns[j + 4] == [:checkmatch, [2]]
227
+ break unless @insns[j + 5][0] == :branch
228
+ target_pc = @insns[j + 5][1][1]
229
+ break unless @insns[target_pc] == [:pop, []]
230
+ nops << j << (j + 4) << target_pc
231
+ new_insns << [j + 5, [:getlocal_checkmatch_branch, [getlocal_operands, @insns[j + 5][1]]]]
232
+ j += 6
233
+ when [:pop, []]
234
+ nops << j
235
+ case_branch_list << [nops, new_insns]
236
+ break
237
+ else
238
+ break
239
+ end
240
+ end
241
+ end
242
+ else
243
+ (@insns.size - 1).times do |i|
244
+ insn0, getlocal_operands = @insns[i]
245
+ next unless [:getlocal, :getblockparam, :getblockparamproxy].include?(insn0) && getlocal_operands[1] == 0
246
+ nops = []
247
+ new_insns = []
248
+ j = i + 1
249
+ while true
250
+ case @insns[j]
251
+ when [:putnil, []]
252
+ break unless @insns[j + 1] == [:putobject, [true]]
253
+ break unless @insns[j + 2][0] == :getconstant # TODO: support A::B::C
254
+ break unless @insns[j + 3] == [:topn, [1]]
255
+ break unless @insns[j + 4] == [:send, [{:mid=>:===, :flag=>20, :orig_argc=>1}, nil]]
256
+ break unless @insns[j + 5][0] == :branch
257
+ target_pc = @insns[j + 5][1][1]
258
+ break unless @insns[target_pc] == [:pop, []]
259
+ nops << (j + 4) #<< target_pc
260
+ new_insns << [j + 5, [:arg_getlocal_send_branch, [getlocal_operands, @insns[j + 4][1], @insns[j + 5][1]]]]
261
+ j += 6
262
+ when [:pop, []]
263
+ #nops << j
264
+ case_branch_list << [nops, new_insns]
265
+ break
266
+ else
267
+ break
268
+ end
269
+ end
270
+ end
271
+ end
272
+ case_branch_list.each do |nops, new_insns|
273
+ nops.each {|i| @insns[i] = [:nop, []] }
274
+ new_insns.each {|i, insn| @insns[i] = insn }
275
+ end
276
+
277
+ # find a pattern: getlocal(recv), ..., send (is_a?, respond_to?), branch
278
+ recv_getlocal_send_branch_list = []
211
279
  (@insns.size - 1).times do |i|
212
280
  insn, operands = @insns[i]
213
281
  if insn == :getlocal && operands[1] == 0
@@ -216,7 +284,7 @@ module TypeProf
216
284
  while @insns[j]
217
285
  sp = check_send_branch(sp, j)
218
286
  if sp == :match
219
- send_branch_list << [i, j]
287
+ recv_getlocal_send_branch_list << [i, j]
220
288
  break
221
289
  end
222
290
  break if !sp
@@ -224,13 +292,54 @@ module TypeProf
224
292
  end
225
293
  end
226
294
  end
227
- send_branch_list.each do |i, j|
295
+ recv_getlocal_send_branch_list.each do |i, j|
228
296
  next if (i + 1 .. j + 1).any? {|i| branch_targets[i] }
229
297
  _insn, getlocal_operands = @insns[i]
230
298
  _insn, send_operands = @insns[j]
231
299
  _insn, branch_operands = @insns[j + 1]
232
300
  @insns[j] = [:nop]
233
- @insns[j + 1] = [:send_branch, [getlocal_operands, send_operands, branch_operands]]
301
+ @insns[j + 1] = [:recv_getlocal_send_branch, [getlocal_operands, send_operands, branch_operands]]
302
+ end
303
+
304
+ # find a pattern: getlocal, send (===), branch
305
+ arg_getlocal_send_branch_list = []
306
+ (@insns.size - 1).times do |i|
307
+ insn1, operands1 = @insns[i]
308
+ next unless insn1 == :getlocal && operands1[1] == 0
309
+ insn2, operands2 = @insns[i + 1]
310
+ next unless insn2 == :send
311
+ send_opt = operands2[0]
312
+ next unless send_opt[:flag] == 16 && send_opt[:orig_argc] == 1
313
+ insn3, _operands3 = @insns[i + 2]
314
+ next unless insn3 == :branch
315
+ arg_getlocal_send_branch_list << i
316
+ end
317
+ arg_getlocal_send_branch_list.each do |i|
318
+ next if (i .. i + 2).any? {|i| branch_targets[i] }
319
+ _insn, getlocal_operands = @insns[i]
320
+ _insn, send_operands = @insns[i + 1]
321
+ _insn, branch_operands = @insns[i + 2]
322
+ @insns[i + 1] = [:nop]
323
+ @insns[i + 2] = [:arg_getlocal_send_branch, [getlocal_operands, send_operands, branch_operands]]
324
+ end
325
+
326
+ # find a pattern: send (block_given?), branch
327
+ send_branch_list = []
328
+ (@insns.size - 1).times do |i|
329
+ insn, _operands = @insns[i]
330
+ if insn == :send
331
+ insn, _operands = @insns[i + 1]
332
+ if insn == :branch
333
+ send_branch_list << i
334
+ end
335
+ end
336
+ end
337
+ send_branch_list.each do |i|
338
+ next if branch_targets[i + 1]
339
+ _insn, send_operands = @insns[i]
340
+ _insn, branch_operands = @insns[i + 1]
341
+ @insns[i] = [:nop]
342
+ @insns[i + 1] = [:send_branch, [send_operands, branch_operands]]
234
343
  end
235
344
 
236
345
  # find a pattern: getlocal, dup, branch
@@ -267,42 +376,6 @@ module TypeProf
267
376
  @insns[i + 1] = [:getlocal_branch, [getlocal_operands, branch_operands]]
268
377
  end
269
378
  end
270
-
271
- # flow-sensitive analysis for `case var; when A; when B; when C; end`
272
- # find a pattern: getlocal, (dup, putobject(true), getconstant(class name), checkmatch, branch)*
273
- case_branch_list = []
274
- (@insns.size - 1).times do |i|
275
- insn0, getlocal_operands = @insns[i]
276
- next unless [:getlocal, :getblockparam, :getblockparamproxy].include?(insn0) && getlocal_operands[1] == 0
277
- nops = [i]
278
- new_insns = []
279
- j = i + 1
280
- while true
281
- case @insns[j]
282
- when [:dup, []]
283
- break unless @insns[j + 1] == [:putnil, []]
284
- break unless @insns[j + 2] == [:putobject, [true]]
285
- break unless @insns[j + 3][0] == :getconstant # TODO: support A::B::C
286
- break unless @insns[j + 4] == [:checkmatch, [2]]
287
- break unless @insns[j + 5][0] == :branch
288
- target_pc = @insns[j + 5][1][1]
289
- break unless @insns[target_pc] == [:pop, []]
290
- nops << j << (j + 4) << target_pc
291
- new_insns << [j + 5, [:getlocal_checkmatch_branch, [getlocal_operands, @insns[j + 5][1]]]]
292
- j += 6
293
- when [:pop, []]
294
- nops << j
295
- case_branch_list << [nops, new_insns]
296
- break
297
- else
298
- break
299
- end
300
- end
301
- end
302
- case_branch_list.each do |nops, new_insns|
303
- nops.each {|i| @insns[i] = [:nop, []] }
304
- new_insns.each {|i, insn| @insns[i] = insn }
305
- end
306
379
  end
307
380
 
308
381
  def check_send_branch(sp, j)
@@ -348,6 +421,8 @@ module TypeProf
348
421
  sp -= argc
349
422
  return :match if insn == :send && sp == 0 && @insns[j + 1][0] == :branch
350
423
  sp += 1
424
+ when :arg_getlocal_send_branch
425
+ return # not implemented
351
426
  when :invokeblock
352
427
  opt, = operands
353
428
  sp -= opt[:orig_argc]