Skip to content

Commit 9ce6a06

Browse files
authored
Merge pull request #3124 from antlechner/stub-return-enum
Load static initializers for enum types returned by opaque methods
2 parents d63fb7a + f9da0bd commit 9ce6a06

13 files changed

+130
-41
lines changed
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
public enum Color {
2+
RED,
3+
GREEN,
4+
BLUE
5+
}
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
public class NondetEnumOpaqueReturn {
2+
3+
public static void canChooseSomeConstant() {
4+
Opaque o = new Opaque();
5+
Color c = o.getC();
6+
if (c == null)
7+
return;
8+
assert c != null;
9+
boolean isRed = c.name().startsWith("RED") && c.name().length() == 3
10+
&& c.ordinal() == 0;
11+
boolean isGreen = c.name().startsWith("GREEN") && c.name().length() == 5
12+
&& c.ordinal() == 1;
13+
boolean isBlue = c.name().startsWith("BLUE") && c.name().length() == 4
14+
&& c.ordinal() == 2;
15+
assert (isRed || isGreen || isBlue);
16+
}
17+
18+
public static void canChooseRed() {
19+
Opaque o = new Opaque();
20+
Color c = o.getC();
21+
if (c == null)
22+
return;
23+
boolean isGreen = c.name().startsWith("GREEN") && c.name().length() == 5
24+
&& c.ordinal() == 1;
25+
boolean isBlue = c.name().startsWith("BLUE") && c.name().length() == 4
26+
&& c.ordinal() == 2;
27+
assert (isGreen || isBlue);
28+
}
29+
30+
public static void canChooseGreen() {
31+
Opaque o = new Opaque();
32+
Color c = o.getC();
33+
if (c == null)
34+
return;
35+
boolean isRed = c.name().startsWith("RED") && c.name().length() == 3
36+
&& c.ordinal() == 0;
37+
boolean isBlue = c.name().startsWith("BLUE") && c.name().length() == 4
38+
&& c.ordinal() == 2;
39+
assert (isRed || isBlue);
40+
}
41+
42+
public static void canChooseBlue() {
43+
Opaque o = new Opaque();
44+
Color c = o.getC();
45+
if (c == null)
46+
return;
47+
boolean isRed = c.name().startsWith("RED") && c.name().length() == 3
48+
&& c.ordinal() == 0;
49+
boolean isGreen = c.name().startsWith("GREEN") && c.name().length() == 5
50+
&& c.ordinal() == 1;
51+
assert (isRed || isGreen);
52+
}
53+
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
public class Opaque {
2+
3+
Color c;
4+
5+
public Color getC() {
6+
return c;
7+
}
8+
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
NondetEnumOpaqueReturn.class
3+
--function NondetEnumOpaqueReturn.canChooseSomeConstant --cp `../../../../scripts/format_classpath.sh . ../../../src/java_bytecode/library/core-models.jar`
4+
^VERIFICATION SUCCESSFUL$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
9+
The test checks that the name and ordinal fields of nondet-initialized enums
10+
which have been returned by an opaque method correspond to those of an enum
11+
constant of the same type.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CORE
2+
NondetEnumOpaqueReturn.class
3+
--function NondetEnumOpaqueReturn.canChooseRed --cp `../../../../scripts/format_classpath.sh . ../../../src/java_bytecode/library/core-models.jar`
4+
^VERIFICATION FAILED$
5+
--
6+
--
7+
Test 1 of 3 to check that any of the enum constants can be chosen.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CORE
2+
NondetEnumOpaqueReturn.class
3+
--function NondetEnumOpaqueReturn.canChooseGreen --cp `../../../../scripts/format_classpath.sh . ../../../src/java_bytecode/library/core-models.jar`
4+
^VERIFICATION FAILED$
5+
--
6+
--
7+
Test 2 of 3 to check that any of the enum constants can be chosen.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CORE
2+
NondetEnumOpaqueReturn.class
3+
--function NondetEnumOpaqueReturn.canChooseBlue --cp `../../../../scripts/format_classpath.sh . ../../../src/java_bytecode/library/core-models.jar`
4+
^VERIFICATION FAILED$
5+
--
6+
--
7+
Test 3 of 3 to check that any of the enum constants can be chosen.

jbmc/src/java_bytecode/ci_lazy_methods.cpp

-31
Original file line numberDiff line numberDiff line change
@@ -392,37 +392,6 @@ void ci_lazy_methodst::initialize_instantiated_classes(
392392
// As in class_loader, ensure these classes stay available
393393
for(const auto &id : extra_instantiated_classes)
394394
needed_lazy_methods.add_needed_class("java::" + id2string(id));
395-
396-
// Special case for enums. We may want to generalise this, see TG-4689
397-
// and the comment in java_object_factoryt::gen_nondet_pointer_init.
398-
for(const auto &found_class : needed_lazy_methods.get_instantiated_classes())
399-
{
400-
const auto &class_type = to_java_class_type(ns.lookup(found_class).type);
401-
if(class_type.get_base("java::java.lang.Enum"))
402-
add_clinit_call(found_class, ns.get_symbol_table(), needed_lazy_methods);
403-
}
404-
}
405-
406-
/// Helper function for `initialize_instantiated_classes`.
407-
/// For a given class id that is being noted as needed in `needed_lazy_methods`,
408-
/// notes that its static initializer is also needed.
409-
/// This applies the same logic to the given class that
410-
/// `java_bytecode_convert_methodt::get_clinit_call` applies e.g. to classes
411-
/// whose constructor we call in a method body. This duplication is unavoidable
412-
/// due to the fact that ci_lazy_methods essentially has to go through the same
413-
/// logic as __CPROVER_start in its initial setup.
414-
/// \param class_id: The given class id
415-
/// \param symbol_table: Used to look up occurrences of static initializers
416-
/// \param [out] needed_lazy_methods: Gets notified of any static initializers
417-
/// that need to be loaded
418-
void ci_lazy_methodst::add_clinit_call(
419-
const irep_idt &class_id,
420-
const symbol_tablet &symbol_table,
421-
ci_lazy_methods_neededt &needed_lazy_methods)
422-
{
423-
const irep_idt &clinit_wrapper = clinit_wrapper_name(class_id);
424-
if(symbol_table.symbols.count(clinit_wrapper))
425-
needed_lazy_methods.add_needed_method(clinit_wrapper);
426395
}
427396

428397
/// Get places where virtual functions are called.

jbmc/src/java_bytecode/ci_lazy_methods.h

-5
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,6 @@ class ci_lazy_methodst:public messaget
120120
const namespacet &ns,
121121
ci_lazy_methods_neededt &needed_lazy_methods);
122122

123-
void add_clinit_call(
124-
const irep_idt &class_id,
125-
const symbol_tablet &symbol_table,
126-
ci_lazy_methods_neededt &needed_lazy_methods);
127-
128123
void gather_virtual_callsites(
129124
const exprt &e,
130125
std::unordered_set<exprt, irep_hash> &result);

jbmc/src/java_bytecode/ci_lazy_methods_needed.cpp

+27
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Author: Chris Smowton, [email protected]
1010
/// Context-insensitive lazy methods container
1111

1212
#include "ci_lazy_methods.h"
13+
#include "java_static_initializers.h"
1314

1415
#include <java_bytecode/select_pointer_type.h>
1516
#include <string>
@@ -25,6 +26,24 @@ void ci_lazy_methods_neededt::add_needed_method(
2526
callable_methods.insert(method_symbol_name);
2627
}
2728

29+
/// For a given class id, note that its static initializer is needed.
30+
/// This applies the same logic to the given class that
31+
/// `java_bytecode_convert_methodt::get_clinit_call` applies e.g. to classes
32+
/// whose constructor we call in a method body. This duplication is unavoidable
33+
/// because ci_lazy_methods essentially has to go through the same logic as
34+
/// __CPROVER_start in its initial setup, and because return values of opaque
35+
/// methods need to be considered in ci_lazy_methods too.
36+
/// \param class_id: The given class id
37+
/// \param symbol_table: Used to look up occurrences of static initializers
38+
void ci_lazy_methods_neededt::add_clinit_call(
39+
const irep_idt &class_id,
40+
const symbol_tablet &symbol_table)
41+
{
42+
const irep_idt &clinit_wrapper = clinit_wrapper_name(class_id);
43+
if(symbol_table.symbols.count(clinit_wrapper))
44+
add_needed_method(clinit_wrapper);
45+
}
46+
2847
/// Notes class `class_symbol_name` will be instantiated, or a static field
2948
/// belonging to it will be accessed. Also notes that its static initializer is
3049
/// therefore reachable.
@@ -42,6 +61,14 @@ bool ci_lazy_methods_neededt::add_needed_class(
4261
if(symbol_table.symbols.count(cprover_validate))
4362
add_needed_method(cprover_validate);
4463

64+
// Special case for enums. We may want to generalise this, the comment in
65+
// \ref java_object_factoryt::gen_nondet_pointer_init (TG-4689).
66+
namespacet ns(symbol_table);
67+
const auto &class_type =
68+
to_java_class_type(ns.lookup(class_symbol_name).type);
69+
if(class_type.get_base("java::java.lang.Enum"))
70+
add_clinit_call(class_symbol_name, symbol_table);
71+
4572
return true;
4673
}
4774

jbmc/src/java_bytecode/ci_lazy_methods_needed.h

+3-5
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,6 @@ class ci_lazy_methods_neededt
3535
pointer_type_selector(pointer_type_selector)
3636
{}
3737

38-
std::unordered_set<irep_idt> get_instantiated_classes()
39-
{
40-
return instantiated_classes;
41-
}
42-
4338
void add_needed_method(const irep_idt &);
4439
// Returns true if new
4540
bool add_needed_class(const irep_idt &);
@@ -61,6 +56,9 @@ class ci_lazy_methods_neededt
6156

6257
const select_pointer_typet &pointer_type_selector;
6358

59+
void
60+
add_clinit_call(const irep_idt &class_id, const symbol_tablet &symbol_table);
61+
6462
void initialize_instantiated_classes_from_pointer(
6563
const pointer_typet &pointer_type,
6664
const namespacet &ns);

0 commit comments

Comments
 (0)