typeprof 0.9.2 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
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]