typeprof 0.21.11 → 0.30.1
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/README.md +15 -31
- data/bin/typeprof +5 -0
- data/doc/doc.ja.md +134 -0
- data/doc/doc.md +136 -0
- data/lib/typeprof/cli/cli.rb +178 -0
- data/lib/typeprof/cli.rb +3 -133
- data/lib/typeprof/code_range.rb +112 -0
- data/lib/typeprof/core/ast/base.rb +263 -0
- data/lib/typeprof/core/ast/call.rb +259 -0
- data/lib/typeprof/core/ast/const.rb +126 -0
- data/lib/typeprof/core/ast/control.rb +433 -0
- data/lib/typeprof/core/ast/meta.rb +150 -0
- data/lib/typeprof/core/ast/method.rb +339 -0
- data/lib/typeprof/core/ast/misc.rb +263 -0
- data/lib/typeprof/core/ast/module.rb +123 -0
- data/lib/typeprof/core/ast/pattern.rb +140 -0
- data/lib/typeprof/core/ast/sig_decl.rb +471 -0
- data/lib/typeprof/core/ast/sig_type.rb +663 -0
- data/lib/typeprof/core/ast/value.rb +319 -0
- data/lib/typeprof/core/ast/variable.rb +315 -0
- data/lib/typeprof/core/ast.rb +472 -0
- data/lib/typeprof/core/builtin.rb +146 -0
- data/lib/typeprof/core/env/method.rb +137 -0
- data/lib/typeprof/core/env/method_entity.rb +55 -0
- data/lib/typeprof/core/env/module_entity.rb +408 -0
- data/lib/typeprof/core/env/static_read.rb +155 -0
- data/lib/typeprof/core/env/type_alias_entity.rb +27 -0
- data/lib/typeprof/core/env/value_entity.rb +32 -0
- data/lib/typeprof/core/env.rb +366 -0
- data/lib/typeprof/core/graph/box.rb +998 -0
- data/lib/typeprof/core/graph/change_set.rb +224 -0
- data/lib/typeprof/core/graph/filter.rb +155 -0
- data/lib/typeprof/core/graph/vertex.rb +225 -0
- data/lib/typeprof/core/service.rb +514 -0
- data/lib/typeprof/core/type.rb +352 -0
- data/lib/typeprof/core/util.rb +81 -0
- data/lib/typeprof/core.rb +31 -0
- data/lib/typeprof/diagnostic.rb +35 -0
- data/lib/typeprof/lsp/messages.rb +415 -0
- data/lib/typeprof/lsp/server.rb +203 -0
- data/lib/typeprof/lsp/text.rb +69 -0
- data/lib/typeprof/lsp/util.rb +51 -0
- data/lib/typeprof/lsp.rb +4 -907
- data/lib/typeprof/version.rb +1 -1
- data/lib/typeprof.rb +4 -18
- data/typeprof.gemspec +5 -7
- metadata +47 -33
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/main.yml +0 -39
- data/.gitignore +0 -9
- data/Gemfile +0 -17
- data/Gemfile.lock +0 -41
- data/Rakefile +0 -10
- data/exe/typeprof +0 -10
- data/lib/typeprof/analyzer.rb +0 -2598
- data/lib/typeprof/arguments.rb +0 -414
- data/lib/typeprof/block.rb +0 -176
- data/lib/typeprof/builtin.rb +0 -893
- data/lib/typeprof/code-range.rb +0 -177
- data/lib/typeprof/config.rb +0 -158
- data/lib/typeprof/container-type.rb +0 -912
- data/lib/typeprof/export.rb +0 -589
- data/lib/typeprof/import.rb +0 -852
- data/lib/typeprof/insns-def.rb +0 -65
- data/lib/typeprof/iseq.rb +0 -864
- data/lib/typeprof/method.rb +0 -355
- data/lib/typeprof/type.rb +0 -1140
- data/lib/typeprof/utils.rb +0 -212
- data/tools/coverage.rb +0 -14
- data/tools/setup-insns-def.rb +0 -30
- data/typeprof-lsp +0 -3
data/lib/typeprof/analyzer.rb
DELETED
@@ -1,2598 +0,0 @@
|
|
1
|
-
module TypeProf
|
2
|
-
class CRef
|
3
|
-
include Utils::StructuralEquality
|
4
|
-
|
5
|
-
def initialize(outer, klass, singleton)
|
6
|
-
@outer = outer
|
7
|
-
@klass = klass
|
8
|
-
@singleton = singleton
|
9
|
-
# flags
|
10
|
-
# scope_visi (= method_visi * module_func_flag)
|
11
|
-
# refinements
|
12
|
-
end
|
13
|
-
|
14
|
-
def extend(klass, singleton)
|
15
|
-
CRef.new(self, klass, singleton)
|
16
|
-
end
|
17
|
-
|
18
|
-
attr_reader :outer, :klass, :singleton
|
19
|
-
|
20
|
-
def pretty_print(q)
|
21
|
-
q.text "CRef["
|
22
|
-
q.pp @klass
|
23
|
-
q.text "]"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
class Context
|
28
|
-
include Utils::StructuralEquality
|
29
|
-
|
30
|
-
def initialize(iseq, cref, mid)
|
31
|
-
@iseq = iseq
|
32
|
-
@cref = cref
|
33
|
-
@mid = mid
|
34
|
-
end
|
35
|
-
|
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
|
45
|
-
|
46
|
-
def detailed_source_location(pc)
|
47
|
-
if @iseq
|
48
|
-
@iseq.detailed_source_location(pc)
|
49
|
-
else
|
50
|
-
nil
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def replace_cref(cref)
|
55
|
-
Context.new(@iseq, cref, @mid)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
class TypedContext
|
60
|
-
include Utils::StructuralEquality
|
61
|
-
|
62
|
-
def initialize(caller_ep, mid)
|
63
|
-
@caller_ep = caller_ep
|
64
|
-
@mid = mid
|
65
|
-
end
|
66
|
-
|
67
|
-
attr_reader :caller_ep, :mid
|
68
|
-
|
69
|
-
def source_location(_pc)
|
70
|
-
if @caller_ep
|
71
|
-
@caller_ep.source_location
|
72
|
-
else
|
73
|
-
"<typed-context:#{ @mid }>"
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def detailed_source_location(_pc)
|
78
|
-
if @caller_ep
|
79
|
-
@caller_ep.source_location
|
80
|
-
else
|
81
|
-
nil
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def replace_cref(cref)
|
86
|
-
# What to do?
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
class ExecutionPoint
|
91
|
-
include Utils::StructuralEquality
|
92
|
-
|
93
|
-
def initialize(ctx, pc, outer)
|
94
|
-
@ctx = ctx
|
95
|
-
@pc = pc
|
96
|
-
@outer = outer
|
97
|
-
end
|
98
|
-
|
99
|
-
def key
|
100
|
-
[@ctx.iseq, @pc]
|
101
|
-
end
|
102
|
-
|
103
|
-
attr_reader :ctx, :pc, :outer
|
104
|
-
|
105
|
-
def jump(pc)
|
106
|
-
ExecutionPoint.new(@ctx, pc, @outer)
|
107
|
-
end
|
108
|
-
|
109
|
-
def next
|
110
|
-
ExecutionPoint.new(@ctx, @pc + 1, @outer)
|
111
|
-
end
|
112
|
-
|
113
|
-
def replace_cref(cref)
|
114
|
-
ExecutionPoint.new(@ctx.replace_cref(cref), @pc, @outer)
|
115
|
-
end
|
116
|
-
|
117
|
-
def source_location
|
118
|
-
@ctx.source_location(@pc)
|
119
|
-
end
|
120
|
-
|
121
|
-
def detailed_source_location
|
122
|
-
@ctx.detailed_source_location(@pc)
|
123
|
-
end
|
124
|
-
|
125
|
-
def absolute_path
|
126
|
-
@ctx.iseq.absolute_path
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
class StaticEnv
|
131
|
-
include Utils::StructuralEquality
|
132
|
-
|
133
|
-
def initialize(recv_ty, blk_ty, mod_func, pub_meth)
|
134
|
-
@recv_ty = recv_ty
|
135
|
-
@blk_ty = blk_ty
|
136
|
-
@mod_func = mod_func
|
137
|
-
@pub_meth = pub_meth
|
138
|
-
|
139
|
-
return if recv_ty == :top #OK
|
140
|
-
recv_ty.each_child_global do |ty|
|
141
|
-
raise ty.inspect if !ty.is_a?(Type::Instance) && !ty.is_a?(Type::Class) && !ty.is_a?(Type::Symbol) && ty != Type.any
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
attr_reader :recv_ty, :blk_ty, :mod_func, :pub_meth
|
146
|
-
|
147
|
-
def merge(other)
|
148
|
-
recv_ty = @recv_ty.union(other.recv_ty)
|
149
|
-
blk_ty = @blk_ty.union(other.blk_ty)
|
150
|
-
mod_func = @mod_func & other.mod_func # ??
|
151
|
-
pub_meth = @pub_meth & other.pub_meth # ??
|
152
|
-
StaticEnv.new(recv_ty, blk_ty, mod_func, pub_meth)
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
class Env
|
157
|
-
include Utils::StructuralEquality
|
158
|
-
|
159
|
-
def initialize(static_env, locals, stack, type_params)
|
160
|
-
@static_env = static_env
|
161
|
-
@locals = locals
|
162
|
-
@stack = stack
|
163
|
-
@type_params = type_params
|
164
|
-
end
|
165
|
-
|
166
|
-
attr_reader :static_env, :locals, :stack, :type_params
|
167
|
-
|
168
|
-
def merge(other)
|
169
|
-
raise if @locals.size != other.locals.size
|
170
|
-
raise if @stack.size != other.stack.size
|
171
|
-
static_env = @static_env.merge(other.static_env)
|
172
|
-
locals = []
|
173
|
-
@locals.zip(other.locals) {|ty1, ty2| locals << ty1.union(ty2) }
|
174
|
-
stack = []
|
175
|
-
@stack.zip(other.stack) {|ty1, ty2| stack << ty1.union(ty2) }
|
176
|
-
if @type_params
|
177
|
-
raise if !other.type_params
|
178
|
-
if @type_params == other.type_params
|
179
|
-
type_params = @type_params
|
180
|
-
else
|
181
|
-
type_params = @type_params.internal_hash.dup
|
182
|
-
other.type_params.internal_hash.each do |id, elems|
|
183
|
-
elems2 = type_params[id]
|
184
|
-
if elems2
|
185
|
-
if elems != elems2
|
186
|
-
type_params[id] = elems.union(elems2)
|
187
|
-
end
|
188
|
-
else
|
189
|
-
type_params[id] = elems
|
190
|
-
end
|
191
|
-
end
|
192
|
-
type_params = Utils::HashWrapper.new(type_params)
|
193
|
-
end
|
194
|
-
else
|
195
|
-
raise if other.type_params
|
196
|
-
end
|
197
|
-
Env.new(static_env, locals, stack, type_params)
|
198
|
-
end
|
199
|
-
|
200
|
-
def push(*tys)
|
201
|
-
tys.each do |ty|
|
202
|
-
raise "nil cannot be pushed to the stack" if ty.nil?
|
203
|
-
ty.each_child do |ty|
|
204
|
-
raise if ty.is_a?(Type::Var)
|
205
|
-
#raise if ty.is_a?(Type::Instance) && ty.klass.type_params.size > 1
|
206
|
-
raise "Array cannot be pushed to the stack" if ty.is_a?(Type::Array)
|
207
|
-
raise "Hash cannot be pushed to the stack" if ty.is_a?(Type::Hash)
|
208
|
-
end
|
209
|
-
end
|
210
|
-
Env.new(@static_env, @locals, @stack + tys, @type_params)
|
211
|
-
end
|
212
|
-
|
213
|
-
def pop(n)
|
214
|
-
stack = @stack.dup
|
215
|
-
tys = stack.pop(n)
|
216
|
-
nenv = Env.new(@static_env, @locals, stack, @type_params)
|
217
|
-
return nenv, tys
|
218
|
-
end
|
219
|
-
|
220
|
-
def setn(i, ty)
|
221
|
-
stack = Utils.array_update(@stack, -i, ty)
|
222
|
-
Env.new(@static_env, @locals, stack, @type_params)
|
223
|
-
end
|
224
|
-
|
225
|
-
def topn(i)
|
226
|
-
push(@stack[-i - 1])
|
227
|
-
end
|
228
|
-
|
229
|
-
def get_local(idx)
|
230
|
-
@locals[idx]
|
231
|
-
end
|
232
|
-
|
233
|
-
def local_update(idx, ty)
|
234
|
-
Env.new(@static_env, Utils.array_update(@locals, idx, ty), @stack, @type_params)
|
235
|
-
end
|
236
|
-
|
237
|
-
def get_container_elem_types(id)
|
238
|
-
@type_params.internal_hash[id]
|
239
|
-
end
|
240
|
-
|
241
|
-
def deploy_type(id, elems)
|
242
|
-
type_params = Utils::HashWrapper.new(@type_params.internal_hash.merge({ id => elems }))
|
243
|
-
Env.new(@static_env, @locals, @stack, type_params)
|
244
|
-
end
|
245
|
-
|
246
|
-
def enable_module_function
|
247
|
-
senv = StaticEnv.new(@static_env.recv_ty, @static_env.blk_ty, true, @static_env.pub_meth)
|
248
|
-
Env.new(senv, @locals, @stack, @type_params)
|
249
|
-
end
|
250
|
-
|
251
|
-
def method_public_set(flag)
|
252
|
-
senv = StaticEnv.new(@static_env.recv_ty, @static_env.blk_ty, @static_env.mod_func, flag)
|
253
|
-
Env.new(senv, @locals, @stack, @type_params)
|
254
|
-
end
|
255
|
-
|
256
|
-
def replace_recv_ty(ty)
|
257
|
-
senv = StaticEnv.new(ty, @static_env.blk_ty, @static_env.mod_func, @static_env.pub_meth)
|
258
|
-
Env.new(senv, @locals, @stack, @type_params)
|
259
|
-
end
|
260
|
-
|
261
|
-
def replace_blk_ty(ty)
|
262
|
-
senv = StaticEnv.new(@static_env.recv_ty, ty, @static_env.mod_func, @static_env.pub_meth)
|
263
|
-
Env.new(senv, @locals, @stack, @type_params)
|
264
|
-
end
|
265
|
-
|
266
|
-
def inspect
|
267
|
-
"Env[#{ @static_env.inspect }, locals:#{ @locals.inspect }, stack:#{ @stack.inspect }, type_params:#{ (@type_params&.internal_hash).inspect }]"
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
class Scratch
|
272
|
-
def inspect
|
273
|
-
"#<Scratch>"
|
274
|
-
end
|
275
|
-
|
276
|
-
def initialize
|
277
|
-
@entrypoints = []
|
278
|
-
|
279
|
-
@worklist = Utils::WorkList.new
|
280
|
-
|
281
|
-
@ep2env = {}
|
282
|
-
|
283
|
-
@class_defs = {}
|
284
|
-
@struct_defs = {}
|
285
|
-
|
286
|
-
@iseq_method_to_ctxs = {}
|
287
|
-
|
288
|
-
@alloc_site_to_global_id = {}
|
289
|
-
|
290
|
-
@callsites, @return_envs = {}, {}
|
291
|
-
@block_to_ctx = {}
|
292
|
-
@method_signatures = {}
|
293
|
-
@block_signatures = {}
|
294
|
-
@return_values = {}
|
295
|
-
@gvar_table = VarTable.new
|
296
|
-
|
297
|
-
@errors = []
|
298
|
-
@reveal_types = {}
|
299
|
-
@backward_edges = {}
|
300
|
-
|
301
|
-
@pending_execution = {}
|
302
|
-
@executed_iseqs = Utils::MutableSet.new
|
303
|
-
|
304
|
-
@loaded_files = {}
|
305
|
-
|
306
|
-
@rbs_reader = RBSReader.new
|
307
|
-
|
308
|
-
@terminated = false
|
309
|
-
|
310
|
-
@anonymous_struct_gen_id = 0
|
311
|
-
|
312
|
-
@types_being_shown = []
|
313
|
-
@namespace = nil
|
314
|
-
|
315
|
-
@lsp_completion = nil
|
316
|
-
@lsp_signature_help = CodeRangeTable.new
|
317
|
-
end
|
318
|
-
|
319
|
-
def add_entrypoint(iseq)
|
320
|
-
@entrypoints << iseq
|
321
|
-
end
|
322
|
-
|
323
|
-
attr_reader :return_envs, :loaded_files, :rbs_reader
|
324
|
-
|
325
|
-
def get_env(ep)
|
326
|
-
@ep2env[ep]
|
327
|
-
end
|
328
|
-
|
329
|
-
def merge_env(ep, env)
|
330
|
-
# TODO: this is wrong; it include not only proceeds but also indirect propagation like out-of-block variable modification
|
331
|
-
#add_edge(ep, @ep)
|
332
|
-
env2 = @ep2env[ep]
|
333
|
-
if env2
|
334
|
-
nenv = env2.merge(env)
|
335
|
-
if nenv != env2 && !@worklist.member?(ep)
|
336
|
-
@worklist.insert(ep.key, ep)
|
337
|
-
end
|
338
|
-
@ep2env[ep] = nenv
|
339
|
-
else
|
340
|
-
@worklist.insert(ep.key, ep)
|
341
|
-
@ep2env[ep] = env
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
attr_reader :class_defs
|
346
|
-
|
347
|
-
class ClassDef # or ModuleDef
|
348
|
-
def initialize(kind, name, absolute_path, superclass)
|
349
|
-
raise unless name.is_a?(Array)
|
350
|
-
@kind = kind
|
351
|
-
@modules = {
|
352
|
-
:before => { true => [], false => [] }, # before = include/extend
|
353
|
-
:after => { true => [], false => [] }, # after = prepend
|
354
|
-
}
|
355
|
-
@name = name
|
356
|
-
@consts = {}
|
357
|
-
@methods = {}
|
358
|
-
@ivars = VarTable.new
|
359
|
-
@cvars = VarTable.new
|
360
|
-
@absolute_path = absolute_path
|
361
|
-
@namespace = nil
|
362
|
-
@superclass = superclass
|
363
|
-
@subclasses = []
|
364
|
-
end
|
365
|
-
|
366
|
-
attr_reader :kind, :modules, :methods, :ivars, :cvars, :absolute_path, :superclass, :subclasses
|
367
|
-
attr_accessor :name, :klass_obj
|
368
|
-
|
369
|
-
def mix_module(kind, mod, type_args, singleton, absolute_path)
|
370
|
-
mod_, module_type_args, absolute_paths = @modules[kind][singleton].find {|m,| m == mod }
|
371
|
-
if mod_
|
372
|
-
raise "inconsistent #{ kind == :after ? "include/extend" : "prepend" } type args in RBS?" if module_type_args != type_args && type_args != [] && type_args != nil
|
373
|
-
else
|
374
|
-
absolute_paths = Utils::MutableSet.new
|
375
|
-
@modules[kind][singleton].unshift([mod, type_args, absolute_paths])
|
376
|
-
end
|
377
|
-
absolute_paths << absolute_path
|
378
|
-
end
|
379
|
-
|
380
|
-
def get_constant(name)
|
381
|
-
ty, locs = @consts[name]
|
382
|
-
ty = ty || Type.any # XXX: warn?
|
383
|
-
return ty, locs
|
384
|
-
end
|
385
|
-
|
386
|
-
def add_constant(name, ty, def_ep)
|
387
|
-
if @consts[name]
|
388
|
-
# XXX: warn!
|
389
|
-
_, eps = @consts[name]
|
390
|
-
@consts[name] = [ty, eps + [def_ep&.detailed_source_location]]
|
391
|
-
return
|
392
|
-
end
|
393
|
-
@consts[name] = [ty, [def_ep&.detailed_source_location]]
|
394
|
-
end
|
395
|
-
|
396
|
-
def consts
|
397
|
-
@consts.lazy.flat_map do |name, (ty, eps)|
|
398
|
-
eps.map do |ep|
|
399
|
-
[name, [ty, ep]]
|
400
|
-
end
|
401
|
-
end
|
402
|
-
end
|
403
|
-
|
404
|
-
def add_class_open(name, open_ep)
|
405
|
-
ty, eps = @consts[name]
|
406
|
-
raise "call this only if the class is opened more than once" if ty.nil?
|
407
|
-
@consts[name] = [ty, eps + [open_ep&.detailed_source_location]]
|
408
|
-
end
|
409
|
-
|
410
|
-
|
411
|
-
def adjust_substitution_for_module(mods, mid, mthd, subst, &blk)
|
412
|
-
mods.each do |mod_def, type_args,|
|
413
|
-
if mod_def.klass_obj.type_params && type_args
|
414
|
-
subst2 = {}
|
415
|
-
mod_def.klass_obj.type_params.zip(type_args) do |(tyvar, *), tyarg|
|
416
|
-
tyvar = Type::Var.new(tyvar)
|
417
|
-
subst2[tyvar] = tyarg.substitute(subst, Config.current.options[:type_depth_limit])
|
418
|
-
end
|
419
|
-
mod_def.adjust_substitution(false, mid, mthd, subst2, false, &blk)
|
420
|
-
end
|
421
|
-
end
|
422
|
-
end
|
423
|
-
|
424
|
-
def adjust_substitution(singleton, mid, mthd, subst, direct, &blk)
|
425
|
-
adjust_substitution_for_module(@modules[:before][singleton], mid, mthd, subst, &blk)
|
426
|
-
|
427
|
-
mthds = @methods[[singleton, mid]]
|
428
|
-
yield subst, direct if mthds&.include?(mthd)
|
429
|
-
|
430
|
-
adjust_substitution_for_module(@modules[:after][singleton], mid, mthd, subst, &blk)
|
431
|
-
end
|
432
|
-
|
433
|
-
def search_method(singleton, mid, visited, &blk)
|
434
|
-
# Currently, circular inclusion of modules is allowed
|
435
|
-
return if visited[self]
|
436
|
-
visited[self] = true
|
437
|
-
|
438
|
-
@modules[:before][singleton].each do |mod_def,|
|
439
|
-
mod_def.search_method(false, mid, visited, &blk)
|
440
|
-
end
|
441
|
-
|
442
|
-
mthds = @methods[[singleton, mid]]
|
443
|
-
yield mthds, @klass_obj, singleton if mthds
|
444
|
-
|
445
|
-
@modules[:after][singleton].each do |mod_def,|
|
446
|
-
mod_def.search_method(false, mid, visited, &blk)
|
447
|
-
end
|
448
|
-
end
|
449
|
-
|
450
|
-
def check_typed(mid, singleton, klass)
|
451
|
-
set = @methods[[singleton, mid]]
|
452
|
-
return nil unless set
|
453
|
-
set = set.select {|mdef| mdef.is_a?(klass) }
|
454
|
-
return nil if set.empty?
|
455
|
-
return set
|
456
|
-
end
|
457
|
-
|
458
|
-
def check_typed_method(mid, singleton)
|
459
|
-
check_typed(mid, singleton, TypedMethodDef)
|
460
|
-
end
|
461
|
-
|
462
|
-
def check_typed_attr(mid, singleton)
|
463
|
-
check_typed(mid, singleton, TypedAttrMethodDef)
|
464
|
-
end
|
465
|
-
|
466
|
-
def add_method(mid, singleton, mdef)
|
467
|
-
@methods[[singleton, mid]] ||= Utils::MutableSet.new
|
468
|
-
@methods[[singleton, mid]] << mdef
|
469
|
-
# Need to restart...?
|
470
|
-
end
|
471
|
-
|
472
|
-
def set_method(mid, singleton, mdef)
|
473
|
-
if mdef
|
474
|
-
@methods[[singleton, mid]] = Utils::MutableSet.new
|
475
|
-
@methods[[singleton, mid]] << mdef
|
476
|
-
else
|
477
|
-
@methods.delete([singleton, mid])
|
478
|
-
end
|
479
|
-
end
|
480
|
-
end
|
481
|
-
|
482
|
-
def mix_module(kind, mixing_mod, mixed_mod, type_args, singleton, caller_ep)
|
483
|
-
return if mixed_mod == Type.any
|
484
|
-
|
485
|
-
mixing_mod = @class_defs[mixing_mod.idx]
|
486
|
-
mixed_mod.each_child do |mixed_mod|
|
487
|
-
if mixed_mod.is_a?(Type::Class)
|
488
|
-
mixed_mod = @class_defs[mixed_mod.idx]
|
489
|
-
if mixed_mod && mixed_mod.kind == :module
|
490
|
-
mixing_mod.mix_module(kind, mixed_mod, type_args, singleton, caller_ep ? caller_ep.ctx.iseq.absolute_path : nil)
|
491
|
-
else
|
492
|
-
warn(caller_ep, "attempted to #{ kind == :after ? "include/extend" : "prepend" } non-module; ignored")
|
493
|
-
end
|
494
|
-
end
|
495
|
-
end
|
496
|
-
end
|
497
|
-
|
498
|
-
def cbase_path(cbase)
|
499
|
-
cbase && cbase.idx != 1 ? @class_defs[cbase.idx].name : []
|
500
|
-
end
|
501
|
-
|
502
|
-
def new_class(cbase, name, type_params, superclass, def_ep)
|
503
|
-
show_name = cbase_path(cbase) + [name]
|
504
|
-
idx = @class_defs.size
|
505
|
-
if superclass
|
506
|
-
superclass_def = @class_defs[superclass.idx] unless superclass == :__root__
|
507
|
-
@class_defs[idx] = ClassDef.new(:class, show_name, def_ep&.absolute_path, superclass_def)
|
508
|
-
superclass_def.subclasses << idx if superclass_def
|
509
|
-
klass = Type::Class.new(:class, idx, type_params, superclass, show_name)
|
510
|
-
@class_defs[idx].klass_obj = klass
|
511
|
-
cbase ||= klass # for bootstrap
|
512
|
-
add_constant(cbase, name, klass, def_ep)
|
513
|
-
return klass
|
514
|
-
else
|
515
|
-
# module
|
516
|
-
@class_defs[idx] = ClassDef.new(:module, show_name, def_ep&.absolute_path, nil)
|
517
|
-
mod = Type::Class.new(:module, idx, type_params, nil, show_name)
|
518
|
-
@class_defs[idx].klass_obj = mod
|
519
|
-
add_constant(cbase, name, mod, def_ep)
|
520
|
-
return mod
|
521
|
-
end
|
522
|
-
end
|
523
|
-
|
524
|
-
def add_superclass_type_args!(klass, tyargs)
|
525
|
-
klass.superclass_type_args = tyargs
|
526
|
-
end
|
527
|
-
|
528
|
-
def new_struct(ep)
|
529
|
-
return @struct_defs[ep] if @struct_defs[ep]
|
530
|
-
|
531
|
-
idx = @class_defs.size
|
532
|
-
superclass = Type::Builtin[:struct]
|
533
|
-
name = "AnonymousStruct_generated_#{ @anonymous_struct_gen_id += 1 }"
|
534
|
-
# Should we pass a superclass here?
|
535
|
-
@class_defs[idx] = ClassDef.new(:class, [name], ep.ctx.iseq.absolute_path, nil)
|
536
|
-
#@class_defs[superclass.idx].subclasses << idx # needed?
|
537
|
-
klass = Type::Class.new(:class, idx, [], superclass, name)
|
538
|
-
add_superclass_type_args!(klass, [Type.any])
|
539
|
-
@class_defs[idx].klass_obj = klass
|
540
|
-
|
541
|
-
@struct_defs[ep] = klass
|
542
|
-
|
543
|
-
klass
|
544
|
-
end
|
545
|
-
|
546
|
-
attr_accessor :namespace
|
547
|
-
|
548
|
-
def get_class_name(klass)
|
549
|
-
if klass == Type.any
|
550
|
-
"???"
|
551
|
-
else
|
552
|
-
path = @class_defs[klass.idx].name
|
553
|
-
if @namespace
|
554
|
-
i = 0
|
555
|
-
i += 1 while @namespace[i] && @namespace[i] == path[i]
|
556
|
-
if path[i]
|
557
|
-
path[i..].join("::")
|
558
|
-
else
|
559
|
-
path.last.to_s
|
560
|
-
end
|
561
|
-
else
|
562
|
-
#"::" + path.join("::")
|
563
|
-
path.join("::")
|
564
|
-
end
|
565
|
-
end
|
566
|
-
end
|
567
|
-
|
568
|
-
def adjust_substitution(klass, singleton, mid, mthd, subst, &blk)
|
569
|
-
direct = true
|
570
|
-
if klass.kind == :class
|
571
|
-
while klass != :__root__
|
572
|
-
class_def = @class_defs[klass.idx]
|
573
|
-
class_def.adjust_substitution(singleton, mid, mthd, subst, direct, &blk)
|
574
|
-
direct = false
|
575
|
-
if klass.superclass && klass.superclass_type_args
|
576
|
-
subst2 = {}
|
577
|
-
klass.superclass.type_params.zip(klass.superclass_type_args) do |(tyvar, *), tyarg|
|
578
|
-
tyvar = Type::Var.new(tyvar)
|
579
|
-
subst2[tyvar] = tyarg.substitute(subst, Config.current.options[:type_depth_limit])
|
580
|
-
end
|
581
|
-
subst = subst2
|
582
|
-
end
|
583
|
-
klass = klass.superclass
|
584
|
-
end
|
585
|
-
else
|
586
|
-
# module
|
587
|
-
class_def = @class_defs[klass.idx]
|
588
|
-
class_def.adjust_substitution(singleton, mid, mthd, subst, direct, &blk)
|
589
|
-
end
|
590
|
-
end
|
591
|
-
|
592
|
-
def traverse_subclasses(klass, &blk)
|
593
|
-
@class_defs[klass.idx].subclasses.each do |subclass|
|
594
|
-
yield @class_defs[subclass]
|
595
|
-
traverse_subclasses(@class_defs[subclass].klass_obj, &blk)
|
596
|
-
end
|
597
|
-
end
|
598
|
-
|
599
|
-
def search_method(klass, singleton, mid, &blk)
|
600
|
-
# XXX: support method alias correctly
|
601
|
-
klass_orig = klass
|
602
|
-
if klass.kind == :class
|
603
|
-
while klass != :__root__
|
604
|
-
class_def = @class_defs[klass.idx]
|
605
|
-
class_def.search_method(singleton, mid, {}, &blk)
|
606
|
-
klass = klass.superclass
|
607
|
-
end
|
608
|
-
else
|
609
|
-
# module
|
610
|
-
class_def = @class_defs[klass.idx]
|
611
|
-
class_def.search_method(singleton, mid, {}, &blk)
|
612
|
-
end
|
613
|
-
if singleton
|
614
|
-
search_method(Type::Builtin[klass_orig.kind], false, mid, &blk)
|
615
|
-
end
|
616
|
-
end
|
617
|
-
|
618
|
-
def get_method(klass, singleton, include_subclasses, mid)
|
619
|
-
if include_subclasses
|
620
|
-
subclasses_mthds = []
|
621
|
-
traverse_subclasses(klass) do |subclass|
|
622
|
-
subclass.search_method(singleton, mid, {}) do |mthds,|
|
623
|
-
subclasses_mthds.concat(mthds.to_a)
|
624
|
-
end
|
625
|
-
end
|
626
|
-
search_method(klass, singleton, mid) {|mthds,| return subclasses_mthds + mthds.to_a }
|
627
|
-
return subclasses_mthds
|
628
|
-
end
|
629
|
-
|
630
|
-
search_method(klass, singleton, mid) {|mthds,| return mthds }
|
631
|
-
end
|
632
|
-
|
633
|
-
def get_all_super_methods(klass, singleton, current_klass, mid)
|
634
|
-
hit = false
|
635
|
-
search_method(klass, singleton, mid) do |mthds, klass0, singleton0|
|
636
|
-
yield mthds, klass0, singleton0 if hit
|
637
|
-
hit = klass0 == current_klass
|
638
|
-
end
|
639
|
-
end
|
640
|
-
|
641
|
-
def get_super_method(ctx, singleton)
|
642
|
-
klass = ctx.cref.klass
|
643
|
-
mid = ctx.mid
|
644
|
-
if klass.kind == :class
|
645
|
-
klass = klass.superclass
|
646
|
-
while klass != :__root__
|
647
|
-
class_def = @class_defs[klass.idx]
|
648
|
-
mthd = class_def.get_method(mid, singleton)
|
649
|
-
return mthd if mthd
|
650
|
-
klass = klass.superclass
|
651
|
-
end
|
652
|
-
else
|
653
|
-
# module
|
654
|
-
class_def = @class_defs[klass.idx]
|
655
|
-
mthd = class_def.get_method(mid, singleton)
|
656
|
-
return mthd if mthd
|
657
|
-
end
|
658
|
-
nil
|
659
|
-
end
|
660
|
-
|
661
|
-
def get_all_methods(klass, singleton, _include_subclasses)
|
662
|
-
names = {}
|
663
|
-
|
664
|
-
if klass.kind == :class
|
665
|
-
while klass != :__root__
|
666
|
-
# TODO: module
|
667
|
-
@class_defs[klass.idx].methods.each_key do |singleton0, name|
|
668
|
-
if singleton == singleton0
|
669
|
-
names[name] = true
|
670
|
-
end
|
671
|
-
end
|
672
|
-
klass = klass.superclass
|
673
|
-
end
|
674
|
-
else
|
675
|
-
@class_defs[klass.idx].methods.each_key do |singleton0, name|
|
676
|
-
if singleton == singleton0
|
677
|
-
names[name] = true
|
678
|
-
end
|
679
|
-
end
|
680
|
-
end
|
681
|
-
|
682
|
-
names
|
683
|
-
end
|
684
|
-
|
685
|
-
def get_constant(klass, name)
|
686
|
-
if klass == Type.any
|
687
|
-
[Type.any, nil]
|
688
|
-
elsif klass.is_a?(Type::Class)
|
689
|
-
@class_defs[klass.idx].get_constant(name)
|
690
|
-
else
|
691
|
-
[Type.any, nil]
|
692
|
-
end
|
693
|
-
end
|
694
|
-
|
695
|
-
def search_constant(cref, name)
|
696
|
-
while cref != :bottom
|
697
|
-
ty, locs = get_constant(cref.klass, name)
|
698
|
-
return ty, locs if ty != Type.any
|
699
|
-
cref = cref.outer
|
700
|
-
end
|
701
|
-
|
702
|
-
return Type.any, nil
|
703
|
-
end
|
704
|
-
|
705
|
-
def add_constant(klass, name, value, def_ep)
|
706
|
-
if klass.is_a?(Type::Class)
|
707
|
-
@class_defs[klass.idx].add_constant(name, value, def_ep)
|
708
|
-
end
|
709
|
-
end
|
710
|
-
|
711
|
-
def check_typed_method(klass, mid, singleton)
|
712
|
-
@class_defs[klass.idx].check_typed_method(mid, singleton)
|
713
|
-
end
|
714
|
-
|
715
|
-
def check_typed_attr(klass, mid, singleton)
|
716
|
-
@class_defs[klass.idx].check_typed_attr(mid, singleton)
|
717
|
-
end
|
718
|
-
|
719
|
-
def add_method(klass, mid, singleton, mdef)
|
720
|
-
@class_defs[klass.idx].add_method(mid, singleton, mdef)
|
721
|
-
mdef
|
722
|
-
end
|
723
|
-
|
724
|
-
def set_method(klass, mid, singleton, mdef)
|
725
|
-
@class_defs[klass.idx].set_method(mid, singleton, mdef)
|
726
|
-
mdef
|
727
|
-
end
|
728
|
-
|
729
|
-
def add_attr_method(klass, mid, ivar, kind, pub_meth, ep)
|
730
|
-
if kind == :reader || kind == :accessor
|
731
|
-
typed_mdef = check_typed_attr(klass, mid, ep.ctx.cref.singleton)
|
732
|
-
unless typed_mdef
|
733
|
-
add_method(klass, mid, false, ExecutedAttrMethodDef.new(ivar, :reader, pub_meth, ep))
|
734
|
-
end
|
735
|
-
end
|
736
|
-
if kind == :writer || kind == :accessor
|
737
|
-
mid = :"#{ mid }="
|
738
|
-
typed_mdef = check_typed_attr(klass, mid, ep.ctx.cref.singleton)
|
739
|
-
unless typed_mdef
|
740
|
-
add_method(klass, mid, false, ExecutedAttrMethodDef.new(ivar, :writer, pub_meth, ep))
|
741
|
-
end
|
742
|
-
end
|
743
|
-
end
|
744
|
-
|
745
|
-
def add_typed_attr_method(klass, mdef)
|
746
|
-
name = mdef.ivar[1..-1]
|
747
|
-
name = mdef.kind == :writer ? :"#{ name }=" : name.to_sym
|
748
|
-
add_method(klass, name, false, mdef)
|
749
|
-
end
|
750
|
-
|
751
|
-
def add_iseq_method(klass, mid, iseq, cref, outer_ep, pub_meth)
|
752
|
-
add_method(klass, mid, false, ISeqMethodDef.new(iseq, cref, outer_ep, pub_meth))
|
753
|
-
end
|
754
|
-
|
755
|
-
def add_singleton_iseq_method(klass, mid, iseq, cref, outer_ep, pub_meth)
|
756
|
-
add_method(klass, mid, true, ISeqMethodDef.new(iseq, cref, outer_ep, pub_meth))
|
757
|
-
end
|
758
|
-
|
759
|
-
def set_custom_method(klass, mid, impl, pub_meth = true)
|
760
|
-
set_method(klass, mid, false, CustomMethodDef.new(impl, pub_meth))
|
761
|
-
end
|
762
|
-
|
763
|
-
def set_singleton_custom_method(klass, mid, impl, pub_meth = true)
|
764
|
-
set_method(klass, mid, true, CustomMethodDef.new(impl, pub_meth))
|
765
|
-
end
|
766
|
-
|
767
|
-
def alias_method(klass, singleton, alias_mid, orig_mid, ep)
|
768
|
-
if klass == Type.any
|
769
|
-
self
|
770
|
-
else
|
771
|
-
mdefs = get_method(klass, singleton, false, orig_mid) # XXX: include_subclass == false??
|
772
|
-
if mdefs
|
773
|
-
# dup is needed for `alias foo foo` (otherwise, "can't add a new key into hash during iteration" error occurs)
|
774
|
-
mdefs.dup.each do |mdef|
|
775
|
-
@class_defs[klass.idx].add_method(alias_mid, singleton, AliasMethodDef.new(orig_mid, mdef, ep))
|
776
|
-
end
|
777
|
-
end
|
778
|
-
end
|
779
|
-
end
|
780
|
-
|
781
|
-
def add_edge(ep, next_ep)
|
782
|
-
(@backward_edges[next_ep] ||= {})[ep] = true
|
783
|
-
end
|
784
|
-
|
785
|
-
def add_iseq_method_call!(iseq_mdef, ctx)
|
786
|
-
@iseq_method_to_ctxs[iseq_mdef] ||= Utils::MutableSet.new
|
787
|
-
@iseq_method_to_ctxs[iseq_mdef] << ctx
|
788
|
-
end
|
789
|
-
|
790
|
-
def add_executed_iseq(iseq)
|
791
|
-
@executed_iseqs << iseq
|
792
|
-
end
|
793
|
-
|
794
|
-
def add_callsite!(callee_ctx, caller_ep, caller_env, &ctn)
|
795
|
-
if callee_ctx.is_a?(Context)
|
796
|
-
@executed_iseqs << callee_ctx.iseq
|
797
|
-
callee_type = callee_ctx.iseq.type
|
798
|
-
if caller_ep.ctx.is_a?(Context) && (callee_type == :method || callee_type == :block)
|
799
|
-
caller_ep.ctx.iseq&.add_called_iseq(caller_ep.pc, callee_ctx.iseq)
|
800
|
-
end
|
801
|
-
end
|
802
|
-
|
803
|
-
@callsites[callee_ctx] ||= {}
|
804
|
-
@callsites[callee_ctx][caller_ep] = ctn
|
805
|
-
merge_return_env(caller_ep) {|env| env ? env.merge(caller_env) : caller_env }
|
806
|
-
|
807
|
-
ret_ty = @return_values[callee_ctx] ||= Type.bot
|
808
|
-
if ret_ty != Type.bot
|
809
|
-
ctn[ret_ty, caller_ep, @return_envs[caller_ep]]
|
810
|
-
end
|
811
|
-
end
|
812
|
-
|
813
|
-
def add_method_signature!(callee_ctx, msig)
|
814
|
-
if @method_signatures[callee_ctx]
|
815
|
-
@method_signatures[callee_ctx] = @method_signatures[callee_ctx].merge(msig)
|
816
|
-
else
|
817
|
-
@method_signatures[callee_ctx] = msig
|
818
|
-
end
|
819
|
-
end
|
820
|
-
|
821
|
-
def merge_return_env(caller_ep)
|
822
|
-
@return_envs[caller_ep] = yield @return_envs[caller_ep]
|
823
|
-
end
|
824
|
-
|
825
|
-
def add_return_value!(callee_ctx, ret_ty)
|
826
|
-
@return_values[callee_ctx] ||= Type.bot
|
827
|
-
@return_values[callee_ctx] = @return_values[callee_ctx].union(ret_ty)
|
828
|
-
|
829
|
-
@callsites[callee_ctx] ||= {}
|
830
|
-
@callsites[callee_ctx].each do |caller_ep, ctn|
|
831
|
-
ctn[ret_ty, caller_ep, @return_envs[caller_ep]]
|
832
|
-
end
|
833
|
-
end
|
834
|
-
|
835
|
-
def add_block_to_ctx!(block_body, ctx)
|
836
|
-
raise if !block_body.is_a?(Block)
|
837
|
-
@block_to_ctx[block_body] ||= Utils::MutableSet.new
|
838
|
-
@block_to_ctx[block_body] << ctx
|
839
|
-
end
|
840
|
-
|
841
|
-
def add_block_signature!(block_body, bsig)
|
842
|
-
if @block_signatures[block_body]
|
843
|
-
@block_signatures[block_body] = @block_signatures[block_body].merge(bsig)
|
844
|
-
else
|
845
|
-
@block_signatures[block_body] = bsig
|
846
|
-
end
|
847
|
-
end
|
848
|
-
|
849
|
-
class VarTable
|
850
|
-
Entry = Struct.new(:rbs_declared, :read_continuations, :type, :absolute_paths, :write_eps)
|
851
|
-
|
852
|
-
def initialize
|
853
|
-
@tbl = {}
|
854
|
-
end
|
855
|
-
|
856
|
-
def add_read!(site, ep, &ctn)
|
857
|
-
entry = @tbl[site] ||= Entry.new(false, {}, Type.bot, Utils::MutableSet.new, Utils::MutableSet.new)
|
858
|
-
entry.read_continuations[ep] = ctn
|
859
|
-
entry.absolute_paths << ep.ctx.iseq.absolute_path if ep.ctx.is_a?(Context)
|
860
|
-
ty = entry.type
|
861
|
-
ty = Type.nil if ty == Type.bot
|
862
|
-
ctn[ty, ep, entry.write_eps]
|
863
|
-
end
|
864
|
-
|
865
|
-
def add_write!(site, ty, ep, scratch)
|
866
|
-
entry = @tbl[site] ||= Entry.new(!ep, {}, Type.bot, Utils::MutableSet.new, Utils::MutableSet.new)
|
867
|
-
if ep
|
868
|
-
if entry.rbs_declared
|
869
|
-
unless Type.match?(ty, entry.type)
|
870
|
-
scratch.warn(ep, "inconsistent assignment to RBS-declared variable")
|
871
|
-
return
|
872
|
-
end
|
873
|
-
end
|
874
|
-
entry.absolute_paths << ep.ctx.iseq.absolute_path
|
875
|
-
entry.write_eps << ep
|
876
|
-
end
|
877
|
-
entry.type = entry.type.union(ty)
|
878
|
-
entry.read_continuations.each do |read_ep, ctn|
|
879
|
-
ctn[ty, read_ep, ep ? [ep] : []]
|
880
|
-
end
|
881
|
-
end
|
882
|
-
|
883
|
-
def dump
|
884
|
-
@tbl
|
885
|
-
end
|
886
|
-
end
|
887
|
-
|
888
|
-
def identify_class_for_ivar(recv, var)
|
889
|
-
klass, singleton = recv.method_dispatch_info
|
890
|
-
return nil unless klass
|
891
|
-
search_method(klass, singleton, var) do |mthds, klass0, singleton0|
|
892
|
-
if mthds.any? {|mthd| mthd.is_a?(AttrMethodDef) }
|
893
|
-
return klass0, singleton
|
894
|
-
end
|
895
|
-
end
|
896
|
-
return klass, singleton
|
897
|
-
end
|
898
|
-
|
899
|
-
def get_ivar(recv, var)
|
900
|
-
recv = recv.base_type while recv.respond_to?(:base_type)
|
901
|
-
|
902
|
-
klass, singleton = identify_class_for_ivar(recv, var.to_s[1..].to_sym) # search attr_reader
|
903
|
-
|
904
|
-
if klass
|
905
|
-
return @class_defs[klass.idx], singleton
|
906
|
-
else
|
907
|
-
return nil
|
908
|
-
end
|
909
|
-
end
|
910
|
-
|
911
|
-
def add_ivar_read!(recv, var, ep, &ctn)
|
912
|
-
recv.each_child do |recv|
|
913
|
-
class_def, singleton = get_ivar(recv, var)
|
914
|
-
next unless class_def
|
915
|
-
class_def.ivars.add_read!([singleton, var], ep, &ctn)
|
916
|
-
end
|
917
|
-
end
|
918
|
-
|
919
|
-
def add_ivar_write!(recv, var, ty, ep)
|
920
|
-
recv.each_child do |recv|
|
921
|
-
class_def, singleton = get_ivar(recv, var)
|
922
|
-
next unless class_def
|
923
|
-
site = [singleton, var]
|
924
|
-
class_def.ivars.add_write!(site, ty, ep, self)
|
925
|
-
end
|
926
|
-
end
|
927
|
-
|
928
|
-
def add_cvar_read!(klass, var, ep, &ctn)
|
929
|
-
klass.each_child do |klass|
|
930
|
-
next unless klass.is_a?(Type::Class)
|
931
|
-
class_def = @class_defs[klass.idx]
|
932
|
-
next unless class_def
|
933
|
-
class_def.cvars.add_read!(var, ep, &ctn)
|
934
|
-
end
|
935
|
-
end
|
936
|
-
|
937
|
-
def add_cvar_write!(klass, var, ty, ep)
|
938
|
-
klass.each_child do |klass|
|
939
|
-
next unless klass.is_a?(Type::Class)
|
940
|
-
class_def = @class_defs[klass.idx]
|
941
|
-
next unless class_def
|
942
|
-
class_def.cvars.add_write!(var, ty, ep, self)
|
943
|
-
end
|
944
|
-
end
|
945
|
-
|
946
|
-
def add_gvar_read!(var, ep, &ctn)
|
947
|
-
@gvar_table.add_read!(var, ep, &ctn)
|
948
|
-
end
|
949
|
-
|
950
|
-
def add_gvar_write!(var, ty, ep)
|
951
|
-
@gvar_table.add_write!(var, ty, ep, self)
|
952
|
-
end
|
953
|
-
|
954
|
-
def error(ep, msg)
|
955
|
-
p [ep.source_location, "[error] " + msg] if Config.current.verbose >= 2
|
956
|
-
@errors << [ep, "[error] " + msg]
|
957
|
-
end
|
958
|
-
|
959
|
-
def warn(ep, msg)
|
960
|
-
p [ep.source_location, "[warning] " + msg] if Config.current.verbose >= 2
|
961
|
-
@errors << [ep, "[warning] " + msg]
|
962
|
-
end
|
963
|
-
|
964
|
-
def reveal_type(ep, ty)
|
965
|
-
key = ep.source_location
|
966
|
-
puts "reveal:#{ ep.source_location }:#{ ty.screen_name(self) }" if Config.current.verbose >= 2
|
967
|
-
if @reveal_types[key]
|
968
|
-
@reveal_types[key] = @reveal_types[key].union(ty)
|
969
|
-
else
|
970
|
-
@reveal_types[key] = ty
|
971
|
-
end
|
972
|
-
end
|
973
|
-
|
974
|
-
def get_container_elem_types(env, ep, id)
|
975
|
-
if ep.outer
|
976
|
-
tmp_ep = ep
|
977
|
-
tmp_ep = tmp_ep.outer while tmp_ep.outer
|
978
|
-
env = @return_envs[tmp_ep]
|
979
|
-
end
|
980
|
-
env.get_container_elem_types(id)
|
981
|
-
end
|
982
|
-
|
983
|
-
def update_container_elem_types(env, ep, id, base_type)
|
984
|
-
if ep.outer
|
985
|
-
tmp_ep = ep
|
986
|
-
tmp_ep = tmp_ep.outer while tmp_ep.outer
|
987
|
-
merge_return_env(tmp_ep) do |menv|
|
988
|
-
elems = menv.get_container_elem_types(id)
|
989
|
-
elems = yield elems
|
990
|
-
menv = menv.deploy_type(id, elems)
|
991
|
-
gid = @alloc_site_to_global_id[id]
|
992
|
-
if gid
|
993
|
-
ty = globalize_type(elems.to_local_type(id, base_type), env, ep)
|
994
|
-
add_ivar_write!(*gid, ty, ep)
|
995
|
-
end
|
996
|
-
menv
|
997
|
-
end
|
998
|
-
env
|
999
|
-
else
|
1000
|
-
elems = env.get_container_elem_types(id)
|
1001
|
-
elems = yield elems
|
1002
|
-
env = env.deploy_type(id, elems)
|
1003
|
-
gid = @alloc_site_to_global_id[id]
|
1004
|
-
if gid
|
1005
|
-
ty = globalize_type(elems.to_local_type(id, base_type), env, ep)
|
1006
|
-
add_ivar_write!(*gid, ty, ep)
|
1007
|
-
end
|
1008
|
-
env
|
1009
|
-
end
|
1010
|
-
end
|
1011
|
-
|
1012
|
-
def get_array_elem_type(env, ep, id, idx = nil)
|
1013
|
-
elems = get_container_elem_types(env, ep, id)
|
1014
|
-
|
1015
|
-
if elems
|
1016
|
-
return elems[idx] || Type.nil if idx
|
1017
|
-
return elems.squash_or_any
|
1018
|
-
else
|
1019
|
-
Type.any
|
1020
|
-
end
|
1021
|
-
end
|
1022
|
-
|
1023
|
-
def get_hash_elem_type(env, ep, id, key_ty = nil)
|
1024
|
-
elems = get_container_elem_types(env, ep, id)
|
1025
|
-
|
1026
|
-
if elems
|
1027
|
-
elems[globalize_type(key_ty, env, ep) || Type.any]
|
1028
|
-
else
|
1029
|
-
Type.any
|
1030
|
-
end
|
1031
|
-
end
|
1032
|
-
|
1033
|
-
def type_profile(cancel_token = nil)
|
1034
|
-
cancel_token ||= Utils::TimerCancelToken.new(Config.current.max_sec)
|
1035
|
-
tick = Time.now
|
1036
|
-
iter_counter = 0
|
1037
|
-
stat_eps = Utils::MutableSet.new
|
1038
|
-
|
1039
|
-
prologue_ctx = Context.new(nil, nil, nil)
|
1040
|
-
prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
|
1041
|
-
prologue_env = Env.new(StaticEnv.new(Type.bot, Type.nil, false, true), [], [], Utils::HashWrapper.new({}))
|
1042
|
-
|
1043
|
-
until @entrypoints.empty?
|
1044
|
-
entrypoint = @entrypoints.shift
|
1045
|
-
if entrypoint.is_a?(String)
|
1046
|
-
file = entrypoint
|
1047
|
-
next if @loaded_files[File.expand_path(file)]
|
1048
|
-
iseq, = ISeq.compile(file)
|
1049
|
-
else
|
1050
|
-
iseq = entrypoint
|
1051
|
-
end
|
1052
|
-
|
1053
|
-
@loaded_files[iseq.absolute_path] = true
|
1054
|
-
ep, env = TypeProf.starting_state(iseq)
|
1055
|
-
merge_env(ep, env)
|
1056
|
-
add_callsite!(ep.ctx, prologue_ep, prologue_env) {|ty, ep| }
|
1057
|
-
|
1058
|
-
while true
|
1059
|
-
until @worklist.empty?
|
1060
|
-
ep = @worklist.deletemin
|
1061
|
-
|
1062
|
-
iter_counter += 1
|
1063
|
-
if Config.current.options[:show_indicator]
|
1064
|
-
tick2 = Time.now
|
1065
|
-
if tick2 - tick >= 1
|
1066
|
-
tick = tick2
|
1067
|
-
$stderr << "\rType Profiling... (%d instructions @ %s)\e[K" % [iter_counter, ep.source_location]
|
1068
|
-
$stderr.flush
|
1069
|
-
end
|
1070
|
-
end
|
1071
|
-
|
1072
|
-
if (Config.current.max_iter && Config.current.max_iter <= iter_counter) || cancel_token.cancelled?
|
1073
|
-
@terminated = true
|
1074
|
-
break
|
1075
|
-
end
|
1076
|
-
|
1077
|
-
stat_eps << ep
|
1078
|
-
step(ep)
|
1079
|
-
end
|
1080
|
-
|
1081
|
-
break if @terminated
|
1082
|
-
|
1083
|
-
break unless Config.current.options[:stub_execution]
|
1084
|
-
|
1085
|
-
begin
|
1086
|
-
iseq, (kind, dummy_continuation) = @pending_execution.first
|
1087
|
-
break if !iseq
|
1088
|
-
@pending_execution.delete(iseq)
|
1089
|
-
end while @executed_iseqs.include?(iseq)
|
1090
|
-
|
1091
|
-
puts "DEBUG: trigger stub execution (#{ iseq&.name || "(nil)" }): rest #{ @pending_execution.size }" if Config.current.verbose >= 2
|
1092
|
-
|
1093
|
-
break if !iseq
|
1094
|
-
case kind
|
1095
|
-
when :method
|
1096
|
-
meth, ep, env = dummy_continuation
|
1097
|
-
merge_env(ep, env)
|
1098
|
-
add_iseq_method_call!(meth, ep.ctx)
|
1099
|
-
|
1100
|
-
when :block
|
1101
|
-
blk, epenvs = dummy_continuation
|
1102
|
-
epenvs.each do |ep, env|
|
1103
|
-
merge_env(ep, env)
|
1104
|
-
add_block_to_ctx!(blk.block_body, ep.ctx)
|
1105
|
-
end
|
1106
|
-
end
|
1107
|
-
end
|
1108
|
-
end
|
1109
|
-
|
1110
|
-
$stderr.print "\r\e[K" if Config.current.options[:show_indicator]
|
1111
|
-
|
1112
|
-
stat_eps
|
1113
|
-
end
|
1114
|
-
|
1115
|
-
def report(stat_eps, output)
|
1116
|
-
Reporters.show_message(@terminated, output)
|
1117
|
-
|
1118
|
-
Reporters.show_error(@errors, @backward_edges, output)
|
1119
|
-
|
1120
|
-
Reporters.show_reveal_types(self, @reveal_types, output)
|
1121
|
-
|
1122
|
-
Reporters.show_gvars(self, @gvar_table, output)
|
1123
|
-
|
1124
|
-
RubySignatureExporter.new(self, @class_defs, @iseq_method_to_ctxs).show(stat_eps, output)
|
1125
|
-
end
|
1126
|
-
|
1127
|
-
def report_lsp
|
1128
|
-
errs = @errors.map do |ep, msg|
|
1129
|
-
[ep&.detailed_source_location, msg]
|
1130
|
-
end
|
1131
|
-
|
1132
|
-
res = RubySignatureExporter.new(self, @class_defs, @iseq_method_to_ctxs).show_lsp
|
1133
|
-
|
1134
|
-
path, loc = Config.current.options[:signature_help_loc]
|
1135
|
-
if path
|
1136
|
-
sig_help_res = []
|
1137
|
-
sig_help = @lsp_signature_help[loc]
|
1138
|
-
if sig_help
|
1139
|
-
recv = sig_help[:recv]
|
1140
|
-
mid = sig_help[:mid]
|
1141
|
-
singleton = sig_help[:singleton]
|
1142
|
-
mdefs = sig_help[:mdefs]
|
1143
|
-
node_id = sig_help[:node_id]
|
1144
|
-
mdefs.each do |mdef|
|
1145
|
-
case mdef
|
1146
|
-
when ISeqMethodDef
|
1147
|
-
ctxs = @iseq_method_to_ctxs[mdef]
|
1148
|
-
next unless ctxs
|
1149
|
-
|
1150
|
-
ctx = ctxs.find {|ctx| ctx.mid == mid } || ctxs.first
|
1151
|
-
|
1152
|
-
method_name = mid
|
1153
|
-
method_name = "self.#{ method_name }" if singleton
|
1154
|
-
|
1155
|
-
str = recv.screen_name(self)
|
1156
|
-
str += singleton ? "." : "#"
|
1157
|
-
str += method_name.to_s
|
1158
|
-
str += ": "
|
1159
|
-
sig, _, sig_help = show_method_signature(ctx)
|
1160
|
-
offset = str.size
|
1161
|
-
sig_help = sig_help.transform_values {|r| (r.begin + offset ... r.end + offset) }
|
1162
|
-
str += sig
|
1163
|
-
sig_help_res << [str, sig_help, node_id]
|
1164
|
-
when AliasMethodDef
|
1165
|
-
# TODO
|
1166
|
-
when TypedMethodDef
|
1167
|
-
mdef.rbs_source&.[](1)&.each do |rbs|
|
1168
|
-
# TODO: sig_help
|
1169
|
-
sig_help_res << [rbs, {}, node_id]
|
1170
|
-
end
|
1171
|
-
end
|
1172
|
-
end
|
1173
|
-
end
|
1174
|
-
end
|
1175
|
-
|
1176
|
-
{
|
1177
|
-
sigs: res,
|
1178
|
-
errors: errs,
|
1179
|
-
completion: @lsp_completion,
|
1180
|
-
signature_help: sig_help_res,
|
1181
|
-
}
|
1182
|
-
end
|
1183
|
-
|
1184
|
-
def globalize_type(ty, env, ep)
|
1185
|
-
if ep.outer
|
1186
|
-
tmp_ep = ep
|
1187
|
-
tmp_ep = tmp_ep.outer while tmp_ep.outer
|
1188
|
-
env = @return_envs[tmp_ep]
|
1189
|
-
end
|
1190
|
-
ty.globalize(env, {}, Config.current.options[:type_depth_limit])
|
1191
|
-
end
|
1192
|
-
|
1193
|
-
def localize_type(ty, env, ep, alloc_site = AllocationSite.new(ep))
|
1194
|
-
if ep.outer
|
1195
|
-
tmp_ep = ep
|
1196
|
-
tmp_ep = tmp_ep.outer while tmp_ep.outer
|
1197
|
-
target_env = @return_envs[tmp_ep]
|
1198
|
-
target_env, ty = ty.localize(target_env, alloc_site, Config.current.options[:type_depth_limit])
|
1199
|
-
merge_return_env(tmp_ep) do |env|
|
1200
|
-
env ? env.merge(target_env) : target_env
|
1201
|
-
end
|
1202
|
-
return env, ty
|
1203
|
-
else
|
1204
|
-
return ty.localize(env, alloc_site, Config.current.options[:type_depth_limit])
|
1205
|
-
end
|
1206
|
-
end
|
1207
|
-
|
1208
|
-
def pend_method_execution(iseq, meth, recv, mid, cref, ep)
|
1209
|
-
ctx = Context.new(iseq, cref, mid)
|
1210
|
-
ep = ExecutionPoint.new(ctx, 0, ep)
|
1211
|
-
locals = [Type.nil] * iseq.locals.size
|
1212
|
-
|
1213
|
-
fargs_format = iseq.fargs_format
|
1214
|
-
lead_num = fargs_format[:lead_num] || 0
|
1215
|
-
post_num = fargs_format[:post_num] || 0
|
1216
|
-
post_index = fargs_format[:post_start]
|
1217
|
-
rest_index = fargs_format[:rest_start]
|
1218
|
-
keyword = fargs_format[:keyword]
|
1219
|
-
kw_index = fargs_format[:kwbits] - keyword.size if keyword
|
1220
|
-
kwrest_index = fargs_format[:kwrest]
|
1221
|
-
block_index = fargs_format[:block_start]
|
1222
|
-
opt = fargs_format[:opt] || [0]
|
1223
|
-
|
1224
|
-
(lead_num + opt.size - 1).times {|i| locals[i] = Type.any }
|
1225
|
-
post_num.times {|i| locals[i + post_index] = Type.any } if post_index
|
1226
|
-
locals[rest_index] = Type.any if rest_index
|
1227
|
-
if keyword
|
1228
|
-
keyword.each_with_index do |kw, i|
|
1229
|
-
case
|
1230
|
-
when kw.is_a?(Symbol) # required keyword
|
1231
|
-
locals[kw_index + i] = Type.any
|
1232
|
-
when kw.size == 2 # optional keyword (default value is a literal)
|
1233
|
-
_key, default_ty = *kw
|
1234
|
-
default_ty = Type.guess_literal_type(default_ty)
|
1235
|
-
default_ty = default_ty.base_type if default_ty.is_a?(Type::Literal)
|
1236
|
-
locals[kw_index + i] = default_ty.union(Type.any)
|
1237
|
-
else # optional keyword (default value is an expression)
|
1238
|
-
locals[kw_index + i] = Type.any
|
1239
|
-
end
|
1240
|
-
end
|
1241
|
-
end
|
1242
|
-
locals[kwrest_index] = Type.any if kwrest_index
|
1243
|
-
locals[block_index] = Type.nil if block_index
|
1244
|
-
|
1245
|
-
env = Env.new(StaticEnv.new(recv, Type.nil, false, true), locals, [], Utils::HashWrapper.new({}))
|
1246
|
-
|
1247
|
-
if !@pending_execution[iseq] || @pending_execution[iseq][0] == :block
|
1248
|
-
@pending_execution[iseq] = [:method, [meth, ep, env]]
|
1249
|
-
end
|
1250
|
-
end
|
1251
|
-
|
1252
|
-
def pend_block_dummy_execution(blk, iseq, nep, nenv)
|
1253
|
-
@pending_execution[iseq] ||= [:block, [blk, {}]]
|
1254
|
-
if @pending_execution[iseq][0] == :block
|
1255
|
-
if @pending_execution[iseq][1][1][nep]
|
1256
|
-
@pending_execution[iseq][1][1][nep] = @pending_execution[iseq][1][1][nep].merge(nenv)
|
1257
|
-
else
|
1258
|
-
@pending_execution[iseq][1][1][nep] = nenv
|
1259
|
-
end
|
1260
|
-
else
|
1261
|
-
# XXX: what to do?
|
1262
|
-
end
|
1263
|
-
end
|
1264
|
-
|
1265
|
-
def get_instance_variable(recv, var, ep, env)
|
1266
|
-
add_ivar_read!(recv, var, ep) do |ty, ep, write_eps|
|
1267
|
-
alloc_site = AllocationSite.new(ep)
|
1268
|
-
nenv, ty = localize_type(ty, env, ep, alloc_site)
|
1269
|
-
case ty
|
1270
|
-
when Type::Local
|
1271
|
-
@alloc_site_to_global_id[ty.id] = [recv, var] # need overwrite check??
|
1272
|
-
end
|
1273
|
-
write_eps.each do |write_ep|
|
1274
|
-
ep.ctx.iseq.add_def_loc(ep.pc, write_ep.detailed_source_location)
|
1275
|
-
end
|
1276
|
-
yield ty, nenv
|
1277
|
-
end
|
1278
|
-
end
|
1279
|
-
|
1280
|
-
def set_instance_variable(recv, var, ty, ep, env)
|
1281
|
-
ty = globalize_type(ty, env, ep)
|
1282
|
-
add_ivar_write!(recv, var, ty, ep)
|
1283
|
-
end
|
1284
|
-
|
1285
|
-
def step(ep)
|
1286
|
-
env = @ep2env[ep]
|
1287
|
-
raise "nil env" unless env
|
1288
|
-
|
1289
|
-
insn = ep.ctx.iseq.insns[ep.pc]
|
1290
|
-
operands = insn.operands
|
1291
|
-
|
1292
|
-
if Config.current.verbose >= 2
|
1293
|
-
# XXX: more dedicated output
|
1294
|
-
puts "DEBUG: stack=%p" % [env.stack]
|
1295
|
-
puts "DEBUG: %s (%s) PC=%d insn=%s sp=%d" % [ep.source_location, ep.ctx.iseq.name, ep.pc, insn.insn, env.stack.size]
|
1296
|
-
end
|
1297
|
-
|
1298
|
-
case insn.insn
|
1299
|
-
when :_iseq_body_start
|
1300
|
-
# XXX: reconstruct and record the method signature
|
1301
|
-
iseq = ep.ctx.iseq
|
1302
|
-
lead_num = iseq.fargs_format[:lead_num] || 0
|
1303
|
-
opt = iseq.fargs_format[:opt] || [0]
|
1304
|
-
rest_start = iseq.fargs_format[:rest_start]
|
1305
|
-
post_start = iseq.fargs_format[:post_start]
|
1306
|
-
post_num = iseq.fargs_format[:post_num] || 0
|
1307
|
-
kw_start = iseq.fargs_format[:kwbits]
|
1308
|
-
keyword = iseq.fargs_format[:keyword]
|
1309
|
-
kw_start -= keyword.size if kw_start
|
1310
|
-
kw_rest = iseq.fargs_format[:kwrest]
|
1311
|
-
block_start = iseq.fargs_format[:block_start]
|
1312
|
-
|
1313
|
-
lead_tys = env.locals[0, lead_num].map {|ty| globalize_type(ty, env, ep) }
|
1314
|
-
opt_tys = opt.size > 1 ? env.locals[lead_num, opt.size - 1].map {|ty| globalize_type(ty, env, ep) } : []
|
1315
|
-
if rest_start # XXX:squash
|
1316
|
-
ty = globalize_type(env.locals[rest_start], env, ep)
|
1317
|
-
rest_ty = Type.bot
|
1318
|
-
ty.each_child_global do |ty|
|
1319
|
-
if ty.is_a?(Type::Array)
|
1320
|
-
rest_ty = rest_ty.union(ty.elems.squash)
|
1321
|
-
else
|
1322
|
-
# XXX: to_ary?
|
1323
|
-
rest_ty = rest_ty.union(ty)
|
1324
|
-
end
|
1325
|
-
end
|
1326
|
-
end
|
1327
|
-
post_tys = (post_start ? env.locals[post_start, post_num] : []).map {|ty| globalize_type(ty, env, ep) }
|
1328
|
-
if keyword
|
1329
|
-
kw_tys = []
|
1330
|
-
keyword.each_with_index do |kw, i|
|
1331
|
-
case
|
1332
|
-
when kw.is_a?(Symbol) # required keyword
|
1333
|
-
key = kw
|
1334
|
-
req = true
|
1335
|
-
when kw.size == 2 # optional keyword (default value is a literal)
|
1336
|
-
key, default_ty = *kw
|
1337
|
-
default_ty = Type.guess_literal_type(default_ty)
|
1338
|
-
default_ty = default_ty.base_type if default_ty.is_a?(Type::Literal)
|
1339
|
-
req = false
|
1340
|
-
else # optional keyword (default value is an expression)
|
1341
|
-
key, = kw
|
1342
|
-
req = false
|
1343
|
-
end
|
1344
|
-
ty = env.locals[kw_start + i]
|
1345
|
-
ty = ty.union(default_ty) if default_ty
|
1346
|
-
ty = globalize_type(ty, env, ep)
|
1347
|
-
kw_tys << [req, key, ty]
|
1348
|
-
end
|
1349
|
-
end
|
1350
|
-
kw_rest_ty = globalize_type(env.locals[kw_rest], env, ep) if kw_rest
|
1351
|
-
kw_rest_ty = nil if kw_rest_ty == Type.nil
|
1352
|
-
if block_start
|
1353
|
-
blk_ty = globalize_type(env.locals[block_start], env, ep)
|
1354
|
-
elsif iseq.type == :method
|
1355
|
-
blk_ty = env.static_env.blk_ty
|
1356
|
-
else
|
1357
|
-
blk_ty = Type.nil
|
1358
|
-
end
|
1359
|
-
msig = MethodSignature.new(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
|
1360
|
-
add_method_signature!(ep.ctx, msig)
|
1361
|
-
when :putspecialobject
|
1362
|
-
kind, = operands
|
1363
|
-
ty = case kind
|
1364
|
-
when 1 then Type::Instance.new(Type::Builtin[:vmcore])
|
1365
|
-
when 2, 3 # CBASE / CONSTBASE
|
1366
|
-
ep.ctx.cref.klass
|
1367
|
-
else
|
1368
|
-
raise NotImplementedError, "unknown special object: #{ type }"
|
1369
|
-
end
|
1370
|
-
env = env.push(ty)
|
1371
|
-
when :putnil
|
1372
|
-
env = env.push(Type.nil)
|
1373
|
-
when :putobject, :duparray
|
1374
|
-
obj, = operands
|
1375
|
-
env, ty = localize_type(Type.guess_literal_type(obj), env, ep)
|
1376
|
-
env = env.push(ty)
|
1377
|
-
when :putstring
|
1378
|
-
str, = operands
|
1379
|
-
ty = Type::Literal.new(str, Type::Instance.new(Type::Builtin[:str]))
|
1380
|
-
env = env.push(ty)
|
1381
|
-
when :putself
|
1382
|
-
ty = env.static_env.recv_ty
|
1383
|
-
if ty.is_a?(Type::Instance)
|
1384
|
-
klass = ty.klass
|
1385
|
-
if klass.type_params.size >= 1
|
1386
|
-
ty = Type::ContainerType.create_empty_instance(klass)
|
1387
|
-
env, ty = localize_type(ty, env, ep, AllocationSite.new(ep))
|
1388
|
-
else
|
1389
|
-
ty = Type::Instance.new(klass)
|
1390
|
-
end
|
1391
|
-
env, ty = localize_type(ty, env, ep)
|
1392
|
-
end
|
1393
|
-
env = env.push(ty)
|
1394
|
-
when :newarray, :newarraykwsplat
|
1395
|
-
len, = operands
|
1396
|
-
env, elems = env.pop(len)
|
1397
|
-
ty = Type::Array.new(Type::Array::Elements.new(elems), Type::Instance.new(Type::Builtin[:ary]))
|
1398
|
-
env, ty = localize_type(ty, env, ep)
|
1399
|
-
env = env.push(ty)
|
1400
|
-
when :newhash
|
1401
|
-
num, = operands
|
1402
|
-
env, tys = env.pop(num)
|
1403
|
-
|
1404
|
-
ty = Type.gen_hash do |h|
|
1405
|
-
tys.each_slice(2) do |k_ty, v_ty|
|
1406
|
-
k_ty = globalize_type(k_ty, env, ep)
|
1407
|
-
h[k_ty] = v_ty
|
1408
|
-
end
|
1409
|
-
end
|
1410
|
-
|
1411
|
-
env, ty = localize_type(ty, env, ep)
|
1412
|
-
env = env.push(ty)
|
1413
|
-
when :newhashfromarray
|
1414
|
-
raise NotImplementedError, "newhashfromarray"
|
1415
|
-
when :newrange
|
1416
|
-
env, tys = env.pop(2)
|
1417
|
-
# XXX: need generics
|
1418
|
-
env = env.push(Type::Instance.new(Type::Builtin[:range]))
|
1419
|
-
|
1420
|
-
when :concatstrings
|
1421
|
-
num, = operands
|
1422
|
-
env, = env.pop(num)
|
1423
|
-
env = env.push(Type::Instance.new(Type::Builtin[:str]))
|
1424
|
-
when :tostring, :anytostring
|
1425
|
-
env, (_ty1, _ty2,) = env.pop(2)
|
1426
|
-
env = env.push(Type::Instance.new(Type::Builtin[:str]))
|
1427
|
-
when :objtostring
|
1428
|
-
env, (_ty1,) = env.pop(1)
|
1429
|
-
env = env.push(Type::Instance.new(Type::Builtin[:str]))
|
1430
|
-
when :freezestring
|
1431
|
-
# do nothing
|
1432
|
-
when :toregexp
|
1433
|
-
_regexp_opt, str_count = operands
|
1434
|
-
env, tys = env.pop(str_count)
|
1435
|
-
# TODO: check if tys are all strings?
|
1436
|
-
env = env.push(Type::Instance.new(Type::Builtin[:regexp]))
|
1437
|
-
when :intern
|
1438
|
-
env, (ty,) = env.pop(1)
|
1439
|
-
# XXX check if ty is String
|
1440
|
-
env = env.push(Type::Instance.new(Type::Builtin[:sym]))
|
1441
|
-
|
1442
|
-
when :definemethod
|
1443
|
-
mid, iseq = operands
|
1444
|
-
do_define_iseq_method(ep, env, mid, iseq, nil)
|
1445
|
-
|
1446
|
-
when :definesmethod
|
1447
|
-
mid, iseq = operands
|
1448
|
-
env, (recv,) = env.pop(1)
|
1449
|
-
cref = ep.ctx.cref
|
1450
|
-
recv.each_child do |recv|
|
1451
|
-
if recv.is_a?(Type::Class)
|
1452
|
-
typed_mdef = check_typed_method(recv, mid, true)
|
1453
|
-
if typed_mdef
|
1454
|
-
mdef = ISeqMethodDef.new(iseq, cref, nil, env.static_env.pub_meth)
|
1455
|
-
typed_mdef.each do |typed_mdef|
|
1456
|
-
typed_mdef.do_match_iseq_mdef(mdef, recv, mid, env, ep, self)
|
1457
|
-
end
|
1458
|
-
else
|
1459
|
-
meth = add_singleton_iseq_method(recv, mid, iseq, cref, nil, env.static_env.pub_meth)
|
1460
|
-
end
|
1461
|
-
|
1462
|
-
pend_method_execution(iseq, meth, recv, mid, ep.ctx.cref, nil)
|
1463
|
-
else
|
1464
|
-
recv = Type.any # XXX: what to do?
|
1465
|
-
end
|
1466
|
-
end
|
1467
|
-
when :defineclass
|
1468
|
-
id, iseq, flags = operands
|
1469
|
-
env, (cbase, superclass) = env.pop(2)
|
1470
|
-
case flags & 7
|
1471
|
-
when 0, 2 # CLASS / MODULE
|
1472
|
-
type = (flags & 7) == 2 ? :module : :class
|
1473
|
-
existing_klass, = get_constant(cbase, id) # TODO: multiple return values
|
1474
|
-
if existing_klass.is_a?(Type::Class)
|
1475
|
-
# record re-opening location
|
1476
|
-
@class_defs[cbase.idx].add_class_open(id, ep)
|
1477
|
-
klass = existing_klass
|
1478
|
-
else
|
1479
|
-
if existing_klass != Type.any
|
1480
|
-
error(ep, "the class \"#{ id }\" is #{ existing_klass.screen_name(self) }")
|
1481
|
-
end
|
1482
|
-
if type == :class
|
1483
|
-
if superclass.is_a?(Type::Class)
|
1484
|
-
# okay
|
1485
|
-
elsif superclass == Type.any
|
1486
|
-
warn(ep, "superclass is any; Object is used instead")
|
1487
|
-
superclass = Type::Builtin[:obj]
|
1488
|
-
elsif superclass == Type.nil
|
1489
|
-
superclass = Type::Builtin[:obj]
|
1490
|
-
elsif superclass.is_a?(Type::Instance)
|
1491
|
-
warn(ep, "superclass is an instance; Object is used instead")
|
1492
|
-
superclass = Type::Builtin[:obj]
|
1493
|
-
else
|
1494
|
-
warn(ep, "superclass is not a class; Object is used instead")
|
1495
|
-
superclass = Type::Builtin[:obj]
|
1496
|
-
end
|
1497
|
-
else # module
|
1498
|
-
superclass = nil
|
1499
|
-
end
|
1500
|
-
if cbase.is_a?(Type::Class)
|
1501
|
-
klass = new_class(cbase, id, [], superclass, ep)
|
1502
|
-
if superclass
|
1503
|
-
add_superclass_type_args!(klass, superclass.type_params.map { Type.any })
|
1504
|
-
|
1505
|
-
# inherited hook
|
1506
|
-
aargs = ActualArguments.new([klass], nil, {}, Type.nil)
|
1507
|
-
do_send(superclass, :inherited, aargs, ep, env) {|_ret_ty, _ep| }
|
1508
|
-
end
|
1509
|
-
else
|
1510
|
-
klass = Type.any
|
1511
|
-
end
|
1512
|
-
end
|
1513
|
-
singleton = false
|
1514
|
-
when 1 # SINGLETON_CLASS
|
1515
|
-
singleton = true
|
1516
|
-
klass = cbase
|
1517
|
-
if klass.is_a?(Type::Class)
|
1518
|
-
elsif klass.is_a?(Type::Any)
|
1519
|
-
else
|
1520
|
-
warn(ep, "A singleton class is open for #{ klass.screen_name(self) }; handled as any")
|
1521
|
-
klass = Type.any
|
1522
|
-
end
|
1523
|
-
else
|
1524
|
-
raise NotImplementedError, "unknown defineclass flag: #{ flags }"
|
1525
|
-
end
|
1526
|
-
ncref = ep.ctx.cref.extend(klass, singleton)
|
1527
|
-
recv = singleton ? Type.any : klass
|
1528
|
-
blk = env.static_env.blk_ty
|
1529
|
-
nctx = Context.new(iseq, ncref, nil)
|
1530
|
-
nep = ExecutionPoint.new(nctx, 0, nil)
|
1531
|
-
locals = [Type.nil] * iseq.locals.size
|
1532
|
-
nenv = Env.new(StaticEnv.new(recv, blk, false, true), locals, [], Utils::HashWrapper.new({}))
|
1533
|
-
merge_env(nep, nenv)
|
1534
|
-
add_callsite!(nep.ctx, ep, env) do |ret_ty, ep, env|
|
1535
|
-
nenv, ret_ty = localize_type(ret_ty, env, ep)
|
1536
|
-
nenv = nenv.push(ret_ty)
|
1537
|
-
merge_env(ep.next, nenv)
|
1538
|
-
end
|
1539
|
-
return
|
1540
|
-
when :send
|
1541
|
-
env, recvs, mid, aargs = setup_actual_arguments(:method, operands, ep, env)
|
1542
|
-
recvs = Type.any if recvs == Type.bot
|
1543
|
-
do_send(recvs, mid, aargs, ep, env) do |ret_ty, ep, env|
|
1544
|
-
nenv, ret_ty, = localize_type(ret_ty, env, ep)
|
1545
|
-
nenv = nenv.push(ret_ty)
|
1546
|
-
merge_env(ep.next, nenv)
|
1547
|
-
end
|
1548
|
-
return
|
1549
|
-
when :recv_getlocal_send_branch
|
1550
|
-
getlocal_operands, send_operands, branch_operands = operands
|
1551
|
-
env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
|
1552
|
-
recvs = Type.any if recvs == Type.bot
|
1553
|
-
recvs.each_child do |recv|
|
1554
|
-
do_send(recv, mid, aargs, ep, env) do |ret_ty, ep, env|
|
1555
|
-
env, ret_ty, = localize_type(ret_ty, env, ep)
|
1556
|
-
|
1557
|
-
branchtype, target, = branch_operands
|
1558
|
-
# branchtype: :if or :unless or :nil
|
1559
|
-
ep_then = ep.next
|
1560
|
-
ep_else = ep.jump(target)
|
1561
|
-
|
1562
|
-
var_idx, _scope_idx, _escaped = getlocal_operands
|
1563
|
-
flow_env = env.local_update(-var_idx+2, recv)
|
1564
|
-
|
1565
|
-
case ret_ty
|
1566
|
-
when Type::Instance.new(Type::Builtin[:true])
|
1567
|
-
merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
|
1568
|
-
when Type::Instance.new(Type::Builtin[:false])
|
1569
|
-
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
1570
|
-
else
|
1571
|
-
merge_env(ep_then, env)
|
1572
|
-
merge_env(ep_else, env)
|
1573
|
-
end
|
1574
|
-
end
|
1575
|
-
end
|
1576
|
-
return
|
1577
|
-
when :arg_getlocal_send_branch
|
1578
|
-
getlocal_operands, send_operands, branch_operands = operands
|
1579
|
-
env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
|
1580
|
-
raise if aargs.lead_tys.size != 1
|
1581
|
-
aarg = aargs.lead_tys[0]
|
1582
|
-
aarg = Type.any if aarg == Type.bot
|
1583
|
-
recvs.each_child do |recv|
|
1584
|
-
aarg.each_child do |aarg|
|
1585
|
-
aargs_tmp = ActualArguments.new([aarg], nil, {}, aargs.blk_ty)
|
1586
|
-
do_send(recv, mid, aargs_tmp, ep, env) do |ret_ty, ep, env|
|
1587
|
-
env, ret_ty, = localize_type(ret_ty, env, ep)
|
1588
|
-
|
1589
|
-
branchtype, target, = branch_operands
|
1590
|
-
# branchtype: :if or :unless or :nil
|
1591
|
-
ep_then = ep.next
|
1592
|
-
ep_else = ep.jump(target)
|
1593
|
-
|
1594
|
-
var_idx, _scope_idx, _escaped = getlocal_operands
|
1595
|
-
flow_env = env.local_update(-var_idx+2, aarg)
|
1596
|
-
|
1597
|
-
case ret_ty
|
1598
|
-
when Type::Instance.new(Type::Builtin[:true])
|
1599
|
-
merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
|
1600
|
-
when Type::Instance.new(Type::Builtin[:false])
|
1601
|
-
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
1602
|
-
else
|
1603
|
-
merge_env(ep_then, env)
|
1604
|
-
merge_env(ep_else, env)
|
1605
|
-
end
|
1606
|
-
end
|
1607
|
-
end
|
1608
|
-
end
|
1609
|
-
return
|
1610
|
-
when :send_branch
|
1611
|
-
send_operands, branch_operands = operands
|
1612
|
-
env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
|
1613
|
-
recvs = Type.any if recvs == Type.bot
|
1614
|
-
do_send(recvs, mid, aargs, ep, env) do |ret_ty, ep, env|
|
1615
|
-
env, ret_ty, = localize_type(ret_ty, env, ep)
|
1616
|
-
|
1617
|
-
branchtype, target, = branch_operands
|
1618
|
-
# branchtype: :if or :unless or :nil
|
1619
|
-
ep_then = ep.next
|
1620
|
-
ep_else = ep.jump(target)
|
1621
|
-
|
1622
|
-
case ret_ty
|
1623
|
-
when Type::Instance.new(Type::Builtin[:true])
|
1624
|
-
merge_env(branchtype == :if ? ep_else : ep_then, env)
|
1625
|
-
when Type::Instance.new(Type::Builtin[:false])
|
1626
|
-
merge_env(branchtype == :if ? ep_then : ep_else, env)
|
1627
|
-
else
|
1628
|
-
merge_env(ep_then, env)
|
1629
|
-
merge_env(ep_else, env)
|
1630
|
-
end
|
1631
|
-
end
|
1632
|
-
return
|
1633
|
-
when :invokeblock
|
1634
|
-
env, recvs, mid, aargs = setup_actual_arguments(:block, operands, ep, env)
|
1635
|
-
blk = env.static_env.blk_ty
|
1636
|
-
case
|
1637
|
-
when blk == Type.nil
|
1638
|
-
env = env.push(Type.any)
|
1639
|
-
when blk == Type.any
|
1640
|
-
#warn(ep, "block is any")
|
1641
|
-
env = env.push(Type.any)
|
1642
|
-
else # Proc
|
1643
|
-
do_invoke_block(blk, aargs, ep, env) do |ret_ty, ep, env|
|
1644
|
-
nenv, ret_ty, = localize_type(ret_ty, env, ep)
|
1645
|
-
nenv = nenv.push(ret_ty)
|
1646
|
-
merge_env(ep.next, nenv)
|
1647
|
-
end
|
1648
|
-
return
|
1649
|
-
end
|
1650
|
-
when :invokesuper
|
1651
|
-
env, recv, _, aargs = setup_actual_arguments(:method, operands, ep, env)
|
1652
|
-
mid = ep.ctx.mid
|
1653
|
-
found = false
|
1654
|
-
recv.each_child_global do |recv|
|
1655
|
-
klass, singleton = recv.method_dispatch_info
|
1656
|
-
next unless klass
|
1657
|
-
get_all_super_methods(klass, singleton, ep.ctx.cref.klass, ep.ctx.mid) do |meths, klass|
|
1658
|
-
found = true
|
1659
|
-
meths.each do |meth|
|
1660
|
-
# XXX: this decomposition is really needed??
|
1661
|
-
# It calls `Object.new` with union receiver which causes an error, but
|
1662
|
-
# it may be a fault of builtin Object.new implementation.
|
1663
|
-
meth.do_send(recv, mid, aargs, ep, env, self) do |ret_ty, ep, env|
|
1664
|
-
nenv, ret_ty, = localize_type(ret_ty, env, ep)
|
1665
|
-
nenv = nenv.push(ret_ty)
|
1666
|
-
merge_env(ep.next, nenv)
|
1667
|
-
end
|
1668
|
-
end
|
1669
|
-
end
|
1670
|
-
end
|
1671
|
-
return if found
|
1672
|
-
error(ep, "no superclass method: #{ env.static_env.recv_ty.screen_name(self) }##{ mid }")
|
1673
|
-
env = env.push(Type.any)
|
1674
|
-
when :invokebuiltin
|
1675
|
-
raise NotImplementedError
|
1676
|
-
when :leave
|
1677
|
-
if env.stack.size != 1
|
1678
|
-
raise "stack inconsistency error: #{ env.stack.inspect }"
|
1679
|
-
end
|
1680
|
-
env, (ty,) = env.pop(1)
|
1681
|
-
ty = globalize_type(ty, env, ep)
|
1682
|
-
add_return_value!(ep.ctx, ty)
|
1683
|
-
return
|
1684
|
-
when :throw
|
1685
|
-
throwtype, = operands
|
1686
|
-
env, (ty,) = env.pop(1)
|
1687
|
-
_no_escape = !!(throwtype & 0x8000)
|
1688
|
-
throwtype = [:none, :return, :break, :next, :retry, :redo][throwtype & 0xff]
|
1689
|
-
case throwtype
|
1690
|
-
when :none
|
1691
|
-
|
1692
|
-
when :return
|
1693
|
-
ty = globalize_type(ty, env, ep)
|
1694
|
-
tmp_ep = ep
|
1695
|
-
tmp_ep = tmp_ep.outer while tmp_ep.outer
|
1696
|
-
add_return_value!(tmp_ep.ctx, ty)
|
1697
|
-
return
|
1698
|
-
when :break
|
1699
|
-
tmp_ep = ep
|
1700
|
-
while true
|
1701
|
-
if tmp_ep.ctx.iseq.type == :block
|
1702
|
-
tmp_ep = tmp_ep.outer
|
1703
|
-
nenv = @return_envs[tmp_ep].push(ty)
|
1704
|
-
merge_env(tmp_ep.next, nenv)
|
1705
|
-
break
|
1706
|
-
end
|
1707
|
-
_type, _iseq, cont, stack_depth = tmp_ep.ctx.iseq.catch_table[tmp_ep.pc]&.find {|type,| type == :break }
|
1708
|
-
if cont
|
1709
|
-
nenv = @return_envs[tmp_ep] || env
|
1710
|
-
nenv, = nenv.pop(nenv.stack.size - stack_depth)
|
1711
|
-
nenv = nenv.push(ty)
|
1712
|
-
tmp_ep = tmp_ep.jump(cont)
|
1713
|
-
merge_env(tmp_ep, nenv)
|
1714
|
-
break
|
1715
|
-
end
|
1716
|
-
tmp_ep = tmp_ep.outer
|
1717
|
-
end
|
1718
|
-
when :next, :redo
|
1719
|
-
# begin; rescue; next; end
|
1720
|
-
tmp_ep = ep.outer
|
1721
|
-
_type, _iseq, cont, stack_depth = tmp_ep.ctx.iseq.catch_table[tmp_ep.pc].find {|type,| type == throwtype }
|
1722
|
-
nenv = @return_envs[tmp_ep]
|
1723
|
-
nenv, = nenv.pop(nenv.stack.size - stack_depth)
|
1724
|
-
nenv = nenv.push(ty) if throwtype == :next
|
1725
|
-
tmp_ep = tmp_ep.jump(cont)
|
1726
|
-
merge_env(tmp_ep, nenv)
|
1727
|
-
when :retry
|
1728
|
-
tmp_ep = ep.outer
|
1729
|
-
_type, _iseq, cont, stack_depth = tmp_ep.ctx.iseq.catch_table[tmp_ep.pc].find {|type,| type == :retry }
|
1730
|
-
nenv = @return_envs[tmp_ep]
|
1731
|
-
nenv, = nenv.pop(nenv.stack.size - stack_depth)
|
1732
|
-
tmp_ep = tmp_ep.jump(cont)
|
1733
|
-
merge_env(tmp_ep, nenv)
|
1734
|
-
else
|
1735
|
-
p throwtype
|
1736
|
-
raise NotImplementedError
|
1737
|
-
end
|
1738
|
-
return
|
1739
|
-
when :once
|
1740
|
-
iseq, = operands
|
1741
|
-
|
1742
|
-
nctx = Context.new(iseq, ep.ctx.cref, ep.ctx.mid)
|
1743
|
-
nep = ExecutionPoint.new(nctx, 0, ep)
|
1744
|
-
raise if iseq.locals != []
|
1745
|
-
nenv = Env.new(env.static_env, [], [], nil)
|
1746
|
-
merge_env(nep, nenv)
|
1747
|
-
add_callsite!(nep.ctx, ep, env) do |ret_ty, ep, env|
|
1748
|
-
nenv, ret_ty = localize_type(ret_ty, env, ep)
|
1749
|
-
nenv = nenv.push(ret_ty)
|
1750
|
-
merge_env(ep.next, nenv)
|
1751
|
-
end
|
1752
|
-
return
|
1753
|
-
|
1754
|
-
when :branch # TODO: check how branchnil is used
|
1755
|
-
branchtype, target, = operands
|
1756
|
-
# branchtype: :if or :unless or :nil
|
1757
|
-
env, (ty,) = env.pop(1)
|
1758
|
-
ep_then = ep.next
|
1759
|
-
ep_else = ep.jump(target)
|
1760
|
-
|
1761
|
-
# TODO: it works for only simple cases: `x = nil; x || 1`
|
1762
|
-
# It would be good to merge "dup; branchif" to make it context-sensitive-like
|
1763
|
-
falsy = ty == Type.nil
|
1764
|
-
|
1765
|
-
merge_env(ep_then, env)
|
1766
|
-
merge_env(ep_else, env) unless branchtype == :if && falsy
|
1767
|
-
return
|
1768
|
-
when :jump
|
1769
|
-
target, = operands
|
1770
|
-
merge_env(ep.jump(target), env)
|
1771
|
-
return
|
1772
|
-
|
1773
|
-
when :setinstancevariable
|
1774
|
-
var, = operands
|
1775
|
-
env, (ty,) = env.pop(1)
|
1776
|
-
recv = env.static_env.recv_ty
|
1777
|
-
set_instance_variable(recv, var, ty, ep, env)
|
1778
|
-
|
1779
|
-
when :getinstancevariable
|
1780
|
-
var, = operands
|
1781
|
-
recv = env.static_env.recv_ty
|
1782
|
-
get_instance_variable(recv, var, ep, env) do |ty, nenv|
|
1783
|
-
merge_env(ep.next, nenv.push(ty))
|
1784
|
-
end
|
1785
|
-
return
|
1786
|
-
|
1787
|
-
when :setclassvariable
|
1788
|
-
var, = operands
|
1789
|
-
env, (ty,) = env.pop(1)
|
1790
|
-
cbase = ep.ctx.cref.klass
|
1791
|
-
ty = globalize_type(ty, env, ep)
|
1792
|
-
# TODO: if superclass has the variable, it should be updated
|
1793
|
-
add_cvar_write!(cbase, var, ty, ep)
|
1794
|
-
|
1795
|
-
when :getclassvariable
|
1796
|
-
var, = operands
|
1797
|
-
cbase = ep.ctx.cref.klass
|
1798
|
-
# TODO: if superclass has the variable, it should be read
|
1799
|
-
add_cvar_read!(cbase, var, ep) do |ty, ep|
|
1800
|
-
nenv, ty = localize_type(ty, env, ep)
|
1801
|
-
merge_env(ep.next, nenv.push(ty))
|
1802
|
-
end
|
1803
|
-
return
|
1804
|
-
|
1805
|
-
when :setglobal
|
1806
|
-
var, = operands
|
1807
|
-
env, (ty,) = env.pop(1)
|
1808
|
-
ty = globalize_type(ty, env, ep)
|
1809
|
-
add_gvar_write!(var, ty, ep)
|
1810
|
-
|
1811
|
-
when :getglobal
|
1812
|
-
var, = operands
|
1813
|
-
ty = Type.builtin_global_variable_type(var)
|
1814
|
-
if ty
|
1815
|
-
ty, locs = get_constant(Type::Builtin[:obj], ty) if ty.is_a?(Symbol)
|
1816
|
-
env, ty = localize_type(ty, env, ep)
|
1817
|
-
env = env.push(ty)
|
1818
|
-
else
|
1819
|
-
add_gvar_read!(var, ep) do |ty, ep|
|
1820
|
-
nenv, ty = localize_type(ty, env, ep)
|
1821
|
-
merge_env(ep.next, nenv.push(ty))
|
1822
|
-
end
|
1823
|
-
# need to return default nil of global variables
|
1824
|
-
return
|
1825
|
-
end
|
1826
|
-
|
1827
|
-
when :getlocal
|
1828
|
-
var_idx, scope_idx, _escaped = operands
|
1829
|
-
if scope_idx == 0
|
1830
|
-
ty = env.get_local(-var_idx+2)
|
1831
|
-
else
|
1832
|
-
tmp_ep = ep
|
1833
|
-
scope_idx.times do
|
1834
|
-
tmp_ep = tmp_ep.outer
|
1835
|
-
end
|
1836
|
-
ty = @return_envs[tmp_ep].get_local(-var_idx+2)
|
1837
|
-
end
|
1838
|
-
env = env.push(ty)
|
1839
|
-
when :getlocal_branch
|
1840
|
-
getlocal_operands, branch_operands = operands
|
1841
|
-
var_idx, _scope_idx, _escaped = getlocal_operands
|
1842
|
-
ret_ty = env.get_local(-var_idx+2)
|
1843
|
-
|
1844
|
-
branchtype, target, = branch_operands
|
1845
|
-
# branchtype: :if or :unless or :nil
|
1846
|
-
ep_then = ep.next
|
1847
|
-
ep_else = ep.jump(target)
|
1848
|
-
|
1849
|
-
var_idx, _scope_idx, _escaped = getlocal_operands
|
1850
|
-
|
1851
|
-
ret_ty.each_child do |ret_ty|
|
1852
|
-
flow_env = env.local_update(-var_idx+2, ret_ty)
|
1853
|
-
case ret_ty
|
1854
|
-
when Type.any
|
1855
|
-
merge_env(ep_then, flow_env)
|
1856
|
-
merge_env(ep_else, flow_env)
|
1857
|
-
when Type::Instance.new(Type::Builtin[:false]), Type.nil
|
1858
|
-
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
1859
|
-
else
|
1860
|
-
merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
|
1861
|
-
end
|
1862
|
-
end
|
1863
|
-
return
|
1864
|
-
when :getlocal_dup_branch
|
1865
|
-
getlocal_operands, _dup_operands, branch_operands = operands
|
1866
|
-
var_idx, _scope_idx, _escaped = getlocal_operands
|
1867
|
-
ret_ty = env.get_local(-var_idx+2)
|
1868
|
-
|
1869
|
-
branchtype, target, = branch_operands
|
1870
|
-
# branchtype: :if or :unless or :nil
|
1871
|
-
ep_then = ep.next
|
1872
|
-
ep_else = ep.jump(target)
|
1873
|
-
|
1874
|
-
ret_ty.each_child do |ret_ty|
|
1875
|
-
flow_env = env.local_update(-var_idx+2, ret_ty).push(ret_ty)
|
1876
|
-
case ret_ty
|
1877
|
-
when Type.any
|
1878
|
-
merge_env(ep_then, flow_env)
|
1879
|
-
merge_env(ep_else, flow_env)
|
1880
|
-
when Type::Instance.new(Type::Builtin[:false]), Type.nil
|
1881
|
-
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
1882
|
-
else
|
1883
|
-
merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
|
1884
|
-
end
|
1885
|
-
end
|
1886
|
-
return
|
1887
|
-
when :dup_setlocal_branch
|
1888
|
-
_dup_operands, setlocal_operands, branch_operands = operands
|
1889
|
-
|
1890
|
-
env, (ret_ty,) = env.pop(1)
|
1891
|
-
|
1892
|
-
var_idx, _scope_idx, _escaped = setlocal_operands
|
1893
|
-
|
1894
|
-
branchtype, target, = branch_operands
|
1895
|
-
# branchtype: :if or :unless or :nil
|
1896
|
-
ep_then = ep.next
|
1897
|
-
ep_else = ep.jump(target)
|
1898
|
-
|
1899
|
-
ret_ty.each_child do |ret_ty|
|
1900
|
-
flow_env = env.local_update(-var_idx+2, ret_ty)
|
1901
|
-
case ret_ty
|
1902
|
-
when Type.any
|
1903
|
-
merge_env(ep_then, flow_env)
|
1904
|
-
merge_env(ep_else, flow_env)
|
1905
|
-
when Type::Instance.new(Type::Builtin[:false]), Type.nil
|
1906
|
-
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
1907
|
-
else
|
1908
|
-
merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
|
1909
|
-
end
|
1910
|
-
end
|
1911
|
-
return
|
1912
|
-
when :getlocal_checkmatch_branch
|
1913
|
-
getlocal_operands, branch_operands = operands
|
1914
|
-
var_idx, _scope_idx, _escaped = getlocal_operands
|
1915
|
-
ret_ty = env.get_local(-var_idx+2)
|
1916
|
-
|
1917
|
-
env, (pattern_ty,) = env.pop(1)
|
1918
|
-
|
1919
|
-
branchtype, target, = branch_operands
|
1920
|
-
# branchtype: :if or :unless or :nil
|
1921
|
-
ep_then = ep.next
|
1922
|
-
ep_else = ep.jump(target)
|
1923
|
-
|
1924
|
-
var_idx, _scope_idx, _escaped = getlocal_operands
|
1925
|
-
|
1926
|
-
ret_ty.each_child do |ret_ty|
|
1927
|
-
flow_env = env.local_update(-var_idx+2, ret_ty)
|
1928
|
-
ret_ty = ret_ty.base_type if ret_ty.is_a?(Type::Symbol)
|
1929
|
-
ret_ty = ret_ty.base_type if ret_ty.is_a?(Type::Local)
|
1930
|
-
if ret_ty.is_a?(Type::Instance)
|
1931
|
-
if ret_ty.klass == pattern_ty # XXX: inheritance
|
1932
|
-
merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
|
1933
|
-
else
|
1934
|
-
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
1935
|
-
end
|
1936
|
-
else
|
1937
|
-
merge_env(ep_then, flow_env)
|
1938
|
-
merge_env(ep_else, flow_env)
|
1939
|
-
end
|
1940
|
-
end
|
1941
|
-
return
|
1942
|
-
when :setlocal, :setblockparam
|
1943
|
-
var_idx, scope_idx, _escaped = operands
|
1944
|
-
env, (ty,) = env.pop(1)
|
1945
|
-
if scope_idx == 0
|
1946
|
-
env = env.local_update(-var_idx+2, ty)
|
1947
|
-
else
|
1948
|
-
tmp_ep = ep
|
1949
|
-
scope_idx.times do
|
1950
|
-
tmp_ep = tmp_ep.outer
|
1951
|
-
end
|
1952
|
-
merge_return_env(tmp_ep) do |env|
|
1953
|
-
env.merge(env.local_update(-var_idx+2, ty))
|
1954
|
-
end
|
1955
|
-
end
|
1956
|
-
when :getconstant
|
1957
|
-
name, = operands
|
1958
|
-
env, (cbase, _allow_nil,) = env.pop(2)
|
1959
|
-
if cbase == Type.nil
|
1960
|
-
ty, locs = search_constant(ep.ctx.cref, name)
|
1961
|
-
env, ty = localize_type(ty, env, ep)
|
1962
|
-
env = env.push(ty)
|
1963
|
-
elsif cbase == Type.any
|
1964
|
-
env = env.push(Type.any) # XXX: warning needed?
|
1965
|
-
else
|
1966
|
-
ty, locs = get_constant(cbase, name)
|
1967
|
-
env, ty = localize_type(ty, env, ep)
|
1968
|
-
env = env.push(ty)
|
1969
|
-
end
|
1970
|
-
locs&.each do |loc|
|
1971
|
-
ep.ctx.iseq.add_def_loc(ep.pc, loc)
|
1972
|
-
end
|
1973
|
-
when :setconstant
|
1974
|
-
name, = operands
|
1975
|
-
env, (ty, cbase) = env.pop(2)
|
1976
|
-
old_ty, old_locs = get_constant(cbase, name)
|
1977
|
-
if old_locs == [nil] # RBS defined
|
1978
|
-
# TODO: it would be better to check if ty is consistent with old_ty (defined in RBS)
|
1979
|
-
# instead of extending the type
|
1980
|
-
env, old_ty = localize_type(globalize_type(old_ty, env, ep), env, ep)
|
1981
|
-
ty = ty.union(old_ty)
|
1982
|
-
elsif old_ty != Type.any # XXX???
|
1983
|
-
warn(ep, "already initialized constant #{ Type::Instance.new(cbase).screen_name(self) }::#{ name }")
|
1984
|
-
end
|
1985
|
-
ty.each_child do |ty|
|
1986
|
-
if ty.is_a?(Type::Class) && cbase.is_a?(Type::Class) && ty.superclass == Type::Builtin[:struct]
|
1987
|
-
@class_defs[ty.idx].name = cbase_path(cbase) + [name]
|
1988
|
-
end
|
1989
|
-
end
|
1990
|
-
add_constant(cbase, name, globalize_type(ty, env, ep), ep)
|
1991
|
-
|
1992
|
-
when :getspecial
|
1993
|
-
key, type = operands
|
1994
|
-
if type == 0
|
1995
|
-
case key
|
1996
|
-
when 0 # VM_SVAR_LASTLINE
|
1997
|
-
env = env.push(Type.any) # or String | NilClass only?
|
1998
|
-
when 1 # VM_SVAR_BACKREF ($~)
|
1999
|
-
merge_env(ep.next, env.push(Type::Instance.new(Type::Builtin[:matchdata])))
|
2000
|
-
# tentatively disabled; it is too conservative
|
2001
|
-
#merge_env(ep.next, env.push(Type.nil))
|
2002
|
-
return
|
2003
|
-
else # flip-flop
|
2004
|
-
env = env.push(Type.bool)
|
2005
|
-
end
|
2006
|
-
else
|
2007
|
-
# NTH_REF ($1, $2, ...) / BACK_REF ($&, $+, ...)
|
2008
|
-
merge_env(ep.next, env.push(Type::Instance.new(Type::Builtin[:str])))
|
2009
|
-
# tentatively disabled; it is too conservative
|
2010
|
-
#merge_env(ep.next, env.push(Type.nil))
|
2011
|
-
return
|
2012
|
-
end
|
2013
|
-
when :setspecial
|
2014
|
-
key, = operands
|
2015
|
-
if key >= 2 # flip-flop
|
2016
|
-
env, = env.pop(1)
|
2017
|
-
else
|
2018
|
-
raise "unknown setspecial key: #{ key }"
|
2019
|
-
end
|
2020
|
-
|
2021
|
-
when :dup
|
2022
|
-
env, (ty,) = env.pop(1)
|
2023
|
-
env = env.push(ty).push(ty)
|
2024
|
-
when :dup_branch
|
2025
|
-
_dup_operands, branch_operands = operands
|
2026
|
-
env, (ty,) = env.pop(1)
|
2027
|
-
|
2028
|
-
branchtype, target, = branch_operands
|
2029
|
-
# branchtype: :if or :unless or :nil
|
2030
|
-
ep_then = ep.next
|
2031
|
-
ep_else = ep.jump(target)
|
2032
|
-
|
2033
|
-
ty.each_child do |ty|
|
2034
|
-
flow_env = env.push(ty)
|
2035
|
-
case ty
|
2036
|
-
when Type.any
|
2037
|
-
merge_env(ep_then, flow_env)
|
2038
|
-
merge_env(ep_else, flow_env)
|
2039
|
-
when Type::Instance.new(Type::Builtin[:false]), Type.nil
|
2040
|
-
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
2041
|
-
else
|
2042
|
-
merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
|
2043
|
-
end
|
2044
|
-
end
|
2045
|
-
return
|
2046
|
-
when :duphash
|
2047
|
-
raw_hash, = operands
|
2048
|
-
ty = Type.guess_literal_type(raw_hash)
|
2049
|
-
env, ty = localize_type(globalize_type(ty, env, ep), env, ep)
|
2050
|
-
env = env.push(ty)
|
2051
|
-
when :dupn
|
2052
|
-
n, = operands
|
2053
|
-
_, tys = env.pop(n)
|
2054
|
-
tys.each {|ty| env = env.push(ty) }
|
2055
|
-
when :pop
|
2056
|
-
env, = env.pop(1)
|
2057
|
-
when :swap
|
2058
|
-
env, (a, b) = env.pop(2)
|
2059
|
-
env = env.push(a).push(b)
|
2060
|
-
when :reverse
|
2061
|
-
n, = operands
|
2062
|
-
env, tys = env.pop(n)
|
2063
|
-
tys.reverse_each {|ty| env = env.push(ty) }
|
2064
|
-
when :defined
|
2065
|
-
env, = env.pop(1)
|
2066
|
-
sym_ty = Type::Symbol.new(nil, Type::Instance.new(Type::Builtin[:sym]))
|
2067
|
-
env = env.push(Type.optional(sym_ty))
|
2068
|
-
when :checkmatch
|
2069
|
-
flag, = operands
|
2070
|
-
|
2071
|
-
# This flag means that the stack top is an array, and the check needs to be applied to find all elements
|
2072
|
-
# However, currently TypeProf uses very conservative interpretation (all check returns both true and false),
|
2073
|
-
# so we just ignore the flag now
|
2074
|
-
_array = flag & 4 != 0
|
2075
|
-
|
2076
|
-
case flag & 3
|
2077
|
-
when 1 # VM_CHECKMATCH_TYPE_WHEN
|
2078
|
-
env, = env.pop(2)
|
2079
|
-
env = env.push(Type.bool)
|
2080
|
-
when 2 # VM_CHECKMATCH_TYPE_CASE
|
2081
|
-
env, = env.pop(2)
|
2082
|
-
env = env.push(Type.bool)
|
2083
|
-
when 3 # VM_CHECKMATCH_TYPE_RESCUE
|
2084
|
-
env, = env.pop(2)
|
2085
|
-
env = env.push(Type.bool)
|
2086
|
-
else
|
2087
|
-
raise "unknown checkmatch flag"
|
2088
|
-
end
|
2089
|
-
when :checkkeyword
|
2090
|
-
env = env.push(Type.bool)
|
2091
|
-
when :adjuststack
|
2092
|
-
n, = operands
|
2093
|
-
env, _ = env.pop(n)
|
2094
|
-
when :nop
|
2095
|
-
when :setn
|
2096
|
-
idx, = operands
|
2097
|
-
if idx >= 1
|
2098
|
-
env, (ty,) = env.pop(1)
|
2099
|
-
env = env.setn(idx, ty).push(ty)
|
2100
|
-
end
|
2101
|
-
when :topn
|
2102
|
-
idx, = operands
|
2103
|
-
env = env.topn(idx)
|
2104
|
-
|
2105
|
-
when :splatarray
|
2106
|
-
env, (ty,) = env.pop(1)
|
2107
|
-
# XXX: vm_splat_array
|
2108
|
-
env = env.push(ty)
|
2109
|
-
when :expandarray
|
2110
|
-
num, flag = operands
|
2111
|
-
env, (ary,) = env.pop(1)
|
2112
|
-
splat = flag & 1 == 1
|
2113
|
-
from_head = flag & 2 == 0
|
2114
|
-
ary.each_child do |ary|
|
2115
|
-
case ary
|
2116
|
-
when Type::Local
|
2117
|
-
if ary.kind == Type::Array
|
2118
|
-
elems = get_container_elem_types(env, ep, ary.id)
|
2119
|
-
elems ||= Type::Array::Elements.new([], Type.any) # XXX
|
2120
|
-
else
|
2121
|
-
elems = Type::Array::Elements.new([], Type.any) # XXX
|
2122
|
-
end
|
2123
|
-
do_expand_array(ep, env, elems, num, splat, from_head)
|
2124
|
-
when Type::Any
|
2125
|
-
nnum = num
|
2126
|
-
nnum += 1 if splat
|
2127
|
-
nenv = env
|
2128
|
-
nnum.times do
|
2129
|
-
nenv = nenv.push(Type.any)
|
2130
|
-
end
|
2131
|
-
add_edge(ep, ep)
|
2132
|
-
merge_env(ep.next, nenv)
|
2133
|
-
else
|
2134
|
-
# TODO: call to_ary (or to_a?)
|
2135
|
-
elems = Type::Array::Elements.new([ary], Type.bot)
|
2136
|
-
do_expand_array(ep, env, elems, num, splat, from_head)
|
2137
|
-
end
|
2138
|
-
end
|
2139
|
-
return
|
2140
|
-
when :concatarray, :concattoarray
|
2141
|
-
env, (ary1, ary2) = env.pop(2)
|
2142
|
-
if ary1.is_a?(Type::Local) && ary1.kind == Type::Array
|
2143
|
-
elems1 = get_container_elem_types(env, ep, ary1.id)
|
2144
|
-
if ary2.is_a?(Type::Local) && ary2.kind == Type::Array
|
2145
|
-
elems2 = get_container_elem_types(env, ep, ary2.id)
|
2146
|
-
elems = Type::Array::Elements.new([], elems1.squash.union(elems2.squash))
|
2147
|
-
env = update_container_elem_types(env, ep, ary1.id, ary1.base_type) { elems }
|
2148
|
-
env = env.push(ary1)
|
2149
|
-
else
|
2150
|
-
elems = Type::Array::Elements.new([], Type.any)
|
2151
|
-
env = update_container_elem_types(env, ep, ary1.id, ary1.base_type) { elems }
|
2152
|
-
env = env.push(ary1)
|
2153
|
-
end
|
2154
|
-
else
|
2155
|
-
ty = Type::Array.new(Type::Array::Elements.new([], Type.any), Type::Instance.new(Type::Builtin[:ary]))
|
2156
|
-
env, ty = localize_type(ty, env, ep)
|
2157
|
-
env = env.push(ty)
|
2158
|
-
end
|
2159
|
-
when :pushtoarray
|
2160
|
-
num, = operands
|
2161
|
-
env, (ary, ty, *tys) = env.pop(num + 1)
|
2162
|
-
if ary.is_a?(Type::Local) && ary.kind == Type::Array
|
2163
|
-
tys.each {|ty0| ty = ty.union(ty0) }
|
2164
|
-
elems = get_container_elem_types(env, ep, ary.id)
|
2165
|
-
elems = Type::Array::Elements.new([], elems.squash.union(ty))
|
2166
|
-
env = update_container_elem_types(env, ep, ary.id, ary.base_type) { elems }
|
2167
|
-
env = env.push(ary)
|
2168
|
-
else
|
2169
|
-
elems = Type::Array::Elements.new([], Type.any)
|
2170
|
-
env = update_container_elem_types(env, ep, ary.id, ary.base_type) { elems }
|
2171
|
-
env = env.push(ary)
|
2172
|
-
end
|
2173
|
-
|
2174
|
-
when :checktype
|
2175
|
-
kind, = operands
|
2176
|
-
case kind
|
2177
|
-
when 5 then klass = :str # T_STRING
|
2178
|
-
when 7 then klass = :ary # T_ARRAY
|
2179
|
-
when 8 then klass = :hash # T_HASH
|
2180
|
-
else
|
2181
|
-
raise NotImplementedError
|
2182
|
-
end
|
2183
|
-
env, (val,) = env.pop(1)
|
2184
|
-
ty = Type.bot
|
2185
|
-
val.each_child do |val|
|
2186
|
-
#globalize_type(val, env, ep).each_child_global do |val|
|
2187
|
-
val = val.base_type while val.respond_to?(:base_type)
|
2188
|
-
case val
|
2189
|
-
when Type::Instance.new(Type::Builtin[klass])
|
2190
|
-
ty = ty.union(Type::Instance.new(Type::Builtin[:true]))
|
2191
|
-
when Type.any
|
2192
|
-
ty = Type.bool
|
2193
|
-
else
|
2194
|
-
ty = ty.union(Type::Instance.new(Type::Builtin[:false]))
|
2195
|
-
end
|
2196
|
-
end
|
2197
|
-
env = env.push(ty)
|
2198
|
-
else
|
2199
|
-
raise "Unknown insn: #{ insn.insn }"
|
2200
|
-
end
|
2201
|
-
|
2202
|
-
add_edge(ep, ep)
|
2203
|
-
merge_env(ep.next, env)
|
2204
|
-
|
2205
|
-
if ep.ctx.iseq.catch_table[ep.pc]
|
2206
|
-
ep.ctx.iseq.catch_table[ep.pc].each do |type, iseq, cont, stack_depth|
|
2207
|
-
next if type != :rescue && type != :ensure
|
2208
|
-
next if env.stack.size < stack_depth
|
2209
|
-
cont_ep = ep.jump(cont)
|
2210
|
-
cont_env, = env.pop(env.stack.size - stack_depth)
|
2211
|
-
nctx = Context.new(iseq, ep.ctx.cref, ep.ctx.mid)
|
2212
|
-
nep = ExecutionPoint.new(nctx, 0, cont_ep)
|
2213
|
-
locals = [Type.nil] * iseq.locals.size
|
2214
|
-
nenv = Env.new(env.static_env, locals, [], Utils::HashWrapper.new({}))
|
2215
|
-
merge_env(nep, nenv)
|
2216
|
-
add_callsite!(nep.ctx, cont_ep, cont_env) do |ret_ty, ep, env|
|
2217
|
-
nenv, ret_ty = localize_type(ret_ty, env, ep)
|
2218
|
-
nenv = nenv.push(ret_ty)
|
2219
|
-
merge_env(ep.jump(cont), nenv)
|
2220
|
-
end
|
2221
|
-
end
|
2222
|
-
end
|
2223
|
-
end
|
2224
|
-
|
2225
|
-
private def do_expand_array(ep, env, elems, num, splat, from_head)
|
2226
|
-
if from_head
|
2227
|
-
lead_tys, rest_ary_ty = elems.take_first(num)
|
2228
|
-
if splat
|
2229
|
-
env, local_ary_ty = localize_type(rest_ary_ty, env, ep)
|
2230
|
-
env = env.push(local_ary_ty)
|
2231
|
-
end
|
2232
|
-
lead_tys.reverse_each do |ty|
|
2233
|
-
env = env.push(ty)
|
2234
|
-
end
|
2235
|
-
else
|
2236
|
-
rest_ary_ty, following_tys = elems.take_last(num)
|
2237
|
-
following_tys.each do |ty|
|
2238
|
-
env = env.push(ty)
|
2239
|
-
end
|
2240
|
-
if splat
|
2241
|
-
env, local_ary_ty = localize_type(rest_ary_ty, env, ep)
|
2242
|
-
env = env.push(local_ary_ty)
|
2243
|
-
end
|
2244
|
-
end
|
2245
|
-
merge_env(ep.next, env)
|
2246
|
-
end
|
2247
|
-
|
2248
|
-
private def ruby_3_3_keywords?
|
2249
|
-
@ruby_3_3_keywords ||=
|
2250
|
-
RubyVM::InstructionSequence.compile("foo(*a, **b)").to_a.last[-2][1][:orig_argc] == 2
|
2251
|
-
end
|
2252
|
-
|
2253
|
-
private def type_to_keywords(ty, ep)
|
2254
|
-
case ty
|
2255
|
-
when Type::Hash
|
2256
|
-
ty.elems.to_keywords
|
2257
|
-
when Type::Union
|
2258
|
-
hash_elems = nil
|
2259
|
-
ty.elems&.each do |(container_kind, base_type), elems|
|
2260
|
-
if container_kind == Type::Hash
|
2261
|
-
elems.to_keywords
|
2262
|
-
hash_elems = hash_elems ? hash_elems.union(elems) : elems
|
2263
|
-
end
|
2264
|
-
end
|
2265
|
-
if hash_elems
|
2266
|
-
hash_elems.to_keywords
|
2267
|
-
else
|
2268
|
-
{ nil => Type.any }
|
2269
|
-
end
|
2270
|
-
else
|
2271
|
-
warn(ep, "non hash is passed to **kwarg?") unless ty == Type.any
|
2272
|
-
{ nil => Type.any }
|
2273
|
-
end
|
2274
|
-
end
|
2275
|
-
|
2276
|
-
private def setup_actual_arguments(kind, operands, ep, env)
|
2277
|
-
opt, blk_iseq = operands
|
2278
|
-
flags = opt[:flag]
|
2279
|
-
mid = opt[:mid]
|
2280
|
-
kw_arg = opt[:kw_arg]
|
2281
|
-
argc = opt[:orig_argc]
|
2282
|
-
argc += 1 if kind == :method # for the receiver
|
2283
|
-
argc += kw_arg.size if kw_arg
|
2284
|
-
|
2285
|
-
flag_args_splat = flags[ 0] != 0
|
2286
|
-
flag_args_blockarg = flags[ 1] != 0
|
2287
|
-
_flag_args_fcall = flags[ 2] != 0
|
2288
|
-
_flag_args_vcall = flags[ 3] != 0
|
2289
|
-
_flag_args_simple = flags[ 4] != 0 # unused in TP
|
2290
|
-
flags <<= 1 if RUBY_VERSION >= "3.3" # blockiseq flag was removed in 3.3
|
2291
|
-
flag_args_kwarg = flags[ 6] != 0
|
2292
|
-
flag_args_kw_splat = flags[ 7] != 0
|
2293
|
-
_flag_tailcall = flags[ 8] != 0
|
2294
|
-
_flag_super = flags[ 9] != 0
|
2295
|
-
_flag_zsuper = flags[10] != 0
|
2296
|
-
|
2297
|
-
argc += 1 if flag_args_blockarg
|
2298
|
-
|
2299
|
-
env, aargs = env.pop(argc)
|
2300
|
-
|
2301
|
-
recv = aargs.shift if kind == :method
|
2302
|
-
|
2303
|
-
if flag_args_blockarg
|
2304
|
-
blk_ty = aargs.pop
|
2305
|
-
elsif blk_iseq
|
2306
|
-
blk_ty = Type::Proc.new(ISeqBlock.new(blk_iseq, ep), Type::Instance.new(Type::Builtin[:proc]))
|
2307
|
-
else
|
2308
|
-
blk_ty = Type.nil
|
2309
|
-
end
|
2310
|
-
|
2311
|
-
new_blk_ty = Type.bot
|
2312
|
-
blk_ty.each_child do |blk_ty|
|
2313
|
-
case blk_ty
|
2314
|
-
when Type.nil
|
2315
|
-
when Type.any
|
2316
|
-
when Type::Proc
|
2317
|
-
when Type::Symbol
|
2318
|
-
blk_ty = Type::Proc.new(SymbolBlock.new(blk_ty.sym), Type::Instance.new(Type::Builtin[:proc]))
|
2319
|
-
else
|
2320
|
-
# XXX: attempt to call to_proc
|
2321
|
-
error(ep, "wrong argument type #{ blk_ty.screen_name(self) } (expected Proc)")
|
2322
|
-
blk_ty = Type.any
|
2323
|
-
end
|
2324
|
-
new_blk_ty = new_blk_ty.union(blk_ty)
|
2325
|
-
end
|
2326
|
-
blk_ty = new_blk_ty
|
2327
|
-
|
2328
|
-
if flag_args_splat
|
2329
|
-
if ruby_3_3_keywords?
|
2330
|
-
if flag_args_kw_splat
|
2331
|
-
kw_tys = type_to_keywords(globalize_type(aargs[-1], env, ep), ep)
|
2332
|
-
aargs = aargs[0..-2]
|
2333
|
-
else
|
2334
|
-
kw_tys = {}
|
2335
|
-
end
|
2336
|
-
rest_ty = aargs.last
|
2337
|
-
aargs = aargs[0..-2]
|
2338
|
-
else
|
2339
|
-
rest_ty = aargs.last
|
2340
|
-
aargs = aargs[0..-2]
|
2341
|
-
if flag_args_kw_splat
|
2342
|
-
# XXX: The types contained in ActualArguments are expected to be all local types.
|
2343
|
-
# This "globalize_type" breaks the invariant, and violates the assertion of Union#globalize that asserts @elems be nil.
|
2344
|
-
# To fix this issue fundamentally, ActualArguments should keep all arguments as-is (as like the VM does),
|
2345
|
-
# and globalize some types on the on-demand bases.
|
2346
|
-
ty = globalize_type(rest_ty, env, ep)
|
2347
|
-
if ty.is_a?(Type::Array)
|
2348
|
-
_, (ty,) = ty.elems.take_last(1)
|
2349
|
-
kw_tys = type_to_keywords(ty, ep)
|
2350
|
-
else
|
2351
|
-
raise NotImplementedError
|
2352
|
-
end
|
2353
|
-
else
|
2354
|
-
kw_tys = {}
|
2355
|
-
end
|
2356
|
-
end
|
2357
|
-
aargs = ActualArguments.new(aargs, rest_ty, kw_tys, blk_ty)
|
2358
|
-
elsif flag_args_kw_splat
|
2359
|
-
last = aargs.last
|
2360
|
-
# XXX: The types contained in ActualArguments are expected to be all local types.
|
2361
|
-
# This "globalize_type" breaks the invariant, and violates the assertion of Union#globalize that asserts @elems be nil.
|
2362
|
-
# To fix this issue fundamentally, ActualArguments should keep all arguments as-is (as like the VM does),
|
2363
|
-
# and globalize some types on the on-demand bases.
|
2364
|
-
ty = globalize_type(last, env, ep)
|
2365
|
-
case ty
|
2366
|
-
when Type::Hash
|
2367
|
-
aargs = aargs[0..-2]
|
2368
|
-
kw_tys = ty.elems.to_keywords
|
2369
|
-
when Type::Union
|
2370
|
-
hash_elems = nil
|
2371
|
-
ty.elems&.each do |(container_kind, base_type), elems|
|
2372
|
-
if container_kind == Type::Hash
|
2373
|
-
hash_elems = hash_elems ? hash_elems.union(elems) : elems
|
2374
|
-
end
|
2375
|
-
end
|
2376
|
-
if hash_elems
|
2377
|
-
kw_tys = hash_elems.to_keywords
|
2378
|
-
else
|
2379
|
-
kw_tys = { nil => Type.any }
|
2380
|
-
end
|
2381
|
-
when Type::Any
|
2382
|
-
aargs = aargs[0..-2]
|
2383
|
-
kw_tys = { nil => Type.any }
|
2384
|
-
else
|
2385
|
-
warn(ep, "non hash is passed to **kwarg?")
|
2386
|
-
kw_tys = { nil => Type.any }
|
2387
|
-
end
|
2388
|
-
aargs = ActualArguments.new(aargs, nil, kw_tys, blk_ty)
|
2389
|
-
elsif flag_args_kwarg
|
2390
|
-
kw_vals = aargs.pop(kw_arg.size)
|
2391
|
-
|
2392
|
-
kw_tys = {}
|
2393
|
-
kw_arg.zip(kw_vals) do |key, v_ty|
|
2394
|
-
kw_tys[key] = v_ty
|
2395
|
-
end
|
2396
|
-
|
2397
|
-
aargs = ActualArguments.new(aargs, nil, kw_tys, blk_ty)
|
2398
|
-
else
|
2399
|
-
aargs = ActualArguments.new(aargs, nil, {}, blk_ty)
|
2400
|
-
end
|
2401
|
-
|
2402
|
-
if blk_iseq
|
2403
|
-
# pending dummy execution
|
2404
|
-
nctx = Context.new(blk_iseq, ep.ctx.cref, ep.ctx.mid)
|
2405
|
-
nep = ExecutionPoint.new(nctx, 0, ep)
|
2406
|
-
nlocals = [Type.any] * blk_iseq.locals.size
|
2407
|
-
nsenv = StaticEnv.new(env.static_env.recv_ty, Type.any, env.static_env.mod_func, env.static_env.pub_meth)
|
2408
|
-
nenv = Env.new(nsenv, nlocals, [], nil)
|
2409
|
-
pend_block_dummy_execution(blk_ty, blk_iseq, nep, nenv)
|
2410
|
-
merge_return_env(ep) {|tenv| tenv ? tenv.merge(env) : env }
|
2411
|
-
end
|
2412
|
-
|
2413
|
-
aargs.node_id = opt[:node_id]
|
2414
|
-
|
2415
|
-
return env, recv, mid, aargs
|
2416
|
-
end
|
2417
|
-
|
2418
|
-
def do_send(recvs, mid, aargs, ep, env, &ctn)
|
2419
|
-
if mid == :__typeprof_lsp_completion
|
2420
|
-
names = {}
|
2421
|
-
recvs.each_child do |recv|
|
2422
|
-
case recv
|
2423
|
-
when Type::Void, Type::Any
|
2424
|
-
else
|
2425
|
-
klass, singleton, include_subclasses = recv.method_dispatch_info
|
2426
|
-
names.merge!(get_all_methods(klass, singleton, include_subclasses)) if klass
|
2427
|
-
end
|
2428
|
-
end
|
2429
|
-
@lsp_completion = names
|
2430
|
-
return ctn[Type.any, ep, env]
|
2431
|
-
end
|
2432
|
-
|
2433
|
-
recvs.each_child do |recv|
|
2434
|
-
case recv
|
2435
|
-
when Type::Void
|
2436
|
-
error(ep, "void's method is called: #{ globalize_type(recv, env, ep).screen_name(self) }##{ mid }")
|
2437
|
-
ctn[Type.any, ep, env]
|
2438
|
-
when Type::Any
|
2439
|
-
ctn[Type.any, ep, env]
|
2440
|
-
else
|
2441
|
-
klass, singleton, include_subclasses = recv.method_dispatch_info
|
2442
|
-
meths = get_method(klass, singleton, include_subclasses, mid) if klass
|
2443
|
-
if meths
|
2444
|
-
path, loc = Config.current.options[:signature_help_loc]
|
2445
|
-
if path && path == ep.ctx.iseq.path && mid != :inherited # XXX: too ad-hoc!!!
|
2446
|
-
path, code_range = ep&.detailed_source_location
|
2447
|
-
if path && code_range&.contain_loc?(loc)
|
2448
|
-
@lsp_signature_help[code_range] = {
|
2449
|
-
recv: recv,
|
2450
|
-
mid: mid,
|
2451
|
-
singleton: singleton,
|
2452
|
-
mdefs: meths,
|
2453
|
-
node_id: aargs.node_id,
|
2454
|
-
}
|
2455
|
-
end
|
2456
|
-
end
|
2457
|
-
meths.each do |meth|
|
2458
|
-
meth.do_send(recv, mid, aargs, ep, env, self, &ctn)
|
2459
|
-
end
|
2460
|
-
else
|
2461
|
-
meths = get_method(klass, singleton, include_subclasses, :method_missing) if klass
|
2462
|
-
if meths
|
2463
|
-
aargs = aargs.for_method_missing(Type::Symbol.new(mid, Type::Instance.new(Type::Builtin[:sym])))
|
2464
|
-
meths.each do |meth|
|
2465
|
-
meth.do_send(recv, :method_missing, aargs, ep, env, self, &ctn)
|
2466
|
-
end
|
2467
|
-
else
|
2468
|
-
error(ep, "undefined method: #{ globalize_type(recv, env, ep).screen_name(self) }##{ mid }")
|
2469
|
-
ctn[Type.any, ep, env]
|
2470
|
-
end
|
2471
|
-
end
|
2472
|
-
end
|
2473
|
-
end
|
2474
|
-
end
|
2475
|
-
|
2476
|
-
def do_invoke_block(blk, aargs, ep, env, replace_recv_ty: nil, replace_cref: nil, &ctn)
|
2477
|
-
blk.each_child do |blk|
|
2478
|
-
if blk.is_a?(Type::Proc)
|
2479
|
-
blk.block_body.do_call(aargs, ep, env, self, replace_recv_ty: replace_recv_ty, replace_cref: replace_cref, &ctn)
|
2480
|
-
else
|
2481
|
-
warn(ep, "non-proc is passed as a block") if blk != Type.any
|
2482
|
-
ctn[Type.any, ep, env]
|
2483
|
-
end
|
2484
|
-
end
|
2485
|
-
end
|
2486
|
-
|
2487
|
-
def do_define_iseq_method(ep, env, mid, iseq, outer_ep)
|
2488
|
-
cref = ep.ctx.cref
|
2489
|
-
if cref.klass.is_a?(Type::Class)
|
2490
|
-
typed_mdef = check_typed_method(cref.klass, mid, ep.ctx.cref.singleton)
|
2491
|
-
recv = cref.klass
|
2492
|
-
recv = Type::Instance.new(recv) unless ep.ctx.cref.singleton
|
2493
|
-
if typed_mdef
|
2494
|
-
mdef = ISeqMethodDef.new(iseq, cref, outer_ep, env.static_env.pub_meth)
|
2495
|
-
typed_mdef.each do |typed_mdef|
|
2496
|
-
typed_mdef.do_match_iseq_mdef(mdef, recv, mid, env, ep, self)
|
2497
|
-
end
|
2498
|
-
else
|
2499
|
-
if ep.ctx.cref.singleton
|
2500
|
-
meth = add_singleton_iseq_method(cref.klass, mid, iseq, cref, outer_ep, true)
|
2501
|
-
else
|
2502
|
-
meth = add_iseq_method(cref.klass, mid, iseq, cref, outer_ep, env.static_env.pub_meth)
|
2503
|
-
if env.static_env.mod_func
|
2504
|
-
add_singleton_iseq_method(cref.klass, mid, iseq, cref, outer_ep, true)
|
2505
|
-
end
|
2506
|
-
end
|
2507
|
-
end
|
2508
|
-
|
2509
|
-
pend_method_execution(iseq, meth, recv, mid, ep.ctx.cref, outer_ep)
|
2510
|
-
else
|
2511
|
-
# XXX: what to do?
|
2512
|
-
end
|
2513
|
-
end
|
2514
|
-
|
2515
|
-
def show_block_signature(blks)
|
2516
|
-
bsig = nil
|
2517
|
-
ret_ty = Type.bot
|
2518
|
-
|
2519
|
-
blks.each do |blk|
|
2520
|
-
blk.each_child_global do |blk|
|
2521
|
-
bsig0 = @block_signatures[blk.block_body]
|
2522
|
-
if bsig0
|
2523
|
-
if bsig
|
2524
|
-
bsig = bsig.merge(bsig0)
|
2525
|
-
else
|
2526
|
-
bsig = bsig0
|
2527
|
-
end
|
2528
|
-
end
|
2529
|
-
|
2530
|
-
@block_to_ctx[blk.block_body]&.each do |blk_ctx|
|
2531
|
-
ret_ty = ret_ty.union(@return_values[blk_ctx]) if @return_values[blk_ctx]
|
2532
|
-
end
|
2533
|
-
end
|
2534
|
-
end
|
2535
|
-
|
2536
|
-
bsig ||= BlockSignature.new([], [], nil, Type.nil)
|
2537
|
-
|
2538
|
-
bsig, = bsig.screen_name(nil, self)
|
2539
|
-
ret_ty = ret_ty.screen_name(self)
|
2540
|
-
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
2541
|
-
|
2542
|
-
bsig = bsig + " " if bsig != ""
|
2543
|
-
"{ #{ bsig }-> #{ ret_ty } }"
|
2544
|
-
end
|
2545
|
-
|
2546
|
-
def show_proc_signature(blks)
|
2547
|
-
farg_tys, ret_ty = nil, Type.bot
|
2548
|
-
|
2549
|
-
blks.each do |blk|
|
2550
|
-
blk.each_child_global do |blk|
|
2551
|
-
next if blk.block_body.is_a?(TypedBlock) # XXX: Support TypedBlock
|
2552
|
-
next unless @block_to_ctx[blk.block_body] # this occurs when screen_name is called before type-profiling finished (e.g., error message)
|
2553
|
-
@block_to_ctx[blk.block_body].each do |blk_ctx|
|
2554
|
-
if farg_tys
|
2555
|
-
if @method_signatures[blk_ctx]
|
2556
|
-
farg_tys = farg_tys.merge_as_block_arguments(@method_signatures[blk_ctx])
|
2557
|
-
else
|
2558
|
-
# this occurs when screen_name is called before type-profiling finished (e.g., error message)
|
2559
|
-
end
|
2560
|
-
else
|
2561
|
-
farg_tys = @method_signatures[blk_ctx]
|
2562
|
-
end
|
2563
|
-
|
2564
|
-
ret_ty = ret_ty.union(@return_values[blk_ctx]) if @return_values[blk_ctx]
|
2565
|
-
end
|
2566
|
-
end
|
2567
|
-
end
|
2568
|
-
|
2569
|
-
return Type.any.screen_name(self) if @types_being_shown.include?(farg_tys) || @types_being_shown.include?(ret_ty)
|
2570
|
-
|
2571
|
-
begin
|
2572
|
-
@types_being_shown << farg_tys << ret_ty
|
2573
|
-
farg_tys, = farg_tys ? farg_tys.screen_name(nil, self) : ["(unknown)"]
|
2574
|
-
ret_ty = ret_ty.screen_name(self)
|
2575
|
-
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
2576
|
-
|
2577
|
-
farg_tys = farg_tys + " " if farg_tys != ""
|
2578
|
-
"^#{ farg_tys }-> #{ ret_ty }"
|
2579
|
-
ensure
|
2580
|
-
@types_being_shown.pop(2)
|
2581
|
-
end
|
2582
|
-
end
|
2583
|
-
|
2584
|
-
def show_method_signature(ctx)
|
2585
|
-
farg_tys = @method_signatures[ctx]
|
2586
|
-
return nil unless farg_tys
|
2587
|
-
ret_ty = ctx.mid == :initialize ? Type::Void.new : @return_values[ctx] || Type.bot
|
2588
|
-
|
2589
|
-
untyped = farg_tys.include_untyped?(self) || ret_ty.include_untyped?(self)
|
2590
|
-
|
2591
|
-
farg_tys, ranges = farg_tys.screen_name(ctx.iseq, self)
|
2592
|
-
ret_ty = ret_ty.screen_name(self)
|
2593
|
-
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
2594
|
-
|
2595
|
-
return "#{ (farg_tys.empty? ? "" : "#{ farg_tys } ") }-> #{ ret_ty }", untyped, ranges
|
2596
|
-
end
|
2597
|
-
end
|
2598
|
-
end
|