Skip to content

Commit ae8da63

Browse files
author
Daniel Kroening
authored
Merge pull request #460 from mgudemann/java_unwind_enum_static
Java unwind enum static
2 parents 30f73f5 + b49a7fc commit ae8da63

10 files changed

+204
-19
lines changed

src/cbmc/cbmc_parse_options.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Author: Daniel Kroening, [email protected]
2727
#include <goto-programs/remove_complex.h>
2828
#include <goto-programs/remove_asm.h>
2929
#include <goto-programs/remove_unused_functions.h>
30+
#include <goto-programs/remove_static_init_loops.h>
3031
#include <goto-programs/goto_inline.h>
3132
#include <goto-programs/show_properties.h>
3233
#include <goto-programs/set_properties.h>
@@ -221,6 +222,10 @@ void cbmc_parse_optionst::get_command_line_options(optionst &options)
221222
// all checks supported by goto_check
222223
GOTO_CHECK_PARSE_OPTIONS(cmdline, options);
223224

225+
// unwind loops in java enum static initialization
226+
if(cmdline.isset("java-unwind-enum-static"))
227+
options.set_option("java-unwind-enum-static", true);
228+
224229
// check assertions
225230
if(cmdline.isset("no-assertions"))
226231
options.set_option("assertions", false);
@@ -541,6 +546,9 @@ int cbmc_parse_optionst::doit()
541546
if(set_properties(goto_functions))
542547
return 7; // should contemplate EX_USAGE from sysexits.h
543548

549+
if(options.get_bool_option("java-unwind-enum-static"))
550+
remove_static_init_loops(symbol_table, goto_functions, options);
551+
544552
// do actual BMC
545553
return do_bmc(bmc, goto_functions);
546554
}
@@ -1126,6 +1134,10 @@ void cbmc_parse_optionst::help()
11261134
"Java Bytecode frontend options:\n"
11271135
" --classpath dir/jar set the classpath\n"
11281136
" --main-class class-name set the name of the main class\n"
1137+
// NOLINTNEXTLINE(whitespace/line_length)
1138+
" --java-max-vla-length limit the length of user-code-created arrays\n"
1139+
// NOLINTNEXTLINE(whitespace/line_length)
1140+
" --java-unwind-enum-static try to unwind loops in static initialization of enums\n"
11291141
"\n"
11301142
"Semantic transformations:\n"
11311143
" --nondet-static add nondeterministic initialization of variables with static lifetime\n" // NOLINT(*)

src/cbmc/cbmc_parse_options.h

+6-5
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ class optionst;
3636
"(smt1)(smt2)(fpa)(cvc3)(cvc4)(boolector)(yices)(z3)(opensmt)(mathsat)" \
3737
"(no-sat-preprocessor)" \
3838
"(no-pretty-names)(beautify)" \
39-
"(dimacs)(refine)(max-node-refinement):(refine-arrays)(refine-arithmetic)(aig)" \
40-
"(16)(32)(64)(LP64)(ILP64)(LLP64)(ILP32)(LP32)" \
39+
"(dimacs)(refine)(max-node-refinement):(refine-arrays)(refine-arithmetic)"\
40+
"(aig)(16)(32)(64)(LP64)(ILP64)(LLP64)(ILP32)(LP32)" \
4141
"(little-endian)(big-endian)" \
4242
"(show-goto-functions)(show-loops)" \
4343
"(show-symbol-table)(show-parse-tree)(show-vcc)" \
44-
"(show-claims)(claim):(show-properties)(show-reachable-properties)(property):" \
45-
"(stop-on-fail)(trace)" \
44+
"(show-claims)(claim):(show-properties)(show-reachable-properties)" \
45+
"(property):(stop-on-fail)(trace)" \
4646
"(error-label):(verbosity):(no-library)" \
4747
"(nondet-static)" \
4848
"(version)" \
@@ -54,8 +54,9 @@ class optionst;
5454
"(string-abstraction)(no-arch)(arch):" \
5555
"(round-to-nearest)(round-to-plus-inf)(round-to-minus-inf)(round-to-zero)" \
5656
"(graphml-witness):" \
57+
"(java-max-vla-length):(java-unwind-enum-static)" \
5758
"(localize-faults)(localize-faults-method):" \
58-
"(fixedbv)(floatbv)(all-claims)(all-properties)" // legacy, and will eventually disappear
59+
"(fixedbv)(floatbv)(all-claims)(all-properties)" // legacy, and will eventually disappear // NOLINT(whitespace/line_length)
5960

6061
class cbmc_parse_optionst:
6162
public parse_options_baset,

src/goto-programs/Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ SRC = goto_convert.cpp goto_convert_function_call.cpp \
1717
goto_trace.cpp xml_goto_trace.cpp vcd_goto_trace.cpp \
1818
graphml_witness.cpp remove_virtual_functions.cpp \
1919
class_hierarchy.cpp show_goto_functions.cpp get_goto_model.cpp \
20-
slice_global_inits.cpp goto_inline_class.cpp class_identifier.cpp
20+
slice_global_inits.cpp goto_inline_class.cpp class_identifier.cpp \
21+
remove_static_init_loops.cpp
2122

2223
INCLUDES= -I ..
2324

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*******************************************************************\
2+
3+
Module: Unwind loops in static initializers
4+
5+
Author: Daniel Kroening, [email protected]
6+
7+
\*******************************************************************/
8+
9+
#include <util/message.h>
10+
#include <util/string2int.h>
11+
12+
#include "remove_static_init_loops.h"
13+
14+
class remove_static_init_loopst
15+
{
16+
public:
17+
explicit remove_static_init_loopst(
18+
const symbol_tablet &_symbol_table):
19+
symbol_table(_symbol_table)
20+
{}
21+
22+
void unwind_enum_static(
23+
const goto_functionst &goto_functions,
24+
optionst &options);
25+
protected:
26+
const symbol_tablet &symbol_table;
27+
};
28+
29+
/*******************************************************************\
30+
31+
Function: unwind_enum_static
32+
33+
Inputs: goto_functions and options
34+
35+
Outputs: side effect is adding <clinit> loops to unwindset
36+
37+
Purpose: unwind static initialization loops of Java enums as far as
38+
the enum has elements, thus flattening them completely
39+
40+
\*******************************************************************/
41+
42+
void remove_static_init_loopst::unwind_enum_static(
43+
const goto_functionst &goto_functions,
44+
optionst &options)
45+
{
46+
size_t unwind_max=0;
47+
forall_goto_functions(f, goto_functions)
48+
{
49+
auto &p=f->second.body;
50+
for(const auto &ins : p.instructions)
51+
{
52+
// is this a loop?
53+
if(ins.is_backwards_goto())
54+
{
55+
size_t loop_id=ins.loop_number;
56+
const std::string java_clinit="<clinit>:()V";
57+
const std::string &fname=id2string(ins.function);
58+
size_t class_prefix_length=fname.find_last_of('.');
59+
assert(
60+
class_prefix_length!=std::string::npos &&
61+
"could not identify class name");
62+
const std::string &classname=fname.substr(0, class_prefix_length);
63+
const symbolt &class_symbol=symbol_table.lookup(classname);
64+
const class_typet &class_type=to_class_type(class_symbol.type);
65+
size_t unwinds=class_type.get_size_t(ID_java_enum_static_unwind);
66+
67+
unwind_max=std::max(unwind_max, unwinds);
68+
if(fname.length()>java_clinit.length())
69+
{
70+
// extend unwindset with unwinds for <clinit> of enum
71+
if(fname.compare(
72+
fname.length()-java_clinit.length(),
73+
java_clinit.length(),
74+
java_clinit)==0 && unwinds>0)
75+
{
76+
const std::string &set=options.get_option("unwindset");
77+
std::string newset;
78+
if(set!="")
79+
newset=",";
80+
newset+=
81+
id2string(ins.function)+"."+std::to_string(loop_id)+":"+
82+
std::to_string(unwinds);
83+
options.set_option("unwindset", set+newset);
84+
}
85+
}
86+
}
87+
}
88+
}
89+
const std::string &vla_length=options.get_option("java-max-vla-length");
90+
if(!vla_length.empty())
91+
{
92+
size_t max_vla_length=safe_string2unsigned(vla_length);
93+
if(max_vla_length<unwind_max)
94+
throw "cannot unwind <clinit> due to insufficient max vla length";
95+
}
96+
}
97+
98+
/*******************************************************************\
99+
100+
Function: remove_static_init_loops
101+
102+
Inputs: symbol table, goto_functions and options
103+
104+
Outputs: side effect is adding <clinit> loops to unwindset
105+
106+
Purpose: this is the entry point for the removal of loops in static
107+
initialization code of Java enums
108+
109+
\*******************************************************************/
110+
111+
void remove_static_init_loops(
112+
const symbol_tablet &symbol_table,
113+
const goto_functionst &goto_functions,
114+
optionst &options)
115+
{
116+
remove_static_init_loopst remove_loops(symbol_table);
117+
remove_loops.unwind_enum_static(goto_functions, options);
118+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*******************************************************************\
2+
3+
Module: Unwind loops in static initializers
4+
5+
Author: Daniel Kroening, [email protected]
6+
7+
\*******************************************************************/
8+
9+
#include <goto-programs/goto_functions.h>
10+
11+
#include <util/options.h>
12+
#include <util/symbol_table.h>
13+
14+
#ifndef CPROVER_GOTO_PROGRAMS_REMOVE_STATIC_INIT_LOOPS_H
15+
#define CPROVER_GOTO_PROGRAMS_REMOVE_STATIC_INIT_LOOPS_H
16+
17+
void remove_static_init_loops(
18+
const symbol_tablet &,
19+
const goto_functionst &,
20+
optionst &);
21+
22+
#endif // CPROVER_GOTO_PROGRAMS_REMOVE_STATIC_INIT_LOOPS_H

src/java_bytecode/java_bytecode_convert_class.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ void java_bytecode_convert_classt::convert(const classt &c)
7979

8080
class_type.set_tag(c.name);
8181
class_type.set(ID_base_name, c.name);
82+
if(c.is_enum)
83+
class_type.set(
84+
ID_java_enum_static_unwind,
85+
std::to_string(c.enum_elements+1));
8286

8387
if(!c.extends.empty())
8488
{

src/java_bytecode/java_bytecode_parse_tree.cpp

+12-7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ void java_bytecode_parse_treet::classt::swap(
3434
{
3535
other.name.swap(name);
3636
other.extends.swap(extends);
37+
other.is_enum=is_enum;
38+
other.enum_elements=enum_elements;
3739
std::swap(other.is_abstract, is_abstract);
3840
other.implements.swap(implements);
3941
other.fields.swap(fields);
@@ -61,10 +63,7 @@ void java_bytecode_parse_treet::output(std::ostream &out) const
6163
for(class_refst::const_iterator it=class_refs.begin();
6264
it!=class_refs.end();
6365
it++)
64-
{
6566
out << " " << *it << '\n';
66-
}
67-
6867
}
6968

7069
/*******************************************************************\
@@ -88,7 +87,8 @@ void java_bytecode_parse_treet::classt::output(std::ostream &out) const
8887
}
8988

9089
out << "class " << name;
91-
if(!extends.empty()) out << " extends " << extends;
90+
if(!extends.empty())
91+
out << " extends " << extends;
9292
out << " {" << '\n';
9393

9494
for(fieldst::const_iterator
@@ -139,7 +139,10 @@ void java_bytecode_parse_treet::annotationt::output(std::ostream &out) const
139139
bool first=true;
140140
for(const auto &element_value_pair : element_value_pairs)
141141
{
142-
if(first) first=false; else out << ", ";
142+
if(first)
143+
first=false;
144+
else
145+
out << ", ";
143146
element_value_pair.output(out);
144147
}
145148

@@ -159,7 +162,8 @@ Function: java_bytecode_parse_treet::annotationt::element_value_pairt::output
159162
160163
\*******************************************************************/
161164

162-
void java_bytecode_parse_treet::annotationt::element_value_pairt::output(std::ostream &out) const
165+
void java_bytecode_parse_treet::annotationt::element_value_pairt::output(
166+
std::ostream &out) const
163167
{
164168
symbol_tablet symbol_table;
165169
namespacet ns(symbol_table);
@@ -233,7 +237,8 @@ void java_bytecode_parse_treet::methodt::output(std::ostream &out) const
233237
for(std::vector<exprt>::const_iterator
234238
a_it=i.args.begin(); a_it!=i.args.end(); a_it++)
235239
{
236-
if(a_it!=i.args.begin()) out << ',';
240+
if(a_it!=i.args.begin())
241+
out << ',';
237242
#if 0
238243
out << ' ' << from_expr(*a_it);
239244
#else

src/java_bytecode/java_bytecode_parse_tree.h

+17-5
Original file line numberDiff line numberDiff line change
@@ -123,15 +123,20 @@ class java_bytecode_parse_treet
123123
class stack_map_table_entryt
124124
{
125125
public:
126-
enum stack_frame_type { SAME, SAME_LOCALS_ONE_STACK, SAME_LOCALS_ONE_STACK_EXTENDED,
127-
CHOP, SAME_EXTENDED, APPEND, FULL};
126+
enum stack_frame_type
127+
{
128+
SAME, SAME_LOCALS_ONE_STACK, SAME_LOCALS_ONE_STACK_EXTENDED,
129+
CHOP, SAME_EXTENDED, APPEND, FULL
130+
};
128131
stack_frame_type type;
129132
size_t offset_delta;
130133
size_t chops;
131134
size_t appends;
132135

133-
typedef std::vector<verification_type_infot> local_verification_type_infot;
134-
typedef std::vector<verification_type_infot> stack_verification_type_infot;
136+
typedef std::vector<verification_type_infot>
137+
local_verification_type_infot;
138+
typedef std::vector<verification_type_infot>
139+
stack_verification_type_infot;
135140

136141
local_verification_type_infot locals;
137142
stack_verification_type_infot stack;
@@ -142,7 +147,10 @@ class java_bytecode_parse_treet
142147

143148
virtual void output(std::ostream &out) const;
144149

145-
inline methodt():is_native(false), is_abstract(false), is_synchronized(false)
150+
inline methodt():
151+
is_native(false),
152+
is_abstract(false),
153+
is_synchronized(false)
146154
{
147155
}
148156
};
@@ -151,13 +159,17 @@ class java_bytecode_parse_treet
151159
{
152160
public:
153161
virtual void output(std::ostream &out) const;
162+
bool is_enum;
154163
};
155164

156165
class classt
157166
{
158167
public:
159168
irep_idt name, extends;
160169
bool is_abstract;
170+
bool is_enum;
171+
size_t enum_elements=0;
172+
161173
typedef std::list<irep_idt> implementst;
162174
implementst implements;
163175

src/java_bytecode/java_bytecode_parser.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ Function: java_bytecode_parsert::rClassFile
264264
#define ACC_ABSTRACT 0x0400
265265
#define ACC_STRICT 0x0800
266266
#define ACC_SYNTHETIC 0x1000
267+
#define ACC_ENUM 0x4000
267268

268269
#ifdef _MSC_VER
269270
#define UNUSED
@@ -295,10 +296,11 @@ void java_bytecode_parsert::rClassFile()
295296

296297
classt &parsed_class=parse_tree.parsed_class;
297298

298-
u2 UNUSED access_flags=read_u2();
299+
u2 access_flags=read_u2();
299300
u2 this_class=read_u2();
300301
u2 super_class=read_u2();
301302

303+
parsed_class.is_enum=(access_flags&ACC_ENUM)!=0;
302304
parsed_class.name=
303305
constant(this_class).type().get(ID_C_base_name);
304306

@@ -310,6 +312,12 @@ void java_bytecode_parsert::rClassFile()
310312
rfields(parsed_class);
311313
rmethods(parsed_class);
312314

315+
// count elements of enum
316+
if(parsed_class.is_enum)
317+
for(fieldt &field : parse_tree.parsed_class.fields)
318+
if(field.is_enum)
319+
parse_tree.parsed_class.enum_elements++;
320+
313321
u2 attributes_count=read_u2();
314322

315323
for(std::size_t j=0; j<attributes_count; j++)
@@ -698,6 +706,7 @@ void java_bytecode_parsert::rfields(classt &parsed_class)
698706
field.name=pool_entry(name_index).s;
699707
field.is_static=(access_flags&ACC_STATIC)!=0;
700708
field.is_final=(access_flags&ACC_FINAL)!=0;
709+
field.is_enum=(access_flags&ACC_ENUM)!=0;
701710
field.signature=id2string(pool_entry(descriptor_index).s);
702711

703712
for(std::size_t j=0; j<attributes_count; j++)

src/util/irep_ids.txt

+1
Original file line numberDiff line numberDiff line change
@@ -736,3 +736,4 @@ bswap
736736
java_bytecode_index
737737
java_instanceof
738738
java_super_method_call
739+
java_enum_static_unwind

0 commit comments

Comments
 (0)