@@ -102,58 +102,37 @@ class no_unique_unimplemented_method_exceptiont : public std::exception
102102 const std::string message;
103103};
104104
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
112106{
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
115110 {
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 ();
134116 }
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+ };
145118
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)
157136{
158137 static const irep_idt jlo = " java::java.lang.Object" ;
159138 // 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(
170149 " produces a type that inherits the stub type " + id2string (interface_id));
171150 }
172151
173- const java_class_typet::methodt *result =
174- get_unique_abstract_method (interface.methods (), ns);
152+ methods_by_name_and_descriptort all_methods;
175153
154+ // First accumulate definitions from child types:
176155 for (const auto &base : interface.bases ())
177156 {
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" ;
180182 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))
183187 {
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 ;
186192 }
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);
189199 }
190200
191- return result ;
201+ return all_methods ;
192202}
193203
194204static 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(
201211 const namespacet ns{symbol_table};
202212 try
203213 {
214+ const methods_by_name_and_descriptort all_methods =
215+ get_interface_methods (functional_interface_tag.get_identifier (), ns);
216+
204217 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+
207233 if (!method_and_descriptor_to_implement)
208234 {
209235 throw no_unique_unimplemented_method_exceptiont (
210- " produces a type with no methods" );
236+ " produces a type with no unimplemented methods" );
211237 }
212238 return method_and_descriptor_to_implement;
213239 }
0 commit comments