typeprof 0.13.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 501623610b473ab2fdd870686387a26ae787b8e3af6254a7a443967acb0b9f17
4
- data.tar.gz: 0ace065239fb606719b1a716c7ce38e312c3b1b07f238d3f1a67e66eaf261567
3
+ metadata.gz: c03cc70d70c73d14871e07c098b0689e11afc8e309662a8d4402c6f2ecbc5d02
4
+ data.tar.gz: '097a04710ef19e6c8f29304c901dd8569e9e74a436dd1d76a5bf515df7fdfa41'
5
5
  SHA512:
6
- metadata.gz: b5b437508d0b83d3966a07266cebef3aa64e98ef4b33e75cd9deb5a615019fdb054f00ffb433d2af573258192bfd7346845dc870db32540b928314999c8e818c
7
- data.tar.gz: 2aaa6b88e7d41676d989b3ac01057af13cf7247569e68b9be57b919e210afa140d016e3d27522fa8f03362fa17a3fb46356d3be1bd770a251c825cb4477334e6
6
+ metadata.gz: f5348942872848edec6b679db2a9afa60b746a6d3908f7066dfcd3a551d04ab728ea1494a5bdcf66867d7bc47ba0dce6ce29e2d29833babb5413262c8da92ca1
7
+ data.tar.gz: 9b214f8a4a24fa433a9c3d7fb205246b161c723206a73db257eb57b3905b34ab7ef378324a7786b63daccdf763616667c2d5d6fcbaf98ff6ff455f26f956082e
@@ -1324,7 +1324,7 @@ module TypeProf
1324
1324
  end
1325
1325
  end
1326
1326
  return
1327
- when :getlocal_send_branch
1327
+ when :recv_getlocal_send_branch
1328
1328
  getlocal_operands, send_operands, branch_operands = operands
1329
1329
  env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
1330
1330
  recvs = Type.any if recvs == Type.bot
@@ -1352,6 +1352,39 @@ module TypeProf
1352
1352
  end
1353
1353
  end
1354
1354
  return
1355
+ when :arg_getlocal_send_branch
1356
+ getlocal_operands, send_operands, branch_operands = operands
1357
+ env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
1358
+ raise if aargs.lead_tys.size != 1
1359
+ aarg = aargs.lead_tys[0]
1360
+ aarg = Type.any if aarg == Type.bot
1361
+ recvs.each_child do |recv|
1362
+ aarg.each_child do |aarg|
1363
+ aargs_tmp = ActualArguments.new([aarg], nil, {}, aargs.blk_ty)
1364
+ do_send(recv, mid, aargs_tmp, ep, env) do |ret_ty, ep, env|
1365
+ env, ret_ty, = localize_type(ret_ty, env, ep)
1366
+
1367
+ branchtype, target, = branch_operands
1368
+ # branchtype: :if or :unless or :nil
1369
+ ep_then = ep.next
1370
+ ep_else = ep.jump(target)
1371
+
1372
+ var_idx, _scope_idx, _escaped = getlocal_operands
1373
+ flow_env = env.local_update(-var_idx+2, aarg)
1374
+
1375
+ case ret_ty
1376
+ when Type::Instance.new(Type::Builtin[:true])
1377
+ merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
1378
+ when Type::Instance.new(Type::Builtin[:false])
1379
+ merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
1380
+ else
1381
+ merge_env(ep_then, env)
1382
+ merge_env(ep_else, env)
1383
+ end
1384
+ end
1385
+ end
1386
+ end
1387
+ return
1355
1388
  when :send_branch
1356
1389
  send_operands, branch_operands = operands
1357
1390
  env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
@@ -1455,7 +1488,7 @@ module TypeProf
1455
1488
  end
1456
1489
  _type, _iseq, cont, stack_depth = tmp_ep.ctx.iseq.catch_table[tmp_ep.pc]&.find {|type,| type == :break }
1457
1490
  if cont
1458
- nenv = @return_envs[tmp_ep]
1491
+ nenv = @return_envs[tmp_ep] || env
1459
1492
  nenv, = nenv.pop(nenv.stack.size - stack_depth)
1460
1493
  nenv = nenv.push(ty)
1461
1494
  tmp_ep = tmp_ep.jump(cont)
@@ -1814,8 +1847,10 @@ module TypeProf
1814
1847
  when :nop
1815
1848
  when :setn
1816
1849
  idx, = operands
1817
- env, (ty,) = env.pop(1)
1818
- env = env.setn(idx, ty).push(ty)
1850
+ if idx >= 1
1851
+ env, (ty,) = env.pop(1)
1852
+ env = env.setn(idx, ty).push(ty)
1853
+ end
1819
1854
  when :topn
1820
1855
  idx, = operands
1821
1856
  env = env.topn(idx)
@@ -163,6 +163,27 @@ module TypeProf
163
163
  end
164
164
  end
165
165
 
166
+ def module_eqq(recv, mid, aargs, ep, env, scratch, &ctn)
167
+ if aargs.lead_tys.size == 1
168
+ aargs.lead_tys[0].each_child do |aarg|
169
+ aarg = aarg.base_type if aarg.is_a?(Type::Symbol) # XXX
170
+ if aarg.is_a?(Type::Instance)
171
+ if aarg.klass == recv # XXX: inheritance
172
+ true_val = Type::Instance.new(Type::Builtin[:true])
173
+ ctn[true_val, ep, env]
174
+ else
175
+ false_val = Type::Instance.new(Type::Builtin[:false])
176
+ ctn[false_val, ep, env]
177
+ end
178
+ else
179
+ ctn[Type.bool, ep, env]
180
+ end
181
+ end
182
+ else
183
+ ctn[Type.bool, ep, env]
184
+ end
185
+ end
186
+
166
187
  def object_module_eval(recv, mid, aargs, ep, env, scratch, &ctn)
167
188
  if aargs.lead_tys.size >= 1
168
189
  scratch.warn(ep, "class_eval with arguments is ignored")
@@ -829,6 +850,7 @@ module TypeProf
829
850
  scratch.set_custom_method(klass_module, :attr_writer, Builtin.method(:module_attr_writer))
830
851
  scratch.set_custom_method(klass_module, :class_eval, Builtin.method(:object_module_eval))
831
852
  scratch.set_custom_method(klass_module, :module_eval, Builtin.method(:object_module_eval))
853
+ scratch.set_custom_method(klass_module, :===, Builtin.method(:module_eqq))
832
854
 
833
855
  scratch.set_custom_method(klass_proc, :[], Builtin.method(:proc_call))
834
856
  scratch.set_custom_method(klass_proc, :call, Builtin.method(:proc_call))
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
- getlocal_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
- getlocal_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,35 @@ module TypeProf
224
292
  end
225
293
  end
226
294
  end
227
- getlocal_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] = [:getlocal_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]]
234
324
  end
235
325
 
236
326
  # find a pattern: send (block_given?), branch
@@ -286,42 +376,6 @@ module TypeProf
286
376
  @insns[i + 1] = [:getlocal_branch, [getlocal_operands, branch_operands]]
287
377
  end
288
378
  end
289
-
290
- # flow-sensitive analysis for `case var; when A; when B; when C; end`
291
- # find a pattern: getlocal, (dup, putobject(true), getconstant(class name), checkmatch, branch)*
292
- case_branch_list = []
293
- (@insns.size - 1).times do |i|
294
- insn0, getlocal_operands = @insns[i]
295
- next unless [:getlocal, :getblockparam, :getblockparamproxy].include?(insn0) && getlocal_operands[1] == 0
296
- nops = [i]
297
- new_insns = []
298
- j = i + 1
299
- while true
300
- case @insns[j]
301
- when [:dup, []]
302
- break unless @insns[j + 1] == [:putnil, []]
303
- break unless @insns[j + 2] == [:putobject, [true]]
304
- break unless @insns[j + 3][0] == :getconstant # TODO: support A::B::C
305
- break unless @insns[j + 4] == [:checkmatch, [2]]
306
- break unless @insns[j + 5][0] == :branch
307
- target_pc = @insns[j + 5][1][1]
308
- break unless @insns[target_pc] == [:pop, []]
309
- nops << j << (j + 4) << target_pc
310
- new_insns << [j + 5, [:getlocal_checkmatch_branch, [getlocal_operands, @insns[j + 5][1]]]]
311
- j += 6
312
- when [:pop, []]
313
- nops << j
314
- case_branch_list << [nops, new_insns]
315
- break
316
- else
317
- break
318
- end
319
- end
320
- end
321
- case_branch_list.each do |nops, new_insns|
322
- nops.each {|i| @insns[i] = [:nop, []] }
323
- new_insns.each {|i, insn| @insns[i] = insn }
324
- end
325
379
  end
326
380
 
327
381
  def check_send_branch(sp, j)
@@ -367,6 +421,8 @@ module TypeProf
367
421
  sp -= argc
368
422
  return :match if insn == :send && sp == 0 && @insns[j + 1][0] == :branch
369
423
  sp += 1
424
+ when :arg_getlocal_send_branch
425
+ return # not implemented
370
426
  when :invokeblock
371
427
  opt, = operands
372
428
  sp -= opt[:orig_argc]
@@ -1,3 +1,3 @@
1
1
  module TypeProf
2
- VERSION = "0.13.0"
2
+ VERSION = "0.14.0"
3
3
  end
data/smoke/break4.rb ADDED
@@ -0,0 +1,17 @@
1
+ # The following "break" is compiled as "throw" instruction since https://github.com/ruby/ruby/commit/34bc8210ed1624dc6ba24afef4616baa5a934df9
2
+ def foo
3
+ begin
4
+ while true
5
+ break 1
6
+ end
7
+ rescue
8
+ "foo"
9
+ end
10
+ end
11
+
12
+ __END__
13
+ # Classes
14
+ class Object
15
+ private
16
+ def foo: -> (Integer | String)
17
+ end
data/smoke/flow11.rb ADDED
@@ -0,0 +1,17 @@
1
+ def foo(arg)
2
+ if Integer === arg
3
+ arg
4
+ else
5
+ 42
6
+ end
7
+ end
8
+
9
+ foo("str")
10
+ foo(1)
11
+
12
+ __END__
13
+ # Classes
14
+ class Object
15
+ private
16
+ def foo: (Integer | String arg) -> Integer
17
+ 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.13.0
4
+ version: 0.14.0
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-03-24 00:00:00.000000000 Z
11
+ date: 2021-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rbs
@@ -138,6 +138,7 @@ files:
138
138
  - smoke/break1.rb
139
139
  - smoke/break2.rb
140
140
  - smoke/break3.rb
141
+ - smoke/break4.rb
141
142
  - smoke/case.rb
142
143
  - smoke/case2.rb
143
144
  - smoke/case3.rb
@@ -192,6 +193,7 @@ files:
192
193
  - smoke/flip-flop.rb
193
194
  - smoke/flow1.rb
194
195
  - smoke/flow10.rb
196
+ - smoke/flow11.rb
195
197
  - smoke/flow2.rb
196
198
  - smoke/flow3.rb
197
199
  - smoke/flow4.rb
@@ -409,7 +411,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
409
411
  - !ruby/object:Gem::Version
410
412
  version: '0'
411
413
  requirements: []
412
- rubygems_version: 3.2.3
414
+ rubygems_version: 3.3.0.dev
413
415
  signing_key:
414
416
  specification_version: 4
415
417
  summary: TypeProf is a type analysis tool for Ruby code based on abstract interpretation