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 +4 -4
- data/lib/typeprof/analyzer.rb +39 -4
- data/lib/typeprof/builtin.rb +22 -0
- data/lib/typeprof/iseq.rb +97 -41
- data/lib/typeprof/version.rb +1 -1
- data/smoke/break4.rb +17 -0
- data/smoke/flow11.rb +17 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c03cc70d70c73d14871e07c098b0689e11afc8e309662a8d4402c6f2ecbc5d02
|
4
|
+
data.tar.gz: '097a04710ef19e6c8f29304c901dd8569e9e74a436dd1d76a5bf515df7fdfa41'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5348942872848edec6b679db2a9afa60b746a6d3908f7066dfcd3a551d04ab728ea1494a5bdcf66867d7bc47ba0dce6ce29e2d29833babb5413262c8da92ca1
|
7
|
+
data.tar.gz: 9b214f8a4a24fa433a9c3d7fb205246b161c723206a73db257eb57b3905b34ab7ef378324a7786b63daccdf763616667c2d5d6fcbaf98ff6ff455f26f956082e
|
data/lib/typeprof/analyzer.rb
CHANGED
@@ -1324,7 +1324,7 @@ module TypeProf
|
|
1324
1324
|
end
|
1325
1325
|
end
|
1326
1326
|
return
|
1327
|
-
when :
|
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
|
-
|
1818
|
-
|
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)
|
data/lib/typeprof/builtin.rb
CHANGED
@@ -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
|
-
#
|
210
|
-
|
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
|
-
|
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
|
-
|
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] = [:
|
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]
|
data/lib/typeprof/version.rb
CHANGED
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
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.
|
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-
|
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.
|
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
|