Skip to content

Nondet-initialize enums to be equal to a constant of the same type #2701

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Aug 14, 2018
Binary file added jbmc/regression/jbmc/NondetEnumArg/Color.class
Binary file not shown.
5 changes: 5 additions & 0 deletions jbmc/regression/jbmc/NondetEnumArg/Color.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public enum Color {
RED,
GREEN,
BLUE
}
Binary file not shown.
46 changes: 46 additions & 0 deletions jbmc/regression/jbmc/NondetEnumArg/NondetEnumArg.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
public class NondetEnumArg {

public static void canChooseSomeConstant(Color c) {
if (c == null)
return;
assert c != null;
boolean isRed = c.name().startsWith("RED") && c.name().length() == 3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May we have tests with switch already?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have one tests for switch on an enum type. It uses --cover location and pretty much just checks that all possible ordinals (except the last one) can be generated for a switch statement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@peterschrammel Do you think the switch test and the new NondetEnumArg test should be combined?

&& c.ordinal() == 0;
boolean isGreen = c.name().startsWith("GREEN") && c.name().length() == 5
&& c.ordinal() == 1;
boolean isBlue = c.name().startsWith("BLUE") && c.name().length() == 4
&& c.ordinal() == 2;
assert (isRed || isGreen || isBlue);
}

public static void canChooseRed(Color c) {
if (c == null)
return;
boolean isGreen = c.name().startsWith("GREEN") && c.name().length() == 5
&& c.ordinal() == 1;
boolean isBlue = c.name().startsWith("BLUE") && c.name().length() == 4
&& c.ordinal() == 2;
assert (isGreen || isBlue);
}

public static void canChooseGreen(Color c) {
if (c == null)
return;
boolean isRed = c.name().startsWith("RED") && c.name().length() == 3
&& c.ordinal() == 0;
boolean isBlue = c.name().startsWith("BLUE") && c.name().length() == 4
&& c.ordinal() == 2;
assert (isRed || isBlue);
}

public static void canChooseBlue(Color c) {
if (c == null)
return;
boolean isRed = c.name().startsWith("RED") && c.name().length() == 3
&& c.ordinal() == 0;
boolean isGreen = c.name().startsWith("GREEN") && c.name().length() == 5
&& c.ordinal() == 1;
assert (isRed || isGreen);
}

}
10 changes: 10 additions & 0 deletions jbmc/regression/jbmc/NondetEnumArg/test_some_constant.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CORE
NondetEnumArg.class
--function NondetEnumArg.canChooseSomeConstant --cp `../../../../scripts/format_classpath.sh . ../../../src/java_bytecode/library/core-models.jar`
^VERIFICATION SUCCESSFUL$
^EXIT=0$
^SIGNAL=0$
--
--
The test checks that the name and ordinal fields of nondet-initialized enums
correspond to those of an enum constant of the same type.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CORE
NondetEnumArg.class
--function NondetEnumArg.canChooseRed --cp `../../../../scripts/format_classpath.sh . ../../../src/java_bytecode/library/core-models.jar`
^VERIFICATION FAILED$
--
--
Test 1 of 3 to check that any of the enum constants can be chosen.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CORE
NondetEnumArg.class
--function NondetEnumArg.canChooseGreen --cp `../../../../scripts/format_classpath.sh . ../../../src/java_bytecode/library/core-models.jar`
^VERIFICATION FAILED$
--
--
Test 2 of 3 to check that any of the enum constants can be chosen.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CORE
NondetEnumArg.class
--function NondetEnumArg.canChooseBlue --cp `../../../../scripts/format_classpath.sh . ../../../src/java_bytecode/library/core-models.jar`
^VERIFICATION FAILED$
--
--
Test 3 of 3 to check that any of the enum constants can be chosen.
35 changes: 34 additions & 1 deletion jbmc/src/java_bytecode/ci_lazy_methods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,17 @@ void ci_lazy_methodst::initialize_instantiated_classes(
{
if(param.type().id()==ID_pointer)
{
const pointer_typet &original_pointer=to_pointer_type(param.type());
const pointer_typet &original_pointer = to_pointer_type(param.type());
const auto &original_type = ns.follow(original_pointer.subtype());
// Special case for enums. We may want to generalise this, see TG-4689
// and the comment in java_object_factoryt::gen_nondet_pointer_init.
if(
can_cast_type<java_class_typet>(original_type) &&
to_java_class_type(original_type).get_base("java::java.lang.Enum"))
{
add_clinit_call_for_pointer_type(
original_pointer, ns.get_symbol_table(), needed_lazy_methods);
}
needed_lazy_methods.add_all_needed_classes(original_pointer);
}
}
Expand All @@ -399,6 +409,29 @@ void ci_lazy_methodst::initialize_instantiated_classes(
needed_lazy_methods.add_needed_class("java::" + id2string(id));
}

/// Helper function for `initialize_instantiated_classes`.
/// For a given pointer_typet that is being noted as needed in
/// `needed_lazy_methods`, notes that its static initializer is also needed.
/// This applies the same logic to the class of `pointer_type` that
/// `java_bytecode_convert_methodt::get_clinit_call` applies e.g. to classes
/// whose constructor we call in a method body. This duplication is unavoidable
/// due to the fact that ci_lazy_methods essentially has to go through the same
/// logic as __CPROVER_start in its initial setup.
/// \param pointer_type: The given pointer_typet
/// \param symbol_table: Used to look up occurrences of static initializers
/// \param [out] needed_lazy_methods: Gets notified of any static initializers
/// that need to be loaded
void ci_lazy_methodst::add_clinit_call_for_pointer_type(
const pointer_typet &pointer_type,
const symbol_tablet &symbol_table,
ci_lazy_methods_neededt &needed_lazy_methods)
{
const irep_idt &pointer_id =
to_symbol_type(pointer_type.subtype()).get_identifier();
const irep_idt &clinit_wrapper = clinit_wrapper_name(pointer_id);
if(symbol_table.symbols.count(clinit_wrapper))
needed_lazy_methods.add_needed_method(clinit_wrapper);
}

/// Get places where virtual functions are called.
/// \param e: expression tree to search
Expand Down
5 changes: 5 additions & 0 deletions jbmc/src/java_bytecode/ci_lazy_methods.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ class ci_lazy_methodst:public messaget
const namespacet &ns,
ci_lazy_methods_neededt &needed_lazy_methods);

void add_clinit_call_for_pointer_type(
const pointer_typet &pointer_type,
const symbol_tablet &symbol_table,
ci_lazy_methods_neededt &needed_lazy_methods);

void gather_virtual_callsites(
const exprt &e,
std::unordered_set<exprt, irep_hash> &result);
Expand Down
Loading