typeprof 0.2.0 → 0.5.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/.github/workflows/main.yml +1 -2
- data/.gitignore +1 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +10 -21
- data/LICENSE +21 -0
- data/README.md +6 -0
- data/doc/demo.md +398 -0
- data/doc/doc.ja.md +6 -1
- data/doc/doc.md +7 -2
- data/exe/typeprof +2 -1
- data/lib/typeprof.rb +9 -0
- data/lib/typeprof/analyzer.rb +427 -325
- data/lib/typeprof/arguments.rb +405 -0
- data/lib/typeprof/block.rb +136 -0
- data/lib/typeprof/builtin.rb +36 -25
- data/lib/typeprof/cli.rb +51 -98
- data/lib/typeprof/config.rb +114 -0
- data/lib/typeprof/container-type.rb +280 -92
- data/lib/typeprof/export.rb +197 -108
- data/lib/typeprof/import.rb +134 -80
- data/lib/typeprof/iseq.rb +42 -1
- data/lib/typeprof/method.rb +178 -82
- data/lib/typeprof/type.rb +228 -369
- data/lib/typeprof/utils.rb +4 -18
- data/lib/typeprof/version.rb +3 -0
- data/smoke/arguments2.rb +55 -0
- data/smoke/array-each3.rb +1 -4
- data/smoke/array12.rb +1 -1
- data/smoke/array6.rb +1 -0
- data/smoke/block-ambiguous.rb +36 -0
- data/smoke/block-args1-rest.rb +62 -0
- data/smoke/block-args1.rb +59 -0
- data/smoke/block-args2-rest.rb +62 -0
- data/smoke/block-args2.rb +59 -0
- data/smoke/block-args3-rest.rb +73 -0
- data/smoke/block-args3.rb +70 -0
- data/smoke/block-blockarg.rb +27 -0
- data/smoke/block-kwarg.rb +52 -0
- data/smoke/block10.rb +1 -1
- data/smoke/block11.rb +1 -1
- data/smoke/block14.rb +17 -0
- data/smoke/block4.rb +2 -2
- data/smoke/block5.rb +1 -0
- data/smoke/block6.rb +1 -1
- data/smoke/block7.rb +0 -2
- data/smoke/block8.rb +2 -2
- data/smoke/block9.rb +1 -1
- data/smoke/blown.rb +1 -1
- data/smoke/class-hierarchy.rb +54 -0
- data/smoke/class-hierarchy2.rb +27 -0
- data/smoke/constant1.rb +11 -6
- data/smoke/constant2.rb +2 -0
- data/smoke/cvar.rb +1 -0
- data/smoke/demo10.rb +1 -1
- data/smoke/demo8.rb +2 -2
- data/smoke/demo9.rb +1 -3
- data/smoke/flow7.rb +1 -7
- data/smoke/flow8.rb +13 -0
- data/smoke/hash-fetch.rb +3 -3
- data/smoke/hash-merge-bang.rb +11 -0
- data/smoke/hash1.rb +1 -1
- data/smoke/hash3.rb +1 -1
- data/smoke/hash4.rb +1 -1
- data/smoke/instance_eval.rb +1 -1
- data/smoke/int_times.rb +1 -1
- data/smoke/ivar2.rb +1 -1
- data/smoke/keyword3.rb +1 -2
- data/smoke/keyword4.rb +1 -1
- data/smoke/kwsplat1.rb +1 -1
- data/smoke/kwsplat2.rb +1 -1
- data/smoke/module4.rb +2 -0
- data/smoke/multiple-superclass.rb +4 -0
- data/smoke/next2.rb +1 -1
- data/smoke/optional1.rb +1 -1
- data/smoke/optional2.rb +1 -1
- data/smoke/optional3.rb +10 -0
- data/smoke/pattern-match1.rb +23 -0
- data/smoke/pattern-match2.rb +15 -0
- data/smoke/proc4.rb +1 -1
- data/smoke/rbs-extend.rb +9 -0
- data/smoke/rbs-extend.rbs +7 -0
- data/smoke/rbs-interface.rb +24 -0
- data/smoke/rbs-interface.rbs +12 -0
- data/smoke/rbs-proc1.rb +9 -0
- data/smoke/rbs-proc1.rbs +3 -0
- data/smoke/rbs-proc2.rb +20 -0
- data/smoke/rbs-proc2.rbs +3 -0
- data/smoke/rbs-proc3.rb +13 -0
- data/smoke/rbs-proc3.rbs +4 -0
- data/smoke/rbs-record.rb +17 -0
- data/smoke/rbs-record.rbs +4 -0
- data/smoke/rbs-tyvar.rb +18 -0
- data/smoke/rbs-tyvar.rbs +5 -0
- data/smoke/rbs-tyvar2.rb +20 -0
- data/smoke/rbs-tyvar2.rbs +9 -0
- data/smoke/rbs-tyvar3.rb +17 -0
- data/smoke/rbs-tyvar3.rbs +5 -0
- data/smoke/rbs-tyvar4.rb +36 -0
- data/smoke/rbs-tyvar5.rb +12 -0
- data/smoke/rbs-tyvar5.rbs +8 -0
- data/smoke/rest1.rb +1 -1
- data/smoke/rest2.rb +1 -1
- data/smoke/rest3.rb +1 -1
- data/smoke/rest5.rb +1 -1
- data/smoke/rest6.rb +1 -1
- data/smoke/retry1.rb +1 -1
- data/smoke/return.rb +1 -1
- data/smoke/singleton_method.rb +3 -0
- data/smoke/step.rb +1 -1
- data/smoke/struct.rb +4 -3
- data/smoke/struct3.rb +14 -0
- data/smoke/symbol-proc.rb +24 -0
- data/smoke/uninitialize-var.rb +12 -0
- data/smoke/user-demo.rb +15 -0
- data/smoke/wrong-extend.rb +1 -0
- data/typeprof.gemspec +4 -2
- metadata +53 -6
- data/run.sh +0 -3
- data/smoke/variadic1.rb.notyet +0 -5
data/doc/doc.ja.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# TypeProf: 抽象解釈に基づくRubyコードの型解析ツール
|
|
2
2
|
|
|
3
|
+
## とりあえずデモ
|
|
4
|
+
|
|
5
|
+
[demo.md](demo.md) を参照。
|
|
6
|
+
|
|
3
7
|
## TypeProfの使い方
|
|
4
8
|
|
|
5
9
|
app.rb を解析する。
|
|
@@ -24,7 +28,8 @@ $ typeprof sig/app.rbs app.rb -o sig/app.gen.rbs
|
|
|
24
28
|
|
|
25
29
|
* `-o OUTFILE`: 標準出力ではなく、指定ファイル名に出力する
|
|
26
30
|
* `-q`: 解析の進捗を表示しない
|
|
27
|
-
* `-v`:
|
|
31
|
+
* `-v`: `-fshow-errors`の別名
|
|
32
|
+
* `-d`: 解析の詳細ログを表示する(現状ではデバッグ用出力に近い)
|
|
28
33
|
* `-I DIR`: `require`のファイル探索ディレクトリを追加する
|
|
29
34
|
* `-r GEMNAME`: `GEMNAME`に対応するRBSをロードする
|
|
30
35
|
* `--exclude-dir DIR`: `DIR`以下のファイルの解析結果を出力から省略する。後に指定されているほうが優先される(`--include-dir foo --exclude-dir foo/bar`の場合う、foo/bar/baz.rbの結果は出力されず、foo/baz.rbの結果は出力される)。
|
data/doc/doc.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# TypeProf: A type analysis tool for Ruby code based on abstract interpretation
|
|
2
2
|
|
|
3
|
+
## Show me demo first
|
|
4
|
+
|
|
5
|
+
See [demo.md](demo.md).
|
|
6
|
+
|
|
3
7
|
## How to use TypeProf
|
|
4
8
|
|
|
5
9
|
Analyze app.rb:
|
|
@@ -24,7 +28,8 @@ Here is a list of currently avaiable options:
|
|
|
24
28
|
|
|
25
29
|
* `-o OUTFILE`: Write the analyze result to OUTFILE instead of standard output
|
|
26
30
|
* `-q`: Hide the progress indicator
|
|
27
|
-
* `-v`:
|
|
31
|
+
* `-v`: Alias to `-fshow-errors`
|
|
32
|
+
* `-d`: Show the analysis log (Currently, the log is just for debugging and may become very huge)
|
|
28
33
|
* `-I DIR`: Add `DIR` to the file search path of `require`
|
|
29
34
|
* `-r GEMNAME`: Load the RBS files of `GEMNAME`
|
|
30
35
|
* `--exclude-dir DIR`: Omit the result of files that are placed under the directory `DIR`. If there are some directory specifications, the latter one is stronger. (Assuming that `--include-dir foo --exclude-dir foo/bar` is specified, the analysis result of foo/bar/baz.rb is omitted, but foo/baz.rb is shown.)
|
|
@@ -234,7 +239,7 @@ If a method returns different abstract values, it can lead to retrospective exec
|
|
|
234
239
|
Even after TypeProf traced all programs as possible, there may be methods or blocks that aren't executed.
|
|
235
240
|
For example, a method is not executed if it is called from nowhere; this is typical for library method that has no test.
|
|
236
241
|
(Basically, when you use TypeProf, it is recommended to invoke all methods with supposed argument types.)
|
|
237
|
-
TypeProf forcibly calls these unreachable methods and blocks with `untyped` as
|
|
242
|
+
TypeProf forcibly calls these unreachable methods and blocks with `untyped` as arguments.
|
|
238
243
|
|
|
239
244
|
```
|
|
240
245
|
def foo(n)
|
data/exe/typeprof
CHANGED
data/lib/typeprof.rb
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
|
+
unless defined?(RubyVM::InstructionSequence)
|
|
2
|
+
puts "Currently, TypeProf can work on a Ruby implementation that supports RubyVM::InstructionSequence, such as CRuby."
|
|
3
|
+
exit 1
|
|
4
|
+
end
|
|
5
|
+
|
|
1
6
|
module TypeProf end
|
|
2
7
|
|
|
8
|
+
require_relative "typeprof/version"
|
|
9
|
+
require_relative "typeprof/config"
|
|
3
10
|
require_relative "typeprof/insns-def"
|
|
4
11
|
require_relative "typeprof/utils"
|
|
5
12
|
require_relative "typeprof/type"
|
|
6
13
|
require_relative "typeprof/container-type"
|
|
7
14
|
require_relative "typeprof/method"
|
|
15
|
+
require_relative "typeprof/block"
|
|
8
16
|
require_relative "typeprof/iseq"
|
|
17
|
+
require_relative "typeprof/arguments"
|
|
9
18
|
require_relative "typeprof/analyzer"
|
|
10
19
|
require_relative "typeprof/import"
|
|
11
20
|
require_relative "typeprof/export"
|
data/lib/typeprof/analyzer.rb
CHANGED
|
@@ -87,6 +87,11 @@ module TypeProf
|
|
|
87
87
|
@recv_ty = recv_ty
|
|
88
88
|
@blk_ty = blk_ty
|
|
89
89
|
@mod_func = mod_func
|
|
90
|
+
|
|
91
|
+
return if recv_ty == :top #OK
|
|
92
|
+
recv_ty.each_child_global do |ty|
|
|
93
|
+
raise ty.inspect if !ty.is_a?(Type::Instance) && !ty.is_a?(Type::Class) && ty != Type.any
|
|
94
|
+
end
|
|
90
95
|
end
|
|
91
96
|
|
|
92
97
|
attr_reader :recv_ty, :blk_ty, :mod_func
|
|
@@ -128,7 +133,9 @@ module TypeProf
|
|
|
128
133
|
other.type_params.internal_hash.each do |id, elems|
|
|
129
134
|
elems2 = type_params[id]
|
|
130
135
|
if elems2
|
|
131
|
-
|
|
136
|
+
if elems != elems2
|
|
137
|
+
type_params[id] = elems.union(elems2)
|
|
138
|
+
end
|
|
132
139
|
else
|
|
133
140
|
type_params[id] = elems
|
|
134
141
|
end
|
|
@@ -145,6 +152,8 @@ module TypeProf
|
|
|
145
152
|
tys.each do |ty|
|
|
146
153
|
raise "nil cannot be pushed to the stack" if ty.nil?
|
|
147
154
|
ty.each_child do |ty|
|
|
155
|
+
raise if ty.is_a?(Type::Var)
|
|
156
|
+
#raise if ty.is_a?(Type::Instance) && ty.klass.type_params.size > 1
|
|
148
157
|
raise "Array cannot be pushed to the stack" if ty.is_a?(Type::Array)
|
|
149
158
|
raise "Hash cannot be pushed to the stack" if ty.is_a?(Type::Hash)
|
|
150
159
|
end
|
|
@@ -176,15 +185,8 @@ module TypeProf
|
|
|
176
185
|
Env.new(@static_env, Utils.array_update(@locals, idx, ty), @stack, @type_params)
|
|
177
186
|
end
|
|
178
187
|
|
|
179
|
-
def
|
|
180
|
-
local_ty =
|
|
181
|
-
type_params = Utils::HashWrapper.new(@type_params.internal_hash.merge({ alloc_site => elems }))
|
|
182
|
-
nenv = Env.new(@static_env, @locals, @stack, type_params)
|
|
183
|
-
return nenv, local_ty
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
def deploy_hash_type(alloc_site, elems, base_ty)
|
|
187
|
-
local_ty = Type::LocalHash.new(alloc_site, base_ty)
|
|
188
|
+
def deploy_type(klass, alloc_site, elems, base_ty)
|
|
189
|
+
local_ty = klass.new(alloc_site, base_ty)
|
|
188
190
|
type_params = Utils::HashWrapper.new(@type_params.internal_hash.merge({ alloc_site => elems }))
|
|
189
191
|
nenv = Env.new(@static_env, @locals, @stack, type_params)
|
|
190
192
|
return nenv, local_ty
|
|
@@ -231,8 +233,11 @@ module TypeProf
|
|
|
231
233
|
|
|
232
234
|
@alloc_site_to_global_id = {}
|
|
233
235
|
|
|
234
|
-
@callsites, @return_envs
|
|
236
|
+
@callsites, @return_envs = {}, {}
|
|
235
237
|
@block_to_ctx = {}
|
|
238
|
+
@method_signatures = {}
|
|
239
|
+
@block_signatures = {}
|
|
240
|
+
@return_values = {}
|
|
236
241
|
@gvar_table = VarTable.new
|
|
237
242
|
|
|
238
243
|
@errors = []
|
|
@@ -245,6 +250,8 @@ module TypeProf
|
|
|
245
250
|
@loaded_features = {}
|
|
246
251
|
|
|
247
252
|
@rbs_reader = RBSReader.new
|
|
253
|
+
|
|
254
|
+
@terminated = false
|
|
248
255
|
end
|
|
249
256
|
|
|
250
257
|
attr_reader :return_envs, :loaded_features, :rbs_reader
|
|
@@ -259,7 +266,7 @@ module TypeProf
|
|
|
259
266
|
env2 = @ep2env[ep]
|
|
260
267
|
if env2
|
|
261
268
|
nenv = env2.merge(env)
|
|
262
|
-
if
|
|
269
|
+
if nenv != env2 && !@worklist.member?(ep)
|
|
263
270
|
@worklist.insert(ep.key, ep)
|
|
264
271
|
end
|
|
265
272
|
@ep2env[ep] = nenv
|
|
@@ -273,6 +280,7 @@ module TypeProf
|
|
|
273
280
|
|
|
274
281
|
class ClassDef # or ModuleDef
|
|
275
282
|
def initialize(kind, name, superclass, absolute_path)
|
|
283
|
+
raise unless name.is_a?(Array)
|
|
276
284
|
@kind = kind
|
|
277
285
|
@superclass = superclass
|
|
278
286
|
@modules = { true => {}, false => {} }
|
|
@@ -282,9 +290,10 @@ module TypeProf
|
|
|
282
290
|
@ivars = VarTable.new
|
|
283
291
|
@cvars = VarTable.new
|
|
284
292
|
@absolute_path = absolute_path
|
|
293
|
+
@namespace = nil
|
|
285
294
|
end
|
|
286
295
|
|
|
287
|
-
attr_reader :kind, :modules, :
|
|
296
|
+
attr_reader :kind, :superclass, :modules, :consts, :methods, :ivars, :cvars, :absolute_path
|
|
288
297
|
attr_accessor :name, :klass_obj
|
|
289
298
|
|
|
290
299
|
def include_module(mod, absolute_path)
|
|
@@ -306,14 +315,15 @@ module TypeProf
|
|
|
306
315
|
end
|
|
307
316
|
|
|
308
317
|
def get_constant(name)
|
|
309
|
-
@consts[name]
|
|
318
|
+
ty, = @consts[name]
|
|
319
|
+
ty || Type.any # XXX: warn?
|
|
310
320
|
end
|
|
311
321
|
|
|
312
|
-
def add_constant(name, ty)
|
|
322
|
+
def add_constant(name, ty, absolute_path)
|
|
313
323
|
if @consts[name]
|
|
314
324
|
# XXX: warn!
|
|
315
325
|
end
|
|
316
|
-
@consts[name] = ty
|
|
326
|
+
@consts[name] = [ty, absolute_path]
|
|
317
327
|
end
|
|
318
328
|
|
|
319
329
|
def get_method(mid, singleton)
|
|
@@ -376,12 +386,12 @@ module TypeProf
|
|
|
376
386
|
end
|
|
377
387
|
end
|
|
378
388
|
|
|
389
|
+
def cbase_path(cbase)
|
|
390
|
+
cbase && cbase.idx != 1 ? @class_defs[cbase.idx].name : []
|
|
391
|
+
end
|
|
392
|
+
|
|
379
393
|
def new_class(cbase, name, type_params, superclass, absolute_path)
|
|
380
|
-
|
|
381
|
-
show_name = "#{ @class_defs[cbase.idx].name }::#{ name }"
|
|
382
|
-
else
|
|
383
|
-
show_name = name.to_s
|
|
384
|
-
end
|
|
394
|
+
show_name = cbase_path(cbase) + [name]
|
|
385
395
|
idx = @class_defs.size
|
|
386
396
|
if superclass
|
|
387
397
|
if superclass == :__root__
|
|
@@ -393,14 +403,14 @@ module TypeProf
|
|
|
393
403
|
klass = Type::Class.new(:class, idx, type_params, superclass, show_name)
|
|
394
404
|
@class_defs[idx].klass_obj = klass
|
|
395
405
|
cbase ||= klass # for bootstrap
|
|
396
|
-
add_constant(cbase, name, klass)
|
|
406
|
+
add_constant(cbase, name, klass, absolute_path)
|
|
397
407
|
return klass
|
|
398
408
|
else
|
|
399
409
|
# module
|
|
400
410
|
@class_defs[idx] = ClassDef.new(:module, show_name, nil, absolute_path)
|
|
401
411
|
mod = Type::Class.new(:module, idx, type_params, nil, show_name)
|
|
402
412
|
@class_defs[idx].klass_obj = mod
|
|
403
|
-
add_constant(cbase, name, mod)
|
|
413
|
+
add_constant(cbase, name, mod, absolute_path)
|
|
404
414
|
return mod
|
|
405
415
|
end
|
|
406
416
|
end
|
|
@@ -410,8 +420,8 @@ module TypeProf
|
|
|
410
420
|
|
|
411
421
|
idx = @class_defs.size
|
|
412
422
|
superclass = Type::Builtin[:struct]
|
|
413
|
-
@class_defs[idx] = ClassDef.new(:class, "(Struct)", superclass.idx, ep.ctx.iseq.absolute_path)
|
|
414
|
-
klass = Type::Class.new(:class, idx, [], superclass, "(Struct)")
|
|
423
|
+
@class_defs[idx] = ClassDef.new(:class, ["(Anonymous Struct)"], superclass.idx, ep.ctx.iseq.absolute_path)
|
|
424
|
+
klass = Type::Class.new(:class, idx, [], superclass, "(Anonymous Struct)")
|
|
415
425
|
@class_defs[idx].klass_obj = klass
|
|
416
426
|
|
|
417
427
|
@struct_defs[ep] = klass
|
|
@@ -419,11 +429,25 @@ module TypeProf
|
|
|
419
429
|
klass
|
|
420
430
|
end
|
|
421
431
|
|
|
432
|
+
attr_accessor :namespace
|
|
433
|
+
|
|
422
434
|
def get_class_name(klass)
|
|
423
435
|
if klass == Type.any
|
|
424
436
|
"???"
|
|
425
437
|
else
|
|
426
|
-
@class_defs[klass.idx].name
|
|
438
|
+
path = @class_defs[klass.idx].name
|
|
439
|
+
if @namespace
|
|
440
|
+
i = 0
|
|
441
|
+
i += 1 while @namespace[i] && @namespace[i] == path[i]
|
|
442
|
+
if path[i]
|
|
443
|
+
path[i..].join("::")
|
|
444
|
+
else
|
|
445
|
+
path.last.to_s
|
|
446
|
+
end
|
|
447
|
+
else
|
|
448
|
+
#"::" + path.join("::")
|
|
449
|
+
path.join("::")
|
|
450
|
+
end
|
|
427
451
|
end
|
|
428
452
|
end
|
|
429
453
|
|
|
@@ -473,11 +497,11 @@ module TypeProf
|
|
|
473
497
|
Type.any
|
|
474
498
|
end
|
|
475
499
|
|
|
476
|
-
def add_constant(klass, name, value)
|
|
500
|
+
def add_constant(klass, name, value, user_defined)
|
|
477
501
|
if klass == Type.any
|
|
478
502
|
self
|
|
479
503
|
else
|
|
480
|
-
@class_defs[klass.idx].add_constant(name, value)
|
|
504
|
+
@class_defs[klass.idx].add_constant(name, value, user_defined)
|
|
481
505
|
end
|
|
482
506
|
end
|
|
483
507
|
|
|
@@ -512,14 +536,6 @@ module TypeProf
|
|
|
512
536
|
add_method(klass, mid, true, ISeqMethodDef.new(iseq, cref))
|
|
513
537
|
end
|
|
514
538
|
|
|
515
|
-
def add_typed_method(recv_ty, mid, fargs, ret_ty)
|
|
516
|
-
add_method(recv_ty.klass, mid, false, TypedMethodDef.new([[fargs, ret_ty]]))
|
|
517
|
-
end
|
|
518
|
-
|
|
519
|
-
def add_singleton_typed_method(recv_ty, mid, fargs, ret_ty)
|
|
520
|
-
add_method(recv_ty.klass, mid, true, TypedMethodDef.new([[fargs, ret_ty]]))
|
|
521
|
-
end
|
|
522
|
-
|
|
523
539
|
def set_custom_method(klass, mid, impl)
|
|
524
540
|
set_method(klass, mid, false, CustomMethodDef.new(impl))
|
|
525
541
|
end
|
|
@@ -550,23 +566,24 @@ module TypeProf
|
|
|
550
566
|
@iseq_method_to_ctxs[iseq_mdef] << ctx
|
|
551
567
|
end
|
|
552
568
|
|
|
553
|
-
def add_callsite!(callee_ctx,
|
|
569
|
+
def add_callsite!(callee_ctx, caller_ep, caller_env, &ctn)
|
|
554
570
|
@executed_iseqs << callee_ctx.iseq if callee_ctx.is_a?(Context)
|
|
555
571
|
|
|
556
572
|
@callsites[callee_ctx] ||= {}
|
|
557
573
|
@callsites[callee_ctx][caller_ep] = ctn
|
|
558
574
|
merge_return_env(caller_ep) {|env| env ? env.merge(caller_env) : caller_env }
|
|
559
575
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
@sig_fargs[callee_ctx] = fargs
|
|
576
|
+
ret_ty = @return_values[callee_ctx] ||= Type.bot
|
|
577
|
+
if ret_ty != Type.bot
|
|
578
|
+
ctn[ret_ty, caller_ep, @return_envs[caller_ep]]
|
|
564
579
|
end
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
def add_method_signature!(callee_ctx, msig)
|
|
583
|
+
if @method_signatures[callee_ctx]
|
|
584
|
+
@method_signatures[callee_ctx] = @method_signatures[callee_ctx].merge(msig)
|
|
585
|
+
else
|
|
586
|
+
@method_signatures[callee_ctx] = msig
|
|
570
587
|
end
|
|
571
588
|
end
|
|
572
589
|
|
|
@@ -574,9 +591,9 @@ module TypeProf
|
|
|
574
591
|
@return_envs[caller_ep] = yield @return_envs[caller_ep]
|
|
575
592
|
end
|
|
576
593
|
|
|
577
|
-
def
|
|
578
|
-
@
|
|
579
|
-
@
|
|
594
|
+
def add_return_value!(callee_ctx, ret_ty)
|
|
595
|
+
@return_values[callee_ctx] ||= Type.bot
|
|
596
|
+
@return_values[callee_ctx] = @return_values[callee_ctx].union(ret_ty)
|
|
580
597
|
|
|
581
598
|
@callsites[callee_ctx] ||= {}
|
|
582
599
|
@callsites[callee_ctx].each do |caller_ep, ctn|
|
|
@@ -584,19 +601,18 @@ module TypeProf
|
|
|
584
601
|
end
|
|
585
602
|
end
|
|
586
603
|
|
|
587
|
-
def
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
else
|
|
592
|
-
@yields[caller_ctx] = [aargs, Utils::MutableSet.new]
|
|
593
|
-
end
|
|
594
|
-
@yields[caller_ctx][1] << blk_ctx
|
|
604
|
+
def add_block_to_ctx!(block_body, ctx)
|
|
605
|
+
raise if !block_body.is_a?(Block)
|
|
606
|
+
@block_to_ctx[block_body] ||= Utils::MutableSet.new
|
|
607
|
+
@block_to_ctx[block_body] << ctx
|
|
595
608
|
end
|
|
596
609
|
|
|
597
|
-
def
|
|
598
|
-
@
|
|
599
|
-
|
|
610
|
+
def add_block_signature!(block_body, bsig)
|
|
611
|
+
if @block_signatures[block_body]
|
|
612
|
+
@block_signatures[block_body] = @block_signatures[block_body].merge(bsig)
|
|
613
|
+
else
|
|
614
|
+
@block_signatures[block_body] = bsig
|
|
615
|
+
end
|
|
600
616
|
end
|
|
601
617
|
|
|
602
618
|
class VarTable
|
|
@@ -617,7 +633,7 @@ module TypeProf
|
|
|
617
633
|
entry = @tbl[site] ||= Entry.new(!ep, {}, Type.bot, Utils::MutableSet.new)
|
|
618
634
|
if ep
|
|
619
635
|
if entry.rbs_declared
|
|
620
|
-
|
|
636
|
+
unless Type.match?(ty, entry.type)
|
|
621
637
|
scratch.warn(ep, "inconsistent assignment to RBS-declared global variable")
|
|
622
638
|
return
|
|
623
639
|
end
|
|
@@ -636,6 +652,7 @@ module TypeProf
|
|
|
636
652
|
end
|
|
637
653
|
|
|
638
654
|
def get_ivar(recv)
|
|
655
|
+
recv = recv.base_type while recv.respond_to?(:base_type)
|
|
639
656
|
case recv
|
|
640
657
|
when Type::Class
|
|
641
658
|
[@class_defs[recv.idx], true]
|
|
@@ -718,7 +735,7 @@ module TypeProf
|
|
|
718
735
|
env.get_container_elem_types(id)
|
|
719
736
|
end
|
|
720
737
|
|
|
721
|
-
def update_container_elem_types(env, ep, id)
|
|
738
|
+
def update_container_elem_types(env, ep, id, base_type)
|
|
722
739
|
if ep.outer
|
|
723
740
|
tmp_ep = ep
|
|
724
741
|
tmp_ep = tmp_ep.outer while tmp_ep.outer
|
|
@@ -728,7 +745,7 @@ module TypeProf
|
|
|
728
745
|
menv = menv.update_container_elem_types(id, elems)
|
|
729
746
|
gid = @alloc_site_to_global_id[id]
|
|
730
747
|
if gid
|
|
731
|
-
ty = globalize_type(elems.to_local_type(id), env, ep)
|
|
748
|
+
ty = globalize_type(elems.to_local_type(id, base_type), env, ep)
|
|
732
749
|
add_ivar_write!(*gid, ty, ep)
|
|
733
750
|
end
|
|
734
751
|
menv
|
|
@@ -740,7 +757,7 @@ module TypeProf
|
|
|
740
757
|
env = env.update_container_elem_types(id, elems)
|
|
741
758
|
gid = @alloc_site_to_global_id[id]
|
|
742
759
|
if gid
|
|
743
|
-
ty = globalize_type(elems.to_local_type(id), env, ep)
|
|
760
|
+
ty = globalize_type(elems.to_local_type(id, base_type), env, ep)
|
|
744
761
|
add_ivar_write!(*gid, ty, ep)
|
|
745
762
|
end
|
|
746
763
|
env
|
|
@@ -752,7 +769,7 @@ module TypeProf
|
|
|
752
769
|
|
|
753
770
|
if elems
|
|
754
771
|
return elems[idx] || Type.nil if idx
|
|
755
|
-
return elems.
|
|
772
|
+
return elems.squash_or_any
|
|
756
773
|
else
|
|
757
774
|
Type.any
|
|
758
775
|
end
|
|
@@ -769,27 +786,35 @@ module TypeProf
|
|
|
769
786
|
end
|
|
770
787
|
|
|
771
788
|
def type_profile
|
|
772
|
-
|
|
773
|
-
|
|
789
|
+
start_time = tick = Time.now
|
|
790
|
+
iter_counter = 0
|
|
774
791
|
stat_eps = Utils::MutableSet.new
|
|
792
|
+
|
|
775
793
|
while true
|
|
776
794
|
until @worklist.empty?
|
|
777
|
-
|
|
795
|
+
ep = @worklist.deletemin
|
|
778
796
|
|
|
779
|
-
|
|
797
|
+
iter_counter += 1
|
|
780
798
|
if Config.verbose >= 1
|
|
781
|
-
|
|
782
|
-
if
|
|
783
|
-
|
|
784
|
-
$stderr << "\rType Profiling... (%d steps @ %s)\e[K" % [
|
|
799
|
+
tick2 = Time.now
|
|
800
|
+
if tick2 - tick >= 1
|
|
801
|
+
tick = tick2
|
|
802
|
+
$stderr << "\rType Profiling... (%d steps @ %s)\e[K" % [iter_counter, ep.source_location]
|
|
785
803
|
$stderr.flush
|
|
786
804
|
end
|
|
787
805
|
end
|
|
788
806
|
|
|
789
|
-
|
|
790
|
-
|
|
807
|
+
if (Config.max_sec && Time.now - start_time >= Config.max_sec) || (Config.max_iter && Config.max_iter <= iter_counter)
|
|
808
|
+
@terminated = true
|
|
809
|
+
break
|
|
810
|
+
end
|
|
811
|
+
|
|
812
|
+
stat_eps << ep
|
|
813
|
+
step(ep)
|
|
791
814
|
end
|
|
792
815
|
|
|
816
|
+
break if @terminated
|
|
817
|
+
|
|
793
818
|
# XXX: it would be good to provide no-dummy-execution mode.
|
|
794
819
|
# It should work as a bit smarter "rbs prototype rb";
|
|
795
820
|
# show all method definitions as "untyped" arguments and return values
|
|
@@ -809,41 +834,11 @@ module TypeProf
|
|
|
809
834
|
merge_env(ep, env)
|
|
810
835
|
add_iseq_method_call!(meth, ep.ctx)
|
|
811
836
|
|
|
812
|
-
fargs_format = iseq.fargs_format
|
|
813
|
-
lead_tys = [Type.any] * (fargs_format[:lead_num] || 0)
|
|
814
|
-
opt_tys = fargs_format[:opt] ? [] : nil
|
|
815
|
-
post_tys = [Type.any] * (fargs_format[:post_num] || 0)
|
|
816
|
-
if fargs_format[:kwbits]
|
|
817
|
-
kw_tys = []
|
|
818
|
-
fargs_format[:keyword].each do |kw|
|
|
819
|
-
case
|
|
820
|
-
when kw.is_a?(Symbol) # required keyword
|
|
821
|
-
key = kw
|
|
822
|
-
req = true
|
|
823
|
-
ty = Type.any
|
|
824
|
-
when kw.size == 2 # optional keyword (default value is a literal)
|
|
825
|
-
key, ty = *kw
|
|
826
|
-
ty = Type.guess_literal_type(ty)
|
|
827
|
-
ty = ty.type if ty.is_a?(Type::Literal)
|
|
828
|
-
else # optional keyword
|
|
829
|
-
key, = kw
|
|
830
|
-
req = false
|
|
831
|
-
ty = Type.any
|
|
832
|
-
end
|
|
833
|
-
kw_tys << [req, key, ty]
|
|
834
|
-
end
|
|
835
|
-
else
|
|
836
|
-
kw_tys = nil
|
|
837
|
-
end
|
|
838
|
-
fargs = FormalArguments.new(lead_tys, opt_tys, nil, post_tys, kw_tys, nil, nil)
|
|
839
|
-
add_callsite!(ep.ctx, fargs, nil, nil) do |_ret_ty, _ep, _env|
|
|
840
|
-
# ignore
|
|
841
|
-
end
|
|
842
|
-
|
|
843
837
|
when :block
|
|
844
|
-
epenvs = dummy_continuation
|
|
838
|
+
blk, epenvs = dummy_continuation
|
|
845
839
|
epenvs.each do |ep, env|
|
|
846
840
|
merge_env(ep, env)
|
|
841
|
+
add_block_to_ctx!(blk.block_body, ep.ctx)
|
|
847
842
|
end
|
|
848
843
|
end
|
|
849
844
|
end
|
|
@@ -853,18 +848,15 @@ module TypeProf
|
|
|
853
848
|
end
|
|
854
849
|
|
|
855
850
|
def report(stat_eps, output)
|
|
851
|
+
Reporters.show_message(@terminated, output)
|
|
852
|
+
|
|
856
853
|
Reporters.show_error(@errors, @backward_edges, output)
|
|
857
854
|
|
|
858
855
|
Reporters.show_reveal_types(self, @reveal_types, output)
|
|
859
856
|
|
|
860
857
|
Reporters.show_gvars(self, @gvar_table, output)
|
|
861
858
|
|
|
862
|
-
|
|
863
|
-
# self, @include_relations, @ivar_table.write, @cvar_table.write, @class_defs
|
|
864
|
-
#).show
|
|
865
|
-
|
|
866
|
-
#return
|
|
867
|
-
RubySignatureExporter.new(self, @class_defs, @iseq_method_to_ctxs, @sig_fargs, @sig_ret, @yields).show(stat_eps, output)
|
|
859
|
+
RubySignatureExporter.new(self, @class_defs, @iseq_method_to_ctxs).show(stat_eps, output)
|
|
868
860
|
end
|
|
869
861
|
|
|
870
862
|
def globalize_type(ty, env, ep)
|
|
@@ -894,18 +886,51 @@ module TypeProf
|
|
|
894
886
|
def pend_method_execution(iseq, meth, recv, mid, cref)
|
|
895
887
|
ctx = Context.new(iseq, cref, mid)
|
|
896
888
|
ep = ExecutionPoint.new(ctx, 0, nil)
|
|
897
|
-
locals = [Type.
|
|
898
|
-
|
|
889
|
+
locals = [Type.nil] * iseq.locals.size
|
|
890
|
+
|
|
891
|
+
fargs_format = iseq.fargs_format
|
|
892
|
+
lead_num = fargs_format[:lead_num] || 0
|
|
893
|
+
post_num = fargs_format[:post_num] || 0
|
|
894
|
+
post_index = fargs_format[:post_start]
|
|
895
|
+
rest_index = fargs_format[:rest_start]
|
|
896
|
+
keyword = fargs_format[:keyword]
|
|
897
|
+
kw_index = fargs_format[:kwbits] - keyword.size if keyword
|
|
898
|
+
kwrest_index = fargs_format[:kwrest]
|
|
899
|
+
block_index = fargs_format[:block_start]
|
|
900
|
+
opt = fargs_format[:opt] || [0]
|
|
901
|
+
|
|
902
|
+
(lead_num + opt.size - 1).times {|i| locals[i] = Type.any }
|
|
903
|
+
post_num.times {|i| locals[i + post_index] = Type.any } if post_index
|
|
904
|
+
locals[rest_index] = Type.any if rest_index
|
|
905
|
+
if keyword
|
|
906
|
+
keyword.each_with_index do |kw, i|
|
|
907
|
+
case
|
|
908
|
+
when kw.is_a?(Symbol) # required keyword
|
|
909
|
+
locals[kw_index + i] = Type.any
|
|
910
|
+
when kw.size == 2 # optional keyword (default value is a literal)
|
|
911
|
+
_key, default_ty = *kw
|
|
912
|
+
default_ty = Type.guess_literal_type(default_ty)
|
|
913
|
+
default_ty = default_ty.base_type if default_ty.is_a?(Type::Literal)
|
|
914
|
+
locals[kw_index + i] = default_ty.union(Type.any)
|
|
915
|
+
else # optional keyword (default value is an expression)
|
|
916
|
+
locals[kw_index + i] = Type.any
|
|
917
|
+
end
|
|
918
|
+
end
|
|
919
|
+
end
|
|
920
|
+
locals[kwrest_index] = Type.any if kwrest_index
|
|
921
|
+
locals[block_index] = Type.nil if block_index
|
|
922
|
+
|
|
923
|
+
env = Env.new(StaticEnv.new(recv, Type.nil, false), locals, [], Utils::HashWrapper.new({}))
|
|
899
924
|
|
|
900
925
|
@pending_execution[iseq] ||= [:method, [meth, ep, env]]
|
|
901
926
|
end
|
|
902
927
|
|
|
903
|
-
def pend_block_dummy_execution(iseq, nep, nenv)
|
|
904
|
-
@pending_execution[iseq] ||= [:block, {}]
|
|
905
|
-
if @pending_execution[iseq][1][nep]
|
|
906
|
-
@pending_execution[iseq][1][nep] = @pending_execution[iseq][1][nep].merge(nenv)
|
|
928
|
+
def pend_block_dummy_execution(blk, iseq, nep, nenv)
|
|
929
|
+
@pending_execution[iseq] ||= [:block, [blk, {}]]
|
|
930
|
+
if @pending_execution[iseq][1][1][nep]
|
|
931
|
+
@pending_execution[iseq][1][1][nep] = @pending_execution[iseq][1][1][nep].merge(nenv)
|
|
907
932
|
else
|
|
908
|
-
@pending_execution[iseq][1][nep] = nenv
|
|
933
|
+
@pending_execution[iseq][1][1][nep] = nenv
|
|
909
934
|
end
|
|
910
935
|
end
|
|
911
936
|
|
|
@@ -914,7 +939,7 @@ module TypeProf
|
|
|
914
939
|
alloc_site = AllocationSite.new(ep)
|
|
915
940
|
nenv, ty = localize_type(ty, env, ep, alloc_site)
|
|
916
941
|
case ty
|
|
917
|
-
when Type::LocalArray, Type::LocalHash
|
|
942
|
+
when Type::LocalCell, Type::LocalArray, Type::LocalHash
|
|
918
943
|
@alloc_site_to_global_id[ty.id] = [recv, var] # need overwrite check??
|
|
919
944
|
end
|
|
920
945
|
yield ty, nenv
|
|
@@ -939,6 +964,67 @@ module TypeProf
|
|
|
939
964
|
end
|
|
940
965
|
|
|
941
966
|
case insn
|
|
967
|
+
when :_iseq_body_start
|
|
968
|
+
# XXX: reconstruct and record the method signature
|
|
969
|
+
iseq = ep.ctx.iseq
|
|
970
|
+
lead_num = iseq.fargs_format[:lead_num] || 0
|
|
971
|
+
opt = iseq.fargs_format[:opt] || [0]
|
|
972
|
+
rest_start = iseq.fargs_format[:rest_start]
|
|
973
|
+
post_start = iseq.fargs_format[:post_start]
|
|
974
|
+
post_num = iseq.fargs_format[:post_num] || 0
|
|
975
|
+
kw_start = iseq.fargs_format[:kwbits]
|
|
976
|
+
keyword = iseq.fargs_format[:keyword]
|
|
977
|
+
kw_start -= keyword.size if kw_start
|
|
978
|
+
kw_rest = iseq.fargs_format[:kwrest]
|
|
979
|
+
block_start = iseq.fargs_format[:block_start]
|
|
980
|
+
|
|
981
|
+
lead_tys = env.locals[0, lead_num].map {|ty| globalize_type(ty, env, ep) }
|
|
982
|
+
opt_tys = opt.size > 1 ? env.locals[lead_num, opt.size - 1].map {|ty| globalize_type(ty, env, ep) } : nil
|
|
983
|
+
if rest_start # XXX:squash
|
|
984
|
+
ty = globalize_type(env.locals[lead_num + opt.size - 1], env, ep)
|
|
985
|
+
rest_ty = Type.bot
|
|
986
|
+
ty.each_child_global do |ty|
|
|
987
|
+
if ty.is_a?(Type::Array)
|
|
988
|
+
rest_ty = rest_ty.union(ty.elems.squash)
|
|
989
|
+
else
|
|
990
|
+
# XXX: to_ary?
|
|
991
|
+
rest_ty = rest_ty.union(ty)
|
|
992
|
+
end
|
|
993
|
+
end
|
|
994
|
+
end
|
|
995
|
+
post_tys = (post_start ? env.locals[post_start, post_num] : []).map {|ty| globalize_type(ty, env, ep) }
|
|
996
|
+
if keyword
|
|
997
|
+
kw_tys = []
|
|
998
|
+
keyword.each_with_index do |kw, i|
|
|
999
|
+
case
|
|
1000
|
+
when kw.is_a?(Symbol) # required keyword
|
|
1001
|
+
key = kw
|
|
1002
|
+
req = true
|
|
1003
|
+
when kw.size == 2 # optional keyword (default value is a literal)
|
|
1004
|
+
key, default_ty = *kw
|
|
1005
|
+
default_ty = Type.guess_literal_type(default_ty)
|
|
1006
|
+
default_ty = default_ty.base_type if default_ty.is_a?(Type::Literal)
|
|
1007
|
+
req = false
|
|
1008
|
+
else # optional keyword (default value is an expression)
|
|
1009
|
+
key, = kw
|
|
1010
|
+
req = false
|
|
1011
|
+
end
|
|
1012
|
+
ty = env.locals[kw_start + i]
|
|
1013
|
+
ty = ty.union(default_ty) if default_ty
|
|
1014
|
+
ty = globalize_type(ty, env, ep)
|
|
1015
|
+
kw_tys << [req, key, ty]
|
|
1016
|
+
end
|
|
1017
|
+
end
|
|
1018
|
+
kw_rest_ty = globalize_type(env.locals[kw_rest], env, ep) if kw_rest
|
|
1019
|
+
if block_start
|
|
1020
|
+
blk_ty = globalize_type(env.locals[block_start], env, ep)
|
|
1021
|
+
elsif iseq.type == :method
|
|
1022
|
+
blk_ty = env.static_env.blk_ty
|
|
1023
|
+
else
|
|
1024
|
+
blk_ty = Type.nil
|
|
1025
|
+
end
|
|
1026
|
+
msig = MethodSignature.new(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
|
|
1027
|
+
add_method_signature!(ep.ctx, msig)
|
|
942
1028
|
when :putspecialobject
|
|
943
1029
|
kind, = operands
|
|
944
1030
|
ty = case kind
|
|
@@ -960,7 +1046,17 @@ module TypeProf
|
|
|
960
1046
|
ty = Type::Literal.new(str, Type::Instance.new(Type::Builtin[:str]))
|
|
961
1047
|
env = env.push(ty)
|
|
962
1048
|
when :putself
|
|
963
|
-
|
|
1049
|
+
ty = env.static_env.recv_ty
|
|
1050
|
+
if ty.is_a?(Type::Instance)
|
|
1051
|
+
klass = ty.klass
|
|
1052
|
+
if klass.type_params.size >= 1
|
|
1053
|
+
ty = Type::ContainerType.create_empty_instance(klass)
|
|
1054
|
+
env, ty = localize_type(ty, env, ep, AllocationSite.new(ep))
|
|
1055
|
+
else
|
|
1056
|
+
ty = Type::Instance.new(klass)
|
|
1057
|
+
end
|
|
1058
|
+
env, ty = localize_type(ty, env, ep)
|
|
1059
|
+
end
|
|
964
1060
|
env = env.push(ty)
|
|
965
1061
|
when :newarray, :newarraykwsplat
|
|
966
1062
|
len, = operands
|
|
@@ -1071,7 +1167,7 @@ module TypeProf
|
|
|
1071
1167
|
elsif superclass == Type.any
|
|
1072
1168
|
warn(ep, "superclass is any; Object is used instead")
|
|
1073
1169
|
superclass = Type::Builtin[:obj]
|
|
1074
|
-
elsif superclass
|
|
1170
|
+
elsif superclass == Type.nil
|
|
1075
1171
|
superclass = Type::Builtin[:obj]
|
|
1076
1172
|
elsif superclass.is_a?(Type::Instance)
|
|
1077
1173
|
warn(ep, "superclass is an instance; Object is used instead")
|
|
@@ -1111,14 +1207,14 @@ module TypeProf
|
|
|
1111
1207
|
locals = [Type.nil] * iseq.locals.size
|
|
1112
1208
|
nenv = Env.new(StaticEnv.new(recv, blk, false), locals, [], Utils::HashWrapper.new({}))
|
|
1113
1209
|
merge_env(nep, nenv)
|
|
1114
|
-
add_callsite!(nep.ctx,
|
|
1210
|
+
add_callsite!(nep.ctx, ep, env) do |ret_ty, ep, env|
|
|
1115
1211
|
nenv, ret_ty = localize_type(ret_ty, env, ep)
|
|
1116
1212
|
nenv = nenv.push(ret_ty)
|
|
1117
1213
|
merge_env(ep.next, nenv)
|
|
1118
1214
|
end
|
|
1119
1215
|
return
|
|
1120
1216
|
when :send
|
|
1121
|
-
env, recvs, mid, aargs = setup_actual_arguments(operands, ep, env)
|
|
1217
|
+
env, recvs, mid, aargs = setup_actual_arguments(:method, operands, ep, env)
|
|
1122
1218
|
recvs = Type.any if recvs == Type.bot
|
|
1123
1219
|
recvs.each_child do |recv|
|
|
1124
1220
|
do_send(recv, mid, aargs, ep, env) do |ret_ty, ep, env|
|
|
@@ -1130,7 +1226,7 @@ module TypeProf
|
|
|
1130
1226
|
return
|
|
1131
1227
|
when :send_branch
|
|
1132
1228
|
getlocal_operands, send_operands, branch_operands = operands
|
|
1133
|
-
env, recvs, mid, aargs = setup_actual_arguments(send_operands, ep, env)
|
|
1229
|
+
env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
|
|
1134
1230
|
recvs = Type.any if recvs == Type.bot
|
|
1135
1231
|
recvs.each_child do |recv|
|
|
1136
1232
|
do_send(recv, mid, aargs, ep, env) do |ret_ty, ep, env|
|
|
@@ -1157,23 +1253,16 @@ module TypeProf
|
|
|
1157
1253
|
end
|
|
1158
1254
|
return
|
|
1159
1255
|
when :invokeblock
|
|
1160
|
-
|
|
1161
|
-
opt, = operands
|
|
1162
|
-
_flags = opt[:flag]
|
|
1163
|
-
orig_argc = opt[:orig_argc]
|
|
1164
|
-
env, aargs = env.pop(orig_argc)
|
|
1256
|
+
env, recvs, mid, aargs = setup_actual_arguments(:block, operands, ep, env)
|
|
1165
1257
|
blk = env.static_env.blk_ty
|
|
1166
1258
|
case
|
|
1167
|
-
when blk
|
|
1259
|
+
when blk == Type.nil
|
|
1168
1260
|
env = env.push(Type.any)
|
|
1169
|
-
when blk
|
|
1261
|
+
when blk == Type.any
|
|
1170
1262
|
#warn(ep, "block is any")
|
|
1171
1263
|
env = env.push(Type.any)
|
|
1172
1264
|
else # Proc
|
|
1173
|
-
|
|
1174
|
-
#
|
|
1175
|
-
aargs = ActualArguments.new(aargs, nil, nil, blk_nil)
|
|
1176
|
-
do_invoke_block(true, env.static_env.blk_ty, aargs, ep, env) do |ret_ty, ep, env|
|
|
1265
|
+
do_invoke_block(blk, aargs, ep, env) do |ret_ty, ep, env|
|
|
1177
1266
|
nenv, ret_ty, = localize_type(ret_ty, env, ep)
|
|
1178
1267
|
nenv = nenv.push(ret_ty)
|
|
1179
1268
|
merge_env(ep.next, nenv)
|
|
@@ -1181,7 +1270,7 @@ module TypeProf
|
|
|
1181
1270
|
return
|
|
1182
1271
|
end
|
|
1183
1272
|
when :invokesuper
|
|
1184
|
-
env, recv, _, aargs = setup_actual_arguments(operands, ep, env)
|
|
1273
|
+
env, recv, _, aargs = setup_actual_arguments(:method, operands, ep, env)
|
|
1185
1274
|
|
|
1186
1275
|
env, recv = localize_type(env.static_env.recv_ty, env, ep)
|
|
1187
1276
|
mid = ep.ctx.mid
|
|
@@ -1214,7 +1303,7 @@ module TypeProf
|
|
|
1214
1303
|
end
|
|
1215
1304
|
env, (ty,) = env.pop(1)
|
|
1216
1305
|
ty = globalize_type(ty, env, ep)
|
|
1217
|
-
|
|
1306
|
+
add_return_value!(ep.ctx, ty)
|
|
1218
1307
|
return
|
|
1219
1308
|
when :throw
|
|
1220
1309
|
throwtype, = operands
|
|
@@ -1228,7 +1317,7 @@ module TypeProf
|
|
|
1228
1317
|
ty = globalize_type(ty, env, ep)
|
|
1229
1318
|
tmp_ep = ep
|
|
1230
1319
|
tmp_ep = tmp_ep.outer while tmp_ep.outer
|
|
1231
|
-
|
|
1320
|
+
add_return_value!(tmp_ep.ctx, ty)
|
|
1232
1321
|
return
|
|
1233
1322
|
when :break
|
|
1234
1323
|
tmp_ep = ep
|
|
@@ -1266,7 +1355,7 @@ module TypeProf
|
|
|
1266
1355
|
raise if iseq.locals != []
|
|
1267
1356
|
nenv = Env.new(env.static_env, [], [], nil)
|
|
1268
1357
|
merge_env(nep, nenv)
|
|
1269
|
-
add_callsite!(nep.ctx,
|
|
1358
|
+
add_callsite!(nep.ctx, ep, env) do |ret_ty, ep, env|
|
|
1270
1359
|
nenv, ret_ty = localize_type(ret_ty, env, ep)
|
|
1271
1360
|
nenv = nenv.push(ret_ty)
|
|
1272
1361
|
merge_env(ep.next, nenv)
|
|
@@ -1282,7 +1371,7 @@ module TypeProf
|
|
|
1282
1371
|
|
|
1283
1372
|
# TODO: it works for only simple cases: `x = nil; x || 1`
|
|
1284
1373
|
# It would be good to merge "dup; branchif" to make it context-sensitive-like
|
|
1285
|
-
falsy = ty
|
|
1374
|
+
falsy = ty == Type.nil
|
|
1286
1375
|
|
|
1287
1376
|
merge_env(ep_then, env)
|
|
1288
1377
|
merge_env(ep_else, env) unless branchtype == :if && falsy
|
|
@@ -1374,8 +1463,37 @@ module TypeProf
|
|
|
1374
1463
|
flow_env = env.local_update(-var_idx+2, ret_ty)
|
|
1375
1464
|
case ret_ty
|
|
1376
1465
|
when Type.any
|
|
1377
|
-
merge_env(ep_then,
|
|
1378
|
-
merge_env(ep_else,
|
|
1466
|
+
merge_env(ep_then, flow_env)
|
|
1467
|
+
merge_env(ep_else, flow_env)
|
|
1468
|
+
when Type::Instance.new(Type::Builtin[:false]), Type.nil
|
|
1469
|
+
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
|
1470
|
+
else
|
|
1471
|
+
merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
|
|
1472
|
+
end
|
|
1473
|
+
end
|
|
1474
|
+
return
|
|
1475
|
+
when :getlocal_dup_branch
|
|
1476
|
+
getlocal_operands, _dup_operands, branch_operands = operands
|
|
1477
|
+
var_idx, _scope_idx, _escaped = getlocal_operands
|
|
1478
|
+
ret_ty = env.get_local(-var_idx+2)
|
|
1479
|
+
unless ret_ty
|
|
1480
|
+
p env.locals
|
|
1481
|
+
raise
|
|
1482
|
+
end
|
|
1483
|
+
|
|
1484
|
+
branchtype, target, = branch_operands
|
|
1485
|
+
# branchtype: :if or :unless or :nil
|
|
1486
|
+
ep_then = ep.next
|
|
1487
|
+
ep_else = ep.jump(target)
|
|
1488
|
+
|
|
1489
|
+
var_idx, _scope_idx, _escaped = getlocal_operands
|
|
1490
|
+
|
|
1491
|
+
ret_ty.each_child do |ret_ty|
|
|
1492
|
+
flow_env = env.local_update(-var_idx+2, ret_ty).push(ret_ty)
|
|
1493
|
+
case ret_ty
|
|
1494
|
+
when Type.any
|
|
1495
|
+
merge_env(ep_then, flow_env)
|
|
1496
|
+
merge_env(ep_else, flow_env)
|
|
1379
1497
|
when Type::Instance.new(Type::Builtin[:false]), Type.nil
|
|
1380
1498
|
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
|
1381
1499
|
else
|
|
@@ -1399,6 +1517,10 @@ module TypeProf
|
|
|
1399
1517
|
|
|
1400
1518
|
ret_ty.each_child do |ret_ty|
|
|
1401
1519
|
flow_env = env.local_update(-var_idx+2, ret_ty)
|
|
1520
|
+
ret_ty = ret_ty.base_type if ret_ty.is_a?(Type::Symbol)
|
|
1521
|
+
ret_ty = ret_ty.base_type if ret_ty.is_a?(Type::LocalCell)
|
|
1522
|
+
ret_ty = ret_ty.base_type if ret_ty.is_a?(Type::LocalArray)
|
|
1523
|
+
ret_ty = ret_ty.base_type if ret_ty.is_a?(Type::LocalHash)
|
|
1402
1524
|
if ret_ty.is_a?(Type::Instance)
|
|
1403
1525
|
if ret_ty.klass == pattern_ty # XXX: inheritance
|
|
1404
1526
|
merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
|
|
@@ -1406,8 +1528,8 @@ module TypeProf
|
|
|
1406
1528
|
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
|
1407
1529
|
end
|
|
1408
1530
|
else
|
|
1409
|
-
merge_env(ep_then,
|
|
1410
|
-
merge_env(ep_else,
|
|
1531
|
+
merge_env(ep_then, flow_env)
|
|
1532
|
+
merge_env(ep_else, flow_env)
|
|
1411
1533
|
end
|
|
1412
1534
|
end
|
|
1413
1535
|
return
|
|
@@ -1428,11 +1550,11 @@ module TypeProf
|
|
|
1428
1550
|
when :getconstant
|
|
1429
1551
|
name, = operands
|
|
1430
1552
|
env, (cbase, _allow_nil,) = env.pop(2)
|
|
1431
|
-
if cbase
|
|
1553
|
+
if cbase == Type.nil
|
|
1432
1554
|
ty = search_constant(ep.ctx.cref, name)
|
|
1433
1555
|
env, ty = localize_type(ty, env, ep)
|
|
1434
1556
|
env = env.push(ty)
|
|
1435
|
-
elsif cbase
|
|
1557
|
+
elsif cbase == Type.any
|
|
1436
1558
|
env = env.push(Type.any) # XXX: warning needed?
|
|
1437
1559
|
else
|
|
1438
1560
|
ty = get_constant(cbase, name)
|
|
@@ -1448,10 +1570,10 @@ module TypeProf
|
|
|
1448
1570
|
end
|
|
1449
1571
|
ty.each_child do |ty|
|
|
1450
1572
|
if ty.is_a?(Type::Class) && ty.superclass == Type::Builtin[:struct]
|
|
1451
|
-
@class_defs[ty.idx].name = name
|
|
1573
|
+
@class_defs[ty.idx].name = cbase_path(cbase) + [name]
|
|
1452
1574
|
end
|
|
1453
1575
|
end
|
|
1454
|
-
add_constant(cbase, name, globalize_type(ty, env, ep))
|
|
1576
|
+
add_constant(cbase, name, globalize_type(ty, env, ep), ep.ctx.iseq.absolute_path)
|
|
1455
1577
|
|
|
1456
1578
|
when :getspecial
|
|
1457
1579
|
key, type = operands
|
|
@@ -1480,6 +1602,28 @@ module TypeProf
|
|
|
1480
1602
|
when :dup
|
|
1481
1603
|
env, (ty,) = env.pop(1)
|
|
1482
1604
|
env = env.push(ty).push(ty)
|
|
1605
|
+
when :dup_branch
|
|
1606
|
+
_dup_operands, branch_operands = operands
|
|
1607
|
+
env, (ty,) = env.pop(1)
|
|
1608
|
+
|
|
1609
|
+
branchtype, target, = branch_operands
|
|
1610
|
+
# branchtype: :if or :unless or :nil
|
|
1611
|
+
ep_then = ep.next
|
|
1612
|
+
ep_else = ep.jump(target)
|
|
1613
|
+
|
|
1614
|
+
ty.each_child do |ty|
|
|
1615
|
+
flow_env = env.push(ty)
|
|
1616
|
+
case ty
|
|
1617
|
+
when Type.any
|
|
1618
|
+
merge_env(ep_then, flow_env)
|
|
1619
|
+
merge_env(ep_else, flow_env)
|
|
1620
|
+
when Type::Instance.new(Type::Builtin[:false]), Type.nil
|
|
1621
|
+
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
|
1622
|
+
else
|
|
1623
|
+
merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
|
|
1624
|
+
end
|
|
1625
|
+
end
|
|
1626
|
+
return
|
|
1483
1627
|
when :duphash
|
|
1484
1628
|
raw_hash, = operands
|
|
1485
1629
|
ty = Type.guess_literal_type(raw_hash)
|
|
@@ -1569,11 +1713,11 @@ module TypeProf
|
|
|
1569
1713
|
if ary2.is_a?(Type::LocalArray)
|
|
1570
1714
|
elems2 = get_container_elem_types(env, ep, ary2.id)
|
|
1571
1715
|
elems = Type::Array::Elements.new([], elems1.squash.union(elems2.squash))
|
|
1572
|
-
env = update_container_elem_types(env, ep, ary1.id) { elems }
|
|
1716
|
+
env = update_container_elem_types(env, ep, ary1.id, ary1.base_type) { elems }
|
|
1573
1717
|
env = env.push(ary1)
|
|
1574
1718
|
else
|
|
1575
1719
|
elems = Type::Array::Elements.new([], Type.any)
|
|
1576
|
-
env = update_container_elem_types(env, ep, ary1.id) { elems }
|
|
1720
|
+
env = update_container_elem_types(env, ep, ary1.id, ary1.base_type) { elems }
|
|
1577
1721
|
env = env.push(ary1)
|
|
1578
1722
|
end
|
|
1579
1723
|
else
|
|
@@ -1583,15 +1727,27 @@ module TypeProf
|
|
|
1583
1727
|
end
|
|
1584
1728
|
|
|
1585
1729
|
when :checktype
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
if res
|
|
1592
|
-
ty = Type::Instance.new(Type::Builtin[:true])
|
|
1730
|
+
kind, = operands
|
|
1731
|
+
case kind
|
|
1732
|
+
when 5 then klass = :str # T_STRING
|
|
1733
|
+
when 7 then klass = :ary # T_ARRAY
|
|
1734
|
+
when 8 then klass = :hash # T_HASH
|
|
1593
1735
|
else
|
|
1594
|
-
|
|
1736
|
+
raise NotImplementedError
|
|
1737
|
+
end
|
|
1738
|
+
env, (val,) = env.pop(1)
|
|
1739
|
+
ty = Type.bot
|
|
1740
|
+
val.each_child do |val|
|
|
1741
|
+
#globalize_type(val, env, ep).each_child_global do |val|
|
|
1742
|
+
val = val.base_type while val.respond_to?(:base_type)
|
|
1743
|
+
case val
|
|
1744
|
+
when Type::Instance.new(Type::Builtin[klass])
|
|
1745
|
+
ty = ty.union(Type::Instance.new(Type::Builtin[:true]))
|
|
1746
|
+
when Type.any
|
|
1747
|
+
ty = Type.bool
|
|
1748
|
+
else
|
|
1749
|
+
ty = ty.union(Type::Instance.new(Type::Builtin[:false]))
|
|
1750
|
+
end
|
|
1595
1751
|
end
|
|
1596
1752
|
env = env.push(ty)
|
|
1597
1753
|
else
|
|
@@ -1612,7 +1768,7 @@ module TypeProf
|
|
|
1612
1768
|
locals = [Type.nil] * iseq.locals.size
|
|
1613
1769
|
nenv = Env.new(env.static_env, locals, [], Utils::HashWrapper.new({}))
|
|
1614
1770
|
merge_env(nep, nenv)
|
|
1615
|
-
add_callsite!(nep.ctx,
|
|
1771
|
+
add_callsite!(nep.ctx, cont_ep, cont_env) do |ret_ty, ep, env|
|
|
1616
1772
|
nenv, ret_ty = localize_type(ret_ty, env, ep)
|
|
1617
1773
|
nenv = nenv.push(ret_ty)
|
|
1618
1774
|
merge_env(ep.jump(cont), nenv)
|
|
@@ -1644,13 +1800,13 @@ module TypeProf
|
|
|
1644
1800
|
merge_env(ep.next, env)
|
|
1645
1801
|
end
|
|
1646
1802
|
|
|
1647
|
-
private def setup_actual_arguments(operands, ep, env)
|
|
1803
|
+
private def setup_actual_arguments(kind, operands, ep, env)
|
|
1648
1804
|
opt, blk_iseq = operands
|
|
1649
1805
|
flags = opt[:flag]
|
|
1650
1806
|
mid = opt[:mid]
|
|
1651
1807
|
kw_arg = opt[:kw_arg]
|
|
1652
1808
|
argc = opt[:orig_argc]
|
|
1653
|
-
argc += 1 # receiver
|
|
1809
|
+
argc += 1 if kind == :method # for the receiver
|
|
1654
1810
|
argc += kw_arg.size if kw_arg
|
|
1655
1811
|
|
|
1656
1812
|
flag_args_splat = flags[ 0] != 0
|
|
@@ -1665,29 +1821,36 @@ module TypeProf
|
|
|
1665
1821
|
_flag_super = flags[ 9] != 0
|
|
1666
1822
|
_flag_zsuper = flags[10] != 0
|
|
1667
1823
|
|
|
1824
|
+
argc += 1 if flag_args_blockarg
|
|
1825
|
+
|
|
1826
|
+
env, aargs = env.pop(argc)
|
|
1827
|
+
|
|
1828
|
+
recv = aargs.shift if kind == :method
|
|
1829
|
+
|
|
1668
1830
|
if flag_args_blockarg
|
|
1669
|
-
|
|
1670
|
-
|
|
1831
|
+
blk_ty = aargs.pop
|
|
1832
|
+
elsif blk_iseq
|
|
1833
|
+
blk_ty = Type::Proc.new(ISeqBlock.new(blk_iseq, ep), Type::Instance.new(Type::Builtin[:proc]))
|
|
1671
1834
|
else
|
|
1672
|
-
|
|
1673
|
-
if blk_iseq
|
|
1674
|
-
# check
|
|
1675
|
-
blk_ty = Type::ISeqProc.new(blk_iseq, ep, Type::Instance.new(Type::Builtin[:proc]))
|
|
1676
|
-
else
|
|
1677
|
-
blk_ty = Type.nil
|
|
1678
|
-
end
|
|
1835
|
+
blk_ty = Type.nil
|
|
1679
1836
|
end
|
|
1680
1837
|
|
|
1838
|
+
new_blk_ty = Type.bot
|
|
1681
1839
|
blk_ty.each_child do |blk_ty|
|
|
1682
1840
|
case blk_ty
|
|
1683
1841
|
when Type.nil
|
|
1684
1842
|
when Type.any
|
|
1685
|
-
when Type::
|
|
1843
|
+
when Type::Proc
|
|
1844
|
+
when Type::Symbol
|
|
1845
|
+
blk_ty = Type::Proc.new(SymbolBlock.new(blk_ty.sym), Type::Instance.new(Type::Builtin[:proc]))
|
|
1686
1846
|
else
|
|
1847
|
+
# XXX: attempt to call to_proc
|
|
1687
1848
|
error(ep, "wrong argument type #{ blk_ty.screen_name(self) } (expected Proc)")
|
|
1688
1849
|
blk_ty = Type.any
|
|
1689
1850
|
end
|
|
1851
|
+
new_blk_ty = new_blk_ty.union(blk_ty)
|
|
1690
1852
|
end
|
|
1853
|
+
blk_ty = new_blk_ty
|
|
1691
1854
|
|
|
1692
1855
|
if flag_args_splat
|
|
1693
1856
|
# assert !flag_args_kwarg
|
|
@@ -1699,65 +1862,69 @@ module TypeProf
|
|
|
1699
1862
|
_, (ty,) = ty.elems.take_last(1)
|
|
1700
1863
|
case ty
|
|
1701
1864
|
when Type::Hash
|
|
1702
|
-
|
|
1865
|
+
kw_tys = ty.elems.to_keywords
|
|
1703
1866
|
when Type::Union
|
|
1704
1867
|
hash_elems = nil
|
|
1705
1868
|
ty.elems&.each do |(container_kind, base_type), elems|
|
|
1706
1869
|
if container_kind == Type::Hash
|
|
1870
|
+
elems.to_keywords
|
|
1707
1871
|
hash_elems = hash_elems ? hash_elems.union(elems) : elems
|
|
1708
1872
|
end
|
|
1709
1873
|
end
|
|
1710
|
-
hash_elems
|
|
1711
|
-
|
|
1874
|
+
if hash_elems
|
|
1875
|
+
kw_tys = hash_elems.to_keywords
|
|
1876
|
+
else
|
|
1877
|
+
kw_tys = { nil => Type.any }
|
|
1878
|
+
end
|
|
1712
1879
|
else
|
|
1713
1880
|
warn(ep, "non hash is passed to **kwarg?") unless ty == Type.any
|
|
1714
|
-
|
|
1881
|
+
kw_tys = { nil => Type.any }
|
|
1715
1882
|
end
|
|
1716
1883
|
else
|
|
1717
1884
|
raise NotImplementedError
|
|
1718
1885
|
end
|
|
1719
|
-
|
|
1886
|
+
else
|
|
1887
|
+
kw_tys = {}
|
|
1720
1888
|
end
|
|
1721
|
-
aargs = ActualArguments.new(aargs, rest_ty,
|
|
1889
|
+
aargs = ActualArguments.new(aargs, rest_ty, kw_tys, blk_ty)
|
|
1722
1890
|
elsif flag_args_kw_splat
|
|
1723
1891
|
last = aargs.last
|
|
1724
1892
|
ty = globalize_type(last, env, ep)
|
|
1725
1893
|
case ty
|
|
1726
1894
|
when Type::Hash
|
|
1727
1895
|
aargs = aargs[0..-2]
|
|
1728
|
-
|
|
1896
|
+
kw_tys = ty.elems.to_keywords
|
|
1729
1897
|
when Type::Union
|
|
1730
1898
|
hash_elems = nil
|
|
1731
|
-
ty.elems
|
|
1899
|
+
ty.elems&.each do |(container_kind, base_type), elems|
|
|
1732
1900
|
if container_kind == Type::Hash
|
|
1733
1901
|
hash_elems = hash_elems ? hash_elems.union(elems) : elems
|
|
1734
1902
|
end
|
|
1735
1903
|
end
|
|
1736
|
-
hash_elems
|
|
1737
|
-
|
|
1904
|
+
if hash_elems
|
|
1905
|
+
kw_tys = hash_elems.to_keywords
|
|
1906
|
+
else
|
|
1907
|
+
kw_tys = { nil => Type.any }
|
|
1908
|
+
end
|
|
1738
1909
|
when Type::Any
|
|
1739
1910
|
aargs = aargs[0..-2]
|
|
1740
|
-
|
|
1911
|
+
kw_tys = { nil => Type.any }
|
|
1741
1912
|
else
|
|
1742
1913
|
warn(ep, "non hash is passed to **kwarg?")
|
|
1743
|
-
|
|
1914
|
+
kw_tys = { nil => Type.any }
|
|
1744
1915
|
end
|
|
1745
|
-
aargs = ActualArguments.new(aargs, nil,
|
|
1916
|
+
aargs = ActualArguments.new(aargs, nil, kw_tys, blk_ty)
|
|
1746
1917
|
elsif flag_args_kwarg
|
|
1747
1918
|
kw_vals = aargs.pop(kw_arg.size)
|
|
1748
1919
|
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
h[k_ty] = v_ty
|
|
1753
|
-
end
|
|
1920
|
+
kw_tys = {}
|
|
1921
|
+
kw_arg.zip(kw_vals) do |key, v_ty|
|
|
1922
|
+
kw_tys[key] = v_ty
|
|
1754
1923
|
end
|
|
1755
1924
|
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
aargs = ActualArguments.new(aargs, nil, kw_ty, blk_ty)
|
|
1925
|
+
aargs = ActualArguments.new(aargs, nil, kw_tys, blk_ty)
|
|
1759
1926
|
else
|
|
1760
|
-
aargs = ActualArguments.new(aargs, nil,
|
|
1927
|
+
aargs = ActualArguments.new(aargs, nil, {}, blk_ty)
|
|
1761
1928
|
end
|
|
1762
1929
|
|
|
1763
1930
|
if blk_iseq
|
|
@@ -1767,7 +1934,7 @@ module TypeProf
|
|
|
1767
1934
|
nlocals = [Type.any] * blk_iseq.locals.size
|
|
1768
1935
|
nsenv = StaticEnv.new(env.static_env.recv_ty, Type.any, env.static_env.mod_func)
|
|
1769
1936
|
nenv = Env.new(nsenv, nlocals, [], nil)
|
|
1770
|
-
pend_block_dummy_execution(blk_iseq, nep, nenv)
|
|
1937
|
+
pend_block_dummy_execution(blk_ty, blk_iseq, nep, nenv)
|
|
1771
1938
|
merge_return_env(ep) {|tenv| tenv ? tenv.merge(env) : env }
|
|
1772
1939
|
end
|
|
1773
1940
|
|
|
@@ -1781,159 +1948,94 @@ module TypeProf
|
|
|
1781
1948
|
meth.do_send(recv, mid, aargs, ep, env, self, &ctn)
|
|
1782
1949
|
end
|
|
1783
1950
|
else
|
|
1784
|
-
|
|
1951
|
+
case recv
|
|
1952
|
+
when Type::Void
|
|
1953
|
+
error(ep, "void's method is called: #{ globalize_type(recv, env, ep).screen_name(self) }##{ mid }")
|
|
1954
|
+
when Type::Any
|
|
1955
|
+
else
|
|
1785
1956
|
error(ep, "undefined method: #{ globalize_type(recv, env, ep).screen_name(self) }##{ mid }")
|
|
1786
1957
|
end
|
|
1787
1958
|
ctn[Type.any, ep, env]
|
|
1788
1959
|
end
|
|
1789
1960
|
end
|
|
1790
1961
|
|
|
1791
|
-
def do_invoke_block(
|
|
1962
|
+
def do_invoke_block(blk, aargs, ep, env, replace_recv_ty: nil, &ctn)
|
|
1792
1963
|
blk.each_child do |blk|
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
next
|
|
1796
|
-
end
|
|
1797
|
-
blk_iseq = blk.iseq
|
|
1798
|
-
blk_ep = blk.ep
|
|
1799
|
-
blk_env = @return_envs[blk_ep]
|
|
1800
|
-
blk_env = blk_env.replace_recv_ty(replace_recv_ty) if replace_recv_ty
|
|
1801
|
-
arg_blk = aargs.blk_ty
|
|
1802
|
-
aargs_ = aargs.lead_tys.map {|aarg| globalize_type(aarg, env, ep) }
|
|
1803
|
-
# XXX: aargs.opt_tys and aargs.kw_ty
|
|
1804
|
-
argc = blk_iseq.fargs_format[:lead_num] || 0
|
|
1805
|
-
# actual argc == 1, not array, formal argc == 1: yield 42 => do |x| : x=42
|
|
1806
|
-
# actual argc == 1, array, formal argc == 1: yield [42,43,44] => do |x| : x=[42,43,44]
|
|
1807
|
-
# actual argc >= 2, formal argc == 1: yield 42,43,44 => do |x| : x=42
|
|
1808
|
-
# actual argc == 1, not array, formal argc >= 2: yield 42 => do |x,y| : x,y=42,nil
|
|
1809
|
-
# actual argc == 1, array, formal argc >= 2: yield [42,43,44] => do |x,y| : x,y=42,43
|
|
1810
|
-
# actual argc >= 2, formal argc >= 2: yield 42,43,44 => do |x,y| : x,y=42,43
|
|
1811
|
-
if aargs_.size >= 2 || argc == 0
|
|
1812
|
-
aargs_.pop while argc < aargs_.size
|
|
1813
|
-
aargs_ << Type.nil while argc > aargs_.size
|
|
1964
|
+
if blk.is_a?(Type::Proc)
|
|
1965
|
+
blk.block_body.do_call(aargs, ep, env, self, replace_recv_ty: replace_recv_ty, &ctn)
|
|
1814
1966
|
else
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
aargs_ = [aarg_ty || Type.nil]
|
|
1818
|
-
else # actual argc == 1 && formal argc >= 2
|
|
1819
|
-
ary_elems = nil
|
|
1820
|
-
any_ty = nil
|
|
1821
|
-
case aarg_ty
|
|
1822
|
-
when Type::Union
|
|
1823
|
-
ary_elems = nil
|
|
1824
|
-
other_elems = nil
|
|
1825
|
-
aarg_ty.elems&.each do |(container_kind, base_type), elems|
|
|
1826
|
-
if container_kind == Type::Array
|
|
1827
|
-
ary_elems = ary_elems ? ary_elems.union(elems) : elems
|
|
1828
|
-
else
|
|
1829
|
-
other_elems = other_elems ? other_elems.union(elems) : elems
|
|
1830
|
-
end
|
|
1831
|
-
end
|
|
1832
|
-
aarg_ty = Type::Union.new(aarg_ty.types, other_elems)
|
|
1833
|
-
any_ty = Type.any if aarg_ty.types.include?(Type.any)
|
|
1834
|
-
when Type::Array
|
|
1835
|
-
ary_elems = aarg_ty.elems
|
|
1836
|
-
aarg_ty = nil
|
|
1837
|
-
when Type::Any
|
|
1838
|
-
any_ty = Type.any
|
|
1839
|
-
end
|
|
1840
|
-
aargs_ = [Type.bot] * argc
|
|
1841
|
-
aargs_[0] = aargs_[0].union(aarg_ty) if aarg_ty
|
|
1842
|
-
argc.times do |i|
|
|
1843
|
-
ty = aargs_[i]
|
|
1844
|
-
ty = ty.union(ary_elems[i]) if ary_elems
|
|
1845
|
-
ty = ty.union(Type.any) if any_ty
|
|
1846
|
-
ty = ty.union(Type.nil) if i >= 1 && aarg_ty
|
|
1847
|
-
aargs_[i] = ty
|
|
1848
|
-
end
|
|
1849
|
-
end
|
|
1967
|
+
warn(ep, "non-proc is passed as a block")
|
|
1968
|
+
ctn[Type.any, ep, env]
|
|
1850
1969
|
end
|
|
1851
|
-
locals = [Type.nil] * blk_iseq.locals.size
|
|
1852
|
-
locals[blk_iseq.fargs_format[:block_start]] = arg_blk if blk_iseq.fargs_format[:block_start]
|
|
1853
|
-
env_blk = blk_env.static_env.blk_ty
|
|
1854
|
-
nfargs = FormalArguments.new(aargs_, [], nil, [], nil, nil, env_blk)
|
|
1855
|
-
nctx = Context.new(blk_iseq, blk_ep.ctx.cref, nil)
|
|
1856
|
-
nep = ExecutionPoint.new(nctx, 0, blk_ep)
|
|
1857
|
-
nenv = Env.new(blk_env.static_env, locals, [], nil)
|
|
1858
|
-
alloc_site = AllocationSite.new(nep)
|
|
1859
|
-
aargs_.each_with_index do |ty, i|
|
|
1860
|
-
alloc_site2 = alloc_site.add_id(i)
|
|
1861
|
-
nenv, ty = localize_type(ty, nenv, nep, alloc_site2) # Use Scratch#localize_type?
|
|
1862
|
-
nenv = nenv.local_update(i, ty)
|
|
1863
|
-
end
|
|
1864
|
-
|
|
1865
|
-
merge_env(nep, nenv)
|
|
1866
|
-
|
|
1867
|
-
# caution: given_block flag is not complete
|
|
1868
|
-
#
|
|
1869
|
-
# def foo
|
|
1870
|
-
# bar do |&blk|
|
|
1871
|
-
# yield
|
|
1872
|
-
# blk.call
|
|
1873
|
-
# end
|
|
1874
|
-
# end
|
|
1875
|
-
#
|
|
1876
|
-
# yield and blk.call call different blocks.
|
|
1877
|
-
# So, a context can have two blocks.
|
|
1878
|
-
# given_block is calculated by comparing "context's block (yield target)" and "blk", but it is not a correct result
|
|
1879
|
-
|
|
1880
|
-
add_yield!(ep.ctx, globalize_type(aargs, env, ep), nep.ctx) if given_block
|
|
1881
|
-
add_block_to_ctx!(blk, nep.ctx)
|
|
1882
|
-
add_callsite!(nep.ctx, nfargs, ep, env, &ctn)
|
|
1883
1970
|
end
|
|
1884
1971
|
end
|
|
1885
1972
|
|
|
1886
|
-
def
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1973
|
+
def show_block_signature(blks)
|
|
1974
|
+
bsig = nil
|
|
1975
|
+
ret_ty = Type.bot
|
|
1976
|
+
|
|
1977
|
+
blks.each do |blk|
|
|
1978
|
+
blk.each_child_global do |blk|
|
|
1979
|
+
bsig0 = @block_signatures[blk.block_body]
|
|
1980
|
+
if bsig0
|
|
1981
|
+
if bsig
|
|
1982
|
+
bsig = bsig.merge(bsig0)
|
|
1983
|
+
else
|
|
1984
|
+
bsig = bsig0
|
|
1985
|
+
end
|
|
1986
|
+
end
|
|
1987
|
+
|
|
1988
|
+
@block_to_ctx[blk.block_body]&.each do |blk_ctx|
|
|
1989
|
+
ret_ty = ret_ty.union(@return_values[blk_ctx]) if @return_values[blk_ctx]
|
|
1892
1990
|
end
|
|
1893
|
-
else
|
|
1894
|
-
# uncalled proc? dummy execution doesn't work?
|
|
1895
|
-
#p blk
|
|
1896
1991
|
end
|
|
1897
1992
|
end
|
|
1898
|
-
|
|
1993
|
+
|
|
1994
|
+
bsig ||= BlockSignature.new([], [], nil, Type.nil)
|
|
1995
|
+
|
|
1996
|
+
bsig = bsig.screen_name(self)#, block: true)
|
|
1997
|
+
ret_ty = ret_ty.screen_name(self)
|
|
1998
|
+
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
|
1999
|
+
|
|
2000
|
+
bsig = bsig + " " if bsig != ""
|
|
2001
|
+
"{ #{ bsig }-> #{ ret_ty } }"
|
|
1899
2002
|
end
|
|
1900
2003
|
|
|
1901
|
-
def
|
|
1902
|
-
|
|
1903
|
-
blk_ctxs.each do |blk_ctx, farg_tys|
|
|
1904
|
-
if all_farg_tys
|
|
1905
|
-
all_farg_tys = all_farg_tys.merge(farg_tys)
|
|
1906
|
-
else
|
|
1907
|
-
all_farg_tys = farg_tys
|
|
1908
|
-
end
|
|
2004
|
+
def show_proc_signature(blks)
|
|
2005
|
+
farg_tys, ret_ty = nil, Type.bot
|
|
1909
2006
|
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
2007
|
+
blks.each do |blk|
|
|
2008
|
+
blk.each_child_global do |blk|
|
|
2009
|
+
next if blk.block_body.is_a?(TypedBlock) # XXX: Support TypedBlock
|
|
2010
|
+
next unless @block_to_ctx[blk.block_body] # this occurs when screen_name is called before type-profiling finished (e.g., error message)
|
|
2011
|
+
@block_to_ctx[blk.block_body].each do |blk_ctx|
|
|
2012
|
+
if farg_tys
|
|
2013
|
+
farg_tys = farg_tys.merge(@method_signatures[blk_ctx])
|
|
2014
|
+
else
|
|
2015
|
+
farg_tys = @method_signatures[blk_ctx]
|
|
2016
|
+
end
|
|
2017
|
+
|
|
2018
|
+
ret_ty = ret_ty.union(@return_values[blk_ctx]) if @return_values[blk_ctx]
|
|
2019
|
+
end
|
|
1914
2020
|
end
|
|
1915
2021
|
end
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
2022
|
+
|
|
2023
|
+
farg_tys = farg_tys ? farg_tys.screen_name(self) : "(unknown)"
|
|
2024
|
+
ret_ty = ret_ty.screen_name(self)
|
|
2025
|
+
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
|
2026
|
+
|
|
2027
|
+
farg_tys = farg_tys + " " if farg_tys != ""
|
|
2028
|
+
"^#{ farg_tys }-> #{ ret_ty }"
|
|
1919
2029
|
end
|
|
1920
2030
|
|
|
1921
|
-
def
|
|
2031
|
+
def show_method_signature(ctx)
|
|
2032
|
+
farg_tys = @method_signatures[ctx]
|
|
2033
|
+
ret_ty = @return_values[ctx] || Type.bot
|
|
2034
|
+
|
|
1922
2035
|
farg_tys = farg_tys.screen_name(self)
|
|
1923
2036
|
ret_ty = ret_ty.screen_name(self)
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
aargs, blk_ctxs = yield_data
|
|
1927
|
-
all_blk_ret_ty = Type.bot
|
|
1928
|
-
blk_ctxs.each do |blk_ctx|
|
|
1929
|
-
all_blk_ret_ty = all_blk_ret_ty.union(@sig_ret[blk_ctx])
|
|
1930
|
-
end
|
|
1931
|
-
all_blk_ret_ty = all_blk_ret_ty.screen_name(self)
|
|
1932
|
-
all_blk_ret_ty = all_blk_ret_ty.include?("|") ? "(#{ all_blk_ret_ty })" : all_blk_ret_ty
|
|
1933
|
-
s << "{ #{ aargs.screen_name(self) } -> #{ all_blk_ret_ty } } " if aargs
|
|
1934
|
-
end
|
|
1935
|
-
s << "-> "
|
|
1936
|
-
s << (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty)
|
|
2037
|
+
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
|
2038
|
+
"#{ (farg_tys.empty? ? "" : "#{ farg_tys } ") }-> #{ ret_ty }"
|
|
1937
2039
|
end
|
|
1938
2040
|
end
|
|
1939
2041
|
end
|