typeprof 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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