typeprof 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (218) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +26 -0
  3. data/.gitignore +7 -0
  4. data/.gitmodules +6 -0
  5. data/Gemfile +12 -0
  6. data/Gemfile.lock +41 -0
  7. data/README.md +53 -0
  8. data/Rakefile +10 -0
  9. data/doc/doc.ja.md +415 -0
  10. data/doc/doc.md +429 -0
  11. data/doc/ppl2019.pdf +0 -0
  12. data/exe/typeprof +5 -0
  13. data/lib/typeprof.rb +13 -0
  14. data/lib/typeprof/analyzer.rb +1911 -0
  15. data/lib/typeprof/builtin.rb +554 -0
  16. data/lib/typeprof/cli.rb +110 -0
  17. data/lib/typeprof/container-type.rb +626 -0
  18. data/lib/typeprof/export.rb +203 -0
  19. data/lib/typeprof/import.rb +546 -0
  20. data/lib/typeprof/insns-def.rb +61 -0
  21. data/lib/typeprof/iseq.rb +387 -0
  22. data/lib/typeprof/method.rb +267 -0
  23. data/lib/typeprof/type.rb +1092 -0
  24. data/lib/typeprof/utils.rb +209 -0
  25. data/run.sh +3 -0
  26. data/smoke/alias.rb +30 -0
  27. data/smoke/alias2.rb +19 -0
  28. data/smoke/any-cbase.rb +5 -0
  29. data/smoke/any1.rb +15 -0
  30. data/smoke/any2.rb +17 -0
  31. data/smoke/arguments.rb +16 -0
  32. data/smoke/array-each.rb +14 -0
  33. data/smoke/array-each2.rb +15 -0
  34. data/smoke/array-each3.rb +15 -0
  35. data/smoke/array-ltlt.rb +13 -0
  36. data/smoke/array-ltlt2.rb +16 -0
  37. data/smoke/array-map.rb +11 -0
  38. data/smoke/array-map2.rb +10 -0
  39. data/smoke/array-map3.rb +22 -0
  40. data/smoke/array-mul.rb +17 -0
  41. data/smoke/array-plus1.rb +10 -0
  42. data/smoke/array-plus2.rb +15 -0
  43. data/smoke/array-pop.rb +11 -0
  44. data/smoke/array-replace.rb +12 -0
  45. data/smoke/array-s-aref.rb +11 -0
  46. data/smoke/array1.rb +26 -0
  47. data/smoke/array10.rb +14 -0
  48. data/smoke/array11.rb +13 -0
  49. data/smoke/array12.rb +24 -0
  50. data/smoke/array13.rb +30 -0
  51. data/smoke/array14.rb +13 -0
  52. data/smoke/array2.rb +27 -0
  53. data/smoke/array3.rb +25 -0
  54. data/smoke/array4.rb +14 -0
  55. data/smoke/array5.rb +13 -0
  56. data/smoke/array6.rb +14 -0
  57. data/smoke/array7.rb +13 -0
  58. data/smoke/array8.rb +13 -0
  59. data/smoke/array9.rb +12 -0
  60. data/smoke/attr.rb +28 -0
  61. data/smoke/backtrace.rb +32 -0
  62. data/smoke/block1.rb +22 -0
  63. data/smoke/block10.rb +14 -0
  64. data/smoke/block11.rb +39 -0
  65. data/smoke/block12.rb +22 -0
  66. data/smoke/block2.rb +14 -0
  67. data/smoke/block3.rb +38 -0
  68. data/smoke/block4.rb +18 -0
  69. data/smoke/block5.rb +18 -0
  70. data/smoke/block6.rb +20 -0
  71. data/smoke/block7.rb +20 -0
  72. data/smoke/block8.rb +27 -0
  73. data/smoke/block9.rb +12 -0
  74. data/smoke/blown.rb +12 -0
  75. data/smoke/break1.rb +18 -0
  76. data/smoke/break2.rb +15 -0
  77. data/smoke/case.rb +16 -0
  78. data/smoke/case2.rb +17 -0
  79. data/smoke/class.rb +5 -0
  80. data/smoke/class_instance_var.rb +9 -0
  81. data/smoke/class_method.rb +25 -0
  82. data/smoke/class_method2.rb +21 -0
  83. data/smoke/class_method3.rb +27 -0
  84. data/smoke/constant1.rb +38 -0
  85. data/smoke/constant2.rb +33 -0
  86. data/smoke/constant3.rb +9 -0
  87. data/smoke/constant4.rb +11 -0
  88. data/smoke/context-sensitive1.rb +12 -0
  89. data/smoke/cvar.rb +28 -0
  90. data/smoke/cvar2.rb +17 -0
  91. data/smoke/demo.rb +80 -0
  92. data/smoke/demo1.rb +16 -0
  93. data/smoke/demo10.rb +20 -0
  94. data/smoke/demo11.rb +11 -0
  95. data/smoke/demo2.rb +14 -0
  96. data/smoke/demo3.rb +16 -0
  97. data/smoke/demo4.rb +27 -0
  98. data/smoke/demo5.rb +13 -0
  99. data/smoke/demo6.rb +21 -0
  100. data/smoke/demo7.rb +14 -0
  101. data/smoke/demo8.rb +18 -0
  102. data/smoke/demo9.rb +18 -0
  103. data/smoke/dummy-execution1.rb +14 -0
  104. data/smoke/dummy-execution2.rb +16 -0
  105. data/smoke/ensure1.rb +20 -0
  106. data/smoke/enumerator.rb +15 -0
  107. data/smoke/expandarray1.rb +22 -0
  108. data/smoke/expandarray2.rb +23 -0
  109. data/smoke/fib.rb +28 -0
  110. data/smoke/flow1.rb +16 -0
  111. data/smoke/flow2.rb +14 -0
  112. data/smoke/flow3.rb +14 -0
  113. data/smoke/flow4.rb +5 -0
  114. data/smoke/flow5.rb +19 -0
  115. data/smoke/flow6.rb +19 -0
  116. data/smoke/flow7.rb +26 -0
  117. data/smoke/for.rb +9 -0
  118. data/smoke/freeze.rb +11 -0
  119. data/smoke/function.rb +16 -0
  120. data/smoke/gvar.rb +13 -0
  121. data/smoke/hash-fetch.rb +27 -0
  122. data/smoke/hash1.rb +18 -0
  123. data/smoke/hash2.rb +12 -0
  124. data/smoke/hash3.rb +13 -0
  125. data/smoke/hash4.rb +10 -0
  126. data/smoke/hash5.rb +14 -0
  127. data/smoke/inheritance.rb +34 -0
  128. data/smoke/inheritance2.rb +29 -0
  129. data/smoke/initialize.rb +26 -0
  130. data/smoke/instance_eval.rb +18 -0
  131. data/smoke/int_times.rb +14 -0
  132. data/smoke/integer.rb +10 -0
  133. data/smoke/ivar.rb +29 -0
  134. data/smoke/ivar2.rb +30 -0
  135. data/smoke/kernel-class.rb +12 -0
  136. data/smoke/keyword1.rb +11 -0
  137. data/smoke/keyword2.rb +11 -0
  138. data/smoke/keyword3.rb +12 -0
  139. data/smoke/keyword4.rb +11 -0
  140. data/smoke/keyword5.rb +15 -0
  141. data/smoke/kwsplat1.rb +42 -0
  142. data/smoke/kwsplat2.rb +12 -0
  143. data/smoke/manual-rbs.rb +27 -0
  144. data/smoke/manual-rbs.rbs +3 -0
  145. data/smoke/manual-rbs2.rb +20 -0
  146. data/smoke/manual-rbs2.rbs +8 -0
  147. data/smoke/masgn1.rb +13 -0
  148. data/smoke/masgn2.rb +17 -0
  149. data/smoke/masgn3.rb +12 -0
  150. data/smoke/method_in_branch.rb +22 -0
  151. data/smoke/module1.rb +29 -0
  152. data/smoke/module2.rb +28 -0
  153. data/smoke/module3.rb +33 -0
  154. data/smoke/module4.rb +29 -0
  155. data/smoke/module_function1.rb +28 -0
  156. data/smoke/module_function2.rb +28 -0
  157. data/smoke/multiple-include.rb +14 -0
  158. data/smoke/multiple-superclass.rb +16 -0
  159. data/smoke/next1.rb +20 -0
  160. data/smoke/next2.rb +16 -0
  161. data/smoke/object-send1.rb +22 -0
  162. data/smoke/once.rb +12 -0
  163. data/smoke/optional1.rb +13 -0
  164. data/smoke/optional2.rb +15 -0
  165. data/smoke/parameterizedd-self.rb +18 -0
  166. data/smoke/pathname1.rb +13 -0
  167. data/smoke/pathname2.rb +13 -0
  168. data/smoke/printf.rb +20 -0
  169. data/smoke/proc.rb +19 -0
  170. data/smoke/proc2.rb +16 -0
  171. data/smoke/proc3.rb +14 -0
  172. data/smoke/proc4.rb +11 -0
  173. data/smoke/range.rb +13 -0
  174. data/smoke/redo1.rb +21 -0
  175. data/smoke/redo2.rb +22 -0
  176. data/smoke/req-keyword.rb +12 -0
  177. data/smoke/rescue1.rb +20 -0
  178. data/smoke/rescue2.rb +22 -0
  179. data/smoke/respond_to.rb +22 -0
  180. data/smoke/rest-farg.rb +10 -0
  181. data/smoke/rest1.rb +25 -0
  182. data/smoke/rest2.rb +30 -0
  183. data/smoke/rest3.rb +36 -0
  184. data/smoke/rest4.rb +18 -0
  185. data/smoke/rest5.rb +10 -0
  186. data/smoke/rest6.rb +11 -0
  187. data/smoke/retry1.rb +20 -0
  188. data/smoke/return.rb +13 -0
  189. data/smoke/reveal.rb +13 -0
  190. data/smoke/singleton_class.rb +8 -0
  191. data/smoke/singleton_method.rb +9 -0
  192. data/smoke/step.rb +17 -0
  193. data/smoke/string-split.rb +11 -0
  194. data/smoke/struct.rb +9 -0
  195. data/smoke/struct2.rb +24 -0
  196. data/smoke/super1.rb +50 -0
  197. data/smoke/super2.rb +16 -0
  198. data/smoke/super3.rb +19 -0
  199. data/smoke/svar1.rb +12 -0
  200. data/smoke/tap1.rb +17 -0
  201. data/smoke/toplevel.rb +12 -0
  202. data/smoke/two-map.rb +17 -0
  203. data/smoke/type_var.rb +10 -0
  204. data/smoke/typed_method.rb +15 -0
  205. data/smoke/union-recv.rb +29 -0
  206. data/smoke/variadic1.rb.notyet +5 -0
  207. data/smoke/wrong-extend.rb +25 -0
  208. data/smoke/wrong-include.rb +26 -0
  209. data/smoke/wrong-rbs.rb +15 -0
  210. data/smoke/wrong-rbs.rbs +7 -0
  211. data/testbed/ao.rb +297 -0
  212. data/testbed/diff-lcs-entrypoint.rb +4 -0
  213. data/testbed/goodcheck-Gemfile.lock +51 -0
  214. data/tools/coverage.rb +14 -0
  215. data/tools/setup-insns-def.rb +30 -0
  216. data/tools/stackprof-wrapper.rb +10 -0
  217. data/typeprof.gemspec +24 -0
  218. metadata +262 -0
@@ -0,0 +1,1911 @@
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
+ end
38
+
39
+ class TypedContext
40
+ include Utils::StructuralEquality
41
+
42
+ def initialize(caller_ep, mid)
43
+ @caller_ep = caller_ep
44
+ @mid = mid
45
+ end
46
+
47
+ attr_reader :caller_ep, :mid
48
+ end
49
+
50
+ class ExecutionPoint
51
+ include Utils::StructuralEquality
52
+
53
+ def initialize(ctx, pc, outer)
54
+ @ctx = ctx
55
+ @pc = pc
56
+ @outer = outer
57
+ end
58
+
59
+ def key
60
+ [@ctx.iseq, @pc]
61
+ end
62
+
63
+ attr_reader :ctx, :pc, :outer
64
+
65
+ def jump(pc)
66
+ ExecutionPoint.new(@ctx, pc, @outer)
67
+ end
68
+
69
+ def next
70
+ ExecutionPoint.new(@ctx, @pc + 1, @outer)
71
+ end
72
+
73
+ def source_location
74
+ iseq = @ctx.iseq
75
+ if iseq
76
+ iseq.source_location(@pc)
77
+ else
78
+ "<builtin>"
79
+ end
80
+ end
81
+ end
82
+
83
+ class StaticEnv
84
+ include Utils::StructuralEquality
85
+
86
+ def initialize(recv_ty, blk_ty, mod_func)
87
+ @recv_ty = recv_ty
88
+ @blk_ty = blk_ty
89
+ @mod_func = mod_func
90
+ end
91
+
92
+ attr_reader :recv_ty, :blk_ty, :mod_func
93
+
94
+ def merge(other)
95
+ recv_ty = @recv_ty.union(other.recv_ty)
96
+ blk_ty = @blk_ty.union(other.blk_ty)
97
+ mod_func = @mod_func & other.mod_func # ??
98
+ StaticEnv.new(recv_ty, blk_ty, mod_func)
99
+ end
100
+ end
101
+
102
+ class Env
103
+ include Utils::StructuralEquality
104
+
105
+ def initialize(static_env, locals, stack, type_params)
106
+ @static_env = static_env
107
+ @locals = locals
108
+ @stack = stack
109
+ @type_params = type_params
110
+ end
111
+
112
+ attr_reader :static_env, :locals, :stack, :type_params
113
+
114
+ def merge(other)
115
+ raise if @locals.size != other.locals.size
116
+ raise if @stack.size != other.stack.size
117
+ static_env = @static_env.merge(other.static_env)
118
+ locals = []
119
+ @locals.zip(other.locals) {|ty1, ty2| locals << ty1.union(ty2) }
120
+ stack = []
121
+ @stack.zip(other.stack) {|ty1, ty2| stack << ty1.union(ty2) }
122
+ if @type_params
123
+ raise if !other.type_params
124
+ if @type_params == other.type_params
125
+ type_params = @type_params
126
+ else
127
+ type_params = @type_params.internal_hash.dup
128
+ other.type_params.internal_hash.each do |id, elems|
129
+ elems2 = type_params[id]
130
+ if elems2
131
+ type_params[id] = elems.union(elems2) if elems != elems2
132
+ else
133
+ type_params[id] = elems
134
+ end
135
+ end
136
+ type_params = Utils::HashWrapper.new(type_params)
137
+ end
138
+ else
139
+ raise if other.type_params
140
+ end
141
+ Env.new(static_env, locals, stack, type_params)
142
+ end
143
+
144
+ def push(*tys)
145
+ tys.each do |ty|
146
+ raise "nil cannot be pushed to the stack" if ty.nil?
147
+ ty.each_child do |ty|
148
+ raise "Array cannot be pushed to the stack" if ty.is_a?(Type::Array)
149
+ raise "Hash cannot be pushed to the stack" if ty.is_a?(Type::Hash)
150
+ end
151
+ end
152
+ Env.new(@static_env, @locals, @stack + tys, @type_params)
153
+ end
154
+
155
+ def pop(n)
156
+ stack = @stack.dup
157
+ tys = stack.pop(n)
158
+ nenv = Env.new(@static_env, @locals, stack, @type_params)
159
+ return nenv, tys
160
+ end
161
+
162
+ def setn(i, ty)
163
+ stack = Utils.array_update(@stack, -i, ty)
164
+ Env.new(@static_env, @locals, stack, @type_params)
165
+ end
166
+
167
+ def topn(i)
168
+ push(@stack[-i - 1])
169
+ end
170
+
171
+ def get_local(idx)
172
+ @locals[idx]
173
+ end
174
+
175
+ def local_update(idx, ty)
176
+ Env.new(@static_env, Utils.array_update(@locals, idx, ty), @stack, @type_params)
177
+ end
178
+
179
+ def deploy_array_type(alloc_site, elems, base_ty)
180
+ local_ty = Type::LocalArray.new(alloc_site, base_ty)
181
+ type_params = Utils::HashWrapper.new(@type_params.internal_hash.merge({ alloc_site => elems }))
182
+ nenv = Env.new(@static_env, @locals, @stack, type_params)
183
+ return nenv, local_ty
184
+ end
185
+
186
+ def deploy_hash_type(alloc_site, elems, base_ty)
187
+ local_ty = Type::LocalHash.new(alloc_site, base_ty)
188
+ type_params = Utils::HashWrapper.new(@type_params.internal_hash.merge({ alloc_site => elems }))
189
+ nenv = Env.new(@static_env, @locals, @stack, type_params)
190
+ return nenv, local_ty
191
+ end
192
+
193
+ def get_container_elem_types(id)
194
+ @type_params.internal_hash[id]
195
+ end
196
+
197
+ def update_container_elem_types(id, elems)
198
+ type_params = Utils::HashWrapper.new(@type_params.internal_hash.merge({ id => elems }))
199
+ Env.new(@static_env, @locals, @stack, type_params)
200
+ end
201
+
202
+ def enable_module_function
203
+ senv = StaticEnv.new(@static_env.recv_ty, @static_env.blk_ty, true)
204
+ Env.new(senv, @locals, @stack, @type_params)
205
+ end
206
+
207
+ def replace_recv_ty(ty)
208
+ senv = StaticEnv.new(ty, @static_env.blk_ty, @static_env.mod_func)
209
+ Env.new(senv, @locals, @stack, @type_params)
210
+ end
211
+
212
+ def inspect
213
+ "Env[#{ @static_env.inspect }, locals:#{ @locals.inspect }, stack:#{ @stack.inspect }, type_params:#{ (@type_params&.internal_hash).inspect }]"
214
+ end
215
+ end
216
+
217
+ class Scratch
218
+ def inspect
219
+ "#<Scratch>"
220
+ end
221
+
222
+ def initialize
223
+ @worklist = Utils::WorkList.new
224
+
225
+ @ep2env = {}
226
+
227
+ @class_defs = {}
228
+ @struct_defs = {}
229
+
230
+ @iseq_method_to_ctxs = {}
231
+
232
+ @alloc_site_to_global_id = {}
233
+
234
+ @callsites, @return_envs, @sig_fargs, @sig_ret, @yields = {}, {}, {}, {}, {}
235
+ @block_to_ctx = {}
236
+ @gvar_table = VarTable.new
237
+
238
+ @include_relations = {}
239
+
240
+ @errors = []
241
+ @reveal_types = {}
242
+ @backward_edges = {}
243
+
244
+ @pending_execution = {}
245
+ @executed_iseqs = Utils::MutableSet.new
246
+
247
+ @loaded_features = {}
248
+
249
+ @rbs_reader = RBSReader.new
250
+ end
251
+
252
+ attr_reader :return_envs, :loaded_features, :rbs_reader
253
+
254
+ def get_env(ep)
255
+ @ep2env[ep]
256
+ end
257
+
258
+ def merge_env(ep, env)
259
+ # TODO: this is wrong; it include not only proceeds but also indirect propagation like out-of-block variable modification
260
+ #add_edge(ep, @ep)
261
+ env2 = @ep2env[ep]
262
+ if env2
263
+ nenv = env2.merge(env)
264
+ if !nenv.eql?(env2) && !@worklist.member?(ep)
265
+ @worklist.insert(ep.key, ep)
266
+ end
267
+ @ep2env[ep] = nenv
268
+ else
269
+ @worklist.insert(ep.key, ep)
270
+ @ep2env[ep] = env
271
+ end
272
+ end
273
+
274
+ attr_reader :class_defs
275
+
276
+ class ClassDef # or ModuleDef
277
+ def initialize(kind, name, superclass)
278
+ @kind = kind
279
+ @superclass = superclass
280
+ @modules = { true => [], false => [] }
281
+ @name = name
282
+ @consts = {}
283
+ @methods = {}
284
+ @ivars = VarTable.new
285
+ @cvars = VarTable.new
286
+ end
287
+
288
+ attr_reader :kind, :modules, :methods, :superclass, :ivars, :cvars
289
+ attr_accessor :name, :klass_obj
290
+
291
+ def include_module(mod, visible)
292
+ # XXX: need to check if mod is already included by the ancestors?
293
+ unless @modules[false].include?([visible, mod])
294
+ @modules[false] << [visible, mod]
295
+ end
296
+ end
297
+
298
+ def extend_module(mod, visible)
299
+ # XXX: need to check if mod is already included by the ancestors?
300
+ unless @modules[true].include?([visible, mod])
301
+ @modules[true] << [visible, mod]
302
+ end
303
+ end
304
+
305
+ def get_constant(name)
306
+ @consts[name] || Type.any # XXX: warn?
307
+ end
308
+
309
+ def add_constant(name, ty)
310
+ if @consts[name]
311
+ # XXX: warn!
312
+ end
313
+ @consts[name] = ty
314
+ end
315
+
316
+ def get_method(mid, singleton)
317
+ @methods[[singleton, mid]] || begin
318
+ @modules[singleton].reverse_each do |_visible, mod|
319
+ meth = mod.get_method(mid, false)
320
+ return meth if meth
321
+ end
322
+ nil
323
+ end
324
+ end
325
+
326
+ def check_typed_method(mid, singleton)
327
+ set = @methods[[singleton, mid]]
328
+ return nil unless set
329
+ set = set.select {|mdef| mdef.is_a?(TypedMethodDef) }
330
+ return nil if set.empty?
331
+ return set
332
+ end
333
+
334
+ def add_method(mid, singleton, mdef)
335
+ @methods[[singleton, mid]] ||= Utils::MutableSet.new
336
+ @methods[[singleton, mid]] << mdef
337
+ # Need to restart...?
338
+ end
339
+ end
340
+
341
+ def include_module(including_mod, included_mod, visible = true)
342
+ return if included_mod == Type.any
343
+
344
+ if visible
345
+ @include_relations[including_mod] ||= Utils::MutableSet.new
346
+ @include_relations[including_mod] << included_mod
347
+ end
348
+
349
+ including_mod = @class_defs[including_mod.idx]
350
+ included_mod.each_child do |included_mod|
351
+ if included_mod.is_a?(Type::Class)
352
+ included_mod = @class_defs[included_mod.idx]
353
+ if included_mod && included_mod.kind == :module
354
+ including_mod.include_module(included_mod, visible)
355
+ else
356
+ warn "including something that is not a module"
357
+ end
358
+ end
359
+ end
360
+ end
361
+
362
+ def extend_module(extending_mod, extended_mod, visible = true)
363
+ extending_mod = @class_defs[extending_mod.idx]
364
+ extended_mod.each_child do |extended_mod|
365
+ if extended_mod.is_a?(Type::Class)
366
+ extended_mod = @class_defs[extended_mod.idx]
367
+ if extended_mod && extended_mod.kind == :module
368
+ extending_mod.extend_module(extended_mod, visible)
369
+ else
370
+ warn "extending something that is not a module"
371
+ end
372
+ end
373
+ end
374
+ end
375
+
376
+ def new_class(cbase, name, type_params, superclass)
377
+ if cbase && cbase.idx != 0
378
+ show_name = "#{ @class_defs[cbase.idx].name }::#{ name }"
379
+ else
380
+ show_name = name.to_s
381
+ end
382
+ idx = @class_defs.size
383
+ if superclass
384
+ if superclass == :__root__
385
+ superclass_idx = superclass = nil
386
+ else
387
+ superclass_idx = superclass.idx
388
+ end
389
+ @class_defs[idx] = ClassDef.new(:class, show_name, superclass_idx)
390
+ klass = Type::Class.new(:class, idx, type_params, superclass, show_name)
391
+ @class_defs[idx].klass_obj = klass
392
+ cbase ||= klass # for bootstrap
393
+ add_constant(cbase, name, klass)
394
+ return klass
395
+ else
396
+ # module
397
+ @class_defs[idx] = ClassDef.new(:module, show_name, nil)
398
+ mod = Type::Class.new(:module, idx, type_params, nil, show_name)
399
+ @class_defs[idx].klass_obj = mod
400
+ add_constant(cbase, name, mod)
401
+ return mod
402
+ end
403
+ end
404
+
405
+ def new_struct(ep)
406
+ return @struct_defs[ep] if @struct_defs[ep]
407
+
408
+ idx = @class_defs.size
409
+ superclass = Type::Builtin[:struct]
410
+ @class_defs[idx] = ClassDef.new(:class, "(Struct)", superclass.idx)
411
+ klass = Type::Class.new(:class, idx, [], superclass, "(Struct)")
412
+ @class_defs[idx].klass_obj = klass
413
+
414
+ @struct_defs[ep] = klass
415
+
416
+ klass
417
+ end
418
+
419
+ def get_class_name(klass)
420
+ if klass == Type.any
421
+ "???"
422
+ else
423
+ @class_defs[klass.idx].name
424
+ end
425
+ end
426
+
427
+ def get_method(klass, singleton, mid)
428
+ idx = klass.idx
429
+ while idx
430
+ class_def = @class_defs[idx]
431
+ mthd = class_def.get_method(mid, singleton)
432
+ # Need to be conservative to include all super candidates...?
433
+ return mthd if mthd
434
+ idx = class_def.superclass
435
+ end
436
+ return get_method(Type::Builtin[:class], false, mid) if singleton
437
+ nil
438
+ end
439
+
440
+ def get_super_method(ctx, singleton)
441
+ idx = ctx.cref.klass.idx
442
+ mid = ctx.mid
443
+ idx = @class_defs[idx].superclass
444
+ while idx
445
+ class_def = @class_defs[idx]
446
+ mthd = class_def.get_method(mid, singleton)
447
+ return mthd if mthd
448
+ idx = class_def.superclass
449
+ end
450
+ nil
451
+ end
452
+
453
+ def get_constant(klass, name)
454
+ if klass == Type.any
455
+ Type.any
456
+ elsif klass.is_a?(Type::Class)
457
+ @class_defs[klass.idx].get_constant(name)
458
+ else
459
+ Type.any
460
+ end
461
+ end
462
+
463
+ def search_constant(cref, name)
464
+ while cref != :bottom
465
+ val = get_constant(cref.klass, name)
466
+ return val if val != Type.any
467
+ cref = cref.outer
468
+ end
469
+
470
+ Type.any
471
+ end
472
+
473
+ def add_constant(klass, name, value)
474
+ if klass == Type.any
475
+ self
476
+ else
477
+ @class_defs[klass.idx].add_constant(name, value)
478
+ end
479
+ end
480
+
481
+ def check_typed_method(klass, mid, singleton)
482
+ @class_defs[klass.idx].check_typed_method(mid, singleton)
483
+ end
484
+
485
+ def add_method(klass, mid, singleton, mdef)
486
+ @class_defs[klass.idx].add_method(mid, singleton, mdef)
487
+ mdef
488
+ end
489
+
490
+ def add_attr_method(klass, mid, ivar, kind)
491
+ if kind == :reader || kind == :accessor
492
+ add_method(klass, mid, false, AttrMethodDef.new(ivar, :reader))
493
+ end
494
+ if kind == :writer || kind == :accessor
495
+ add_method(klass, :"#{ mid }=", false, AttrMethodDef.new(ivar, :writer))
496
+ end
497
+ end
498
+
499
+ def add_iseq_method(klass, mid, iseq, cref)
500
+ add_method(klass, mid, false, ISeqMethodDef.new(iseq, cref))
501
+ end
502
+
503
+ def add_singleton_iseq_method(klass, mid, iseq, cref)
504
+ add_method(klass, mid, true, ISeqMethodDef.new(iseq, cref))
505
+ end
506
+
507
+ def add_typed_method(recv_ty, mid, fargs, ret_ty)
508
+ add_method(recv_ty.klass, mid, false, TypedMethodDef.new([[fargs, ret_ty]]))
509
+ end
510
+
511
+ def add_singleton_typed_method(recv_ty, mid, fargs, ret_ty)
512
+ add_method(recv_ty.klass, mid, true, TypedMethodDef.new([[fargs, ret_ty]]))
513
+ end
514
+
515
+ def add_custom_method(klass, mid, impl)
516
+ add_method(klass, mid, false, CustomMethodDef.new(impl))
517
+ end
518
+
519
+ def add_singleton_custom_method(klass, mid, impl)
520
+ add_method(klass, mid, true, CustomMethodDef.new(impl))
521
+ end
522
+
523
+ def alias_method(klass, singleton, new, old)
524
+ if klass == Type.any
525
+ self
526
+ else
527
+ mdefs = get_method(klass, singleton, old)
528
+ if mdefs
529
+ mdefs.each do |mdef|
530
+ @class_defs[klass.idx].add_method(new, singleton, mdef)
531
+ end
532
+ end
533
+ end
534
+ end
535
+
536
+ def add_edge(ep, next_ep)
537
+ (@backward_edges[next_ep] ||= {})[ep] = true
538
+ end
539
+
540
+ def add_iseq_method_call!(iseq_mdef, ctx)
541
+ @iseq_method_to_ctxs[iseq_mdef] ||= Utils::MutableSet.new
542
+ @iseq_method_to_ctxs[iseq_mdef] << ctx
543
+ end
544
+
545
+ def add_callsite!(callee_ctx, fargs, caller_ep, caller_env, &ctn)
546
+ @executed_iseqs << callee_ctx.iseq if callee_ctx.is_a?(Context)
547
+
548
+ @callsites[callee_ctx] ||= {}
549
+ @callsites[callee_ctx][caller_ep] = ctn
550
+ merge_return_env(caller_ep) {|env| env ? env.merge(caller_env) : caller_env }
551
+
552
+ if @sig_fargs[callee_ctx]
553
+ @sig_fargs[callee_ctx] = @sig_fargs[callee_ctx].merge(fargs)
554
+ else
555
+ @sig_fargs[callee_ctx] = fargs
556
+ end
557
+ ret_ty = @sig_ret[callee_ctx] ||= Type.bot
558
+ unless ret_ty.eql?(Type.bot)
559
+ @callsites[callee_ctx].each do |caller_ep, ctn|
560
+ ctn[ret_ty, caller_ep, @return_envs[caller_ep]]
561
+ end
562
+ end
563
+ end
564
+
565
+ def merge_return_env(caller_ep)
566
+ @return_envs[caller_ep] = yield @return_envs[caller_ep]
567
+ end
568
+
569
+ def add_return_type!(callee_ctx, ret_ty)
570
+ @sig_ret[callee_ctx] ||= Type.bot
571
+ @sig_ret[callee_ctx] = @sig_ret[callee_ctx].union(ret_ty)
572
+
573
+ @callsites[callee_ctx] ||= {}
574
+ @callsites[callee_ctx].each do |caller_ep, ctn|
575
+ ctn[ret_ty, caller_ep, @return_envs[caller_ep]]
576
+ end
577
+ end
578
+
579
+ def add_yield!(caller_ctx, aargs, blk_ctx)
580
+ aargs_acc, = @yields[caller_ctx]
581
+ if aargs_acc
582
+ @yields[caller_ctx][0] = aargs_acc.merge(aargs)
583
+ else
584
+ @yields[caller_ctx] = [aargs, Utils::MutableSet.new]
585
+ end
586
+ @yields[caller_ctx][1] << blk_ctx
587
+ end
588
+
589
+ def add_block_to_ctx!(blk, ctx)
590
+ @block_to_ctx[blk] ||= Utils::MutableSet.new
591
+ @block_to_ctx[blk] << ctx
592
+ end
593
+
594
+ class VarTable
595
+ def initialize
596
+ @read, @write = {}, {}
597
+ end
598
+
599
+ attr_reader :write
600
+
601
+ def add_read!(site, ep, &ctn)
602
+ @read[site] ||= {}
603
+ @read[site][ep] = ctn
604
+ @write[site] ||= Type.bot
605
+ ctn[@write[site], ep]
606
+ end
607
+
608
+ def add_write!(site, ty, &ctn)
609
+ @write[site] ||= Type.bot
610
+ @write[site] = @write[site].union(ty)
611
+ @read[site] ||= {}
612
+ @read[site].each do |ep, ctn|
613
+ ctn[ty, ep]
614
+ end
615
+ end
616
+ end
617
+
618
+ def get_ivar(recv)
619
+ case recv
620
+ when Type::Class
621
+ [@class_defs[recv.idx], true]
622
+ when Type::Instance
623
+ [@class_defs[recv.klass.idx], false]
624
+ when Type::Any
625
+ return
626
+ else
627
+ warn "???"
628
+ return
629
+ end
630
+ end
631
+
632
+ def add_ivar_read!(recv, var, ep, &ctn)
633
+ recv.each_child do |recv|
634
+ class_def, singleton = get_ivar(recv)
635
+ next unless class_def
636
+ class_def.ivars.add_read!([singleton, var], ep, &ctn)
637
+ end
638
+ end
639
+
640
+ def add_ivar_write!(recv, var, ty, &ctn)
641
+ recv.each_child do |recv|
642
+ class_def, singleton = get_ivar(recv)
643
+ next unless class_def
644
+ class_def.ivars.add_write!([singleton, var], ty, &ctn)
645
+ end
646
+ end
647
+
648
+ def add_cvar_read!(klass, var, ep, &ctn)
649
+ klass.each_child do |klass|
650
+ class_def = @class_defs[klass.idx]
651
+ next unless class_def
652
+ class_def.cvars.add_read!(var, ep, &ctn)
653
+ end
654
+ end
655
+
656
+ def add_cvar_write!(klass, var, ty, &ctn)
657
+ klass.each_child do |klass|
658
+ class_def = @class_defs[klass.idx]
659
+ next unless class_def
660
+ class_def.cvars.add_write!(var, ty, &ctn)
661
+ end
662
+ end
663
+
664
+ def add_gvar_read!(var, ep, &ctn)
665
+ @gvar_table.add_read!(var, ep, &ctn)
666
+ end
667
+
668
+ def add_gvar_write!(var, ty, &ctn)
669
+ @gvar_table.add_write!(var, ty, &ctn)
670
+ end
671
+
672
+ def error(ep, msg)
673
+ p [ep.source_location, "[error] " + msg] if Config.verbose >= 2
674
+ @errors << [ep, "[error] " + msg]
675
+ end
676
+
677
+ def warn(ep, msg)
678
+ p [ep.source_location, "[warning] " + msg] if Config.verbose >= 2
679
+ @errors << [ep, "[warning] " + msg]
680
+ end
681
+
682
+ def reveal_type(ep, ty)
683
+ key = ep.source_location
684
+ puts "reveal:#{ ep.source_location }:#{ ty.screen_name(self) }" if Config.verbose >= 2
685
+ if @reveal_types[key]
686
+ @reveal_types[key] = @reveal_types[key].union(ty)
687
+ else
688
+ @reveal_types[key] = ty
689
+ end
690
+ end
691
+
692
+ def get_container_elem_types(env, ep, id)
693
+ if ep.outer
694
+ tmp_ep = ep
695
+ tmp_ep = tmp_ep.outer while tmp_ep.outer
696
+ env = @return_envs[tmp_ep]
697
+ end
698
+ env.get_container_elem_types(id)
699
+ end
700
+
701
+ def update_container_elem_types(env, ep, id)
702
+ if ep.outer
703
+ tmp_ep = ep
704
+ tmp_ep = tmp_ep.outer while tmp_ep.outer
705
+ merge_return_env(tmp_ep) do |menv|
706
+ elems = menv.get_container_elem_types(id)
707
+ elems = yield elems
708
+ menv = menv.update_container_elem_types(id, elems)
709
+ gid = @alloc_site_to_global_id[id]
710
+ if gid
711
+ ty = globalize_type(elems.to_local_type(id), env, ep)
712
+ add_ivar_write!(*gid, ty)
713
+ end
714
+ menv
715
+ end
716
+ env
717
+ else
718
+ elems = env.get_container_elem_types(id)
719
+ elems = yield elems
720
+ env = env.update_container_elem_types(id, elems)
721
+ gid = @alloc_site_to_global_id[id]
722
+ if gid
723
+ ty = globalize_type(elems.to_local_type(id), env, ep)
724
+ add_ivar_write!(*gid, ty)
725
+ end
726
+ env
727
+ end
728
+ end
729
+
730
+ def get_array_elem_type(env, ep, id, idx = nil)
731
+ elems = get_container_elem_types(env, ep, id)
732
+
733
+ if elems
734
+ return elems[idx] || Type.nil if idx
735
+ return elems.squash
736
+ else
737
+ Type.any
738
+ end
739
+ end
740
+
741
+ def get_hash_elem_type(env, ep, id, key_ty = nil)
742
+ elems = get_container_elem_types(env, ep, id)
743
+
744
+ if elems
745
+ elems[globalize_type(key_ty, env, ep) || Type.any]
746
+ else
747
+ Type.any
748
+ end
749
+ end
750
+
751
+ def type_profile
752
+ counter = 0
753
+ stat_eps = Utils::MutableSet.new
754
+ while true
755
+ until @worklist.empty?
756
+ counter += 1
757
+ if counter % 1000 == 0 && Config.verbose >= 1
758
+ puts "iter %d, remain: %d" % [counter, @worklist.size]
759
+ #exit if counter == 20000
760
+ end
761
+ @ep = @worklist.deletemin
762
+ stat_eps << @ep
763
+ step(@ep) # TODO: deletemin
764
+ end
765
+
766
+ # XXX: it would be good to provide no-dummy-execution mode.
767
+ # It should work as a bit smarter "rbs prototype rb";
768
+ # show all method definitions as "untyped" arguments and return values
769
+
770
+ begin
771
+ iseq, (kind, dummy_continuation) = @pending_execution.first
772
+ break if !iseq
773
+ @pending_execution.delete(iseq)
774
+ end while @executed_iseqs.include?(iseq)
775
+
776
+ puts "DEBUG: trigger dummy execution (#{ iseq&.name || "(nil)" }): rest #{ @pending_execution.size }" if Config.verbose >= 2
777
+
778
+ break if !iseq
779
+ case kind
780
+ when :method
781
+ meth, ep, env = dummy_continuation
782
+ merge_env(ep, env)
783
+ add_iseq_method_call!(meth, ep.ctx)
784
+
785
+ fargs_format = iseq.fargs_format
786
+ lead_tys = [Type.any] * (fargs_format[:lead_num] || 0)
787
+ opt_tys = fargs_format[:opt] ? [] : nil
788
+ post_tys = [Type.any] * (fargs_format[:post_num] || 0)
789
+ if fargs_format[:kwbits]
790
+ kw_tys = []
791
+ fargs_format[:keyword].each do |kw|
792
+ case
793
+ when kw.is_a?(Symbol) # required keyword
794
+ key = kw
795
+ req = true
796
+ ty = Type.any
797
+ when kw.size == 2 # optional keyword (default value is a literal)
798
+ key, ty = *kw
799
+ ty = Type.guess_literal_type(ty)
800
+ ty = ty.type if ty.is_a?(Type::Literal)
801
+ else # optional keyword
802
+ key, = kw
803
+ req = false
804
+ ty = Type.any
805
+ end
806
+ kw_tys << [req, key, ty]
807
+ end
808
+ else
809
+ kw_tys = nil
810
+ end
811
+ fargs = FormalArguments.new(lead_tys, opt_tys, nil, post_tys, kw_tys, nil, nil)
812
+ add_callsite!(ep.ctx, fargs, nil, nil) do |_ret_ty, _ep, _env|
813
+ # ignore
814
+ end
815
+
816
+ when :block
817
+ epenvs = dummy_continuation
818
+ epenvs.each do |ep, env|
819
+ merge_env(ep, env)
820
+ end
821
+ end
822
+ end
823
+
824
+ stat_eps
825
+ end
826
+
827
+ def report(stat_eps, output)
828
+ Reporters.show_error(@errors, @backward_edges, output)
829
+
830
+ Reporters.show_reveal_types(self, @reveal_types, output)
831
+
832
+ Reporters.show_gvars(self, @gvar_table.write, output)
833
+
834
+ #RubySignatureExporter2.new(
835
+ # self, @include_relations, @ivar_table.write, @cvar_table.write, @class_defs
836
+ #).show
837
+
838
+ #return
839
+ RubySignatureExporter.new(self, @class_defs, @iseq_method_to_ctxs, @sig_fargs, @sig_ret, @yields).show(stat_eps, output)
840
+ end
841
+
842
+ def globalize_type(ty, env, ep)
843
+ if ep.outer
844
+ tmp_ep = ep
845
+ tmp_ep = tmp_ep.outer while tmp_ep.outer
846
+ env = @return_envs[tmp_ep]
847
+ end
848
+ ty.globalize(env, {}, Config.options[:type_depth_limit])
849
+ end
850
+
851
+ def localize_type(ty, env, ep, alloc_site = AllocationSite.new(ep))
852
+ if ep.outer
853
+ tmp_ep = ep
854
+ tmp_ep = tmp_ep.outer while tmp_ep.outer
855
+ target_env = @return_envs[tmp_ep]
856
+ target_env, ty = ty.localize(target_env, alloc_site, Config.options[:type_depth_limit])
857
+ merge_return_env(tmp_ep) do |env|
858
+ env ? env.merge(target_env) : target_env
859
+ end
860
+ return env, ty
861
+ else
862
+ return ty.localize(env, alloc_site, Config.options[:type_depth_limit])
863
+ end
864
+ end
865
+
866
+ def pend_method_execution(iseq, meth, recv, mid, cref)
867
+ ctx = Context.new(iseq, cref, mid)
868
+ ep = ExecutionPoint.new(ctx, 0, nil)
869
+ locals = [Type.any] * iseq.locals.size
870
+ env = Env.new(StaticEnv.new(recv, Type.any, false), locals, [], Utils::HashWrapper.new({}))
871
+
872
+ @pending_execution[iseq] ||= [:method, [meth, ep, env]]
873
+ end
874
+
875
+ def pend_block_dummy_execution(iseq, nep, nenv)
876
+ @pending_execution[iseq] ||= [:block, {}]
877
+ if @pending_execution[iseq][1][nep]
878
+ @pending_execution[iseq][1][nep] = @pending_execution[iseq][1][nep].merge(nenv)
879
+ else
880
+ @pending_execution[iseq][1][nep] = nenv
881
+ end
882
+ end
883
+
884
+ def get_instance_variable(recv, var, ep, env)
885
+ add_ivar_read!(recv, var, ep) do |ty, ep|
886
+ alloc_site = AllocationSite.new(ep)
887
+ nenv, ty = localize_type(ty, env, ep, alloc_site)
888
+ case ty
889
+ when Type::LocalArray, Type::LocalHash
890
+ @alloc_site_to_global_id[ty.id] = [recv, var] # need overwrite check??
891
+ end
892
+ yield ty, nenv
893
+ end
894
+ end
895
+
896
+ def set_instance_variable(recv, var, ty, ep, env)
897
+ ty = globalize_type(ty, env, ep)
898
+ add_ivar_write!(recv, var, ty)
899
+ end
900
+
901
+ def step(ep)
902
+ env = @ep2env[ep]
903
+ raise "nil env" unless env
904
+
905
+ insn, operands = ep.ctx.iseq.insns[ep.pc]
906
+
907
+ if Config.verbose >= 2
908
+ # XXX: more dedicated output
909
+ puts "DEBUG: stack=%p" % [env.stack]
910
+ puts "DEBUG: %s (%s) PC=%d insn=%s sp=%d" % [ep.source_location, ep.ctx.iseq.name, ep.pc, insn, env.stack.size]
911
+ end
912
+
913
+ case insn
914
+ when :putspecialobject
915
+ kind, = operands
916
+ ty = case kind
917
+ when 1 then Type::Instance.new(Type::Builtin[:vmcore])
918
+ when 2, 3 # CBASE / CONSTBASE
919
+ ep.ctx.cref.klass
920
+ else
921
+ raise NotImplementedError, "unknown special object: #{ type }"
922
+ end
923
+ env = env.push(ty)
924
+ when :putnil
925
+ env = env.push(Type.nil)
926
+ when :putobject, :duparray
927
+ obj, = operands
928
+ env, ty = localize_type(Type.guess_literal_type(obj), env, ep)
929
+ env = env.push(ty)
930
+ when :putstring
931
+ str, = operands
932
+ ty = Type::Literal.new(str, Type::Instance.new(Type::Builtin[:str]))
933
+ env = env.push(ty)
934
+ when :putself
935
+ env, ty = localize_type(env.static_env.recv_ty, env, ep)
936
+ env = env.push(ty)
937
+ when :newarray, :newarraykwsplat
938
+ len, = operands
939
+ env, elems = env.pop(len)
940
+ ty = Type::Array.new(Type::Array::Elements.new(elems), Type::Instance.new(Type::Builtin[:ary]))
941
+ env, ty = localize_type(ty, env, ep)
942
+ env = env.push(ty)
943
+ when :newhash
944
+ num, = operands
945
+ env, tys = env.pop(num)
946
+
947
+ ty = Type.gen_hash do |h|
948
+ tys.each_slice(2) do |k_ty, v_ty|
949
+ k_ty = globalize_type(k_ty, env, ep)
950
+ h[k_ty] = v_ty
951
+ end
952
+ end
953
+
954
+ env, ty = localize_type(ty, env, ep)
955
+ env = env.push(ty)
956
+ when :newhashfromarray
957
+ raise NotImplementedError, "newhashfromarray"
958
+ when :newrange
959
+ env, tys = env.pop(2)
960
+ # XXX: need generics
961
+ env = env.push(Type::Instance.new(Type::Builtin[:range]))
962
+
963
+ when :concatstrings
964
+ num, = operands
965
+ env, = env.pop(num)
966
+ env = env.push(Type::Instance.new(Type::Builtin[:str]))
967
+ when :tostring
968
+ env, (_ty1, _ty2,) = env.pop(2)
969
+ env = env.push(Type::Instance.new(Type::Builtin[:str]))
970
+ when :freezestring
971
+ # do nothing
972
+ when :toregexp
973
+ _regexp_opt, str_count = operands
974
+ env, tys = env.pop(str_count)
975
+ # TODO: check if tys are all strings?
976
+ env = env.push(Type::Instance.new(Type::Builtin[:regexp]))
977
+ when :intern
978
+ env, (ty,) = env.pop(1)
979
+ # XXX check if ty is String
980
+ env = env.push(Type::Instance.new(Type::Builtin[:sym]))
981
+
982
+ when :definemethod
983
+ mid, iseq = operands
984
+ cref = ep.ctx.cref
985
+ recv = env.static_env.recv_ty
986
+ if cref.klass.is_a?(Type::Class)
987
+ typed_mdef = check_typed_method(cref.klass, mid, ep.ctx.cref.singleton)
988
+ if typed_mdef
989
+ mdef = ISeqMethodDef.new(iseq, cref)
990
+ typed_mdef.each do |typed_mdef|
991
+ typed_mdef.do_match_iseq_mdef(mdef, recv, mid, env, ep, self)
992
+ end
993
+ else
994
+ if ep.ctx.cref.singleton
995
+ meth = add_singleton_iseq_method(cref.klass, mid, iseq, cref)
996
+ else
997
+ meth = add_iseq_method(cref.klass, mid, iseq, cref)
998
+ if env.static_env.mod_func
999
+ add_singleton_iseq_method(cref.klass, mid, iseq, cref)
1000
+ end
1001
+ end
1002
+
1003
+ recv = Type::Instance.new(recv) if recv.is_a?(Type::Class)
1004
+ pend_method_execution(iseq, meth, recv, mid, ep.ctx.cref)
1005
+ end
1006
+ else
1007
+ # XXX: what to do?
1008
+ end
1009
+
1010
+ when :definesmethod
1011
+ mid, iseq = operands
1012
+ env, (recv,) = env.pop(1)
1013
+ cref = ep.ctx.cref
1014
+ recv.each_child do |recv|
1015
+ if recv.is_a?(Type::Class)
1016
+ meth = add_singleton_iseq_method(recv, mid, iseq, cref)
1017
+ pend_method_execution(iseq, meth, recv, mid, ep.ctx.cref)
1018
+ else
1019
+ recv = Type.any # XXX: what to do?
1020
+ end
1021
+ end
1022
+ when :defineclass
1023
+ id, iseq, flags = operands
1024
+ env, (cbase, superclass) = env.pop(2)
1025
+ case flags & 7
1026
+ when 0, 2 # CLASS / MODULE
1027
+ type = (flags & 7) == 2 ? :module : :class
1028
+ existing_klass = get_constant(cbase, id) # TODO: multiple return values
1029
+ if existing_klass.is_a?(Type::Class)
1030
+ klass = existing_klass
1031
+ else
1032
+ if existing_klass != Type.any
1033
+ error(ep, "the class \"#{ id }\" is #{ existing_klass.screen_name(self) }")
1034
+ id = :"#{ id }(dummy)"
1035
+ end
1036
+ existing_klass = get_constant(cbase, id) # TODO: multiple return values
1037
+ if existing_klass != Type.any
1038
+ klass = existing_klass
1039
+ else
1040
+ if type == :class
1041
+ if superclass.is_a?(Type::Class)
1042
+ # okay
1043
+ elsif superclass == Type.any
1044
+ warn(ep, "superclass is any; Object is used instead")
1045
+ superclass = Type::Builtin[:obj]
1046
+ elsif superclass.eql?(Type.nil)
1047
+ superclass = Type::Builtin[:obj]
1048
+ elsif superclass.is_a?(Type::Instance)
1049
+ warn(ep, "superclass is an instance; Object is used instead")
1050
+ superclass = Type::Builtin[:obj]
1051
+ else
1052
+ warn(ep, "superclass is not a class; Object is used instead")
1053
+ superclass = Type::Builtin[:obj]
1054
+ end
1055
+ else # module
1056
+ superclass = nil
1057
+ end
1058
+ if cbase == Type.any
1059
+ klass = Type.any
1060
+ else
1061
+ klass = new_class(cbase, id, [], superclass)
1062
+ end
1063
+ end
1064
+ end
1065
+ singleton = false
1066
+ when 1 # SINGLETON_CLASS
1067
+ singleton = true
1068
+ klass = cbase
1069
+ if klass.is_a?(Type::Class)
1070
+ elsif klass.is_a?(Type::Any)
1071
+ else
1072
+ warn(ep, "A singleton class is open for #{ klass.screen_name(self) }; handled as any")
1073
+ klass = Type.any
1074
+ end
1075
+ else
1076
+ raise NotImplementedError, "unknown defineclass flag: #{ flags }"
1077
+ end
1078
+ ncref = ep.ctx.cref.extend(klass, singleton)
1079
+ recv = singleton ? Type.any : klass
1080
+ blk = env.static_env.blk_ty
1081
+ nctx = Context.new(iseq, ncref, nil)
1082
+ nep = ExecutionPoint.new(nctx, 0, nil)
1083
+ locals = [Type.nil] * iseq.locals.size
1084
+ nenv = Env.new(StaticEnv.new(recv, blk, false), locals, [], Utils::HashWrapper.new({}))
1085
+ merge_env(nep, nenv)
1086
+ add_callsite!(nep.ctx, nil, ep, env) do |ret_ty, ep, env|
1087
+ nenv, ret_ty = localize_type(ret_ty, env, ep)
1088
+ nenv = nenv.push(ret_ty)
1089
+ merge_env(ep.next, nenv)
1090
+ end
1091
+ return
1092
+ when :send
1093
+ env, recvs, mid, aargs = setup_actual_arguments(operands, ep, env)
1094
+ recvs = Type.any if recvs == Type.bot
1095
+ recvs.each_child do |recv|
1096
+ do_send(recv, mid, aargs, ep, env) do |ret_ty, ep, env|
1097
+ nenv, ret_ty, = localize_type(ret_ty, env, ep)
1098
+ nenv = nenv.push(ret_ty)
1099
+ merge_env(ep.next, nenv)
1100
+ end
1101
+ end
1102
+ return
1103
+ when :send_branch
1104
+ getlocal_operands, send_operands, branch_operands = operands
1105
+ env, recvs, mid, aargs = setup_actual_arguments(send_operands, ep, env)
1106
+ recvs = Type.any if recvs == Type.bot
1107
+ recvs.each_child do |recv|
1108
+ do_send(recv, mid, aargs, ep, env) do |ret_ty, ep, env|
1109
+ env, ret_ty, = localize_type(ret_ty, env, ep)
1110
+
1111
+ branchtype, target, = branch_operands
1112
+ # branchtype: :if or :unless or :nil
1113
+ ep_then = ep.next
1114
+ ep_else = ep.jump(target)
1115
+
1116
+ var_idx, _scope_idx, _escaped = getlocal_operands
1117
+ flow_env = env.local_update(-var_idx+2, recv)
1118
+
1119
+ case ret_ty
1120
+ when Type::Instance.new(Type::Builtin[:true])
1121
+ merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
1122
+ when Type::Instance.new(Type::Builtin[:false])
1123
+ merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
1124
+ else
1125
+ merge_env(ep_then, env)
1126
+ merge_env(ep_else, env)
1127
+ end
1128
+ end
1129
+ end
1130
+ return
1131
+ when :invokeblock
1132
+ # XXX: need block parameter, unknown block, etc. Use setup_actual_arguments
1133
+ opt, = operands
1134
+ _flags = opt[:flag]
1135
+ orig_argc = opt[:orig_argc]
1136
+ env, aargs = env.pop(orig_argc)
1137
+ blk = env.static_env.blk_ty
1138
+ case
1139
+ when blk.eql?(Type.nil)
1140
+ env = env.push(Type.any)
1141
+ when blk.eql?(Type.any)
1142
+ #warn(ep, "block is any")
1143
+ env = env.push(Type.any)
1144
+ else # Proc
1145
+ blk_nil = Type.nil
1146
+ #
1147
+ aargs = ActualArguments.new(aargs, nil, nil, blk_nil)
1148
+ do_invoke_block(true, env.static_env.blk_ty, aargs, ep, env) do |ret_ty, ep, env|
1149
+ nenv, ret_ty, = localize_type(ret_ty, env, ep)
1150
+ nenv = nenv.push(ret_ty)
1151
+ merge_env(ep.next, nenv)
1152
+ end
1153
+ return
1154
+ end
1155
+ when :invokesuper
1156
+ env, recv, _, aargs = setup_actual_arguments(operands, ep, env)
1157
+
1158
+ env, recv = localize_type(env.static_env.recv_ty, env, ep)
1159
+ mid = ep.ctx.mid
1160
+ singleton = !recv.is_a?(Type::Instance) # TODO: any?
1161
+ # XXX: need to support included module...
1162
+ meths = get_super_method(ep.ctx, singleton) # TODO: multiple return values
1163
+ if meths
1164
+ meths.each do |meth|
1165
+ # XXX: this decomposition is really needed??
1166
+ # It calls `Object.new` with union receiver which causes an error, but
1167
+ # it may be a fault of builtin Object.new implementation.
1168
+ recv.each_child do |recv|
1169
+ meth.do_send(recv, mid, aargs, ep, env, self) do |ret_ty, ep, env|
1170
+ nenv, ret_ty, = localize_type(ret_ty, env, ep)
1171
+ nenv = nenv.push(ret_ty)
1172
+ merge_env(ep.next, nenv)
1173
+ end
1174
+ end
1175
+ end
1176
+ return
1177
+ else
1178
+ error(ep, "no superclass method: #{ env.static_env.recv_ty.screen_name(self) }##{ mid }")
1179
+ env = env.push(Type.any)
1180
+ end
1181
+ when :invokebuiltin
1182
+ raise NotImplementedError
1183
+ when :leave
1184
+ if env.stack.size != 1
1185
+ raise "stack inconsistency error: #{ env.stack.inspect }"
1186
+ end
1187
+ env, (ty,) = env.pop(1)
1188
+ ty = globalize_type(ty, env, ep)
1189
+ add_return_type!(ep.ctx, ty)
1190
+ return
1191
+ when :throw
1192
+ throwtype, = operands
1193
+ env, (ty,) = env.pop(1)
1194
+ _no_escape = !!(throwtype & 0x8000)
1195
+ throwtype = [:none, :return, :break, :next, :retry, :redo][throwtype & 0xff]
1196
+ case throwtype
1197
+ when :none
1198
+
1199
+ when :return
1200
+ ty = globalize_type(ty, env, ep)
1201
+ tmp_ep = ep
1202
+ tmp_ep = tmp_ep.outer while tmp_ep.outer
1203
+ add_return_type!(tmp_ep.ctx, ty)
1204
+ return
1205
+ when :break
1206
+ tmp_ep = ep
1207
+ tmp_ep = tmp_ep.outer while tmp_ep.ctx.iseq.type != :block
1208
+ tmp_ep = tmp_ep.outer
1209
+ nenv = @return_envs[tmp_ep].push(ty)
1210
+ merge_env(tmp_ep.next, nenv)
1211
+ # TODO: jump to ensure?
1212
+ when :next, :redo
1213
+ # begin; rescue; next; end
1214
+ tmp_ep = ep.outer
1215
+ _type, _iseq, cont, stack_depth = tmp_ep.ctx.iseq.catch_table[tmp_ep.pc].find {|type,| type == throwtype }
1216
+ nenv = @return_envs[tmp_ep]
1217
+ nenv, = nenv.pop(nenv.stack.size - stack_depth)
1218
+ nenv = nenv.push(ty) if throwtype == :next
1219
+ tmp_ep = tmp_ep.jump(cont)
1220
+ merge_env(tmp_ep, nenv)
1221
+ when :retry
1222
+ tmp_ep = ep.outer
1223
+ _type, _iseq, cont, stack_depth = tmp_ep.ctx.iseq.catch_table[tmp_ep.pc].find {|type,| type == :retry }
1224
+ nenv = @return_envs[tmp_ep]
1225
+ nenv, = nenv.pop(nenv.stack.size - stack_depth)
1226
+ tmp_ep = tmp_ep.jump(cont)
1227
+ merge_env(tmp_ep, nenv)
1228
+ else
1229
+ p throwtype
1230
+ raise NotImplementedError
1231
+ end
1232
+ return
1233
+ when :once
1234
+ iseq, = operands
1235
+
1236
+ nctx = Context.new(iseq, ep.ctx.cref, ep.ctx.mid)
1237
+ nep = ExecutionPoint.new(nctx, 0, ep)
1238
+ raise if iseq.locals != []
1239
+ nenv = Env.new(env.static_env, [], [], nil)
1240
+ merge_env(nep, nenv)
1241
+ add_callsite!(nep.ctx, nil, ep, env) do |ret_ty, ep, env|
1242
+ nenv, ret_ty = localize_type(ret_ty, env, ep)
1243
+ nenv = nenv.push(ret_ty)
1244
+ merge_env(ep.next, nenv)
1245
+ end
1246
+ return
1247
+
1248
+ when :branch # TODO: check how branchnil is used
1249
+ branchtype, target, = operands
1250
+ # branchtype: :if or :unless or :nil
1251
+ env, (ty,) = env.pop(1)
1252
+ ep_then = ep.next
1253
+ ep_else = ep.jump(target)
1254
+
1255
+ # TODO: it works for only simple cases: `x = nil; x || 1`
1256
+ # It would be good to merge "dup; branchif" to make it context-sensitive-like
1257
+ falsy = ty.eql?(Type.nil)
1258
+
1259
+ merge_env(ep_then, env)
1260
+ merge_env(ep_else, env) unless branchtype == :if && falsy
1261
+ return
1262
+ when :jump
1263
+ target, = operands
1264
+ merge_env(ep.jump(target), env)
1265
+ return
1266
+
1267
+ when :setinstancevariable
1268
+ var, = operands
1269
+ env, (ty,) = env.pop(1)
1270
+ recv = env.static_env.recv_ty
1271
+ set_instance_variable(recv, var, ty, ep, env)
1272
+
1273
+ when :getinstancevariable
1274
+ var, = operands
1275
+ recv = env.static_env.recv_ty
1276
+ get_instance_variable(recv, var, ep, env) do |ty, nenv|
1277
+ merge_env(ep.next, nenv.push(ty))
1278
+ end
1279
+ return
1280
+
1281
+ when :setclassvariable
1282
+ var, = operands
1283
+ env, (ty,) = env.pop(1)
1284
+ cbase = ep.ctx.cref.klass
1285
+ ty = globalize_type(ty, env, ep)
1286
+ # TODO: if superclass has the variable, it should be updated
1287
+ add_cvar_write!(cbase, var, ty)
1288
+
1289
+ when :getclassvariable
1290
+ var, = operands
1291
+ cbase = ep.ctx.cref.klass
1292
+ # TODO: if superclass has the variable, it should be read
1293
+ add_cvar_read!(cbase, var, ep) do |ty, ep|
1294
+ nenv, ty = localize_type(ty, env, ep)
1295
+ merge_env(ep.next, nenv.push(ty))
1296
+ end
1297
+ return
1298
+
1299
+ when :setglobal
1300
+ var, = operands
1301
+ env, (ty,) = env.pop(1)
1302
+ ty = globalize_type(ty, env, ep)
1303
+ add_gvar_write!(var, ty)
1304
+
1305
+ when :getglobal
1306
+ var, = operands
1307
+ ty = Type.builtin_global_variable_type(var)
1308
+ if ty
1309
+ ty = get_constant(Type::Builtin[:obj], ty) if ty.is_a?(Symbol)
1310
+ env, ty = localize_type(ty, env, ep)
1311
+ env = env.push(ty)
1312
+ else
1313
+ add_gvar_read!(var, ep) do |ty, ep|
1314
+ ty = Type.nil if ty == Type.bot # HACK
1315
+ nenv, ty = localize_type(ty, env, ep)
1316
+ merge_env(ep.next, nenv.push(ty))
1317
+ end
1318
+ # need to return default nil of global variables
1319
+ return
1320
+ end
1321
+
1322
+ when :getlocal, :getblockparam, :getblockparamproxy
1323
+ var_idx, scope_idx, _escaped = operands
1324
+ if scope_idx == 0
1325
+ ty = env.get_local(-var_idx+2)
1326
+ else
1327
+ tmp_ep = ep
1328
+ scope_idx.times do
1329
+ tmp_ep = tmp_ep.outer
1330
+ end
1331
+ ty = @return_envs[tmp_ep].get_local(-var_idx+2)
1332
+ end
1333
+ env = env.push(ty)
1334
+ when :getlocal_branch
1335
+ getlocal_operands, branch_operands = operands
1336
+ var_idx, _scope_idx, _escaped = getlocal_operands
1337
+ ret_ty = env.get_local(-var_idx+2)
1338
+
1339
+ branchtype, target, = branch_operands
1340
+ # branchtype: :if or :unless or :nil
1341
+ ep_then = ep.next
1342
+ ep_else = ep.jump(target)
1343
+
1344
+ var_idx, _scope_idx, _escaped = getlocal_operands
1345
+
1346
+ ret_ty.each_child do |ret_ty|
1347
+ flow_env = env.local_update(-var_idx+2, ret_ty)
1348
+ case ret_ty
1349
+ when Type.any
1350
+ merge_env(ep_then, env)
1351
+ merge_env(ep_else, env)
1352
+ when Type::Instance.new(Type::Builtin[:false]), Type.nil
1353
+ merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
1354
+ else
1355
+ merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
1356
+ end
1357
+ end
1358
+ return
1359
+ when :getlocal_checkmatch_branch
1360
+ getlocal_operands, branch_operands = operands
1361
+ var_idx, _scope_idx, _escaped = getlocal_operands
1362
+ ret_ty = env.get_local(-var_idx+2)
1363
+
1364
+ env, (pattern_ty,) = env.pop(1)
1365
+
1366
+ branchtype, target, = branch_operands
1367
+ # branchtype: :if or :unless or :nil
1368
+ ep_then = ep.next
1369
+ ep_else = ep.jump(target)
1370
+
1371
+ var_idx, _scope_idx, _escaped = getlocal_operands
1372
+
1373
+ ret_ty.each_child do |ret_ty|
1374
+ flow_env = env.local_update(-var_idx+2, ret_ty)
1375
+ if ret_ty.is_a?(Type::Instance)
1376
+ if ret_ty.klass == pattern_ty # XXX: inheritance
1377
+ merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
1378
+ else
1379
+ merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
1380
+ end
1381
+ else
1382
+ merge_env(ep_then, env)
1383
+ merge_env(ep_else, env)
1384
+ end
1385
+ end
1386
+ return
1387
+ when :setlocal, :setblockparam
1388
+ var_idx, scope_idx, _escaped = operands
1389
+ env, (ty,) = env.pop(1)
1390
+ if scope_idx == 0
1391
+ env = env.local_update(-var_idx+2, ty)
1392
+ else
1393
+ tmp_ep = ep
1394
+ scope_idx.times do
1395
+ tmp_ep = tmp_ep.outer
1396
+ end
1397
+ merge_return_env(tmp_ep) do |env|
1398
+ env.merge(env.local_update(-var_idx+2, ty))
1399
+ end
1400
+ end
1401
+ when :getconstant
1402
+ name, = operands
1403
+ env, (cbase, _allow_nil,) = env.pop(2)
1404
+ if cbase.eql?(Type.nil)
1405
+ ty = search_constant(ep.ctx.cref, name)
1406
+ env, ty = localize_type(ty, env, ep)
1407
+ env = env.push(ty)
1408
+ elsif cbase.eql?(Type.any)
1409
+ env = env.push(Type.any) # XXX: warning needed?
1410
+ else
1411
+ ty = get_constant(cbase, name)
1412
+ env, ty = localize_type(ty, env, ep)
1413
+ env = env.push(ty)
1414
+ end
1415
+ when :setconstant
1416
+ name, = operands
1417
+ env, (ty, cbase) = env.pop(2)
1418
+ old_ty = get_constant(cbase, name)
1419
+ if old_ty != Type.any # XXX???
1420
+ warn(ep, "already initialized constant #{ Type::Instance.new(cbase).screen_name(self) }::#{ name }")
1421
+ end
1422
+ ty.each_child do |ty|
1423
+ if ty.is_a?(Type::Class) && ty.superclass == Type::Builtin[:struct]
1424
+ @class_defs[ty.idx].name = name.to_s
1425
+ end
1426
+ end
1427
+ add_constant(cbase, name, globalize_type(ty, env, ep))
1428
+
1429
+ when :getspecial
1430
+ key, type = operands
1431
+ if type == 0
1432
+ raise NotImplementedError
1433
+ case key
1434
+ when 0 # VM_SVAR_LASTLINE
1435
+ env = env.push(Type.any) # or String | NilClass only?
1436
+ when 1 # VM_SVAR_BACKREF ($~)
1437
+ merge_env(ep.next, env.push(Type::Instance.new(Type::Builtin[:matchdata])))
1438
+ merge_env(ep.next, env.push(Type.nil))
1439
+ return
1440
+ else # flip-flop
1441
+ env = env.push(Type.bool)
1442
+ end
1443
+ else
1444
+ # NTH_REF ($1, $2, ...) / BACK_REF ($&, $+, ...)
1445
+ merge_env(ep.next, env.push(Type::Instance.new(Type::Builtin[:str])))
1446
+ merge_env(ep.next, env.push(Type.nil))
1447
+ return
1448
+ end
1449
+ when :setspecial
1450
+ # flip-flop
1451
+ raise NotImplementedError, "setspecial"
1452
+
1453
+ when :dup
1454
+ env, (ty,) = env.pop(1)
1455
+ env = env.push(ty).push(ty)
1456
+ when :duphash
1457
+ raw_hash, = operands
1458
+ ty = Type.guess_literal_type(raw_hash)
1459
+ env, ty = localize_type(globalize_type(ty, env, ep), env, ep)
1460
+ env = env.push(ty)
1461
+ when :dupn
1462
+ n, = operands
1463
+ _, tys = env.pop(n)
1464
+ tys.each {|ty| env = env.push(ty) }
1465
+ when :pop
1466
+ env, = env.pop(1)
1467
+ when :swap
1468
+ env, (a, b) = env.pop(2)
1469
+ env = env.push(a).push(b)
1470
+ when :reverse
1471
+ n, = operands
1472
+ env, tys = env.pop(n)
1473
+ tys.reverse_each {|ty| env = env.push(ty) }
1474
+ when :defined
1475
+ env, = env.pop(1)
1476
+ sym_ty = Type::Symbol.new(nil, Type::Instance.new(Type::Builtin[:sym]))
1477
+ env = env.push(Type.optional(sym_ty))
1478
+ when :checkmatch
1479
+ flag, = operands
1480
+ _array = flag & 4 != 0
1481
+ case flag & 3
1482
+ when 1
1483
+ raise NotImplementedError
1484
+ when 2 # VM_CHECKMATCH_TYPE_CASE
1485
+ env, = env.pop(2)
1486
+ env = env.push(Type.bool)
1487
+ when 3 # VM_CHECKMATCH_TYPE_RESCUE
1488
+ env, = env.pop(2)
1489
+ env = env.push(Type.bool)
1490
+ else
1491
+ raise "unknown checkmatch flag"
1492
+ end
1493
+ when :checkkeyword
1494
+ env = env.push(Type.bool)
1495
+ when :adjuststack
1496
+ n, = operands
1497
+ env, _ = env.pop(n)
1498
+ when :nop
1499
+ when :setn
1500
+ idx, = operands
1501
+ env, (ty,) = env.pop(1)
1502
+ env = env.setn(idx, ty).push(ty)
1503
+ when :topn
1504
+ idx, = operands
1505
+ env = env.topn(idx)
1506
+
1507
+ when :splatarray
1508
+ env, (ty,) = env.pop(1)
1509
+ # XXX: vm_splat_array
1510
+ env = env.push(ty)
1511
+ when :expandarray
1512
+ num, flag = operands
1513
+ env, (ary,) = env.pop(1)
1514
+ splat = flag & 1 == 1
1515
+ from_head = flag & 2 == 0
1516
+ ary.each_child do |ary|
1517
+ case ary
1518
+ when Type::LocalArray
1519
+ elems = get_container_elem_types(env, ep, ary.id)
1520
+ elems ||= Type::Array::Elements.new([], Type.any) # XXX
1521
+ do_expand_array(ep, env, elems, num, splat, from_head)
1522
+ when Type::Any
1523
+ nnum = num
1524
+ nnum += 1 if splat
1525
+ nenv = env
1526
+ nnum.times do
1527
+ nenv = nenv.push(Type.any)
1528
+ end
1529
+ add_edge(ep, ep)
1530
+ merge_env(ep.next, nenv)
1531
+ else
1532
+ # TODO: call to_ary (or to_a?)
1533
+ elems = Type::Array::Elements.new([ary], Type.bot)
1534
+ do_expand_array(ep, env, elems, num, splat, from_head)
1535
+ end
1536
+ end
1537
+ return
1538
+ when :concatarray
1539
+ env, (ary1, ary2) = env.pop(2)
1540
+ if ary1.is_a?(Type::LocalArray)
1541
+ elems1 = get_container_elem_types(env, ep, ary1.id)
1542
+ if ary2.is_a?(Type::LocalArray)
1543
+ elems2 = get_container_elem_types(env, ep, ary2.id)
1544
+ elems = Type::Array::Elements.new([], elems1.squash.union(elems2.squash))
1545
+ env = update_container_elem_types(env, ep, ary1.id) { elems }
1546
+ env = env.push(ary1)
1547
+ else
1548
+ elems = Type::Array::Elements.new([], Type.any)
1549
+ env = update_container_elem_types(env, ep, ary1.id) { elems }
1550
+ env = env.push(ary1)
1551
+ end
1552
+ else
1553
+ ty = Type::Array.new(Type::Array::Elements.new([], Type.any), Type::Instance.new(Type::Builtin[:ary]))
1554
+ env, ty = localize_type(ty, env, ep)
1555
+ env = env.push(ty)
1556
+ end
1557
+
1558
+ when :checktype
1559
+ type, = operands
1560
+ raise NotImplementedError if type != 5 # T_STRING
1561
+ # XXX: is_a?
1562
+ env, (val,) = env.pop(1)
1563
+ res = globalize_type(val, env, ep) == Type::Instance.new(Type::Builtin[:str])
1564
+ if res
1565
+ ty = Type::Instance.new(Type::Builtin[:true])
1566
+ else
1567
+ ty = Type::Instance.new(Type::Builtin[:false])
1568
+ end
1569
+ env = env.push(ty)
1570
+ else
1571
+ raise "Unknown insn: #{ insn }"
1572
+ end
1573
+
1574
+ add_edge(ep, ep)
1575
+ merge_env(ep.next, env)
1576
+
1577
+ if ep.ctx.iseq.catch_table[ep.pc]
1578
+ ep.ctx.iseq.catch_table[ep.pc].each do |type, iseq, cont, stack_depth|
1579
+ next if type != :rescue && type != :ensure
1580
+ next if env.stack.size < stack_depth
1581
+ cont_ep = ep.jump(cont)
1582
+ cont_env, = env.pop(env.stack.size - stack_depth)
1583
+ nctx = Context.new(iseq, ep.ctx.cref, ep.ctx.mid)
1584
+ nep = ExecutionPoint.new(nctx, 0, cont_ep)
1585
+ locals = [Type.nil] * iseq.locals.size
1586
+ nenv = Env.new(env.static_env, locals, [], Utils::HashWrapper.new({}))
1587
+ merge_env(nep, nenv)
1588
+ add_callsite!(nep.ctx, nil, cont_ep, cont_env) do |ret_ty, ep, env|
1589
+ nenv, ret_ty = localize_type(ret_ty, env, ep)
1590
+ nenv = nenv.push(ret_ty)
1591
+ merge_env(ep.jump(cont), nenv)
1592
+ end
1593
+ end
1594
+ end
1595
+ end
1596
+
1597
+ private def do_expand_array(ep, env, elems, num, splat, from_head)
1598
+ if from_head
1599
+ lead_tys, rest_ary_ty = elems.take_first(num)
1600
+ if splat
1601
+ env, local_ary_ty = localize_type(rest_ary_ty, env, ep)
1602
+ env = env.push(local_ary_ty)
1603
+ end
1604
+ lead_tys.reverse_each do |ty|
1605
+ env = env.push(ty)
1606
+ end
1607
+ else
1608
+ rest_ary_ty, following_tys = elems.take_last(num)
1609
+ following_tys.each do |ty|
1610
+ env = env.push(ty)
1611
+ end
1612
+ if splat
1613
+ env, local_ary_ty = localize_type(rest_ary_ty, env, ep)
1614
+ env = env.push(local_ary_ty)
1615
+ end
1616
+ end
1617
+ merge_env(ep.next, env)
1618
+ end
1619
+
1620
+ private def setup_actual_arguments(operands, ep, env)
1621
+ opt, blk_iseq = operands
1622
+ flags = opt[:flag]
1623
+ mid = opt[:mid]
1624
+ kw_arg = opt[:kw_arg]
1625
+ argc = opt[:orig_argc]
1626
+ argc += 1 # receiver
1627
+ argc += kw_arg.size if kw_arg
1628
+
1629
+ flag_args_splat = flags[ 0] != 0
1630
+ flag_args_blockarg = flags[ 1] != 0
1631
+ _flag_args_fcall = flags[ 2] != 0
1632
+ _flag_args_vcall = flags[ 3] != 0
1633
+ _flag_args_simple = flags[ 4] != 0 # unused in TP
1634
+ _flag_blockiseq = flags[ 5] != 0 # unused in VM :-)
1635
+ flag_args_kwarg = flags[ 6] != 0
1636
+ flag_args_kw_splat = flags[ 7] != 0
1637
+ _flag_tailcall = flags[ 8] != 0
1638
+ _flag_super = flags[ 9] != 0
1639
+ _flag_zsuper = flags[10] != 0
1640
+
1641
+ if flag_args_blockarg
1642
+ env, (recv, *aargs, blk_ty) = env.pop(argc + 1)
1643
+ raise "both block arg and actual block given" if blk_iseq
1644
+ else
1645
+ env, (recv, *aargs) = env.pop(argc)
1646
+ if blk_iseq
1647
+ # check
1648
+ blk_ty = Type::ISeqProc.new(blk_iseq, ep, Type::Instance.new(Type::Builtin[:proc]))
1649
+ else
1650
+ blk_ty = Type.nil
1651
+ end
1652
+ end
1653
+
1654
+ blk_ty.each_child do |blk_ty|
1655
+ case blk_ty
1656
+ when Type.nil
1657
+ when Type.any
1658
+ when Type::ISeqProc
1659
+ else
1660
+ error(ep, "wrong argument type #{ blk_ty.screen_name(self) } (expected Proc)")
1661
+ blk_ty = Type.any
1662
+ end
1663
+ end
1664
+
1665
+ if flag_args_splat
1666
+ # assert !flag_args_kwarg
1667
+ rest_ty = aargs.last
1668
+ aargs = aargs[0..-2]
1669
+ if flag_args_kw_splat
1670
+ ty = globalize_type(rest_ty, env, ep)
1671
+ if ty.is_a?(Type::Array)
1672
+ _, (ty,) = ty.elems.take_last(1)
1673
+ case ty
1674
+ when Type::Hash
1675
+ kw_ty = ty
1676
+ when Type::Union
1677
+ hash_elems = nil
1678
+ ty.elems&.each do |(container_kind, base_type), elems|
1679
+ if container_kind == Type::Hash
1680
+ hash_elems = hash_elems ? hash_elems.union(elems) : elems
1681
+ end
1682
+ end
1683
+ hash_elems ||= Type::Hash::Elements.new({Type.any => Type.any})
1684
+ kw_ty = Type::Hash.new(hash_elems, Type::Instance.new(Type::Builtin[:hash]))
1685
+ else
1686
+ warn(ep, "non hash is passed to **kwarg?") unless ty == Type.any
1687
+ kw_ty = nil
1688
+ end
1689
+ else
1690
+ raise NotImplementedError
1691
+ end
1692
+ # XXX: should we remove kw_ty from rest_ty?
1693
+ end
1694
+ aargs = ActualArguments.new(aargs, rest_ty, kw_ty, blk_ty)
1695
+ elsif flag_args_kw_splat
1696
+ last = aargs.last
1697
+ ty = globalize_type(last, env, ep)
1698
+ case ty
1699
+ when Type::Hash
1700
+ aargs = aargs[0..-2]
1701
+ kw_ty = ty
1702
+ when Type::Union
1703
+ hash_elems = nil
1704
+ ty.elems.each do |(container_kind, base_type), elems|
1705
+ if container_kind == Type::Hash
1706
+ hash_elems = hash_elems ? hash_elems.union(elems) : elems
1707
+ end
1708
+ end
1709
+ hash_elems ||= Type::Hash::Elements.new({Type.any => Type.any})
1710
+ kw_ty = Type::Hash.new(hash_elems, Type::Instance.new(Type::Builtin[:hash]))
1711
+ when Type::Any
1712
+ aargs = aargs[0..-2]
1713
+ kw_ty = ty
1714
+ else
1715
+ warn(ep, "non hash is passed to **kwarg?")
1716
+ kw_ty = nil
1717
+ end
1718
+ aargs = ActualArguments.new(aargs, nil, kw_ty, blk_ty)
1719
+ elsif flag_args_kwarg
1720
+ kw_vals = aargs.pop(kw_arg.size)
1721
+
1722
+ kw_ty = Type.gen_hash do |h|
1723
+ kw_arg.zip(kw_vals) do |key, v_ty|
1724
+ k_ty = Type::Symbol.new(key, Type::Instance.new(Type::Builtin[:sym]))
1725
+ h[k_ty] = v_ty
1726
+ end
1727
+ end
1728
+
1729
+ # kw_ty is Type::Hash, but we don't have to localize it, maybe?
1730
+
1731
+ aargs = ActualArguments.new(aargs, nil, kw_ty, blk_ty)
1732
+ else
1733
+ aargs = ActualArguments.new(aargs, nil, nil, blk_ty)
1734
+ end
1735
+
1736
+ if blk_iseq
1737
+ # pending dummy execution
1738
+ nctx = Context.new(blk_iseq, ep.ctx.cref, ep.ctx.mid)
1739
+ nep = ExecutionPoint.new(nctx, 0, ep)
1740
+ nlocals = [Type.any] * blk_iseq.locals.size
1741
+ nsenv = StaticEnv.new(env.static_env.recv_ty, Type.any, env.static_env.mod_func)
1742
+ nenv = Env.new(nsenv, nlocals, [], nil)
1743
+ pend_block_dummy_execution(blk_iseq, nep, nenv)
1744
+ merge_return_env(ep) {|tenv| tenv ? tenv.merge(env) : env }
1745
+ end
1746
+
1747
+ return env, recv, mid, aargs
1748
+ end
1749
+
1750
+ def do_send(recv, mid, aargs, ep, env, &ctn)
1751
+ meths = recv.get_method(mid, self)
1752
+ if meths
1753
+ meths.each do |meth|
1754
+ meth.do_send(recv, mid, aargs, ep, env, self, &ctn)
1755
+ end
1756
+ else
1757
+ if recv != Type.any # XXX: should be configurable
1758
+ error(ep, "undefined method: #{ globalize_type(recv, env, ep).screen_name(self) }##{ mid }")
1759
+ end
1760
+ ctn[Type.any, ep, env]
1761
+ end
1762
+ end
1763
+
1764
+ def do_invoke_block(given_block, blk, aargs, ep, env, replace_recv_ty: nil, &ctn)
1765
+ blk.each_child do |blk|
1766
+ unless blk.is_a?(Type::ISeqProc)
1767
+ warn(ep, "non-iseq-proc is passed as a block")
1768
+ next
1769
+ end
1770
+ blk_iseq = blk.iseq
1771
+ blk_ep = blk.ep
1772
+ blk_env = @return_envs[blk_ep]
1773
+ blk_env = blk_env.replace_recv_ty(replace_recv_ty) if replace_recv_ty
1774
+ arg_blk = aargs.blk_ty
1775
+ aargs_ = aargs.lead_tys.map {|aarg| globalize_type(aarg, env, ep) }
1776
+ argc = blk_iseq.fargs_format[:lead_num] || 0
1777
+ # actual argc == 1, not array, formal argc == 1: yield 42 => do |x| : x=42
1778
+ # actual argc == 1, array, formal argc == 1: yield [42,43,44] => do |x| : x=[42,43,44]
1779
+ # actual argc >= 2, formal argc == 1: yield 42,43,44 => do |x| : x=42
1780
+ # actual argc == 1, not array, formal argc >= 2: yield 42 => do |x,y| : x,y=42,nil
1781
+ # actual argc == 1, array, formal argc >= 2: yield [42,43,44] => do |x,y| : x,y=42,43
1782
+ # actual argc >= 2, formal argc >= 2: yield 42,43,44 => do |x,y| : x,y=42,43
1783
+ if aargs_.size >= 2 || argc == 0
1784
+ aargs_.pop while argc < aargs_.size
1785
+ aargs_ << Type.nil while argc > aargs_.size
1786
+ else
1787
+ aarg_ty, = aargs_
1788
+ if argc == 1
1789
+ aargs_ = [aarg_ty || Type.nil]
1790
+ else # actual argc == 1 && formal argc >= 2
1791
+ ary_elems = nil
1792
+ any_ty = nil
1793
+ case aarg_ty
1794
+ when Type::Union
1795
+ ary_elems = nil
1796
+ other_elems = nil
1797
+ aarg_ty.elems&.each do |(container_kind, base_type), elems|
1798
+ if container_kind == Type::Array
1799
+ ary_elems = ary_elems ? ary_elems.union(elems) : elems
1800
+ else
1801
+ other_elems = other_elems ? other_elems.union(elems) : elems
1802
+ end
1803
+ end
1804
+ aarg_ty = Type::Union.new(aarg_ty.types, other_elems)
1805
+ any_ty = Type.any if aarg_ty.types.include?(Type.any)
1806
+ when Type::Array
1807
+ ary_elems = aarg_ty.elems
1808
+ aarg_ty = nil
1809
+ when Type::Any
1810
+ any_ty = Type.any
1811
+ end
1812
+ aargs_ = [Type.bot] * argc
1813
+ aargs_[0] = aargs_[0].union(aarg_ty) if aarg_ty
1814
+ argc.times do |i|
1815
+ ty = aargs_[i]
1816
+ ty = ty.union(ary_elems[i]) if ary_elems
1817
+ ty = ty.union(Type.any) if any_ty
1818
+ ty = ty.union(Type.nil) if i >= 1 && aarg_ty
1819
+ aargs_[i] = ty
1820
+ end
1821
+ end
1822
+ end
1823
+ locals = [Type.nil] * blk_iseq.locals.size
1824
+ locals[blk_iseq.fargs_format[:block_start]] = arg_blk if blk_iseq.fargs_format[:block_start]
1825
+ env_blk = blk_env.static_env.blk_ty
1826
+ nfargs = FormalArguments.new(aargs_, [], nil, [], nil, nil, env_blk) # XXX: aargs_ -> fargs
1827
+ nctx = Context.new(blk_iseq, blk_ep.ctx.cref, nil)
1828
+ nep = ExecutionPoint.new(nctx, 0, blk_ep)
1829
+ nenv = Env.new(blk_env.static_env, locals, [], nil)
1830
+ alloc_site = AllocationSite.new(nep)
1831
+ aargs_.each_with_index do |ty, i|
1832
+ alloc_site2 = alloc_site.add_id(i)
1833
+ nenv, ty = localize_type(ty, nenv, nep, alloc_site2) # Use Scratch#localize_type?
1834
+ nenv = nenv.local_update(i, ty)
1835
+ end
1836
+
1837
+ merge_env(nep, nenv)
1838
+
1839
+ # caution: given_block flag is not complete
1840
+ #
1841
+ # def foo
1842
+ # bar do |&blk|
1843
+ # yield
1844
+ # blk.call
1845
+ # end
1846
+ # end
1847
+ #
1848
+ # yield and blk.call call different blocks.
1849
+ # So, a context can have two blocks.
1850
+ # given_block is calculated by comparing "context's block (yield target)" and "blk", but it is not a correct result
1851
+
1852
+ add_yield!(ep.ctx, globalize_type(aargs, env, ep), nep.ctx) if given_block
1853
+ add_block_to_ctx!(blk, nep.ctx)
1854
+ add_callsite!(nep.ctx, nfargs, ep, env, &ctn)
1855
+ end
1856
+ end
1857
+
1858
+ def proc_screen_name(blk)
1859
+ blk_ctxs = []
1860
+ blk.each_child_global do |blk|
1861
+ if @block_to_ctx[blk]
1862
+ @block_to_ctx[blk].each do |ctx|
1863
+ blk_ctxs << [ctx, @sig_fargs[ctx]]
1864
+ end
1865
+ else
1866
+ # uncalled proc? dummy execution doesn't work?
1867
+ #p blk
1868
+ end
1869
+ end
1870
+ show_block_signature(blk_ctxs)
1871
+ end
1872
+
1873
+ def show_block_signature(blk_ctxs)
1874
+ all_farg_tys = all_ret_tys = nil
1875
+ blk_ctxs.each do |blk_ctx, farg_tys|
1876
+ if all_farg_tys
1877
+ all_farg_tys = all_farg_tys.merge(farg_tys)
1878
+ else
1879
+ all_farg_tys = farg_tys
1880
+ end
1881
+
1882
+ if all_ret_tys
1883
+ all_ret_tys = all_ret_tys.union(@sig_ret[blk_ctx])
1884
+ else
1885
+ all_ret_tys = @sig_ret[blk_ctx]
1886
+ end
1887
+ end
1888
+ return "" if !all_farg_tys
1889
+ # XXX: should support @yields[blk_ctx] (block's block)
1890
+ show_signature(all_farg_tys, nil, all_ret_tys)
1891
+ end
1892
+
1893
+ def show_signature(farg_tys, yield_data, ret_ty)
1894
+ farg_tys = farg_tys.screen_name(self)
1895
+ ret_ty = ret_ty.screen_name(self)
1896
+ s = farg_tys.empty? ? "" : "(#{ farg_tys.join(", ") }) "
1897
+ if yield_data
1898
+ aargs, blk_ctxs = yield_data
1899
+ all_blk_ret_ty = Type.bot
1900
+ blk_ctxs.each do |blk_ctx|
1901
+ all_blk_ret_ty = all_blk_ret_ty.union(@sig_ret[blk_ctx])
1902
+ end
1903
+ all_blk_ret_ty = all_blk_ret_ty.screen_name(self)
1904
+ all_blk_ret_ty = all_blk_ret_ty.include?("|") ? "(#{ all_blk_ret_ty })" : all_blk_ret_ty
1905
+ s << "{ #{ aargs.screen_name(self) } -> #{ all_blk_ret_ty } } " if aargs
1906
+ end
1907
+ s << "-> "
1908
+ s << (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty)
1909
+ end
1910
+ end
1911
+ end