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
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
require "rbconfig"
|
|
2
|
+
|
|
3
|
+
module TypeProf
|
|
4
|
+
ConfigData = Struct.new(
|
|
5
|
+
:rb_files,
|
|
6
|
+
:rbs_files,
|
|
7
|
+
:output,
|
|
8
|
+
:gem_rbs_features,
|
|
9
|
+
:verbose,
|
|
10
|
+
:dir_filter,
|
|
11
|
+
:max_iter,
|
|
12
|
+
:max_sec,
|
|
13
|
+
:options,
|
|
14
|
+
keyword_init: true
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
class ConfigData
|
|
18
|
+
def initialize(**opt)
|
|
19
|
+
opt[:output] ||= $stdout
|
|
20
|
+
opt[:gem_rbs_features] ||= []
|
|
21
|
+
opt[:dir_filter] ||= DEFAULT_DIR_FILTER
|
|
22
|
+
opt[:verbose] ||= 0
|
|
23
|
+
opt[:options] ||= {}
|
|
24
|
+
opt[:options] = {
|
|
25
|
+
type_depth_limit: 5,
|
|
26
|
+
pedantic_output: false,
|
|
27
|
+
show_errors: false,
|
|
28
|
+
stackprof: nil,
|
|
29
|
+
}.merge(opt[:options])
|
|
30
|
+
super(**opt)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def check_dir_filter(path)
|
|
34
|
+
dir_filter.reverse_each do |cond, dir|
|
|
35
|
+
return cond unless dir
|
|
36
|
+
return cond if path.start_with?(dir)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
DEFAULT_DIR_FILTER = [
|
|
41
|
+
[:include],
|
|
42
|
+
[:exclude, RbConfig::CONFIG["prefix"]],
|
|
43
|
+
[:exclude, Gem.dir],
|
|
44
|
+
[:exclude, Gem.user_dir],
|
|
45
|
+
]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.analyze(config)
|
|
49
|
+
# Deploy the config to the TypeProf::Config (Note: This is thread unsafe)
|
|
50
|
+
if TypeProf.const_defined?(:Config)
|
|
51
|
+
TypeProf.send(:remove_const, :Config)
|
|
52
|
+
end
|
|
53
|
+
TypeProf.const_set(:Config, config)
|
|
54
|
+
|
|
55
|
+
if Config.options[:stackprof]
|
|
56
|
+
require "stackprof"
|
|
57
|
+
out = "typeprof-stackprof-#{ Config.options[:stackprof] }.dump"
|
|
58
|
+
StackProf.start(mode: Config.options[:stackprof], out: out, raw: true)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
scratch = Scratch.new
|
|
62
|
+
Builtin.setup_initial_global_env(scratch)
|
|
63
|
+
|
|
64
|
+
Config.gem_rbs_features.each do |feature|
|
|
65
|
+
Import.import_library(scratch, feature)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
prologue_ctx = Context.new(nil, nil, nil)
|
|
69
|
+
prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
|
|
70
|
+
prologue_env = Env.new(StaticEnv.new(:top, Type.nil, false), [], [], Utils::HashWrapper.new({}))
|
|
71
|
+
|
|
72
|
+
Config.rb_files.each do |file|
|
|
73
|
+
if file.respond_to?(:read)
|
|
74
|
+
iseq = ISeq.compile_str(file.read, file.to_s)
|
|
75
|
+
else
|
|
76
|
+
iseq = ISeq.compile(file)
|
|
77
|
+
end
|
|
78
|
+
ep, env = TypeProf.starting_state(iseq)
|
|
79
|
+
scratch.merge_env(ep, env)
|
|
80
|
+
scratch.add_callsite!(ep.ctx, prologue_ep, prologue_env) {|ty, ep| }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
Config.rbs_files.each do |path|
|
|
84
|
+
Import.import_rbs_file(scratch, path)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
result = scratch.type_profile
|
|
88
|
+
|
|
89
|
+
if Config.output.respond_to?(:write)
|
|
90
|
+
scratch.report(result, Config.output)
|
|
91
|
+
else
|
|
92
|
+
open(Config.output, "w") do |output|
|
|
93
|
+
scratch.report(result, output)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
ensure
|
|
98
|
+
if Config.options[:stackprof] && defined?(StackProf)
|
|
99
|
+
StackProf.stop
|
|
100
|
+
StackProf.results
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def self.starting_state(iseq)
|
|
105
|
+
cref = CRef.new(:bottom, Type::Builtin[:obj], false) # object
|
|
106
|
+
recv = Type::Instance.new(Type::Builtin[:obj])
|
|
107
|
+
ctx = Context.new(iseq, cref, nil)
|
|
108
|
+
ep = ExecutionPoint.new(ctx, 0, nil)
|
|
109
|
+
locals = [Type.nil] * iseq.locals.size
|
|
110
|
+
env = Env.new(StaticEnv.new(recv, Type.nil, false), locals, [], Utils::HashWrapper.new({}))
|
|
111
|
+
|
|
112
|
+
return ep, env
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -6,22 +6,197 @@ module TypeProf
|
|
|
6
6
|
raise if !val.is_a?(Utils::StructuralEquality) && !val.is_a?(Integer) && !val.is_a?(Symbol)
|
|
7
7
|
@val = val
|
|
8
8
|
@parent = parent
|
|
9
|
-
@_hash ||= (@val.hash ^ @parent.hash)
|
|
10
9
|
end
|
|
11
10
|
|
|
12
11
|
attr_reader :val, :parent
|
|
13
12
|
|
|
14
|
-
def hash
|
|
15
|
-
@_hash
|
|
16
|
-
end
|
|
17
|
-
|
|
18
13
|
def add_id(val)
|
|
19
14
|
AllocationSite.new(val, self)
|
|
20
15
|
end
|
|
21
16
|
end
|
|
22
17
|
|
|
23
18
|
class Type # or AbstractValue
|
|
24
|
-
#
|
|
19
|
+
# Cell, Array, and Hash are types for global interface, e.g., TypedISeq.
|
|
20
|
+
# Do not push such types to local environment, stack, etc.
|
|
21
|
+
|
|
22
|
+
# The most basic container type for default type parameter class
|
|
23
|
+
class Cell < Type
|
|
24
|
+
def initialize(elems, base_type)
|
|
25
|
+
raise if !elems.is_a?(Cell::Elements)
|
|
26
|
+
@elems = elems # Cell::Elements
|
|
27
|
+
raise unless base_type
|
|
28
|
+
@base_type = base_type
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
attr_reader :elems, :base_type
|
|
32
|
+
|
|
33
|
+
def inspect
|
|
34
|
+
"Type::Cell[#{ @elems.inspect }, base_type: #{ @base_type.inspect }]"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def screen_name(scratch)
|
|
38
|
+
str = @elems.screen_name(scratch)
|
|
39
|
+
if str.start_with?("*")
|
|
40
|
+
str = @base_type.screen_name(scratch) + str[1..]
|
|
41
|
+
end
|
|
42
|
+
str
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def localize(env, alloc_site, depth)
|
|
46
|
+
return env, Type.any if depth <= 0
|
|
47
|
+
alloc_site = alloc_site.add_id(:cell)
|
|
48
|
+
env, elems = @elems.localize(env, alloc_site, depth)
|
|
49
|
+
env.deploy_type(LocalCell, alloc_site, elems, @base_type)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def limit_size(limit)
|
|
53
|
+
return Type.any if limit <= 0
|
|
54
|
+
Cell.new(@elems.limit_size(limit - 1), @base_type)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def get_method(mid, scratch)
|
|
58
|
+
raise
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def consistent?(other, subst)
|
|
62
|
+
case other
|
|
63
|
+
when Type::Any then true
|
|
64
|
+
when Type::Var then other.add_subst!(self, subst)
|
|
65
|
+
when Type::Union
|
|
66
|
+
other.types.each do |ty2|
|
|
67
|
+
return true if consistent?(ty2, subst)
|
|
68
|
+
end
|
|
69
|
+
return false
|
|
70
|
+
when Type::Cell
|
|
71
|
+
@elems.size == other.elems.size &&
|
|
72
|
+
@base_type.consistent?(other.base_type, subst) &&
|
|
73
|
+
@elems.zip(other.elems).all? {|elem1, elem2| elem1..consistent?(elem2, subst) }
|
|
74
|
+
else
|
|
75
|
+
self == other
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def substitute(subst, depth)
|
|
80
|
+
return Type.any if depth <= 0
|
|
81
|
+
elems = @elems.substitute(subst, depth)
|
|
82
|
+
Cell.new(elems, @base_type)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class Elements
|
|
86
|
+
include Utils::StructuralEquality
|
|
87
|
+
|
|
88
|
+
def initialize(elems)
|
|
89
|
+
@elems = elems
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
attr_reader :elems
|
|
93
|
+
|
|
94
|
+
def to_local_type(id, base_ty)
|
|
95
|
+
Type::LocalCell.new(id, base_ty)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def globalize(env, visited, depth)
|
|
99
|
+
Elements.new(@elems.map {|ty| ty.globalize(env, visited, depth) })
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def localize(env, alloc_site, depth)
|
|
103
|
+
elems = @elems.map.with_index do |ty, i|
|
|
104
|
+
alloc_site2 = alloc_site.add_id(i)
|
|
105
|
+
env, ty = ty.localize(env, alloc_site2, depth)
|
|
106
|
+
ty
|
|
107
|
+
end
|
|
108
|
+
return env, Elements.new(elems)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def limit_size(limit)
|
|
112
|
+
Elements.new(@elems.map {|ty| ty.limit_size(limit) })
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def screen_name(scratch)
|
|
116
|
+
"*[#{ @elems.map {|ty| ty.screen_name(scratch) }.join(", ") }]"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def pretty_print(q)
|
|
120
|
+
q.group(9, "Elements[", "]") do
|
|
121
|
+
q.seplist(@elems) do |elem|
|
|
122
|
+
q.pp elem
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def consistent?(other, subst)
|
|
128
|
+
false if @elems.size != other.elems.size
|
|
129
|
+
@elems.zip(other.elems) do |ty0, ty1|
|
|
130
|
+
return false unless ty0.consistent?(ty1, subst)
|
|
131
|
+
end
|
|
132
|
+
return true
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def substitute(subst, depth)
|
|
136
|
+
Elements.new(@elems.map {|ty| ty.substitute(subst, depth) })
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def [](idx)
|
|
140
|
+
@elems[idx]
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def update(idx, ty)
|
|
144
|
+
Elements.new(Utils.array_update(@elems, idx, @elems[idx].union(ty)))
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def union(other)
|
|
148
|
+
return self if self == other
|
|
149
|
+
elems = []
|
|
150
|
+
@elems.zip(other.elems) do |ty0, ty1|
|
|
151
|
+
elems << ty0.union(ty1)
|
|
152
|
+
end
|
|
153
|
+
Elements.new(elems)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
class LocalCell < Type
|
|
159
|
+
def initialize(id, base_type)
|
|
160
|
+
@id = id
|
|
161
|
+
raise unless base_type
|
|
162
|
+
@base_type = base_type
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
attr_reader :id, :base_type
|
|
166
|
+
|
|
167
|
+
def inspect
|
|
168
|
+
"Type::LocalCell[#{ @id }, base_type: #{ @base_type.inspect }]"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def screen_name(scratch)
|
|
172
|
+
#raise "LocalArray must not be included in signature"
|
|
173
|
+
"LocalCell!"
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def globalize(env, visited, depth)
|
|
177
|
+
if visited[self] || depth <= 0
|
|
178
|
+
Type.any
|
|
179
|
+
else
|
|
180
|
+
visited[self] = true
|
|
181
|
+
elems = env.get_container_elem_types(@id)
|
|
182
|
+
if elems
|
|
183
|
+
elems = elems.globalize(env, visited, depth - 1)
|
|
184
|
+
else
|
|
185
|
+
elems = Cell::Elements.new([]) # XXX
|
|
186
|
+
end
|
|
187
|
+
Cell.new(elems, @base_type)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def get_method(mid, scratch)
|
|
192
|
+
@base_type.get_method(mid, scratch)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def consistent?(other, subst)
|
|
196
|
+
raise "must not be used"
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
25
200
|
# Do not insert Array type to local environment, stack, etc.
|
|
26
201
|
class Array < Type
|
|
27
202
|
def initialize(elems, base_type)
|
|
@@ -29,7 +204,6 @@ module TypeProf
|
|
|
29
204
|
@elems = elems # Array::Elements
|
|
30
205
|
raise unless base_type
|
|
31
206
|
@base_type = base_type
|
|
32
|
-
# XXX: need infinite recursion
|
|
33
207
|
end
|
|
34
208
|
|
|
35
209
|
attr_reader :elems, :base_type
|
|
@@ -47,18 +221,11 @@ module TypeProf
|
|
|
47
221
|
str
|
|
48
222
|
end
|
|
49
223
|
|
|
50
|
-
def globalize(env, visited, depth)
|
|
51
|
-
return Type.any if depth <= 0
|
|
52
|
-
elems = @elems.globalize(env, visited, depth - 1)
|
|
53
|
-
base_ty = @base_type.globalize(env, visited, depth - 1)
|
|
54
|
-
Array.new(elems, base_ty)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
224
|
def localize(env, alloc_site, depth)
|
|
58
225
|
return env, Type.any if depth <= 0
|
|
59
226
|
alloc_site = alloc_site.add_id(:ary)
|
|
60
227
|
env, elems = @elems.localize(env, alloc_site, depth - 1)
|
|
61
|
-
env.
|
|
228
|
+
env.deploy_type(LocalArray, alloc_site, elems, @base_type)
|
|
62
229
|
end
|
|
63
230
|
|
|
64
231
|
def limit_size(limit)
|
|
@@ -78,6 +245,7 @@ module TypeProf
|
|
|
78
245
|
other.types.each do |ty2|
|
|
79
246
|
return true if consistent?(ty2, subst)
|
|
80
247
|
end
|
|
248
|
+
return false
|
|
81
249
|
when Type::Array
|
|
82
250
|
@base_type.consistent?(other.base_type, subst) && @elems.consistent?(other.elems, subst)
|
|
83
251
|
else
|
|
@@ -102,8 +270,7 @@ module TypeProf
|
|
|
102
270
|
|
|
103
271
|
attr_reader :lead_tys, :rest_ty
|
|
104
272
|
|
|
105
|
-
def to_local_type(id)
|
|
106
|
-
base_ty = Type::Instance.new(Type::Builtin[:ary])
|
|
273
|
+
def to_local_type(id, base_ty)
|
|
107
274
|
Type::LocalArray.new(id, base_ty)
|
|
108
275
|
end
|
|
109
276
|
|
|
@@ -171,6 +338,11 @@ module TypeProf
|
|
|
171
338
|
@lead_tys.inject(@rest_ty) {|ty1, ty2| ty1.union(ty2) } #.union(Type.nil) # is this needed?
|
|
172
339
|
end
|
|
173
340
|
|
|
341
|
+
def squash_or_any
|
|
342
|
+
ty = squash
|
|
343
|
+
ty == Type.bot ? Type.any : ty
|
|
344
|
+
end
|
|
345
|
+
|
|
174
346
|
def [](idx)
|
|
175
347
|
if idx >= 0
|
|
176
348
|
if idx < @lead_tys.size
|
|
@@ -365,18 +537,11 @@ module TypeProf
|
|
|
365
537
|
@elems.screen_name(scratch)
|
|
366
538
|
end
|
|
367
539
|
|
|
368
|
-
def globalize(env, visited, depth)
|
|
369
|
-
return Type.any if depth <= 0
|
|
370
|
-
elems = @elems.globalize(env, visited, depth - 1)
|
|
371
|
-
base_ty = @base_type.globalize(env, visited, depth - 1)
|
|
372
|
-
Hash.new(elems, base_ty)
|
|
373
|
-
end
|
|
374
|
-
|
|
375
540
|
def localize(env, alloc_site, depth)
|
|
376
541
|
return env, Type.any if depth <= 0
|
|
377
542
|
alloc_site = alloc_site.add_id(:hash)
|
|
378
543
|
env, elems = @elems.localize(env, alloc_site, depth - 1)
|
|
379
|
-
env.
|
|
544
|
+
env.deploy_type(LocalHash, alloc_site, elems, @base_type)
|
|
380
545
|
end
|
|
381
546
|
|
|
382
547
|
def limit_size(limit)
|
|
@@ -396,6 +561,7 @@ module TypeProf
|
|
|
396
561
|
other.types.each do |ty2|
|
|
397
562
|
return true if consistent?(ty2, subst)
|
|
398
563
|
end
|
|
564
|
+
return false
|
|
399
565
|
when Type::Hash
|
|
400
566
|
@base_type.consistent?(other.base_type, subst) && @elems.consistent?(other.elems, subst)
|
|
401
567
|
else
|
|
@@ -427,8 +593,7 @@ module TypeProf
|
|
|
427
593
|
|
|
428
594
|
attr_reader :map_tys
|
|
429
595
|
|
|
430
|
-
def to_local_type(id)
|
|
431
|
-
base_ty = Type::Instance.new(Type::Builtin[:hash])
|
|
596
|
+
def to_local_type(id, base_ty)
|
|
432
597
|
Type::LocalHash.new(id, base_ty)
|
|
433
598
|
end
|
|
434
599
|
|
|
@@ -579,6 +744,22 @@ module TypeProf
|
|
|
579
744
|
|
|
580
745
|
Elements.new(map_tys)
|
|
581
746
|
end
|
|
747
|
+
|
|
748
|
+
def to_keywords
|
|
749
|
+
kw_tys = {}
|
|
750
|
+
@map_tys.each do |key_ty, val_ty|
|
|
751
|
+
if key_ty.is_a?(Type::Symbol)
|
|
752
|
+
kw_tys[key_ty.sym] = val_ty
|
|
753
|
+
else
|
|
754
|
+
all_val_ty = Type.bot
|
|
755
|
+
@map_tys.each do |_key_ty, val_ty|
|
|
756
|
+
all_val_ty = all_val_ty.union(val_ty)
|
|
757
|
+
end
|
|
758
|
+
return { nil => all_val_ty }
|
|
759
|
+
end
|
|
760
|
+
end
|
|
761
|
+
kw_tys
|
|
762
|
+
end
|
|
582
763
|
end
|
|
583
764
|
end
|
|
584
765
|
|
data/lib/typeprof/export.rb
CHANGED
|
@@ -25,19 +25,26 @@ module TypeProf
|
|
|
25
25
|
ntrace
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
def show_message(terminated, output)
|
|
29
|
+
if terminated
|
|
30
|
+
output.puts "# CAUTION: Type profiling was terminated prematurely because of the limitation"
|
|
31
|
+
output.puts
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
28
35
|
def show_error(errors, backward_edge, output)
|
|
29
36
|
return if errors.empty?
|
|
30
37
|
return unless Config.options[:show_errors]
|
|
31
38
|
|
|
32
39
|
output.puts "# Errors"
|
|
33
40
|
errors.each do |ep, msg|
|
|
34
|
-
if ENV["
|
|
41
|
+
if ENV["TP_DETAIL"]
|
|
35
42
|
backtrace = filter_backtrace(generate_analysis_trace(ep, {}, backward_edge))
|
|
36
43
|
else
|
|
37
44
|
backtrace = [ep]
|
|
38
45
|
end
|
|
39
46
|
loc, *backtrace = backtrace.map do |ep|
|
|
40
|
-
ep
|
|
47
|
+
ep&.source_location
|
|
41
48
|
end
|
|
42
49
|
output.puts "#{ loc }: #{ msg }"
|
|
43
50
|
backtrace.each do |loc|
|
|
@@ -57,13 +64,15 @@ module TypeProf
|
|
|
57
64
|
output.puts
|
|
58
65
|
end
|
|
59
66
|
|
|
60
|
-
def show_gvars(scratch,
|
|
67
|
+
def show_gvars(scratch, gvars, output)
|
|
61
68
|
# A signature for global variables is not supported in RBS
|
|
62
|
-
return if
|
|
69
|
+
return if gvars.dump.empty?
|
|
63
70
|
|
|
64
71
|
output.puts "# Global variables"
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
gvars.dump.each do |gvar_name, entry|
|
|
73
|
+
next if entry.type == Type.bot
|
|
74
|
+
s = entry.rbs_declared ? "#" : ""
|
|
75
|
+
output.puts s + "#{ gvar_name } : #{ entry.type.screen_name(scratch) }"
|
|
67
76
|
end
|
|
68
77
|
output.puts
|
|
69
78
|
end
|
|
@@ -72,122 +81,148 @@ module TypeProf
|
|
|
72
81
|
class RubySignatureExporter
|
|
73
82
|
def initialize(
|
|
74
83
|
scratch,
|
|
75
|
-
class_defs, iseq_method_to_ctxs
|
|
84
|
+
class_defs, iseq_method_to_ctxs
|
|
76
85
|
)
|
|
77
86
|
@scratch = scratch
|
|
78
87
|
@class_defs = class_defs
|
|
79
88
|
@iseq_method_to_ctxs = iseq_method_to_ctxs
|
|
80
|
-
@sig_fargs = sig_fargs
|
|
81
|
-
@sig_ret = sig_ret
|
|
82
|
-
@yields = yields
|
|
83
89
|
end
|
|
84
90
|
|
|
85
|
-
def
|
|
86
|
-
|
|
91
|
+
def conv_class(namespace, class_def, inner_classes)
|
|
92
|
+
@scratch.namespace = namespace
|
|
87
93
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
included_mods = class_def.modules[false].filter_map do |visible, mod_def|
|
|
93
|
-
mod_def.name if visible
|
|
94
|
-
end
|
|
94
|
+
if class_def.superclass
|
|
95
|
+
omit = @class_defs[class_def.superclass].klass_obj == Type::Builtin[:obj] || class_def.klass_obj == Type::Builtin[:obj]
|
|
96
|
+
superclass = omit ? nil : @scratch.get_class_name(@class_defs[class_def.superclass].klass_obj)
|
|
97
|
+
end
|
|
95
98
|
|
|
96
|
-
|
|
97
|
-
iseq_methods = {}
|
|
98
|
-
attr_methods = {}
|
|
99
|
-
class_def.methods.each do |(singleton, mid), mdefs|
|
|
100
|
-
mdefs.each do |mdef|
|
|
101
|
-
case mdef
|
|
102
|
-
when ISeqMethodDef
|
|
103
|
-
ctxs = @iseq_method_to_ctxs[mdef]
|
|
104
|
-
next unless ctxs
|
|
99
|
+
@scratch.namespace = class_def.name
|
|
105
100
|
|
|
106
|
-
|
|
107
|
-
|
|
101
|
+
consts = {}
|
|
102
|
+
class_def.consts.each do |name, (ty, absolute_path)|
|
|
103
|
+
next if ty.is_a?(Type::Class)
|
|
104
|
+
next if !absolute_path || Config.check_dir_filter(absolute_path) == :exclude
|
|
105
|
+
consts[name] = ty.screen_name(@scratch)
|
|
106
|
+
end
|
|
108
107
|
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
included_mods = class_def.modules[false].filter_map do |mod_def, absolute_paths|
|
|
109
|
+
next if absolute_paths.all? {|path| !path || Config.check_dir_filter(path) == :exclude }
|
|
110
|
+
Type::Instance.new(mod_def.klass_obj).screen_name(@scratch)
|
|
111
|
+
end
|
|
111
112
|
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
extended_mods = class_def.modules[true].filter_map do |mod_def, absolute_paths|
|
|
114
|
+
next if absolute_paths.all? {|path| !path || Config.check_dir_filter(path) == :exclude }
|
|
115
|
+
Type::Instance.new(mod_def.klass_obj).screen_name(@scratch)
|
|
116
|
+
end
|
|
114
117
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
118
|
+
explicit_methods = {}
|
|
119
|
+
iseq_methods = {}
|
|
120
|
+
attr_methods = {}
|
|
121
|
+
ivars = class_def.ivars.dump
|
|
122
|
+
cvars = class_def.cvars.dump
|
|
123
|
+
|
|
124
|
+
class_def.methods.each do |(singleton, mid), mdefs|
|
|
125
|
+
mdefs.each do |mdef|
|
|
126
|
+
case mdef
|
|
127
|
+
when ISeqMethodDef
|
|
128
|
+
ctxs = @iseq_method_to_ctxs[mdef]
|
|
129
|
+
next unless ctxs
|
|
130
|
+
|
|
131
|
+
ctxs.each do |ctx|
|
|
132
|
+
next if mid != ctx.mid
|
|
133
|
+
next if Config.check_dir_filter(ctx.iseq.absolute_path) == :exclude
|
|
134
|
+
|
|
135
|
+
method_name = ctx.mid
|
|
136
|
+
method_name = "self.#{ method_name }" if singleton
|
|
137
|
+
|
|
138
|
+
iseq_methods[method_name] ||= []
|
|
139
|
+
iseq_methods[method_name] << @scratch.show_method_signature(ctx)
|
|
140
|
+
end
|
|
141
|
+
when AttrMethodDef
|
|
142
|
+
next if Config.check_dir_filter(mdef.absolute_path) == :exclude
|
|
143
|
+
mid = mid.to_s[0..-2].to_sym if mid.to_s.end_with?("=")
|
|
144
|
+
method_name = mid
|
|
145
|
+
method_name = "self.#{ mid }" if singleton
|
|
146
|
+
method_name = [method_name, :"@#{ mid }" != mdef.ivar]
|
|
147
|
+
if attr_methods[method_name]
|
|
148
|
+
if attr_methods[method_name][0] != mdef.kind
|
|
149
|
+
attr_methods[method_name][0] = :accessor
|
|
135
150
|
end
|
|
151
|
+
else
|
|
152
|
+
entry = ivars[[singleton, mdef.ivar]]
|
|
153
|
+
ty = entry ? entry.type : Type.any
|
|
154
|
+
attr_methods[method_name] = [mdef.kind, ty.screen_name(@scratch)]
|
|
155
|
+
end
|
|
156
|
+
when TypedMethodDef
|
|
157
|
+
if mdef.rbs_source
|
|
158
|
+
method_name, sigs = mdef.rbs_source
|
|
159
|
+
explicit_methods[method_name] = sigs
|
|
136
160
|
end
|
|
137
161
|
end
|
|
138
162
|
end
|
|
163
|
+
end
|
|
139
164
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
165
|
+
ivars = ivars.map do |(singleton, var), entry|
|
|
166
|
+
next if entry.absolute_paths.all? {|path| Config.check_dir_filter(path) == :exclude }
|
|
167
|
+
ty = entry.type
|
|
168
|
+
next unless var.to_s.start_with?("@")
|
|
169
|
+
var = "self.#{ var }" if singleton
|
|
170
|
+
next if attr_methods[[singleton ? "self.#{ var.to_s[1..] }" : var.to_s[1..].to_sym, false]]
|
|
171
|
+
[var, ty.screen_name(@scratch), entry.rbs_declared]
|
|
172
|
+
end.compact
|
|
146
173
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
174
|
+
cvars = cvars.map do |var, entry|
|
|
175
|
+
next if entry.absolute_paths.all? {|path| Config.check_dir_filter(path) == :exclude }
|
|
176
|
+
[var, entry.type.screen_name(@scratch), entry.rbs_declared]
|
|
177
|
+
end
|
|
150
178
|
|
|
151
|
-
|
|
179
|
+
if !class_def.absolute_path || Config.check_dir_filter(class_def.absolute_path) == :exclude
|
|
180
|
+
return nil if consts.empty? && included_mods.empty? && extended_mods.empty? && ivars.empty? && cvars.empty? && iseq_methods.empty? && attr_methods.empty? && inner_classes.empty?
|
|
181
|
+
end
|
|
152
182
|
|
|
153
|
-
|
|
154
|
-
first = false
|
|
183
|
+
@scratch.namespace = nil
|
|
155
184
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
185
|
+
ClassData.new(
|
|
186
|
+
kind: class_def.kind,
|
|
187
|
+
name: class_def.name,
|
|
188
|
+
superclass: superclass,
|
|
189
|
+
consts: consts,
|
|
190
|
+
included_mods: included_mods,
|
|
191
|
+
extended_mods: extended_mods,
|
|
192
|
+
ivars: ivars,
|
|
193
|
+
cvars: cvars,
|
|
194
|
+
attr_methods: attr_methods,
|
|
195
|
+
explicit_methods: explicit_methods,
|
|
196
|
+
iseq_methods: iseq_methods,
|
|
197
|
+
inner_classes: inner_classes,
|
|
198
|
+
)
|
|
199
|
+
end
|
|
160
200
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
end
|
|
171
|
-
attr_methods.each do |(method_name, hidden), (kind, ty)|
|
|
172
|
-
output.puts " attr_#{ kind } #{ method_name }#{ hidden ? "()" : "" } : #{ ty }"
|
|
173
|
-
end
|
|
174
|
-
explicit_methods.each do |method_name, sigs|
|
|
175
|
-
sigs = sigs.sort.join("\n" + "#" + " " * (method_name.size + 6) + "| ")
|
|
176
|
-
output.puts "# def #{ method_name } : #{ sigs }"
|
|
177
|
-
end
|
|
178
|
-
iseq_methods.each do |method_name, sigs|
|
|
179
|
-
sigs = sigs.sort.join("\n" + " " * (method_name.size + 7) + "| ")
|
|
180
|
-
output.puts " def #{ method_name } : #{ sigs }"
|
|
201
|
+
ClassData = Struct.new(:kind, :name, :superclass, :consts, :included_mods, :extended_mods, :ivars, :cvars, :attr_methods, :explicit_methods, :iseq_methods, :inner_classes, keyword_init: true)
|
|
202
|
+
|
|
203
|
+
def show(stat_eps, output)
|
|
204
|
+
# make the class hierarchy
|
|
205
|
+
root = {}
|
|
206
|
+
@class_defs.each_value do |class_def|
|
|
207
|
+
h = root
|
|
208
|
+
class_def.name.each do |name|
|
|
209
|
+
h = h[name] ||= {}
|
|
181
210
|
end
|
|
182
|
-
|
|
211
|
+
h[:class_def] = class_def
|
|
183
212
|
end
|
|
184
213
|
|
|
214
|
+
hierarchy = build_class_hierarchy([], root)
|
|
215
|
+
|
|
216
|
+
output.puts "# Classes" # and Modules
|
|
217
|
+
|
|
218
|
+
show_class_hierarchy(0, hierarchy, output, true)
|
|
219
|
+
|
|
185
220
|
if ENV["TP_STAT"]
|
|
186
|
-
output.puts "
|
|
187
|
-
output.puts "
|
|
188
|
-
output.puts "
|
|
189
|
-
output.puts " %d methods (in total)" % stat_methods.size
|
|
221
|
+
output.puts ""
|
|
222
|
+
output.puts "# TypeProf statistics:"
|
|
223
|
+
output.puts "# %d execution points" % stat_eps.size
|
|
190
224
|
end
|
|
225
|
+
|
|
191
226
|
if ENV["TP_COVERAGE"]
|
|
192
227
|
coverage = {}
|
|
193
228
|
stat_eps.each do |ep|
|
|
@@ -196,8 +231,78 @@ module TypeProf
|
|
|
196
231
|
(coverage[path] ||= [])[lineno] ||= 0
|
|
197
232
|
(coverage[path] ||= [])[lineno] += 1
|
|
198
233
|
end
|
|
199
|
-
File.binwrite("coverage.dump", Marshal.dump(coverage))
|
|
234
|
+
File.binwrite("typeprof-analysis-coverage.dump", Marshal.dump(coverage))
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def build_class_hierarchy(namespace, hierarchy)
|
|
239
|
+
hierarchy.map do |name, h|
|
|
240
|
+
class_def = h.delete(:class_def)
|
|
241
|
+
class_data = conv_class(namespace, class_def, build_class_hierarchy(namespace + [name], h))
|
|
242
|
+
class_data
|
|
243
|
+
end.compact
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def show_class_hierarchy(depth, hierarchy, output, first)
|
|
247
|
+
hierarchy.each do |class_data|
|
|
248
|
+
output.puts unless first
|
|
249
|
+
first = false
|
|
250
|
+
|
|
251
|
+
show_class_data(depth, class_data, output)
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def show_const(namespace, path)
|
|
256
|
+
return path.last.to_s if namespace == path
|
|
257
|
+
i = 0
|
|
258
|
+
i += 1 while namespace[i] && namespace[i] == path[i]
|
|
259
|
+
path[i..].join("::")
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def show_class_data(depth, class_data, output)
|
|
263
|
+
indent = " " * depth
|
|
264
|
+
name = class_data.name.last
|
|
265
|
+
superclass = " < " + class_data.superclass if class_data.superclass
|
|
266
|
+
output.puts indent + "#{ class_data.kind } #{ name }#{ superclass }"
|
|
267
|
+
first = true
|
|
268
|
+
class_data.consts.each do |name, ty|
|
|
269
|
+
output.puts indent + " #{ name } : #{ ty }"
|
|
270
|
+
first = false
|
|
271
|
+
end
|
|
272
|
+
class_data.included_mods.sort.each do |mod|
|
|
273
|
+
output.puts indent + " include #{ mod }"
|
|
274
|
+
first = false
|
|
275
|
+
end
|
|
276
|
+
class_data.extended_mods.sort.each do |mod|
|
|
277
|
+
output.puts indent + " extend #{ mod }"
|
|
278
|
+
first = false
|
|
279
|
+
end
|
|
280
|
+
class_data.ivars.each do |var, ty, rbs_declared|
|
|
281
|
+
s = rbs_declared ? "# " : " "
|
|
282
|
+
output.puts indent + s + "#{ var } : #{ ty }" unless var.start_with?("_")
|
|
283
|
+
first = false
|
|
284
|
+
end
|
|
285
|
+
class_data.cvars.each do |var, ty, rbs_declared|
|
|
286
|
+
s = rbs_declared ? "# " : " "
|
|
287
|
+
output.puts indent + s + "#{ var } : #{ ty }"
|
|
288
|
+
first = false
|
|
289
|
+
end
|
|
290
|
+
class_data.attr_methods.each do |(method_name, hidden), (kind, ty)|
|
|
291
|
+
output.puts indent + " attr_#{ kind } #{ method_name }#{ hidden ? "()" : "" } : #{ ty }"
|
|
292
|
+
first = false
|
|
293
|
+
end
|
|
294
|
+
class_data.explicit_methods.each do |method_name, sigs|
|
|
295
|
+
sigs = sigs.sort.join("\n" + indent + "#" + " " * (method_name.size + 6) + "| ")
|
|
296
|
+
output.puts indent + "# def #{ method_name } : #{ sigs }"
|
|
297
|
+
first = false
|
|
298
|
+
end
|
|
299
|
+
class_data.iseq_methods.each do |method_name, sigs|
|
|
300
|
+
sigs = sigs.sort.join("\n" + indent + " " * (method_name.size + 7) + "| ")
|
|
301
|
+
output.puts indent + " def #{ method_name } : #{ sigs }"
|
|
302
|
+
first = false
|
|
200
303
|
end
|
|
304
|
+
show_class_hierarchy(depth + 1, class_data.inner_classes, output, first)
|
|
305
|
+
output.puts indent + "end"
|
|
201
306
|
end
|
|
202
307
|
end
|
|
203
308
|
end
|