typeprof 0.15.0 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dcefd3a4da176820246ad53757286aa1f7f8b2d21a6d1300763c91ba252e95ca
4
- data.tar.gz: 1bf7429a4cf7d59e1ab4ca10f5f065a22b85adbbda922c67060d0ede64aab60f
3
+ metadata.gz: 52c9e6c0c338fb19da0264929bce36b903cdedf7be3b6a031deef525e28be8d4
4
+ data.tar.gz: 8c17fe8549a7c44c8a11a1db9a40384b05f4025e5ec23140fee09eaf349d855c
5
5
  SHA512:
6
- metadata.gz: ff14f14aa923ca807db9d7f1bc7f537c46e735bd194a0b59758dfdba0e04c04175ca0505fe6c00f739fec7933eeeab5010646891f56e14d5245f5b8613dd7d3b
7
- data.tar.gz: 73a05bece353a7b4dbefba9f16e723fb088795e9f029b143721b03543e53ae19a8adb9908b3de41c839fbfb10a8d34c76ed0fa4f1b072801d7334233be63b1c9
6
+ metadata.gz: 74d8eddbd6010287d33c9891a59112f7f9edf1aa2f3ea24d07142e1d81e1c03508b1362a3630f092138352616a8258e973784d926293882894ba933123851f88
7
+ data.tar.gz: 77bc3c7f268b11663f561829c4b1a2c4917e137c0e56c3b4e734f29862c011e7095b2d1939a352534f78003159d90f07f8de81ce1304819f4f79e5d5ac6f7242
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- typeprof (0.15.0)
4
+ typeprof (0.15.1)
5
5
  rbs (>= 1.3.1)
6
6
 
7
7
  GEM
@@ -11,7 +11,7 @@ GEM
11
11
  docile (1.4.0)
12
12
  power_assert (2.0.0)
13
13
  rake (13.0.1)
14
- rbs (1.3.1)
14
+ rbs (1.3.3)
15
15
  simplecov (0.21.2)
16
16
  docile (~> 1.1)
17
17
  simplecov-html (~> 0.11)
@@ -286,6 +286,7 @@ module TypeProf
286
286
  @anonymous_struct_gen_id = 0
287
287
 
288
288
  @types_being_shown = []
289
+ @namespace = nil
289
290
  end
290
291
 
291
292
  def add_entrypoint(iseq)
@@ -1118,15 +1119,16 @@ module TypeProf
1118
1119
  env = @ep2env[ep]
1119
1120
  raise "nil env" unless env
1120
1121
 
1121
- insn, operands = ep.ctx.iseq.insns[ep.pc]
1122
+ insn = ep.ctx.iseq.insns[ep.pc]
1123
+ operands = insn.operands
1122
1124
 
1123
1125
  if Config.verbose >= 2
1124
1126
  # XXX: more dedicated output
1125
1127
  puts "DEBUG: stack=%p" % [env.stack]
1126
- puts "DEBUG: %s (%s) PC=%d insn=%s sp=%d" % [ep.source_location, ep.ctx.iseq.name, ep.pc, insn, env.stack.size]
1128
+ puts "DEBUG: %s (%s) PC=%d insn=%s sp=%d" % [ep.source_location, ep.ctx.iseq.name, ep.pc, insn.insn, env.stack.size]
1127
1129
  end
1128
1130
 
1129
- case insn
1131
+ case insn.insn
1130
1132
  when :_iseq_body_start
1131
1133
  # XXX: reconstruct and record the method signature
1132
1134
  iseq = ep.ctx.iseq
@@ -1641,7 +1643,7 @@ module TypeProf
1641
1643
  return
1642
1644
  end
1643
1645
 
1644
- when :getlocal, :getblockparam, :getblockparamproxy
1646
+ when :getlocal
1645
1647
  var_idx, scope_idx, _escaped = operands
1646
1648
  if scope_idx == 0
1647
1649
  ty = env.get_local(-var_idx+2)
@@ -1682,18 +1684,12 @@ module TypeProf
1682
1684
  getlocal_operands, _dup_operands, branch_operands = operands
1683
1685
  var_idx, _scope_idx, _escaped = getlocal_operands
1684
1686
  ret_ty = env.get_local(-var_idx+2)
1685
- unless ret_ty
1686
- p env.locals
1687
- raise
1688
- end
1689
1687
 
1690
1688
  branchtype, target, = branch_operands
1691
1689
  # branchtype: :if or :unless or :nil
1692
1690
  ep_then = ep.next
1693
1691
  ep_else = ep.jump(target)
1694
1692
 
1695
- var_idx, _scope_idx, _escaped = getlocal_operands
1696
-
1697
1693
  ret_ty.each_child do |ret_ty|
1698
1694
  flow_env = env.local_update(-var_idx+2, ret_ty).push(ret_ty)
1699
1695
  case ret_ty
@@ -1707,6 +1703,31 @@ module TypeProf
1707
1703
  end
1708
1704
  end
1709
1705
  return
1706
+ when :dup_setlocal_branch
1707
+ _dup_operands, setlocal_operands, branch_operands = operands
1708
+
1709
+ env, (ret_ty,) = env.pop(1)
1710
+
1711
+ var_idx, _scope_idx, _escaped = setlocal_operands
1712
+
1713
+ branchtype, target, = branch_operands
1714
+ # branchtype: :if or :unless or :nil
1715
+ ep_then = ep.next
1716
+ ep_else = ep.jump(target)
1717
+
1718
+ ret_ty.each_child do |ret_ty|
1719
+ flow_env = env.local_update(-var_idx+2, ret_ty)
1720
+ case ret_ty
1721
+ when Type.any
1722
+ merge_env(ep_then, flow_env)
1723
+ merge_env(ep_else, flow_env)
1724
+ when Type::Instance.new(Type::Builtin[:false]), Type.nil
1725
+ merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
1726
+ else
1727
+ merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
1728
+ end
1729
+ end
1730
+ return
1710
1731
  when :getlocal_checkmatch_branch
1711
1732
  getlocal_operands, branch_operands = operands
1712
1733
  var_idx, _scope_idx, _escaped = getlocal_operands
@@ -1970,7 +1991,7 @@ module TypeProf
1970
1991
  end
1971
1992
  env = env.push(ty)
1972
1993
  else
1973
- raise "Unknown insn: #{ insn }"
1994
+ raise "Unknown insn: #{ insn.insn }"
1974
1995
  end
1975
1996
 
1976
1997
  add_edge(ep, ep)
@@ -2204,7 +2225,7 @@ module TypeProf
2204
2225
  if blk.is_a?(Type::Proc)
2205
2226
  blk.block_body.do_call(aargs, ep, env, self, replace_recv_ty: replace_recv_ty, replace_cref: replace_cref, &ctn)
2206
2227
  else
2207
- warn(ep, "non-proc is passed as a block")
2228
+ warn(ep, "non-proc is passed as a block") if blk != Type.any
2208
2229
  ctn[Type.any, ep, env]
2209
2230
  end
2210
2231
  end
@@ -813,6 +813,7 @@ module TypeProf
813
813
  Type::Builtin[:exc] = scratch.get_constant(klass_obj, :Exception)
814
814
  Type::Builtin[:encoding] = scratch.get_constant(klass_obj, :Encoding)
815
815
  Type::Builtin[:enumerator] = scratch.get_constant(klass_obj, :Enumerator)
816
+ Type::Builtin[:kernel] = scratch.get_constant(klass_obj, :Kernel)
816
817
 
817
818
  klass_vmcore = Type::Builtin[:vmcore]
818
819
  klass_ary = Type::Builtin[:ary]
@@ -825,6 +826,7 @@ module TypeProf
825
826
  scratch.set_custom_method(klass_vmcore, :"core#undef_method", Builtin.method(:vmcore_undef_method))
826
827
  scratch.set_custom_method(klass_vmcore, :"core#hash_merge_kwd", Builtin.method(:vmcore_hash_merge_kwd))
827
828
  scratch.set_custom_method(klass_vmcore, :"core#raise", Builtin.method(:vmcore_raise))
829
+
828
830
  scratch.set_custom_method(klass_vmcore, :lambda, Builtin.method(:lambda))
829
831
  scratch.set_singleton_custom_method(klass_obj, :"new", Builtin.method(:object_s_new))
830
832
  scratch.set_custom_method(klass_obj, :p, Builtin.method(:kernel_p), false)
@@ -880,6 +882,12 @@ module TypeProf
880
882
  str_ty = Type::Instance.new(Type::Builtin[:str])
881
883
  env_ty = Type.gen_hash {|h| h[str_ty] = Type.optional(str_ty) }
882
884
  scratch.add_constant(klass_obj, :ENV, env_ty, false)
885
+
886
+ scratch.search_method(Type::Builtin[:kernel], false, :sprintf) do |mdefs,|
887
+ mdefs.each do |mdef|
888
+ scratch.add_method(klass_vmcore, :"core#sprintf", false, mdef)
889
+ end
890
+ end
883
891
  end
884
892
  end
885
893
  end
data/lib/typeprof/iseq.rb CHANGED
@@ -3,75 +3,65 @@ module TypeProf
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
5
 
6
- include Utils::StructuralEquality
7
-
8
- def self.compile(file)
9
- opt = RubyVM::InstructionSequence.compile_option
10
- opt[:inline_const_cache] = false
11
- opt[:peephole_optimization] = false
12
- opt[:specialized_instruction] = false
13
- opt[:operands_unification] = false
14
- opt[:coverage_enabled] = false
15
- new(RubyVM::InstructionSequence.compile_file(file, **opt).to_a)
6
+ class << self
7
+ def compile(file)
8
+ compile_core(nil, file)
9
+ end
10
+
11
+ def compile_str(str, path = nil)
12
+ compile_core(str, path)
13
+ end
14
+
15
+ private def compile_core(str, path)
16
+ opt = RubyVM::InstructionSequence.compile_option
17
+ opt[:inline_const_cache] = false
18
+ opt[:peephole_optimization] = false
19
+ opt[:specialized_instruction] = false
20
+ opt[:operands_unification] = false
21
+ opt[:coverage_enabled] = false
22
+
23
+ if str
24
+ iseq = RubyVM::InstructionSequence.compile(str, path, **opt)
25
+ else
26
+ iseq = RubyVM::InstructionSequence.compile_file(path, **opt)
27
+ end
28
+
29
+ return new(iseq.to_a)
30
+ end
16
31
  end
17
32
 
18
- def self.compile_str(str, path = nil)
19
- opt = RubyVM::InstructionSequence.compile_option
20
- opt[:inline_const_cache] = false
21
- opt[:peephole_optimization] = false
22
- opt[:specialized_instruction] = false
23
- opt[:operands_unification] = false
24
- opt[:coverage_enabled] = false
25
- new(RubyVM::InstructionSequence.compile(str, path, **opt).to_a)
33
+ Insn = Struct.new(:insn, :operands, :lineno)
34
+ class Insn
35
+ def check?(insn_cmp, operands_cmp = nil)
36
+ return insn == insn_cmp && (!operands_cmp || operands == operands_cmp)
37
+ end
26
38
  end
27
39
 
28
- FRESH_ID = [0]
40
+ ISEQ_FRESH_ID = [0]
29
41
 
30
42
  def initialize(iseq)
31
- @id = FRESH_ID[0]
32
- FRESH_ID[0] += 1
43
+ @id = (ISEQ_FRESH_ID[0] += 1)
33
44
 
34
45
  _magic, _major_version, _minor_version, _format_type, _misc,
35
46
  @name, @path, @absolute_path, @start_lineno, @type,
36
47
  @locals, @fargs_format, catch_table, insns = *iseq
37
48
 
38
- case @type
39
- when :method, :block
40
- if @fargs_format[:opt]
41
- label = @fargs_format[:opt].last
42
- i = insns.index(label) + 1
43
- else
44
- i = insns.find_index {|insn| insn.is_a?(Array) }
45
- end
46
- # skip keyword initialization
47
- while insns[i][0] == :checkkeyword
48
- raise if insns[i + 1][0] != :branchif
49
- label = insns[i + 1][1]
50
- i = insns.index(label) + 1
51
- end
52
- insns[i, 0] = [[:_iseq_body_start]]
53
- end
49
+ convert_insns(insns)
54
50
 
55
- # rescue/ensure clauses need to have a dedicated return addresses
56
- # because they requires to be virtually called.
57
- # So, this preprocess adds "nop" to make a new insn for their return addresses
58
- special_labels = {}
59
- catch_table.map do |type, iseq, first, last, cont, stack_depth|
60
- special_labels[cont] = true if type == :rescue || type == :ensure
61
- end
51
+ add_body_start_marker(insns)
62
52
 
63
- @insns = []
64
- @linenos = []
53
+ add_exception_cont_marker(insns, catch_table)
65
54
 
66
- labels = setup_iseq(insns, special_labels)
55
+ labels = create_label_table(insns)
67
56
 
68
- # checkmatch->branch
69
- # send->branch
57
+ @insns = setup_insns(insns, labels)
58
+
59
+ @fargs_format[:opt] = @fargs_format[:opt].map {|l| labels[l] } if @fargs_format[:opt]
70
60
 
71
61
  @catch_table = []
72
62
  catch_table.map do |type, iseq, first, last, cont, stack_depth|
73
63
  iseq = iseq ? ISeq.new(iseq) : nil
74
- target = labels[special_labels[cont] ? :"#{ cont }_special" : cont]
64
+ target = labels[cont]
75
65
  entry = [type, iseq, target, stack_depth]
76
66
  labels[first].upto(labels[last]) do |i|
77
67
  @catch_table[i] ||= []
@@ -79,43 +69,143 @@ module TypeProf
79
69
  end
80
70
  end
81
71
 
82
- merge_branches
72
+ rename_insn_types
73
+
74
+ unify_instructions
75
+ end
76
+
77
+ def source_location(pc)
78
+ "#{ @path }:#{ @insns[pc].lineno }"
79
+ end
83
80
 
84
- analyze_stack
81
+ attr_reader :name, :path, :absolute_path, :start_lineno, :type, :locals, :fargs_format, :catch_table, :insns
82
+ attr_reader :id
83
+
84
+ def pretty_print(q)
85
+ q.text "ISeq["
86
+ q.group do
87
+ q.nest(1) do
88
+ q.breakable ""
89
+ q.text "@type= #{ @type }"
90
+ q.breakable ", "
91
+ q.text "@name= #{ @name }"
92
+ q.breakable ", "
93
+ q.text "@path= #{ @path }"
94
+ q.breakable ", "
95
+ q.text "@absolute_path= #{ @absolute_path }"
96
+ q.breakable ", "
97
+ q.text "@start_lineno= #{ @start_lineno }"
98
+ q.breakable ", "
99
+ q.text "@fargs_format= #{ @fargs_format.inspect }"
100
+ q.breakable ", "
101
+ q.text "@insns="
102
+ q.group(2) do
103
+ @insns.each_with_index do |(insn, *operands), i|
104
+ q.breakable
105
+ q.group(2, "#{ i }: #{ insn.to_s }", "") do
106
+ q.pp operands
107
+ end
108
+ end
109
+ end
110
+ end
111
+ q.breakable
112
+ end
113
+ q.text "]"
85
114
  end
86
115
 
87
116
  def <=>(other)
88
117
  @id <=> other.id
89
118
  end
90
119
 
91
- def setup_iseq(insns, special_labels)
120
+ # Remove lineno entry and convert instructions to Insn instances
121
+ def convert_insns(insns)
122
+ ninsns = []
123
+ lineno = 0
124
+ insns.each do |e|
125
+ case e
126
+ when Integer # lineno
127
+ lineno = e
128
+ when Symbol # label or trace
129
+ ninsns << e
130
+ when Array
131
+ insn, *operands = e
132
+ ninsns << Insn.new(insn, operands, lineno)
133
+ else
134
+ raise "unknown iseq entry: #{ e }"
135
+ end
136
+ end
137
+ insns.replace(ninsns)
138
+ end
139
+
140
+ # Insert a dummy instruction "_iseq_body_start"
141
+ def add_body_start_marker(insns)
142
+ case @type
143
+ when :method, :block
144
+ # skip initialization code of optional arguments
145
+ if @fargs_format[:opt]
146
+ label = @fargs_format[:opt].last
147
+ i = insns.index(label) + 1
148
+ else
149
+ i = insns.find_index {|insn| insn.is_a?(Insn) }
150
+ end
151
+
152
+ # skip initialization code of keyword arguments
153
+ while insns[i][0] == :checkkeyword
154
+ raise if insns[i + 1].insn != :branchif
155
+ label = insns[i + 1].operands[0]
156
+ i = insns.index(label) + 1
157
+ end
158
+
159
+ insns.insert(i, Insn.new(:_iseq_body_start, [], @start_lineno))
160
+ end
161
+ end
162
+
163
+ # Insert "nop" instruction to continuation point of exception handlers
164
+ def add_exception_cont_marker(insns, catch_table)
165
+ # rescue/ensure clauses need to have a dedicated return addresses
166
+ # because they requires to be virtually called.
167
+ # So, this preprocess adds "nop" to make a new insn for their return addresses
168
+ exception_cont_labels = {}
169
+ catch_table.map! do |type, iseq, first, last, cont, stack_depth|
170
+ if type == :rescue || type == :ensure
171
+ exception_cont_labels[cont] = true
172
+ cont = :"#{ cont }_exception_cont"
173
+ end
174
+ [type, iseq, first, last, cont, stack_depth]
175
+ end
176
+
92
177
  i = 0
178
+ while i < insns.size
179
+ e = insns[i]
180
+ if exception_cont_labels[e]
181
+ insns.insert(i, :"#{ e }_exception_cont", Insn.new(:nop, []))
182
+ i += 2
183
+ end
184
+ i += 1
185
+ end
186
+ end
187
+
188
+ def create_label_table(insns)
189
+ pc = 0
93
190
  labels = {}
94
- ninsns = []
95
191
  insns.each do |e|
96
- if e.is_a?(Symbol) && e.to_s.start_with?("label")
97
- if special_labels[e]
98
- labels[:"#{ e }_special"] = i
99
- ninsns << [:nop]
100
- i += 1
101
- end
102
- labels[e] = i
192
+ if e.is_a?(Symbol)
193
+ labels[e] = pc
103
194
  else
104
- i += 1 if e.is_a?(Array)
105
- ninsns << e
195
+ pc += 1
106
196
  end
107
197
  end
198
+ labels
199
+ end
108
200
 
109
- lineno = 0
110
- ninsns.each do |e|
201
+ def setup_insns(insns, labels)
202
+ ninsns = []
203
+ insns.each do |e|
111
204
  case e
112
- when Integer # lineno
113
- lineno = e
114
205
  when Symbol # label or trace
115
206
  nil
116
- when Array
117
- insn, *operands = e
118
- operands = (INSN_TABLE[insn] || []).zip(operands).map do |type, operand|
207
+ when Insn
208
+ operands = (INSN_TABLE[e.insn] || []).zip(e.operands).map do |type, operand|
119
209
  case type
120
210
  when "ISEQ"
121
211
  operand && ISeq.new(operand)
@@ -131,81 +221,64 @@ module TypeProf
131
221
  end
132
222
  end
133
223
 
134
- @insns << [insn, operands]
135
- @linenos << lineno
224
+ ninsns << Insn.new(e.insn, operands, e.lineno)
136
225
  else
137
226
  raise "unknown iseq entry: #{ e }"
138
227
  end
139
228
  end
140
-
141
- @fargs_format[:opt] = @fargs_format[:opt].map {|l| labels[l] } if @fargs_format[:opt]
142
-
143
- labels
229
+ ninsns
144
230
  end
145
231
 
146
- def merge_branches
147
- @insns.size.times do |i|
148
- insn, operands = @insns[i]
149
- case insn
232
+ def rename_insn_types
233
+ @insns.each do |insn|
234
+ case insn.insn
150
235
  when :branchif
151
- @insns[i] = [:branch, [:if] + operands]
236
+ insn.insn, insn.operands = :branch, [:if] + insn.operands
152
237
  when :branchunless
153
- @insns[i] = [:branch, [:unless] + operands]
238
+ insn.insn, insn.operands = :branch, [:unless] + insn.operands
154
239
  when :branchnil
155
- @insns[i] = [:branch, [:nil] + operands]
240
+ insn.insn, insn.operands = :branch, [:nil] + insn.operands
241
+ when :getblockparam, :getblockparamproxy
242
+ insn.insn = :getlocal
156
243
  end
157
244
  end
158
245
  end
159
246
 
160
- def source_location(pc)
161
- "#{ @path }:#{ @linenos[pc] }"
162
- end
163
-
164
- attr_reader :name, :path, :absolute_path, :start_lineno, :type, :locals, :fargs_format, :catch_table, :insns, :linenos
165
- attr_reader :id
166
-
167
- def pretty_print(q)
168
- q.text "ISeq["
169
- q.group do
170
- q.nest(1) do
171
- q.breakable ""
172
- q.text "@type= #{ @type }"
173
- q.breakable ", "
174
- q.text "@name= #{ @name }"
175
- q.breakable ", "
176
- q.text "@path= #{ @path }"
177
- q.breakable ", "
178
- q.text "@absolute_path= #{ @absolute_path }"
179
- q.breakable ", "
180
- q.text "@start_lineno= #{ @start_lineno }"
181
- q.breakable ", "
182
- q.text "@fargs_format= #{ @fargs_format.inspect }"
183
- q.breakable ", "
184
- q.text "@insns="
185
- q.group(2) do
186
- @insns.each_with_index do |(insn, *operands), i|
187
- q.breakable
188
- q.group(2, "#{ i }: #{ insn.to_s }", "") do
189
- q.pp operands
190
- end
191
- end
192
- end
193
- end
194
- q.breakable
195
- end
196
- q.text "]"
197
- end
247
+ # Unify some instructions for flow-sensitive analysis
248
+ def unify_instructions
249
+ # This method rewrites instructions to enable flow-sensitive analysis.
250
+ #
251
+ # Consider `if x; ...; else; ... end`.
252
+ # When the variable `x` is of type "Integer | nil",
253
+ # we want to make sure that `x` is "Integer" in then clause.
254
+ # So, we need to split the environment to two ones:
255
+ # one is that `x` is of type "Integer", and the other is that
256
+ # `x` is type "nil".
257
+ #
258
+ # However, `if x` is compiled to "getlocal; branch".
259
+ # TypeProf evaluates them as follows:
260
+ #
261
+ # * "getlocal" pushes the value of `x` to the stack, amd
262
+ # * "branch" checks the value on the top of the stack
263
+ #
264
+ # TypeProf does not keep where the value comes from, so
265
+ # it is difficult to split the environment when evaluating "branch".
266
+ #
267
+ # This method rewrites "getlocal; branch" to "nop; getlocal_branch".
268
+ # The two instructions are unified to "getlocal_branch" instruction,
269
+ # so TypeProf can split the environment.
270
+ #
271
+ # This is a very fragile appoach because it highly depends on the compiler of Ruby.
198
272
 
199
- def analyze_stack
200
273
  # gather branch targets
201
274
  # TODO: catch_table should be also considered
202
275
  branch_targets = {}
203
- @insns.each do |insn, operands|
204
- case insn
276
+ @insns.each do |insn|
277
+ case insn.insn
205
278
  when :branch
206
- branch_targets[operands[1]] = true
279
+ branch_targets[insn.operands[1]] = true
207
280
  when :jump
208
- branch_targets[operands[0]] = true
281
+ branch_targets[insn.operands[0]] = true
209
282
  end
210
283
  end
211
284
 
@@ -215,25 +288,27 @@ module TypeProf
215
288
  case_branch_list = []
216
289
  if CASE_WHEN_CHECKMATCH
217
290
  (@insns.size - 1).times do |i|
218
- insn0, getlocal_operands = @insns[i]
219
- next unless [:getlocal, :getblockparam, :getblockparamproxy].include?(insn0) && getlocal_operands[1] == 0
291
+ insn = @insns[i]
292
+ next unless insn.insn == :getlocal && insn.operands[1] == 0
293
+ getlocal_operands = insn.operands
220
294
  nops = [i]
221
295
  new_insns = []
222
296
  j = i + 1
223
297
  while true
224
- case @insns[j]
225
- when [:dup, []]
226
- break unless @insns[j + 1] == [:putnil, []]
227
- break unless @insns[j + 2] == [:putobject, [true]]
228
- break unless @insns[j + 3][0] == :getconstant # TODO: support A::B::C
229
- break unless @insns[j + 4] == [:checkmatch, [2]]
230
- break unless @insns[j + 5][0] == :branch
231
- target_pc = @insns[j + 5][1][1]
232
- break unless @insns[target_pc] == [:pop, []]
298
+ case @insns[j].insn
299
+ when :dup
300
+ break unless @insns[j + 1].check?(:putnil, [])
301
+ break unless @insns[j + 2].check?(:putobject, [true])
302
+ break unless @insns[j + 3].check?(:getconstant) # TODO: support A::B::C
303
+ break unless @insns[j + 4].check?(:checkmatch, [2])
304
+ break unless @insns[j + 5].check?(:branch)
305
+ target_pc = @insns[j + 5].operands[1]
306
+ break unless @insns[target_pc].check?(:pop, [])
233
307
  nops << j << (j + 4) << target_pc
234
- new_insns << [j + 5, [:getlocal_checkmatch_branch, [getlocal_operands, @insns[j + 5][1]]]]
308
+ branch_operands = @insns[j + 5][1]
309
+ new_insns << [j + 5, Insn.new(:getlocal_checkmatch_branch, [getlocal_operands, branch_operands])]
235
310
  j += 6
236
- when [:pop, []]
311
+ when :pop
237
312
  nops << j
238
313
  case_branch_list << [nops, new_insns]
239
314
  break
@@ -244,25 +319,28 @@ module TypeProf
244
319
  end
245
320
  else
246
321
  (@insns.size - 1).times do |i|
247
- insn0, getlocal_operands = @insns[i]
248
- next unless [:getlocal, :getblockparam, :getblockparamproxy].include?(insn0) && getlocal_operands[1] == 0
322
+ insn = @insns[i]
323
+ next unless insn.insn == :getlocal && insn.operands[1] == 0
324
+ getlocal_operands = insn.operands
249
325
  nops = []
250
326
  new_insns = []
251
327
  j = i + 1
252
328
  while true
253
- case @insns[j]
254
- when [:putnil, []]
255
- break unless @insns[j + 1] == [:putobject, [true]]
256
- break unless @insns[j + 2][0] == :getconstant # TODO: support A::B::C
257
- break unless @insns[j + 3] == [:topn, [1]]
258
- break unless @insns[j + 4] == [:send, [{:mid=>:===, :flag=>20, :orig_argc=>1}, nil]]
259
- break unless @insns[j + 5][0] == :branch
260
- target_pc = @insns[j + 5][1][1]
261
- break unless @insns[target_pc] == [:pop, []]
329
+ insn = @insns[j]
330
+ if insn.check?(:putnil, [])
331
+ break unless @insns[j + 1].check?(:putobject, [true])
332
+ break unless @insns[j + 2].check?(:getconstant) # TODO: support A::B::C
333
+ break unless @insns[j + 3].check?(:topn, [1])
334
+ break unless @insns[j + 4].check?(:send, [{:mid=>:===, :flag=>20, :orig_argc=>1}, nil])
335
+ break unless @insns[j + 5].check?(:branch)
336
+ target_pc = @insns[j + 5].operands[1]
337
+ break unless @insns[target_pc].check?(:pop, [])
262
338
  nops << (j + 4) #<< target_pc
263
- new_insns << [j + 5, [:arg_getlocal_send_branch, [getlocal_operands, @insns[j + 4][1], @insns[j + 5][1]]]]
339
+ send_operands = @insns[j + 4][1]
340
+ branch_operands = @insns[j + 5][1]
341
+ new_insns << [j + 5, Insn.new(:arg_getlocal_send_branch, [getlocal_operands, send_operands, branch_operands])]
264
342
  j += 6
265
- when [:pop, []]
343
+ elsif insn.check?(:pop, [])
266
344
  #nops << j
267
345
  case_branch_list << [nops, new_insns]
268
346
  break
@@ -273,15 +351,15 @@ module TypeProf
273
351
  end
274
352
  end
275
353
  case_branch_list.each do |nops, new_insns|
276
- nops.each {|i| @insns[i] = [:nop, []] }
354
+ nops.each {|i| @insns[i] = Insn.new(:nop, []) }
277
355
  new_insns.each {|i, insn| @insns[i] = insn }
278
356
  end
279
357
 
280
358
  # find a pattern: getlocal(recv), ..., send (is_a?, respond_to?), branch
281
359
  recv_getlocal_send_branch_list = []
282
360
  (@insns.size - 1).times do |i|
283
- insn, operands = @insns[i]
284
- if insn == :getlocal && operands[1] == 0
361
+ insn = @insns[i]
362
+ if insn.insn == :getlocal && insn.operands[1] == 0
285
363
  j = i + 1
286
364
  sp = 1
287
365
  while @insns[j]
@@ -297,94 +375,118 @@ module TypeProf
297
375
  end
298
376
  recv_getlocal_send_branch_list.each do |i, j|
299
377
  next if (i + 1 .. j + 1).any? {|i| branch_targets[i] }
300
- _insn, getlocal_operands = @insns[i]
301
- _insn, send_operands = @insns[j]
302
- _insn, branch_operands = @insns[j + 1]
303
- @insns[j] = [:nop]
304
- @insns[j + 1] = [:recv_getlocal_send_branch, [getlocal_operands, send_operands, branch_operands]]
378
+ getlocal_operands = @insns[i].operands
379
+ send_operands = @insns[j].operands
380
+ branch_operands = @insns[j + 1].operands
381
+ @insns[j] = Insn.new(:nop, [])
382
+ @insns[j + 1] = Insn.new(:recv_getlocal_send_branch, [getlocal_operands, send_operands, branch_operands])
305
383
  end
306
384
 
307
385
  # find a pattern: getlocal, send (===), branch
308
386
  arg_getlocal_send_branch_list = []
309
387
  (@insns.size - 1).times do |i|
310
- insn1, operands1 = @insns[i]
311
- next unless insn1 == :getlocal && operands1[1] == 0
312
- insn2, operands2 = @insns[i + 1]
313
- next unless insn2 == :send
314
- send_opt = operands2[0]
315
- next unless send_opt[:flag] == 16 && send_opt[:orig_argc] == 1
316
- insn3, _operands3 = @insns[i + 2]
317
- next unless insn3 == :branch
388
+ insn1 = @insns[i]
389
+ next unless insn1.insn == :getlocal && insn1.operands[1] == 0
390
+ insn2 = @insns[i + 1]
391
+ next unless insn2.insn == :send
392
+ send_operands = insn2.operands[0]
393
+ next unless send_operands[:flag] == 16 && send_operands[:orig_argc] == 1
394
+ insn3 = @insns[i + 2]
395
+ next unless insn3.insn == :branch
318
396
  arg_getlocal_send_branch_list << i
319
397
  end
320
398
  arg_getlocal_send_branch_list.each do |i|
321
399
  next if (i .. i + 2).any? {|i| branch_targets[i] }
322
- _insn, getlocal_operands = @insns[i]
323
- _insn, send_operands = @insns[i + 1]
324
- _insn, branch_operands = @insns[i + 2]
325
- @insns[i + 1] = [:nop]
326
- @insns[i + 2] = [:arg_getlocal_send_branch, [getlocal_operands, send_operands, branch_operands]]
400
+ getlocal_operands = @insns[i].operands
401
+ send_operands = @insns[i + 1].operands
402
+ branch_operands = @insns[i + 2].operands
403
+ @insns[i + 1] = Insn.new(:nop, [])
404
+ @insns[i + 2] = Insn.new(:arg_getlocal_send_branch, [getlocal_operands, send_operands, branch_operands])
327
405
  end
328
406
 
329
407
  # find a pattern: send (block_given?), branch
330
408
  send_branch_list = []
331
409
  (@insns.size - 1).times do |i|
332
- insn, _operands = @insns[i]
333
- if insn == :send
334
- insn, _operands = @insns[i + 1]
335
- if insn == :branch
410
+ insn = @insns[i]
411
+ if insn.insn == :send
412
+ insn = @insns[i + 1]
413
+ if insn.insn == :branch
336
414
  send_branch_list << i
337
415
  end
338
416
  end
339
417
  end
340
418
  send_branch_list.each do |i|
341
419
  next if branch_targets[i + 1]
342
- _insn, send_operands = @insns[i]
343
- _insn, branch_operands = @insns[i + 1]
344
- @insns[i] = [:nop]
345
- @insns[i + 1] = [:send_branch, [send_operands, branch_operands]]
420
+ send_operands = @insns[i].operands
421
+ branch_operands = @insns[i + 1].operands
422
+ @insns[i] = Insn.new(:nop, [])
423
+ @insns[i + 1] = Insn.new(:send_branch, [send_operands, branch_operands])
346
424
  end
347
425
 
348
426
  # find a pattern: getlocal, dup, branch
349
427
  (@insns.size - 2).times do |i|
350
428
  next if branch_targets[i + 1] || branch_targets[i + 2]
351
- insn0, getlocal_operands = @insns[i]
352
- insn1, dup_operands = @insns[i + 1]
353
- insn2, branch_operands = @insns[i + 2]
354
- if insn0 == :getlocal && insn1 == :dup && insn2 == :branch && getlocal_operands[1] == 0
355
- @insns[i ] = [:nop]
356
- @insns[i + 1] = [:nop]
357
- @insns[i + 2] = [:getlocal_dup_branch, [getlocal_operands, dup_operands, branch_operands]]
429
+ insn0 = @insns[i]
430
+ insn1 = @insns[i + 1]
431
+ insn2 = @insns[i + 2]
432
+ if insn0.insn == :getlocal && insn1.insn == :dup && insn2.insn == :branch && insn0.operands[1] == 0
433
+ getlocal_operands = insn0.operands
434
+ dup_operands = insn1.operands
435
+ branch_operands = insn2.operands
436
+ @insns[i ] = Insn.new(:nop, [])
437
+ @insns[i + 1] = Insn.new(:nop, [])
438
+ @insns[i + 2] = Insn.new(:getlocal_dup_branch, [getlocal_operands, dup_operands, branch_operands])
439
+ end
440
+ end
441
+
442
+ # find a pattern: dup, setlocal, branch
443
+ (@insns.size - 2).times do |i|
444
+ next if branch_targets[i + 1] || branch_targets[i + 2]
445
+ insn0 = @insns[i]
446
+ insn1 = @insns[i + 1]
447
+ insn2 = @insns[i + 2]
448
+ if insn0.insn == :dup && insn1.insn == :setlocal && insn2.insn == :branch && insn1.operands[1] == 0
449
+ dup_operands = insn0.operands
450
+ setlocal_operands = insn1.operands
451
+ branch_operands = insn2.operands
452
+ @insns[i ] = Insn.new(:nop, [])
453
+ @insns[i + 1] = Insn.new(:nop, [])
454
+ @insns[i + 2] = Insn.new(:dup_setlocal_branch, [dup_operands, setlocal_operands, branch_operands])
358
455
  end
359
456
  end
360
457
 
361
458
  # find a pattern: dup, branch
362
459
  (@insns.size - 1).times do |i|
363
460
  next if branch_targets[i + 1]
364
- insn0, dup_operands = @insns[i]
365
- insn1, branch_operands = @insns[i + 1]
366
- if insn0 == :dup && insn1 == :branch
367
- @insns[i ] = [:nop]
368
- @insns[i + 1] = [:dup_branch, [dup_operands, branch_operands]]
461
+ insn0 = @insns[i]
462
+ insn1 = @insns[i + 1]
463
+ if insn0.insn == :dup && insn1.insn == :branch
464
+ dup_operands = insn0.operands
465
+ branch_operands = insn1.operands
466
+ @insns[i ] = Insn.new(:nop, [])
467
+ @insns[i + 1] = Insn.new(:dup_branch, [dup_operands, branch_operands])
369
468
  end
370
469
  end
371
470
 
372
471
  # find a pattern: getlocal, branch
373
472
  (@insns.size - 1).times do |i|
374
473
  next if branch_targets[i + 1]
375
- insn0, getlocal_operands = @insns[i]
376
- insn1, branch_operands = @insns[i + 1]
377
- if [:getlocal, :getblockparam, :getblockparamproxy].include?(insn0) && getlocal_operands[1] == 0 && insn1 == :branch
378
- @insns[i ] = [:nop]
379
- @insns[i + 1] = [:getlocal_branch, [getlocal_operands, branch_operands]]
474
+ insn0 = @insns[i]
475
+ insn1 = @insns[i + 1]
476
+ if insn0.insn == :getlocal && insn0.operands[1] == 0 && insn1.insn == :branch
477
+ getlocal_operands = insn0.operands
478
+ branch_operands = insn1.operands
479
+ @insns[i ] = Insn.new(:nop, [])
480
+ @insns[i + 1] = Insn.new(:getlocal_branch, [getlocal_operands, branch_operands])
380
481
  end
381
482
  end
382
483
  end
383
484
 
384
485
  def check_send_branch(sp, j)
385
- insn, operands = @insns[j]
486
+ insn = @insns[j]
487
+ operands = insn.operands
386
488
 
387
- case insn
489
+ case insn.insn
388
490
  when :putspecialobject, :putnil, :putobject, :duparray, :putstring,
389
491
  :putself
390
492
  sp += 1
@@ -422,7 +524,7 @@ module TypeProf
422
524
  argc += 1 # receiver
423
525
  argc += kw_arg.size if kw_arg
424
526
  sp -= argc
425
- return :match if insn == :send && sp == 0 && @insns[j + 1][0] == :branch
527
+ return :match if insn.insn == :send && sp == 0 && @insns[j + 1].insn == :branch
426
528
  sp += 1
427
529
  when :arg_getlocal_send_branch
428
530
  return # not implemented
@@ -283,12 +283,12 @@ module TypeProf
283
283
  # XXX: a block is passed to a method that does not accept block.
284
284
  # Should we call the passed block with any arguments?
285
285
  ret_ty = ret_ty.remove_type_vars
286
- ctn[ret_ty, caller_ep, ncaller_env]
286
+ ctn[ret_ty, caller_ep, ncaller_env] if ret_ty != Type.bot
287
287
  end
288
288
  else
289
289
  ret_ty = ret_ty.substitute(subst, Config.options[:type_depth_limit])
290
290
  ret_ty = ret_ty.remove_type_vars
291
- ctn[ret_ty, caller_ep, ncaller_env]
291
+ ctn[ret_ty, caller_ep, ncaller_env] if ret_ty != Type.bot
292
292
  end
293
293
  end
294
294
 
data/lib/typeprof/type.rb CHANGED
@@ -895,7 +895,7 @@ module TypeProf
895
895
  fargs << ("**" + all_val_ty.screen_name(scratch))
896
896
  end
897
897
  if Config.options[:show_parameter_names]
898
- farg_names = farg_names.map {|name| name == :type ? :type_ : name } # XXX: workaround of RBS parser bug
898
+ farg_names = farg_names.map {|name| RBS::Parser::KEYWORDS.key?(name.to_s) ? "`#{name}`" : name }
899
899
  farg_names = farg_names.map {|name| name.is_a?(Integer) ? "noname_#{ name }" : name }
900
900
  fargs = fargs.zip(farg_names).map {|farg, name| name ? "#{ farg } #{ name }" : farg }
901
901
  end
@@ -1,3 +1,3 @@
1
1
  module TypeProf
2
- VERSION = "0.15.0"
2
+ VERSION = "0.15.1"
3
3
  end
data/smoke/break2.rb CHANGED
@@ -12,5 +12,5 @@ __END__
12
12
  # Classes
13
13
  class Object
14
14
  private
15
- def foo: -> (Integer | String)
15
+ def foo: -> String
16
16
  end
@@ -0,0 +1,17 @@
1
+ def type(type)
2
+ end
3
+
4
+ def out(*out)
5
+ end
6
+
7
+ def untyped(untyped:)
8
+ end
9
+
10
+ __END__
11
+ # Classes
12
+ class Object
13
+ private
14
+ def type: (untyped `type`) -> nil
15
+ def out: (*untyped `out`) -> nil
16
+ def untyped: (untyped: untyped) -> nil
17
+ end
data/smoke/next2.rb CHANGED
@@ -13,5 +13,5 @@ __END__
13
13
  # Classes
14
14
  class Object
15
15
  private
16
- def foo: { -> (Integer | String) } -> (Integer | String)
16
+ def foo: { -> Integer } -> Integer
17
17
  end
data/smoke/or_raise.rb ADDED
@@ -0,0 +1,18 @@
1
+ def foo
2
+ if rand < 0.5
3
+ 1
4
+ end
5
+ end
6
+
7
+ def bar
8
+ x = foo or raise("nil")
9
+ x.times { }
10
+ end
11
+
12
+ __END__
13
+ # Classes
14
+ class Object
15
+ private
16
+ def foo: -> Integer?
17
+ def bar: -> Integer
18
+ end
@@ -1,4 +1,5 @@
1
1
  # RUBY_VERSION >= 3.0
2
+ # NO_SHOW_ERRORS
2
3
 
3
4
  def foo
4
5
  case [:a, :b, :c]
@@ -11,12 +12,6 @@ end
11
12
  foo
12
13
 
13
14
  __END__
14
- # Errors
15
- smoke/pattern-match1.rb:5: [error] undefined method: nil#length
16
- smoke/pattern-match1.rb:5: [error] undefined method: nil#[]
17
- smoke/pattern-match1.rb:5: [error] undefined method: nil#[]
18
- smoke/pattern-match1.rb:5: [error] undefined method: nil#[]
19
-
20
15
  # Classes
21
16
  class Object
22
17
  private
data/testbed/ao.rb CHANGED
@@ -291,7 +291,7 @@ end
291
291
 
292
292
  top
293
293
 
294
- if _dummy = false
294
+ if $0 == __FILE__
295
295
  v = Vec.new(0.0, 0.0, 0.0)
296
296
  v.vadd(v)
297
297
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typeprof
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0
4
+ version: 0.15.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yusuke Endoh
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-21 00:00:00.000000000 Z
11
+ date: 2021-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rbs
@@ -217,6 +217,7 @@ files:
217
217
  - smoke/hash4.rb
218
218
  - smoke/hash5.rb
219
219
  - smoke/huge_union.rb
220
+ - smoke/identifier_keywords.rb
220
221
  - smoke/included.rb
221
222
  - smoke/inheritance.rb
222
223
  - smoke/inheritance2.rb
@@ -276,6 +277,7 @@ files:
276
277
  - smoke/optional1.rb
277
278
  - smoke/optional2.rb
278
279
  - smoke/optional3.rb
280
+ - smoke/or_raise.rb
279
281
  - smoke/parameterizedd-self.rb
280
282
  - smoke/parameterizedd-self2.rb
281
283
  - smoke/pathname1.rb