typeprof 0.1.2 → 0.4.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/.gitignore +3 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +10 -21
- data/LICENSE +21 -0
- data/README.md +1 -1
- data/doc/demo.md +398 -0
- data/doc/doc.ja.md +11 -1
- data/doc/doc.md +16 -7
- data/exe/typeprof +2 -1
- data/lib/typeprof.rb +9 -0
- data/lib/typeprof/analyzer.rb +455 -364
- data/lib/typeprof/arguments.rb +397 -0
- data/lib/typeprof/block.rb +133 -0
- data/lib/typeprof/builtin.rb +125 -116
- data/lib/typeprof/cli.rb +62 -71
- data/lib/typeprof/config.rb +114 -0
- data/lib/typeprof/container-type.rb +208 -27
- data/lib/typeprof/export.rb +201 -96
- data/lib/typeprof/import.rb +451 -365
- data/lib/typeprof/iseq.rb +43 -2
- data/lib/typeprof/method.rb +139 -100
- data/lib/typeprof/type.rb +138 -297
- 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/block11.rb +1 -1
- data/smoke/block13.rb +9 -0
- data/smoke/block13.rbs +3 -0
- 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/class.rb +2 -0
- data/smoke/constant1.rb +13 -5
- data/smoke/constant2.rb +2 -0
- data/smoke/cvar.rb +1 -0
- data/smoke/demo10.rb +1 -1
- data/smoke/demo5.rb +3 -0
- 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/gvar.rb +1 -1
- data/smoke/gvar2.rb +17 -0
- data/smoke/gvar2.rbs +1 -0
- data/smoke/hash4.rb +1 -1
- data/smoke/inheritance2.rb +6 -0
- data/smoke/instance_eval.rb +1 -1
- data/smoke/int_times.rb +1 -1
- data/smoke/ivar3.rb +16 -0
- data/smoke/ivar3.rbs +3 -0
- data/smoke/keyword3.rb +1 -2
- data/smoke/keyword4.rb +1 -1
- data/smoke/manual-rbs2.rb +1 -1
- data/smoke/manual-rbs3.rb +12 -0
- data/smoke/manual-rbs3.rbs +3 -0
- data/smoke/module4.rb +5 -0
- data/smoke/multiple-superclass.rb +12 -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/proc4.rb +1 -1
- data/smoke/rbs-alias.rb +9 -0
- data/smoke/rbs-alias.rbs +4 -0
- data/smoke/rbs-attr.rb +26 -0
- data/smoke/rbs-attr.rbs +5 -0
- 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 +25 -0
- data/smoke/rbs-tyvar3.rbs +4 -0
- data/smoke/rbs-vars.rb +39 -0
- data/smoke/rbs-vars.rbs +7 -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 +6 -2
- data/smoke/struct3.rb +14 -0
- data/smoke/super1.rb +18 -0
- data/smoke/symbol-proc.rb +24 -0
- data/smoke/union-recv.rb +6 -0
- data/smoke/user-demo.rb +15 -0
- data/smoke/wrong-extend.rb +1 -0
- data/tools/setup-insns-def.rb +1 -1
- data/tools/stackprof-wrapper.rb +1 -1
- data/typeprof.gemspec +12 -4
- metadata +68 -10
- data/.gitmodules +0 -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,13 @@ $ 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`: 解析の詳細ログを表示する(現状ではデバッグ用出力に近い)
|
|
33
|
+
* `-I DIR`: `require`のファイル探索ディレクトリを追加する
|
|
34
|
+
* `-r GEMNAME`: `GEMNAME`に対応するRBSをロードする
|
|
35
|
+
* `--exclude-dir DIR`: `DIR`以下のファイルの解析結果を出力から省略する。後に指定されているほうが優先される(`--include-dir foo --exclude-dir foo/bar`の場合う、foo/bar/baz.rbの結果は出力されず、foo/baz.rbの結果は出力される)。
|
|
36
|
+
* `--include-dir DIR`: `DIR`以下のファイルの解析結果を出力に含める。後に指定されているほうが優先される(`--exclude-dir foo --include-dir foo/bar`の場合、
|
|
37
|
+
foo/bar/baz.rbの結果は出力されるが、foo/baz.rbの結果は出力されない)。
|
|
28
38
|
* `-fshow-errors`: 実行中に見つけたバグの可能性を出力します(多くの場合、大量のfalse positiveが出ます)。
|
|
29
39
|
* `-fpedantic-output`: デフォルトでは`A | untyped`と推定されたところを単に`A`と出力しますが、より生の出力、つまり`A | untyped`と出力します。
|
|
30
40
|
* `-fshow-container-raw-elements`: (後で書く)
|
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,12 @@ 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)
|
|
33
|
+
* `-I DIR`: Add `DIR` to the file search path of `require`
|
|
34
|
+
* `-r GEMNAME`: Load the RBS files of `GEMNAME`
|
|
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.)
|
|
36
|
+
* `--include-dir DIR`: Show the result of files that are placed under the directory `DIR`. If there are some directory specifications, the latter one is stronger. (Assuming that `--exclude-dir foo --include-dir foo/bar` is specified, the analysis result of foo/bar/baz.rb is shown, but foo/baz.rb is omitted.)
|
|
28
37
|
* `-fshow-errors`: Prints out possible bugs found during execution (often a lot of false positives).
|
|
29
38
|
* `-fpedantic-output`: When TypeProf inferred a type `A | untyped`, it simply outputs `A` by default. But this option forces it to output `A | untyped`.
|
|
30
39
|
* `-fshow-container-raw-elements`: (undocumented yet)
|
|
@@ -50,7 +59,7 @@ p foo(42) #=> String
|
|
|
50
59
|
The analysis results of TypeProf are as follows.
|
|
51
60
|
|
|
52
61
|
```
|
|
53
|
-
$ ruby exe/
|
|
62
|
+
$ ruby exe/typeprof test.rb
|
|
54
63
|
# Revealed types
|
|
55
64
|
# test.rb:2 #=> Integer
|
|
56
65
|
# test.rb:6 #=> String
|
|
@@ -84,7 +93,7 @@ p Foo.new.a #=> Integer | String
|
|
|
84
93
|
```
|
|
85
94
|
|
|
86
95
|
```
|
|
87
|
-
$ ruby exe/
|
|
96
|
+
$ ruby exe/typeprof test.rb
|
|
88
97
|
# Revealed types
|
|
89
98
|
# test.rb:11 #=> Integer | String
|
|
90
99
|
|
|
@@ -230,7 +239,7 @@ If a method returns different abstract values, it can lead to retrospective exec
|
|
|
230
239
|
Even after TypeProf traced all programs as possible, there may be methods or blocks that aren't executed.
|
|
231
240
|
For example, a method is not executed if it is called from nowhere; this is typical for library method that has no test.
|
|
232
241
|
(Basically, when you use TypeProf, it is recommended to invoke all methods with supposed argument types.)
|
|
233
|
-
TypeProf forcibly calls these unreachable methods and blocks with `untyped` as
|
|
242
|
+
TypeProf forcibly calls these unreachable methods and blocks with `untyped` as arguments.
|
|
234
243
|
|
|
235
244
|
```
|
|
236
245
|
def foo(n)
|
|
@@ -299,7 +308,7 @@ class Object
|
|
|
299
308
|
def foo: (Integer) -> Integer | (String) -> String
|
|
300
309
|
end
|
|
301
310
|
```
|
|
302
|
-
|
|
311
|
+
|
|
303
312
|
```
|
|
304
313
|
# test.rb
|
|
305
314
|
def foo(n)
|
|
@@ -386,9 +395,9 @@ You can update the types; this allows the following code to initialize the array
|
|
|
386
395
|
```
|
|
387
396
|
def foo
|
|
388
397
|
a = []
|
|
389
|
-
|
|
398
|
+
|
|
390
399
|
100.times {|n| a << n.to_s}
|
|
391
|
-
|
|
400
|
+
|
|
392
401
|
a
|
|
393
402
|
end
|
|
394
403
|
|
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
|
@@ -128,7 +128,13 @@ module TypeProf
|
|
|
128
128
|
other.type_params.internal_hash.each do |id, elems|
|
|
129
129
|
elems2 = type_params[id]
|
|
130
130
|
if elems2
|
|
131
|
-
|
|
131
|
+
if elems != elems2
|
|
132
|
+
if elems.is_a?(Array) # should be refactored as Cell::Elements
|
|
133
|
+
type_params[id] = elems.zip(elems2).map {|elem1, elem2| elem1.union(elem2) }
|
|
134
|
+
else
|
|
135
|
+
type_params[id] = elems.union(elems2)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
132
138
|
else
|
|
133
139
|
type_params[id] = elems
|
|
134
140
|
end
|
|
@@ -176,15 +182,8 @@ module TypeProf
|
|
|
176
182
|
Env.new(@static_env, Utils.array_update(@locals, idx, ty), @stack, @type_params)
|
|
177
183
|
end
|
|
178
184
|
|
|
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)
|
|
185
|
+
def deploy_type(klass, alloc_site, elems, base_ty)
|
|
186
|
+
local_ty = klass.new(alloc_site, base_ty)
|
|
188
187
|
type_params = Utils::HashWrapper.new(@type_params.internal_hash.merge({ alloc_site => elems }))
|
|
189
188
|
nenv = Env.new(@static_env, @locals, @stack, type_params)
|
|
190
189
|
return nenv, local_ty
|
|
@@ -231,12 +230,13 @@ module TypeProf
|
|
|
231
230
|
|
|
232
231
|
@alloc_site_to_global_id = {}
|
|
233
232
|
|
|
234
|
-
@callsites, @return_envs
|
|
233
|
+
@callsites, @return_envs = {}, {}
|
|
235
234
|
@block_to_ctx = {}
|
|
235
|
+
@method_signatures = {}
|
|
236
|
+
@block_signatures = {}
|
|
237
|
+
@return_values = {}
|
|
236
238
|
@gvar_table = VarTable.new
|
|
237
239
|
|
|
238
|
-
@include_relations = {}
|
|
239
|
-
|
|
240
240
|
@errors = []
|
|
241
241
|
@reveal_types = {}
|
|
242
242
|
@backward_edges = {}
|
|
@@ -247,6 +247,8 @@ module TypeProf
|
|
|
247
247
|
@loaded_features = {}
|
|
248
248
|
|
|
249
249
|
@rbs_reader = RBSReader.new
|
|
250
|
+
|
|
251
|
+
@terminated = false
|
|
250
252
|
end
|
|
251
253
|
|
|
252
254
|
attr_reader :return_envs, :loaded_features, :rbs_reader
|
|
@@ -261,7 +263,7 @@ module TypeProf
|
|
|
261
263
|
env2 = @ep2env[ep]
|
|
262
264
|
if env2
|
|
263
265
|
nenv = env2.merge(env)
|
|
264
|
-
if
|
|
266
|
+
if nenv != env2 && !@worklist.member?(ep)
|
|
265
267
|
@worklist.insert(ep.key, ep)
|
|
266
268
|
end
|
|
267
269
|
@ep2env[ep] = nenv
|
|
@@ -274,48 +276,56 @@ module TypeProf
|
|
|
274
276
|
attr_reader :class_defs
|
|
275
277
|
|
|
276
278
|
class ClassDef # or ModuleDef
|
|
277
|
-
def initialize(kind, name, superclass)
|
|
279
|
+
def initialize(kind, name, superclass, absolute_path)
|
|
280
|
+
raise unless name.is_a?(Array)
|
|
278
281
|
@kind = kind
|
|
279
282
|
@superclass = superclass
|
|
280
|
-
@modules = { true =>
|
|
283
|
+
@modules = { true => {}, false => {} }
|
|
281
284
|
@name = name
|
|
282
285
|
@consts = {}
|
|
283
286
|
@methods = {}
|
|
284
287
|
@ivars = VarTable.new
|
|
285
288
|
@cvars = VarTable.new
|
|
289
|
+
@absolute_path = absolute_path
|
|
290
|
+
@namespace = nil
|
|
286
291
|
end
|
|
287
292
|
|
|
288
|
-
attr_reader :kind, :modules, :
|
|
293
|
+
attr_reader :kind, :superclass, :modules, :consts, :methods, :ivars, :cvars, :absolute_path
|
|
289
294
|
attr_accessor :name, :klass_obj
|
|
290
295
|
|
|
291
|
-
def include_module(mod,
|
|
296
|
+
def include_module(mod, absolute_path)
|
|
292
297
|
# XXX: need to check if mod is already included by the ancestors?
|
|
293
|
-
|
|
294
|
-
|
|
298
|
+
absolute_paths = @modules[false][mod]
|
|
299
|
+
unless absolute_paths
|
|
300
|
+
@modules[false][mod] = absolute_paths = Utils::MutableSet.new
|
|
295
301
|
end
|
|
302
|
+
absolute_paths << absolute_path
|
|
296
303
|
end
|
|
297
304
|
|
|
298
|
-
def extend_module(mod,
|
|
305
|
+
def extend_module(mod, absolute_path)
|
|
299
306
|
# XXX: need to check if mod is already included by the ancestors?
|
|
300
|
-
|
|
301
|
-
|
|
307
|
+
absolute_paths = @modules[true][mod]
|
|
308
|
+
unless absolute_paths
|
|
309
|
+
@modules[true][mod] = absolute_paths = Utils::MutableSet.new
|
|
302
310
|
end
|
|
311
|
+
absolute_paths << absolute_path
|
|
303
312
|
end
|
|
304
313
|
|
|
305
314
|
def get_constant(name)
|
|
306
|
-
@consts[name]
|
|
315
|
+
ty, = @consts[name]
|
|
316
|
+
ty || Type.any # XXX: warn?
|
|
307
317
|
end
|
|
308
318
|
|
|
309
|
-
def add_constant(name, ty)
|
|
319
|
+
def add_constant(name, ty, absolute_path)
|
|
310
320
|
if @consts[name]
|
|
311
321
|
# XXX: warn!
|
|
312
322
|
end
|
|
313
|
-
@consts[name] = ty
|
|
323
|
+
@consts[name] = [ty, absolute_path]
|
|
314
324
|
end
|
|
315
325
|
|
|
316
326
|
def get_method(mid, singleton)
|
|
317
327
|
@methods[[singleton, mid]] || begin
|
|
318
|
-
@modules[singleton].
|
|
328
|
+
@modules[singleton].each_key do |mod|
|
|
319
329
|
meth = mod.get_method(mid, false)
|
|
320
330
|
return meth if meth
|
|
321
331
|
end
|
|
@@ -336,22 +346,22 @@ module TypeProf
|
|
|
336
346
|
@methods[[singleton, mid]] << mdef
|
|
337
347
|
# Need to restart...?
|
|
338
348
|
end
|
|
349
|
+
|
|
350
|
+
def set_method(mid, singleton, mdef)
|
|
351
|
+
@methods[[singleton, mid]] = Utils::MutableSet.new
|
|
352
|
+
@methods[[singleton, mid]] << mdef
|
|
353
|
+
end
|
|
339
354
|
end
|
|
340
355
|
|
|
341
|
-
def include_module(including_mod, included_mod,
|
|
356
|
+
def include_module(including_mod, included_mod, absolute_path)
|
|
342
357
|
return if included_mod == Type.any
|
|
343
358
|
|
|
344
|
-
if visible
|
|
345
|
-
@include_relations[including_mod] ||= Utils::MutableSet.new
|
|
346
|
-
@include_relations[including_mod] << included_mod
|
|
347
|
-
end
|
|
348
|
-
|
|
349
359
|
including_mod = @class_defs[including_mod.idx]
|
|
350
360
|
included_mod.each_child do |included_mod|
|
|
351
361
|
if included_mod.is_a?(Type::Class)
|
|
352
362
|
included_mod = @class_defs[included_mod.idx]
|
|
353
363
|
if included_mod && included_mod.kind == :module
|
|
354
|
-
including_mod.include_module(included_mod,
|
|
364
|
+
including_mod.include_module(included_mod, absolute_path)
|
|
355
365
|
else
|
|
356
366
|
warn "including something that is not a module"
|
|
357
367
|
end
|
|
@@ -359,13 +369,13 @@ module TypeProf
|
|
|
359
369
|
end
|
|
360
370
|
end
|
|
361
371
|
|
|
362
|
-
def extend_module(extending_mod, extended_mod,
|
|
372
|
+
def extend_module(extending_mod, extended_mod, absolute_path)
|
|
363
373
|
extending_mod = @class_defs[extending_mod.idx]
|
|
364
374
|
extended_mod.each_child do |extended_mod|
|
|
365
375
|
if extended_mod.is_a?(Type::Class)
|
|
366
376
|
extended_mod = @class_defs[extended_mod.idx]
|
|
367
377
|
if extended_mod && extended_mod.kind == :module
|
|
368
|
-
extending_mod.extend_module(extended_mod,
|
|
378
|
+
extending_mod.extend_module(extended_mod, absolute_path)
|
|
369
379
|
else
|
|
370
380
|
warn "extending something that is not a module"
|
|
371
381
|
end
|
|
@@ -373,12 +383,12 @@ module TypeProf
|
|
|
373
383
|
end
|
|
374
384
|
end
|
|
375
385
|
|
|
376
|
-
def
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
386
|
+
def cbase_path(cbase)
|
|
387
|
+
cbase && cbase.idx != 1 ? @class_defs[cbase.idx].name : []
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def new_class(cbase, name, type_params, superclass, absolute_path)
|
|
391
|
+
show_name = cbase_path(cbase) + [name]
|
|
382
392
|
idx = @class_defs.size
|
|
383
393
|
if superclass
|
|
384
394
|
if superclass == :__root__
|
|
@@ -386,18 +396,18 @@ module TypeProf
|
|
|
386
396
|
else
|
|
387
397
|
superclass_idx = superclass.idx
|
|
388
398
|
end
|
|
389
|
-
@class_defs[idx] = ClassDef.new(:class, show_name, superclass_idx)
|
|
399
|
+
@class_defs[idx] = ClassDef.new(:class, show_name, superclass_idx, absolute_path)
|
|
390
400
|
klass = Type::Class.new(:class, idx, type_params, superclass, show_name)
|
|
391
401
|
@class_defs[idx].klass_obj = klass
|
|
392
402
|
cbase ||= klass # for bootstrap
|
|
393
|
-
add_constant(cbase, name, klass)
|
|
403
|
+
add_constant(cbase, name, klass, absolute_path)
|
|
394
404
|
return klass
|
|
395
405
|
else
|
|
396
406
|
# module
|
|
397
|
-
@class_defs[idx] = ClassDef.new(:module, show_name, nil)
|
|
407
|
+
@class_defs[idx] = ClassDef.new(:module, show_name, nil, absolute_path)
|
|
398
408
|
mod = Type::Class.new(:module, idx, type_params, nil, show_name)
|
|
399
409
|
@class_defs[idx].klass_obj = mod
|
|
400
|
-
add_constant(cbase, name, mod)
|
|
410
|
+
add_constant(cbase, name, mod, absolute_path)
|
|
401
411
|
return mod
|
|
402
412
|
end
|
|
403
413
|
end
|
|
@@ -407,8 +417,8 @@ module TypeProf
|
|
|
407
417
|
|
|
408
418
|
idx = @class_defs.size
|
|
409
419
|
superclass = Type::Builtin[:struct]
|
|
410
|
-
@class_defs[idx] = ClassDef.new(:class, "(Struct)", superclass.idx)
|
|
411
|
-
klass = Type::Class.new(:class, idx, [], superclass, "(Struct)")
|
|
420
|
+
@class_defs[idx] = ClassDef.new(:class, ["(Anonymous Struct)"], superclass.idx, ep.ctx.iseq.absolute_path)
|
|
421
|
+
klass = Type::Class.new(:class, idx, [], superclass, "(Anonymous Struct)")
|
|
412
422
|
@class_defs[idx].klass_obj = klass
|
|
413
423
|
|
|
414
424
|
@struct_defs[ep] = klass
|
|
@@ -416,11 +426,25 @@ module TypeProf
|
|
|
416
426
|
klass
|
|
417
427
|
end
|
|
418
428
|
|
|
429
|
+
attr_accessor :namespace
|
|
430
|
+
|
|
419
431
|
def get_class_name(klass)
|
|
420
432
|
if klass == Type.any
|
|
421
433
|
"???"
|
|
422
434
|
else
|
|
423
|
-
@class_defs[klass.idx].name
|
|
435
|
+
path = @class_defs[klass.idx].name
|
|
436
|
+
if @namespace
|
|
437
|
+
i = 0
|
|
438
|
+
i += 1 while @namespace[i] && @namespace[i] == path[i]
|
|
439
|
+
if path[i]
|
|
440
|
+
path[i..].join("::")
|
|
441
|
+
else
|
|
442
|
+
path.last.to_s
|
|
443
|
+
end
|
|
444
|
+
else
|
|
445
|
+
#"::" + path.join("::")
|
|
446
|
+
path.join("::")
|
|
447
|
+
end
|
|
424
448
|
end
|
|
425
449
|
end
|
|
426
450
|
|
|
@@ -470,11 +494,11 @@ module TypeProf
|
|
|
470
494
|
Type.any
|
|
471
495
|
end
|
|
472
496
|
|
|
473
|
-
def add_constant(klass, name, value)
|
|
497
|
+
def add_constant(klass, name, value, user_defined)
|
|
474
498
|
if klass == Type.any
|
|
475
499
|
self
|
|
476
500
|
else
|
|
477
|
-
@class_defs[klass.idx].add_constant(name, value)
|
|
501
|
+
@class_defs[klass.idx].add_constant(name, value, user_defined)
|
|
478
502
|
end
|
|
479
503
|
end
|
|
480
504
|
|
|
@@ -487,12 +511,17 @@ module TypeProf
|
|
|
487
511
|
mdef
|
|
488
512
|
end
|
|
489
513
|
|
|
490
|
-
def
|
|
514
|
+
def set_method(klass, mid, singleton, mdef)
|
|
515
|
+
@class_defs[klass.idx].set_method(mid, singleton, mdef)
|
|
516
|
+
mdef
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
def add_attr_method(klass, absolute_path, mid, ivar, kind)
|
|
491
520
|
if kind == :reader || kind == :accessor
|
|
492
|
-
add_method(klass, mid, false, AttrMethodDef.new(ivar, :reader))
|
|
521
|
+
add_method(klass, mid, false, AttrMethodDef.new(ivar, :reader, absolute_path))
|
|
493
522
|
end
|
|
494
523
|
if kind == :writer || kind == :accessor
|
|
495
|
-
add_method(klass, :"#{ mid }=", false, AttrMethodDef.new(ivar, :writer))
|
|
524
|
+
add_method(klass, :"#{ mid }=", false, AttrMethodDef.new(ivar, :writer, absolute_path))
|
|
496
525
|
end
|
|
497
526
|
end
|
|
498
527
|
|
|
@@ -504,20 +533,12 @@ module TypeProf
|
|
|
504
533
|
add_method(klass, mid, true, ISeqMethodDef.new(iseq, cref))
|
|
505
534
|
end
|
|
506
535
|
|
|
507
|
-
def
|
|
508
|
-
|
|
509
|
-
end
|
|
510
|
-
|
|
511
|
-
def add_singleton_typed_method(recv_ty, mid, fargs, ret_ty)
|
|
512
|
-
add_method(recv_ty.klass, mid, true, TypedMethodDef.new([[fargs, ret_ty]]))
|
|
513
|
-
end
|
|
514
|
-
|
|
515
|
-
def add_custom_method(klass, mid, impl)
|
|
516
|
-
add_method(klass, mid, false, CustomMethodDef.new(impl))
|
|
536
|
+
def set_custom_method(klass, mid, impl)
|
|
537
|
+
set_method(klass, mid, false, CustomMethodDef.new(impl))
|
|
517
538
|
end
|
|
518
539
|
|
|
519
|
-
def
|
|
520
|
-
|
|
540
|
+
def set_singleton_custom_method(klass, mid, impl)
|
|
541
|
+
set_method(klass, mid, true, CustomMethodDef.new(impl))
|
|
521
542
|
end
|
|
522
543
|
|
|
523
544
|
def alias_method(klass, singleton, new, old)
|
|
@@ -542,33 +563,36 @@ module TypeProf
|
|
|
542
563
|
@iseq_method_to_ctxs[iseq_mdef] << ctx
|
|
543
564
|
end
|
|
544
565
|
|
|
545
|
-
def add_callsite!(callee_ctx,
|
|
566
|
+
def add_callsite!(callee_ctx, caller_ep, caller_env, &ctn)
|
|
546
567
|
@executed_iseqs << callee_ctx.iseq if callee_ctx.is_a?(Context)
|
|
547
568
|
|
|
548
569
|
@callsites[callee_ctx] ||= {}
|
|
549
570
|
@callsites[callee_ctx][caller_ep] = ctn
|
|
550
571
|
merge_return_env(caller_ep) {|env| env ? env.merge(caller_env) : caller_env }
|
|
551
572
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
else
|
|
555
|
-
@sig_fargs[callee_ctx] = fargs
|
|
556
|
-
end
|
|
557
|
-
ret_ty = @sig_ret[callee_ctx] ||= Type.bot
|
|
558
|
-
unless ret_ty.eql?(Type.bot)
|
|
573
|
+
ret_ty = @return_values[callee_ctx] ||= Type.bot
|
|
574
|
+
if ret_ty != Type.bot
|
|
559
575
|
@callsites[callee_ctx].each do |caller_ep, ctn|
|
|
560
576
|
ctn[ret_ty, caller_ep, @return_envs[caller_ep]]
|
|
561
577
|
end
|
|
562
578
|
end
|
|
563
579
|
end
|
|
564
580
|
|
|
581
|
+
def add_method_signature!(callee_ctx, msig)
|
|
582
|
+
if @method_signatures[callee_ctx]
|
|
583
|
+
@method_signatures[callee_ctx] = @method_signatures[callee_ctx].merge(msig)
|
|
584
|
+
else
|
|
585
|
+
@method_signatures[callee_ctx] = msig
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
|
|
565
589
|
def merge_return_env(caller_ep)
|
|
566
590
|
@return_envs[caller_ep] = yield @return_envs[caller_ep]
|
|
567
591
|
end
|
|
568
592
|
|
|
569
|
-
def
|
|
570
|
-
@
|
|
571
|
-
@
|
|
593
|
+
def add_return_value!(callee_ctx, ret_ty)
|
|
594
|
+
@return_values[callee_ctx] ||= Type.bot
|
|
595
|
+
@return_values[callee_ctx] = @return_values[callee_ctx].union(ret_ty)
|
|
572
596
|
|
|
573
597
|
@callsites[callee_ctx] ||= {}
|
|
574
598
|
@callsites[callee_ctx].each do |caller_ep, ctn|
|
|
@@ -576,43 +600,54 @@ module TypeProf
|
|
|
576
600
|
end
|
|
577
601
|
end
|
|
578
602
|
|
|
579
|
-
def
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
else
|
|
584
|
-
@yields[caller_ctx] = [aargs, Utils::MutableSet.new]
|
|
585
|
-
end
|
|
586
|
-
@yields[caller_ctx][1] << blk_ctx
|
|
603
|
+
def add_block_to_ctx!(block_body, ctx)
|
|
604
|
+
raise if !block_body.is_a?(Block)
|
|
605
|
+
@block_to_ctx[block_body] ||= Utils::MutableSet.new
|
|
606
|
+
@block_to_ctx[block_body] << ctx
|
|
587
607
|
end
|
|
588
608
|
|
|
589
|
-
def
|
|
590
|
-
@
|
|
591
|
-
|
|
609
|
+
def add_block_signature!(block_body, bsig)
|
|
610
|
+
if @block_signatures[block_body]
|
|
611
|
+
@block_signatures[block_body] = @block_signatures[block_body].merge(bsig)
|
|
612
|
+
else
|
|
613
|
+
@block_signatures[block_body] = bsig
|
|
614
|
+
end
|
|
592
615
|
end
|
|
593
616
|
|
|
594
617
|
class VarTable
|
|
618
|
+
Entry = Struct.new(:rbs_declared, :read_continuations, :type, :absolute_paths)
|
|
619
|
+
|
|
595
620
|
def initialize
|
|
596
|
-
@
|
|
621
|
+
@tbl = {}
|
|
597
622
|
end
|
|
598
623
|
|
|
599
|
-
attr_reader :write
|
|
600
|
-
|
|
601
624
|
def add_read!(site, ep, &ctn)
|
|
602
|
-
@
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
ctn[
|
|
625
|
+
entry = @tbl[site] ||= Entry.new(false, {}, Type.bot, Utils::MutableSet.new)
|
|
626
|
+
entry.read_continuations[ep] = ctn
|
|
627
|
+
entry.absolute_paths << ep.ctx.iseq.absolute_path
|
|
628
|
+
ctn[entry.type, ep]
|
|
606
629
|
end
|
|
607
630
|
|
|
608
|
-
def add_write!(site, ty,
|
|
609
|
-
@
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
631
|
+
def add_write!(site, ty, ep, scratch)
|
|
632
|
+
entry = @tbl[site] ||= Entry.new(!ep, {}, Type.bot, Utils::MutableSet.new)
|
|
633
|
+
if ep
|
|
634
|
+
if entry.rbs_declared
|
|
635
|
+
if !entry.type.consistent?(ty, {})
|
|
636
|
+
scratch.warn(ep, "inconsistent assignment to RBS-declared global variable")
|
|
637
|
+
return
|
|
638
|
+
end
|
|
639
|
+
end
|
|
640
|
+
entry.absolute_paths << ep.ctx.iseq.absolute_path
|
|
641
|
+
end
|
|
642
|
+
entry.type = entry.type.union(ty)
|
|
643
|
+
entry.read_continuations.each do |ep, ctn|
|
|
613
644
|
ctn[ty, ep]
|
|
614
645
|
end
|
|
615
646
|
end
|
|
647
|
+
|
|
648
|
+
def dump
|
|
649
|
+
@tbl
|
|
650
|
+
end
|
|
616
651
|
end
|
|
617
652
|
|
|
618
653
|
def get_ivar(recv)
|
|
@@ -637,11 +672,11 @@ module TypeProf
|
|
|
637
672
|
end
|
|
638
673
|
end
|
|
639
674
|
|
|
640
|
-
def add_ivar_write!(recv, var, ty,
|
|
675
|
+
def add_ivar_write!(recv, var, ty, ep)
|
|
641
676
|
recv.each_child do |recv|
|
|
642
677
|
class_def, singleton = get_ivar(recv)
|
|
643
678
|
next unless class_def
|
|
644
|
-
class_def.ivars.add_write!([singleton, var], ty,
|
|
679
|
+
class_def.ivars.add_write!([singleton, var], ty, ep, self)
|
|
645
680
|
end
|
|
646
681
|
end
|
|
647
682
|
|
|
@@ -653,11 +688,11 @@ module TypeProf
|
|
|
653
688
|
end
|
|
654
689
|
end
|
|
655
690
|
|
|
656
|
-
def add_cvar_write!(klass, var, ty,
|
|
691
|
+
def add_cvar_write!(klass, var, ty, ep)
|
|
657
692
|
klass.each_child do |klass|
|
|
658
693
|
class_def = @class_defs[klass.idx]
|
|
659
694
|
next unless class_def
|
|
660
|
-
class_def.cvars.add_write!(var, ty,
|
|
695
|
+
class_def.cvars.add_write!(var, ty, ep, self)
|
|
661
696
|
end
|
|
662
697
|
end
|
|
663
698
|
|
|
@@ -665,8 +700,8 @@ module TypeProf
|
|
|
665
700
|
@gvar_table.add_read!(var, ep, &ctn)
|
|
666
701
|
end
|
|
667
702
|
|
|
668
|
-
def add_gvar_write!(var, ty,
|
|
669
|
-
@gvar_table.add_write!(var, ty,
|
|
703
|
+
def add_gvar_write!(var, ty, ep)
|
|
704
|
+
@gvar_table.add_write!(var, ty, ep, self)
|
|
670
705
|
end
|
|
671
706
|
|
|
672
707
|
def error(ep, msg)
|
|
@@ -698,7 +733,7 @@ module TypeProf
|
|
|
698
733
|
env.get_container_elem_types(id)
|
|
699
734
|
end
|
|
700
735
|
|
|
701
|
-
def update_container_elem_types(env, ep, id)
|
|
736
|
+
def update_container_elem_types(env, ep, id, base_type)
|
|
702
737
|
if ep.outer
|
|
703
738
|
tmp_ep = ep
|
|
704
739
|
tmp_ep = tmp_ep.outer while tmp_ep.outer
|
|
@@ -708,8 +743,8 @@ module TypeProf
|
|
|
708
743
|
menv = menv.update_container_elem_types(id, elems)
|
|
709
744
|
gid = @alloc_site_to_global_id[id]
|
|
710
745
|
if gid
|
|
711
|
-
ty = globalize_type(elems.to_local_type(id), env, ep)
|
|
712
|
-
add_ivar_write!(*gid, ty)
|
|
746
|
+
ty = globalize_type(elems.to_local_type(id, base_type), env, ep)
|
|
747
|
+
add_ivar_write!(*gid, ty, ep)
|
|
713
748
|
end
|
|
714
749
|
menv
|
|
715
750
|
end
|
|
@@ -720,8 +755,8 @@ module TypeProf
|
|
|
720
755
|
env = env.update_container_elem_types(id, elems)
|
|
721
756
|
gid = @alloc_site_to_global_id[id]
|
|
722
757
|
if gid
|
|
723
|
-
ty = globalize_type(elems.to_local_type(id), env, ep)
|
|
724
|
-
add_ivar_write!(*gid, ty)
|
|
758
|
+
ty = globalize_type(elems.to_local_type(id, base_type), env, ep)
|
|
759
|
+
add_ivar_write!(*gid, ty, ep)
|
|
725
760
|
end
|
|
726
761
|
env
|
|
727
762
|
end
|
|
@@ -732,7 +767,7 @@ module TypeProf
|
|
|
732
767
|
|
|
733
768
|
if elems
|
|
734
769
|
return elems[idx] || Type.nil if idx
|
|
735
|
-
return elems.
|
|
770
|
+
return elems.squash_or_any
|
|
736
771
|
else
|
|
737
772
|
Type.any
|
|
738
773
|
end
|
|
@@ -749,20 +784,35 @@ module TypeProf
|
|
|
749
784
|
end
|
|
750
785
|
|
|
751
786
|
def type_profile
|
|
752
|
-
|
|
787
|
+
start_time = tick = Time.now
|
|
788
|
+
iter_counter = 0
|
|
753
789
|
stat_eps = Utils::MutableSet.new
|
|
790
|
+
|
|
754
791
|
while true
|
|
755
792
|
until @worklist.empty?
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
793
|
+
ep = @worklist.deletemin
|
|
794
|
+
|
|
795
|
+
iter_counter += 1
|
|
796
|
+
if Config.verbose >= 1
|
|
797
|
+
tick2 = Time.now
|
|
798
|
+
if tick2 - tick >= 1
|
|
799
|
+
tick = tick2
|
|
800
|
+
$stderr << "\rType Profiling... (%d steps @ %s)\e[K" % [iter_counter, ep.source_location]
|
|
801
|
+
$stderr.flush
|
|
802
|
+
end
|
|
803
|
+
end
|
|
804
|
+
|
|
805
|
+
if (Config.max_sec && Time.now - start_time >= Config.max_sec) || (Config.max_iter && Config.max_iter <= iter_counter)
|
|
806
|
+
@terminated = true
|
|
807
|
+
break
|
|
760
808
|
end
|
|
761
|
-
|
|
762
|
-
stat_eps <<
|
|
763
|
-
step(
|
|
809
|
+
|
|
810
|
+
stat_eps << ep
|
|
811
|
+
step(ep)
|
|
764
812
|
end
|
|
765
813
|
|
|
814
|
+
break if @terminated
|
|
815
|
+
|
|
766
816
|
# XXX: it would be good to provide no-dummy-execution mode.
|
|
767
817
|
# It should work as a bit smarter "rbs prototype rb";
|
|
768
818
|
# show all method definitions as "untyped" arguments and return values
|
|
@@ -782,61 +832,29 @@ module TypeProf
|
|
|
782
832
|
merge_env(ep, env)
|
|
783
833
|
add_iseq_method_call!(meth, ep.ctx)
|
|
784
834
|
|
|
785
|
-
fargs_format = iseq.fargs_format
|
|
786
|
-
lead_tys = [Type.any] * (fargs_format[:lead_num] || 0)
|
|
787
|
-
opt_tys = fargs_format[:opt] ? [] : nil
|
|
788
|
-
post_tys = [Type.any] * (fargs_format[:post_num] || 0)
|
|
789
|
-
if fargs_format[:kwbits]
|
|
790
|
-
kw_tys = []
|
|
791
|
-
fargs_format[:keyword].each do |kw|
|
|
792
|
-
case
|
|
793
|
-
when kw.is_a?(Symbol) # required keyword
|
|
794
|
-
key = kw
|
|
795
|
-
req = true
|
|
796
|
-
ty = Type.any
|
|
797
|
-
when kw.size == 2 # optional keyword (default value is a literal)
|
|
798
|
-
key, ty = *kw
|
|
799
|
-
ty = Type.guess_literal_type(ty)
|
|
800
|
-
ty = ty.type if ty.is_a?(Type::Literal)
|
|
801
|
-
else # optional keyword
|
|
802
|
-
key, = kw
|
|
803
|
-
req = false
|
|
804
|
-
ty = Type.any
|
|
805
|
-
end
|
|
806
|
-
kw_tys << [req, key, ty]
|
|
807
|
-
end
|
|
808
|
-
else
|
|
809
|
-
kw_tys = nil
|
|
810
|
-
end
|
|
811
|
-
fargs = FormalArguments.new(lead_tys, opt_tys, nil, post_tys, kw_tys, nil, nil)
|
|
812
|
-
add_callsite!(ep.ctx, fargs, nil, nil) do |_ret_ty, _ep, _env|
|
|
813
|
-
# ignore
|
|
814
|
-
end
|
|
815
|
-
|
|
816
835
|
when :block
|
|
817
|
-
epenvs = dummy_continuation
|
|
836
|
+
blk, epenvs = dummy_continuation
|
|
818
837
|
epenvs.each do |ep, env|
|
|
819
838
|
merge_env(ep, env)
|
|
839
|
+
add_block_to_ctx!(blk.block_body, ep.ctx)
|
|
820
840
|
end
|
|
821
841
|
end
|
|
822
842
|
end
|
|
843
|
+
$stderr.print "\r\e[K" if Config.verbose >= 1
|
|
823
844
|
|
|
824
845
|
stat_eps
|
|
825
846
|
end
|
|
826
847
|
|
|
827
848
|
def report(stat_eps, output)
|
|
849
|
+
Reporters.show_message(@terminated, output)
|
|
850
|
+
|
|
828
851
|
Reporters.show_error(@errors, @backward_edges, output)
|
|
829
852
|
|
|
830
853
|
Reporters.show_reveal_types(self, @reveal_types, output)
|
|
831
854
|
|
|
832
|
-
Reporters.show_gvars(self, @gvar_table
|
|
833
|
-
|
|
834
|
-
#RubySignatureExporter2.new(
|
|
835
|
-
# self, @include_relations, @ivar_table.write, @cvar_table.write, @class_defs
|
|
836
|
-
#).show
|
|
855
|
+
Reporters.show_gvars(self, @gvar_table, output)
|
|
837
856
|
|
|
838
|
-
|
|
839
|
-
RubySignatureExporter.new(self, @class_defs, @iseq_method_to_ctxs, @sig_fargs, @sig_ret, @yields).show(stat_eps, output)
|
|
857
|
+
RubySignatureExporter.new(self, @class_defs, @iseq_method_to_ctxs).show(stat_eps, output)
|
|
840
858
|
end
|
|
841
859
|
|
|
842
860
|
def globalize_type(ty, env, ep)
|
|
@@ -867,17 +885,35 @@ module TypeProf
|
|
|
867
885
|
ctx = Context.new(iseq, cref, mid)
|
|
868
886
|
ep = ExecutionPoint.new(ctx, 0, nil)
|
|
869
887
|
locals = [Type.any] * iseq.locals.size
|
|
888
|
+
|
|
889
|
+
keyword = iseq.fargs_format[:keyword]
|
|
890
|
+
if keyword
|
|
891
|
+
kw_start = iseq.fargs_format[:kwbits]
|
|
892
|
+
kw_start -= iseq.fargs_format[:keyword].size if kw_start
|
|
893
|
+
keyword.each_with_index do |kw, i|
|
|
894
|
+
case
|
|
895
|
+
when kw.is_a?(Symbol) # required keyword
|
|
896
|
+
when kw.size == 2 # optional keyword (default value is a literal)
|
|
897
|
+
_key, default_ty = *kw
|
|
898
|
+
default_ty = Type.guess_literal_type(default_ty)
|
|
899
|
+
default_ty = default_ty.type if default_ty.is_a?(Type::Literal)
|
|
900
|
+
locals[kw_start + i] = default_ty.union(Type.any)
|
|
901
|
+
else # optional keyword (default value is an expression)
|
|
902
|
+
end
|
|
903
|
+
end
|
|
904
|
+
end
|
|
905
|
+
|
|
870
906
|
env = Env.new(StaticEnv.new(recv, Type.any, false), locals, [], Utils::HashWrapper.new({}))
|
|
871
907
|
|
|
872
908
|
@pending_execution[iseq] ||= [:method, [meth, ep, env]]
|
|
873
909
|
end
|
|
874
910
|
|
|
875
|
-
def pend_block_dummy_execution(iseq, nep, nenv)
|
|
876
|
-
@pending_execution[iseq] ||= [:block, {}]
|
|
877
|
-
if @pending_execution[iseq][1][nep]
|
|
878
|
-
@pending_execution[iseq][1][nep] = @pending_execution[iseq][1][nep].merge(nenv)
|
|
911
|
+
def pend_block_dummy_execution(blk, iseq, nep, nenv)
|
|
912
|
+
@pending_execution[iseq] ||= [:block, [blk, {}]]
|
|
913
|
+
if @pending_execution[iseq][1][1][nep]
|
|
914
|
+
@pending_execution[iseq][1][1][nep] = @pending_execution[iseq][1][1][nep].merge(nenv)
|
|
879
915
|
else
|
|
880
|
-
@pending_execution[iseq][1][nep] = nenv
|
|
916
|
+
@pending_execution[iseq][1][1][nep] = nenv
|
|
881
917
|
end
|
|
882
918
|
end
|
|
883
919
|
|
|
@@ -886,7 +922,7 @@ module TypeProf
|
|
|
886
922
|
alloc_site = AllocationSite.new(ep)
|
|
887
923
|
nenv, ty = localize_type(ty, env, ep, alloc_site)
|
|
888
924
|
case ty
|
|
889
|
-
when Type::LocalArray, Type::LocalHash
|
|
925
|
+
when Type::LocalCell, Type::LocalArray, Type::LocalHash
|
|
890
926
|
@alloc_site_to_global_id[ty.id] = [recv, var] # need overwrite check??
|
|
891
927
|
end
|
|
892
928
|
yield ty, nenv
|
|
@@ -895,7 +931,7 @@ module TypeProf
|
|
|
895
931
|
|
|
896
932
|
def set_instance_variable(recv, var, ty, ep, env)
|
|
897
933
|
ty = globalize_type(ty, env, ep)
|
|
898
|
-
add_ivar_write!(recv, var, ty)
|
|
934
|
+
add_ivar_write!(recv, var, ty, ep)
|
|
899
935
|
end
|
|
900
936
|
|
|
901
937
|
def step(ep)
|
|
@@ -911,6 +947,67 @@ module TypeProf
|
|
|
911
947
|
end
|
|
912
948
|
|
|
913
949
|
case insn
|
|
950
|
+
when :_iseq_body_start
|
|
951
|
+
# XXX: reconstruct and record the method signature
|
|
952
|
+
iseq = ep.ctx.iseq
|
|
953
|
+
lead_num = iseq.fargs_format[:lead_num] || 0
|
|
954
|
+
opt = iseq.fargs_format[:opt] || [0]
|
|
955
|
+
rest_start = iseq.fargs_format[:rest_start]
|
|
956
|
+
post_start = iseq.fargs_format[:post_start]
|
|
957
|
+
post_num = iseq.fargs_format[:post_num] || 0
|
|
958
|
+
kw_start = iseq.fargs_format[:kwbits]
|
|
959
|
+
keyword = iseq.fargs_format[:keyword]
|
|
960
|
+
kw_start -= keyword.size if kw_start
|
|
961
|
+
kw_rest = iseq.fargs_format[:kwrest]
|
|
962
|
+
block_start = iseq.fargs_format[:block_start]
|
|
963
|
+
|
|
964
|
+
lead_tys = env.locals[0, lead_num].map {|ty| globalize_type(ty, env, ep) }
|
|
965
|
+
opt_tys = opt.size > 1 ? env.locals[lead_num, opt.size - 1].map {|ty| globalize_type(ty, env, ep) } : nil
|
|
966
|
+
if rest_start # XXX:squash
|
|
967
|
+
ty = globalize_type(env.locals[lead_num + opt.size - 1], env, ep)
|
|
968
|
+
rest_ty = Type.bot
|
|
969
|
+
ty.each_child_global do |ty|
|
|
970
|
+
if ty.is_a?(Type::Array)
|
|
971
|
+
rest_ty = rest_ty.union(ty.elems.squash)
|
|
972
|
+
else
|
|
973
|
+
# XXX: to_ary?
|
|
974
|
+
rest_ty = rest_ty.union(ty)
|
|
975
|
+
end
|
|
976
|
+
end
|
|
977
|
+
end
|
|
978
|
+
post_tys = (post_start ? env.locals[post_start, post_num] : []).map {|ty| globalize_type(ty, env, ep) }
|
|
979
|
+
if keyword
|
|
980
|
+
kw_tys = []
|
|
981
|
+
keyword.each_with_index do |kw, i|
|
|
982
|
+
case
|
|
983
|
+
when kw.is_a?(Symbol) # required keyword
|
|
984
|
+
key = kw
|
|
985
|
+
req = true
|
|
986
|
+
when kw.size == 2 # optional keyword (default value is a literal)
|
|
987
|
+
key, default_ty = *kw
|
|
988
|
+
default_ty = Type.guess_literal_type(default_ty)
|
|
989
|
+
default_ty = default_ty.type if default_ty.is_a?(Type::Literal)
|
|
990
|
+
req = false
|
|
991
|
+
else # optional keyword (default value is an expression)
|
|
992
|
+
key, = kw
|
|
993
|
+
req = false
|
|
994
|
+
end
|
|
995
|
+
ty = env.locals[kw_start + i]
|
|
996
|
+
ty = ty.union(default_ty) if default_ty
|
|
997
|
+
ty = globalize_type(ty, env, ep)
|
|
998
|
+
kw_tys << [req, key, ty]
|
|
999
|
+
end
|
|
1000
|
+
end
|
|
1001
|
+
kw_rest_ty = globalize_type(env.locals[kw_rest], env, ep) if kw_rest
|
|
1002
|
+
if block_start
|
|
1003
|
+
blk_ty = globalize_type(env.locals[block_start], env, ep)
|
|
1004
|
+
elsif iseq.type == :method
|
|
1005
|
+
blk_ty = env.static_env.blk_ty
|
|
1006
|
+
else
|
|
1007
|
+
blk_ty = Type.nil
|
|
1008
|
+
end
|
|
1009
|
+
msig = MethodSignature.new(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
|
|
1010
|
+
add_method_signature!(ep.ctx, msig)
|
|
914
1011
|
when :putspecialobject
|
|
915
1012
|
kind, = operands
|
|
916
1013
|
ty = case kind
|
|
@@ -985,6 +1082,7 @@ module TypeProf
|
|
|
985
1082
|
recv = env.static_env.recv_ty
|
|
986
1083
|
if cref.klass.is_a?(Type::Class)
|
|
987
1084
|
typed_mdef = check_typed_method(cref.klass, mid, ep.ctx.cref.singleton)
|
|
1085
|
+
recv = Type::Instance.new(recv) if recv.is_a?(Type::Class)
|
|
988
1086
|
if typed_mdef
|
|
989
1087
|
mdef = ISeqMethodDef.new(iseq, cref)
|
|
990
1088
|
typed_mdef.each do |typed_mdef|
|
|
@@ -1000,7 +1098,6 @@ module TypeProf
|
|
|
1000
1098
|
end
|
|
1001
1099
|
end
|
|
1002
1100
|
|
|
1003
|
-
recv = Type::Instance.new(recv) if recv.is_a?(Type::Class)
|
|
1004
1101
|
pend_method_execution(iseq, meth, recv, mid, ep.ctx.cref)
|
|
1005
1102
|
end
|
|
1006
1103
|
else
|
|
@@ -1043,7 +1140,7 @@ module TypeProf
|
|
|
1043
1140
|
elsif superclass == Type.any
|
|
1044
1141
|
warn(ep, "superclass is any; Object is used instead")
|
|
1045
1142
|
superclass = Type::Builtin[:obj]
|
|
1046
|
-
elsif superclass
|
|
1143
|
+
elsif superclass == Type.nil
|
|
1047
1144
|
superclass = Type::Builtin[:obj]
|
|
1048
1145
|
elsif superclass.is_a?(Type::Instance)
|
|
1049
1146
|
warn(ep, "superclass is an instance; Object is used instead")
|
|
@@ -1058,7 +1155,7 @@ module TypeProf
|
|
|
1058
1155
|
if cbase == Type.any
|
|
1059
1156
|
klass = Type.any
|
|
1060
1157
|
else
|
|
1061
|
-
klass = new_class(cbase, id, [], superclass)
|
|
1158
|
+
klass = new_class(cbase, id, [], superclass, ep.ctx.iseq.absolute_path)
|
|
1062
1159
|
end
|
|
1063
1160
|
end
|
|
1064
1161
|
end
|
|
@@ -1083,14 +1180,14 @@ module TypeProf
|
|
|
1083
1180
|
locals = [Type.nil] * iseq.locals.size
|
|
1084
1181
|
nenv = Env.new(StaticEnv.new(recv, blk, false), locals, [], Utils::HashWrapper.new({}))
|
|
1085
1182
|
merge_env(nep, nenv)
|
|
1086
|
-
add_callsite!(nep.ctx,
|
|
1183
|
+
add_callsite!(nep.ctx, ep, env) do |ret_ty, ep, env|
|
|
1087
1184
|
nenv, ret_ty = localize_type(ret_ty, env, ep)
|
|
1088
1185
|
nenv = nenv.push(ret_ty)
|
|
1089
1186
|
merge_env(ep.next, nenv)
|
|
1090
1187
|
end
|
|
1091
1188
|
return
|
|
1092
1189
|
when :send
|
|
1093
|
-
env, recvs, mid, aargs = setup_actual_arguments(operands, ep, env)
|
|
1190
|
+
env, recvs, mid, aargs = setup_actual_arguments(:method, operands, ep, env)
|
|
1094
1191
|
recvs = Type.any if recvs == Type.bot
|
|
1095
1192
|
recvs.each_child do |recv|
|
|
1096
1193
|
do_send(recv, mid, aargs, ep, env) do |ret_ty, ep, env|
|
|
@@ -1102,7 +1199,7 @@ module TypeProf
|
|
|
1102
1199
|
return
|
|
1103
1200
|
when :send_branch
|
|
1104
1201
|
getlocal_operands, send_operands, branch_operands = operands
|
|
1105
|
-
env, recvs, mid, aargs = setup_actual_arguments(send_operands, ep, env)
|
|
1202
|
+
env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
|
|
1106
1203
|
recvs = Type.any if recvs == Type.bot
|
|
1107
1204
|
recvs.each_child do |recv|
|
|
1108
1205
|
do_send(recv, mid, aargs, ep, env) do |ret_ty, ep, env|
|
|
@@ -1129,23 +1226,16 @@ module TypeProf
|
|
|
1129
1226
|
end
|
|
1130
1227
|
return
|
|
1131
1228
|
when :invokeblock
|
|
1132
|
-
|
|
1133
|
-
opt, = operands
|
|
1134
|
-
_flags = opt[:flag]
|
|
1135
|
-
orig_argc = opt[:orig_argc]
|
|
1136
|
-
env, aargs = env.pop(orig_argc)
|
|
1229
|
+
env, recvs, mid, aargs = setup_actual_arguments(:block, operands, ep, env)
|
|
1137
1230
|
blk = env.static_env.blk_ty
|
|
1138
1231
|
case
|
|
1139
|
-
when blk
|
|
1232
|
+
when blk == Type.nil
|
|
1140
1233
|
env = env.push(Type.any)
|
|
1141
|
-
when blk
|
|
1234
|
+
when blk == Type.any
|
|
1142
1235
|
#warn(ep, "block is any")
|
|
1143
1236
|
env = env.push(Type.any)
|
|
1144
1237
|
else # Proc
|
|
1145
|
-
|
|
1146
|
-
#
|
|
1147
|
-
aargs = ActualArguments.new(aargs, nil, nil, blk_nil)
|
|
1148
|
-
do_invoke_block(true, env.static_env.blk_ty, aargs, ep, env) do |ret_ty, ep, env|
|
|
1238
|
+
do_invoke_block(blk, aargs, ep, env) do |ret_ty, ep, env|
|
|
1149
1239
|
nenv, ret_ty, = localize_type(ret_ty, env, ep)
|
|
1150
1240
|
nenv = nenv.push(ret_ty)
|
|
1151
1241
|
merge_env(ep.next, nenv)
|
|
@@ -1153,7 +1243,7 @@ module TypeProf
|
|
|
1153
1243
|
return
|
|
1154
1244
|
end
|
|
1155
1245
|
when :invokesuper
|
|
1156
|
-
env, recv, _, aargs = setup_actual_arguments(operands, ep, env)
|
|
1246
|
+
env, recv, _, aargs = setup_actual_arguments(:method, operands, ep, env)
|
|
1157
1247
|
|
|
1158
1248
|
env, recv = localize_type(env.static_env.recv_ty, env, ep)
|
|
1159
1249
|
mid = ep.ctx.mid
|
|
@@ -1186,7 +1276,7 @@ module TypeProf
|
|
|
1186
1276
|
end
|
|
1187
1277
|
env, (ty,) = env.pop(1)
|
|
1188
1278
|
ty = globalize_type(ty, env, ep)
|
|
1189
|
-
|
|
1279
|
+
add_return_value!(ep.ctx, ty)
|
|
1190
1280
|
return
|
|
1191
1281
|
when :throw
|
|
1192
1282
|
throwtype, = operands
|
|
@@ -1200,7 +1290,7 @@ module TypeProf
|
|
|
1200
1290
|
ty = globalize_type(ty, env, ep)
|
|
1201
1291
|
tmp_ep = ep
|
|
1202
1292
|
tmp_ep = tmp_ep.outer while tmp_ep.outer
|
|
1203
|
-
|
|
1293
|
+
add_return_value!(tmp_ep.ctx, ty)
|
|
1204
1294
|
return
|
|
1205
1295
|
when :break
|
|
1206
1296
|
tmp_ep = ep
|
|
@@ -1238,7 +1328,7 @@ module TypeProf
|
|
|
1238
1328
|
raise if iseq.locals != []
|
|
1239
1329
|
nenv = Env.new(env.static_env, [], [], nil)
|
|
1240
1330
|
merge_env(nep, nenv)
|
|
1241
|
-
add_callsite!(nep.ctx,
|
|
1331
|
+
add_callsite!(nep.ctx, ep, env) do |ret_ty, ep, env|
|
|
1242
1332
|
nenv, ret_ty = localize_type(ret_ty, env, ep)
|
|
1243
1333
|
nenv = nenv.push(ret_ty)
|
|
1244
1334
|
merge_env(ep.next, nenv)
|
|
@@ -1254,7 +1344,7 @@ module TypeProf
|
|
|
1254
1344
|
|
|
1255
1345
|
# TODO: it works for only simple cases: `x = nil; x || 1`
|
|
1256
1346
|
# It would be good to merge "dup; branchif" to make it context-sensitive-like
|
|
1257
|
-
falsy = ty
|
|
1347
|
+
falsy = ty == Type.nil
|
|
1258
1348
|
|
|
1259
1349
|
merge_env(ep_then, env)
|
|
1260
1350
|
merge_env(ep_else, env) unless branchtype == :if && falsy
|
|
@@ -1284,7 +1374,7 @@ module TypeProf
|
|
|
1284
1374
|
cbase = ep.ctx.cref.klass
|
|
1285
1375
|
ty = globalize_type(ty, env, ep)
|
|
1286
1376
|
# TODO: if superclass has the variable, it should be updated
|
|
1287
|
-
add_cvar_write!(cbase, var, ty)
|
|
1377
|
+
add_cvar_write!(cbase, var, ty, ep)
|
|
1288
1378
|
|
|
1289
1379
|
when :getclassvariable
|
|
1290
1380
|
var, = operands
|
|
@@ -1300,7 +1390,7 @@ module TypeProf
|
|
|
1300
1390
|
var, = operands
|
|
1301
1391
|
env, (ty,) = env.pop(1)
|
|
1302
1392
|
ty = globalize_type(ty, env, ep)
|
|
1303
|
-
add_gvar_write!(var, ty)
|
|
1393
|
+
add_gvar_write!(var, ty, ep)
|
|
1304
1394
|
|
|
1305
1395
|
when :getglobal
|
|
1306
1396
|
var, = operands
|
|
@@ -1311,7 +1401,6 @@ module TypeProf
|
|
|
1311
1401
|
env = env.push(ty)
|
|
1312
1402
|
else
|
|
1313
1403
|
add_gvar_read!(var, ep) do |ty, ep|
|
|
1314
|
-
ty = Type.nil if ty == Type.bot # HACK
|
|
1315
1404
|
nenv, ty = localize_type(ty, env, ep)
|
|
1316
1405
|
merge_env(ep.next, nenv.push(ty))
|
|
1317
1406
|
end
|
|
@@ -1347,8 +1436,37 @@ module TypeProf
|
|
|
1347
1436
|
flow_env = env.local_update(-var_idx+2, ret_ty)
|
|
1348
1437
|
case ret_ty
|
|
1349
1438
|
when Type.any
|
|
1350
|
-
merge_env(ep_then,
|
|
1351
|
-
merge_env(ep_else,
|
|
1439
|
+
merge_env(ep_then, flow_env)
|
|
1440
|
+
merge_env(ep_else, flow_env)
|
|
1441
|
+
when Type::Instance.new(Type::Builtin[:false]), Type.nil
|
|
1442
|
+
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
|
1443
|
+
else
|
|
1444
|
+
merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
|
|
1445
|
+
end
|
|
1446
|
+
end
|
|
1447
|
+
return
|
|
1448
|
+
when :getlocal_dup_branch
|
|
1449
|
+
getlocal_operands, _dup_operands, branch_operands = operands
|
|
1450
|
+
var_idx, _scope_idx, _escaped = getlocal_operands
|
|
1451
|
+
ret_ty = env.get_local(-var_idx+2)
|
|
1452
|
+
unless ret_ty
|
|
1453
|
+
p env.locals
|
|
1454
|
+
raise
|
|
1455
|
+
end
|
|
1456
|
+
|
|
1457
|
+
branchtype, target, = branch_operands
|
|
1458
|
+
# branchtype: :if or :unless or :nil
|
|
1459
|
+
ep_then = ep.next
|
|
1460
|
+
ep_else = ep.jump(target)
|
|
1461
|
+
|
|
1462
|
+
var_idx, _scope_idx, _escaped = getlocal_operands
|
|
1463
|
+
|
|
1464
|
+
ret_ty.each_child do |ret_ty|
|
|
1465
|
+
flow_env = env.local_update(-var_idx+2, ret_ty).push(ret_ty)
|
|
1466
|
+
case ret_ty
|
|
1467
|
+
when Type.any
|
|
1468
|
+
merge_env(ep_then, flow_env)
|
|
1469
|
+
merge_env(ep_else, flow_env)
|
|
1352
1470
|
when Type::Instance.new(Type::Builtin[:false]), Type.nil
|
|
1353
1471
|
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
|
1354
1472
|
else
|
|
@@ -1372,6 +1490,10 @@ module TypeProf
|
|
|
1372
1490
|
|
|
1373
1491
|
ret_ty.each_child do |ret_ty|
|
|
1374
1492
|
flow_env = env.local_update(-var_idx+2, ret_ty)
|
|
1493
|
+
ret_ty = ret_ty.base_type if ret_ty.is_a?(Type::Symbol)
|
|
1494
|
+
ret_ty = ret_ty.base_type if ret_ty.is_a?(Type::LocalCell)
|
|
1495
|
+
ret_ty = ret_ty.base_type if ret_ty.is_a?(Type::LocalArray)
|
|
1496
|
+
ret_ty = ret_ty.base_type if ret_ty.is_a?(Type::LocalHash)
|
|
1375
1497
|
if ret_ty.is_a?(Type::Instance)
|
|
1376
1498
|
if ret_ty.klass == pattern_ty # XXX: inheritance
|
|
1377
1499
|
merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
|
|
@@ -1379,8 +1501,8 @@ module TypeProf
|
|
|
1379
1501
|
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
|
1380
1502
|
end
|
|
1381
1503
|
else
|
|
1382
|
-
merge_env(ep_then,
|
|
1383
|
-
merge_env(ep_else,
|
|
1504
|
+
merge_env(ep_then, flow_env)
|
|
1505
|
+
merge_env(ep_else, flow_env)
|
|
1384
1506
|
end
|
|
1385
1507
|
end
|
|
1386
1508
|
return
|
|
@@ -1401,11 +1523,11 @@ module TypeProf
|
|
|
1401
1523
|
when :getconstant
|
|
1402
1524
|
name, = operands
|
|
1403
1525
|
env, (cbase, _allow_nil,) = env.pop(2)
|
|
1404
|
-
if cbase
|
|
1526
|
+
if cbase == Type.nil
|
|
1405
1527
|
ty = search_constant(ep.ctx.cref, name)
|
|
1406
1528
|
env, ty = localize_type(ty, env, ep)
|
|
1407
1529
|
env = env.push(ty)
|
|
1408
|
-
elsif cbase
|
|
1530
|
+
elsif cbase == Type.any
|
|
1409
1531
|
env = env.push(Type.any) # XXX: warning needed?
|
|
1410
1532
|
else
|
|
1411
1533
|
ty = get_constant(cbase, name)
|
|
@@ -1421,10 +1543,10 @@ module TypeProf
|
|
|
1421
1543
|
end
|
|
1422
1544
|
ty.each_child do |ty|
|
|
1423
1545
|
if ty.is_a?(Type::Class) && ty.superclass == Type::Builtin[:struct]
|
|
1424
|
-
@class_defs[ty.idx].name = name
|
|
1546
|
+
@class_defs[ty.idx].name = cbase_path(cbase) + [name]
|
|
1425
1547
|
end
|
|
1426
1548
|
end
|
|
1427
|
-
add_constant(cbase, name, globalize_type(ty, env, ep))
|
|
1549
|
+
add_constant(cbase, name, globalize_type(ty, env, ep), ep.ctx.iseq.absolute_path)
|
|
1428
1550
|
|
|
1429
1551
|
when :getspecial
|
|
1430
1552
|
key, type = operands
|
|
@@ -1453,6 +1575,28 @@ module TypeProf
|
|
|
1453
1575
|
when :dup
|
|
1454
1576
|
env, (ty,) = env.pop(1)
|
|
1455
1577
|
env = env.push(ty).push(ty)
|
|
1578
|
+
when :dup_branch
|
|
1579
|
+
_dup_operands, branch_operands = operands
|
|
1580
|
+
env, (ty,) = env.pop(1)
|
|
1581
|
+
|
|
1582
|
+
branchtype, target, = branch_operands
|
|
1583
|
+
# branchtype: :if or :unless or :nil
|
|
1584
|
+
ep_then = ep.next
|
|
1585
|
+
ep_else = ep.jump(target)
|
|
1586
|
+
|
|
1587
|
+
ty.each_child do |ty|
|
|
1588
|
+
flow_env = env.push(ty)
|
|
1589
|
+
case ty
|
|
1590
|
+
when Type.any
|
|
1591
|
+
merge_env(ep_then, flow_env)
|
|
1592
|
+
merge_env(ep_else, flow_env)
|
|
1593
|
+
when Type::Instance.new(Type::Builtin[:false]), Type.nil
|
|
1594
|
+
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
|
1595
|
+
else
|
|
1596
|
+
merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
|
|
1597
|
+
end
|
|
1598
|
+
end
|
|
1599
|
+
return
|
|
1456
1600
|
when :duphash
|
|
1457
1601
|
raw_hash, = operands
|
|
1458
1602
|
ty = Type.guess_literal_type(raw_hash)
|
|
@@ -1542,11 +1686,11 @@ module TypeProf
|
|
|
1542
1686
|
if ary2.is_a?(Type::LocalArray)
|
|
1543
1687
|
elems2 = get_container_elem_types(env, ep, ary2.id)
|
|
1544
1688
|
elems = Type::Array::Elements.new([], elems1.squash.union(elems2.squash))
|
|
1545
|
-
env = update_container_elem_types(env, ep, ary1.id) { elems }
|
|
1689
|
+
env = update_container_elem_types(env, ep, ary1.id, ary1.base_type) { elems }
|
|
1546
1690
|
env = env.push(ary1)
|
|
1547
1691
|
else
|
|
1548
1692
|
elems = Type::Array::Elements.new([], Type.any)
|
|
1549
|
-
env = update_container_elem_types(env, ep, ary1.id) { elems }
|
|
1693
|
+
env = update_container_elem_types(env, ep, ary1.id, ary1.base_type) { elems }
|
|
1550
1694
|
env = env.push(ary1)
|
|
1551
1695
|
end
|
|
1552
1696
|
else
|
|
@@ -1585,7 +1729,7 @@ module TypeProf
|
|
|
1585
1729
|
locals = [Type.nil] * iseq.locals.size
|
|
1586
1730
|
nenv = Env.new(env.static_env, locals, [], Utils::HashWrapper.new({}))
|
|
1587
1731
|
merge_env(nep, nenv)
|
|
1588
|
-
add_callsite!(nep.ctx,
|
|
1732
|
+
add_callsite!(nep.ctx, cont_ep, cont_env) do |ret_ty, ep, env|
|
|
1589
1733
|
nenv, ret_ty = localize_type(ret_ty, env, ep)
|
|
1590
1734
|
nenv = nenv.push(ret_ty)
|
|
1591
1735
|
merge_env(ep.jump(cont), nenv)
|
|
@@ -1617,13 +1761,13 @@ module TypeProf
|
|
|
1617
1761
|
merge_env(ep.next, env)
|
|
1618
1762
|
end
|
|
1619
1763
|
|
|
1620
|
-
private def setup_actual_arguments(operands, ep, env)
|
|
1764
|
+
private def setup_actual_arguments(kind, operands, ep, env)
|
|
1621
1765
|
opt, blk_iseq = operands
|
|
1622
1766
|
flags = opt[:flag]
|
|
1623
1767
|
mid = opt[:mid]
|
|
1624
1768
|
kw_arg = opt[:kw_arg]
|
|
1625
1769
|
argc = opt[:orig_argc]
|
|
1626
|
-
argc += 1 # receiver
|
|
1770
|
+
argc += 1 if kind == :method # for the receiver
|
|
1627
1771
|
argc += kw_arg.size if kw_arg
|
|
1628
1772
|
|
|
1629
1773
|
flag_args_splat = flags[ 0] != 0
|
|
@@ -1638,29 +1782,36 @@ module TypeProf
|
|
|
1638
1782
|
_flag_super = flags[ 9] != 0
|
|
1639
1783
|
_flag_zsuper = flags[10] != 0
|
|
1640
1784
|
|
|
1785
|
+
argc += 1 if flag_args_blockarg
|
|
1786
|
+
|
|
1787
|
+
env, aargs = env.pop(argc)
|
|
1788
|
+
|
|
1789
|
+
recv = aargs.shift if kind == :method
|
|
1790
|
+
|
|
1641
1791
|
if flag_args_blockarg
|
|
1642
|
-
|
|
1643
|
-
|
|
1792
|
+
blk_ty = aargs.pop
|
|
1793
|
+
elsif blk_iseq
|
|
1794
|
+
blk_ty = Type::Proc.new(ISeqBlock.new(blk_iseq, ep), Type::Instance.new(Type::Builtin[:proc]))
|
|
1644
1795
|
else
|
|
1645
|
-
|
|
1646
|
-
if blk_iseq
|
|
1647
|
-
# check
|
|
1648
|
-
blk_ty = Type::ISeqProc.new(blk_iseq, ep, Type::Instance.new(Type::Builtin[:proc]))
|
|
1649
|
-
else
|
|
1650
|
-
blk_ty = Type.nil
|
|
1651
|
-
end
|
|
1796
|
+
blk_ty = Type.nil
|
|
1652
1797
|
end
|
|
1653
1798
|
|
|
1799
|
+
new_blk_ty = Type.bot
|
|
1654
1800
|
blk_ty.each_child do |blk_ty|
|
|
1655
1801
|
case blk_ty
|
|
1656
1802
|
when Type.nil
|
|
1657
1803
|
when Type.any
|
|
1658
|
-
when Type::
|
|
1804
|
+
when Type::Proc
|
|
1805
|
+
when Type::Symbol
|
|
1806
|
+
blk_ty = Type::Proc.new(SymbolBlock.new(blk_ty.sym), Type::Instance.new(Type::Builtin[:proc]))
|
|
1659
1807
|
else
|
|
1808
|
+
# XXX: attempt to call to_proc
|
|
1660
1809
|
error(ep, "wrong argument type #{ blk_ty.screen_name(self) } (expected Proc)")
|
|
1661
1810
|
blk_ty = Type.any
|
|
1662
1811
|
end
|
|
1812
|
+
new_blk_ty = new_blk_ty.union(blk_ty)
|
|
1663
1813
|
end
|
|
1814
|
+
blk_ty = new_blk_ty
|
|
1664
1815
|
|
|
1665
1816
|
if flag_args_splat
|
|
1666
1817
|
# assert !flag_args_kwarg
|
|
@@ -1672,33 +1823,38 @@ module TypeProf
|
|
|
1672
1823
|
_, (ty,) = ty.elems.take_last(1)
|
|
1673
1824
|
case ty
|
|
1674
1825
|
when Type::Hash
|
|
1675
|
-
|
|
1826
|
+
kw_tys = ty.elems.to_keywords
|
|
1676
1827
|
when Type::Union
|
|
1677
1828
|
hash_elems = nil
|
|
1678
1829
|
ty.elems&.each do |(container_kind, base_type), elems|
|
|
1679
1830
|
if container_kind == Type::Hash
|
|
1831
|
+
elems.to_keywords
|
|
1680
1832
|
hash_elems = hash_elems ? hash_elems.union(elems) : elems
|
|
1681
1833
|
end
|
|
1682
1834
|
end
|
|
1683
|
-
hash_elems
|
|
1684
|
-
|
|
1835
|
+
if hash_elems
|
|
1836
|
+
kw_tys = hash_elems.to_keywords
|
|
1837
|
+
else
|
|
1838
|
+
kw_tys = { nil => Type.any }
|
|
1839
|
+
end
|
|
1685
1840
|
else
|
|
1686
1841
|
warn(ep, "non hash is passed to **kwarg?") unless ty == Type.any
|
|
1687
|
-
|
|
1842
|
+
kw_tys = { nil => Type.any }
|
|
1688
1843
|
end
|
|
1689
1844
|
else
|
|
1690
1845
|
raise NotImplementedError
|
|
1691
1846
|
end
|
|
1692
|
-
|
|
1847
|
+
else
|
|
1848
|
+
kw_tys = {}
|
|
1693
1849
|
end
|
|
1694
|
-
aargs = ActualArguments.new(aargs, rest_ty,
|
|
1850
|
+
aargs = ActualArguments.new(aargs, rest_ty, kw_tys, blk_ty)
|
|
1695
1851
|
elsif flag_args_kw_splat
|
|
1696
1852
|
last = aargs.last
|
|
1697
1853
|
ty = globalize_type(last, env, ep)
|
|
1698
1854
|
case ty
|
|
1699
1855
|
when Type::Hash
|
|
1700
1856
|
aargs = aargs[0..-2]
|
|
1701
|
-
|
|
1857
|
+
kw_tys = ty.elems.to_keywords
|
|
1702
1858
|
when Type::Union
|
|
1703
1859
|
hash_elems = nil
|
|
1704
1860
|
ty.elems.each do |(container_kind, base_type), elems|
|
|
@@ -1706,31 +1862,30 @@ module TypeProf
|
|
|
1706
1862
|
hash_elems = hash_elems ? hash_elems.union(elems) : elems
|
|
1707
1863
|
end
|
|
1708
1864
|
end
|
|
1709
|
-
hash_elems
|
|
1710
|
-
|
|
1865
|
+
if hash_elems
|
|
1866
|
+
kw_tys = hash_elems.to_keywords
|
|
1867
|
+
else
|
|
1868
|
+
kw_tys = { nil => Type.any }
|
|
1869
|
+
end
|
|
1711
1870
|
when Type::Any
|
|
1712
1871
|
aargs = aargs[0..-2]
|
|
1713
|
-
|
|
1872
|
+
kw_tys = { nil => Type.any }
|
|
1714
1873
|
else
|
|
1715
1874
|
warn(ep, "non hash is passed to **kwarg?")
|
|
1716
|
-
|
|
1875
|
+
kw_tys = { nil => Type.any }
|
|
1717
1876
|
end
|
|
1718
|
-
aargs = ActualArguments.new(aargs, nil,
|
|
1877
|
+
aargs = ActualArguments.new(aargs, nil, kw_tys, blk_ty)
|
|
1719
1878
|
elsif flag_args_kwarg
|
|
1720
1879
|
kw_vals = aargs.pop(kw_arg.size)
|
|
1721
1880
|
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
h[k_ty] = v_ty
|
|
1726
|
-
end
|
|
1881
|
+
kw_tys = {}
|
|
1882
|
+
kw_arg.zip(kw_vals) do |key, v_ty|
|
|
1883
|
+
kw_tys[key] = v_ty
|
|
1727
1884
|
end
|
|
1728
1885
|
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
aargs = ActualArguments.new(aargs, nil, kw_ty, blk_ty)
|
|
1886
|
+
aargs = ActualArguments.new(aargs, nil, kw_tys, blk_ty)
|
|
1732
1887
|
else
|
|
1733
|
-
aargs = ActualArguments.new(aargs, nil,
|
|
1888
|
+
aargs = ActualArguments.new(aargs, nil, {}, blk_ty)
|
|
1734
1889
|
end
|
|
1735
1890
|
|
|
1736
1891
|
if blk_iseq
|
|
@@ -1740,7 +1895,7 @@ module TypeProf
|
|
|
1740
1895
|
nlocals = [Type.any] * blk_iseq.locals.size
|
|
1741
1896
|
nsenv = StaticEnv.new(env.static_env.recv_ty, Type.any, env.static_env.mod_func)
|
|
1742
1897
|
nenv = Env.new(nsenv, nlocals, [], nil)
|
|
1743
|
-
pend_block_dummy_execution(blk_iseq, nep, nenv)
|
|
1898
|
+
pend_block_dummy_execution(blk_ty, blk_iseq, nep, nenv)
|
|
1744
1899
|
merge_return_env(ep) {|tenv| tenv ? tenv.merge(env) : env }
|
|
1745
1900
|
end
|
|
1746
1901
|
|
|
@@ -1754,158 +1909,94 @@ module TypeProf
|
|
|
1754
1909
|
meth.do_send(recv, mid, aargs, ep, env, self, &ctn)
|
|
1755
1910
|
end
|
|
1756
1911
|
else
|
|
1757
|
-
|
|
1912
|
+
case recv
|
|
1913
|
+
when Type::Void
|
|
1914
|
+
error(ep, "void's method is called: #{ globalize_type(recv, env, ep).screen_name(self) }##{ mid }")
|
|
1915
|
+
when Type::Any
|
|
1916
|
+
else
|
|
1758
1917
|
error(ep, "undefined method: #{ globalize_type(recv, env, ep).screen_name(self) }##{ mid }")
|
|
1759
1918
|
end
|
|
1760
1919
|
ctn[Type.any, ep, env]
|
|
1761
1920
|
end
|
|
1762
1921
|
end
|
|
1763
1922
|
|
|
1764
|
-
def do_invoke_block(
|
|
1923
|
+
def do_invoke_block(blk, aargs, ep, env, replace_recv_ty: nil, &ctn)
|
|
1765
1924
|
blk.each_child do |blk|
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
next
|
|
1769
|
-
end
|
|
1770
|
-
blk_iseq = blk.iseq
|
|
1771
|
-
blk_ep = blk.ep
|
|
1772
|
-
blk_env = @return_envs[blk_ep]
|
|
1773
|
-
blk_env = blk_env.replace_recv_ty(replace_recv_ty) if replace_recv_ty
|
|
1774
|
-
arg_blk = aargs.blk_ty
|
|
1775
|
-
aargs_ = aargs.lead_tys.map {|aarg| globalize_type(aarg, env, ep) }
|
|
1776
|
-
argc = blk_iseq.fargs_format[:lead_num] || 0
|
|
1777
|
-
# actual argc == 1, not array, formal argc == 1: yield 42 => do |x| : x=42
|
|
1778
|
-
# actual argc == 1, array, formal argc == 1: yield [42,43,44] => do |x| : x=[42,43,44]
|
|
1779
|
-
# actual argc >= 2, formal argc == 1: yield 42,43,44 => do |x| : x=42
|
|
1780
|
-
# actual argc == 1, not array, formal argc >= 2: yield 42 => do |x,y| : x,y=42,nil
|
|
1781
|
-
# actual argc == 1, array, formal argc >= 2: yield [42,43,44] => do |x,y| : x,y=42,43
|
|
1782
|
-
# actual argc >= 2, formal argc >= 2: yield 42,43,44 => do |x,y| : x,y=42,43
|
|
1783
|
-
if aargs_.size >= 2 || argc == 0
|
|
1784
|
-
aargs_.pop while argc < aargs_.size
|
|
1785
|
-
aargs_ << Type.nil while argc > aargs_.size
|
|
1925
|
+
if blk.is_a?(Type::Proc)
|
|
1926
|
+
blk.block_body.do_call(aargs, ep, env, self, replace_recv_ty: replace_recv_ty, &ctn)
|
|
1786
1927
|
else
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
aargs_ = [aarg_ty || Type.nil]
|
|
1790
|
-
else # actual argc == 1 && formal argc >= 2
|
|
1791
|
-
ary_elems = nil
|
|
1792
|
-
any_ty = nil
|
|
1793
|
-
case aarg_ty
|
|
1794
|
-
when Type::Union
|
|
1795
|
-
ary_elems = nil
|
|
1796
|
-
other_elems = nil
|
|
1797
|
-
aarg_ty.elems&.each do |(container_kind, base_type), elems|
|
|
1798
|
-
if container_kind == Type::Array
|
|
1799
|
-
ary_elems = ary_elems ? ary_elems.union(elems) : elems
|
|
1800
|
-
else
|
|
1801
|
-
other_elems = other_elems ? other_elems.union(elems) : elems
|
|
1802
|
-
end
|
|
1803
|
-
end
|
|
1804
|
-
aarg_ty = Type::Union.new(aarg_ty.types, other_elems)
|
|
1805
|
-
any_ty = Type.any if aarg_ty.types.include?(Type.any)
|
|
1806
|
-
when Type::Array
|
|
1807
|
-
ary_elems = aarg_ty.elems
|
|
1808
|
-
aarg_ty = nil
|
|
1809
|
-
when Type::Any
|
|
1810
|
-
any_ty = Type.any
|
|
1811
|
-
end
|
|
1812
|
-
aargs_ = [Type.bot] * argc
|
|
1813
|
-
aargs_[0] = aargs_[0].union(aarg_ty) if aarg_ty
|
|
1814
|
-
argc.times do |i|
|
|
1815
|
-
ty = aargs_[i]
|
|
1816
|
-
ty = ty.union(ary_elems[i]) if ary_elems
|
|
1817
|
-
ty = ty.union(Type.any) if any_ty
|
|
1818
|
-
ty = ty.union(Type.nil) if i >= 1 && aarg_ty
|
|
1819
|
-
aargs_[i] = ty
|
|
1820
|
-
end
|
|
1821
|
-
end
|
|
1822
|
-
end
|
|
1823
|
-
locals = [Type.nil] * blk_iseq.locals.size
|
|
1824
|
-
locals[blk_iseq.fargs_format[:block_start]] = arg_blk if blk_iseq.fargs_format[:block_start]
|
|
1825
|
-
env_blk = blk_env.static_env.blk_ty
|
|
1826
|
-
nfargs = FormalArguments.new(aargs_, [], nil, [], nil, nil, env_blk) # XXX: aargs_ -> fargs
|
|
1827
|
-
nctx = Context.new(blk_iseq, blk_ep.ctx.cref, nil)
|
|
1828
|
-
nep = ExecutionPoint.new(nctx, 0, blk_ep)
|
|
1829
|
-
nenv = Env.new(blk_env.static_env, locals, [], nil)
|
|
1830
|
-
alloc_site = AllocationSite.new(nep)
|
|
1831
|
-
aargs_.each_with_index do |ty, i|
|
|
1832
|
-
alloc_site2 = alloc_site.add_id(i)
|
|
1833
|
-
nenv, ty = localize_type(ty, nenv, nep, alloc_site2) # Use Scratch#localize_type?
|
|
1834
|
-
nenv = nenv.local_update(i, ty)
|
|
1928
|
+
warn(ep, "non-proc is passed as a block")
|
|
1929
|
+
ctn[Type.any, ep, env]
|
|
1835
1930
|
end
|
|
1836
|
-
|
|
1837
|
-
merge_env(nep, nenv)
|
|
1838
|
-
|
|
1839
|
-
# caution: given_block flag is not complete
|
|
1840
|
-
#
|
|
1841
|
-
# def foo
|
|
1842
|
-
# bar do |&blk|
|
|
1843
|
-
# yield
|
|
1844
|
-
# blk.call
|
|
1845
|
-
# end
|
|
1846
|
-
# end
|
|
1847
|
-
#
|
|
1848
|
-
# yield and blk.call call different blocks.
|
|
1849
|
-
# So, a context can have two blocks.
|
|
1850
|
-
# given_block is calculated by comparing "context's block (yield target)" and "blk", but it is not a correct result
|
|
1851
|
-
|
|
1852
|
-
add_yield!(ep.ctx, globalize_type(aargs, env, ep), nep.ctx) if given_block
|
|
1853
|
-
add_block_to_ctx!(blk, nep.ctx)
|
|
1854
|
-
add_callsite!(nep.ctx, nfargs, ep, env, &ctn)
|
|
1855
1931
|
end
|
|
1856
1932
|
end
|
|
1857
1933
|
|
|
1858
|
-
def
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1934
|
+
def show_block_signature(blks)
|
|
1935
|
+
bsig = nil
|
|
1936
|
+
ret_ty = Type.bot
|
|
1937
|
+
|
|
1938
|
+
blks.each do |blk|
|
|
1939
|
+
blk.each_child_global do |blk|
|
|
1940
|
+
bsig0 = @block_signatures[blk.block_body]
|
|
1941
|
+
if bsig0
|
|
1942
|
+
if bsig
|
|
1943
|
+
bsig = bsig.merge(bsig0)
|
|
1944
|
+
else
|
|
1945
|
+
bsig = bsig0
|
|
1946
|
+
end
|
|
1947
|
+
end
|
|
1948
|
+
|
|
1949
|
+
@block_to_ctx[blk.block_body].each do |blk_ctx|
|
|
1950
|
+
ret_ty = ret_ty.union(@return_values[blk_ctx]) if @return_values[blk_ctx]
|
|
1864
1951
|
end
|
|
1865
|
-
else
|
|
1866
|
-
# uncalled proc? dummy execution doesn't work?
|
|
1867
|
-
#p blk
|
|
1868
1952
|
end
|
|
1869
1953
|
end
|
|
1870
|
-
|
|
1954
|
+
|
|
1955
|
+
bsig ||= BlockSignature.new([], [], nil, Type.nil)
|
|
1956
|
+
|
|
1957
|
+
bsig = bsig.screen_name(self)#, block: true)
|
|
1958
|
+
ret_ty = ret_ty.screen_name(self)
|
|
1959
|
+
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
|
1960
|
+
|
|
1961
|
+
bsig = bsig + " " if bsig != ""
|
|
1962
|
+
"{ #{ bsig }-> #{ ret_ty } }"
|
|
1871
1963
|
end
|
|
1872
1964
|
|
|
1873
|
-
def
|
|
1874
|
-
|
|
1875
|
-
blk_ctxs.each do |blk_ctx, farg_tys|
|
|
1876
|
-
if all_farg_tys
|
|
1877
|
-
all_farg_tys = all_farg_tys.merge(farg_tys)
|
|
1878
|
-
else
|
|
1879
|
-
all_farg_tys = farg_tys
|
|
1880
|
-
end
|
|
1965
|
+
def show_proc_signature(blks)
|
|
1966
|
+
farg_tys, ret_ty = nil, Type.bot
|
|
1881
1967
|
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1968
|
+
blks.each do |blk|
|
|
1969
|
+
blk.each_child_global do |blk|
|
|
1970
|
+
next if blk.block_body.is_a?(TypedBlock) # XXX: Support TypedBlock
|
|
1971
|
+
next unless @block_to_ctx[blk.block_body] # this occurs when screen_name is called before type-profiling finished (e.g., error message)
|
|
1972
|
+
@block_to_ctx[blk.block_body].each do |blk_ctx|
|
|
1973
|
+
if farg_tys
|
|
1974
|
+
farg_tys = farg_tys.merge(@method_signatures[blk_ctx])
|
|
1975
|
+
else
|
|
1976
|
+
farg_tys = @method_signatures[blk_ctx]
|
|
1977
|
+
end
|
|
1978
|
+
|
|
1979
|
+
ret_ty = ret_ty.union(@return_values[blk_ctx]) if @return_values[blk_ctx]
|
|
1980
|
+
end
|
|
1886
1981
|
end
|
|
1887
1982
|
end
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1983
|
+
|
|
1984
|
+
farg_tys = farg_tys ? farg_tys.screen_name(self) : "(unknown)"
|
|
1985
|
+
ret_ty = ret_ty.screen_name(self)
|
|
1986
|
+
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
|
1987
|
+
|
|
1988
|
+
farg_tys = farg_tys + " " if farg_tys != ""
|
|
1989
|
+
"^#{ farg_tys }-> #{ ret_ty }"
|
|
1891
1990
|
end
|
|
1892
1991
|
|
|
1893
|
-
def
|
|
1992
|
+
def show_method_signature(ctx)
|
|
1993
|
+
farg_tys = @method_signatures[ctx]
|
|
1994
|
+
ret_ty = @return_values[ctx] || Type.bot
|
|
1995
|
+
|
|
1894
1996
|
farg_tys = farg_tys.screen_name(self)
|
|
1895
1997
|
ret_ty = ret_ty.screen_name(self)
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
aargs, blk_ctxs = yield_data
|
|
1899
|
-
all_blk_ret_ty = Type.bot
|
|
1900
|
-
blk_ctxs.each do |blk_ctx|
|
|
1901
|
-
all_blk_ret_ty = all_blk_ret_ty.union(@sig_ret[blk_ctx])
|
|
1902
|
-
end
|
|
1903
|
-
all_blk_ret_ty = all_blk_ret_ty.screen_name(self)
|
|
1904
|
-
all_blk_ret_ty = all_blk_ret_ty.include?("|") ? "(#{ all_blk_ret_ty })" : all_blk_ret_ty
|
|
1905
|
-
s << "{ #{ aargs.screen_name(self) } -> #{ all_blk_ret_ty } } " if aargs
|
|
1906
|
-
end
|
|
1907
|
-
s << "-> "
|
|
1908
|
-
s << (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty)
|
|
1998
|
+
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
|
1999
|
+
"#{ (farg_tys.empty? ? "" : "#{ farg_tys } ") }-> #{ ret_ty }"
|
|
1909
2000
|
end
|
|
1910
2001
|
end
|
|
1911
2002
|
end
|