Class: Mirah::JVM::Types::TypeFactory

Inherits:
SimpleTypes
  • Object
show all
Includes:
Logging::Logged, TypeSystem
Defined in:
lib/mirah/jvm/types/factory.rb,
lib/mirah/jvm/types/basic_types.rb

Defined Under Namespace

Classes: ParanoidHash

Constant Summary

Constants included from Logging::Logged

Logging::Logged::VLEVELS

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logging::Logged

#error, #info, #log, #logger, #logger_name, #logging?, #vlog, #warning

Constructor Details

#initializeTypeFactory

Returns a new instance of TypeFactory.



58
59
60
61
62
63
64
65
66
67
# File 'lib/mirah/jvm/types/factory.rb', line 58

def initialize
  super(":unused")
  @known_types = ParanoidHash.new
  @anonymous_classes = Hash.new {|h, k| h[k] = 0}
  @declarations = []
  @mirrors = {}
  @futures = {}
  @fields = {}
  create_basic_types
end

Instance Attribute Details

#bootclasspathObject

Returns the value of attribute bootclasspath.



762
763
764
# File 'lib/mirah/jvm/types/factory.rb', line 762

def bootclasspath
  @bootclasspath
end

#known_typesObject (readonly)

Returns the value of attribute known_types.



49
50
51
# File 'lib/mirah/jvm/types/factory.rb', line 49

def known_types
  @known_types
end

#packageObject

Returns the value of attribute package.



48
49
50
# File 'lib/mirah/jvm/types/factory.rb', line 48

def package
  @package
end

Instance Method Details

#_build_generic_type_future(bounds, position) ⇒ Object



353
354
355
356
357
358
359
360
361
# File 'lib/mirah/jvm/types/factory.rb', line 353

def _build_generic_type_future(bounds, position)
  typeName = "java.lang.Object"
  if bounds.size > 1
    raise ArgumentError, "Multiple bounds on type variables are not supported."
  elsif bounds.size == 1
    typeName = bounds[0].raw_type.getClassName
  end
  GenericTypeFuture.new(position, type(nil, typeName))
end

#_declare_method(target, name, args, type) ⇒ Object



460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
# File 'lib/mirah/jvm/types/factory.rb', line 460

def _declare_method(target, name, args, type)
  return if args.any? {|a| a.isError }
  return unless type.kind_of?(MethodFuture) && type.returnType.isResolved
  resolved = type.returnType.resolve
  resolved = resolved.returnType if resolved.respond_to?(:returnType)
  log "Learned {0} method {1}.{2}({3}) = {4}", [
          target.meta? ? "static" : "instance",
          target.full_name,
          name,
          args.map{|a| a.full_name}.join(', '),
          resolved.full_name].to_java
  rewritten_name = name.sub(/=$/, '_set')
  if target.meta?
    target.unmeta.declare_static_method(rewritten_name, args, resolved, [])
  else
    target.declare_method(rewritten_name, args, resolved, [])
  end      
end

#_find_method_type(scope, target, name, argTypes, macroTypes, position) ⇒ Object



271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/mirah/jvm/types/factory.rb', line 271

def _find_method_type(scope, target, name, argTypes, macroTypes, position)
  if target.respond_to?(:isError) && target.isError
    return target
  end
  type = BaseTypeFuture.new(nil)
  target.find_method2(target, name, argTypes, macroTypes, target.meta?, scope) do |method|
    if method.nil?
      unless argTypes.any?{|t| t && t.isError && (type.resolved(t); true)}
        type.resolved(ErrorType.new([
            ["Cannot find %s method %s(%s) on %s" %
                [ target.meta? ? "static" : "instance",
                  name,
                  argTypes.map{|t| t ? t.full_name : "?"}.join(', '),
                  target.full_name], position]]))
      end
    elsif method.kind_of?(Exception)
      type.resolved(ErrorType.new([[method.message, position]]))
    else
      result = method.return_type

      # Handle generics.
      begin
        if name == 'new' and target.type_parameters
          result = Mirah::JVM::Types::GenericType.new(result) # Upgrade to a generic type.
          target.type_parameters.each do |var|
            result.type_parameter_map.put(var.name, _build_generic_type_future(var.bounds, position))
          end

          genericParameterTypes = method.member.generic_parameter_types
          if genericParameterTypes
            genericParameterTypes.each_index do |i|
              _handle_nested_generic_parameter(genericParameterTypes[i], argTypes[i], result.type_parameter_map, position)
            end
          end
        elsif target.generic? && method.respond_to?(:member)
          genericParameterTypes = method.member.generic_parameter_types
          if genericParameterTypes
            genericParameterTypes.each_index do |i|
              _handle_nested_generic_parameter(genericParameterTypes[i], argTypes[i], target.type_parameter_map, position)
            end
          end

          result = _handle_nested_generic_return(result, method.member.generic_return_type, target.type_parameter_map, position)
          result.resolve if result.respond_to?(:resolve)
        end
      rescue => ex
        Mirah.print_error("Error inferring generics: #{ex.message}", position)
        log("Error inferring generics: #{ex.message}\n#{ex.backtrace.join("\n")}")
        result = method.return_type
      end

      if result.kind_of?(TypeFuture)
        if result.isResolved
          type.resolved(result.resolve)
        else
          result.onUpdate {|x, resolved| type.resolved(resolved) }
        end
      else
        type.resolved(result)
      end

      # TODO(shepheb): This is modifying the argTypes of _find_method_type, and it shouldn't be.
      # Moved to the bottom so the generics code above can access the original argTypes that were passed to _find_method_type.
      argTypes = method.argument_types
    end
  end
  argTypes = argTypes.map do |t|
    if t.nil?
      t
    elsif t.isBlock
      type.position_set(position) if (position && type.position.nil?)
      # This should only happen if type is an error.
      type.resolve
    else
      t
    end
  end
  return_type = AssignableTypeFuture.new(nil)
  return_type.assign(type, position)
  MethodFuture.new(name, argTypes, return_type, false, position)
end

#_handle_nested_generic_parameter(expectedType, providedType, type_parameter_map, position) ⇒ Object



363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
# File 'lib/mirah/jvm/types/factory.rb', line 363

def _handle_nested_generic_parameter(expectedType, providedType, type_parameter_map, position)
  if expectedType.kind_of?(BiteScript::ASM::TypeVariable)
    gtf = type_parameter_map.get(expectedType.name)
    gtf.assign(SimpleFuture.new(providedType), position)
  elsif expectedType.kind_of?(BiteScript::ASM::Wildcard) && providedType.kind_of?(TypeFuture)
    # TODO(shepheb): Handle bounds here.
    gtf = type_parameter_map.get(expectedType.upper_bound.name)
    gtf.assign(providedType, position)
  elsif expectedType.kind_of?(BiteScript::ASM::ParameterizedType)
    # We can assume assignable_from? here, or this method would not have been called.
    expectedParameters = expectedType.type_arguments
    # Look up the values of the provided type's parameters.
    providedParameters = providedType.type_parameters.map do |var|
      if providedType.generic?
        providedType.type_parameter_map.get(var.name)
      else
        type_parameter_map.get(var.name)
      end
    end

    if expectedParameters && providedParameters && expectedParameters.size == providedParameters.size
      expectedParameters.each_index do |i|
        _handle_nested_generic_parameter(expectedParameters[i], providedParameters[i], type_parameter_map, position)
      end
    else
      raise ArgumentError, "Type parameter mismatch: Expected #{expectedParameters}, found #{providedParameters}."
    end
  end
end

#_handle_nested_generic_return(returnType, genericReturnType, type_parameter_map, position) ⇒ Object

TODO(shepheb): Handles only one level of nesting, it should handle arbitrary depth by recursion.



394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/mirah/jvm/types/factory.rb', line 394

def _handle_nested_generic_return(returnType, genericReturnType, type_parameter_map, position)
  if genericReturnType.kind_of?(BiteScript::ASM::TypeVariable)
    type_parameter_map.get(genericReturnType.name)
  elsif genericReturnType.kind_of?(BiteScript::ASM::ParameterizedType)
    returnType = GenericType.new(returnType)
    expectedTypeParameters = returnType.jvm_type.type_parameters
    providedTypeParameters = genericReturnType.type_arguments
    if expectedTypeParameters && providedTypeParameters && expectedTypeParameters.size == providedTypeParameters.size
      expectedTypeParameters.each_index do |i|
        returnType.type_parameter_map.put(expectedTypeParameters[i].name, type_parameter_map.get(providedTypeParameters[i].name))
      end
    else
      raise ArgumentError, "Type parameter mismatch: Expected #{expectedTypeParameters}, found #{providedTypeParameters}"
    end
    returnType
  else
    returnType
  end
end

#addDefaultImports(scope) ⇒ Object

TypeSystem methods



113
114
115
# File 'lib/mirah/jvm/types/factory.rb', line 113

def addDefaultImports(scope)
  scope.import('java.lang.*', '*')
end

#addMacro(klass, macro) ⇒ Object



515
516
517
# File 'lib/mirah/jvm/types/factory.rb', line 515

def addMacro(klass, macro)
  klass.unmeta.add_compiled_macro(macro)
end

#base_classpathObject



715
716
717
718
719
720
721
722
723
724
# File 'lib/mirah/jvm/types/factory.rb', line 715

def base_classpath
  if __FILE__.include? '.jar'
    Mirah::Env.encode_paths([__FILE__.split('!').first.split(Mirah::Env.path_separator).last])
  else
    Mirah::Env.encode_paths(['.',
                             File.dirname(__FILE__) + '/../../../../javalib/mirah-builtins.jar',
                             File.dirname(__FILE__) + '/../../../../javalib/mirah-parser.jar',
                             File.dirname(__FILE__) + '/../../../../javalib/mirah-bootstrap.jar'])
  end
end

#basic_type(scope, name) ⇒ Object

Raises:

  • (ArgumentError)


572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
# File 'lib/mirah/jvm/types/factory.rb', line 572

def basic_type(scope, name)
  if name.kind_of?(Type) || name.kind_of?(NarrowingType)
    return name.basic_type
  end
  orig = name
  if name.kind_of? Java::JavaClass
    #TODO is is possible to get here anymore?
    if name.array?
      return type(scope, name.component_type, true)
    else
      name = name.name
    end
  elsif name.respond_to? :java_class
    name = name.java_class.name
  end
  name = name.to_s unless name.kind_of?(::String)
  raise ArgumentError, "Bad Type #{orig}" if name =~ /Java::/
  raise ArgumentError, "Bad Type #{orig.inspect}" if name == '' || name.nil?
  find_type(scope, name)
end

#bootstrap_loaderObject



741
742
743
744
745
746
747
748
749
750
751
752
753
754
# File 'lib/mirah/jvm/types/factory.rb', line 741

def bootstrap_loader
  @bootstrap_loader ||= begin
    parent = if bootclasspath
               Mirah::Util::IsolatedResourceLoader.new(Mirah::Env.make_urls(bootclasspath))
             end
    if __FILE__ =~ /^(file:.+jar)!/
      bootstrap_urls = [java.net.URL.new($1)].to_java(java.net.URL)
    else
      bootstrap_jar = File.expand_path("#{__FILE__}/../../../../../javalib/mirah-bootstrap.jar")
      bootstrap_urls = [java.io.File.new(bootstrap_jar).to_uri.to_url].to_java(java.net.URL)
    end
    URLClassLoader.new(bootstrap_urls, parent)
  end
end

#box(type) ⇒ Object



187
188
189
190
191
192
193
194
195
196
197
# File 'lib/mirah/jvm/types/factory.rb', line 187

def box(type)
  boxed = BaseTypeFuture.new(nil)
  type.on_update do |_, resolved|
    if resolved.isError || !resolved.primitive?
      boxed.resolved(resolved)
    else
      boxed.resolved(resolved.box_type)
    end
  end
  boxed
end

#cache_and_wrap(resolved_type) ⇒ Object



99
100
101
# File 'lib/mirah/jvm/types/factory.rb', line 99

def cache_and_wrap(resolved_type)
  @futures[resolved_type.name] ||= wrap(resolved_type)
end

#cache_and_wrap_type(name) ⇒ Object



103
104
105
106
107
108
109
110
# File 'lib/mirah/jvm/types/factory.rb', line 103

def cache_and_wrap_type(name)
  @futures[name] ||= begin
    type = type(nil, name)
    wrapper = wrap(type)
    wrapper.resolved(ErrorType.new([["Cannot find class #{name}"]])) if type.nil?
    wrapper
  end
end

#classpathObject



726
727
728
# File 'lib/mirah/jvm/types/factory.rb', line 726

def classpath
  @classpath ||= base_classpath
end

#classpath=(classpath) ⇒ Object



730
731
732
733
734
735
# File 'lib/mirah/jvm/types/factory.rb', line 730

def classpath=(classpath)
  if classpath
    @classpath = Mirah::Env.encode_paths [classpath, base_classpath]
  end
  @resource_loader = nil
end

#create_basic_typesObject



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/mirah/jvm/types/basic_types.rb', line 18

def create_basic_types
  @known_types.update(
    'boolean' => BooleanType.new(self, 'boolean', java.lang.Boolean),
    'byte' => IntegerType.new(self, 'byte', java.lang.Byte),
    'char' => CharacterType.new(self, 'char', java.lang.Character),
    'short' => IntegerType.new(self, 'short', java.lang.Short),
    'int' => IntegerType.new(self, 'int', java.lang.Integer),
    'long' => LongType.new(self, 'long', java.lang.Long),
    'float' => FloatType.new(self, 'float', java.lang.Float),
    'double' => DoubleType.new(self, 'double', java.lang.Double)
  )
  @known_types['fixnum'] = @known_types['int']
  @known_types['Object'] = type(nil, 'java.lang.Object')
  @known_types['string'] = @known_types['String'] = @known_types['java.lang.String'] =
      StringType.new(self, get_mirror('java.lang.String'))
  type(nil, 'java.lang.Class')
  @known_types['Iterable'] = @known_types['java.lang.Iterable'] =
      IterableType.new(self, get_mirror('java.lang.Iterable'))
  @known_types['void'] = VoidType.new(self)
  @known_types['null'] = NullType.new(self)
  @known_types['implicit_nil'] = ImplicitNilType.new(self)
end

#declare_type(scope, name) ⇒ Object



658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
# File 'lib/mirah/jvm/types/factory.rb', line 658

def declare_type(scope, name)
  full_name = name
  package = scope.package
  if !name.include?('.')
    if package && !package.empty?
      full_name = "#{package}.#{name}"
    else
      scope.on_package_change do
        full_name = "#{scope.package}.#{name}"
        scope.import(full_name, name)
        @known_types[full_name] = @known_types[name]
      end
    end
  end
  if @known_types.include? full_name
    @known_types[full_name]
  else
    scope.import(full_name, name)
    @known_types[full_name] = TypeDefinition.new(self, scope, full_name, nil)
  end
end

#define_type(scope, node) ⇒ Object



680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
# File 'lib/mirah/jvm/types/factory.rb', line 680

def define_type(scope, node)
  if node.name.nil?
    outer_node = node.find_ancestor {|n| (n != node && n.kind_of?(ClassDefinition)) || n.kind_of?(Script)}
    if outer_node.kind_of?(ClassDefinition)
      outer_name = outer_node.name.identifier
    else
      outer_name = Mirah::JVM::Compiler::JVMBytecode.classname_from_filename(node.position.source.name || 'DashE')
    end
    id = (@anonymous_classes[outer_name] += 1)
    node.name_set(SimpleString.new("#{outer_name}$#{id}"))
  end
  name = node.name.identifier
  full_name = name
  package = scope.package
  if !name.include?('.') && package && !package.empty?
    full_name = "#{package}.#{name}"
  end
  if @known_types.include?(full_name) && @known_types[full_name].kind_of?(TypeDefinition)
    existing = @known_types[full_name]
    unless existing.node
      existing.node = node
      existing.scope = scope
    end
    existing
  else
    if InterfaceDeclaration === node
      klass = InterfaceDefinition
    else
      klass = TypeDefinition
    end
    scope.import(full_name, name)
    @known_types[full_name] = klass.new(self, scope, full_name, node)
  end
end

#define_types(builder) ⇒ Object



550
551
552
553
554
# File 'lib/mirah/jvm/types/factory.rb', line 550

def define_types(builder)
  @declarations.each do |declaration|
    declaration.define(builder)
  end
end

#defineType(scope, node, name, superclass, interfaces) ⇒ Object



498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
# File 'lib/mirah/jvm/types/factory.rb', line 498

def defineType(scope, node, name, superclass, interfaces)
  # TODO what if superclass or interfaces change later?
  log("Defining type #{name} < #{superclass.resolve.name if superclass} #{interfaces.map{|i|i.resolve.name}.inspect}")
  type = define_type(scope, node)
  future = @futures[type.name]
  if future
    future.resolved(type)
    future
  else
    cache_and_wrap(type)
  end
rescue => ex
  Mirah.print_error("Error defining type #{name}: #{ex.message}", node.position)
  puts ex.backtrace.join("\n\t")
  ErrorType.new([["Internal error: #{ex}", node.position]])
end

#extendClass(classname, extensions) ⇒ Object



519
520
521
# File 'lib/mirah/jvm/types/factory.rb', line 519

def extendClass(classname, extensions)
  get_type(classname).load_extensions(extensions)
end

#find_type(scope, name) ⇒ Object



593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
# File 'lib/mirah/jvm/types/factory.rb', line 593

def find_type(scope, name)
  type = get_type(name)
  return type if type

  if scope
    imports = scope.imports
    if imports.include?(name)
      name = imports[name] while imports.include?(name)
      type = get_type(name)
      return type if type
    end

    # TODO support inner class names
    if name !~ /\./
      return package_search(name, scope)
    end
  end
  return nil
end

#get(scope, typeref) ⇒ Object



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/mirah/jvm/types/factory.rb', line 225

def get(scope, typeref)
  basic_type = if scope.nil?
    cache_and_wrap_type(typeref.name)
  else
    imports = scope.imports
    name = typeref.name
    name = imports[name] while imports.include?(name)
    types = [ cache_and_wrap_type(name), nil ]
    packages = []
    packages << scope.package if scope.package && scope.package != ''
    (packages + scope.search_packages).each do |package|
      types << cache_and_wrap_type("#{package}.#{name}")
      types << nil
    end

    future = PickFirst.new(types, nil)
    future.position_set(typeref.position)
    future.error_message_set("Cannot find class #{typeref.name}")
    future
  end
  if typeref.isArray
    getArrayType(basic_type)
  elsif typeref.isStatic
    getMetaType(basic_type)
  else
    basic_type
  end
end

#get_mirror(name) ⇒ Object



776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
# File 'lib/mirah/jvm/types/factory.rb', line 776

def get_mirror(name)
  @mirrors[name] ||= begin
    classname = name.tr('.', '/')
    stream = resource_loader.getResourceAsStream(classname + ".class")
    if stream
      mirror = BiteScript::ASM::ClassMirror.load(stream)
      mirror if mirror.type.class_name == name
    else
      # TODO(ribrdb) Should this use a separate sourcepath?
      url = resource_loader.getResource(classname + ".java")
      if url
        file = java.io.File.new(url.toURI)
        mirrors = JavaSourceMirror.load(file, self) rescue []
        mirrors.each do |mirror|
          @mirrors[mirror.type.class_name] = mirror
        end if mirrors
        @mirrors[name]
      end
    end
  end
end

#get_type(full_name) ⇒ Object



630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
# File 'lib/mirah/jvm/types/factory.rb', line 630

def get_type(full_name)
  type = @known_types[full_name]
  return type.basic_type if type
  mirror = get_mirror(full_name)
  unless mirror
    if full_name =~ /^(.+)\.([^.]+)/
      outer_name = $1
      inner_name = $2
      outer_type = get_type(outer_name)
      return nil unless outer_type
      full_name = "#{outer_type.name}$#{inner_name}"
      mirror = get_mirror(full_name)
      return nil unless mirror
    else
      return nil
    end
  end
  type = Type.new(self, mirror).load_extensions
  if full_name.include? '$'
    @known_types[full_name.gsub('$', '.')] = type
  end
  @known_types[full_name] = type
end

#getAbstractMethods(type) ⇒ Object



798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
# File 'lib/mirah/jvm/types/factory.rb', line 798

def getAbstractMethods(type)
  methods = []
  unless type.isError
    object = get_type("java.lang.Object")
    interfaces = [type]
    until interfaces.empty?
      interface = interfaces.pop
      abstract_methods = interface.declared_instance_methods.select {|m| m.abstract?}
      methods += abstract_methods.select do |m|
        begin
          # Skip methods defined on Object
          object.java_method(m.name, *m.argument_types)
          false
        rescue NameError
          true
        end
      end
      interfaces.concat(interface.interfaces)
    end
    # TODO ensure this works with hierarchies of abstract classes
    # reject the methods implemented by the abstract class
    if type.abstract?
      implemented_methods = type.declared_instance_methods.reject{|m| m.abstract?}.map { |m| [m.name, m.argument_types, m.return_type] }
      methods = methods.reject{|m| implemented_methods.include? [m.name, m.argument_types, m.return_type] }
    end
  end
  methods.map do |method|
    MethodType.new(method.name, method.argument_types, method.return_type, false)
  end
end

#getArrayLiteralType(type, position) ⇒ Object



199
200
201
202
203
204
205
206
207
208
209
# File 'lib/mirah/jvm/types/factory.rb', line 199

def getArrayLiteralType(type, position)
  result = Mirah::JVM::Types::GenericType.new(type(nil, 'java.util.List')) # Upgrade to a generic type.
  variable = result.type_parameters[0]
  result.type_parameter_map[variable.name] = _build_generic_type_future(variable.bounds, position)
  result.type_parameter_map[variable.name].assign(box(type), position)
  wrap(result)
rescue => ex
  Mirah.print_error("Error inferring generics: #{ex.message}", position)
  log("Error inferring generics: #{ex.message}\n#{ex.backtrace.join("\n")}")
  cache_and_wrap_type('java.util.List')
end

#getArrayType(type) ⇒ Object



177
178
179
180
181
182
183
184
185
# File 'lib/mirah/jvm/types/factory.rb', line 177

def getArrayType(type)
  if type.kind_of?(Type)
    type.array_type
  else
    future = BaseTypeFuture.new(nil)
    type.on_update {|_, resolved| future.resolved(resolved.array_type)}
    future
  end
end

#getBaseExceptionTypeObject



120
# File 'lib/mirah/jvm/types/factory.rb', line 120

def getBaseExceptionType; cache_and_wrap_type('java.lang.Throwable') end

#getBlockTypeObject



626
627
628
# File 'lib/mirah/jvm/types/factory.rb', line 626

def getBlockType
  @block_type ||= BlockType.new
end

#getBooleanTypeObject



125
# File 'lib/mirah/jvm/types/factory.rb', line 125

def getBooleanType; cache_and_wrap_type('boolean') end

#getCharType(value) ⇒ Object



144
# File 'lib/mirah/jvm/types/factory.rb', line 144

def getCharType(value) cache_and_wrap_type('char') end

#getDefaultExceptionTypeObject



121
# File 'lib/mirah/jvm/types/factory.rb', line 121

def getDefaultExceptionType; cache_and_wrap_type('java.lang.Exception') end

#getFieldType(target, name, position) ⇒ Object



479
480
481
482
483
484
485
486
487
488
489
490
# File 'lib/mirah/jvm/types/factory.rb', line 479

def getFieldType(target, name, position)
  # This is currently only used for fields in the current class
  klass = target.resolve
  key = [klass, name]
  t = @fields[key]
  unless t
    t = AssignableTypeFuture.new(position)
    @fields[key] = t
    t.on_update {|x, resolved| klass.unmeta.declare_field(name, resolved, klass.meta?)}
  end
  t
end

#getFixnumType(value) ⇒ Object



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/mirah/jvm/types/factory.rb', line 127

def getFixnumType(value)
  long = java.lang.Long.new(value)
  if long.int_value != value
    cache_and_wrap_type('long')
  elsif long.short_value != value
    cache_and_wrap_type('int')
  elsif long.byte_value != value
    wide = type(nil, 'int')
    narrow = type(nil, 'short')
    NarrowingTypeFuture.new(nil, wide, narrow)
  else
    wide = type(nil, 'int')
    narrow = type(nil, 'byte')
    NarrowingTypeFuture.new(nil, wide, narrow)
  end
end

#getFloatType(value) ⇒ Object



146
147
148
149
150
151
152
153
154
155
# File 'lib/mirah/jvm/types/factory.rb', line 146

def getFloatType(value)
  d = java.lang.Double.new(value)
  if d.float_value != value
    cache_and_wrap_type('double')
  else
    wide = type(nil, 'double')
    narrow = type(nil, 'float')
    NarrowingTypeFuture.new(nil, wide, narrow)
  end
end

#getHashLiteralType(key_type, value_type, position) ⇒ Object



211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/mirah/jvm/types/factory.rb', line 211

def getHashLiteralType(key_type, value_type, position)
  result = Mirah::JVM::Types::GenericType.new(type(nil, 'java.util.HashMap')) # Upgrade to a generic type.
  generic_key, generic_value = result.type_parameters
  for variable, type in [[generic_key, key_type], [generic_value, value_type]]
    result.type_parameter_map[variable.name] = _build_generic_type_future(variable.bounds, position)
    result.type_parameter_map[variable.name].assign(box(type), position)
  end
  wrap(result)
rescue => ex
  Mirah.print_error("Error inferring generics: #{ex.message}", position)
  log("Error inferring generics: #{ex.message}\n#{ex.backtrace.join("\n")}")
  cache_and_wrap_type('java.util.HashMap')
end

#getHashTypeObject



122
# File 'lib/mirah/jvm/types/factory.rb', line 122

def getHashType; cache_and_wrap_type('java.util.HashMap') end

#getImplicitNilTypeObject



118
# File 'lib/mirah/jvm/types/factory.rb', line 118

def getImplicitNilType; cache_and_wrap_type('implicit_nil') end

#getLocalType(scope, name, position) ⇒ Object



254
255
256
# File 'lib/mirah/jvm/types/factory.rb', line 254

def getLocalType(scope, name, position)
  scope.local_type(name, position)
end

#getMainType(scope, script) ⇒ Object



492
493
494
495
496
# File 'lib/mirah/jvm/types/factory.rb', line 492

def getMainType(scope, script)
  filename = File.basename(script.position.source.name || 'DashE')
  classname = Mirah::JVM::Compiler::JVMBytecode.classname_from_filename(filename)
  getMetaType(cache_and_wrap(declare_type(scope, classname)))
end

#getMetaType(type) ⇒ Object



157
158
159
160
161
162
163
164
165
166
167
# File 'lib/mirah/jvm/types/factory.rb', line 157

def getMetaType(type)
  if type.kind_of?(Type)
    type.meta
  else
    future = BaseTypeFuture.new(nil)
    type.on_update {|_, resolved| future.resolved(resolved.meta)}
    future.position_set(type.position)
    future.error_message_set(type.error_message)
    future
  end
end

#getMethodDefType(target, name, argTypes, returnType, position) ⇒ Object



414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
# File 'lib/mirah/jvm/types/factory.rb', line 414

def getMethodDefType(target, name, argTypes, returnType, position)
  if target.nil?
    return ErrorType.new([["No target", position]])
  end
  unless argTypes.all? {|a| a.hasDeclaration}
    infer_override_args(target, name, argTypes)
  end
  if returnType.nil?
    returnType = infer_override_return_type(target, name, argTypes)
  end

  args = argTypes.map {|a| a.resolve}
  target = target.resolve
  type = _find_method_type(nil, target, name, args, nil, position)
  type.onUpdate do |m, resolved|
    _declare_method(target, name, args, type)
  end

  args.each_with_index do |arg, i|
    if arg.isError
      argTypes[i].onUpdate do |x, resolved|
        args[i] = resolved
        _declare_method(target, name, args, type)
      end
    end
  end

  if type.kind_of?(ErrorType)
    puts "Got error type for method #{name} on #{target.resolve} (#{target.resolve.class})"
    position = type.position rescue nil
    return_type = AssignableTypeFuture.new(position)
    return_type.declare(type, position)
    type = MethodFuture.new(name, args, return_type, false, position)
  elsif returnType
    type.returnType.declare(returnType, position)
  end

  type.to_java(MethodFuture)
rescue => ex
  target_name = target.respond_to?(:name) ? target.name : target.resolve.name
  error("Error getting method def type #{target_name}.#{name}: #{ex.message}\n#{ex.backtrace.join("\n\t")}")
  return_type = AssignableTypeFuture.new(nil)
  return_type.declare(ErrorType.new([["Internal error: #{ex}"]]), nil)
  MethodFuture.new(name, [], return_type, false, nil)
end

#getMethodType(call) ⇒ Object



258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/mirah/jvm/types/factory.rb', line 258

def getMethodType(call)
  target = call.resolved_target
  argTypes = call.resolved_parameters
  macro_types = call.parameter_nodes.map do |node|
    get_type(node.java_class.name)
  end if call.parameter_nodes
  _find_method_type(call.scope, target, call.name, argTypes, macro_types, call.position)
rescue => ex
  Mirah.print_error("Error getting method type #{target.name}.#{call.name}: #{ex.message}", call.position)
  puts ex.backtrace.join("\n\t")
  ErrorType.new([["Internal error: #{ex}", call.position]])
end

#getNullTypeObject



117
# File 'lib/mirah/jvm/types/factory.rb', line 117

def getNullType; cache_and_wrap_type('null') end

#getRegexTypeObject



123
# File 'lib/mirah/jvm/types/factory.rb', line 123

def getRegexType; cache_and_wrap_type('java.util.regex.Pattern') end

#getStringTypeObject



124
# File 'lib/mirah/jvm/types/factory.rb', line 124

def getStringType; cache_and_wrap_type('java.lang.String') end

#getSuperClass(future) ⇒ Object



169
170
171
172
173
174
175
# File 'lib/mirah/jvm/types/factory.rb', line 169

def getSuperClass(future)
  superclass = BaseTypeFuture.new(nil)
  future.on_update do |_, type|
    superclass.resolved(type.superclass)
  end
  superclass
end

#getVoidTypeObject



119
# File 'lib/mirah/jvm/types/factory.rb', line 119

def getVoidType; cache_and_wrap_type('void') end

#infer_override_args(target, name, arg_types) ⇒ Object



523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
# File 'lib/mirah/jvm/types/factory.rb', line 523

def infer_override_args(target, name, arg_types)
  # TODO What if the method we're overriding hasn't been inferred yet?
  log("Infering argument types for #{name}")
  by_name = target.resolve.find_callable_methods(name, true)
  by_name_and_arity = by_name.select {|m| m.argument_types.size == arg_types.size}
  filtered_args = Set.new(by_name_and_arity.map {|m| m.argument_types})
  if filtered_args.size == 1
    arg_types.zip(filtered_args.first).each do |arg, super_arg|
      arg.declare(cache_and_wrap(super_arg), arg.position)
    end
  else
    log("Found method types:")
    filtered_args.each {|args| log("  #{args.map{|a|a.full_name}.inspect}")}
    arg_types.each {|arg| arg.declare(ErrorType.new([["Missing declaration"]]), nil)}
  # TODO else give a more useful error?
  end
end

#infer_override_return_type(target, name, arg_types) ⇒ Object



541
542
543
544
545
546
547
548
# File 'lib/mirah/jvm/types/factory.rb', line 541

def infer_override_return_type(target, name, arg_types)
  by_name = target.resolve.find_callable_methods(name, true)
  by_name_and_arity = {}
  by_name.each {|m| by_name_and_arity[m.argument_types] = m if m.argument_types.size == arg_types.size }
  resolved_args = arg_types.map {|a| a.resolve}
  match = by_name_and_arity[resolved_args]
  return cache_and_wrap(match.return_type) if match
end

#initialize_copy(other) ⇒ Object



69
70
71
72
73
74
75
76
77
# File 'lib/mirah/jvm/types/factory.rb', line 69

def initialize_copy(other)
  @known_types = other.known_types.dup
  @known_types.delete_if do |key, value|
    value.basic_type.kind_of?(Mirah::JVM::Types::TypeDefinition)
  end
  @declarations = []
  @futures = {}
  @fields = {}
end

#known_type(scope, name) ⇒ Object



654
655
656
# File 'lib/mirah/jvm/types/factory.rb', line 654

def known_type(scope, name)
  basic_type(scope, name)
end

#maybe_initialize_builtins(compiler) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/mirah/jvm/types/factory.rb', line 79

def maybe_initialize_builtins(compiler)
  if Builtins
    begin
      Builtins.initialize_builtins(compiler.type_system)
    rescue NativeException => ex
      error("Error initializing builtins", ex.cause)
    rescue => ex
      error("Error initializing builtins: #{ex.message}\n\t#{ex.backtrace.join("\n\t")}")
    end
  else
    warning "Unable to initialize builtins"
  end
end

#mirror_class(klass) ⇒ Object



764
765
766
767
768
769
770
771
772
773
774
# File 'lib/mirah/jvm/types/factory.rb', line 764

def mirror_class(klass)
  name = klass.name.tr('.', '/')
  if klass.respond_to?(:resource_as_stream)
    stream = klass.resource_as_stream("/#{name}.class")
  elsif klass.respond_to?(:get_resource_as_stream)
    stream = klass.get_resource_as_stream("/#{name}.class")
  else
    return get_mirror(klass.name)
  end
  BiteScript::ASM::ClassMirror.load(stream)
end

#package_search(name, scope) ⇒ Object



613
614
615
616
617
618
619
620
621
622
623
624
# File 'lib/mirah/jvm/types/factory.rb', line 613

def package_search(name, scope)
  packages = []
  current_package = scope.package
  packages << current_package unless current_package.nil? || current_package.empty?
  packages.concat(scope.search_packages)
  packages << 'java.lang'
  packages.each do |package|
    type =  get_type("#{package}.#{name}")
    return type if type
  end
  return nil
end

#resource_loaderObject



737
738
739
# File 'lib/mirah/jvm/types/factory.rb', line 737

def resource_loader
  @resource_loader ||= URLClassLoader.new(Mirah::Env.make_urls(classpath), bootstrap_loader)
end

#type(scope, name, array = false, meta = false) ⇒ Object



556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
# File 'lib/mirah/jvm/types/factory.rb', line 556

def type(scope, name, array=false, meta=false)
  if name.kind_of?(BiteScript::ASM::Type)
    if name.getDescriptor[0] == ?[
      return type(scope, name.getElementType, true, meta)
    else
      name = name.getClassName
    end
  elsif name.kind_of?(Type) && name.array?
    array = true
  end
  type = basic_type(scope, name)
  type = type.array_type if type && array
  type = type.meta if type && meta
  return type
end

#wrap(resolved_type) ⇒ Object



93
94
95
96
97
# File 'lib/mirah/jvm/types/factory.rb', line 93

def wrap(resolved_type)
  future = BaseTypeFuture.new(nil)
  future.resolved(resolved_type) if resolved_type
  future
end