Skip to content

Commit 74f55a0

Browse files
committed
Java lambdas: support all functional interfaces
Previously we failed to detect a functional interface when the abstract method being implemented was inherited, or the interface also declared methods which are defined on java.lang.Object such as toString.
1 parent bf16106 commit 74f55a0

File tree

1 file changed

+99
-30
lines changed

1 file changed

+99
-30
lines changed

jbmc/src/java_bytecode/lambda_synthesis.cpp

Lines changed: 99 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,83 @@ lambda_method_handle(
9292
symbol_table, lambda_method_handles, lambda_handle_index);
9393
}
9494

95+
class no_unique_unimplemented_method_exceptiont : public std::exception
96+
{
97+
public:
98+
explicit no_unique_unimplemented_method_exceptiont(const std::string &s)
99+
: message(s)
100+
{
101+
}
102+
const std::string message;
103+
};
104+
105+
static optionalt<irep_idt> get_unique_abstract_method(
106+
const java_class_typet::methodst &methods,
107+
const namespacet &ns)
108+
{
109+
optionalt<irep_idt> result;
110+
for(const auto &method : methods)
111+
{
112+
const auto &mangled_name = method.get_name();
113+
static const irep_idt equals = "equals";
114+
static const irep_idt hashCode = "hashCode";
115+
if(method.get_base_name() == equals || method.get_base_name() == hashCode)
116+
{
117+
// equals and hashCode methods can't be the implemented method of a
118+
// functional interface, even if the interface re-declares them as
119+
// abstract.
120+
continue;
121+
}
122+
if(!ns.lookup(mangled_name).type.get_bool(ID_C_abstract))
123+
continue;
124+
if(result.has_value() && mangled_name != *result)
125+
{
126+
throw no_unique_unimplemented_method_exceptiont(
127+
"produces a type with at least two unimplemented methods");
128+
}
129+
result = mangled_name;
130+
}
131+
return result;
132+
}
133+
134+
static optionalt<irep_idt> get_unique_unimplemented_method(
135+
const irep_idt &interface_id,
136+
const namespacet &ns)
137+
{
138+
static const irep_idt jlo = "java::java.lang.Object";
139+
// Terminate recursion at Object; any other base of an interface must
140+
// itself be an interface.
141+
if(jlo == interface_id)
142+
return {};
143+
144+
const java_class_typet &interface =
145+
to_java_class_type(ns.lookup(interface_id).type);
146+
147+
if(interface.get_is_stub())
148+
{
149+
throw no_unique_unimplemented_method_exceptiont(
150+
"produces a type that inherits the stub type " + id2string(interface_id));
151+
}
152+
153+
optionalt<irep_idt> result =
154+
get_unique_abstract_method(interface.methods(), ns);
155+
156+
for(const auto &base : interface.bases())
157+
{
158+
optionalt<irep_idt> base_result =
159+
get_unique_unimplemented_method(base.type().get_identifier(), ns);
160+
if(base_result.has_value() && result.has_value() && *base_result != *result)
161+
{
162+
throw no_unique_unimplemented_method_exceptiont(
163+
"produces a type with at least two unimplemented methods");
164+
}
165+
else if(base_result.has_value())
166+
result = base_result;
167+
}
168+
169+
return result;
170+
}
171+
95172
static optionalt<irep_idt> interface_method_id(
96173
const symbol_tablet &symbol_table,
97174
const struct_tag_typet &functional_interface_tag,
@@ -100,34 +177,25 @@ static optionalt<irep_idt> interface_method_id(
100177
const messaget &log)
101178
{
102179
const namespacet ns{symbol_table};
103-
const java_class_typet &implemented_interface_type = [&] {
104-
const symbolt &implemented_interface_symbol =
105-
ns.lookup(functional_interface_tag.get_identifier());
106-
return to_java_class_type(implemented_interface_symbol.type);
107-
}();
108-
109-
if(implemented_interface_type.get_is_stub())
180+
try
110181
{
111-
log.debug() << "ignoring invokedynamic at " << method_identifier
112-
<< " address " << instruction_address
113-
<< " which produces a stub type "
114-
<< functional_interface_tag.get_identifier() << messaget::eom;
115-
return {};
182+
optionalt<irep_idt> method_to_implement = get_unique_unimplemented_method(
183+
functional_interface_tag.get_identifier(), ns);
184+
if(!method_to_implement)
185+
{
186+
throw no_unique_unimplemented_method_exceptiont(
187+
"produces a type with no methods");
188+
}
189+
return method_to_implement;
116190
}
117-
else if(implemented_interface_type.methods().size() != 1)
191+
catch(const no_unique_unimplemented_method_exceptiont &e)
118192
{
119-
log.debug()
120-
<< "ignoring invokedynamic at " << method_identifier << " address "
121-
<< instruction_address << " which produces type "
122-
<< functional_interface_tag.get_identifier()
123-
<< " which should have exactly one abstract method but actually has "
124-
<< implemented_interface_type.methods().size()
125-
<< ". Note default methods are not supported yet."
126-
<< "Also note methods declared in an inherited interface are not "
127-
<< "supported either." << messaget::eom;
193+
log.debug() << "ignoring invokedynamic at " << method_identifier
194+
<< " address " << instruction_address << " with type "
195+
<< functional_interface_tag.get_identifier() << " which "
196+
<< e.message << "." << messaget::eom;
128197
return {};
129198
}
130-
return implemented_interface_type.methods().at(0).get_name();
131199
}
132200

133201
symbolt synthetic_class_symbol(
@@ -223,23 +291,25 @@ static symbolt constructor_symbol(
223291
return constructor_symbol;
224292
}
225293

226-
symbolt implemented_method_symbol(
294+
static symbolt implemented_method_symbol(
227295
synthetic_methods_mapt &synthetic_methods,
228296
const symbolt &interface_method_symbol,
229-
const struct_tag_typet &functional_interface_tag,
230297
const irep_idt &synthetic_class_name)
231298
{
232299
const std::string implemented_method_name = [&] {
233300
std::string implemented_method_name =
234301
id2string(interface_method_symbol.name);
235-
const std::string &functional_interface_tag_str =
236-
id2string(functional_interface_tag.get_identifier());
302+
const std::string &implemented_interface_tag_str =
303+
id2string(interface_method_symbol.type.get(ID_C_class));
237304
INVARIANT(
238-
has_prefix(implemented_method_name, functional_interface_tag_str),
305+
!implemented_interface_tag_str.empty(),
306+
"implemented method's type must specify its declaring class");
307+
INVARIANT(
308+
has_prefix(implemented_method_name, implemented_interface_tag_str),
239309
"method names should be prefixed by their defining type");
240310
implemented_method_name.replace(
241311
0,
242-
functional_interface_tag_str.length(),
312+
implemented_interface_tag_str.length(),
243313
id2string(synthetic_class_name));
244314
return implemented_method_name;
245315
}();
@@ -339,7 +409,6 @@ void create_invokedynamic_synthetic_classes(
339409
symbol_table.add(implemented_method_symbol(
340410
synthetic_methods,
341411
symbol_table.lookup_ref(*interface_method_id),
342-
functional_interface_tag,
343412
synthetic_class_name));
344413
symbol_table.add(synthetic_class_symbol(
345414
synthetic_class_name,

0 commit comments

Comments
 (0)