Skip to content

Commit a1f60ea

Browse files
authored
Merge pull request #5352 from smowton/smowton/feature/default-methods
Java frontend: support default methods
2 parents c608235 + 1231e22 commit a1f60ea

File tree

12 files changed

+139
-35
lines changed

12 files changed

+139
-35
lines changed
Binary file not shown.
617 Bytes
Binary file not shown.
Binary file not shown.
1.58 KB
Binary file not shown.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
public class Test extends Parent implements TestInterface, BothInterface {
2+
3+
public int parentInterfaceOverriddenByTest(int x) { return x + 7; }
4+
public int testInterfaceOverriddenByTest(int x) { return x + 8; }
5+
6+
public static void testme() {
7+
8+
Test test = new Test();
9+
assert test.testInterfaceNotOverridden(1) == 2;
10+
assert test.testInterfaceOverriddenByTest(1) == 9;
11+
assert test.parentInterfaceNotOverridden(1) == 4;
12+
assert test.parentInterfaceOverriddenByParent(1) == 7;
13+
assert test.parentInterfaceOverriddenByTest(1) == 8;
14+
assert test.testInterfaceOverriddenByParent(1) == 11;
15+
assert test.bothInterfaceOverriddenByParent(1) == 13;
16+
17+
// Check we made it here:
18+
assert false;
19+
20+
}
21+
22+
public static void main(String[] args) { testme(); }
23+
24+
}
25+
26+
class Parent implements ParentInterface, BothInterface {
27+
28+
public int parentInterfaceOverriddenByParent(int x) { return x + 6; }
29+
// Note this isn't an override here, as this class doesn't implement TestInterface,
30+
// but it will become one when Test implements that interface.
31+
public int testInterfaceOverriddenByParent(int x) { return x + 10; }
32+
public int bothInterfaceOverriddenByParent(int x) { return x + 12; }
33+
34+
}
35+
36+
interface TestInterface {
37+
38+
default int testInterfaceNotOverridden(int x) { return x + 1; }
39+
default int testInterfaceOverriddenByTest(int x) { return x + 2; }
40+
default int testInterfaceOverriddenByParent(int x) { return x + 9; }
41+
42+
}
43+
44+
interface ParentInterface {
45+
46+
default int parentInterfaceNotOverridden(int x) { return x + 3; }
47+
default int parentInterfaceOverriddenByParent(int x) { return x + 4; }
48+
default int parentInterfaceOverriddenByTest(int x) { return x + 5; }
49+
50+
}
51+
52+
interface BothInterface {
53+
54+
default int bothInterfaceOverriddenByParent(int x) { return x + 11; }
55+
56+
}
Binary file not shown.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
Test
3+
--function Test.testme
4+
line 9 assertion at file Test\.java line 9 function java::Test\.testme:\(\)V bytecode-index 14: SUCCESS
5+
line 10 assertion at file Test\.java line 10 function java::Test\.testme:\(\)V bytecode-index 25: SUCCESS
6+
line 11 assertion at file Test\.java line 11 function java::Test\.testme:\(\)V bytecode-index 36: SUCCESS
7+
line 12 assertion at file Test\.java line 12 function java::Test\.testme:\(\)V bytecode-index 47: SUCCESS
8+
line 13 assertion at file Test\.java line 13 function java::Test\.testme:\(\)V bytecode-index 58: SUCCESS
9+
line 14 assertion at file Test\.java line 14 function java::Test\.testme:\(\)V bytecode-index 69: SUCCESS
10+
line 15 assertion at file Test\.java line 15 function java::Test\.testme:\(\)V bytecode-index 80: SUCCESS
11+
line 18 assertion at file Test\.java line 18 function java::Test\.testme:\(\)V bytecode-index 86: FAILURE
12+
^EXIT=10$
13+
^SIGNAL=0$

jbmc/src/java_bytecode/ci_lazy_methods.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -546,8 +546,8 @@ irep_idt ci_lazy_methodst::get_virtual_method_target(
546546
if(!instantiated_classes.count(classname))
547547
return irep_idt();
548548

549-
resolve_inherited_componentt call_resolver{symbol_table};
550-
const auto resolved_call = call_resolver(classname, call_basename, false);
549+
auto resolved_call =
550+
get_inherited_method_implementation(call_basename, classname, symbol_table);
551551

552552
if(resolved_call)
553553
return resolved_call->get_full_component_identifier();

jbmc/src/java_bytecode/java_bytecode_convert_method.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3252,16 +3252,17 @@ void java_bytecode_convert_method(
32523252
}
32533253

32543254
/// Returns true iff method \p methodid from class \p classname is
3255-
/// a method inherited from a class (and not an interface!) from which
3255+
/// a method inherited from a class or interface from which
32563256
/// \p classname inherits, either directly or indirectly.
32573257
/// \param classname: class whose method is referenced
32583258
/// \param mangled_method_name: The particular overload of a given method.
32593259
bool java_bytecode_convert_methodt::is_method_inherited(
32603260
const irep_idt &classname,
32613261
const irep_idt &mangled_method_name) const
32623262
{
3263-
const auto inherited_method = get_inherited_component(
3264-
classname, mangled_method_name, symbol_table, false);
3263+
const auto inherited_method = get_inherited_method_implementation(
3264+
mangled_method_name, classname, symbol_table);
3265+
32653266
return inherited_method.has_value();
32663267
}
32673268

src/goto-programs/remove_virtual_functions.cpp

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ class get_virtual_calleest
5252
const optionalt<symbol_exprt> &,
5353
const irep_idt &,
5454
dispatch_table_entriest &,
55-
dispatch_table_entries_mapt &,
56-
const function_call_resolvert &) const;
55+
dispatch_table_entries_mapt &) const;
5756
exprt
5857
get_method(const irep_idt &class_id, const irep_idt &component_name) const;
5958
};
@@ -510,14 +509,12 @@ goto_programt::targett remove_virtual_functionst::remove_virtual_function(
510509
/// get_child_functions_rec("C", C.f, "f")
511510
/// -> [{"C", C.f}, {"B", C.f}, {"A", A.f}]
512511
/// \param entry_map: map of class identifiers to dispatch table entries
513-
/// \param resolve_function_call`: function to resolve abstract method call
514512
void get_virtual_calleest::get_child_functions_rec(
515513
const irep_idt &this_id,
516514
const optionalt<symbol_exprt> &last_method_defn,
517515
const irep_idt &component_name,
518516
dispatch_table_entriest &functions,
519-
dispatch_table_entries_mapt &entry_map,
520-
const function_call_resolvert &resolve_function_call) const
517+
dispatch_table_entries_mapt &entry_map) const
521518
{
522519
auto findit=class_hierarchy.class_map.find(this_id);
523520
if(findit==class_hierarchy.class_map.end())
@@ -550,7 +547,8 @@ void get_virtual_calleest::get_child_functions_rec(
550547
}
551548
if(!function.symbol_expr.has_value())
552549
{
553-
const auto resolved_call = resolve_function_call(child, component_name);
550+
const auto resolved_call = get_inherited_method_implementation(
551+
component_name, child, symbol_table);
554552
if(resolved_call)
555553
{
556554
function.class_id = resolved_call->get_class_identifier();
@@ -566,12 +564,7 @@ void get_virtual_calleest::get_child_functions_rec(
566564
entry_map.emplace(child, function);
567565

568566
get_child_functions_rec(
569-
child,
570-
function.symbol_expr,
571-
component_name,
572-
functions,
573-
entry_map,
574-
resolve_function_call);
567+
child, function.symbol_expr, component_name, functions, entry_map);
575568
}
576569
}
577570

@@ -590,15 +583,8 @@ void get_virtual_calleest::get_functions(
590583
const std::string function_name_string(id2string(function_name));
591584
INVARIANT(!class_id.empty(), "All virtual functions must have a class");
592585

593-
resolve_inherited_componentt get_virtual_call_target{symbol_table};
594-
const function_call_resolvert resolve_function_call =
595-
[&get_virtual_call_target](
596-
const irep_idt &class_id, const irep_idt &function_name) {
597-
return get_virtual_call_target(class_id, function_name, false);
598-
};
599-
600-
const auto resolved_call =
601-
get_virtual_call_target(class_id, function_name, false);
586+
auto resolved_call =
587+
get_inherited_method_implementation(function_name, class_id, symbol_table);
602588

603589
// might be an abstract function
604590
dispatch_table_entryt root_function(class_id);
@@ -618,12 +604,7 @@ void get_virtual_calleest::get_functions(
618604
// iterate over all children, transitively
619605
dispatch_table_entries_mapt entry_map;
620606
get_child_functions_rec(
621-
class_id,
622-
root_function.symbol_expr,
623-
function_name,
624-
functions,
625-
entry_map,
626-
resolve_function_call);
607+
class_id, root_function.symbol_expr, function_name, functions, entry_map);
627608

628609
if(root_function.symbol_expr.has_value())
629610
functions.push_back(root_function);

src/goto-programs/resolve_inherited_component.cpp

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,15 @@ resolve_inherited_componentt::resolve_inherited_componentt(
2727
/// class specifier)
2828
/// \param include_interfaces: If true, consider inheritance from interfaces
2929
/// (parent types other than the first listed)
30+
/// \param user_filter: Predicate that should return true for symbols that can
31+
/// be returned. Those for which it returns false will be ignored.
3032
/// \return The concrete component that has been resolved
3133
optionalt<resolve_inherited_componentt::inherited_componentt>
3234
resolve_inherited_componentt::operator()(
3335
const irep_idt &class_id,
3436
const irep_idt &component_name,
35-
bool include_interfaces)
37+
bool include_interfaces,
38+
const std::function<bool(const symbolt &)> user_filter)
3639
{
3740
PRECONDITION(!class_id.empty());
3841
PRECONDITION(!component_name.empty());
@@ -47,7 +50,8 @@ resolve_inherited_componentt::operator()(
4750
const irep_idt &full_component_identifier=
4851
build_full_component_identifier(current_class, component_name);
4952

50-
if(symbol_table.has_symbol(full_component_identifier))
53+
const symbolt *symbol = symbol_table.lookup(full_component_identifier);
54+
if(symbol && user_filter(*symbol))
5155
{
5256
return inherited_componentt(current_class, component_name);
5357
}
@@ -102,3 +106,43 @@ irep_idt resolve_inherited_componentt::inherited_componentt::
102106
return resolve_inherited_componentt::build_full_component_identifier(
103107
class_identifier, component_identifier);
104108
}
109+
110+
/// Given a class and a component, identify the concrete method it is
111+
/// resolved to. For example, a reference Child.abc refers to Child's method or
112+
/// field if it exists, or else Parent.abc, and so on regarding Parent's
113+
/// ancestors. If none are found, an empty string will be returned.
114+
/// This looks first for non-abstract methods inherited from the first base
115+
/// (i.e., for Java the superclass), then for non-abstract methods inherited
116+
/// otherwise (for Java, interface default methods), then for any abstract
117+
/// declaration.
118+
/// \param classname: The name of the class the function is being called on
119+
/// \param call_basename: The base name of the component (i.e. without the
120+
/// class specifier)
121+
/// \param symbol_table: Global symbol table
122+
/// \return The concrete component that has been resolved
123+
optionalt<resolve_inherited_componentt::inherited_componentt>
124+
get_inherited_method_implementation(
125+
const irep_idt &call_basename,
126+
const irep_idt &classname,
127+
const symbol_tablet &symbol_table)
128+
{
129+
resolve_inherited_componentt call_resolver{symbol_table};
130+
auto exclude_abstract_methods = [&](const symbolt &symbol) {
131+
return !symbol.type.get_bool(ID_C_abstract);
132+
};
133+
134+
auto resolved_call =
135+
call_resolver(classname, call_basename, false, exclude_abstract_methods);
136+
if(!resolved_call)
137+
{
138+
// Check for a default implementation:
139+
resolved_call =
140+
call_resolver(classname, call_basename, true, exclude_abstract_methods);
141+
}
142+
if(!resolved_call)
143+
{
144+
// Finally accept any abstract definition, which will likely get stubbed:
145+
resolved_call = call_resolver(classname, call_basename, true);
146+
}
147+
return resolved_call;
148+
}

src/goto-programs/resolve_inherited_component.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ class resolve_inherited_componentt
4747
optionalt<inherited_componentt> operator()(
4848
const irep_idt &class_id,
4949
const irep_idt &component_name,
50-
bool include_interfaces);
50+
bool include_interfaces,
51+
std::function<bool(const symbolt &)> user_filter = [](const symbolt &) {
52+
return true;
53+
});
5154

5255
static irep_idt build_full_component_identifier(
5356
const irep_idt &class_name, const irep_idt &component_name);
@@ -56,4 +59,10 @@ class resolve_inherited_componentt
5659
const symbol_tablet &symbol_table;
5760
};
5861

62+
optionalt<resolve_inherited_componentt::inherited_componentt>
63+
get_inherited_method_implementation(
64+
const irep_idt &call_basename,
65+
const irep_idt &classname,
66+
const symbol_tablet &symbol_table);
67+
5968
#endif // CPROVER_GOTO_PROGRAMS_RESOLVE_INHERITED_COMPONENT_H

0 commit comments

Comments
 (0)