typeprof 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +9 -20
- data/LICENSE +21 -0
- data/doc/doc.ja.md +2 -1
- data/doc/doc.md +3 -2
- data/exe/typeprof +2 -1
- data/lib/typeprof.rb +1 -0
- data/lib/typeprof/analyzer.rb +163 -84
- data/lib/typeprof/builtin.rb +20 -19
- data/lib/typeprof/cli.rb +51 -98
- data/lib/typeprof/config.rb +114 -0
- data/lib/typeprof/container-type.rb +125 -21
- data/lib/typeprof/export.rb +20 -8
- data/lib/typeprof/import.rb +43 -20
- data/lib/typeprof/iseq.rb +17 -1
- data/lib/typeprof/method.rb +33 -9
- data/lib/typeprof/type.rb +52 -24
- data/lib/typeprof/utils.rb +4 -18
- data/lib/typeprof/version.rb +3 -0
- data/smoke/arguments2.rb +55 -0
- data/smoke/hash4.rb +1 -1
- data/smoke/keyword3.rb +1 -2
- data/smoke/keyword4.rb +1 -1
- data/smoke/module4.rb +2 -0
- data/smoke/optional1.rb +1 -1
- data/smoke/optional2.rb +1 -1
- data/smoke/rbs-extend.rb +9 -0
- data/smoke/rbs-extend.rbs +7 -0
- data/smoke/rbs-interface.rb +24 -0
- data/smoke/rbs-interface.rbs +12 -0
- data/smoke/rbs-tyvar.rb +18 -0
- data/smoke/rbs-tyvar.rbs +5 -0
- data/smoke/rbs-tyvar2.rb +20 -0
- data/smoke/rbs-tyvar2.rbs +9 -0
- data/smoke/rest1.rb +1 -1
- data/smoke/rest3.rb +1 -1
- data/smoke/rest6.rb +1 -1
- data/smoke/retry1.rb +1 -1
- data/smoke/step.rb +1 -1
- data/smoke/user-demo.rb +15 -0
- data/smoke/wrong-extend.rb +1 -0
- data/typeprof.gemspec +4 -2
- metadata +17 -5
- data/run.sh +0 -3
    
        data/lib/typeprof/builtin.rb
    CHANGED
    
    | @@ -65,10 +65,14 @@ module TypeProf | |
| 65 65 |  | 
| 66 66 | 
             
                def object_s_new(recv, mid, aargs, ep, env, scratch, &ctn)
         | 
| 67 67 | 
             
                  ty = Type::Instance.new(recv)
         | 
| 68 | 
            +
                  if recv.type_params.size >= 1
         | 
| 69 | 
            +
                    ty = Type::Cell.new([Type.bot] * recv.type_params.size, ty)
         | 
| 70 | 
            +
                    env, ty = scratch.localize_type(ty, env, ep, AllocationSite.new(ep).add_id(:object_s_new))
         | 
| 71 | 
            +
                  end
         | 
| 68 72 | 
             
                  meths = scratch.get_method(recv, false, :initialize)
         | 
| 69 73 | 
             
                  meths.flat_map do |meth|
         | 
| 70 74 | 
             
                    meth.do_send(ty, :initialize, aargs, ep, env, scratch) do |ret_ty, ep, env|
         | 
| 71 | 
            -
                      ctn[ | 
| 75 | 
            +
                      ctn[ty, ep, env]
         | 
| 72 76 | 
             
                    end
         | 
| 73 77 | 
             
                  end
         | 
| 74 78 | 
             
                end
         | 
| @@ -118,7 +122,7 @@ module TypeProf | |
| 118 122 | 
             
                  else
         | 
| 119 123 | 
             
                    mid_ty = aargs.rest_ty
         | 
| 120 124 | 
             
                  end
         | 
| 121 | 
            -
                  aargs = ActualArguments.new(aargs.lead_tys[1..-1], aargs.rest_ty, aargs. | 
| 125 | 
            +
                  aargs = ActualArguments.new(aargs.lead_tys[1..-1], aargs.rest_ty, aargs.kw_tys, aargs.blk_ty)
         | 
| 122 126 | 
             
                  found = false
         | 
| 123 127 | 
             
                  mid_ty.each_child do |mid|
         | 
| 124 128 | 
             
                    if mid.is_a?(Type::Symbol)
         | 
| @@ -138,7 +142,7 @@ module TypeProf | |
| 138 142 | 
             
                    ctn[type.any, ep, env]
         | 
| 139 143 | 
             
                    return
         | 
| 140 144 | 
             
                  end
         | 
| 141 | 
            -
                  naargs = ActualArguments.new([recv], Type.nil,  | 
| 145 | 
            +
                  naargs = ActualArguments.new([recv], Type.nil, {}, Type.nil)
         | 
| 142 146 | 
             
                  given_block = env.static_env.blk_ty == recv
         | 
| 143 147 | 
             
                  scratch.do_invoke_block(given_block, aargs.blk_ty, naargs, ep, env, replace_recv_ty: recv) do |_ret_ty, ep|
         | 
| 144 148 | 
             
                    ctn[recv, ep, scratch.return_envs[ep]]
         | 
| @@ -244,7 +248,10 @@ module TypeProf | |
| 244 248 | 
             
                def array_aset(recv, mid, aargs, ep, env, scratch, &ctn)
         | 
| 245 249 | 
             
                  return ctn[Type.any, ep, env] unless recv.is_a?(Type::LocalArray)
         | 
| 246 250 |  | 
| 247 | 
            -
                   | 
| 251 | 
            +
                  if aargs.lead_tys.size != 2
         | 
| 252 | 
            +
                    #raise NotImplementedError # XXX
         | 
| 253 | 
            +
                    ctn[Type.any, ep, env]
         | 
| 254 | 
            +
                  end
         | 
| 248 255 |  | 
| 249 256 | 
             
                  idx = aargs.lead_tys.first
         | 
| 250 257 | 
             
                  if idx.is_a?(Type::Literal)
         | 
| @@ -316,14 +323,12 @@ module TypeProf | |
| 316 323 |  | 
| 317 324 | 
             
                def struct_initialize(recv, mid, aargs, ep, env, scratch, &ctn)
         | 
| 318 325 | 
             
                  recv = Type::Instance.new(recv)
         | 
| 319 | 
            -
                  scratch. | 
| 320 | 
            -
                     | 
| 321 | 
            -
                    ty.elems.lead_tys.zip(aargs.lead_tys) do |sym, ty|
         | 
| 326 | 
            +
                  scratch.add_ivar_read!(recv, :_members, ep) do |member_ary_ty, ep|
         | 
| 327 | 
            +
                    member_ary_ty.elems.lead_tys.zip(aargs.lead_tys) do |sym, ty|
         | 
| 322 328 | 
             
                      ty ||= Type.nil
         | 
| 323 329 | 
             
                      scratch.set_instance_variable(recv, sym.sym, ty, ep, env)
         | 
| 324 330 | 
             
                    end
         | 
| 325 331 | 
             
                  end
         | 
| 326 | 
            -
                  #scratch.set_instance_variable(recv, , ty, ep, env)
         | 
| 327 332 | 
             
                  ctn[recv, ep, env]
         | 
| 328 333 | 
             
                end
         | 
| 329 334 |  | 
| @@ -337,9 +342,8 @@ module TypeProf | |
| 337 342 | 
             
                    return
         | 
| 338 343 | 
             
                  end
         | 
| 339 344 | 
             
                  if struct_klass != recv
         | 
| 340 | 
            -
                    scratch. | 
| 341 | 
            -
                       | 
| 342 | 
            -
                      scratch.set_instance_variable(Type::Instance.new(recv), :_members, ty, ep, env)
         | 
| 345 | 
            +
                    scratch.add_ivar_read!(Type::Instance.new(struct_klass), :_members, ep) do |ty, ep|
         | 
| 346 | 
            +
                      scratch.add_ivar_write!(Type::Instance.new(recv), :_members, ty, ep)
         | 
| 343 347 | 
             
                    end
         | 
| 344 348 | 
             
                  end
         | 
| 345 349 | 
             
                  meths = scratch.get_method(recv, false, :initialize)
         | 
| @@ -364,7 +368,7 @@ module TypeProf | |
| 364 368 | 
             
                  fields = fields.map {|field| Type::Symbol.new(field, Type::Instance.new(Type::Builtin[:sym])) }
         | 
| 365 369 | 
             
                  base_ty = Type::Instance.new(Type::Builtin[:ary])
         | 
| 366 370 | 
             
                  fields = Type::Array.new(Type::Array::Elements.new(fields), base_ty)
         | 
| 367 | 
            -
                  scratch. | 
| 371 | 
            +
                  scratch.add_ivar_write!(Type::Instance.new(struct_klass), :_members, fields, ep)
         | 
| 368 372 | 
             
                  #set_singleton_custom_method(struct_klass, :members, Builtin.method(:...))
         | 
| 369 373 |  | 
| 370 374 | 
             
                  ctn[struct_klass, ep, env]
         | 
| @@ -372,10 +376,10 @@ module TypeProf | |
| 372 376 |  | 
| 373 377 | 
             
                def self.file_load(path, ep, env, scratch, &ctn)
         | 
| 374 378 | 
             
                  iseq = ISeq.compile(path)
         | 
| 375 | 
            -
                  callee_ep, callee_env =  | 
| 379 | 
            +
                  callee_ep, callee_env = TypeProf.starting_state(iseq)
         | 
| 376 380 | 
             
                  scratch.merge_env(callee_ep, callee_env)
         | 
| 377 381 |  | 
| 378 | 
            -
                  scratch.add_callsite!(callee_ep.ctx,  | 
| 382 | 
            +
                  scratch.add_callsite!(callee_ep.ctx, ep, env) do |_ret_ty, ep|
         | 
| 379 383 | 
             
                    ret_ty = Type::Instance.new(Type::Builtin[:true])
         | 
| 380 384 | 
             
                    ctn[ret_ty, ep, env]
         | 
| 381 385 | 
             
                  end
         | 
| @@ -482,8 +486,8 @@ module TypeProf | |
| 482 486 | 
             
                def self.setup_initial_global_env(scratch)
         | 
| 483 487 | 
             
                  klass_basic_obj = scratch.new_class(nil, :BasicObject, [], :__root__, nil) # cbase, name, superclass
         | 
| 484 488 | 
             
                  klass_obj = scratch.new_class(nil, :Object, [], klass_basic_obj, nil)
         | 
| 485 | 
            -
                  scratch.add_constant(klass_obj,  | 
| 486 | 
            -
                  scratch.add_constant(klass_obj,  | 
| 489 | 
            +
                  scratch.add_constant(klass_obj, :Object, klass_obj)
         | 
| 490 | 
            +
                  scratch.add_constant(klass_obj, :BasicObject, klass_basic_obj)
         | 
| 487 491 |  | 
| 488 492 | 
             
                  Type::Builtin[:basic_obj] = klass_basic_obj
         | 
| 489 493 | 
             
                  Type::Builtin[:obj]   = klass_obj
         | 
| @@ -550,9 +554,6 @@ module TypeProf | |
| 550 554 | 
             
                  scratch.set_custom_method(klass_obj, :require, Builtin.method(:kernel_require))
         | 
| 551 555 | 
             
                  scratch.set_custom_method(klass_obj, :require_relative, Builtin.method(:kernel_require_relative))
         | 
| 552 556 | 
             
                  scratch.set_custom_method(klass_obj, :Array, Builtin.method(:kernel_Array))
         | 
| 553 | 
            -
             | 
| 554 | 
            -
                  fargs, ret_ty = FormalArguments.new([], [], nil, [], nil, nil, Type.any), Type.any
         | 
| 555 | 
            -
                  scratch.add_method(klass_obj, :initialize, false, TypedMethodDef.new([[fargs, ret_ty]], nil))
         | 
| 556 557 | 
             
                end
         | 
| 557 558 | 
             
              end
         | 
| 558 559 | 
             
            end
         | 
    
        data/lib/typeprof/cli.rb
    CHANGED
    
    | @@ -1,64 +1,62 @@ | |
| 1 1 | 
             
            require "optparse"
         | 
| 2 | 
            -
            require "rbconfig"
         | 
| 3 2 |  | 
| 4 3 | 
             
            module TypeProf
         | 
| 5 | 
            -
               | 
| 6 | 
            -
                 | 
| 7 | 
            -
                  [:include],
         | 
| 8 | 
            -
                  [:exclude, RbConfig::CONFIG["prefix"]],
         | 
| 9 | 
            -
                  [:exclude, Gem.dir],
         | 
| 10 | 
            -
                  [:exclude, Gem.user_dir],
         | 
| 11 | 
            -
                ]
         | 
| 4 | 
            +
              module CLI
         | 
| 5 | 
            +
                module_function
         | 
| 12 6 |  | 
| 13 | 
            -
                def  | 
| 7 | 
            +
                def parse(argv)
         | 
| 14 8 | 
             
                  opt = OptionParser.new
         | 
| 15 9 |  | 
| 16 | 
            -
                   | 
| 10 | 
            +
                  output = nil
         | 
| 17 11 |  | 
| 18 12 | 
             
                  # Verbose level:
         | 
| 19 13 | 
             
                  # * 0: no output
         | 
| 20 14 | 
             
                  # * 1: show indicator
         | 
| 21 15 | 
             
                  # * 2: debug print
         | 
| 22 | 
            -
                   | 
| 23 | 
            -
             | 
| 24 | 
            -
                   | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
                   | 
| 29 | 
            -
             | 
| 30 | 
            -
                   | 
| 31 | 
            -
             | 
| 32 | 
            -
                  opt.on("- | 
| 33 | 
            -
                  opt.on(" | 
| 34 | 
            -
                  opt.on("- | 
| 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 }
         | 
| 35 29 | 
             
                  opt.on("-I DIR") {|v| $LOAD_PATH << v }
         | 
| 36 | 
            -
                  opt.on("-r FEATURE") {|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 }
         | 
| 37 33 |  | 
| 38 34 | 
             
                  opt.on("--include-dir DIR") do |dir|
         | 
| 39 35 | 
             
                    # When `--include-dir` option is specified as the first directory option,
         | 
| 40 36 | 
             
                    # typeprof will exclude any files by default unless a file path matches the explicit option
         | 
| 41 | 
            -
                     | 
| 42 | 
            -
                     | 
| 37 | 
            +
                    dir_filter ||= [[:exclude]]
         | 
| 38 | 
            +
                    dir_filter << [:include, File.expand_path(dir)]
         | 
| 43 39 | 
             
                  end
         | 
| 44 40 | 
             
                  opt.on("--exclude-dir DIR") do |dir|
         | 
| 45 41 | 
             
                    # When `--exclude-dir` option is specified as the first directory option,
         | 
| 46 42 | 
             
                    # typeprof will include any files by default, except Ruby's install directory and Gem directories
         | 
| 47 | 
            -
                     | 
| 48 | 
            -
                     | 
| 43 | 
            +
                    dir_filter ||= ConfigData::DEFAULT_DIR_FILTER
         | 
| 44 | 
            +
                    dir_filter << [:exclude, File.expand_path(dir)]
         | 
| 49 45 | 
             
                  end
         | 
| 50 46 |  | 
| 51 47 | 
             
                  opt.on("-f OPTION") do |v|
         | 
| 52 48 | 
             
                    key, args = v.split("=", 2)
         | 
| 53 49 | 
             
                    case key
         | 
| 54 50 | 
             
                    when "type-depth-limit"
         | 
| 55 | 
            -
                       | 
| 51 | 
            +
                      options[:type_depth_limit] = Integer(args)
         | 
| 56 52 | 
             
                    when "pedantic-output"
         | 
| 57 | 
            -
                       | 
| 53 | 
            +
                      options[:pedantic_output] = true
         | 
| 58 54 | 
             
                    when "show-errors"
         | 
| 59 | 
            -
                       | 
| 55 | 
            +
                      options[:show_errors] = true
         | 
| 60 56 | 
             
                    when "show-container-raw-elements"
         | 
| 61 | 
            -
                       | 
| 57 | 
            +
                      options[:show_container_raw_elements] = true
         | 
| 58 | 
            +
                    when "stackprof"
         | 
| 59 | 
            +
                      options[:stackprof] = args ? args.to_sym : :cpu
         | 
| 62 60 | 
             
                    else
         | 
| 63 61 | 
             
                      raise OptionParser::InvalidOption.new("unknown option: #{ key }")
         | 
| 64 62 | 
             
                    end
         | 
| @@ -66,83 +64,38 @@ module TypeProf | |
| 66 64 |  | 
| 67 65 | 
             
                  opt.parse!(argv)
         | 
| 68 66 |  | 
| 69 | 
            -
                   | 
| 70 | 
            -
                   | 
| 71 | 
            -
                   | 
| 67 | 
            +
                  dir_filter ||= ConfigData::DEFAULT_DIR_FILTER
         | 
| 68 | 
            +
                  rb_files = []
         | 
| 69 | 
            +
                  rbs_files = []
         | 
| 72 70 | 
             
                  argv.each do |path|
         | 
| 73 71 | 
             
                    if File.extname(path) == ".rbs"
         | 
| 74 | 
            -
                       | 
| 72 | 
            +
                      rbs_files << path
         | 
| 75 73 | 
             
                    else
         | 
| 76 | 
            -
                       | 
| 74 | 
            +
                      rb_files << path
         | 
| 77 75 | 
             
                    end
         | 
| 78 76 | 
             
                  end
         | 
| 79 77 |  | 
| 80 | 
            -
                   | 
| 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
         | 
| 81 83 |  | 
| 82 | 
            -
                   | 
| 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 | 
            +
                  )
         | 
| 83 95 |  | 
| 84 96 | 
             
                rescue OptionParser::InvalidOption
         | 
| 85 97 | 
             
                  puts $!
         | 
| 86 98 | 
             
                  exit
         | 
| 87 99 | 
             
                end
         | 
| 88 | 
            -
             | 
| 89 | 
            -
                attr_reader :verbose, :options, :dir_filter
         | 
| 90 | 
            -
                attr_accessor :output
         | 
| 91 | 
            -
             | 
| 92 | 
            -
                def check_dir_filter(path)
         | 
| 93 | 
            -
                  @dir_filter.reverse_each do |cond, dir|
         | 
| 94 | 
            -
                    return cond unless dir
         | 
| 95 | 
            -
                    return cond if path.start_with?(dir)
         | 
| 96 | 
            -
                  end
         | 
| 97 | 
            -
                end
         | 
| 98 | 
            -
             | 
| 99 | 
            -
                def run
         | 
| 100 | 
            -
                  scratch = Scratch.new
         | 
| 101 | 
            -
                  Builtin.setup_initial_global_env(scratch)
         | 
| 102 | 
            -
             | 
| 103 | 
            -
                  @rbs_features_to_load.each do |feature|
         | 
| 104 | 
            -
                    Import.import_library(scratch, feature)
         | 
| 105 | 
            -
                  end
         | 
| 106 | 
            -
             | 
| 107 | 
            -
                  prologue_ctx = Context.new(nil, nil, nil)
         | 
| 108 | 
            -
                  prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
         | 
| 109 | 
            -
                  prologue_env = Env.new(StaticEnv.new(:top, Type.nil, false), [], [], Utils::HashWrapper.new({}))
         | 
| 110 | 
            -
             | 
| 111 | 
            -
                  @rb_files.each do |path|
         | 
| 112 | 
            -
                    if path == "-"
         | 
| 113 | 
            -
                      iseq = ISeq.compile_str($<.read)
         | 
| 114 | 
            -
                    else
         | 
| 115 | 
            -
                      iseq = ISeq.compile(path)
         | 
| 116 | 
            -
                    end
         | 
| 117 | 
            -
                    ep, env = CLI.starting_state(iseq)
         | 
| 118 | 
            -
                    scratch.merge_env(ep, env)
         | 
| 119 | 
            -
                    scratch.add_callsite!(ep.ctx, nil, prologue_ep, prologue_env) {|ty, ep| }
         | 
| 120 | 
            -
                  end
         | 
| 121 | 
            -
             | 
| 122 | 
            -
                  @rbs_files.each do |path|
         | 
| 123 | 
            -
                    Import.import_rbs_file(scratch, path)
         | 
| 124 | 
            -
                  end
         | 
| 125 | 
            -
             | 
| 126 | 
            -
                  result = scratch.type_profile
         | 
| 127 | 
            -
             | 
| 128 | 
            -
                  if @output
         | 
| 129 | 
            -
                    open(@output, "w") do |output|
         | 
| 130 | 
            -
                      scratch.report(result, output)
         | 
| 131 | 
            -
                    end
         | 
| 132 | 
            -
                  else
         | 
| 133 | 
            -
                    scratch.report(result, $stdout)
         | 
| 134 | 
            -
                  end
         | 
| 135 | 
            -
                end
         | 
| 136 | 
            -
             | 
| 137 | 
            -
                def self.starting_state(iseq)
         | 
| 138 | 
            -
                  cref = CRef.new(:bottom, Type::Builtin[:obj], false) # object
         | 
| 139 | 
            -
                  recv = Type::Instance.new(Type::Builtin[:obj])
         | 
| 140 | 
            -
                  ctx = Context.new(iseq, cref, nil)
         | 
| 141 | 
            -
                  ep = ExecutionPoint.new(ctx, 0, nil)
         | 
| 142 | 
            -
                  locals = [Type.nil] * iseq.locals.size
         | 
| 143 | 
            -
                  env = Env.new(StaticEnv.new(recv, Type.nil, false), locals, [], Utils::HashWrapper.new({}))
         | 
| 144 | 
            -
             | 
| 145 | 
            -
                  return ep, env
         | 
| 146 | 
            -
                end
         | 
| 147 100 | 
             
              end
         | 
| 148 101 | 
             
            end
         | 
| @@ -0,0 +1,114 @@ | |
| 1 | 
            +
            require "rbconfig"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module TypeProf
         | 
| 4 | 
            +
              ConfigData = Struct.new(
         | 
| 5 | 
            +
                :rb_files,
         | 
| 6 | 
            +
                :rbs_files,
         | 
| 7 | 
            +
                :output,
         | 
| 8 | 
            +
                :gem_rbs_features,
         | 
| 9 | 
            +
                :verbose,
         | 
| 10 | 
            +
                :dir_filter,
         | 
| 11 | 
            +
                :max_iter,
         | 
| 12 | 
            +
                :max_sec,
         | 
| 13 | 
            +
                :options,
         | 
| 14 | 
            +
                keyword_init: true
         | 
| 15 | 
            +
              )
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              class ConfigData
         | 
| 18 | 
            +
                def initialize(**opt)
         | 
| 19 | 
            +
                  opt[:output] ||= $stdout
         | 
| 20 | 
            +
                  opt[:gem_rbs_features] ||= []
         | 
| 21 | 
            +
                  opt[:dir_filter] ||= DEFAULT_DIR_FILTER
         | 
| 22 | 
            +
                  opt[:verbose] ||= 0
         | 
| 23 | 
            +
                  opt[:options] ||= {}
         | 
| 24 | 
            +
                  opt[:options] = {
         | 
| 25 | 
            +
                    type_depth_limit: 5,
         | 
| 26 | 
            +
                    pedantic_output: false,
         | 
| 27 | 
            +
                    show_errors: false,
         | 
| 28 | 
            +
                    stackprof: nil,
         | 
| 29 | 
            +
                  }.merge(opt[:options])
         | 
| 30 | 
            +
                  super(**opt)
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def check_dir_filter(path)
         | 
| 34 | 
            +
                  dir_filter.reverse_each do |cond, dir|
         | 
| 35 | 
            +
                    return cond unless dir
         | 
| 36 | 
            +
                    return cond if path.start_with?(dir)
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                DEFAULT_DIR_FILTER = [
         | 
| 41 | 
            +
                  [:include],
         | 
| 42 | 
            +
                  [:exclude, RbConfig::CONFIG["prefix"]],
         | 
| 43 | 
            +
                  [:exclude, Gem.dir],
         | 
| 44 | 
            +
                  [:exclude, Gem.user_dir],
         | 
| 45 | 
            +
                ]
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              def self.analyze(config)
         | 
| 49 | 
            +
                # Deploy the config to the TypeProf::Config (Note: This is thread unsafe)
         | 
| 50 | 
            +
                if TypeProf.const_defined?(:Config)
         | 
| 51 | 
            +
                  TypeProf.send(:remove_const, :Config)
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
                TypeProf.const_set(:Config, config)
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                if Config.options[:stackprof]
         | 
| 56 | 
            +
                  require "stackprof"
         | 
| 57 | 
            +
                  out = "typeprof-stackprof-#{ Config.options[:stackprof] }.dump"
         | 
| 58 | 
            +
                  StackProf.start(mode: Config.options[:stackprof], out: out, raw: true)
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                scratch = Scratch.new
         | 
| 62 | 
            +
                Builtin.setup_initial_global_env(scratch)
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                Config.gem_rbs_features.each do |feature|
         | 
| 65 | 
            +
                  Import.import_library(scratch, feature)
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                prologue_ctx = Context.new(nil, nil, nil)
         | 
| 69 | 
            +
                prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
         | 
| 70 | 
            +
                prologue_env = Env.new(StaticEnv.new(:top, Type.nil, false), [], [], Utils::HashWrapper.new({}))
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                Config.rb_files.each do |file|
         | 
| 73 | 
            +
                  if file.respond_to?(:read)
         | 
| 74 | 
            +
                    iseq = ISeq.compile_str(file.read, file.to_s)
         | 
| 75 | 
            +
                  else
         | 
| 76 | 
            +
                    iseq = ISeq.compile(file)
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
                  ep, env = TypeProf.starting_state(iseq)
         | 
| 79 | 
            +
                  scratch.merge_env(ep, env)
         | 
| 80 | 
            +
                  scratch.add_callsite!(ep.ctx, prologue_ep, prologue_env) {|ty, ep| }
         | 
| 81 | 
            +
                end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                Config.rbs_files.each do |path|
         | 
| 84 | 
            +
                  Import.import_rbs_file(scratch, path)
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                result = scratch.type_profile
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                if Config.output.respond_to?(:write)
         | 
| 90 | 
            +
                  scratch.report(result, Config.output)
         | 
| 91 | 
            +
                else
         | 
| 92 | 
            +
                  open(Config.output, "w") do |output|
         | 
| 93 | 
            +
                    scratch.report(result, output)
         | 
| 94 | 
            +
                  end
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
              ensure
         | 
| 98 | 
            +
                if Config.options[:stackprof] && defined?(StackProf)
         | 
| 99 | 
            +
                  StackProf.stop
         | 
| 100 | 
            +
                  StackProf.results
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
              end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
              def self.starting_state(iseq)
         | 
| 105 | 
            +
                cref = CRef.new(:bottom, Type::Builtin[:obj], false) # object
         | 
| 106 | 
            +
                recv = Type::Instance.new(Type::Builtin[:obj])
         | 
| 107 | 
            +
                ctx = Context.new(iseq, cref, nil)
         | 
| 108 | 
            +
                ep = ExecutionPoint.new(ctx, 0, nil)
         | 
| 109 | 
            +
                locals = [Type.nil] * iseq.locals.size
         | 
| 110 | 
            +
                env = Env.new(StaticEnv.new(recv, Type.nil, false), locals, [], Utils::HashWrapper.new({}))
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                return ep, env
         | 
| 113 | 
            +
              end
         | 
| 114 | 
            +
            end
         | 
| @@ -6,22 +6,123 @@ module TypeProf | |
| 6 6 | 
             
                  raise if !val.is_a?(Utils::StructuralEquality) && !val.is_a?(Integer) && !val.is_a?(Symbol)
         | 
| 7 7 | 
             
                  @val = val
         | 
| 8 8 | 
             
                  @parent = parent
         | 
| 9 | 
            -
                  @_hash ||= (@val.hash ^ @parent.hash)
         | 
| 10 9 | 
             
                end
         | 
| 11 10 |  | 
| 12 11 | 
             
                attr_reader :val, :parent
         | 
| 13 12 |  | 
| 14 | 
            -
                def hash
         | 
| 15 | 
            -
                  @_hash
         | 
| 16 | 
            -
                end
         | 
| 17 | 
            -
             | 
| 18 13 | 
             
                def add_id(val)
         | 
| 19 14 | 
             
                  AllocationSite.new(val, self)
         | 
| 20 15 | 
             
                end
         | 
| 21 16 | 
             
              end
         | 
| 22 17 |  | 
| 23 18 | 
             
              class Type # or AbstractValue
         | 
| 24 | 
            -
                #  | 
| 19 | 
            +
                # Cell, Array, and Hash are types for global interface, e.g., TypedISeq.
         | 
| 20 | 
            +
                # Do not push such types to local environment, stack, etc.
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                # The most basic container type for default type parameter class
         | 
| 23 | 
            +
                class Cell < Type
         | 
| 24 | 
            +
                  def initialize(elems, base_type)
         | 
| 25 | 
            +
                    @elems = elems # Array[Symbol]
         | 
| 26 | 
            +
                    raise unless base_type
         | 
| 27 | 
            +
                    @base_type = base_type
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  attr_reader :elems, :base_type
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def inspect
         | 
| 33 | 
            +
                    "Type::Cell[#{ @elems.inspect }, base_type: #{ @base_type.inspect }]"
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def screen_name(scratch)
         | 
| 37 | 
            +
                    "#{ @base_type.screen_name(scratch) }[#{ @elems.map {|elem| elem.screen_name(scratch) }.join(", ") }]"
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  def localize(env, alloc_site, depth)
         | 
| 41 | 
            +
                    return env, Type.any if depth <= 0
         | 
| 42 | 
            +
                    alloc_site = alloc_site.add_id(:cell)
         | 
| 43 | 
            +
                    elems = @elems.map.with_index do |elem, idx|
         | 
| 44 | 
            +
                      env, elem = elem.localize(env, alloc_site.add_id(idx), depth - 1)
         | 
| 45 | 
            +
                      elem
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
                    env.deploy_cell_type(alloc_site, elems, @base_type)
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  def limit_size(limit)
         | 
| 51 | 
            +
                    return Type.any if limit <= 0
         | 
| 52 | 
            +
                    Cell.new(@elems.map {|elem| elem.limit_size(limit - 1) }, @base_type)
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  def get_method(mid, scratch)
         | 
| 56 | 
            +
                    raise
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  def consistent?(other, subst)
         | 
| 60 | 
            +
                    case other
         | 
| 61 | 
            +
                    when Type::Any then true
         | 
| 62 | 
            +
                    when Type::Var then other.add_subst!(self, subst)
         | 
| 63 | 
            +
                    when Type::Union
         | 
| 64 | 
            +
                      other.types.each do |ty2|
         | 
| 65 | 
            +
                        return true if consistent?(ty2, subst)
         | 
| 66 | 
            +
                      end
         | 
| 67 | 
            +
                      return false
         | 
| 68 | 
            +
                    when Type::Cell
         | 
| 69 | 
            +
                      @elems.size == other.elems.size &&
         | 
| 70 | 
            +
                        @base_type.consistent?(other.base_type, subst) &&
         | 
| 71 | 
            +
                        @elems.zip(other.elems).all? {|elem1, elem2| elem1..consistent?(elem2, subst) }
         | 
| 72 | 
            +
                    else
         | 
| 73 | 
            +
                      self == other
         | 
| 74 | 
            +
                    end
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  def substitute(subst, depth)
         | 
| 78 | 
            +
                    return Type.any if depth <= 0
         | 
| 79 | 
            +
                    elems = @elems.map {|elem| elem.substitute(subst, depth - 1) }
         | 
| 80 | 
            +
                    Cell.new(elems, @base_type)
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                class LocalCell < Type
         | 
| 85 | 
            +
                  def initialize(id, base_type)
         | 
| 86 | 
            +
                    @id = id
         | 
| 87 | 
            +
                    raise unless base_type
         | 
| 88 | 
            +
                    @base_type = base_type
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  attr_reader :id, :base_type
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                  def inspect
         | 
| 94 | 
            +
                    "Type::LocalCell[#{ @id }, base_type: #{ @base_type.inspect }]"
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  def screen_name(scratch)
         | 
| 98 | 
            +
                    #raise "LocalArray must not be included in signature"
         | 
| 99 | 
            +
                    "LocalCell!"
         | 
| 100 | 
            +
                  end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                  def globalize(env, visited, depth)
         | 
| 103 | 
            +
                    if visited[self] || depth <= 0
         | 
| 104 | 
            +
                      Type.any
         | 
| 105 | 
            +
                    else
         | 
| 106 | 
            +
                      visited[self] = true
         | 
| 107 | 
            +
                      elems = env.get_container_elem_types(@id)
         | 
| 108 | 
            +
                      if elems
         | 
| 109 | 
            +
                        elems = elems.map {|elem| elem.globalize(env, visited, depth - 1) }
         | 
| 110 | 
            +
                      else
         | 
| 111 | 
            +
                        elems = [] # XXX
         | 
| 112 | 
            +
                      end
         | 
| 113 | 
            +
                      Cell.new(elems, @base_type)
         | 
| 114 | 
            +
                    end
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                  def get_method(mid, scratch)
         | 
| 118 | 
            +
                    @base_type.get_method(mid, scratch)
         | 
| 119 | 
            +
                  end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                  def consistent?(other, subst)
         | 
| 122 | 
            +
                    raise "must not be used"
         | 
| 123 | 
            +
                  end
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
             | 
| 25 126 | 
             
                # Do not insert Array type to local environment, stack, etc.
         | 
| 26 127 | 
             
                class Array < Type
         | 
| 27 128 | 
             
                  def initialize(elems, base_type)
         | 
| @@ -29,7 +130,6 @@ module TypeProf | |
| 29 130 | 
             
                    @elems = elems # Array::Elements
         | 
| 30 131 | 
             
                    raise unless base_type
         | 
| 31 132 | 
             
                    @base_type = base_type
         | 
| 32 | 
            -
                    # XXX: need infinite recursion
         | 
| 33 133 | 
             
                  end
         | 
| 34 134 |  | 
| 35 135 | 
             
                  attr_reader :elems, :base_type
         | 
| @@ -47,13 +147,6 @@ module TypeProf | |
| 47 147 | 
             
                    str
         | 
| 48 148 | 
             
                  end
         | 
| 49 149 |  | 
| 50 | 
            -
                  def globalize(env, visited, depth)
         | 
| 51 | 
            -
                    return Type.any if depth <= 0
         | 
| 52 | 
            -
                    elems = @elems.globalize(env, visited, depth - 1)
         | 
| 53 | 
            -
                    base_ty = @base_type.globalize(env, visited, depth - 1)
         | 
| 54 | 
            -
                    Array.new(elems, base_ty)
         | 
| 55 | 
            -
                  end
         | 
| 56 | 
            -
             | 
| 57 150 | 
             
                  def localize(env, alloc_site, depth)
         | 
| 58 151 | 
             
                    return env, Type.any if depth <= 0
         | 
| 59 152 | 
             
                    alloc_site = alloc_site.add_id(:ary)
         | 
| @@ -78,6 +171,7 @@ module TypeProf | |
| 78 171 | 
             
                      other.types.each do |ty2|
         | 
| 79 172 | 
             
                        return true if consistent?(ty2, subst)
         | 
| 80 173 | 
             
                      end
         | 
| 174 | 
            +
                      return false
         | 
| 81 175 | 
             
                    when Type::Array
         | 
| 82 176 | 
             
                      @base_type.consistent?(other.base_type, subst) && @elems.consistent?(other.elems, subst)
         | 
| 83 177 | 
             
                    else
         | 
| @@ -365,13 +459,6 @@ module TypeProf | |
| 365 459 | 
             
                    @elems.screen_name(scratch)
         | 
| 366 460 | 
             
                  end
         | 
| 367 461 |  | 
| 368 | 
            -
                  def globalize(env, visited, depth)
         | 
| 369 | 
            -
                    return Type.any if depth <= 0
         | 
| 370 | 
            -
                    elems = @elems.globalize(env, visited, depth - 1)
         | 
| 371 | 
            -
                    base_ty = @base_type.globalize(env, visited, depth - 1)
         | 
| 372 | 
            -
                    Hash.new(elems, base_ty)
         | 
| 373 | 
            -
                  end
         | 
| 374 | 
            -
             | 
| 375 462 | 
             
                  def localize(env, alloc_site, depth)
         | 
| 376 463 | 
             
                    return env, Type.any if depth <= 0
         | 
| 377 464 | 
             
                    alloc_site = alloc_site.add_id(:hash)
         | 
| @@ -396,6 +483,7 @@ module TypeProf | |
| 396 483 | 
             
                      other.types.each do |ty2|
         | 
| 397 484 | 
             
                        return true if consistent?(ty2, subst)
         | 
| 398 485 | 
             
                      end
         | 
| 486 | 
            +
                      return false
         | 
| 399 487 | 
             
                    when Type::Hash
         | 
| 400 488 | 
             
                      @base_type.consistent?(other.base_type, subst) && @elems.consistent?(other.elems, subst)
         | 
| 401 489 | 
             
                    else
         | 
| @@ -579,6 +667,22 @@ module TypeProf | |
| 579 667 |  | 
| 580 668 | 
             
                      Elements.new(map_tys)
         | 
| 581 669 | 
             
                    end
         | 
| 670 | 
            +
             | 
| 671 | 
            +
                    def to_keywords
         | 
| 672 | 
            +
                      kw_tys = {}
         | 
| 673 | 
            +
                      @map_tys.each do |key_ty, val_ty|
         | 
| 674 | 
            +
                        if key_ty.is_a?(Type::Symbol)
         | 
| 675 | 
            +
                          kw_tys[key_ty.sym] = val_ty
         | 
| 676 | 
            +
                        else
         | 
| 677 | 
            +
                          all_val_ty = Type.bot
         | 
| 678 | 
            +
                          @map_tys.each do |_key_ty, val_ty|
         | 
| 679 | 
            +
                            all_val_ty = all_val_ty.union(val_ty)
         | 
| 680 | 
            +
                          end
         | 
| 681 | 
            +
                          return { nil => all_val_ty }
         | 
| 682 | 
            +
                        end
         | 
| 683 | 
            +
                      end
         | 
| 684 | 
            +
                      kw_tys
         | 
| 685 | 
            +
                    end
         | 
| 582 686 | 
             
                  end
         | 
| 583 687 | 
             
                end
         | 
| 584 688 |  |