Module: Duby::JVM::MethodLookup

Included in:
Compiler::JVM, Types::Type, Typer::JavaTyper
Defined in:
lib/duby/jvm/method_lookup.rb

Constant Summary collapse

BOOLEAN =
Java::boolean.java_class
BYTE =
Java::byte.java_class
SHORT =
Java::short.java_class
CHAR =
Java::char.java_class
INT =
Java::int.java_class
LONG =
Java::long.java_class
FLOAT =
Java::float.java_class
DOUBLE =
Java::double.java_class
PrimitiveConversions =
{
  BOOLEAN => [BOOLEAN],
  BYTE => [BYTE, SHORT, CHAR, INT, LONG, FLOAT, DOUBLE],
  SHORT => [SHORT, INT, LONG, FLOAT, DOUBLE],
  CHAR => [CHAR, INT, LONG, FLOAT, DOUBLE],
  INT => [INT, LONG, FLOAT, DOUBLE],
  LONG => [LONG, DOUBLE],
  FLOAT => [FLOAT, DOUBLE],
  DOUBLE => [DOUBLE]
}

Instance Method Summary collapse

Instance Method Details

#each_is_exact(incoming, target) ⇒ Object



125
126
127
128
129
130
131
132
133
# File 'lib/duby/jvm/method_lookup.rb', line 125

def each_is_exact(incoming, target)
  incoming.each_with_index do |in_type, i|
    target_type = target[i]
    
    # exact match
    return false unless target_type == in_type
  end
  return true
end

#each_is_exact_or_subtype_or_convertible(incoming, target) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/duby/jvm/method_lookup.rb', line 135

def each_is_exact_or_subtype_or_convertible(incoming, target)
  incoming.each_with_index do |in_type, i|
    target_type = target[i]
    
    # exact match
    next if target_type == in_type
    
    # primitive is safely convertible
    if target_type.primitive?
      if in_type.primitive?
        next if primitive_convertible? in_type, target_type
      end
      return false
    end
    
    # object type is assignable
    return false unless target_type.assignable_from? in_type
  end
  return true
end

#find_jls(mapped_type, name, mapped_params, meta) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/duby/jvm/method_lookup.rb', line 52

def find_jls(mapped_type, name, mapped_params, meta)
  # mapped_type = jvm_type(mapped_type)
  # mapped_params = convert_params(mapped_params)
  if meta
    all_methods = mapped_type.declared_class_methods
  else
    all_methods = []
    cls = mapped_type
    while cls
      all_methods += cls.declared_instance_methods
      cls = cls.superclass
    end
  end
  by_name = all_methods.select {|m| m.name == name && mapped_params.size <= m.argument_types.size}
  by_name_and_arity = by_name.select {|m| m.argument_types.size == mapped_params.size}

  phase1_methods = phase1(mapped_params, by_name_and_arity)

  if phase1_methods.size > 1
    raise "Ambiguous targets invoking #{mapped_type}.#{name}:\n#{phase1_methods}"
  end

  phase1_methods[0] ||
    phase2(mapped_params, by_name) ||
    phase3(mapped_params, by_name)
end

#find_method(mapped_type, name, mapped_params, meta) ⇒ Object

def jvm_type(type)

return type if type.kind_of? Java::JavaClass
return type.jvm_type

end

def convert_params(params)

params.map {|param| jvm_type(param)}

end

Raises:

  • (ArgumentError)


16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/duby/jvm/method_lookup.rb', line 16

def find_method(mapped_type, name, mapped_params, meta)
  # mapped_type = jvm_type(mapped_type)
  # mapped_params = convert_params(mapped_params)
  raise ArgumentError if mapped_params.any? {|p| p.nil?}
  if name == 'new'
    if meta
      name = "<init>"
      constructor = true
    else
      constructor = false
    end
  end

  begin
    if constructor
      method = mapped_type.constructor(*mapped_params)
    else
      method = mapped_type.java_method(name, *mapped_params)
    end
  rescue NameError
    unless constructor
      # exact args failed, do a deeper search
      log "Failed to locate method #{mapped_type}.#{name}(#{mapped_params})"

      method = find_jls(mapped_type, name, mapped_params, meta)
    end
    unless method
      log "Failed to locate method #{name}(#{mapped_params}) on #{mapped_type}"
      return nil
    end
  end

  log "Found method #{method.declaring_class}.#{name}(#{method.argument_types}) from #{mapped_type}"
  return method
end

#is_more_specific?(potential, current) ⇒ Boolean

Returns:

  • (Boolean)


113
114
115
# File 'lib/duby/jvm/method_lookup.rb', line 113

def is_more_specific?(potential, current)
  each_is_exact_or_subtype_or_convertible(potential, current)
end

#log(msg) ⇒ Object

dummy log; it’s expected the inclusion target will have it



5
# File 'lib/duby/jvm/method_lookup.rb', line 5

def log(msg); end

#phase1(mapped_params, potentials) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/duby/jvm/method_lookup.rb', line 79

def phase1(mapped_params, potentials)
  # cycle through methods looking for more specific matches; gather matches of equal specificity
  methods = potentials.inject([]) do |currents, potential|
    method_params = potential.argument_types
    
    # exact match always wins; duplicates not possible
    return [potential] if each_is_exact(mapped_params, method_params)
    
    # otherwise, check for potential match and compare to current
    # TODO: missing ambiguity check; picks last method of equal specificity
    if each_is_exact_or_subtype_or_convertible(mapped_params, method_params)
      if currents.size > 0
        if is_more_specific?(potential.argument_types, currents[0].argument_types)
          # potential is better, dump all currents
          currents = [potential]
        elsif is_more_specific?(currents[0].argument_types, potential.argument_types)
          # currents are better, try next potential
          #next
        else
          # equal specificity, append to currents
          currents << potential
        end
      else
        # no previous matches, use potential
        currents = [potential]
      end
    end
    
    currents
  end

  methods
end

#phase2(mapped_params, potentials) ⇒ Object



117
118
119
# File 'lib/duby/jvm/method_lookup.rb', line 117

def phase2(mapped_params, potentials)
  nil
end

#phase3(mapped_params, potentials) ⇒ Object



121
122
123
# File 'lib/duby/jvm/method_lookup.rb', line 121

def phase3(mapped_params, potentials)
  nil
end

#primitive_convertible?(in_type, target_type) ⇒ Boolean

Returns:

  • (Boolean)


176
177
178
179
180
181
182
# File 'lib/duby/jvm/method_lookup.rb', line 176

def primitive_convertible?(in_type, target_type)
  if PrimitiveConversions.include? in_type
    PrimitiveConversions[in_type].include?(target_type)
  else
    in_type.convertible_to?(target_type)
  end
end