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