typeprof 0.1.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/Gemfile +2 -2
  4. data/Gemfile.lock +10 -21
  5. data/LICENSE +21 -0
  6. data/README.md +1 -1
  7. data/doc/demo.md +398 -0
  8. data/doc/doc.ja.md +11 -1
  9. data/doc/doc.md +16 -7
  10. data/exe/typeprof +2 -1
  11. data/lib/typeprof.rb +9 -0
  12. data/lib/typeprof/analyzer.rb +455 -364
  13. data/lib/typeprof/arguments.rb +397 -0
  14. data/lib/typeprof/block.rb +133 -0
  15. data/lib/typeprof/builtin.rb +125 -116
  16. data/lib/typeprof/cli.rb +62 -71
  17. data/lib/typeprof/config.rb +114 -0
  18. data/lib/typeprof/container-type.rb +208 -27
  19. data/lib/typeprof/export.rb +201 -96
  20. data/lib/typeprof/import.rb +451 -365
  21. data/lib/typeprof/iseq.rb +43 -2
  22. data/lib/typeprof/method.rb +139 -100
  23. data/lib/typeprof/type.rb +138 -297
  24. data/lib/typeprof/utils.rb +4 -18
  25. data/lib/typeprof/version.rb +3 -0
  26. data/smoke/arguments2.rb +55 -0
  27. data/smoke/array-each3.rb +1 -4
  28. data/smoke/array12.rb +1 -1
  29. data/smoke/array6.rb +1 -0
  30. data/smoke/block-ambiguous.rb +36 -0
  31. data/smoke/block-args1-rest.rb +62 -0
  32. data/smoke/block-args1.rb +59 -0
  33. data/smoke/block-args2-rest.rb +62 -0
  34. data/smoke/block-args2.rb +59 -0
  35. data/smoke/block-args3-rest.rb +73 -0
  36. data/smoke/block-args3.rb +70 -0
  37. data/smoke/block-blockarg.rb +27 -0
  38. data/smoke/block-kwarg.rb +52 -0
  39. data/smoke/block11.rb +1 -1
  40. data/smoke/block13.rb +9 -0
  41. data/smoke/block13.rbs +3 -0
  42. data/smoke/block14.rb +17 -0
  43. data/smoke/block4.rb +2 -2
  44. data/smoke/block5.rb +1 -0
  45. data/smoke/block6.rb +1 -1
  46. data/smoke/block7.rb +0 -2
  47. data/smoke/block8.rb +2 -2
  48. data/smoke/block9.rb +1 -1
  49. data/smoke/blown.rb +1 -1
  50. data/smoke/class-hierarchy.rb +54 -0
  51. data/smoke/class-hierarchy2.rb +27 -0
  52. data/smoke/class.rb +2 -0
  53. data/smoke/constant1.rb +13 -5
  54. data/smoke/constant2.rb +2 -0
  55. data/smoke/cvar.rb +1 -0
  56. data/smoke/demo10.rb +1 -1
  57. data/smoke/demo5.rb +3 -0
  58. data/smoke/demo8.rb +2 -2
  59. data/smoke/demo9.rb +1 -3
  60. data/smoke/flow7.rb +1 -7
  61. data/smoke/flow8.rb +13 -0
  62. data/smoke/gvar.rb +1 -1
  63. data/smoke/gvar2.rb +17 -0
  64. data/smoke/gvar2.rbs +1 -0
  65. data/smoke/hash4.rb +1 -1
  66. data/smoke/inheritance2.rb +6 -0
  67. data/smoke/instance_eval.rb +1 -1
  68. data/smoke/int_times.rb +1 -1
  69. data/smoke/ivar3.rb +16 -0
  70. data/smoke/ivar3.rbs +3 -0
  71. data/smoke/keyword3.rb +1 -2
  72. data/smoke/keyword4.rb +1 -1
  73. data/smoke/manual-rbs2.rb +1 -1
  74. data/smoke/manual-rbs3.rb +12 -0
  75. data/smoke/manual-rbs3.rbs +3 -0
  76. data/smoke/module4.rb +5 -0
  77. data/smoke/multiple-superclass.rb +12 -0
  78. data/smoke/next2.rb +1 -1
  79. data/smoke/optional1.rb +1 -1
  80. data/smoke/optional2.rb +1 -1
  81. data/smoke/optional3.rb +10 -0
  82. data/smoke/proc4.rb +1 -1
  83. data/smoke/rbs-alias.rb +9 -0
  84. data/smoke/rbs-alias.rbs +4 -0
  85. data/smoke/rbs-attr.rb +26 -0
  86. data/smoke/rbs-attr.rbs +5 -0
  87. data/smoke/rbs-extend.rb +9 -0
  88. data/smoke/rbs-extend.rbs +7 -0
  89. data/smoke/rbs-interface.rb +24 -0
  90. data/smoke/rbs-interface.rbs +12 -0
  91. data/smoke/rbs-proc1.rb +9 -0
  92. data/smoke/rbs-proc1.rbs +3 -0
  93. data/smoke/rbs-proc2.rb +20 -0
  94. data/smoke/rbs-proc2.rbs +3 -0
  95. data/smoke/rbs-proc3.rb +13 -0
  96. data/smoke/rbs-proc3.rbs +4 -0
  97. data/smoke/rbs-record.rb +17 -0
  98. data/smoke/rbs-record.rbs +4 -0
  99. data/smoke/rbs-tyvar.rb +18 -0
  100. data/smoke/rbs-tyvar.rbs +5 -0
  101. data/smoke/rbs-tyvar2.rb +20 -0
  102. data/smoke/rbs-tyvar2.rbs +9 -0
  103. data/smoke/rbs-tyvar3.rb +25 -0
  104. data/smoke/rbs-tyvar3.rbs +4 -0
  105. data/smoke/rbs-vars.rb +39 -0
  106. data/smoke/rbs-vars.rbs +7 -0
  107. data/smoke/rest1.rb +1 -1
  108. data/smoke/rest2.rb +1 -1
  109. data/smoke/rest3.rb +1 -1
  110. data/smoke/rest5.rb +1 -1
  111. data/smoke/rest6.rb +1 -1
  112. data/smoke/retry1.rb +1 -1
  113. data/smoke/return.rb +1 -1
  114. data/smoke/singleton_method.rb +3 -0
  115. data/smoke/step.rb +1 -1
  116. data/smoke/struct.rb +6 -2
  117. data/smoke/struct3.rb +14 -0
  118. data/smoke/super1.rb +18 -0
  119. data/smoke/symbol-proc.rb +24 -0
  120. data/smoke/union-recv.rb +6 -0
  121. data/smoke/user-demo.rb +15 -0
  122. data/smoke/wrong-extend.rb +1 -0
  123. data/tools/setup-insns-def.rb +1 -1
  124. data/tools/stackprof-wrapper.rb +1 -1
  125. data/typeprof.gemspec +12 -4
  126. metadata +68 -10
  127. data/.gitmodules +0 -6
  128. data/run.sh +0 -3
  129. data/smoke/variadic1.rb.notyet +0 -5
@@ -59,16 +59,19 @@ module TypeProf
59
59
  end
60
60
 
61
61
  def proc_call(recv, mid, aargs, ep, env, scratch, &ctn)
62
- given_block = env.static_env.blk_ty == recv
63
- scratch.do_invoke_block(given_block, recv, aargs, ep, env, &ctn)
62
+ scratch.do_invoke_block(recv, aargs, ep, env, &ctn)
64
63
  end
65
64
 
66
65
  def object_s_new(recv, mid, aargs, ep, env, scratch, &ctn)
67
66
  ty = Type::Instance.new(recv)
67
+ if recv.type_params.size >= 1
68
+ ty = Type::Cell.new(Type::Cell::Elements.new([Type.bot] * recv.type_params.size), ty)
69
+ env, ty = scratch.localize_type(ty, env, ep, AllocationSite.new(ep).add_id(:object_s_new))
70
+ end
68
71
  meths = scratch.get_method(recv, false, :initialize)
69
72
  meths.flat_map do |meth|
70
73
  meth.do_send(ty, :initialize, aargs, ep, env, scratch) do |ret_ty, ep, env|
71
- ctn[Type::Instance.new(recv), ep, env]
74
+ ctn[ty, ep, env]
72
75
  end
73
76
  end
74
77
  end
@@ -118,7 +121,7 @@ module TypeProf
118
121
  else
119
122
  mid_ty = aargs.rest_ty
120
123
  end
121
- aargs = ActualArguments.new(aargs.lead_tys[1..-1], aargs.rest_ty, aargs.kw_ty, aargs.blk_ty)
124
+ aargs = ActualArguments.new(aargs.lead_tys[1..-1], aargs.rest_ty, aargs.kw_tys, aargs.blk_ty)
122
125
  found = false
123
126
  mid_ty.each_child do |mid|
124
127
  if mid.is_a?(Type::Symbol)
@@ -138,16 +141,15 @@ module TypeProf
138
141
  ctn[type.any, ep, env]
139
142
  return
140
143
  end
141
- naargs = ActualArguments.new([recv], Type.nil, nil, Type.nil)
142
- given_block = env.static_env.blk_ty == recv
143
- scratch.do_invoke_block(given_block, aargs.blk_ty, naargs, ep, env, replace_recv_ty: recv) do |_ret_ty, ep|
144
+ naargs = ActualArguments.new([recv], nil, {}, Type.nil)
145
+ scratch.do_invoke_block(aargs.blk_ty, naargs, ep, env, replace_recv_ty: recv) do |_ret_ty, ep|
144
146
  ctn[recv, ep, scratch.return_envs[ep]]
145
147
  end
146
148
  end
147
149
 
148
150
  def module_include(recv, mid, aargs, ep, env, scratch, &ctn)
149
151
  arg = aargs.lead_tys[0]
150
- scratch.include_module(recv, arg)
152
+ scratch.include_module(recv, arg, ep.ctx.iseq.absolute_path)
151
153
  ctn[recv, ep, env]
152
154
  end
153
155
 
@@ -155,7 +157,7 @@ module TypeProf
155
157
  arg = aargs.lead_tys[0]
156
158
  arg.each_child do |arg|
157
159
  if arg.is_a?(Type::Class)
158
- scratch.extend_module(recv, arg)
160
+ scratch.extend_module(recv, arg, ep.ctx.iseq.absolute_path)
159
161
  end
160
162
  end
161
163
  ctn[recv, ep, env]
@@ -180,7 +182,7 @@ module TypeProf
180
182
  aargs.lead_tys.each do |aarg|
181
183
  sym = get_sym("attr_accessor", aarg, ep, scratch) or next
182
184
  cref = ep.ctx.cref
183
- scratch.add_attr_method(cref.klass, sym, :"@#{ sym }", :accessor)
185
+ scratch.add_attr_method(cref.klass, ep.ctx.iseq.absolute_path, sym, :"@#{ sym }", :accessor)
184
186
  end
185
187
  ctn[Type.nil, ep, env]
186
188
  end
@@ -189,7 +191,7 @@ module TypeProf
189
191
  aargs.lead_tys.each do |aarg|
190
192
  sym = get_sym("attr_reader", aarg, ep, scratch) or next
191
193
  cref = ep.ctx.cref
192
- scratch.add_attr_method(cref.klass, sym, :"@#{ sym }", :reader)
194
+ scratch.add_attr_method(cref.klass, ep.ctx.iseq.absolute_path, sym, :"@#{ sym }", :reader)
193
195
  end
194
196
  ctn[Type.nil, ep, env]
195
197
  end
@@ -198,7 +200,7 @@ module TypeProf
198
200
  aargs.lead_tys.each do |aarg|
199
201
  sym = get_sym("attr_writer", aarg, ep, scratch) or next
200
202
  cref = ep.ctx.cref
201
- scratch.add_attr_method(cref.klass, sym, :"@#{ sym }", :writer)
203
+ scratch.add_attr_method(cref.klass, ep.ctx.iseq.absolute_path, sym, :"@#{ sym }", :writer)
202
204
  end
203
205
  ctn[Type.nil, ep, env]
204
206
  end
@@ -244,7 +246,10 @@ module TypeProf
244
246
  def array_aset(recv, mid, aargs, ep, env, scratch, &ctn)
245
247
  return ctn[Type.any, ep, env] unless recv.is_a?(Type::LocalArray)
246
248
 
247
- raise NotImplementedError if aargs.lead_tys.size != 2
249
+ if aargs.lead_tys.size != 2
250
+ #raise NotImplementedError # XXX
251
+ ctn[Type.any, ep, env]
252
+ end
248
253
 
249
254
  idx = aargs.lead_tys.first
250
255
  if idx.is_a?(Type::Literal)
@@ -256,7 +261,7 @@ module TypeProf
256
261
 
257
262
  ty = aargs.lead_tys.last
258
263
 
259
- env = scratch.update_container_elem_types(env, ep, recv.id) do |elems|
264
+ env = scratch.update_container_elem_types(env, ep, recv.id, recv.base_type) do |elems|
260
265
  elems.update(idx, ty)
261
266
  end
262
267
 
@@ -283,8 +288,6 @@ module TypeProf
283
288
  return
284
289
  end
285
290
  idx = aargs.lead_tys.first
286
- #idx = scratch.globalize_type(idx, env, ep)
287
- # XXX: recv may be a union
288
291
  recv.each_child do |recv|
289
292
  if recv.is_a?(Type::LocalHash)
290
293
  ty = scratch.get_hash_elem_type(env, ep, recv.id, idx)
@@ -309,7 +312,7 @@ module TypeProf
309
312
  return ctn[ty, ep, env]
310
313
  end
311
314
 
312
- env = scratch.update_container_elem_types(env, ep, recv.id) do |elems|
315
+ env = scratch.update_container_elem_types(env, ep, recv.id, recv.base_type) do |elems|
313
316
  elems.update(idx, ty)
314
317
  end
315
318
 
@@ -318,14 +321,12 @@ module TypeProf
318
321
 
319
322
  def struct_initialize(recv, mid, aargs, ep, env, scratch, &ctn)
320
323
  recv = Type::Instance.new(recv)
321
- scratch.get_instance_variable(recv, :_members, ep, env) do |ty, nenv|
322
- ty = scratch.globalize_type(ty, nenv, ep)
323
- ty.elems.lead_tys.zip(aargs.lead_tys) do |sym, ty|
324
+ scratch.add_ivar_read!(recv, :_members, ep) do |member_ary_ty, ep|
325
+ member_ary_ty.elems.lead_tys.zip(aargs.lead_tys) do |sym, ty|
324
326
  ty ||= Type.nil
325
327
  scratch.set_instance_variable(recv, sym.sym, ty, ep, env)
326
328
  end
327
329
  end
328
- #scratch.set_instance_variable(recv, , ty, ep, env)
329
330
  ctn[recv, ep, env]
330
331
  end
331
332
 
@@ -339,9 +340,8 @@ module TypeProf
339
340
  return
340
341
  end
341
342
  if struct_klass != recv
342
- scratch.get_instance_variable(Type::Instance.new(struct_klass), :_members, ep, env) do |ty, nenv|
343
- ty = scratch.globalize_type(ty, nenv, ep)
344
- scratch.set_instance_variable(Type::Instance.new(recv), :_members, ty, ep, env)
343
+ scratch.add_ivar_read!(Type::Instance.new(struct_klass), :_members, ep) do |ty, ep|
344
+ scratch.add_ivar_write!(Type::Instance.new(recv), :_members, ty, ep)
345
345
  end
346
346
  end
347
347
  meths = scratch.get_method(recv, false, :initialize)
@@ -358,76 +358,85 @@ module TypeProf
358
358
  fields = aargs.lead_tys.map {|ty| get_sym("Struct.new", ty, ep, scratch) }.compact
359
359
  struct_klass = scratch.new_struct(ep)
360
360
 
361
- scratch.add_singleton_custom_method(struct_klass, :new, Builtin.method(:struct_i_new))
362
- scratch.add_singleton_custom_method(struct_klass, :[], Builtin.method(:struct_i_new))
361
+ scratch.set_singleton_custom_method(struct_klass, :new, Builtin.method(:struct_i_new))
362
+ scratch.set_singleton_custom_method(struct_klass, :[], Builtin.method(:struct_i_new))
363
363
  fields.each do |field|
364
- scratch.add_attr_method(struct_klass, field, field, :accessor)
364
+ scratch.add_attr_method(struct_klass, ep.ctx.iseq.absolute_path, field, field, :accessor)
365
365
  end
366
366
  fields = fields.map {|field| Type::Symbol.new(field, Type::Instance.new(Type::Builtin[:sym])) }
367
367
  base_ty = Type::Instance.new(Type::Builtin[:ary])
368
368
  fields = Type::Array.new(Type::Array::Elements.new(fields), base_ty)
369
- scratch.set_instance_variable(Type::Instance.new(struct_klass), :_members, fields, ep, env)
370
- #add_singleton_custom_method(struct_klass, :members, Builtin.method(:...))
369
+ scratch.add_ivar_write!(Type::Instance.new(struct_klass), :_members, fields, ep)
370
+ #set_singleton_custom_method(struct_klass, :members, Builtin.method(:...))
371
371
 
372
372
  ctn[struct_klass, ep, env]
373
373
  end
374
374
 
375
- def file_load(path, ep, env, scratch, &ctn)
375
+ def self.file_load(path, ep, env, scratch, &ctn)
376
376
  iseq = ISeq.compile(path)
377
- callee_ep, callee_env = CLI.starting_state(iseq)
377
+ callee_ep, callee_env = TypeProf.starting_state(iseq)
378
378
  scratch.merge_env(callee_ep, callee_env)
379
379
 
380
- scratch.add_callsite!(callee_ep.ctx, nil, ep, env) do |_ret_ty, ep|
380
+ scratch.add_callsite!(callee_ep.ctx, ep, env) do |_ret_ty, ep|
381
381
  ret_ty = Type::Instance.new(Type::Builtin[:true])
382
382
  ctn[ret_ty, ep, env]
383
383
  end
384
384
  end
385
385
 
386
+ def self.file_require(feature, scratch)
387
+ return :done, :false if scratch.loaded_features[feature]
388
+ scratch.loaded_features[feature] = true
389
+
390
+ # XXX: dynamic RBS load is really needed?? Another idea:
391
+ #
392
+ # * RBS should be loaded in advance of analysis
393
+ # * require "some_gem/foo" should be ignored
394
+ # * require "app/foo" should always load .rb file (in this case, app/foo.rb)
395
+ return :done, :true if Import.import_library(scratch, feature)
396
+
397
+ # Try to analyze the source code of the gem
398
+ begin
399
+ gem feature
400
+ rescue Gem::MissingSpecError, Gem::LoadError
401
+ end
402
+
403
+ begin
404
+ filetype, path = $LOAD_PATH.resolve_feature_path(feature)
405
+ if filetype == :rb
406
+ return :do, path if File.readable?(path)
407
+
408
+ return :error, "failed to load: #{ path }"
409
+ else
410
+ return :error, "cannot load a .so file: #{ path }"
411
+ end
412
+ rescue LoadError
413
+ return :error, "failed to require: #{ feature }"
414
+ end
415
+ end
416
+
386
417
  def kernel_require(recv, mid, aargs, ep, env, scratch, &ctn)
387
418
  raise NotImplementedError if aargs.lead_tys.size != 1
388
419
  feature = aargs.lead_tys.first
389
420
  if feature.is_a?(Type::Literal)
390
421
  feature = feature.lit
391
422
 
392
- begin
393
- if scratch.loaded_features[feature]
394
- result = Type::Instance.new(Type::Builtin[:false])
395
- return ctn[result, ep, env]
396
- end
397
- scratch.loaded_features[feature] = true
398
-
399
- # XXX: dynamic RBS load is really needed?? Another idea:
400
- #
401
- # * RBS should be loaded in advance of analysis
402
- # * require "some_gem/foo" should be ignored
403
- # * require "app/foo" should always load .rb file (in this case, app/foo.rb)
404
- if RubySignatureImporter.import_library(scratch, feature)
405
- result = Type::Instance.new(Type::Builtin[:true])
406
- return ctn[result, ep, env]
407
- end
408
-
409
- begin
410
- gem feature
411
- rescue Gem::MissingSpecError, Gem::LoadError
412
- end
413
- filetype, path = $LOAD_PATH.resolve_feature_path(feature)
414
- if filetype == :rb
415
- # TODO: if there is RBS file for the library, do not read the source code
416
- return file_load(path, ep, env, scratch, &ctn) if File.readable?(path)
417
-
418
- scratch.warn(ep, "failed to load: #{ path }")
419
- else
420
- scratch.warn(ep, "cannot load a .so file: #{ path }")
421
- end
422
- rescue LoadError
423
- scratch.warn(ep, "failed to require: #{ feature }")
423
+ action, arg = Builtin.file_require(feature, scratch)
424
+ case action
425
+ when :do
426
+ Builtin.file_load(arg, ep, env, scratch, &ctn)
427
+ when :done
428
+ result = Type::Instance.new(Type::Builtin[arg])
429
+ ctn[result, ep, env]
430
+ when :error
431
+ scratch.warn(ep, arg)
432
+ result = Type::Instance.new(Type.bool)
433
+ ctn[result, ep, env]
424
434
  end
425
435
  else
426
436
  scratch.warn(ep, "require target cannot be identified statically")
437
+ result = Type::Instance.new(Type.bool)
438
+ ctn[result, ep, env]
427
439
  end
428
-
429
- result = Type::Instance.new(Type::Builtin[:true])
430
- ctn[result, ep, env]
431
440
  end
432
441
 
433
442
  def kernel_require_relative(recv, mid, aargs, ep, env, scratch, &ctn)
@@ -443,7 +452,7 @@ module TypeProf
443
452
  scratch.loaded_features[feature] = true
444
453
 
445
454
  path = File.join(File.dirname(ep.ctx.iseq.path), feature) + ".rb" # XXX
446
- return file_load(path, ep, env, scratch, &ctn) if File.readable?(path)
455
+ return Builtin.file_load(path, ep, env, scratch, &ctn) if File.readable?(path)
447
456
 
448
457
  scratch.warn(ep, "failed to load: #{ path }")
449
458
  else
@@ -473,20 +482,17 @@ module TypeProf
473
482
  end
474
483
 
475
484
  def self.setup_initial_global_env(scratch)
476
- klass_obj = scratch.new_class(nil, :Object, [], :__root__) # cbase, name, superclass
477
- scratch.add_constant(klass_obj, "Object", klass_obj)
478
- klass_true = scratch.new_class(klass_obj, :TrueClass, [], klass_obj) # ???
479
- klass_false = scratch.new_class(klass_obj, :FalseClass, [], klass_obj) # ???
480
- klass_nil = scratch.new_class(klass_obj, :NilClass, [], klass_obj) # ???
485
+ klass_basic_obj = scratch.new_class(nil, :BasicObject, [], :__root__, nil) # cbase, name, superclass
486
+ klass_obj = scratch.new_class(nil, :Object, [], klass_basic_obj, nil)
487
+ scratch.add_constant(klass_obj, :Object, klass_obj, nil)
488
+ scratch.add_constant(klass_obj, :BasicObject, klass_basic_obj, nil)
481
489
 
490
+ Type::Builtin[:basic_obj] = klass_basic_obj
482
491
  Type::Builtin[:obj] = klass_obj
483
- Type::Builtin[:true] = klass_true
484
- Type::Builtin[:false] = klass_false
485
- Type::Builtin[:nil] = klass_nil
486
492
 
487
- RubySignatureImporter.import_builtin(scratch)
493
+ Import.import_builtin(scratch)
488
494
 
489
- Type::Builtin[:vmcore] = scratch.new_class(klass_obj, :VMCore, [], klass_obj)
495
+ Type::Builtin[:vmcore] = scratch.new_class(klass_obj, :VMCore, [], klass_obj, nil)
490
496
  Type::Builtin[:int] = scratch.get_constant(klass_obj, :Integer)
491
497
  Type::Builtin[:float] = scratch.get_constant(klass_obj, :Float)
492
498
  Type::Builtin[:rational] = scratch.get_constant(klass_obj, :Rational)
@@ -511,44 +517,47 @@ module TypeProf
511
517
  klass_proc = Type::Builtin[:proc]
512
518
  klass_module = Type::Builtin[:module]
513
519
 
514
- scratch.add_custom_method(klass_vmcore, :"core#set_method_alias", Builtin.method(:vmcore_set_method_alias))
515
- scratch.add_custom_method(klass_vmcore, :"core#undef_method", Builtin.method(:vmcore_undef_method))
516
- scratch.add_custom_method(klass_vmcore, :"core#hash_merge_kwd", Builtin.method(:vmcore_hash_merge_kwd))
517
- scratch.add_custom_method(klass_vmcore, :lambda, Builtin.method(:lambda))
518
- scratch.add_singleton_custom_method(klass_obj, :"new", Builtin.method(:object_s_new))
519
- scratch.add_singleton_custom_method(klass_obj, :"attr_accessor", Builtin.method(:module_attr_accessor))
520
- scratch.add_singleton_custom_method(klass_obj, :"attr_reader", Builtin.method(:module_attr_reader))
521
- scratch.add_singleton_custom_method(klass_obj, :"attr_writer", Builtin.method(:module_attr_writer))
522
- scratch.add_custom_method(klass_obj, :p, Builtin.method(:kernel_p))
523
- scratch.add_custom_method(klass_obj, :is_a?, Builtin.method(:object_is_a?))
524
- scratch.add_custom_method(klass_obj, :respond_to?, Builtin.method(:object_respond_to?))
525
- scratch.add_custom_method(klass_obj, :class, Builtin.method(:object_class))
526
- scratch.add_custom_method(klass_obj, :send, Builtin.method(:object_send))
527
- scratch.add_custom_method(klass_obj, :instance_eval, Builtin.method(:object_instance_eval))
528
-
529
- scratch.add_custom_method(klass_module, :include, Builtin.method(:module_include))
530
- scratch.add_custom_method(klass_module, :extend, Builtin.method(:module_extend))
531
- scratch.add_custom_method(klass_module, :module_function, Builtin.method(:module_module_function))
532
-
533
- scratch.add_custom_method(klass_proc, :[], Builtin.method(:proc_call))
534
- scratch.add_custom_method(klass_proc, :call, Builtin.method(:proc_call))
535
-
536
- scratch.add_custom_method(klass_ary, :[], Builtin.method(:array_aref))
537
- scratch.add_custom_method(klass_ary, :[]=, Builtin.method(:array_aset))
538
- scratch.add_custom_method(klass_ary, :pop, Builtin.method(:array_pop))
539
-
540
- scratch.add_custom_method(klass_hash, :[], Builtin.method(:hash_aref))
541
- scratch.add_custom_method(klass_hash, :[]=, Builtin.method(:hash_aset))
542
-
543
- scratch.add_custom_method(klass_struct, :initialize, Builtin.method(:struct_initialize))
544
- scratch.add_singleton_custom_method(klass_struct, :new, Builtin.method(:struct_s_new))
545
-
546
- scratch.add_custom_method(klass_obj, :require, Builtin.method(:kernel_require))
547
- scratch.add_custom_method(klass_obj, :require_relative, Builtin.method(:kernel_require_relative))
548
- scratch.add_custom_method(klass_obj, :Array, Builtin.method(:kernel_Array))
549
-
550
- fargs, ret_ty = FormalArguments.new([], [], nil, [], nil, nil, Type.any), Type.any
551
- scratch.add_method(klass_obj, :initialize, false, TypedMethodDef.new([[fargs, ret_ty]], nil))
520
+ scratch.set_custom_method(klass_vmcore, :"core#set_method_alias", Builtin.method(:vmcore_set_method_alias))
521
+ scratch.set_custom_method(klass_vmcore, :"core#undef_method", Builtin.method(:vmcore_undef_method))
522
+ scratch.set_custom_method(klass_vmcore, :"core#hash_merge_kwd", Builtin.method(:vmcore_hash_merge_kwd))
523
+ scratch.set_custom_method(klass_vmcore, :lambda, Builtin.method(:lambda))
524
+ scratch.set_singleton_custom_method(klass_obj, :"new", Builtin.method(:object_s_new))
525
+ scratch.set_singleton_custom_method(klass_obj, :"attr_accessor", Builtin.method(:module_attr_accessor))
526
+ scratch.set_singleton_custom_method(klass_obj, :"attr_reader", Builtin.method(:module_attr_reader))
527
+ scratch.set_singleton_custom_method(klass_obj, :"attr_writer", Builtin.method(:module_attr_writer))
528
+ scratch.set_custom_method(klass_obj, :p, Builtin.method(:kernel_p))
529
+ scratch.set_custom_method(klass_obj, :is_a?, Builtin.method(:object_is_a?))
530
+ scratch.set_custom_method(klass_obj, :respond_to?, Builtin.method(:object_respond_to?))
531
+ scratch.set_custom_method(klass_obj, :class, Builtin.method(:object_class))
532
+ scratch.set_custom_method(klass_obj, :send, Builtin.method(:object_send))
533
+ scratch.set_custom_method(klass_obj, :instance_eval, Builtin.method(:object_instance_eval))
534
+ scratch.set_custom_method(klass_obj, :proc, Builtin.method(:lambda))
535
+
536
+ scratch.set_custom_method(klass_module, :include, Builtin.method(:module_include))
537
+ scratch.set_custom_method(klass_module, :extend, Builtin.method(:module_extend))
538
+ scratch.set_custom_method(klass_module, :module_function, Builtin.method(:module_module_function))
539
+
540
+ scratch.set_custom_method(klass_proc, :[], Builtin.method(:proc_call))
541
+ scratch.set_custom_method(klass_proc, :call, Builtin.method(:proc_call))
542
+
543
+ scratch.set_custom_method(klass_ary, :[], Builtin.method(:array_aref))
544
+ scratch.set_custom_method(klass_ary, :[]=, Builtin.method(:array_aset))
545
+ scratch.set_custom_method(klass_ary, :pop, Builtin.method(:array_pop))
546
+
547
+ scratch.set_custom_method(klass_hash, :[], Builtin.method(:hash_aref))
548
+ scratch.set_custom_method(klass_hash, :[]=, Builtin.method(:hash_aset))
549
+
550
+ scratch.set_custom_method(klass_struct, :initialize, Builtin.method(:struct_initialize))
551
+ scratch.set_singleton_custom_method(klass_struct, :new, Builtin.method(:struct_s_new))
552
+
553
+ scratch.set_custom_method(klass_obj, :require, Builtin.method(:kernel_require))
554
+ scratch.set_custom_method(klass_obj, :require_relative, Builtin.method(:kernel_require_relative))
555
+ scratch.set_custom_method(klass_obj, :Array, Builtin.method(:kernel_Array))
556
+
557
+ # ENV: Hash[String, String]
558
+ str_ty = Type::Instance.new(Type::Builtin[:str])
559
+ env_ty = Type.gen_hash {|h| h[str_ty] = Type.optional(str_ty) }
560
+ scratch.add_constant(klass_obj, :ENV, env_ty, false)
552
561
  end
553
562
  end
554
563
  end
@@ -1,38 +1,62 @@
1
1
  require "optparse"
2
2
 
3
3
  module TypeProf
4
- class CLI
5
- def initialize(argv)
4
+ module CLI
5
+ module_function
6
+
7
+ def parse(argv)
6
8
  opt = OptionParser.new
7
9
 
8
- @output = nil
10
+ output = nil
9
11
 
10
12
  # Verbose level:
11
13
  # * 0: no output
12
14
  # * 1: show indicator
13
15
  # * 2: debug print
14
- @verbose = 1
15
-
16
- @options = {
17
- type_depth_limit: 5,
18
- pedantic_output: false,
19
- show_errors: false,
20
- }
16
+ verbose = 1
17
+
18
+ options = {}
19
+ dir_filter = nil
20
+ gem_rbs_features = []
21
+ version = false
22
+ max_sec = max_iter = nil
23
+
24
+ opt.on("-o OUTFILE") {|v| output = v }
25
+ opt.on("-q", "--quiet") { verbose = 0 }
26
+ opt.on("-v", "--verbose") { options[:show_errors] = true }
27
+ opt.on("--version") { version = true }
28
+ opt.on("-d", "--debug") { verbose = 2 }
29
+ opt.on("-I DIR") {|v| $LOAD_PATH << v }
30
+ opt.on("-r FEATURE") {|v| gem_rbs_features << v }
31
+ opt.on("--max-second SECOND", Float) {|v| max_sec = v }
32
+ opt.on("--max-iteration TIMES", Integer) {|v| max_iter = v }
33
+
34
+ opt.on("--include-dir DIR") do |dir|
35
+ # When `--include-dir` option is specified as the first directory option,
36
+ # typeprof will exclude any files by default unless a file path matches the explicit option
37
+ dir_filter ||= [[:exclude]]
38
+ dir_filter << [:include, File.expand_path(dir)]
39
+ end
40
+ opt.on("--exclude-dir DIR") do |dir|
41
+ # When `--exclude-dir` option is specified as the first directory option,
42
+ # typeprof will include any files by default, except Ruby's install directory and Gem directories
43
+ dir_filter ||= ConfigData::DEFAULT_DIR_FILTER
44
+ dir_filter << [:exclude, File.expand_path(dir)]
45
+ end
21
46
 
22
- opt.on("-o OUTFILE") {|v| @output = v }
23
- opt.on("-q", "--quiet") {|v| @verbose = 0 }
24
- opt.on("-v", "--verbose") {|v| @verbose = 2 }
25
47
  opt.on("-f OPTION") do |v|
26
48
  key, args = v.split("=", 2)
27
49
  case key
28
50
  when "type-depth-limit"
29
- @options[:type_depth_limit] = Integer(args)
51
+ options[:type_depth_limit] = Integer(args)
30
52
  when "pedantic-output"
31
- @options[:pedantic_output] = true
53
+ options[:pedantic_output] = true
32
54
  when "show-errors"
33
- @options[:show_errors] = true
55
+ options[:show_errors] = true
34
56
  when "show-container-raw-elements"
35
- @options[:show_container_raw_elements] = true
57
+ options[:show_container_raw_elements] = true
58
+ when "stackprof"
59
+ options[:stackprof] = args ? args.to_sym : :cpu
36
60
  else
37
61
  raise OptionParser::InvalidOption.new("unknown option: #{ key }")
38
62
  end
@@ -40,71 +64,38 @@ module TypeProf
40
64
 
41
65
  opt.parse!(argv)
42
66
 
43
- @rb_files = []
44
- @rbs_files = []
67
+ dir_filter ||= ConfigData::DEFAULT_DIR_FILTER
68
+ rb_files = []
69
+ rbs_files = []
45
70
  argv.each do |path|
46
71
  if File.extname(path) == ".rbs"
47
- @rbs_files << path
72
+ rbs_files << path
48
73
  else
49
- @rb_files << path
74
+ rb_files << path
50
75
  end
51
76
  end
52
77
 
53
- raise OptionParser::InvalidOption.new("no input files") if @rb_files.empty?
78
+ puts "typeprof #{ VERSION }" if version
79
+ if rb_files.empty?
80
+ exit if version
81
+ raise OptionParser::InvalidOption.new("no input files")
82
+ end
54
83
 
55
- TypeProf.const_set(:Config, self)
84
+ ConfigData.new(
85
+ rb_files: rb_files,
86
+ rbs_files: rbs_files,
87
+ output: output,
88
+ gem_rbs_features: gem_rbs_features,
89
+ verbose: verbose,
90
+ dir_filter: dir_filter,
91
+ max_sec: max_sec,
92
+ max_iter: max_iter,
93
+ options: options,
94
+ )
56
95
 
57
96
  rescue OptionParser::InvalidOption
58
97
  puts $!
59
98
  exit
60
99
  end
61
-
62
- attr_reader :verbose, :options, :files
63
- attr_accessor :output
64
-
65
- def run
66
- scratch = Scratch.new
67
- Builtin.setup_initial_global_env(scratch)
68
-
69
- prologue_ctx = Context.new(nil, nil, nil)
70
- prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
71
- prologue_env = Env.new(StaticEnv.new(:top, Type.nil, false), [], [], Utils::HashWrapper.new({}))
72
-
73
- @rb_files.each do |path|
74
- if path == "-"
75
- iseq = ISeq.compile_str($<.read)
76
- else
77
- iseq = ISeq.compile(path)
78
- end
79
- ep, env = CLI.starting_state(iseq)
80
- scratch.merge_env(ep, env)
81
- scratch.add_callsite!(ep.ctx, nil, prologue_ep, prologue_env) {|ty, ep| }
82
- end
83
-
84
- @rbs_files.each do |path|
85
- RubySignatureImporter.import_rbs_file(scratch, path)
86
- end
87
-
88
- result = scratch.type_profile
89
-
90
- if @output
91
- open(@output, "w") do |output|
92
- scratch.report(result, output)
93
- end
94
- else
95
- scratch.report(result, $stdout)
96
- end
97
- end
98
-
99
- def self.starting_state(iseq)
100
- cref = CRef.new(:bottom, Type::Builtin[:obj], false) # object
101
- recv = Type::Instance.new(Type::Builtin[:obj])
102
- ctx = Context.new(iseq, cref, nil)
103
- ep = ExecutionPoint.new(ctx, 0, nil)
104
- locals = [Type.nil] * iseq.locals.size
105
- env = Env.new(StaticEnv.new(recv, Type.nil, false), locals, [], Utils::HashWrapper.new({}))
106
-
107
- return ep, env
108
- end
109
100
  end
110
101
  end