typeprof 0.5.3 → 0.8.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 -4
- data/doc/doc.ja.md +3 -4
- data/doc/doc.md +3 -4
- data/lib/typeprof/analyzer.rb +248 -166
- data/lib/typeprof/arguments.rb +12 -6
- data/lib/typeprof/builtin.rb +123 -23
- data/lib/typeprof/cli.rb +33 -34
- data/lib/typeprof/config.rb +6 -4
- data/lib/typeprof/container-type.rb +175 -112
- data/lib/typeprof/export.rb +23 -17
- data/lib/typeprof/import.rb +58 -53
- data/lib/typeprof/method.rb +59 -125
- data/lib/typeprof/type.rb +26 -14
- data/lib/typeprof/version.rb +1 -1
- data/smoke/alias.rb +1 -0
- data/smoke/any1.rb +1 -0
- data/smoke/any2.rb +1 -0
- data/smoke/arguments.rb +1 -0
- data/smoke/arguments2.rb +1 -0
- data/smoke/array-each.rb +1 -0
- data/smoke/array-each2.rb +1 -0
- data/smoke/array-each3.rb +2 -1
- data/smoke/array-ltlt.rb +1 -0
- data/smoke/array-ltlt2.rb +1 -0
- data/smoke/array-map.rb +1 -0
- data/smoke/array-map2.rb +1 -0
- data/smoke/array-map3.rb +1 -0
- data/smoke/array-mul.rb +1 -0
- data/smoke/array-plus1.rb +1 -0
- data/smoke/array-pop.rb +1 -0
- data/smoke/array-range-aref.rb +71 -0
- data/smoke/array-replace.rb +1 -0
- data/smoke/array-s-aref.rb +1 -0
- data/smoke/array1.rb +1 -0
- data/smoke/array10.rb +1 -0
- data/smoke/array11.rb +2 -1
- data/smoke/array12.rb +1 -0
- data/smoke/array13.rb +1 -0
- data/smoke/array14.rb +1 -0
- data/smoke/array2.rb +1 -0
- data/smoke/array4.rb +1 -0
- data/smoke/array5.rb +1 -0
- data/smoke/array6.rb +3 -2
- data/smoke/array7.rb +1 -0
- data/smoke/array8.rb +1 -1
- data/smoke/array9.rb +1 -0
- data/smoke/autoload.rb +14 -0
- data/smoke/backtrace.rb +1 -0
- data/smoke/block-ambiguous.rb +1 -0
- data/smoke/block-args1-rest.rb +1 -0
- data/smoke/block-args1.rb +1 -0
- data/smoke/block-args2-rest.rb +1 -0
- data/smoke/block-args2.rb +4 -3
- data/smoke/block-args3-rest.rb +1 -0
- data/smoke/block-args3.rb +5 -4
- data/smoke/block-blockarg.rb +2 -1
- data/smoke/block-kwarg.rb +1 -0
- data/smoke/block1.rb +1 -0
- data/smoke/block10.rb +1 -0
- data/smoke/block11.rb +1 -0
- data/smoke/block12.rb +1 -0
- data/smoke/block14.rb +1 -0
- data/smoke/block2.rb +1 -0
- data/smoke/block4.rb +1 -0
- data/smoke/block5.rb +2 -1
- data/smoke/block6.rb +1 -0
- data/smoke/block7.rb +1 -0
- data/smoke/block8.rb +1 -0
- data/smoke/block9.rb +1 -0
- data/smoke/blown.rb +1 -0
- data/smoke/break1.rb +1 -0
- data/smoke/break2.rb +1 -0
- data/smoke/case.rb +1 -0
- data/smoke/case2.rb +1 -0
- data/smoke/class_method3.rb +2 -0
- data/smoke/constant2.rb +2 -2
- data/smoke/constant3.rb +1 -0
- data/smoke/constant4.rb +1 -0
- data/smoke/context-sensitive1.rb +1 -0
- data/smoke/cvar.rb +1 -0
- data/smoke/define_method.rb +16 -0
- data/smoke/define_method2.rb +18 -0
- data/smoke/demo.rb +1 -0
- data/smoke/demo1.rb +1 -0
- data/smoke/demo10.rb +1 -0
- data/smoke/demo11.rb +1 -0
- data/smoke/demo2.rb +1 -0
- data/smoke/demo3.rb +1 -0
- data/smoke/demo5.rb +1 -1
- data/smoke/demo7.rb +1 -0
- data/smoke/demo8.rb +1 -0
- data/smoke/demo9.rb +2 -1
- data/smoke/dummy-execution1.rb +1 -0
- data/smoke/ensure1.rb +1 -0
- data/smoke/enumerator.rb +1 -0
- data/smoke/expandarray1.rb +1 -0
- data/smoke/expandarray2.rb +1 -0
- data/smoke/flow1.rb +1 -0
- data/smoke/flow2.rb +1 -0
- data/smoke/flow3.rb +1 -0
- data/smoke/flow5.rb +1 -0
- data/smoke/flow6.rb +1 -0
- data/smoke/flow7.rb +1 -0
- data/smoke/flow8.rb +1 -0
- data/smoke/freeze.rb +1 -0
- data/smoke/function.rb +1 -0
- data/smoke/gvar.rb +1 -0
- data/smoke/gvar2.rb +1 -0
- data/smoke/hash-fetch.rb +1 -0
- data/smoke/hash-merge-bang.rb +1 -0
- data/smoke/hash1.rb +3 -1
- data/smoke/hash2.rb +1 -0
- data/smoke/hash3.rb +1 -0
- data/smoke/hash4.rb +2 -1
- data/smoke/inheritance2.rb +2 -2
- data/smoke/initialize.rb +1 -0
- data/smoke/int_times.rb +1 -0
- data/smoke/integer.rb +1 -0
- data/smoke/ivar.rb +1 -0
- data/smoke/ivar2.rb +1 -1
- data/smoke/kernel-class.rb +2 -1
- data/smoke/keyword1.rb +1 -0
- data/smoke/keyword2.rb +1 -0
- data/smoke/keyword3.rb +1 -0
- data/smoke/keyword4.rb +1 -0
- data/smoke/keyword5.rb +1 -0
- data/smoke/kwrest.rb +12 -0
- data/smoke/kwrest.rbs +3 -0
- data/smoke/kwsplat1.rb +2 -1
- data/smoke/kwsplat2.rb +1 -0
- data/smoke/manual-rbs.rb +1 -0
- data/smoke/manual-rbs2.rb +1 -0
- data/smoke/masgn1.rb +1 -0
- data/smoke/masgn2.rb +1 -0
- data/smoke/masgn3.rb +1 -0
- data/smoke/method_in_branch.rb +1 -0
- data/smoke/method_missing.rb +28 -0
- data/smoke/multiple-superclass.rb +1 -1
- data/smoke/next1.rb +1 -0
- data/smoke/next2.rb +1 -0
- data/smoke/object-send1.rb +1 -0
- data/smoke/once.rb +1 -0
- data/smoke/optional1.rb +1 -0
- data/smoke/optional2.rb +1 -0
- data/smoke/optional3.rb +1 -0
- data/smoke/parameterizedd-self.rb +3 -2
- data/smoke/parameterizedd-self2.rb +15 -0
- data/smoke/pathname1.rb +1 -0
- data/smoke/pathname2.rb +1 -0
- data/smoke/pattern-match1.rb +1 -0
- data/smoke/pattern-match2.rb +1 -0
- data/smoke/proc.rb +1 -0
- data/smoke/proc2.rb +1 -0
- data/smoke/proc3.rb +1 -0
- data/smoke/proc4.rb +1 -0
- data/smoke/range.rb +1 -0
- data/smoke/rbs-alias.rb +1 -0
- data/smoke/rbs-attr.rb +3 -2
- data/smoke/rbs-attr2.rb +11 -0
- data/smoke/rbs-attr2.rbs +3 -0
- data/smoke/rbs-extend.rb +1 -0
- data/smoke/rbs-interface.rb +1 -0
- data/smoke/rbs-proc1.rb +1 -0
- data/smoke/rbs-proc2.rb +1 -0
- data/smoke/rbs-proc3.rb +1 -0
- data/smoke/rbs-record.rb +1 -0
- data/smoke/rbs-tyvar.rb +1 -0
- data/smoke/rbs-tyvar2.rb +1 -0
- data/smoke/rbs-tyvar3.rb +1 -0
- data/smoke/rbs-tyvar5.rb +1 -0
- data/smoke/rbs-tyvar6.rb +18 -0
- data/smoke/rbs-tyvar6.rbs +12 -0
- data/smoke/rbs-tyvar7.rb +12 -0
- data/smoke/rbs-tyvar7.rbs +7 -0
- data/smoke/rbs-vars.rb +1 -2
- data/smoke/redo1.rb +1 -0
- data/smoke/redo2.rb +1 -0
- data/smoke/req-keyword.rb +1 -0
- data/smoke/rescue1.rb +1 -0
- data/smoke/rescue2.rb +1 -0
- data/smoke/respond_to.rb +1 -0
- data/smoke/rest-farg.rb +1 -0
- data/smoke/rest1.rb +1 -0
- data/smoke/rest2.rb +1 -0
- data/smoke/rest3.rb +1 -0
- data/smoke/rest4.rb +1 -0
- data/smoke/rest5.rb +1 -0
- data/smoke/rest6.rb +1 -0
- data/smoke/retry1.rb +1 -0
- data/smoke/return.rb +1 -0
- data/smoke/step.rb +1 -0
- data/smoke/string-split.rb +1 -0
- data/smoke/struct.rb +2 -2
- data/smoke/struct2.rb +1 -0
- data/smoke/super1.rb +1 -0
- data/smoke/super4.rb +43 -0
- data/smoke/super5.rb +36 -0
- data/smoke/svar1.rb +1 -0
- data/smoke/symbol-proc.rb +1 -0
- data/smoke/tap1.rb +1 -0
- data/smoke/toplevel.rb +1 -0
- data/smoke/two-map.rb +1 -0
- data/smoke/type_var.rb +1 -0
- data/smoke/typed_method.rb +1 -0
- data/smoke/uninitialize-var.rb +1 -0
- data/smoke/union-recv.rb +2 -2
- metadata +18 -2
data/lib/typeprof/arguments.rb
CHANGED
|
@@ -10,6 +10,10 @@ module TypeProf
|
|
|
10
10
|
raise unless blk_ty
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
def for_method_missing(mid)
|
|
14
|
+
ActualArguments.new([mid] + @lead_tys, @rest_ty, @kw_tys, @blk_ty)
|
|
15
|
+
end
|
|
16
|
+
|
|
13
17
|
attr_reader :lead_tys, :rest_ty, :kw_tys, :blk_ty
|
|
14
18
|
|
|
15
19
|
def globalize(caller_env, visited, depth)
|
|
@@ -148,12 +152,14 @@ module TypeProf
|
|
|
148
152
|
lead_tys = ty.elems.lead_tys
|
|
149
153
|
rest_ty = ty.elems.rest_ty
|
|
150
154
|
when Type::Union
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
if ty.elems
|
|
156
|
+
other_elems = {}
|
|
157
|
+
ty.elems.each do |(container_kind, base_type), elems|
|
|
158
|
+
if container_kind == Type::Array
|
|
159
|
+
rest_ty = rest_ty ? rest_ty.union(elems.squash) : elems.squash
|
|
160
|
+
else
|
|
161
|
+
other_elems[[container_kind, base_type]] = elems
|
|
162
|
+
end
|
|
157
163
|
end
|
|
158
164
|
end
|
|
159
165
|
lead_tys = [Type::Union.new(ty.types, other_elems)]
|
data/lib/typeprof/builtin.rb
CHANGED
|
@@ -33,10 +33,10 @@ module TypeProf
|
|
|
33
33
|
h2 = aargs.lead_tys[1]
|
|
34
34
|
elems = nil
|
|
35
35
|
h1.each_child do |h1|
|
|
36
|
-
if h1.is_a?(Type::
|
|
36
|
+
if h1.is_a?(Type::Local) && h1.kind == Type::Hash
|
|
37
37
|
h1_elems = scratch.get_container_elem_types(env, ep, h1.id)
|
|
38
38
|
h2.each_child do |h2|
|
|
39
|
-
if h2.is_a?(Type::
|
|
39
|
+
if h2.is_a?(Type::Local) && h2.kind == Type::Hash
|
|
40
40
|
h2_elems = scratch.get_container_elem_types(env, ep, h2.id)
|
|
41
41
|
elems0 = h1_elems.union(h2_elems)
|
|
42
42
|
if elems
|
|
@@ -100,7 +100,8 @@ module TypeProf
|
|
|
100
100
|
raise unless aargs.lead_tys.size != 0
|
|
101
101
|
sym = get_sym("respond_to?", aargs.lead_tys[0], ep, scratch)
|
|
102
102
|
if sym
|
|
103
|
-
|
|
103
|
+
klass, singleton = recv.method_dispatch_info
|
|
104
|
+
if scratch.get_method(klass, singleton, sym)
|
|
104
105
|
true_val = Type::Instance.new(Type::Builtin[:true])
|
|
105
106
|
ctn[true_val, ep, env]
|
|
106
107
|
else
|
|
@@ -142,8 +143,8 @@ module TypeProf
|
|
|
142
143
|
|
|
143
144
|
def object_instance_eval(recv, mid, aargs, ep, env, scratch, &ctn)
|
|
144
145
|
if aargs.lead_tys.size >= 1
|
|
145
|
-
scratch.warn(ep, "instance_eval with arguments
|
|
146
|
-
ctn[
|
|
146
|
+
scratch.warn(ep, "instance_eval with arguments is ignored")
|
|
147
|
+
ctn[Type.any, ep, env]
|
|
147
148
|
return
|
|
148
149
|
end
|
|
149
150
|
naargs = ActualArguments.new([recv], nil, {}, Type.nil)
|
|
@@ -153,20 +154,30 @@ module TypeProf
|
|
|
153
154
|
end
|
|
154
155
|
|
|
155
156
|
def module_include(recv, mid, aargs, ep, env, scratch, &ctn)
|
|
157
|
+
if aargs.lead_tys.size != 1
|
|
158
|
+
scratch.warn(ep, "Module#include without an argument is ignored")
|
|
159
|
+
ctn[Type.any, ep, env]
|
|
160
|
+
return
|
|
161
|
+
end
|
|
156
162
|
arg = aargs.lead_tys[0]
|
|
157
163
|
arg.each_child do |arg|
|
|
158
164
|
if arg.is_a?(Type::Class)
|
|
159
|
-
scratch.include_module(recv, arg, false, ep.ctx.iseq.absolute_path)
|
|
165
|
+
scratch.include_module(recv, arg, nil, false, ep.ctx.iseq.absolute_path)
|
|
160
166
|
end
|
|
161
167
|
end
|
|
162
168
|
ctn[recv, ep, env]
|
|
163
169
|
end
|
|
164
170
|
|
|
165
171
|
def module_extend(recv, mid, aargs, ep, env, scratch, &ctn)
|
|
172
|
+
if aargs.lead_tys.size != 1
|
|
173
|
+
scratch.warn(ep, "Module#extend without an argument is ignored")
|
|
174
|
+
ctn[Type.any, ep, env]
|
|
175
|
+
return
|
|
176
|
+
end
|
|
166
177
|
arg = aargs.lead_tys[0]
|
|
167
178
|
arg.each_child do |arg|
|
|
168
179
|
if arg.is_a?(Type::Class)
|
|
169
|
-
scratch.include_module(recv, arg, true, ep.ctx.iseq.absolute_path)
|
|
180
|
+
scratch.include_module(recv, arg, nil, true, ep.ctx.iseq.absolute_path)
|
|
170
181
|
end
|
|
171
182
|
end
|
|
172
183
|
ctn[recv, ep, env]
|
|
@@ -178,7 +189,7 @@ module TypeProf
|
|
|
178
189
|
else
|
|
179
190
|
aargs.lead_tys.each do |aarg|
|
|
180
191
|
sym = get_sym("module_function", aarg, ep, scratch) or next
|
|
181
|
-
meths =
|
|
192
|
+
meths = scratch.get_method(recv, false, sym)
|
|
182
193
|
meths.each do |mdef|
|
|
183
194
|
scratch.add_method(recv, sym, true, mdef)
|
|
184
195
|
end
|
|
@@ -187,6 +198,67 @@ module TypeProf
|
|
|
187
198
|
end
|
|
188
199
|
end
|
|
189
200
|
|
|
201
|
+
def module_public(recv, mid, aargs, ep, env, scratch, &ctn)
|
|
202
|
+
if aargs.lead_tys.empty?
|
|
203
|
+
ctn[recv, ep, env.method_public_set(true)]
|
|
204
|
+
else
|
|
205
|
+
aargs.lead_tys.each do |aarg|
|
|
206
|
+
sym = get_sym("public", aarg, ep, scratch) or next
|
|
207
|
+
meths = scratch.get_method(recv, false, sym)
|
|
208
|
+
next unless meths
|
|
209
|
+
meths.each do |mdef|
|
|
210
|
+
mdef.pub_meth = true
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
ctn[recv, ep, env]
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def module_private(recv, mid, aargs, ep, env, scratch, &ctn)
|
|
218
|
+
if aargs.lead_tys.empty?
|
|
219
|
+
ctn[recv, ep, env.method_public_set(false)]
|
|
220
|
+
else
|
|
221
|
+
aargs.lead_tys.each do |aarg|
|
|
222
|
+
sym = get_sym("private", aarg, ep, scratch) or next
|
|
223
|
+
meths = scratch.get_method(recv, false, sym)
|
|
224
|
+
next unless meths
|
|
225
|
+
meths.each do |mdef|
|
|
226
|
+
mdef.pub_meth = false
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
ctn[recv, ep, env]
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def module_define_method(recv, mid, aargs, ep, env, scratch, &ctn)
|
|
234
|
+
if aargs.lead_tys.size != 1
|
|
235
|
+
scratch.warn(ep, "Module#define with #{ aargs.lead_tys.size } argument is ignored")
|
|
236
|
+
ctn[Type.any, ep, env]
|
|
237
|
+
return
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
mid, = aargs.lead_tys
|
|
241
|
+
if mid.is_a?(Type::Symbol)
|
|
242
|
+
mid = mid.sym
|
|
243
|
+
aargs.blk_ty.each_child do |blk_ty|
|
|
244
|
+
if blk_ty.is_a?(Type::Proc)
|
|
245
|
+
blk = blk_ty.block_body
|
|
246
|
+
case blk
|
|
247
|
+
when ISeqBlock
|
|
248
|
+
scratch.do_define_iseq_method(ep, env, mid, blk.iseq, ep)
|
|
249
|
+
else
|
|
250
|
+
# XXX: what to do?
|
|
251
|
+
end
|
|
252
|
+
else
|
|
253
|
+
# XXX: what to do?
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
else
|
|
257
|
+
# XXX: what to do?
|
|
258
|
+
end
|
|
259
|
+
ctn[Type.any, ep, env]
|
|
260
|
+
end
|
|
261
|
+
|
|
190
262
|
def module_attr_accessor(recv, mid, aargs, ep, env, scratch, &ctn)
|
|
191
263
|
aargs.lead_tys.each do |aarg|
|
|
192
264
|
sym = get_sym("attr_accessor", aarg, ep, scratch) or next
|
|
@@ -222,21 +294,16 @@ module TypeProf
|
|
|
222
294
|
end
|
|
223
295
|
|
|
224
296
|
def array_aref(recv, mid, aargs, ep, env, scratch, &ctn)
|
|
225
|
-
return ctn[Type.any, ep, env] unless recv.is_a?(Type::
|
|
297
|
+
return ctn[Type.any, ep, env] unless recv.is_a?(Type::Local) && recv.kind == Type::Array
|
|
226
298
|
|
|
227
299
|
case aargs.lead_tys.size
|
|
228
300
|
when 1
|
|
229
301
|
idx = aargs.lead_tys.first
|
|
230
302
|
if idx.is_a?(Type::Literal)
|
|
231
303
|
idx = idx.lit
|
|
232
|
-
if idx.is_a?(Range)
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
ret_ty = Type::Array.new(Type::Array::Elements.new([], ty), base_ty)
|
|
236
|
-
ctn[ret_ty, ep, env]
|
|
237
|
-
return
|
|
238
|
-
end
|
|
239
|
-
idx = nil if !idx.is_a?(Integer)
|
|
304
|
+
idx = nil if !idx.is_a?(Integer) && !idx.is_a?(Range)
|
|
305
|
+
elsif idx == Type::Instance.new(Type::Builtin[:range])
|
|
306
|
+
idx = (nil..nil)
|
|
240
307
|
else
|
|
241
308
|
idx = nil
|
|
242
309
|
end
|
|
@@ -253,7 +320,7 @@ module TypeProf
|
|
|
253
320
|
end
|
|
254
321
|
|
|
255
322
|
def array_aset(recv, mid, aargs, ep, env, scratch, &ctn)
|
|
256
|
-
return ctn[Type.any, ep, env] unless recv.is_a?(Type::
|
|
323
|
+
return ctn[Type.any, ep, env] unless recv.is_a?(Type::Local) && recv.kind == Type::Array
|
|
257
324
|
|
|
258
325
|
if aargs.lead_tys.size != 2
|
|
259
326
|
#raise NotImplementedError # XXX
|
|
@@ -278,7 +345,7 @@ module TypeProf
|
|
|
278
345
|
end
|
|
279
346
|
|
|
280
347
|
def array_pop(recv, mid, aargs, ep, env, scratch, &ctn)
|
|
281
|
-
return ctn[Type.any, ep, env] unless recv.is_a?(Type::
|
|
348
|
+
return ctn[Type.any, ep, env] unless recv.is_a?(Type::Local) && recv.kind == Type::Array
|
|
282
349
|
|
|
283
350
|
if aargs.lead_tys.size != 0
|
|
284
351
|
ctn[Type.any, ep, env]
|
|
@@ -290,7 +357,7 @@ module TypeProf
|
|
|
290
357
|
end
|
|
291
358
|
|
|
292
359
|
def hash_aref(recv, mid, aargs, ep, env, scratch, &ctn)
|
|
293
|
-
return ctn[Type.any, ep, env] unless recv.is_a?(Type::
|
|
360
|
+
return ctn[Type.any, ep, env] unless recv.is_a?(Type::Local) && recv.kind == Type::Hash
|
|
294
361
|
|
|
295
362
|
if aargs.lead_tys.size != 1
|
|
296
363
|
ctn[Type.any, ep, env]
|
|
@@ -298,7 +365,7 @@ module TypeProf
|
|
|
298
365
|
end
|
|
299
366
|
idx = aargs.lead_tys.first
|
|
300
367
|
recv.each_child do |recv|
|
|
301
|
-
if recv.is_a?(Type::
|
|
368
|
+
if recv.is_a?(Type::Local) && recv.kind == Type::Hash
|
|
302
369
|
ty = scratch.get_hash_elem_type(env, ep, recv.id, idx)
|
|
303
370
|
else
|
|
304
371
|
ty = Type.any
|
|
@@ -308,7 +375,7 @@ module TypeProf
|
|
|
308
375
|
end
|
|
309
376
|
|
|
310
377
|
def hash_aset(recv, mid, aargs, ep, env, scratch, &ctn)
|
|
311
|
-
return ctn[Type.any, ep, env] unless recv.is_a?(Type::
|
|
378
|
+
return ctn[Type.any, ep, env] unless recv.is_a?(Type::Local) && recv.kind == Type::Hash
|
|
312
379
|
|
|
313
380
|
raise NotImplementedError if aargs.lead_tys.size != 2
|
|
314
381
|
|
|
@@ -316,7 +383,7 @@ module TypeProf
|
|
|
316
383
|
idx = scratch.globalize_type(idx, env, ep)
|
|
317
384
|
ty = aargs.lead_tys.last
|
|
318
385
|
|
|
319
|
-
unless recv.is_a?(Type::
|
|
386
|
+
unless recv.is_a?(Type::Local) && recv.kind == Type::Hash
|
|
320
387
|
# to ignore: class OptionMap < Hash
|
|
321
388
|
return ctn[ty, ep, env]
|
|
322
389
|
end
|
|
@@ -473,6 +540,31 @@ module TypeProf
|
|
|
473
540
|
ctn[result, ep, env]
|
|
474
541
|
end
|
|
475
542
|
|
|
543
|
+
def kernel_autoload(recv, mid, aargs, ep, env, scratch, &ctn)
|
|
544
|
+
raise NotImplementedError if aargs.lead_tys.size != 2
|
|
545
|
+
feature = aargs.lead_tys[1]
|
|
546
|
+
if feature.is_a?(Type::Literal)
|
|
547
|
+
feature = feature.lit
|
|
548
|
+
|
|
549
|
+
action, arg = Builtin.file_require(feature, scratch)
|
|
550
|
+
case action
|
|
551
|
+
when :do
|
|
552
|
+
Builtin.file_load(arg, ep, env, scratch, &ctn)
|
|
553
|
+
when :done
|
|
554
|
+
when :error
|
|
555
|
+
scratch.warn(ep, arg)
|
|
556
|
+
end
|
|
557
|
+
ctn[Type.nil, ep, env]
|
|
558
|
+
else
|
|
559
|
+
scratch.warn(ep, "autoload target cannot be identified statically")
|
|
560
|
+
ctn[Type.nil, ep, env]
|
|
561
|
+
end
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
def module_autoload(recv, mid, aargs, ep, env, scratch, &ctn)
|
|
565
|
+
kernel_autoload(recv, mid, aargs, ep, env, scratch, &ctn)
|
|
566
|
+
end
|
|
567
|
+
|
|
476
568
|
def kernel_Array(recv, mid, aargs, ep, env, scratch, &ctn)
|
|
477
569
|
raise NotImplementedError if aargs.lead_tys.size != 1
|
|
478
570
|
ty = aargs.lead_tys.first
|
|
@@ -546,6 +638,9 @@ module TypeProf
|
|
|
546
638
|
scratch.set_custom_method(klass_module, :include, Builtin.method(:module_include))
|
|
547
639
|
scratch.set_custom_method(klass_module, :extend, Builtin.method(:module_extend))
|
|
548
640
|
scratch.set_custom_method(klass_module, :module_function, Builtin.method(:module_module_function))
|
|
641
|
+
scratch.set_custom_method(klass_module, :public, Builtin.method(:module_public))
|
|
642
|
+
scratch.set_custom_method(klass_module, :private, Builtin.method(:module_private))
|
|
643
|
+
scratch.set_custom_method(klass_module, :define_method, Builtin.method(:module_define_method))
|
|
549
644
|
|
|
550
645
|
scratch.set_custom_method(klass_proc, :[], Builtin.method(:proc_call))
|
|
551
646
|
scratch.set_custom_method(klass_proc, :call, Builtin.method(:proc_call))
|
|
@@ -563,6 +658,11 @@ module TypeProf
|
|
|
563
658
|
scratch.set_custom_method(klass_obj, :require, Builtin.method(:kernel_require))
|
|
564
659
|
scratch.set_custom_method(klass_obj, :require_relative, Builtin.method(:kernel_require_relative))
|
|
565
660
|
scratch.set_custom_method(klass_obj, :Array, Builtin.method(:kernel_Array))
|
|
661
|
+
scratch.set_custom_method(klass_obj, :autoload, Builtin.method(:kernel_autoload))
|
|
662
|
+
scratch.set_custom_method(klass_module, :autoload, Builtin.method(:module_autoload))
|
|
663
|
+
|
|
664
|
+
# remove BasicObject#method_missing
|
|
665
|
+
scratch.set_method(klass_basic_obj, :method_missing, false, nil)
|
|
566
666
|
|
|
567
667
|
# ENV: Hash[String, String]
|
|
568
668
|
str_ty = Type::Instance.new(Type::Builtin[:str])
|
data/lib/typeprof/cli.rb
CHANGED
|
@@ -7,60 +7,59 @@ module TypeProf
|
|
|
7
7
|
def parse(argv)
|
|
8
8
|
opt = OptionParser.new
|
|
9
9
|
|
|
10
|
+
opt.banner = "Usage: #{ opt.program_name } [options] files..."
|
|
11
|
+
|
|
10
12
|
output = nil
|
|
11
13
|
|
|
12
14
|
# Verbose level:
|
|
13
|
-
# * 0:
|
|
14
|
-
# * 1:
|
|
15
|
-
# * 2:
|
|
15
|
+
# * 0: none
|
|
16
|
+
# * 1: default level
|
|
17
|
+
# * 2: debugging level
|
|
16
18
|
verbose = 1
|
|
17
19
|
|
|
18
20
|
options = {}
|
|
19
21
|
dir_filter = nil
|
|
20
22
|
gem_rbs_features = []
|
|
21
|
-
|
|
23
|
+
show_version = false
|
|
22
24
|
max_sec = max_iter = nil
|
|
23
25
|
|
|
24
|
-
opt.
|
|
25
|
-
opt.
|
|
26
|
-
opt.on("-
|
|
27
|
-
opt.on("--
|
|
28
|
-
opt.on("-
|
|
29
|
-
opt.on("
|
|
30
|
-
opt.on("-
|
|
31
|
-
opt.on("
|
|
32
|
-
opt.on("--max-iteration TIMES", Integer) {|v| max_iter = v }
|
|
26
|
+
opt.separator ""
|
|
27
|
+
opt.separator "Options:"
|
|
28
|
+
opt.on("-o OUTFILE", "Output to OUTFILE instead of stdout") {|v| output = v }
|
|
29
|
+
opt.on("-q", "--quiet", "Do not display progress indicator") { options[:show_indicator] = false }
|
|
30
|
+
opt.on("-v", "--verbose", "Alias to --show-errors") { options[:show_errors] = true }
|
|
31
|
+
opt.on("--version", "Display typeprof version") { show_version = true }
|
|
32
|
+
opt.on("-I DIR", "Add DIR to the load/require path") {|v| $LOAD_PATH << v }
|
|
33
|
+
opt.on("-r FEATURE", "Require RBS of the FEATURE gem") {|v| gem_rbs_features << v }
|
|
33
34
|
|
|
34
|
-
opt.
|
|
35
|
+
opt.separator ""
|
|
36
|
+
opt.separator "Analysis output options:"
|
|
37
|
+
opt.on("--include-dir DIR", "Include the analysis result of .rb file in DIR") do |dir|
|
|
35
38
|
# When `--include-dir` option is specified as the first directory option,
|
|
36
39
|
# typeprof will exclude any files by default unless a file path matches the explicit option
|
|
37
40
|
dir_filter ||= [[:exclude]]
|
|
38
41
|
dir_filter << [:include, File.expand_path(dir)]
|
|
39
42
|
end
|
|
40
|
-
opt.on("--exclude-dir DIR") do |dir|
|
|
43
|
+
opt.on("--exclude-dir DIR", "Exclude the analysis result of .rb file in DIR") do |dir|
|
|
41
44
|
# When `--exclude-dir` option is specified as the first directory option,
|
|
42
45
|
# typeprof will include any files by default, except Ruby's install directory and Gem directories
|
|
43
46
|
dir_filter ||= ConfigData::DEFAULT_DIR_FILTER
|
|
44
47
|
dir_filter << [:exclude, File.expand_path(dir)]
|
|
45
48
|
end
|
|
49
|
+
opt.on("--[no-]show-errors", "Display possible errors found during the analysis") {|v| options[:show_errors] = v }
|
|
50
|
+
opt.on("--[no-]show-untyped", "Display \"Foo | untyped\" instead of \"Foo\"") {|v| options[:show_untyped] = v }
|
|
46
51
|
|
|
47
|
-
opt.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
when "stackprof"
|
|
59
|
-
options[:stackprof] = args ? args.to_sym : :cpu
|
|
60
|
-
else
|
|
61
|
-
raise OptionParser::InvalidOption.new("unknown option: #{ key }")
|
|
62
|
-
end
|
|
63
|
-
end
|
|
52
|
+
opt.separator ""
|
|
53
|
+
opt.separator "Analysis limit options:"
|
|
54
|
+
opt.on("--max-second SECOND", Float, "Limit the maxium time of analysis (in second)") {|v| max_sec = v }
|
|
55
|
+
opt.on("--max-iteration TIMES", Integer, "Limit the maxium instruction count of analysis") {|v| max_iter = v }
|
|
56
|
+
|
|
57
|
+
opt.separator ""
|
|
58
|
+
opt.separator "Advanced options:"
|
|
59
|
+
opt.on("--[no-]stub-execution", "Force to call all unreachable methods with \"untyped\" arguments") {|v| options[:stub_execution] = v }
|
|
60
|
+
opt.on("--type-depth-limit DEPTH", Integer, "Limit the maximum depth of nested types") {|v| options[:type_depth_limit] = v }
|
|
61
|
+
opt.on("--debug", "Display analysis log (for debugging purpose)") { verbose = 2 }
|
|
62
|
+
opt.on("--[no-]stackprof MODE", /\Acpu|wall|object\z/, "Enable stackprof (for debugging purpose)") {|v| options[:stackprof] = v.to_sym }
|
|
64
63
|
|
|
65
64
|
opt.parse!(argv)
|
|
66
65
|
|
|
@@ -75,9 +74,9 @@ module TypeProf
|
|
|
75
74
|
end
|
|
76
75
|
end
|
|
77
76
|
|
|
78
|
-
puts "typeprof #{ VERSION }" if
|
|
77
|
+
puts "typeprof #{ VERSION }" if show_version
|
|
79
78
|
if rb_files.empty?
|
|
80
|
-
exit if
|
|
79
|
+
exit if show_version
|
|
81
80
|
raise OptionParser::InvalidOption.new("no input files")
|
|
82
81
|
end
|
|
83
82
|
|
data/lib/typeprof/config.rb
CHANGED
|
@@ -22,9 +22,11 @@ module TypeProf
|
|
|
22
22
|
opt[:verbose] ||= 0
|
|
23
23
|
opt[:options] ||= {}
|
|
24
24
|
opt[:options] = {
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
show_indicator: true,
|
|
26
|
+
show_untyped: false,
|
|
27
27
|
show_errors: false,
|
|
28
|
+
stub_execution: true,
|
|
29
|
+
type_depth_limit: 5,
|
|
28
30
|
stackprof: nil,
|
|
29
31
|
}.merge(opt[:options])
|
|
30
32
|
super(**opt)
|
|
@@ -67,7 +69,7 @@ module TypeProf
|
|
|
67
69
|
|
|
68
70
|
prologue_ctx = Context.new(nil, nil, nil)
|
|
69
71
|
prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
|
|
70
|
-
prologue_env = Env.new(StaticEnv.new(:top, Type.nil, false), [], [], Utils::HashWrapper.new({}))
|
|
72
|
+
prologue_env = Env.new(StaticEnv.new(:top, Type.nil, false, true), [], [], Utils::HashWrapper.new({}))
|
|
71
73
|
|
|
72
74
|
Config.rb_files.each do |rb|
|
|
73
75
|
if rb.is_a?(Array) # [String name, String content]
|
|
@@ -111,7 +113,7 @@ module TypeProf
|
|
|
111
113
|
ctx = Context.new(iseq, cref, nil)
|
|
112
114
|
ep = ExecutionPoint.new(ctx, 0, nil)
|
|
113
115
|
locals = [Type.nil] * iseq.locals.size
|
|
114
|
-
env = Env.new(StaticEnv.new(recv, Type.nil, false), locals, [], Utils::HashWrapper.new({}))
|
|
116
|
+
env = Env.new(StaticEnv.new(recv, Type.nil, false, false), locals, [], Utils::HashWrapper.new({}))
|
|
115
117
|
|
|
116
118
|
return ep, env
|
|
117
119
|
end
|
|
@@ -77,7 +77,9 @@ module TypeProf
|
|
|
77
77
|
return env, Type.any if depth <= 0
|
|
78
78
|
alloc_site = alloc_site.add_id(:cell).add_id(@base_type)
|
|
79
79
|
env, elems = @elems.localize(env, alloc_site, depth)
|
|
80
|
-
|
|
80
|
+
ty = Local.new(Cell, alloc_site, @base_type)
|
|
81
|
+
env = env.deploy_type(alloc_site, elems)
|
|
82
|
+
return env, ty
|
|
81
83
|
end
|
|
82
84
|
|
|
83
85
|
def limit_size(limit)
|
|
@@ -85,7 +87,7 @@ module TypeProf
|
|
|
85
87
|
Cell.new(@elems.limit_size(limit - 1), @base_type)
|
|
86
88
|
end
|
|
87
89
|
|
|
88
|
-
def
|
|
90
|
+
def method_dispatch_info
|
|
89
91
|
raise
|
|
90
92
|
end
|
|
91
93
|
|
|
@@ -95,6 +97,19 @@ module TypeProf
|
|
|
95
97
|
Cell.new(elems, @base_type)
|
|
96
98
|
end
|
|
97
99
|
|
|
100
|
+
def generate_substitution
|
|
101
|
+
subst = {}
|
|
102
|
+
tyvars = @base_type.klass.type_params.map {|name,| Type::Var.new(name) }
|
|
103
|
+
tyvars.zip(@elems.elems) do |tyvar, elem|
|
|
104
|
+
if subst[tyvar]
|
|
105
|
+
subst[tyvar] = subst[tyvar].union(elem)
|
|
106
|
+
else
|
|
107
|
+
subst[tyvar] = elem
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
subst
|
|
111
|
+
end
|
|
112
|
+
|
|
98
113
|
class Elements # Cell
|
|
99
114
|
include Utils::StructuralEquality
|
|
100
115
|
|
|
@@ -102,10 +117,14 @@ module TypeProf
|
|
|
102
117
|
@elems = elems
|
|
103
118
|
end
|
|
104
119
|
|
|
120
|
+
def self.dummy_elements
|
|
121
|
+
Elements.new([]) # XXX
|
|
122
|
+
end
|
|
123
|
+
|
|
105
124
|
attr_reader :elems
|
|
106
125
|
|
|
107
126
|
def to_local_type(id, base_ty)
|
|
108
|
-
Type::
|
|
127
|
+
Type::Local.new(Cell, id, base_ty)
|
|
109
128
|
end
|
|
110
129
|
|
|
111
130
|
def globalize(env, visited, depth)
|
|
@@ -180,45 +199,6 @@ module TypeProf
|
|
|
180
199
|
end
|
|
181
200
|
end
|
|
182
201
|
|
|
183
|
-
class LocalCell < ContainerType
|
|
184
|
-
def initialize(id, base_type)
|
|
185
|
-
@id = id
|
|
186
|
-
raise unless base_type
|
|
187
|
-
@base_type = base_type
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
attr_reader :id, :base_type
|
|
191
|
-
|
|
192
|
-
def inspect
|
|
193
|
-
"Type::LocalCell[#{ @id }, base_type: #{ @base_type.inspect }]"
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
def screen_name(scratch)
|
|
197
|
-
#raise "LocalArray must not be included in signature"
|
|
198
|
-
"LocalCell!"
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
def globalize(env, visited, depth)
|
|
202
|
-
if visited[self] || depth <= 0
|
|
203
|
-
Type.any
|
|
204
|
-
else
|
|
205
|
-
visited[self] = true
|
|
206
|
-
elems = env.get_container_elem_types(@id)
|
|
207
|
-
if elems
|
|
208
|
-
elems = elems.globalize(env, visited, depth - 1)
|
|
209
|
-
else
|
|
210
|
-
elems = Cell::Elements.new([]) # XXX
|
|
211
|
-
end
|
|
212
|
-
visited.delete(self)
|
|
213
|
-
Cell.new(elems, @base_type)
|
|
214
|
-
end
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
def get_method(mid, scratch)
|
|
218
|
-
@base_type.get_method(mid, scratch)
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
|
|
222
202
|
# Do not insert Array type to local environment, stack, etc.
|
|
223
203
|
class Array < ContainerType
|
|
224
204
|
def initialize(elems, base_type)
|
|
@@ -228,6 +208,10 @@ module TypeProf
|
|
|
228
208
|
@base_type = base_type
|
|
229
209
|
end
|
|
230
210
|
|
|
211
|
+
def self.dummy_elements
|
|
212
|
+
Elements.new([], Type.any)
|
|
213
|
+
end
|
|
214
|
+
|
|
231
215
|
attr_reader :elems, :base_type
|
|
232
216
|
|
|
233
217
|
def inspect
|
|
@@ -247,7 +231,9 @@ module TypeProf
|
|
|
247
231
|
return env, Type.any if depth <= 0
|
|
248
232
|
alloc_site = alloc_site.add_id(:ary).add_id(@base_type)
|
|
249
233
|
env, elems = @elems.localize(env, alloc_site, depth - 1)
|
|
250
|
-
|
|
234
|
+
ty = Local.new(Array, alloc_site, @base_type)
|
|
235
|
+
env = env.deploy_type(alloc_site, elems)
|
|
236
|
+
return env, ty
|
|
251
237
|
end
|
|
252
238
|
|
|
253
239
|
def limit_size(limit)
|
|
@@ -255,7 +241,7 @@ module TypeProf
|
|
|
255
241
|
Array.new(@elems.limit_size(limit - 1), @base_type)
|
|
256
242
|
end
|
|
257
243
|
|
|
258
|
-
def
|
|
244
|
+
def method_dispatch_info
|
|
259
245
|
raise
|
|
260
246
|
end
|
|
261
247
|
|
|
@@ -265,6 +251,10 @@ module TypeProf
|
|
|
265
251
|
Array.new(elems, @base_type)
|
|
266
252
|
end
|
|
267
253
|
|
|
254
|
+
def generate_substitution
|
|
255
|
+
{ Type::Var.new(:Elem) => @elems.squash }
|
|
256
|
+
end
|
|
257
|
+
|
|
268
258
|
class Elements # Array
|
|
269
259
|
include Utils::StructuralEquality
|
|
270
260
|
|
|
@@ -277,7 +267,7 @@ module TypeProf
|
|
|
277
267
|
attr_reader :lead_tys, :rest_ty
|
|
278
268
|
|
|
279
269
|
def to_local_type(id, base_ty)
|
|
280
|
-
Type::
|
|
270
|
+
Type::Local.new(Array, id, base_ty)
|
|
281
271
|
end
|
|
282
272
|
|
|
283
273
|
def globalize(env, visited, depth)
|
|
@@ -305,7 +295,10 @@ module TypeProf
|
|
|
305
295
|
end
|
|
306
296
|
|
|
307
297
|
def screen_name(scratch)
|
|
308
|
-
if
|
|
298
|
+
if @rest_ty == Type.bot
|
|
299
|
+
if @lead_tys.empty?
|
|
300
|
+
return "Array[bot]" # RBS does not allow an empty tuple "[]"
|
|
301
|
+
end
|
|
309
302
|
s = @lead_tys.map do |ty|
|
|
310
303
|
ty.screen_name(scratch)
|
|
311
304
|
end
|
|
@@ -360,7 +353,57 @@ module TypeProf
|
|
|
360
353
|
end
|
|
361
354
|
|
|
362
355
|
def [](idx)
|
|
363
|
-
if idx
|
|
356
|
+
if idx.is_a?(Range)
|
|
357
|
+
if @rest_ty == Type.bot
|
|
358
|
+
lead_tys = @lead_tys[idx]
|
|
359
|
+
if lead_tys
|
|
360
|
+
rest_ty = Type.bot
|
|
361
|
+
else
|
|
362
|
+
return Type.nil
|
|
363
|
+
end
|
|
364
|
+
else
|
|
365
|
+
b, e = idx.begin, idx.end
|
|
366
|
+
b = 0 if !b
|
|
367
|
+
if !e
|
|
368
|
+
lead_tys = @lead_tys[idx] || []
|
|
369
|
+
rest_ty = @rest_ty
|
|
370
|
+
elsif b >= 0
|
|
371
|
+
if e >= 0
|
|
372
|
+
if b <= e
|
|
373
|
+
if e < @lead_tys.size
|
|
374
|
+
lead_tys = @lead_tys[idx]
|
|
375
|
+
rest_ty = Type.bot
|
|
376
|
+
else
|
|
377
|
+
lead_tys = @lead_tys[idx] || []
|
|
378
|
+
rest_ty = @rest_ty
|
|
379
|
+
end
|
|
380
|
+
else
|
|
381
|
+
return Type.nil
|
|
382
|
+
end
|
|
383
|
+
else
|
|
384
|
+
lead_tys = @lead_tys[idx] || []
|
|
385
|
+
e = idx.exclude_end? ? e : e == -1 ? @lead_tys.size : e + 1
|
|
386
|
+
rest_ty = (@lead_tys[e + 1..] || []).inject(@rest_ty) {|ty0, ty1| ty0.union(ty1) }
|
|
387
|
+
end
|
|
388
|
+
else
|
|
389
|
+
lead_tys = []
|
|
390
|
+
if e >= 0
|
|
391
|
+
rest_ty = e < @lead_tys.size ? Type.bot : @rest_ty
|
|
392
|
+
range = [0, @lead_tys.size + b].max .. (idx.exclude_end? ? e - 1 : e)
|
|
393
|
+
rest_ty = @lead_tys[range].inject(rest_ty) {|ty0, ty1| ty0.union(ty1) }
|
|
394
|
+
else
|
|
395
|
+
if b <= e
|
|
396
|
+
range = [0, @lead_tys.size + b].max .. (idx.exclude_end? ? e - 1 : e)
|
|
397
|
+
rest_ty = @lead_tys[range].inject(@rest_ty) {|ty0, ty1| ty0.union(ty1) }
|
|
398
|
+
else
|
|
399
|
+
return Type.nil
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
base_ty = Type::Instance.new(Type::Builtin[:ary])
|
|
405
|
+
Array.new(Elements.new(lead_tys, rest_ty), base_ty)
|
|
406
|
+
elsif idx >= 0
|
|
364
407
|
if idx < @lead_tys.size
|
|
365
408
|
@lead_tys[idx]
|
|
366
409
|
elsif @rest_ty == Type.bot
|
|
@@ -491,48 +534,6 @@ module TypeProf
|
|
|
491
534
|
end
|
|
492
535
|
end
|
|
493
536
|
|
|
494
|
-
# Do not insert Array type to local environment, stack, etc.
|
|
495
|
-
class LocalArray < ContainerType
|
|
496
|
-
def initialize(id, base_type)
|
|
497
|
-
@id = id
|
|
498
|
-
raise unless base_type
|
|
499
|
-
@base_type = base_type
|
|
500
|
-
end
|
|
501
|
-
|
|
502
|
-
attr_reader :id, :base_type
|
|
503
|
-
|
|
504
|
-
def inspect
|
|
505
|
-
"Type::LocalArray[#{ @id }, base_type: #{ @base_type.inspect }]"
|
|
506
|
-
end
|
|
507
|
-
|
|
508
|
-
def screen_name(scratch)
|
|
509
|
-
#raise "LocalArray must not be included in signature"
|
|
510
|
-
"LocalArray!"
|
|
511
|
-
end
|
|
512
|
-
|
|
513
|
-
def globalize(env, visited, depth)
|
|
514
|
-
if visited[self] || depth <= 0
|
|
515
|
-
Type.any
|
|
516
|
-
else
|
|
517
|
-
visited[self] = true
|
|
518
|
-
elems = env.get_container_elem_types(@id)
|
|
519
|
-
if elems
|
|
520
|
-
elems = elems.globalize(env, visited, depth - 1)
|
|
521
|
-
else
|
|
522
|
-
# TODO: currently out-of-scope array cannot be accessed
|
|
523
|
-
elems = Array::Elements.new([], Type.any)
|
|
524
|
-
end
|
|
525
|
-
visited.delete(self)
|
|
526
|
-
Array.new(elems, @base_type)
|
|
527
|
-
end
|
|
528
|
-
end
|
|
529
|
-
|
|
530
|
-
def get_method(mid, scratch)
|
|
531
|
-
@base_type.get_method(mid, scratch)
|
|
532
|
-
end
|
|
533
|
-
end
|
|
534
|
-
|
|
535
|
-
|
|
536
537
|
class Hash < ContainerType
|
|
537
538
|
def initialize(elems, base_type)
|
|
538
539
|
@elems = elems
|
|
@@ -554,7 +555,9 @@ module TypeProf
|
|
|
554
555
|
return env, Type.any if depth <= 0
|
|
555
556
|
alloc_site = alloc_site.add_id(:hash).add_id(@base_type)
|
|
556
557
|
env, elems = @elems.localize(env, alloc_site, depth - 1)
|
|
557
|
-
|
|
558
|
+
ty = Local.new(Hash, alloc_site, @base_type)
|
|
559
|
+
env = env.deploy_type(alloc_site, elems)
|
|
560
|
+
return env, ty
|
|
558
561
|
end
|
|
559
562
|
|
|
560
563
|
def limit_size(limit)
|
|
@@ -562,7 +565,7 @@ module TypeProf
|
|
|
562
565
|
Hash.new(@elems.limit_size(limit - 1), @base_type)
|
|
563
566
|
end
|
|
564
567
|
|
|
565
|
-
def
|
|
568
|
+
def method_dispatch_info
|
|
566
569
|
raise
|
|
567
570
|
end
|
|
568
571
|
|
|
@@ -572,6 +575,14 @@ module TypeProf
|
|
|
572
575
|
Hash.new(elems, @base_type)
|
|
573
576
|
end
|
|
574
577
|
|
|
578
|
+
def generate_substitution
|
|
579
|
+
tyvar_k = Type::Var.new(:K)
|
|
580
|
+
tyvar_v = Type::Var.new(:V)
|
|
581
|
+
k_ty0, v_ty0 = @elems.squash
|
|
582
|
+
# XXX: need to heuristically replace ret type Hash[K, V] with self, instead of conversative type?
|
|
583
|
+
{ tyvar_k => k_ty0, tyvar_v => v_ty0 }
|
|
584
|
+
end
|
|
585
|
+
|
|
575
586
|
class Elements # Hash
|
|
576
587
|
include Utils::StructuralEquality
|
|
577
588
|
|
|
@@ -580,18 +591,21 @@ module TypeProf
|
|
|
580
591
|
raise unless k_ty.is_a?(Type)
|
|
581
592
|
raise unless v_ty.is_a?(Type)
|
|
582
593
|
raise if k_ty.is_a?(Type::Union)
|
|
583
|
-
raise if k_ty.is_a?(Type::
|
|
584
|
-
raise if k_ty.is_a?(Type::LocalHash)
|
|
594
|
+
raise if k_ty.is_a?(Type::Local)
|
|
585
595
|
raise if k_ty.is_a?(Type::Array)
|
|
586
596
|
raise if k_ty.is_a?(Type::Hash)
|
|
587
597
|
end
|
|
588
598
|
@map_tys = map_tys
|
|
589
599
|
end
|
|
590
600
|
|
|
601
|
+
def self.dummy_elements
|
|
602
|
+
Elements.new({Type.any => Type.any})
|
|
603
|
+
end
|
|
604
|
+
|
|
591
605
|
attr_reader :map_tys
|
|
592
606
|
|
|
593
607
|
def to_local_type(id, base_ty)
|
|
594
|
-
Type::
|
|
608
|
+
Type::Local.new(Hash, id, base_ty)
|
|
595
609
|
end
|
|
596
610
|
|
|
597
611
|
def globalize(env, visited, depth)
|
|
@@ -631,16 +645,22 @@ module TypeProf
|
|
|
631
645
|
end
|
|
632
646
|
|
|
633
647
|
def screen_name(scratch)
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
648
|
+
if !@map_tys.empty? && @map_tys.all? {|k_ty,| k_ty.is_a?(Type::Symbol) }
|
|
649
|
+
s = @map_tys.map do |k_ty, v_ty|
|
|
650
|
+
v = v_ty.screen_name(scratch)
|
|
637
651
|
"#{ k_ty.sym }: #{ v }"
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
652
|
+
end.join(", ")
|
|
653
|
+
"{#{ s }}"
|
|
654
|
+
else
|
|
655
|
+
k_ty = v_ty = Type.bot
|
|
656
|
+
@map_tys.each do |k, v|
|
|
657
|
+
k_ty = k_ty.union(k)
|
|
658
|
+
v_ty = v_ty.union(v)
|
|
641
659
|
end
|
|
642
|
-
|
|
643
|
-
|
|
660
|
+
k_ty = k_ty.screen_name(scratch)
|
|
661
|
+
v_ty = v_ty.screen_name(scratch)
|
|
662
|
+
"Hash[#{ k_ty }, #{ v_ty }]"
|
|
663
|
+
end
|
|
644
664
|
end
|
|
645
665
|
|
|
646
666
|
def pretty_print(q)
|
|
@@ -773,21 +793,23 @@ module TypeProf
|
|
|
773
793
|
end
|
|
774
794
|
end
|
|
775
795
|
|
|
776
|
-
class
|
|
777
|
-
def initialize(id, base_type)
|
|
796
|
+
class Local < ContainerType
|
|
797
|
+
def initialize(kind, id, base_type)
|
|
798
|
+
@kind = kind
|
|
778
799
|
@id = id
|
|
800
|
+
raise unless base_type
|
|
779
801
|
@base_type = base_type
|
|
780
802
|
end
|
|
781
803
|
|
|
782
|
-
attr_reader :id, :base_type
|
|
804
|
+
attr_reader :kind, :id, :base_type
|
|
783
805
|
|
|
784
806
|
def inspect
|
|
785
|
-
"Type::
|
|
807
|
+
"Type::Local[#{ @kind }, #{ @id }, base_type: #{ @base_type.inspect }]"
|
|
786
808
|
end
|
|
787
809
|
|
|
788
810
|
def screen_name(scratch)
|
|
789
|
-
#raise "
|
|
790
|
-
"
|
|
811
|
+
#raise "Local type must not be included in signature"
|
|
812
|
+
"Local[#{ @kind }]"
|
|
791
813
|
end
|
|
792
814
|
|
|
793
815
|
def globalize(env, visited, depth)
|
|
@@ -799,15 +821,56 @@ module TypeProf
|
|
|
799
821
|
if elems
|
|
800
822
|
elems = elems.globalize(env, visited, depth - 1)
|
|
801
823
|
else
|
|
802
|
-
|
|
824
|
+
# TODO: currently out-of-scope array cannot be accessed
|
|
825
|
+
elems = @kind::Elements.dummy_elements
|
|
803
826
|
end
|
|
804
827
|
visited.delete(self)
|
|
805
|
-
|
|
828
|
+
@kind.new(elems, @base_type)
|
|
806
829
|
end
|
|
807
830
|
end
|
|
808
831
|
|
|
809
|
-
def
|
|
810
|
-
@base_type.
|
|
832
|
+
def method_dispatch_info
|
|
833
|
+
@base_type.method_dispatch_info
|
|
834
|
+
end
|
|
835
|
+
|
|
836
|
+
def update_container_elem_type(subst, env, caller_ep, scratch)
|
|
837
|
+
case
|
|
838
|
+
when @kind == Cell
|
|
839
|
+
tyvars = @base_type.klass.type_params.map {|name,| Type::Var.new(name) }
|
|
840
|
+
# XXX: This should be skipped when the called methods belongs to superclass
|
|
841
|
+
tyvars.each_with_index do |tyvar, idx|
|
|
842
|
+
ty = subst[tyvar]
|
|
843
|
+
if ty
|
|
844
|
+
env, ty = scratch.localize_type(ty, env, caller_ep)
|
|
845
|
+
env = scratch.update_container_elem_types(env, caller_ep, @id, @base_type) do |elems|
|
|
846
|
+
elems.update(idx, ty)
|
|
847
|
+
end
|
|
848
|
+
end
|
|
849
|
+
end
|
|
850
|
+
when @kind == Array
|
|
851
|
+
tyvar_elem = Type::Var.new(:Elem)
|
|
852
|
+
if subst[tyvar_elem]
|
|
853
|
+
ty = subst[tyvar_elem]
|
|
854
|
+
env, ty = scratch.localize_type(ty, env, caller_ep)
|
|
855
|
+
env = scratch.update_container_elem_types(env, caller_ep, @id, @base_type) do |elems|
|
|
856
|
+
elems.update(nil, ty)
|
|
857
|
+
end
|
|
858
|
+
end
|
|
859
|
+
when @kind == Hash
|
|
860
|
+
tyvar_k = Type::Var.new(:K)
|
|
861
|
+
tyvar_v = Type::Var.new(:V)
|
|
862
|
+
if subst[tyvar_k] && subst[tyvar_v]
|
|
863
|
+
k_ty = subst[tyvar_k]
|
|
864
|
+
v_ty = subst[tyvar_v]
|
|
865
|
+
alloc_site = AllocationSite.new(caller_ep)
|
|
866
|
+
env, k_ty = scratch.localize_type(k_ty, env, caller_ep, alloc_site.add_id(:k))
|
|
867
|
+
env, v_ty = scratch.localize_type(v_ty, env, caller_ep, alloc_site.add_id(:v))
|
|
868
|
+
env = scratch.update_container_elem_types(env, caller_ep, @id, @base_type) do |elems|
|
|
869
|
+
elems.update(k_ty, v_ty)
|
|
870
|
+
end
|
|
871
|
+
end
|
|
872
|
+
end
|
|
873
|
+
env
|
|
811
874
|
end
|
|
812
875
|
end
|
|
813
876
|
end
|