Skip to content

Commit ac18b8c

Browse files
committed
java-unwind-enum-static: also unwind clone loop in Enum.values()
If an enumeration type's values() method clones an array, we assume it is cloning the array of enumeration values and therefore is bounded by the enumeration's size, similar to the existing handling of enumeration static initializers.
1 parent 4985554 commit ac18b8c

File tree

4 files changed

+46
-17
lines changed

4 files changed

+46
-17
lines changed

jbmc/src/java_bytecode/java_enum_static_init_unwind_handler.cpp

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,46 @@ Author: Chris Smowton, [email protected]
1414
#include <util/invariant.h>
1515
#include <util/suffix.h>
1616

17+
/// Check if we may be in a function that loops over the cases of an
18+
/// enumeration (note we return a candidate function that matches a pattern;
19+
/// our caller must verify it really belongs to an enumeration).
20+
/// At the moment we know of two cases that definitely do so:
21+
/// * An enumeration type's static initialiser
22+
/// * The array[reference].clone() method when called from an enumeration type's
23+
/// 'values()' method
24+
/// \param context: the current call stack
25+
/// \return the name of an enclosing function that may be defined on the
26+
/// relevant enum type, or an empty string if we don't find one.
27+
static irep_idt
28+
find_enum_function_on_stack(const goto_symex_statet::call_stackt &context)
29+
{
30+
static irep_idt reference_array_clone_id =
31+
"java::array[reference].clone:()Ljava/lang/Object;";
32+
33+
PRECONDITION(!context.empty());
34+
const irep_idt &current_function = context.back().function_identifier;
35+
36+
if(context.size() >= 2 && current_function == reference_array_clone_id)
37+
{
38+
const irep_idt &clone_caller =
39+
context.at(context.size() - 2).function_identifier;
40+
if(id2string(clone_caller).find(".values:()[L") != std::string::npos)
41+
return clone_caller;
42+
else
43+
return irep_idt();
44+
}
45+
else if(has_suffix(id2string(current_function), ".<clinit>:()V"))
46+
return current_function;
47+
else
48+
return irep_idt();
49+
}
50+
1751
/// Unwind handler that special-cases the clinit (static initializer) functions
18-
/// of enumeration classes. When java_bytecode_convert_classt has annotated them
52+
/// of enumeration classes, and VALUES array cloning in their values() methods.
53+
/// When java_bytecode_convert_classt has annotated them
1954
/// with a size of the enumeration type, this forces unwinding of any loop in
2055
/// the static initializer to at least that many iterations, with intent to
21-
/// permit population of the enumeration's value array.
56+
/// permit population / copying of the enumeration's value array.
2257
/// \param function_id: function the loop is in
2358
/// \param loop_number: ordinal number of the loop (ignored)
2459
/// \param unwind_count: iteration count that is about to begin
@@ -29,17 +64,17 @@ Author: Chris Smowton, [email protected]
2964
/// unwind_count is <= the enumeration size, or unknown (defer / no decision)
3065
/// otherwise.
3166
tvt java_enum_static_init_unwind_handler(
32-
const irep_idt &function_id,
67+
const goto_symex_statet::call_stackt &context,
3368
unsigned loop_number,
3469
unsigned unwind_count,
3570
unsigned &unwind_max,
3671
const symbol_tablet &symbol_table)
3772
{
38-
const std::string &function_str = id2string(function_id);
39-
if(!has_suffix(function_str, ".<clinit>:()V"))
73+
const irep_idt enum_function_id = find_enum_function_on_stack(context);
74+
if(enum_function_id.empty())
4075
return tvt::unknown();
4176

42-
const symbolt &function_symbol = symbol_table.lookup_ref(function_str);
77+
const symbolt &function_symbol = symbol_table.lookup_ref(enum_function_id);
4378
irep_idt class_id = function_symbol.type.get(ID_C_class);
4479
INVARIANT(
4580
!class_id.empty(), "functions should have their defining class annotated");

jbmc/src/java_bytecode/java_enum_static_init_unwind_handler.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ Author: Chris Smowton, [email protected]
1212
#ifndef CPROVER_JAVA_BYTECODE_JAVA_ENUM_STATIC_INIT_UNWIND_HANDLER_H
1313
#define CPROVER_JAVA_BYTECODE_JAVA_ENUM_STATIC_INIT_UNWIND_HANDLER_H
1414

15+
#include <goto-symex/goto_symex_state.h>
16+
1517
#include <util/symbol_table.h>
1618
#include <util/threeval.h>
1719

1820
tvt java_enum_static_init_unwind_handler(
19-
const irep_idt &function_id,
21+
const goto_symex_statet::call_stackt &context,
2022
unsigned loop_number,
2123
unsigned unwind_count,
2224
unsigned &unwind_max,

jbmc/src/jbmc/jbmc_parse_options.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -485,11 +485,7 @@ int jbmc_parse_optionst::doit()
485485
unsigned unwind,
486486
unsigned &max_unwind) {
487487
return java_enum_static_init_unwind_handler(
488-
context,
489-
loop_number,
490-
unwind,
491-
max_unwind,
492-
symbol_table);
488+
context, loop_number, unwind, max_unwind, symbol_table);
493489
});
494490
};
495491
}

src/cbmc/symex_bmc.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,7 @@ bool symex_bmct::get_unwind(
117117
for(auto handler : loop_unwind_handlers)
118118
{
119119
abort_unwind_decision =
120-
handler(
121-
context,
122-
source.pc->loop_number,
123-
unwind,
124-
this_loop_limit);
120+
handler(context, source.pc->loop_number, unwind, this_loop_limit);
125121
if(abort_unwind_decision.is_known())
126122
break;
127123
}

0 commit comments

Comments
 (0)