Module: Duby::JVM::MethodLookup

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

Instance Method Summary collapse

Instance Method Details

#each_is_exact(incoming, target) ⇒ Object



167
168
169
170
171
172
173
174
175
# File 'lib/duby/jvm/method_lookup.rb', line 167

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



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/duby/jvm/method_lookup.rb', line 177

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

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



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/duby/jvm/method_lookup.rb', line 124

def field_lookup(mapped_params, mapped_type, meta, name)
  log("Attempting #{meta ? 'static' : 'instance'} field lookup for '#{name}' on class #{mapped_type}")
  # if we get to this point, the potentials do not match, so we ignore them
  
  
  # search for a field of the given name
  if name =~ /_set$/
    # setter
    setter = true
    name = name[0..-5]
    field = mapped_type.field_setter(name)
  else
    # getter
    setter = false

    # field accesses don't take arguments
    return if mapped_params.size > 0
    field = mapped_type.field_getter(name)
  end

  return nil unless field

  if (meta && !field.static?) ||
      (!meta && field.static?)
    field == nil
  end

  # check accessibility
  # TODO: protected field access check appropriate to current type
  if setter
    raise "cannot set final field '#{name}' on class #{mapped_type}" if field.final?
  end
  raise "cannot access field '#{name}' on class #{mapped_type}" unless field.public?

  field
end

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



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/duby/jvm/method_lookup.rb', line 40

def find_jls(mapped_type, name, mapped_params, meta, constructor)
  if constructor
    by_name = mapped_type.unmeta.declared_constructors
  elsif meta
    by_name = mapped_type.declared_class_methods(name)
  else
    by_name = []
    cls = mapped_type
    while cls
      by_name += cls.declared_instance_methods(name)
      cls = cls.superclass
    end
  end
  # filter by arity
  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
    method_list = phase1_methods.map do |m|
      
      "#{m.name}(#{m.parameter_types.map(&:name).join(', ')})"
    end.join("\n")
    raise "Ambiguous targets invoking #{mapped_type}.#{name}:\n#{method_list}"
  end

  phase1_methods[0] ||
    phase2(mapped_params, by_name) ||
    phase3(mapped_params, by_name) ||
    field_lookup(mapped_params, mapped_type, meta, name) ||
    inner_class(mapped_params, mapped_type, meta, name)
end

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

Raises:

  • (ArgumentError)


7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/duby/jvm/method_lookup.rb', line 7

def find_method(mapped_type, name, mapped_params, meta)
  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
    # exact args failed, do a deeper search
    log "No exact match for #{mapped_type.name}.#{name}(#{mapped_params.map(&:name).join ', '})"

    method = find_jls(mapped_type, name, mapped_params, meta, constructor)

    unless method
      log "Failed to locate method #{mapped_type.name}.#{name}(#{mapped_params.map(&:name).join ', '})"
      return nil
    end
  end

  log "Found method #{method.declaring_class.name}.#{name}(#{method.argument_types.map(&:name).join ', '}) from #{mapped_type.name}"
  return method
end

#inner_class(params, type, meta, name) ⇒ Object



161
162
163
164
165
# File 'lib/duby/jvm/method_lookup.rb', line 161

def inner_class(params, type, meta, name)
  return unless params.size == 0 && meta
  log("Attempting inner class lookup for '#{name}' on #{type}")
  type.inner_class_getter(name)
end

#is_more_specific?(potential, current) ⇒ Boolean

Returns:

  • (Boolean)


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

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



73
74
75
76
77
78
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
# File 'lib/duby/jvm/method_lookup.rb', line 73

def phase1(mapped_params, potentials)
  log "Beginning JLS phase 1 search with params (#{mapped_params.map(&:name)})"

  # cycle through methods looking for more specific matches; gather matches of equal specificity
  methods = potentials.inject([]) do |currents, potential|
    method_params = potential.argument_types
    raise "Bad arguments for method #{potential.declaring_class}.#{potential.name}" unless method_params.all?

    # exact match always wins; duplicates not possible
    if each_is_exact(mapped_params, method_params)
      return [potential]
    end

    # 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



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

def phase2(mapped_params, potentials)
  nil
end

#phase3(mapped_params, potentials) ⇒ Object



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

def phase3(mapped_params, potentials)
  nil
end

#primitive_convertible?(in_type, target_type) ⇒ Boolean

Returns:

  • (Boolean)


198
199
200
# File 'lib/duby/jvm/method_lookup.rb', line 198

def primitive_convertible?(in_type, target_type)
  in_type.convertible_to?(target_type)
end