@@ -102,58 +102,37 @@ class no_unique_unimplemented_method_exceptiont : public std::exception
102
102
const std::string message;
103
103
};
104
104
105
- // / Find the unique abstract method that isn't equals(...) or hashCode() in a
106
- // / list of methods. Returns the method if one is found, or null if there are no
107
- // / matching methods, or throws no_unique_unimplemented_method_exceptiont if
108
- // / there are at least two.
109
- static const java_class_typet::methodt *get_unique_abstract_method (
110
- const java_class_typet::methodst &methods,
111
- const namespacet &ns)
105
+ struct compare_base_name_and_descriptort
112
106
{
113
- const java_class_typet::methodt *result = nullptr ;
114
- for (const auto &method : methods)
107
+ int operator ()(
108
+ const java_class_typet::methodt *a,
109
+ const java_class_typet::methodt *b) const
115
110
{
116
- const auto &mangled_name = method.get_name ();
117
- static const irep_idt equals = " equals" ;
118
- static const irep_idt hashCode = " hashCode" ;
119
- if (method.get_base_name () == equals || method.get_base_name () == hashCode)
120
- {
121
- // equals and hashCode methods can't be the implemented method of a
122
- // functional interface, even if the interface re-declares them as
123
- // abstract.
124
- continue ;
125
- }
126
- if (!ns.lookup (mangled_name).type .get_bool (ID_C_abstract))
127
- continue ;
128
- if (result)
129
- {
130
- throw no_unique_unimplemented_method_exceptiont (
131
- " produces a type with at least two unimplemented methods" );
132
- }
133
- result = &method;
111
+ return a->get_base_name () == b->get_base_name ()
112
+ ? (a->get_descriptor () == b->get_descriptor ()
113
+ ? 0
114
+ : a->get_descriptor () < b->get_descriptor ())
115
+ : a->get_base_name () < b->get_base_name ();
134
116
}
135
- return result;
136
- }
137
-
138
- static bool same_base_name_and_descriptor (
139
- const java_class_typet::methodt &method1,
140
- const java_class_typet::methodt &method2)
141
- {
142
- return method1.get_base_name () == method2.get_base_name () &&
143
- method1.get_descriptor () == method2.get_descriptor ();
144
- }
117
+ };
145
118
146
- // / Find the unique unimplemented method on a given interface type, including
147
- // / considering its parents. Returns the method if one is found, or empty if
148
- // / there are no unimplemented methods, or throws
149
- // / no_unique_unimplemented_method_exceptiont if there are at least two.
150
- // / If there are multiple name-and-descriptor-compatible unimplemented methods,
151
- // / for example because both If1.f(int) and If2.f(int) are unimplemented but
152
- // / due to their compatible descriptors both can be satisfied with one method,
153
- // / one of the matching methods is chosen arbitrarily.
154
- static const java_class_typet::methodt *get_unique_unimplemented_method (
155
- const irep_idt &interface_id,
156
- const namespacet &ns)
119
+ // / Map from method, indexed by name and descriptor but not defining class, onto
120
+ // / defined-ness (i.e. true if defined with a default method, false if abstract)
121
+ typedef std::map<
122
+ const java_class_typet::methodt *,
123
+ bool ,
124
+ compare_base_name_and_descriptort>
125
+ methods_by_name_and_descriptort;
126
+
127
+ // / Find all methods defined by this method and its parent types, returned as
128
+ // / a map from const java_class_typet::methodt * onto a boolean value which is
129
+ // / true if the method is defined (i.e. has a default definition) as of this
130
+ // / node in the class graph.
131
+ // / If there are multiple name-and-descriptor-compatible methods,
132
+ // / for example because both If1.f(int) and If2.f(int) are inherited here, only
133
+ // / one is stored in the map, chosen arbitrarily.
134
+ static const methods_by_name_and_descriptort
135
+ get_interface_methods (const irep_idt &interface_id, const namespacet &ns)
157
136
{
158
137
static const irep_idt jlo = " java::java.lang.Object" ;
159
138
// Terminate recursion at Object; any other base of an interface must
@@ -170,25 +149,56 @@ static const java_class_typet::methodt *get_unique_unimplemented_method(
170
149
" produces a type that inherits the stub type " + id2string (interface_id));
171
150
}
172
151
173
- const java_class_typet::methodt *result =
174
- get_unique_abstract_method (interface.methods (), ns);
152
+ methods_by_name_and_descriptort all_methods;
175
153
154
+ // First accumulate definitions from child types:
176
155
for (const auto &base : interface.bases ())
177
156
{
178
- const java_class_typet::methodt *base_result =
179
- get_unique_unimplemented_method (base.type ().get_identifier (), ns);
157
+ const methods_by_name_and_descriptort base_methods =
158
+ get_interface_methods (base.type ().get_identifier (), ns);
159
+ for (const auto base_method : base_methods)
160
+ {
161
+ if (base_method.second )
162
+ {
163
+ // Any base definition fills any abstract definition from another base:
164
+ all_methods[base_method.first ] = true ;
165
+ }
166
+ else
167
+ {
168
+ // An abstract method incoming from a base falls to any existing
169
+ // definition, so only insert if not present:
170
+ all_methods.emplace (base_method.first , false );
171
+ }
172
+ }
173
+ }
174
+
175
+ // Now insert defintions from this class:
176
+ for (const auto &method : interface.methods ())
177
+ {
178
+ static const irep_idt equals = " equals" ;
179
+ static const irep_idt equals_descriptor = " (Ljava/lang/Object;)Z" ;
180
+ static const irep_idt hashCode = " hashCode" ;
181
+ static const irep_idt hashCode_descriptor = " ()I" ;
180
182
if (
181
- base_result && result &&
182
- !same_base_name_and_descriptor (*base_result, *result))
183
+ (method.get_base_name () == equals &&
184
+ method.get_descriptor () == equals_descriptor) ||
185
+ (method.get_base_name () == hashCode &&
186
+ method.get_descriptor () == hashCode_descriptor))
183
187
{
184
- throw no_unique_unimplemented_method_exceptiont (
185
- " produces a type with at least two unimplemented methods" );
188
+ // Ignore any uses of functions that are certainly defined on
189
+ // java.lang.Object-- even if explicitly made abstract, they can't be the
190
+ // implemented method of a functional interface.
191
+ continue ;
186
192
}
187
- else if (base_result)
188
- result = base_result;
193
+
194
+ // Note unlike inherited definitions, an abstract definition here *does*
195
+ // wipe out a non-abstract definition (i.e. a default method) from a parent
196
+ // type.
197
+ all_methods[&method] =
198
+ !ns.lookup (method.get_name ()).type .get_bool (ID_C_abstract);
189
199
}
190
200
191
- return result ;
201
+ return all_methods ;
192
202
}
193
203
194
204
static const java_class_typet::methodt *try_get_unique_unimplemented_method (
@@ -201,13 +211,29 @@ static const java_class_typet::methodt *try_get_unique_unimplemented_method(
201
211
const namespacet ns{symbol_table};
202
212
try
203
213
{
214
+ const methods_by_name_and_descriptort all_methods =
215
+ get_interface_methods (functional_interface_tag.get_identifier (), ns);
216
+
204
217
const java_class_typet::methodt *method_and_descriptor_to_implement =
205
- get_unique_unimplemented_method (
206
- functional_interface_tag.get_identifier (), ns);
218
+ nullptr ;
219
+
220
+ for (const auto &entry : all_methods)
221
+ {
222
+ if (!entry.second )
223
+ {
224
+ if (method_and_descriptor_to_implement != nullptr )
225
+ {
226
+ throw no_unique_unimplemented_method_exceptiont (
227
+ " produces a type with at least two unimplemented methods" );
228
+ }
229
+ method_and_descriptor_to_implement = entry.first ;
230
+ }
231
+ }
232
+
207
233
if (!method_and_descriptor_to_implement)
208
234
{
209
235
throw no_unique_unimplemented_method_exceptiont (
210
- " produces a type with no methods" );
236
+ " produces a type with no unimplemented methods" );
211
237
}
212
238
return method_and_descriptor_to_implement;
213
239
}
0 commit comments