123456789_123456789_123456789_123456789_123456789_

Class: TypeProf::Import

Relationships & Source Files
Inherits: Object
Defined in: lib/typeprof/import.rb

Class Method Summary

Instance Method Summary

Constructor Details

.new(scratch, json) ⇒ Import

[ GitHub ]

  
# File 'lib/typeprof/import.rb', line 555

def initialize(scratch, json)
  @scratch = scratch
  @json = json
end

Class Method Details

.import_builtin(scratch)

[ GitHub ]

  
# File 'lib/typeprof/import.rb', line 530

def self.import_builtin(scratch)
  Import.new(scratch, scratch.rbs_reader.load_builtin).import
end

.import_library(scratch, feature)

[ GitHub ]

  
# File 'lib/typeprof/import.rb', line 534

def self.import_library(scratch, feature)
  begin
    json = scratch.rbs_reader.load_library(feature)
  rescue RBS::EnvironmentLoader::UnknownLibraryError
    return nil
  rescue RBS::DuplicatedDeclarationError, RBSReader::RBSCollectionDefined
    return true
  end
  # need cache?
  Import.new(scratch, json).import
end

.import_rbs_code(scratch, rbs_name, rbs_code)

[ GitHub ]

  
# File 'lib/typeprof/import.rb', line 551

def self.import_rbs_code(scratch, rbs_name, rbs_code)
  Import.new(scratch, scratch.rbs_reader.load_rbs_string(rbs_name, rbs_code)).import(true)
end

.import_rbs_files(scratch, rbs_paths)

[ GitHub ]

  
# File 'lib/typeprof/import.rb', line 546

def self.import_rbs_files(scratch, rbs_paths)
  rbs_paths = rbs_paths.map {|rbs_path| Pathname(rbs_path) }
  Import.new(scratch, scratch.rbs_reader.load_paths(rbs_paths)).import(true)
end

Instance Method Details

#conv_attr_defs(mdef, rbs_source)

[ GitHub ]

  
# File 'lib/typeprof/import.rb', line 663

def conv_attr_defs(mdef, rbs_source)
  ivar = :"@#{ mdef[:ivar] }"
  kind = mdef[:kind]
  pub_meth = mdef[:visibility]

  defs = []
  if kind == :reader || kind == :accessor
    defs << TypedAttrMethodDef.new(ivar, :reader, pub_meth, rbs_source)
  end
  if kind == :writer || kind == :accessor
    defs << TypedAttrMethodDef.new(ivar, :writer, pub_meth, rbs_source)
  end
  raise if defs.empty?
  defs
end

#conv_block(blk)

[ GitHub ]

  
# File 'lib/typeprof/import.rb', line 713

def conv_block(blk)
  return [Type.nil] unless blk

  required_block = blk[:required_block]
  lead_tys = blk[:lead_tys]
  opt_tys = blk[:opt_tys]
  rest_ty = blk[:rest_ty]
  req_kw_tys = blk[:req_kw_tys]
  opt_kw_tys = blk[:opt_kw_tys]
  rest_kw_ty = blk[:rest_kw_ty]
  ret_ty = blk[:ret_ty]

  lead_tys = lead_tys.map {|ty| conv_type(ty) }
  opt_tys = opt_tys.map {|ty| conv_type(ty) }
  rest_ty = conv_type(rest_ty) if rest_ty
  kw_tys = []
  req_kw_tys.each {|key, ty| kw_tys << [true, key, conv_type(ty)] }
  opt_kw_tys.each {|key, ty| kw_tys << [false, key, conv_type(ty)] }
  if rest_kw_ty
    ty = conv_type(rest_kw_ty)
    kw_rest_ty = Type.gen_hash do |h|
      k_ty = Type::Instance.new(Type::Builtin[:sym])
      h[k_ty] = ty
    end
  end

  msig = MethodSignature.new(lead_tys, opt_tys, rest_ty, [], kw_tys, kw_rest_ty, Type.nil)

  ret_ty = conv_type(ret_ty)

  ret = [Type::Proc.new(TypedBlock.new(msig, ret_ty), Type::Builtin[:proc])]
  ret << Type.nil unless required_block
  ret
end

#conv_func(sig_ret)

[ GitHub ]

  
# File 'lib/typeprof/import.rb', line 679

def conv_func(sig_ret)
  #type_params = sig_ret[:type_params] # XXX
  lead_tys = sig_ret[:lead_tys]
  opt_tys = sig_ret[:opt_tys]
  rest_ty = sig_ret[:rest_ty]
  req_kw_tys = sig_ret[:req_kw_tys]
  opt_kw_tys = sig_ret[:opt_kw_tys]
  rest_kw_ty = sig_ret[:rest_kw_ty]
  blk = sig_ret[:blk]
  ret_ty = sig_ret[:ret_ty]

  lead_tys = lead_tys.map {|ty| conv_type(ty) }
  opt_tys = opt_tys.map {|ty| conv_type(ty) }
  rest_ty = conv_type(rest_ty) if rest_ty
  kw_tys = []
  req_kw_tys.each {|key, ty| kw_tys << [true, key, conv_type(ty)] }
  opt_kw_tys.each {|key, ty| kw_tys << [false, key, conv_type(ty)] }
  if rest_kw_ty
    ty = conv_type(rest_kw_ty)
    kw_rest_ty = Type.gen_hash do |h|
      k_ty = Type::Instance.new(Type::Builtin[:sym])
      h[k_ty] = ty
    end
  end

  blks = conv_block(blk)

  ret_ty = conv_type(ret_ty)

  blks.map do |blk|
    [MethodSignature.new(lead_tys, opt_tys, rest_ty, [], kw_tys, kw_rest_ty, blk), ret_ty]
  end
end

#conv_method_def(method_name, mdef, rbs_source)

[ GitHub ]

  
# File 'lib/typeprof/import.rb', line 655

def conv_method_def(method_name, mdef, rbs_source)
  sig_rets = mdef[:sig_rets].flat_map do |sig_ret|
    conv_func(sig_ret)
  end

  TypedMethodDef.new(sig_rets, rbs_source, mdef[:visibility])
end

#conv_type(ty)

[ GitHub ]

  
# File 'lib/typeprof/import.rb', line 748

def conv_type(ty)
  case ty.first
  when :class then path_to_klass(ty[1])
  when :instance then Type::Instance.new(path_to_klass(ty[1]))
  when :cell
    Type::Cell.new(Type::Cell::Elements.new(ty[2].map {|ty| conv_type(ty) }), conv_type(ty[1]))
  when :any then Type.any
  when :void then Type::Void.new
  when :nil then Type.nil
  when :optional then Type.optional(conv_type(ty[1]))
  when :bool then Type.bool
  when :self then Type::Var.new(:self)
  when :int then Type::Instance.new(Type::Builtin[:int])
  when :str then Type::Instance.new(Type::Builtin[:str])
  when :sym then Type::Symbol.new(ty.last, Type::Instance.new(Type::Builtin[:sym]))
  when :true  then Type::Instance.new(Type::Builtin[:true])
  when :false then Type::Instance.new(Type::Builtin[:false])
  when :array
    _, path, lead_tys, rest_ty = ty
    lead_tys = lead_tys.map {|ty| conv_type(ty) }
    rest_ty = conv_type(rest_ty)
    base_type = Type::Instance.new(path_to_klass(path))
    Type::Array.new(Type::Array::Elements.new(lead_tys, rest_ty), base_type)
  when :hash
    _, path, (k, v) = ty
    Type.gen_hash(Type::Instance.new(path_to_klass(path))) do |h|
      k_ty = conv_type(k)
      v_ty = conv_type(v)
      h[k_ty] = v_ty
    end
  when :hash_record
    _, path, key_tys = ty
    Type.gen_hash(Type::Instance.new(path_to_klass(path))) do |h|
      key_tys.each do |key, ty|
        k_ty = Type::Symbol.new(key, Type::Instance.new(Type::Builtin[:sym]))
        v_ty = conv_type(ty)
        h[k_ty] = v_ty
      end
    end
  when :union
    tys = ty[1]
    Type::Union.create(Utils::Set[*tys.map {|ty2| conv_type(ty2) }], nil) # XXX: Array and Hash support
  when :intersection
    tys = ty[1]
    conv_type(tys.first) # XXX: This is wrong! We need to support intersection type
  when :var
    Type::Var.new(ty[1])
  when :proc
    msig, ret_ty = conv_func(ty[1]).first # Currently, RBS Proc does not accept a block, so the size should be always one
    Type::Proc.new(TypedBlock.new(msig, ret_ty), Type::Instance.new(Type::Builtin[:proc]))
  else
    pp ty
    raise NotImplementedError
  end
end

#import(explicit = false)

[ GitHub ]

  
# File 'lib/typeprof/import.rb', line 560

def import(explicit = false)
  classes = @json[:classes].map do |classpath, cdef|
    type_params = cdef[:type_params]
    superclass, superclass_type_args = cdef[:superclass]
    members = cdef[:members]

    name = classpath.last
    superclass = path_to_klass(superclass) if superclass
    base_klass = path_to_klass(classpath[0..-2])

    klass, = @scratch.get_constant(base_klass, name)
    if klass.is_a?(Type::Any)
      klass = @scratch.new_class(base_klass, name, type_params, superclass, nil)

      # There builtin classes are needed to interpret RBS declarations
      case classpath
      when [:NilClass]   then Type::Builtin[:nil]   = klass
      when [:TrueClass]  then Type::Builtin[:true]  = klass
      when [:FalseClass] then Type::Builtin[:false] = klass
      when [:Integer]    then Type::Builtin[:int]   = klass
      when [:String]     then Type::Builtin[:str]   = klass
      when [:Symbol]     then Type::Builtin[:sym]   = klass
      when [:Array]      then Type::Builtin[:ary]   = klass
      when [:Hash]       then Type::Builtin[:hash]  = klass
      when [:Proc]       then Type::Builtin[:proc]  = klass
      end
    end

    [klass, superclass_type_args, members]
  end

  classes.each do |klass, superclass_type_args, members|
    @scratch.add_superclass_type_args!(klass, superclass_type_args&.map {|ty| conv_type(ty) })
    modules = members[:modules]
    methods = members[:methods]
    attr_methods = members[:attr_methods]
    ivars = members[:ivars]
    cvars = members[:cvars]
    rbs_sources = members[:rbs_sources]

    modules.each do |kind, mods|
      mods.each do |mod, type_args|
        type_args = type_args&.map {|ty| conv_type(ty) }
        case kind
        when :include
          @scratch.mix_module(:after, klass, path_to_klass(mod), type_args, false, nil)
        when :extend
          @scratch.mix_module(:after, klass, path_to_klass(mod), type_args, true, nil)
        when :prepend
          @scratch.mix_module(:before, klass, path_to_klass(mod), type_args, false, nil)
        end
      end
    end

    methods.each do |(singleton, method_name), mdef|
      rbs_source = explicit ? rbs_sources[[singleton, method_name]] : nil
      mdef = conv_method_def(method_name, mdef, rbs_source)
      @scratch.add_method(klass, method_name, singleton, mdef)
    end

    attr_methods.each do |(singleton, method_name), mdef|
      rbs_source = explicit ? rbs_sources[[singleton, method_name]] : nil
      ty = conv_type(mdef[:ty]).remove_type_vars
      mdefs = conv_attr_defs(mdef, rbs_source)
      mdefs.each do |mdef|
        @scratch.add_typed_attr_method(klass, mdef)
      end
      @scratch.add_ivar_write!(Type::Instance.new(klass), :"@#{ mdef[:ivar] }", ty, nil)
    end

    ivars.each do |ivar_name, ty|
      ty = conv_type(ty).remove_type_vars
      @scratch.add_ivar_write!(Type::Instance.new(klass), ivar_name, ty, nil)
    end

    cvars.each do |ivar_name, ty|
      ty = conv_type(ty).remove_type_vars
      @scratch.add_cvar_write!(klass, ivar_name, ty, nil)
    end
  end

  @json[:constants].each do |classpath, value|
    base_klass = path_to_klass(classpath[0..-2])
    value = conv_type(value).remove_type_vars
    @scratch.add_constant(base_klass, classpath[-1], value, nil)
  end

  @json[:globals].each do |name, ty|
    ty = conv_type(ty).remove_type_vars
    @scratch.add_gvar_write!(name, ty, nil)
  end

  true
end

#path_to_klass(path)

[ GitHub ]

  
# File 'lib/typeprof/import.rb', line 804

def path_to_klass(path)
  klass = Type::Builtin[:obj]
  path.each do |name|
    klass, = @scratch.get_constant(klass, name)
    if klass == Type.any
      raise TypeProfError.new("A constant `#{ path.join("::") }' is used but not defined in RBS")
    end
  end
  klass
end