typeprof 0.13.0 → 0.14.0

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: 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