class TypeProf::ISeqMethodDef

Attributes

iseq[R]

Public Class Methods

new(iseq, cref, outer_ep, pub_meth) click to toggle source
# File typeprof-0.21.8/lib/typeprof/method.rb, line 9
def initialize(iseq, cref, outer_ep, pub_meth)
  @iseq = iseq
  raise if iseq.nil?
  @cref = cref
  @outer_ep = outer_ep
  @pub_meth = pub_meth
end

Public Instance Methods

do_check_send(msig, recv, mid, ep, scratch) click to toggle source
# File typeprof-0.21.8/lib/typeprof/method.rb, line 52
def do_check_send(msig, recv, mid, ep, scratch)
  klass, singleton = recv.method_dispatch_info
  cur_subst = {}
  direct_method = true
  scratch.adjust_substitution(klass, singleton, mid, self, recv.generate_substitution) do |subst, direct|
    direct_method &&= direct
    cur_subst = Type.merge_substitution(cur_subst, subst)
  end

  lead_num = @iseq.fargs_format[:lead_num] || 0
  post_num = @iseq.fargs_format[:post_num] || 0
  rest_start = @iseq.fargs_format[:rest_start]
  opt = @iseq.fargs_format[:opt] || [0]

  # TODO: check keywords
  if rest_start
    # almost ok
  else
    if msig.rest_ty
      scratch.error(ep, "RBS says that a rest argument is accepted, but the method definition does not accept one")
      return
    end
    if msig.lead_tys.size + msig.post_tys.size < lead_num + post_num
      scratch.error(ep, "RBS says that the arity may be %d, but the method definition requires at least %d arguments" % [msig.lead_tys.size + msig.post_tys.size, lead_num + post_num])
      return
    end
    if msig.lead_tys.size + msig.opt_tys.size + msig.post_tys.size > lead_num + opt.size - 1 + post_num
      scratch.error(ep, "RBS says that the arity may be %d, but the method definition requires at most %d arguments" % [msig.lead_tys.size + msig.opt_tys.size + msig.post_tys.size, lead_num + opt.size - 1 + post_num])
      return
    end
  end

  lead_num = @iseq.fargs_format[:lead_num] || 0
  post_start = @iseq.fargs_format[:post_start]
  kw_start = @iseq.fargs_format[:kwbits]
  keyword = @iseq.fargs_format[:keyword]
  kw_start -= keyword.size if kw_start
  kw_rest = @iseq.fargs_format[:kwrest]
  block_start = @iseq.fargs_format[:block_start]

  # XXX: need to check .rbs msig and .rb fargs

  ctx = Context.new(@iseq, @cref, mid)
  callee_ep = ExecutionPoint.new(ctx, 0, @outer_ep)

  locals = [Type.nil] * @iseq.locals.size
  nenv = Env.new(StaticEnv.new(recv, msig.blk_ty, false, true), locals, [], Utils::HashWrapper.new({}))
  alloc_site = AllocationSite.new(callee_ep)
  idx = 0
  msig.lead_tys.each_with_index do |ty, i|
    alloc_site2 = alloc_site.add_id(idx += 1)
    ty = ty.substitute(cur_subst, Config.current.options[:type_depth_limit]).remove_type_vars
    nenv, ty = scratch.localize_type(ty, nenv, callee_ep, alloc_site2)
    nenv = nenv.local_update(i, ty)
  end
  if msig.opt_tys
    msig.opt_tys.each_with_index do |ty, i|
      alloc_site2 = alloc_site.add_id(idx += 1)
      ty = ty.substitute(cur_subst, Config.current.options[:type_depth_limit]).remove_type_vars
      nenv, ty = scratch.localize_type(ty, nenv, callee_ep, alloc_site2)
      nenv = nenv.local_update(lead_num + i, ty)
    end
  end
  if msig.rest_ty
    alloc_site2 = alloc_site.add_id(idx += 1)
    ty = Type::Array.new(Type::Array::Elements.new([], msig.rest_ty), Type::Instance.new(Type::Builtin[:ary]))
    ty = ty.substitute(cur_subst, Config.current.options[:type_depth_limit]).remove_type_vars
    nenv, rest_ty = scratch.localize_type(ty, nenv, callee_ep, alloc_site2)
    # TODO: handle a case where rest_start is not found
    nenv = nenv.local_update(rest_start, rest_ty)
  elsif rest_start
    alloc_site2 = alloc_site.add_id(idx += 1)
    ty = Type::Array.new(Type::Array::Elements.new([], Type.any), Type::Instance.new(Type::Builtin[:ary]))
    nenv, rest_ty = scratch.localize_type(ty, nenv, callee_ep, alloc_site2)
    nenv = nenv.local_update(rest_start, rest_ty)
  end
  if msig.post_tys
    msig.post_tys.each_with_index do |ty, i|
      alloc_site2 = alloc_site.add_id(idx += 1)
      ty = ty.substitute(cur_subst, Config.current.options[:type_depth_limit]).remove_type_vars
      nenv, ty = scratch.localize_type(ty, nenv, callee_ep, alloc_site2)
      nenv = nenv.local_update(post_start + i, ty)
    end
  end
  if msig.kw_tys && keyword # TODO: support the case where RBS writes kw_tys and RB method accepts **kwrest
    msig.kw_tys.each do |_, key, ty|
      i = keyword.index {|callee_key,| callee_key == key }
      unless i
        # warn
        next
      end
      alloc_site2 = alloc_site.add_id(key)
      ty = ty.substitute(cur_subst, Config.current.options[:type_depth_limit]).remove_type_vars
      nenv, ty = scratch.localize_type(ty, nenv, callee_ep, alloc_site2)
      nenv = nenv.local_update(kw_start + i, ty)
    end
  end
  if msig.kw_rest_ty
    ty = msig.kw_rest_ty
    alloc_site2 = alloc_site.add_id(:**)
    ty = ty.substitute(cur_subst, Config.current.options[:type_depth_limit]).remove_type_vars
    nenv, ty = scratch.localize_type(ty, nenv, callee_ep, alloc_site2)
    nenv = nenv.local_update(kw_rest, ty)
  elsif kw_rest
    alloc_site2 = alloc_site.add_id(:**)
    ty = Type.gen_hash {}
    nenv, ty = scratch.localize_type(ty, nenv, callee_ep, alloc_site2)
    nenv = nenv.local_update(kw_rest, ty)
  end
  nenv = nenv.local_update(block_start, msig.blk_ty) if block_start

  opt.each do |start_pc|
    scratch.merge_env(callee_ep.jump(start_pc), nenv)
  end
  scratch.add_executed_iseq(@iseq)

  ctx
end
do_send(recv, mid, aargs, caller_ep, caller_env, scratch, &ctn) click to toggle source
# File typeprof-0.21.8/lib/typeprof/method.rb, line 19
def do_send(recv, mid, aargs, caller_ep, caller_env, scratch, &ctn)
  recv = recv.base_type while recv.respond_to?(:base_type)
  recv = scratch.globalize_type(recv, caller_env, caller_ep)
  aargs = scratch.globalize_type(aargs, caller_env, caller_ep)

  locals = [Type.nil] * @iseq.locals.size

  blk_ty, start_pcs = aargs.setup_formal_arguments(:method, locals, @iseq.fargs_format)
  if blk_ty.is_a?(String)
    scratch.error(caller_ep, blk_ty)
    ctn[Type.any, caller_ep, caller_env]
    return
  end

  nctx = Context.new(@iseq, @cref, mid)
  callee_ep = ExecutionPoint.new(nctx, 0, @outer_ep)
  nenv = Env.new(StaticEnv.new(recv, blk_ty, false, true), locals, [], Utils::HashWrapper.new({}))
  alloc_site = AllocationSite.new(callee_ep)
  locals.each_with_index do |ty, i|
    alloc_site2 = alloc_site.add_id(i)
    # nenv is top-level, so it is okay to call Type#localize directly
    nenv, ty = ty.localize(nenv, alloc_site2, Config.current.options[:type_depth_limit])
    nenv = nenv.local_update(i, ty)
  end

  start_pcs.each do |start_pc|
    scratch.merge_env(ExecutionPoint.new(nctx, start_pc, @outer_ep), nenv)
  end

  scratch.add_iseq_method_call!(self, nctx)
  scratch.add_callsite!(nctx, caller_ep, caller_env, &ctn)
end