typeprof 0.10.0 → 0.14.1

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.
data/lib/typeprof/iseq.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  module TypeProf
2
2
  class ISeq
3
+ # https://github.com/ruby/ruby/pull/4468
4
+ CASE_WHEN_CHECKMATCH = RubyVM::InstructionSequence.compile("case 1; when Integer; end").to_a.last.any? {|insn,| insn == :checkmatch }
5
+
3
6
  include Utils::StructuralEquality
4
7
 
5
8
  def self.compile(file)
@@ -206,8 +209,76 @@ module TypeProf
206
209
  end
207
210
  end
208
211
 
209
- # find a pattern: getlocal, ..., send (is_a?, respond_to?), branch
210
- getlocal_send_branch_list = []
212
+ # flow-sensitive analysis for `case var; when A; when B; when C; end`
213
+ # find a pattern: getlocal, (dup, putobject(true), getconstant(class name), checkmatch, branch)* for ..Ruby 3.0
214
+ # find a pattern: getlocal, (putobject(true), getconstant(class name), top(1), send(===), branch)* for Ruby 3.1..
215
+ case_branch_list = []
216
+ if CASE_WHEN_CHECKMATCH
217
+ (@insns.size - 1).times do |i|
218
+ insn0, getlocal_operands = @insns[i]
219
+ next unless [:getlocal, :getblockparam, :getblockparamproxy].include?(insn0) && getlocal_operands[1] == 0
220
+ nops = [i]
221
+ new_insns = []
222
+ j = i + 1
223
+ 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, []]
233
+ nops << j << (j + 4) << target_pc
234
+ new_insns << [j + 5, [:getlocal_checkmatch_branch, [getlocal_operands, @insns[j + 5][1]]]]
235
+ j += 6
236
+ when [:pop, []]
237
+ nops << j
238
+ case_branch_list << [nops, new_insns]
239
+ break
240
+ else
241
+ break
242
+ end
243
+ end
244
+ end
245
+ else
246
+ (@insns.size - 1).times do |i|
247
+ insn0, getlocal_operands = @insns[i]
248
+ next unless [:getlocal, :getblockparam, :getblockparamproxy].include?(insn0) && getlocal_operands[1] == 0
249
+ nops = []
250
+ new_insns = []
251
+ j = i + 1
252
+ 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, []]
262
+ nops << (j + 4) #<< target_pc
263
+ new_insns << [j + 5, [:arg_getlocal_send_branch, [getlocal_operands, @insns[j + 4][1], @insns[j + 5][1]]]]
264
+ j += 6
265
+ when [:pop, []]
266
+ #nops << j
267
+ case_branch_list << [nops, new_insns]
268
+ break
269
+ else
270
+ break
271
+ end
272
+ end
273
+ end
274
+ end
275
+ case_branch_list.each do |nops, new_insns|
276
+ nops.each {|i| @insns[i] = [:nop, []] }
277
+ new_insns.each {|i, insn| @insns[i] = insn }
278
+ end
279
+
280
+ # find a pattern: getlocal(recv), ..., send (is_a?, respond_to?), branch
281
+ recv_getlocal_send_branch_list = []
211
282
  (@insns.size - 1).times do |i|
212
283
  insn, operands = @insns[i]
213
284
  if insn == :getlocal && operands[1] == 0
@@ -216,7 +287,7 @@ module TypeProf
216
287
  while @insns[j]
217
288
  sp = check_send_branch(sp, j)
218
289
  if sp == :match
219
- getlocal_send_branch_list << [i, j]
290
+ recv_getlocal_send_branch_list << [i, j]
220
291
  break
221
292
  end
222
293
  break if !sp
@@ -224,13 +295,35 @@ module TypeProf
224
295
  end
225
296
  end
226
297
  end
227
- getlocal_send_branch_list.each do |i, j|
298
+ recv_getlocal_send_branch_list.each do |i, j|
228
299
  next if (i + 1 .. j + 1).any? {|i| branch_targets[i] }
229
300
  _insn, getlocal_operands = @insns[i]
230
301
  _insn, send_operands = @insns[j]
231
302
  _insn, branch_operands = @insns[j + 1]
232
303
  @insns[j] = [:nop]
233
- @insns[j + 1] = [:getlocal_send_branch, [getlocal_operands, send_operands, branch_operands]]
304
+ @insns[j + 1] = [:recv_getlocal_send_branch, [getlocal_operands, send_operands, branch_operands]]
305
+ end
306
+
307
+ # find a pattern: getlocal, send (===), branch
308
+ arg_getlocal_send_branch_list = []
309
+ (@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
318
+ arg_getlocal_send_branch_list << i
319
+ end
320
+ arg_getlocal_send_branch_list.each do |i|
321
+ 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]]
234
327
  end
235
328
 
236
329
  # find a pattern: send (block_given?), branch
@@ -286,42 +379,6 @@ module TypeProf
286
379
  @insns[i + 1] = [:getlocal_branch, [getlocal_operands, branch_operands]]
287
380
  end
288
381
  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
382
  end
326
383
 
327
384
  def check_send_branch(sp, j)
@@ -367,6 +424,8 @@ module TypeProf
367
424
  sp -= argc
368
425
  return :match if insn == :send && sp == 0 && @insns[j + 1][0] == :branch
369
426
  sp += 1
427
+ when :arg_getlocal_send_branch
428
+ return # not implemented
370
429
  when :invokeblock
371
430
  opt, = operands
372
431
  sp -= opt[:orig_argc]
data/lib/typeprof/type.rb CHANGED
@@ -162,6 +162,10 @@ module TypeProf
162
162
  substitute(DummySubstitution, Config.options[:type_depth_limit])
163
163
  end
164
164
 
165
+ def include_untyped?(_scratch)
166
+ false
167
+ end
168
+
165
169
  class Any < Type
166
170
  def initialize
167
171
  end
@@ -185,6 +189,10 @@ module TypeProf
185
189
  def substitute(_subst, _depth)
186
190
  self
187
191
  end
192
+
193
+ def include_untyped?(_scratch)
194
+ true
195
+ end
188
196
  end
189
197
 
190
198
  class Void < Any
@@ -379,6 +387,17 @@ module TypeProf
379
387
  end
380
388
  ty
381
389
  end
390
+
391
+ def include_untyped?(scratch)
392
+ @types.each do |ty|
393
+ return true if ty.include_untyped?(scratch)
394
+ end
395
+ @elems&.each do |(container_kind, base_type), elems|
396
+ return true if base_type.include_untyped?(scratch)
397
+ return true if elems.include_untyped?(scratch)
398
+ end
399
+ false
400
+ end
382
401
  end
383
402
 
384
403
  def self.any
@@ -583,6 +602,10 @@ module TypeProf
583
602
  def screen_name(scratch)
584
603
  scratch.show_proc_signature([self])
585
604
  end
605
+
606
+ def include_untyped?(scratch)
607
+ false # XXX: need to check the block signatures recursively
608
+ end
586
609
  end
587
610
 
588
611
  class Symbol < Type
@@ -869,6 +892,17 @@ module TypeProf
869
892
  @blk_ty = blk_ty
870
893
  end
871
894
 
895
+ def include_untyped?(scratch)
896
+ return true if @lead_tys.any? {|ty| ty.include_untyped?(scratch) }
897
+ return true if @opt_tys.any? {|ty| ty.include_untyped?(scratch) }
898
+ return true if @rest_ty&.include_untyped?(scratch)
899
+ return true if @post_tys.any? {|ty| ty.include_untyped?(scratch) }
900
+ return true if @kw_tys&.any? {|_, _, ty| ty.include_untyped?(scratch) }
901
+ return true if @kw_rest_ty&.include_untyped?(scratch)
902
+ return true if @blk_ty&.include_untyped?(scratch)
903
+ false
904
+ end
905
+
872
906
  attr_reader :lead_tys, :opt_tys, :rest_ty, :post_tys, :kw_tys, :kw_rest_ty, :blk_ty
873
907
 
874
908
  def substitute(subst, depth)
@@ -1,3 +1,3 @@
1
1
  module TypeProf
2
- VERSION = "0.10.0"
2
+ VERSION = "0.14.1"
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
@@ -0,0 +1,22 @@
1
+ module Foo
2
+ end
3
+
4
+ module Bar
5
+ Foo.class_eval do
6
+ @foo = self
7
+ def foo
8
+ "str"
9
+ end
10
+ end
11
+ end
12
+
13
+ __END__
14
+ # Classes
15
+ module Foo
16
+ self.@foo: singleton(Foo)
17
+
18
+ def self.foo: -> String
19
+ end
20
+
21
+ module Bar
22
+ end
@@ -0,0 +1,18 @@
1
+ class Human
2
+ define_method(:fo) { }
3
+
4
+ [:a, :b].each { |m|
5
+ define_method(m) { }
6
+ }
7
+ end
8
+
9
+ Human.new.a
10
+ Human.new.b
11
+
12
+ __END__
13
+ # Classes
14
+ class Human
15
+ def fo: -> nil
16
+ def a: -> nil
17
+ def b: -> nil
18
+ end
data/smoke/extended.rb ADDED
@@ -0,0 +1,38 @@
1
+ module Foo
2
+ @extended = []
3
+ def self.extended(klass)
4
+ @extended << klass
5
+ end
6
+ end
7
+
8
+ class C
9
+ extend Foo
10
+ end
11
+
12
+ class D
13
+ extend Foo
14
+ end
15
+
16
+ class E
17
+ extend Foo
18
+ end
19
+
20
+ __END__
21
+ # Classes
22
+ module Foo
23
+ self.@extended: Array[singleton(C) | singleton(D) | singleton(E)]
24
+
25
+ def self.extended: (singleton(C) | singleton(D) | singleton(E) klass) -> (Array[singleton(C) | singleton(D) | singleton(E)])
26
+ end
27
+
28
+ class C
29
+ extend Foo
30
+ end
31
+
32
+ class D
33
+ extend Foo
34
+ end
35
+
36
+ class E
37
+ extend Foo
38
+ 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
data/smoke/included.rb ADDED
@@ -0,0 +1,38 @@
1
+ module Foo
2
+ @included = []
3
+ def self.included(klass)
4
+ @included << klass
5
+ end
6
+ end
7
+
8
+ class C
9
+ include Foo
10
+ end
11
+
12
+ class D
13
+ include Foo
14
+ end
15
+
16
+ class E
17
+ include Foo
18
+ end
19
+
20
+ __END__
21
+ # Classes
22
+ module Foo
23
+ self.@included: Array[singleton(C) | singleton(D) | singleton(E)]
24
+
25
+ def self.included: (singleton(C) | singleton(D) | singleton(E) klass) -> (Array[singleton(C) | singleton(D) | singleton(E)])
26
+ end
27
+
28
+ class C
29
+ include Foo
30
+ end
31
+
32
+ class D
33
+ include Foo
34
+ end
35
+
36
+ class E
37
+ include Foo
38
+ end
@@ -0,0 +1,26 @@
1
+ class Foo
2
+ @inherited = []
3
+ def self.inherited(klass)
4
+ @inherited << klass
5
+ end
6
+ end
7
+
8
+ class Bar < Foo
9
+ end
10
+
11
+ class Baz < Foo
12
+ end
13
+
14
+ __END__
15
+ # Classes
16
+ class Foo
17
+ self.@inherited: Array[singleton(Bar) | singleton(Baz)]
18
+
19
+ def self.inherited: (singleton(Bar) | singleton(Baz) klass) -> (Array[singleton(Bar) | singleton(Baz)])
20
+ end
21
+
22
+ class Bar < Foo
23
+ end
24
+
25
+ class Baz < Foo
26
+ end
@@ -13,6 +13,6 @@ end
13
13
  __END__
14
14
  # Classes
15
15
  class C
16
- def self.foo: { (C) -> nil } -> C
16
+ def self.foo: { (C) -> nil } -> nil
17
17
  def log: (Integer n) -> nil
18
18
  end