typeprof 0.8.0 → 0.11.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/Gemfile.lock +6 -5
- data/doc/demo.md +1 -1
- data/doc/todo.md +133 -0
- data/lib/typeprof/analyzer.rb +177 -76
- data/lib/typeprof/arguments.rb +2 -0
- data/lib/typeprof/block.rb +40 -2
- data/lib/typeprof/builtin.rb +279 -111
- data/lib/typeprof/cli.rb +5 -0
- data/lib/typeprof/config.rb +15 -1
- data/lib/typeprof/container-type.rb +5 -4
- data/lib/typeprof/export.rb +115 -70
- data/lib/typeprof/import.rb +71 -33
- data/lib/typeprof/iseq.rb +46 -11
- data/lib/typeprof/method.rb +42 -21
- data/lib/typeprof/type.rb +93 -15
- data/lib/typeprof/version.rb +1 -1
- data/smoke/alias.rb +4 -4
- data/smoke/alias2.rb +6 -4
- data/smoke/any1.rb +1 -1
- data/smoke/any2.rb +2 -2
- data/smoke/arguments.rb +2 -2
- data/smoke/arguments2.rb +10 -10
- data/smoke/array-each.rb +1 -1
- data/smoke/array-each2.rb +1 -1
- data/smoke/array-each3.rb +1 -1
- data/smoke/array-ltlt.rb +1 -1
- data/smoke/array-ltlt2.rb +1 -1
- data/smoke/array-map.rb +1 -1
- data/smoke/array-map2.rb +1 -1
- data/smoke/array-map3.rb +3 -3
- data/smoke/array-mul.rb +2 -2
- data/smoke/array-plus1.rb +1 -1
- data/smoke/array-plus2.rb +3 -2
- data/smoke/array-pop.rb +1 -1
- data/smoke/array-range-aref.rb +11 -11
- data/smoke/array-replace.rb +1 -1
- data/smoke/array-s-aref.rb +1 -1
- data/smoke/array1.rb +5 -5
- data/smoke/array10.rb +1 -1
- data/smoke/array11.rb +1 -1
- data/smoke/array12.rb +3 -3
- data/smoke/array13.rb +4 -4
- data/smoke/array14.rb +1 -1
- data/smoke/array15.rb +16 -0
- data/smoke/array2.rb +3 -3
- data/smoke/array3.rb +5 -4
- data/smoke/array4.rb +1 -1
- data/smoke/array5.rb +1 -1
- data/smoke/array6.rb +3 -2
- data/smoke/array7.rb +1 -1
- data/smoke/array8.rb +1 -1
- data/smoke/array9.rb +1 -1
- data/smoke/attr-module.rb +27 -0
- data/smoke/attr-vis.rb +43 -0
- data/smoke/attr-vis.rbs +4 -0
- data/smoke/attr.rb +5 -5
- data/smoke/autoload.rb +1 -1
- data/smoke/backtrace.rb +3 -3
- data/smoke/block-ambiguous.rb +8 -8
- data/smoke/block-args1-rest.rb +12 -11
- data/smoke/block-args1.rb +10 -10
- data/smoke/block-args2-rest.rb +12 -11
- data/smoke/block-args2.rb +10 -10
- data/smoke/block-args3-rest.rb +14 -13
- data/smoke/block-args3.rb +12 -12
- data/smoke/block-blockarg.rb +4 -4
- data/smoke/block-kwarg.rb +10 -10
- data/smoke/block1.rb +1 -1
- data/smoke/block10.rb +1 -1
- data/smoke/block11.rb +5 -5
- data/smoke/block12.rb +2 -2
- data/smoke/block14.rb +2 -2
- data/smoke/block2.rb +1 -1
- data/smoke/block3.rb +3 -3
- data/smoke/block4.rb +2 -2
- data/smoke/block5.rb +3 -2
- data/smoke/block6.rb +2 -2
- data/smoke/block7.rb +1 -1
- data/smoke/block8.rb +3 -3
- data/smoke/block9.rb +1 -1
- data/smoke/block_given.rb +37 -0
- data/smoke/blown.rb +1 -1
- data/smoke/break1.rb +2 -2
- data/smoke/break2.rb +1 -1
- data/smoke/break3.rb +13 -0
- data/smoke/case.rb +1 -1
- data/smoke/case2.rb +1 -1
- data/smoke/case3.rb +17 -0
- data/smoke/class-hierarchy.rb +5 -5
- data/smoke/class-hierarchy2.rb +3 -3
- data/smoke/class-new.rb +15 -0
- data/smoke/class_instance_var.rb +1 -1
- data/smoke/class_method.rb +2 -2
- data/smoke/class_method2.rb +2 -2
- data/smoke/class_method3.rb +2 -2
- data/smoke/constant1.rb +6 -6
- data/smoke/constant2.rb +5 -4
- data/smoke/constant3.rb +1 -1
- data/smoke/constant4.rb +1 -1
- data/smoke/context-sensitive1.rb +1 -1
- data/smoke/cvar.rb +6 -5
- data/smoke/cvar2.rb +2 -2
- data/smoke/define_method.rb +2 -2
- data/smoke/define_method2.rb +2 -2
- data/smoke/define_method3.rb +14 -0
- data/smoke/define_method3.rbs +3 -0
- data/smoke/define_method4.rb +15 -0
- data/smoke/define_method4.rbs +3 -0
- data/smoke/define_method5.rb +12 -0
- data/smoke/define_method6.rb +19 -0
- data/smoke/demo.rb +6 -6
- data/smoke/demo1.rb +1 -1
- data/smoke/demo10.rb +2 -2
- data/smoke/demo11.rb +1 -1
- data/smoke/demo2.rb +1 -1
- data/smoke/demo3.rb +1 -1
- data/smoke/demo4.rb +3 -3
- data/smoke/demo5.rb +1 -1
- data/smoke/demo6.rb +4 -3
- data/smoke/demo7.rb +1 -1
- data/smoke/demo8.rb +2 -2
- data/smoke/demo9.rb +3 -2
- data/smoke/dummy-execution1.rb +2 -2
- data/smoke/dummy-execution2.rb +2 -2
- data/smoke/dummy_element.rb +14 -0
- data/smoke/ensure1.rb +2 -2
- data/smoke/enum_for.rb +15 -0
- data/smoke/enum_for2.rb +17 -0
- data/smoke/enumerator.rb +2 -2
- data/smoke/expandarray1.rb +1 -1
- data/smoke/expandarray2.rb +1 -1
- data/smoke/fib.rb +2 -2
- data/smoke/flip-flop.rb +28 -0
- data/smoke/flow1.rb +1 -1
- data/smoke/flow10.rb +17 -0
- data/smoke/flow2.rb +1 -1
- data/smoke/flow3.rb +1 -1
- data/smoke/flow5.rb +1 -1
- data/smoke/flow6.rb +1 -1
- data/smoke/flow7.rb +1 -1
- data/smoke/flow8.rb +1 -1
- data/smoke/flow9.rb +12 -0
- data/smoke/freeze.rb +1 -1
- data/smoke/function.rb +2 -2
- data/smoke/gvar.rb +2 -2
- data/smoke/gvar2.rb +3 -3
- data/smoke/hash-bot.rb +12 -0
- data/smoke/hash-fetch.rb +3 -3
- data/smoke/hash-merge-bang.rb +1 -1
- data/smoke/hash1.rb +2 -2
- data/smoke/hash2.rb +1 -1
- data/smoke/hash3.rb +1 -1
- data/smoke/hash4.rb +1 -1
- data/smoke/hash5.rb +1 -1
- data/smoke/inheritance.rb +4 -4
- data/smoke/inheritance2.rb +2 -2
- data/smoke/initialize.rb +6 -5
- data/smoke/instance_eval.rb +2 -2
- data/smoke/instance_eval2.rb +10 -0
- data/smoke/instance_eval3.rb +25 -0
- data/smoke/int_times.rb +1 -1
- data/smoke/integer.rb +1 -1
- data/smoke/ivar.rb +5 -4
- data/smoke/ivar2.rb +4 -4
- data/smoke/ivar3.rb +4 -3
- data/smoke/ivar4.rb +21 -0
- data/smoke/kernel-class.rb +1 -1
- data/smoke/keyword1.rb +1 -1
- data/smoke/keyword2.rb +1 -1
- data/smoke/keyword3.rb +1 -1
- data/smoke/keyword4.rb +1 -1
- data/smoke/keyword5.rb +1 -1
- data/smoke/kwrest.rb +3 -2
- data/smoke/kwsplat1.rb +4 -4
- data/smoke/kwsplat2.rb +1 -1
- data/smoke/lit-complex.rb +10 -0
- data/smoke/lit-encoding.rb +10 -0
- data/smoke/manual-rbs.rb +5 -4
- data/smoke/manual-rbs2.rb +1 -1
- data/smoke/manual-rbs3.rb +3 -2
- data/smoke/masgn1.rb +1 -1
- data/smoke/masgn2.rb +2 -2
- data/smoke/masgn3.rb +1 -1
- data/smoke/method_in_branch.rb +3 -3
- data/smoke/method_missing.rb +5 -4
- data/smoke/module1.rb +2 -2
- data/smoke/module2.rb +1 -1
- data/smoke/module3.rb +3 -3
- data/smoke/module4.rb +3 -2
- data/smoke/module5.rb +17 -0
- data/smoke/module6.rb +40 -0
- data/smoke/module_function1.rb +4 -3
- data/smoke/module_function2.rb +4 -3
- data/smoke/multiple-include.rb +2 -1
- data/smoke/multiple-superclass.rb +1 -1
- data/smoke/next1.rb +2 -2
- data/smoke/next2.rb +1 -1
- data/smoke/object-send1.rb +3 -3
- data/smoke/object-send2.rb +10 -0
- data/smoke/object-send3.rb +18 -0
- data/smoke/once.rb +1 -1
- data/smoke/optional1.rb +1 -1
- data/smoke/optional2.rb +1 -1
- data/smoke/optional3.rb +1 -1
- data/smoke/parameterizedd-self.rb +3 -2
- data/smoke/parameterizedd-self2.rb +1 -1
- data/smoke/pathname1.rb +1 -1
- data/smoke/pathname2.rb +1 -1
- data/smoke/pattern-match1.rb +1 -1
- data/smoke/pattern-match2.rb +1 -1
- data/smoke/prepend1.rb +33 -0
- data/smoke/prepend2.rb +10 -0
- data/smoke/prepend2.rbs +9 -0
- data/smoke/primitive_method.rb +19 -0
- data/smoke/printf.rb +2 -2
- data/smoke/proc.rb +2 -2
- data/smoke/proc2.rb +1 -1
- data/smoke/proc3.rb +1 -1
- data/smoke/proc4.rb +1 -1
- data/smoke/proc5.rb +19 -0
- data/smoke/public.rb +38 -0
- data/smoke/range.rb +1 -1
- data/smoke/rbs-alias.rb +1 -1
- data/smoke/rbs-attr.rb +5 -5
- data/smoke/rbs-attr2.rb +1 -1
- data/smoke/rbs-extend.rb +1 -1
- data/smoke/rbs-interface.rb +4 -4
- data/smoke/rbs-module.rb +26 -0
- data/smoke/rbs-module.rbs +4 -0
- data/smoke/rbs-opt-and-rest.rb +10 -0
- data/smoke/rbs-opt-and-rest.rbs +3 -0
- data/smoke/rbs-proc1.rb +1 -1
- data/smoke/rbs-proc2.rb +2 -2
- data/smoke/rbs-proc3.rb +1 -1
- data/smoke/rbs-record.rb +2 -2
- data/smoke/rbs-tyvar.rb +2 -2
- data/smoke/rbs-tyvar2.rb +2 -2
- data/smoke/rbs-tyvar3.rb +2 -2
- data/smoke/rbs-tyvar4.rb +4 -3
- data/smoke/rbs-tyvar5.rb +1 -1
- data/smoke/rbs-tyvar6.rb +3 -3
- data/smoke/rbs-tyvar7.rb +1 -1
- data/smoke/rbs-vars.rb +6 -6
- data/smoke/redo1.rb +2 -2
- data/smoke/redo2.rb +2 -2
- data/smoke/req-keyword.rb +1 -1
- data/smoke/rescue1.rb +2 -2
- data/smoke/rescue2.rb +2 -2
- data/smoke/rescue3.rb +20 -0
- data/smoke/rescue4.rb +17 -0
- data/smoke/respond_to.rb +1 -1
- data/smoke/rest-farg.rb +1 -1
- data/smoke/rest1.rb +2 -2
- data/smoke/rest2.rb +1 -1
- data/smoke/rest3.rb +6 -6
- data/smoke/rest4.rb +2 -2
- data/smoke/rest5.rb +1 -1
- data/smoke/rest6.rb +1 -1
- data/smoke/retry1.rb +2 -2
- data/smoke/return.rb +1 -1
- data/smoke/simple.rb +12 -0
- data/smoke/singleton_method.rb +1 -1
- data/smoke/step.rb +3 -3
- data/smoke/string-split.rb +1 -1
- data/smoke/struct-keyword_init.rb +10 -0
- data/smoke/struct.rb +1 -1
- data/smoke/struct2.rb +4 -4
- data/smoke/struct3.rb +2 -2
- data/smoke/struct4.rb +7 -0
- data/smoke/struct5.rb +16 -0
- data/smoke/struct6.rb +15 -0
- data/smoke/struct7.rb +17 -0
- data/smoke/stub-keyword.rb +10 -0
- data/smoke/super1.rb +4 -4
- data/smoke/super2.rb +1 -1
- data/smoke/super3.rb +4 -3
- data/smoke/super4.rb +7 -5
- data/smoke/super5.rb +6 -4
- data/smoke/svar1.rb +1 -1
- data/smoke/symbol-proc-attr.rb +22 -0
- data/smoke/symbol-proc-attr2.rb +15 -0
- data/smoke/symbol-proc-bot.rb +13 -0
- data/smoke/symbol-proc.rb +3 -3
- data/smoke/tap1.rb +2 -2
- data/smoke/toplevel.rb +1 -1
- data/smoke/two-map.rb +2 -2
- data/smoke/type_var.rb +3 -3
- data/smoke/typed_method.rb +1 -1
- data/smoke/uninitialize-var.rb +1 -1
- data/smoke/union-recv.rb +2 -2
- data/smoke/user-demo.rb +3 -3
- data/smoke/wrong-extend.rb +3 -2
- data/smoke/wrong-include.rb +3 -2
- data/smoke/wrong-include2.rb +17 -0
- data/typeprof.gemspec +1 -1
- metadata +59 -6
- data/tools/stackprof-wrapper.rb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88521dfb48f311cdb5b651e88afc2627a28040a466a22b50303415531e663cfd
|
4
|
+
data.tar.gz: 4bd17f9d016d6e7a60ed72804eb050a5611af0869275e63d3486700a53befaaf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f88b069345f02729a09c0516baf4ff31c8aef640adf5a2c53f62f72a9fb8b96b4309993ae79e37ece4ec33972f762005b3898ca7c894cda65c3727513b812b6b
|
7
|
+
data.tar.gz: aa5fec1acd9dd6395d3d16e044f08e7b72fac0f41dc6168683c631fcadca33daf2415b9956ba00cd22c573c6732719fad9eeef3d601a16943d59be6986703c20
|
data/Gemfile.lock
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
typeprof (0.
|
5
|
-
rbs (>= 0.
|
4
|
+
typeprof (0.11.0)
|
5
|
+
rbs (>= 1.0.0)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
10
|
coverage-helpers (1.0.0)
|
11
|
-
docile (1.3.
|
11
|
+
docile (1.3.4)
|
12
12
|
power_assert (1.2.0)
|
13
13
|
rake (13.0.1)
|
14
|
-
rbs (0.
|
14
|
+
rbs (1.0.0)
|
15
15
|
simplecov (0.20.0)
|
16
16
|
docile (~> 1.1)
|
17
17
|
simplecov-html (~> 0.11)
|
@@ -24,6 +24,7 @@ GEM
|
|
24
24
|
|
25
25
|
PLATFORMS
|
26
26
|
ruby
|
27
|
+
x86_64-linux
|
27
28
|
|
28
29
|
DEPENDENCIES
|
29
30
|
coverage-helpers
|
@@ -35,4 +36,4 @@ DEPENDENCIES
|
|
35
36
|
typeprof!
|
36
37
|
|
37
38
|
BUNDLED WITH
|
38
|
-
2.2.
|
39
|
+
2.2.3
|
data/doc/demo.md
CHANGED
@@ -39,7 +39,7 @@ class Object
|
|
39
39
|
end
|
40
40
|
```
|
41
41
|
|
42
|
-
|
42
|
+
You can [try this analysis online](https://mame.github.io/typeprof-playground/#rb=def+hello_message%28user%29%0A++%22The+name+is+%22+%2B+user.name%0Aend%0A%0Adef+type_error_demo%28user%29%0A++%22The+age+is+%22+%2B+user.age%0Aend%0A%0Auser+%3D+User.new%28name%3A+%22John%22%2C+age%3A+20%29%0A%0Ahello_message%28user%29%0Atype_error_demo%28user%29&rbs=class+User%0A++attr_reader+name%3A+String%0A++attr_reader+age%3A+Integer%0A%0A++def+initialize%3A+%28name%3A+String%2C+age%3A+Integer%29+-%3E+void%0Aend).
|
43
43
|
|
44
44
|
## A simple demo to generate the signature prototype of "User" class
|
45
45
|
|
data/doc/todo.md
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
# TypeProf milestones and TODOs
|
2
|
+
|
3
|
+
## Big milestones
|
4
|
+
|
5
|
+
### Rails support
|
6
|
+
|
7
|
+
There are many known issues for the analysis of typical Ruby programs including Rails apps.
|
8
|
+
|
9
|
+
* The main difficulty is that they use many language extensions like `ActiveSupport`.
|
10
|
+
Some features (for example, `blank?` and `Time.now + 1.day`) are trivial to support,
|
11
|
+
but others (for example, `ActiveSupport::Concern` and Zeitwerk) will require special support.
|
12
|
+
* The other difficulty is that they heavily use meta-programming features like `ActiveRecord`.
|
13
|
+
It dynamically defines some methods based on external data (such as DB schema) from the code.
|
14
|
+
|
15
|
+
Currently, projects called [`gem_rbs`](https://github.com/ruby/gem_rbs) and [`rbs_rails`](https://github.com/pocke/rbs_rails) are in progress.
|
16
|
+
The former provides several RBS files for some major gems including Rails.
|
17
|
+
The latter is a tool to generate RBS prototype of a target Rails application by introspection (executing it and monitoring DB schema, etc).
|
18
|
+
TypeProf can use their results to improve analysis precision and performance.
|
19
|
+
|
20
|
+
What we need to do:
|
21
|
+
|
22
|
+
* Experimentally apply TypeProf to some Rails programs and identify problems
|
23
|
+
* Make TypeProf able to work together with `rbs_rails` for supporting trivial core extensions and `ActiveRecord`.
|
24
|
+
* Implement special support for some fundamental language extensions of Rails like `ActiveSupport::Concern`.
|
25
|
+
(It would be best if TypeProf has a plugin system and if we can factor out the special support as a plugin for Rails.)
|
26
|
+
|
27
|
+
### Error detection and diagnosis feature
|
28
|
+
|
29
|
+
At present, TypeProf focuses on generation of RBS prototype from no-type-annotated Ruby code.
|
30
|
+
However, it is possible for TypeProf to report possible errors found during the analysis.
|
31
|
+
In fact, an option `-v` experimentally shows possible errors found.
|
32
|
+
There are some reasons why it is disabled by default:
|
33
|
+
|
34
|
+
* (1) There are too many false positives.
|
35
|
+
* (2) Some kind of error reporting is not implemented yet.
|
36
|
+
* (3) Some reported errors are difficult for a user to understand.
|
37
|
+
|
38
|
+
For (1), we will research how we can avoid false positives to support typical Ruby coding patterns as much as possible.
|
39
|
+
The primary way is to improve the analysis precision, e.g., enhancing flow-sensitive analysis.
|
40
|
+
If the S/N ratio of an error type is too low, we need to consider to suppress the kind of reports.
|
41
|
+
Also, we may try allowing users to guide TypeProf to analyze their program well.
|
42
|
+
(The simplest way is to write inline type casts in the code, but we need to find more Ruby/RBS way.)
|
43
|
+
We may also explore a "TypeProf-friendly coding style" which TypeProf can analyze well.
|
44
|
+
(In principle, the plainer code is, the better TypeProf can analyze.)
|
45
|
+
|
46
|
+
For (2), currently, TypeProf checks the argument types of a call to a method whose type signature is declared in RBS.
|
47
|
+
However, it does not check the return type yet. Redefinition of constants should be warned too.
|
48
|
+
We will survey what errors and warnings TypeProf can print, and evaluate the S/N ratio of each report.
|
49
|
+
|
50
|
+
For (3), since TypeProf uses whole program analysis, an error may be reported at a very different place from its root bug.
|
51
|
+
Thus, if TypeProf shows a possible type error, a diagnosis feature is needed to answer why TypeProf thinks that the error may occur.
|
52
|
+
TypeProf has already implemented a very primitive diagnosis feature, `Kernel#p`, to check what type an expression has.
|
53
|
+
Another idea is to create a pseudo backtrace why TypeProf thought the possible type error may occur.
|
54
|
+
We should consider this feature with LSP support.
|
55
|
+
|
56
|
+
### Performance improvement
|
57
|
+
|
58
|
+
Currently, TypeProf is painfully slow. Even if a target application is small.
|
59
|
+
|
60
|
+
The main reason is that TypeProf analyzes not only the application code but also library code:
|
61
|
+
if an application requires `"foo"`, TypeProf actually loads `foo.rb` even from a gem,
|
62
|
+
and furthermore, if `foo.rb` requires `"bar"`, it loads `bar.rb` recursively.
|
63
|
+
|
64
|
+
RBS will help to stop this cascade;
|
65
|
+
when an application requires `"foo"`, TypeProf loads `sig/foo.rbs` instead of `foo.rb` if the `foo` gem contains both.
|
66
|
+
Such a RBS file is optional for TypeProf but required for Steep.
|
67
|
+
So, we think many gems will eventually equip their RBS declarations.
|
68
|
+
|
69
|
+
That being said, we should continue to improve the analysis performance of TypeProf. We have some ideas.
|
70
|
+
|
71
|
+
* Unfortunately, TypeProf often analyzes one method more than once when it accepts multiple types.
|
72
|
+
As TypeProf squashes the argument types to a union, this duplicated analysis is not necessarily needed.
|
73
|
+
But when TypeProf first analyzes a method, it is difficult to determine if the method will accept another type in further analysis.
|
74
|
+
So, we need good heuristics to guess whether a method accepts multiple types or not, and if so, delay its analysis.
|
75
|
+
* Currently, TypeProf executes the bytecode instructions step by step.
|
76
|
+
This requires creating an environment object after each instruction, which is very heavy.
|
77
|
+
Many environment creations can be omitted by executing each basic block instead of each instruction.
|
78
|
+
(Basic block execution will also make flow-sensitive analysis easier.)
|
79
|
+
* The slowest calculation in TypeProf is to create an instance of a Type class.
|
80
|
+
The creation uses memoization; TypeProf keeps all Type instances created so far, and reuses them if already exist.
|
81
|
+
However, it is very heavy to check if an instance already exists or not.
|
82
|
+
(Currently, it is very simply implemented by a big Hash table.)
|
83
|
+
We've already improved the memoization routine several times but looks like it is still the No.1 bottleneck.
|
84
|
+
We need to investigate and try improving more.
|
85
|
+
* TypeProf heavily uses Hash objects (including above) mainly to represent a set.
|
86
|
+
A union of sets is done by `Hash#merge`, which takes O(n).
|
87
|
+
A more lightweight data structure may make TypeProf faster.
|
88
|
+
(But clever structure often has a big constant term, so we need to evaluate the performance carefully.)
|
89
|
+
* Reusing an old analysis and incrementally updating it will bring a super big improvement.
|
90
|
+
This would be especially helpful for LSP support, so we need to tackle it after the analysis approach is mature.
|
91
|
+
|
92
|
+
### Language Server Protocol (LSP) support
|
93
|
+
|
94
|
+
In the future, we want TypeProf to serve as a language server to show the result in IDE in real-time.
|
95
|
+
However, the current analysis approach is too slow for IDE. So we need to improve the performance first.
|
96
|
+
|
97
|
+
Even if TypeProf becomes fast enough, its approach has a fundamental problem.
|
98
|
+
Since TypeProf uses whole program analysis, one edit may cause a cascade of propagation:
|
99
|
+
if a user write `foo(42)`, an Integer is propagated to a method `foo`,
|
100
|
+
and if `foo` passes its argument to a method `bar`, it is propagated to `bar`, ...
|
101
|
+
So, a breakthrough for LSP may be still needed, e.g, limiting the propagation range in real-time analysis,
|
102
|
+
assuming that a type interface of module boundary is fixed, etc.
|
103
|
+
|
104
|
+
## Relatively smaller TODOs
|
105
|
+
|
106
|
+
* Support more RBS features
|
107
|
+
* TypeProf does not deal with some RBS types well yet.
|
108
|
+
* For example, the `instance` type is handled as `untyped.
|
109
|
+
* The `self` type is handled well only when it is used as a return type.
|
110
|
+
* Using a value of the `void` type should be warned appropriately.
|
111
|
+
* RBS's `interface` is supported just like a module (i.e., `include _Foo` is explicitly required in RBS),
|
112
|
+
but it should be checked structually (i.e., it should be determined as a method set.)
|
113
|
+
* The variance of type parameters is currently ignored.
|
114
|
+
|
115
|
+
* Support more Ruby features
|
116
|
+
* Some meta-programming features like `Class.new`, `Object#method`, etc.
|
117
|
+
* It is possible to support `Class.new` by per-allocation-site approach:
|
118
|
+
e.g., In TypeProf, `A = Class.new; B = Class.new` will create two classes, but `2.times { Class.new }` will create one class.
|
119
|
+
* The analysis precision can be improved more for some Ruby features like pattern matching, keyword arguments, etc.
|
120
|
+
* For example, `foo(*args, k:1)` is currently compiled as if it is `foo(*(args + [{ :k => 1 }]))` into Ruby bytecode.
|
121
|
+
This mixes the keyword arguments to a rest array, and makes it difficult for TypeProf to track the keyword arguments.
|
122
|
+
* Support Enumerator as an Array-type container.
|
123
|
+
* Support `Module#protect` (but RBS does not yet).
|
124
|
+
* More heuristics may help such as `==` returns a bool regardless to its receiver and argument types.
|
125
|
+
|
126
|
+
* Make TypeProf more useful as a tool
|
127
|
+
* Currently, TypeProf provides only the analysis engine and a minimal set of features.
|
128
|
+
* The analysis result would be useful not only to generate RBS prototype
|
129
|
+
but also identifying the source location of a method definition, listing callsites of a method,
|
130
|
+
searching a method call by its argument types, etc.
|
131
|
+
* Sometimes, TypeProf prints very big union type, such as `Integer | Float | Complex | Rational | ...`.
|
132
|
+
Worse, the same big type is printed multiple times.
|
133
|
+
It may be useful to factor out such a long type by using type alias, for example.
|
data/lib/typeprof/analyzer.rb
CHANGED
@@ -34,6 +34,14 @@ module TypeProf
|
|
34
34
|
end
|
35
35
|
|
36
36
|
attr_reader :iseq, :cref, :mid
|
37
|
+
|
38
|
+
def source_location(pc)
|
39
|
+
if @iseq
|
40
|
+
@iseq.source_location(pc)
|
41
|
+
else
|
42
|
+
"<builtin>"
|
43
|
+
end
|
44
|
+
end
|
37
45
|
end
|
38
46
|
|
39
47
|
class TypedContext
|
@@ -45,6 +53,14 @@ module TypeProf
|
|
45
53
|
end
|
46
54
|
|
47
55
|
attr_reader :caller_ep, :mid
|
56
|
+
|
57
|
+
def source_location(_pc)
|
58
|
+
if @caller_ep
|
59
|
+
@caller_ep.source_location
|
60
|
+
else
|
61
|
+
"<typed-context:#{ @mid }>"
|
62
|
+
end
|
63
|
+
end
|
48
64
|
end
|
49
65
|
|
50
66
|
class ExecutionPoint
|
@@ -71,12 +87,7 @@ module TypeProf
|
|
71
87
|
end
|
72
88
|
|
73
89
|
def source_location
|
74
|
-
|
75
|
-
if iseq
|
76
|
-
iseq.source_location(@pc)
|
77
|
-
else
|
78
|
-
"<builtin>"
|
79
|
-
end
|
90
|
+
@ctx.source_location(@pc)
|
80
91
|
end
|
81
92
|
end
|
82
93
|
|
@@ -91,7 +102,7 @@ module TypeProf
|
|
91
102
|
|
92
103
|
return if recv_ty == :top #OK
|
93
104
|
recv_ty.each_child_global do |ty|
|
94
|
-
raise ty.inspect if !ty.is_a?(Type::Instance) && !ty.is_a?(Type::Class) && ty != Type.any
|
105
|
+
raise ty.inspect if !ty.is_a?(Type::Instance) && !ty.is_a?(Type::Class) && !ty.is_a?(Type::Symbol) && ty != Type.any
|
95
106
|
end
|
96
107
|
end
|
97
108
|
|
@@ -211,6 +222,11 @@ module TypeProf
|
|
211
222
|
Env.new(senv, @locals, @stack, @type_params)
|
212
223
|
end
|
213
224
|
|
225
|
+
def replace_blk_ty(ty)
|
226
|
+
senv = StaticEnv.new(@static_env.recv_ty, ty, @static_env.mod_func, @static_env.pub_meth)
|
227
|
+
Env.new(senv, @locals, @stack, @type_params)
|
228
|
+
end
|
229
|
+
|
214
230
|
def inspect
|
215
231
|
"Env[#{ @static_env.inspect }, locals:#{ @locals.inspect }, stack:#{ @stack.inspect }, type_params:#{ (@type_params&.internal_hash).inspect }]"
|
216
232
|
end
|
@@ -284,7 +300,10 @@ module TypeProf
|
|
284
300
|
def initialize(kind, name, absolute_path)
|
285
301
|
raise unless name.is_a?(Array)
|
286
302
|
@kind = kind
|
287
|
-
@modules = {
|
303
|
+
@modules = {
|
304
|
+
:before => { true => [], false => [] }, # before = include/extend
|
305
|
+
:after => { true => [], false => [] }, # after = prepend
|
306
|
+
}
|
288
307
|
@name = name
|
289
308
|
@consts = {}
|
290
309
|
@methods = {}
|
@@ -297,13 +316,13 @@ module TypeProf
|
|
297
316
|
attr_reader :kind, :modules, :consts, :methods, :ivars, :cvars, :absolute_path
|
298
317
|
attr_accessor :name, :klass_obj
|
299
318
|
|
300
|
-
def
|
301
|
-
|
302
|
-
if
|
303
|
-
raise "inconsistent include/extend type args in RBS?" if module_type_args != type_args && type_args != [] && type_args != nil
|
319
|
+
def mix_module(kind, mod, type_args, singleton, absolute_path)
|
320
|
+
mod_, module_type_args, absolute_paths = @modules[kind][singleton].find {|m,| m == mod }
|
321
|
+
if mod_
|
322
|
+
raise "inconsistent #{ kind == :after ? "include/extend" : "prepend" } type args in RBS?" if module_type_args != type_args && type_args != [] && type_args != nil
|
304
323
|
else
|
305
324
|
absolute_paths = Utils::MutableSet.new
|
306
|
-
@modules[singleton].unshift([mod, type_args, absolute_paths])
|
325
|
+
@modules[kind][singleton].unshift([mod, type_args, absolute_paths])
|
307
326
|
end
|
308
327
|
absolute_paths << absolute_path
|
309
328
|
end
|
@@ -320,10 +339,8 @@ module TypeProf
|
|
320
339
|
@consts[name] = [ty, absolute_path]
|
321
340
|
end
|
322
341
|
|
323
|
-
def
|
324
|
-
|
325
|
-
yield subst, direct if mthds&.include?(mthd)
|
326
|
-
@modules[singleton].each do |mod_def, type_args,|
|
342
|
+
def adjust_substitution_for_module(mods, mid, mthd, subst, &blk)
|
343
|
+
mods.each do |mod_def, type_args,|
|
327
344
|
if mod_def.klass_obj.type_params && type_args
|
328
345
|
subst2 = {}
|
329
346
|
mod_def.klass_obj.type_params.zip(type_args) do |(tyvar, *), tyarg|
|
@@ -335,11 +352,29 @@ module TypeProf
|
|
335
352
|
end
|
336
353
|
end
|
337
354
|
|
338
|
-
def
|
355
|
+
def adjust_substitution(singleton, mid, mthd, subst, direct, &blk)
|
356
|
+
adjust_substitution_for_module(@modules[:before][singleton], mid, mthd, subst, &blk)
|
357
|
+
|
358
|
+
mthds = @methods[[singleton, mid]]
|
359
|
+
yield subst, direct if mthds&.include?(mthd)
|
360
|
+
|
361
|
+
adjust_substitution_for_module(@modules[:after][singleton], mid, mthd, subst, &blk)
|
362
|
+
end
|
363
|
+
|
364
|
+
def search_method(singleton, mid, visited, &blk)
|
365
|
+
# Currently, circular inclusion of modules is allowed
|
366
|
+
return if visited[self]
|
367
|
+
visited[self] = true
|
368
|
+
|
369
|
+
@modules[:before][singleton].each do |mod_def,|
|
370
|
+
mod_def.search_method(false, mid, visited, &blk)
|
371
|
+
end
|
372
|
+
|
339
373
|
mthds = @methods[[singleton, mid]]
|
340
374
|
yield mthds, @klass_obj, singleton if mthds
|
341
|
-
|
342
|
-
|
375
|
+
|
376
|
+
@modules[:after][singleton].each do |mod_def,|
|
377
|
+
mod_def.search_method(false, mid, visited, &blk)
|
343
378
|
end
|
344
379
|
end
|
345
380
|
|
@@ -367,17 +402,17 @@ module TypeProf
|
|
367
402
|
end
|
368
403
|
end
|
369
404
|
|
370
|
-
def
|
371
|
-
return if
|
405
|
+
def mix_module(kind, mixing_mod, mixed_mod, type_args, singleton, caller_ep)
|
406
|
+
return if mixed_mod == Type.any
|
372
407
|
|
373
|
-
|
374
|
-
|
375
|
-
if
|
376
|
-
|
377
|
-
if
|
378
|
-
|
408
|
+
mixing_mod = @class_defs[mixing_mod.idx]
|
409
|
+
mixed_mod.each_child do |mixed_mod|
|
410
|
+
if mixed_mod.is_a?(Type::Class)
|
411
|
+
mixed_mod = @class_defs[mixed_mod.idx]
|
412
|
+
if mixed_mod && mixed_mod.kind == :module
|
413
|
+
mixing_mod.mix_module(kind, mixed_mod, type_args, singleton, caller_ep ? caller_ep.ctx.iseq.absolute_path : nil)
|
379
414
|
else
|
380
|
-
warn "
|
415
|
+
warn(caller_ep, "attempted to #{ kind == :after ? "include/extend" : "prepend" } non-module; ignored")
|
381
416
|
end
|
382
417
|
end
|
383
418
|
end
|
@@ -479,13 +514,13 @@ module TypeProf
|
|
479
514
|
if klass.kind == :class
|
480
515
|
while klass != :__root__
|
481
516
|
class_def = @class_defs[klass.idx]
|
482
|
-
class_def.search_method(singleton, mid, &blk)
|
517
|
+
class_def.search_method(singleton, mid, {}, &blk)
|
483
518
|
klass = klass.superclass
|
484
519
|
end
|
485
520
|
else
|
486
521
|
# module
|
487
522
|
class_def = @class_defs[klass.idx]
|
488
|
-
class_def.search_method(singleton, mid, &blk)
|
523
|
+
class_def.search_method(singleton, mid, {}, &blk)
|
489
524
|
end
|
490
525
|
if singleton
|
491
526
|
search_method(Type::Builtin[klass_orig.kind], false, mid, &blk)
|
@@ -545,9 +580,7 @@ module TypeProf
|
|
545
580
|
end
|
546
581
|
|
547
582
|
def add_constant(klass, name, value, user_defined)
|
548
|
-
if klass
|
549
|
-
self
|
550
|
-
else
|
583
|
+
if klass.is_a?(Type::Class)
|
551
584
|
@class_defs[klass.idx].add_constant(name, value, user_defined)
|
552
585
|
end
|
553
586
|
end
|
@@ -566,12 +599,12 @@ module TypeProf
|
|
566
599
|
mdef
|
567
600
|
end
|
568
601
|
|
569
|
-
def add_attr_method(klass,
|
602
|
+
def add_attr_method(klass, mid, ivar, kind, pub_meth, ep)
|
570
603
|
if kind == :reader || kind == :accessor
|
571
|
-
add_method(klass, mid, false, AttrMethodDef.new(ivar, :reader,
|
604
|
+
add_method(klass, mid, false, AttrMethodDef.new(ivar, :reader, pub_meth, ep))
|
572
605
|
end
|
573
606
|
if kind == :writer || kind == :accessor
|
574
|
-
add_method(klass, :"#{ mid }=", false, AttrMethodDef.new(ivar, :writer,
|
607
|
+
add_method(klass, :"#{ mid }=", false, AttrMethodDef.new(ivar, :writer, pub_meth, ep))
|
575
608
|
end
|
576
609
|
end
|
577
610
|
|
@@ -583,22 +616,22 @@ module TypeProf
|
|
583
616
|
add_method(klass, mid, true, ISeqMethodDef.new(iseq, cref, outer_ep, pub_meth))
|
584
617
|
end
|
585
618
|
|
586
|
-
def set_custom_method(klass, mid, impl)
|
587
|
-
set_method(klass, mid, false, CustomMethodDef.new(impl))
|
619
|
+
def set_custom_method(klass, mid, impl, pub_meth = true)
|
620
|
+
set_method(klass, mid, false, CustomMethodDef.new(impl, pub_meth))
|
588
621
|
end
|
589
622
|
|
590
|
-
def set_singleton_custom_method(klass, mid, impl)
|
591
|
-
set_method(klass, mid, true, CustomMethodDef.new(impl))
|
623
|
+
def set_singleton_custom_method(klass, mid, impl, pub_meth = true)
|
624
|
+
set_method(klass, mid, true, CustomMethodDef.new(impl, pub_meth))
|
592
625
|
end
|
593
626
|
|
594
|
-
def alias_method(klass, singleton,
|
627
|
+
def alias_method(klass, singleton, alias_mid, orig_mid, ep)
|
595
628
|
if klass == Type.any
|
596
629
|
self
|
597
630
|
else
|
598
|
-
mdefs = get_method(klass, singleton,
|
631
|
+
mdefs = get_method(klass, singleton, orig_mid)
|
599
632
|
if mdefs
|
600
633
|
mdefs.each do |mdef|
|
601
|
-
@class_defs[klass.idx].add_method(
|
634
|
+
@class_defs[klass.idx].add_method(alias_mid, singleton, AliasMethodDef.new(orig_mid, mdef, ep))
|
602
635
|
end
|
603
636
|
end
|
604
637
|
end
|
@@ -613,6 +646,10 @@ module TypeProf
|
|
613
646
|
@iseq_method_to_ctxs[iseq_mdef] << ctx
|
614
647
|
end
|
615
648
|
|
649
|
+
def add_executed_iseq(iseq)
|
650
|
+
@executed_iseqs << iseq
|
651
|
+
end
|
652
|
+
|
616
653
|
def add_callsite!(callee_ctx, caller_ep, caller_env, &ctn)
|
617
654
|
@executed_iseqs << callee_ctx.iseq if callee_ctx.is_a?(Context)
|
618
655
|
|
@@ -672,8 +709,10 @@ module TypeProf
|
|
672
709
|
def add_read!(site, ep, &ctn)
|
673
710
|
entry = @tbl[site] ||= Entry.new(false, {}, Type.bot, Utils::MutableSet.new)
|
674
711
|
entry.read_continuations[ep] = ctn
|
675
|
-
entry.absolute_paths << ep.ctx.iseq.absolute_path
|
676
|
-
|
712
|
+
entry.absolute_paths << ep.ctx.iseq.absolute_path if ep.ctx.is_a?(Context)
|
713
|
+
ty = entry.type
|
714
|
+
ty = Type.nil if ty == Type.bot
|
715
|
+
ctn[ty, ep]
|
677
716
|
end
|
678
717
|
|
679
718
|
def add_write!(site, ty, ep, scratch)
|
@@ -681,7 +720,7 @@ module TypeProf
|
|
681
720
|
if ep
|
682
721
|
if entry.rbs_declared
|
683
722
|
unless Type.match?(ty, entry.type)
|
684
|
-
scratch.warn(ep, "inconsistent assignment to RBS-declared
|
723
|
+
scratch.warn(ep, "inconsistent assignment to RBS-declared variable")
|
685
724
|
return
|
686
725
|
end
|
687
726
|
end
|
@@ -731,6 +770,7 @@ module TypeProf
|
|
731
770
|
|
732
771
|
def add_cvar_read!(klass, var, ep, &ctn)
|
733
772
|
klass.each_child do |klass|
|
773
|
+
next unless klass.is_a?(Type::Class)
|
734
774
|
class_def = @class_defs[klass.idx]
|
735
775
|
next unless class_def
|
736
776
|
class_def.cvars.add_read!(var, ep, &ctn)
|
@@ -739,6 +779,7 @@ module TypeProf
|
|
739
779
|
|
740
780
|
def add_cvar_write!(klass, var, ty, ep)
|
741
781
|
klass.each_child do |klass|
|
782
|
+
next unless klass.is_a?(Type::Class)
|
742
783
|
class_def = @class_defs[klass.idx]
|
743
784
|
next unless class_def
|
744
785
|
class_def.cvars.add_write!(var, ty, ep, self)
|
@@ -870,7 +911,7 @@ module TypeProf
|
|
870
911
|
@pending_execution.delete(iseq)
|
871
912
|
end while @executed_iseqs.include?(iseq)
|
872
913
|
|
873
|
-
puts "DEBUG: trigger
|
914
|
+
puts "DEBUG: trigger stub execution (#{ iseq&.name || "(nil)" }): rest #{ @pending_execution.size }" if Config.verbose >= 2
|
874
915
|
|
875
916
|
break if !iseq
|
876
917
|
case kind
|
@@ -974,10 +1015,14 @@ module TypeProf
|
|
974
1015
|
|
975
1016
|
def pend_block_dummy_execution(blk, iseq, nep, nenv)
|
976
1017
|
@pending_execution[iseq] ||= [:block, [blk, {}]]
|
977
|
-
if @pending_execution[iseq][
|
978
|
-
|
1018
|
+
if @pending_execution[iseq][0] == :block
|
1019
|
+
if @pending_execution[iseq][1][1][nep]
|
1020
|
+
@pending_execution[iseq][1][1][nep] = @pending_execution[iseq][1][1][nep].merge(nenv)
|
1021
|
+
else
|
1022
|
+
@pending_execution[iseq][1][1][nep] = nenv
|
1023
|
+
end
|
979
1024
|
else
|
980
|
-
|
1025
|
+
# XXX: what to do?
|
981
1026
|
end
|
982
1027
|
end
|
983
1028
|
|
@@ -1026,9 +1071,9 @@ module TypeProf
|
|
1026
1071
|
block_start = iseq.fargs_format[:block_start]
|
1027
1072
|
|
1028
1073
|
lead_tys = env.locals[0, lead_num].map {|ty| globalize_type(ty, env, ep) }
|
1029
|
-
opt_tys = opt.size > 1 ? env.locals[lead_num, opt.size - 1].map {|ty| globalize_type(ty, env, ep) } :
|
1074
|
+
opt_tys = opt.size > 1 ? env.locals[lead_num, opt.size - 1].map {|ty| globalize_type(ty, env, ep) } : []
|
1030
1075
|
if rest_start # XXX:squash
|
1031
|
-
ty = globalize_type(env.locals[
|
1076
|
+
ty = globalize_type(env.locals[rest_start], env, ep)
|
1032
1077
|
rest_ty = Type.bot
|
1033
1078
|
ty.each_child_global do |ty|
|
1034
1079
|
if ty.is_a?(Type::Array)
|
@@ -1063,6 +1108,7 @@ module TypeProf
|
|
1063
1108
|
end
|
1064
1109
|
end
|
1065
1110
|
kw_rest_ty = globalize_type(env.locals[kw_rest], env, ep) if kw_rest
|
1111
|
+
kw_rest_ty = nil if kw_rest_ty == Type.nil
|
1066
1112
|
if block_start
|
1067
1113
|
blk_ty = globalize_type(env.locals[block_start], env, ep)
|
1068
1114
|
elsif iseq.type == :method
|
@@ -1197,11 +1243,11 @@ module TypeProf
|
|
1197
1243
|
else # module
|
1198
1244
|
superclass = nil
|
1199
1245
|
end
|
1200
|
-
if cbase
|
1201
|
-
klass = Type.any
|
1202
|
-
else
|
1246
|
+
if cbase.is_a?(Type::Class)
|
1203
1247
|
klass = new_class(cbase, id, [], superclass, ep.ctx.iseq.absolute_path)
|
1204
1248
|
add_superclass_type_args!(klass, superclass.type_params.map { Type.any }) if superclass
|
1249
|
+
else
|
1250
|
+
klass = Type.any
|
1205
1251
|
end
|
1206
1252
|
end
|
1207
1253
|
singleton = false
|
@@ -1242,7 +1288,7 @@ module TypeProf
|
|
1242
1288
|
end
|
1243
1289
|
end
|
1244
1290
|
return
|
1245
|
-
when :
|
1291
|
+
when :getlocal_send_branch
|
1246
1292
|
getlocal_operands, send_operands, branch_operands = operands
|
1247
1293
|
env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
|
1248
1294
|
recvs = Type.any if recvs == Type.bot
|
@@ -1270,6 +1316,31 @@ module TypeProf
|
|
1270
1316
|
end
|
1271
1317
|
end
|
1272
1318
|
return
|
1319
|
+
when :send_branch
|
1320
|
+
send_operands, branch_operands = operands
|
1321
|
+
env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
|
1322
|
+
recvs = Type.any if recvs == Type.bot
|
1323
|
+
recvs.each_child do |recv|
|
1324
|
+
do_send(recv, mid, aargs, ep, env) do |ret_ty, ep, env|
|
1325
|
+
env, ret_ty, = localize_type(ret_ty, env, ep)
|
1326
|
+
|
1327
|
+
branchtype, target, = branch_operands
|
1328
|
+
# branchtype: :if or :unless or :nil
|
1329
|
+
ep_then = ep.next
|
1330
|
+
ep_else = ep.jump(target)
|
1331
|
+
|
1332
|
+
case ret_ty
|
1333
|
+
when Type::Instance.new(Type::Builtin[:true])
|
1334
|
+
merge_env(branchtype == :if ? ep_else : ep_then, env)
|
1335
|
+
when Type::Instance.new(Type::Builtin[:false])
|
1336
|
+
merge_env(branchtype == :if ? ep_then : ep_else, env)
|
1337
|
+
else
|
1338
|
+
merge_env(ep_then, env)
|
1339
|
+
merge_env(ep_else, env)
|
1340
|
+
end
|
1341
|
+
end
|
1342
|
+
end
|
1343
|
+
return
|
1273
1344
|
when :invokeblock
|
1274
1345
|
env, recvs, mid, aargs = setup_actual_arguments(:block, operands, ep, env)
|
1275
1346
|
blk = env.static_env.blk_ty
|
@@ -1339,11 +1410,24 @@ module TypeProf
|
|
1339
1410
|
return
|
1340
1411
|
when :break
|
1341
1412
|
tmp_ep = ep
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
|
1346
|
-
|
1413
|
+
while true
|
1414
|
+
if tmp_ep.ctx.iseq.type == :block
|
1415
|
+
tmp_ep = tmp_ep.outer
|
1416
|
+
nenv = @return_envs[tmp_ep].push(ty)
|
1417
|
+
merge_env(tmp_ep.next, nenv)
|
1418
|
+
break
|
1419
|
+
end
|
1420
|
+
_type, _iseq, cont, stack_depth = tmp_ep.ctx.iseq.catch_table[tmp_ep.pc]&.find {|type,| type == :break }
|
1421
|
+
if cont
|
1422
|
+
nenv = @return_envs[tmp_ep]
|
1423
|
+
nenv, = nenv.pop(nenv.stack.size - stack_depth)
|
1424
|
+
nenv = nenv.push(ty)
|
1425
|
+
tmp_ep = tmp_ep.jump(cont)
|
1426
|
+
merge_env(tmp_ep, nenv)
|
1427
|
+
break
|
1428
|
+
end
|
1429
|
+
tmp_ep = tmp_ep.outer
|
1430
|
+
end
|
1347
1431
|
when :next, :redo
|
1348
1432
|
# begin; rescue; next; end
|
1349
1433
|
tmp_ep = ep.outer
|
@@ -1585,7 +1669,7 @@ module TypeProf
|
|
1585
1669
|
warn(ep, "already initialized constant #{ Type::Instance.new(cbase).screen_name(self) }::#{ name }")
|
1586
1670
|
end
|
1587
1671
|
ty.each_child do |ty|
|
1588
|
-
if ty.is_a?(Type::Class) && ty.superclass == Type::Builtin[:struct]
|
1672
|
+
if ty.is_a?(Type::Class) && cbase.is_a?(Type::Class) && ty.superclass == Type::Builtin[:struct]
|
1589
1673
|
@class_defs[ty.idx].name = cbase_path(cbase) + [name]
|
1590
1674
|
end
|
1591
1675
|
end
|
@@ -1594,7 +1678,6 @@ module TypeProf
|
|
1594
1678
|
when :getspecial
|
1595
1679
|
key, type = operands
|
1596
1680
|
if type == 0
|
1597
|
-
raise NotImplementedError
|
1598
1681
|
case key
|
1599
1682
|
when 0 # VM_SVAR_LASTLINE
|
1600
1683
|
env = env.push(Type.any) # or String | NilClass only?
|
@@ -1612,8 +1695,12 @@ module TypeProf
|
|
1612
1695
|
return
|
1613
1696
|
end
|
1614
1697
|
when :setspecial
|
1615
|
-
|
1616
|
-
|
1698
|
+
key, = operands
|
1699
|
+
if key >= 2 # flip-flop
|
1700
|
+
env, = env.pop(1)
|
1701
|
+
else
|
1702
|
+
raise "unknown setspecial key: #{ key }"
|
1703
|
+
end
|
1617
1704
|
|
1618
1705
|
when :dup
|
1619
1706
|
env, (ty,) = env.pop(1)
|
@@ -1664,10 +1751,16 @@ module TypeProf
|
|
1664
1751
|
env = env.push(Type.optional(sym_ty))
|
1665
1752
|
when :checkmatch
|
1666
1753
|
flag, = operands
|
1754
|
+
|
1755
|
+
# This flag means that the stack top is an array, and the check needs to be applied to find all elements
|
1756
|
+
# However, currently TypeProf uses very conservative interpretation (all check returns both true and false),
|
1757
|
+
# so we just ignore the flag now
|
1667
1758
|
_array = flag & 4 != 0
|
1759
|
+
|
1668
1760
|
case flag & 3
|
1669
|
-
when 1
|
1670
|
-
|
1761
|
+
when 1 # VM_CHECKMATCH_TYPE_WHEN
|
1762
|
+
env, = env.pop(2)
|
1763
|
+
env = env.push(Type.bool)
|
1671
1764
|
when 2 # VM_CHECKMATCH_TYPE_CASE
|
1672
1765
|
env, = env.pop(2)
|
1673
1766
|
env = env.push(Type.bool)
|
@@ -1877,6 +1970,10 @@ module TypeProf
|
|
1877
1970
|
rest_ty = aargs.last
|
1878
1971
|
aargs = aargs[0..-2]
|
1879
1972
|
if flag_args_kw_splat
|
1973
|
+
# XXX: The types contained in ActualArguments are expected to be all local types.
|
1974
|
+
# This "globalize_type" breaks the invariant, and violates the assertion of Union#globalize that asserts @elems be nil.
|
1975
|
+
# To fix this issue fundamentally, ActualArguments should keep all arguments as-is (as like the VM does),
|
1976
|
+
# and globalize some types on the on-demand bases.
|
1880
1977
|
ty = globalize_type(rest_ty, env, ep)
|
1881
1978
|
if ty.is_a?(Type::Array)
|
1882
1979
|
_, (ty,) = ty.elems.take_last(1)
|
@@ -1909,6 +2006,10 @@ module TypeProf
|
|
1909
2006
|
aargs = ActualArguments.new(aargs, rest_ty, kw_tys, blk_ty)
|
1910
2007
|
elsif flag_args_kw_splat
|
1911
2008
|
last = aargs.last
|
2009
|
+
# XXX: The types contained in ActualArguments are expected to be all local types.
|
2010
|
+
# This "globalize_type" breaks the invariant, and violates the assertion of Union#globalize that asserts @elems be nil.
|
2011
|
+
# To fix this issue fundamentally, ActualArguments should keep all arguments as-is (as like the VM does),
|
2012
|
+
# and globalize some types on the on-demand bases.
|
1912
2013
|
ty = globalize_type(last, env, ep)
|
1913
2014
|
case ty
|
1914
2015
|
when Type::Hash
|
@@ -2003,10 +2104,10 @@ module TypeProf
|
|
2003
2104
|
|
2004
2105
|
def do_define_iseq_method(ep, env, mid, iseq, outer_ep)
|
2005
2106
|
cref = ep.ctx.cref
|
2006
|
-
recv = env.static_env.recv_ty
|
2007
2107
|
if cref.klass.is_a?(Type::Class)
|
2008
2108
|
typed_mdef = check_typed_method(cref.klass, mid, ep.ctx.cref.singleton)
|
2009
|
-
recv =
|
2109
|
+
recv = cref.klass
|
2110
|
+
recv = Type::Instance.new(recv) unless ep.ctx.cref.singleton
|
2010
2111
|
if typed_mdef
|
2011
2112
|
mdef = ISeqMethodDef.new(iseq, cref, outer_ep, env.static_env.pub_meth)
|
2012
2113
|
typed_mdef.each do |typed_mdef|
|
@@ -2021,9 +2122,9 @@ module TypeProf
|
|
2021
2122
|
add_singleton_iseq_method(cref.klass, mid, iseq, cref, outer_ep, true)
|
2022
2123
|
end
|
2023
2124
|
end
|
2024
|
-
|
2025
|
-
pend_method_execution(iseq, meth, recv, mid, ep.ctx.cref, outer_ep)
|
2026
2125
|
end
|
2126
|
+
|
2127
|
+
pend_method_execution(iseq, meth, recv, mid, ep.ctx.cref, outer_ep)
|
2027
2128
|
else
|
2028
2129
|
# XXX: what to do?
|
2029
2130
|
end
|
@@ -2052,7 +2153,7 @@ module TypeProf
|
|
2052
2153
|
|
2053
2154
|
bsig ||= BlockSignature.new([], [], nil, Type.nil)
|
2054
2155
|
|
2055
|
-
bsig = bsig.screen_name(self)
|
2156
|
+
bsig = bsig.screen_name(nil, self)
|
2056
2157
|
ret_ty = ret_ty.screen_name(self)
|
2057
2158
|
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
2058
2159
|
|
@@ -2069,7 +2170,7 @@ module TypeProf
|
|
2069
2170
|
next unless @block_to_ctx[blk.block_body] # this occurs when screen_name is called before type-profiling finished (e.g., error message)
|
2070
2171
|
@block_to_ctx[blk.block_body].each do |blk_ctx|
|
2071
2172
|
if farg_tys
|
2072
|
-
farg_tys = farg_tys.
|
2173
|
+
farg_tys = farg_tys.merge_as_block_arguments(@method_signatures[blk_ctx])
|
2073
2174
|
else
|
2074
2175
|
farg_tys = @method_signatures[blk_ctx]
|
2075
2176
|
end
|
@@ -2079,7 +2180,7 @@ module TypeProf
|
|
2079
2180
|
end
|
2080
2181
|
end
|
2081
2182
|
|
2082
|
-
farg_tys = farg_tys ? farg_tys.screen_name(self) : "(unknown)"
|
2183
|
+
farg_tys = farg_tys ? farg_tys.screen_name(nil, self) : "(unknown)"
|
2083
2184
|
ret_ty = ret_ty.screen_name(self)
|
2084
2185
|
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
2085
2186
|
|
@@ -2091,7 +2192,7 @@ module TypeProf
|
|
2091
2192
|
farg_tys = @method_signatures[ctx]
|
2092
2193
|
ret_ty = @return_values[ctx] || Type.bot
|
2093
2194
|
|
2094
|
-
farg_tys = farg_tys.screen_name(self)
|
2195
|
+
farg_tys = farg_tys.screen_name(ctx.iseq, self)
|
2095
2196
|
ret_ty = ret_ty.screen_name(self)
|
2096
2197
|
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
2097
2198
|
"#{ (farg_tys.empty? ? "" : "#{ farg_tys } ") }-> #{ ret_ty }"
|