Skip to content

Commit b048787

Browse files
Merge pull request #796 from smowton/smowton/feature/clinit_on_demand
Run clinit methods on demand. Fixes commons-io coverage.
2 parents 75337a2 + c631419 commit b048787

18 files changed

+330
-91
lines changed

regression/cbmc-java/covered1/test.desc

+1-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ covered1.class
1111
.*\"coveredLines\": \"28\",$
1212
.*\"coveredLines\": \"28\",$
1313
.*\"coveredLines\": \"29\",$
14-
.*\"coveredLines\": \"9,18\",$
1514
.*\"coveredLines\": \"18\",$
1615
.*\"coveredLines\": \"18\",$
17-
.*\"coveredLines\": \"18,35\",$
16+
.*\"coveredLines\": \"35\",$
1817
--
1918
^warning: ignoring
314 Bytes
Binary file not shown.
314 Bytes
Binary file not shown.
588 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
public class static_init {
2+
3+
// A should be initialised first, then B will begin init
4+
// after A.x is set.
5+
public static void main() {
6+
assert(A.x == 1 && B.x == 1 && B.y == 2 && A.y == 2);
7+
}
8+
9+
}
10+
11+
class A {
12+
13+
public static int x;
14+
public static int y;
15+
static {
16+
x = 1;
17+
y = B.y;
18+
}
19+
20+
}
21+
22+
class B {
23+
24+
public static int x;
25+
public static int y;
26+
static {
27+
x = A.x;
28+
y = 2;
29+
30+
}
31+
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
static_init.class
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
314 Bytes
Binary file not shown.
314 Bytes
Binary file not shown.
587 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
public class static_init {
2+
3+
// B will begin init first, but then begin A init before it
4+
// has set B.y, leading to the unintuitive result given here.
5+
public static void main() {
6+
assert(B.x == 1 && B.y == 2 && A.x == 1 && A.y == 0);
7+
}
8+
9+
}
10+
11+
class A {
12+
13+
public static int x;
14+
public static int y;
15+
static {
16+
x = 1;
17+
y = B.y;
18+
}
19+
20+
}
21+
22+
class B {
23+
24+
public static int x;
25+
public static int y;
26+
static {
27+
x = A.x;
28+
y = 2;
29+
30+
}
31+
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
static_init.class
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring

src/cegis/cegis-util/type_helper.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ bool instanceof(const typet &lhs, const typet &rhs, const namespacet &ns)
3434
if (type_eq(lhs, rhs, ns)) return true;
3535
assert(ID_class == lhs.id());
3636
const class_typet &lhs_class=to_class_type(lhs);
37-
const irept::subt &bases=lhs_class.bases();
38-
for (const irept &base : bases)
37+
const class_typet::basest &bases=lhs_class.bases();
38+
for(const exprt &base : bases)
3939
{
40-
const typet &type=static_cast<const typet &>(base.find(ID_type));
40+
const typet &type=base.type();
4141
if (instanceof(ns.follow(type), rhs, ns)) return true;
4242
}
4343
return false;

src/java_bytecode/java_bytecode_convert_class.cpp

+1-15
Original file line numberDiff line numberDiff line change
@@ -171,21 +171,7 @@ void java_bytecode_convert_classt::convert(const classt &c)
171171
method_identifier,
172172
method,
173173
symbol_table);
174-
if(lazy_methods_mode==LAZY_METHODS_MODE_EAGER)
175-
{
176-
// Upgrade to a fully-realized symbol now:
177-
java_bytecode_convert_method(
178-
*class_symbol,
179-
method,
180-
symbol_table,
181-
get_message_handler(),
182-
max_array_length);
183-
}
184-
else
185-
{
186-
// Wait for our caller to decide what needs elaborating.
187-
lazy_methods[method_identifier]=std::make_pair(class_symbol, &method);
188-
}
174+
lazy_methods[method_identifier]=std::make_pair(class_symbol, &method);
189175
}
190176

191177
// is this a root class?

src/java_bytecode/java_bytecode_convert_method.cpp

+204-2
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,174 @@ void java_bytecode_convert_methodt::check_static_field_stub(
911911

912912
/*******************************************************************\
913913
914+
Function: java_bytecode_convert_methodt::class_needs_clinit
915+
916+
Inputs: classname: Class name
917+
918+
Outputs: Returns true if the given class or one of its parents
919+
has a static initializer
920+
921+
Purpose: Determine whether a `new` or static access against `classname`
922+
should be prefixed with a static initialization check.
923+
924+
\*******************************************************************/
925+
926+
bool java_bytecode_convert_methodt::class_needs_clinit(
927+
const irep_idt &classname)
928+
{
929+
auto findit_any=any_superclass_has_clinit_method.insert({classname, false});
930+
if(!findit_any.second)
931+
return findit_any.first->second;
932+
933+
auto findit_here=class_has_clinit_method.insert({classname, false});
934+
if(findit_here.second)
935+
{
936+
const irep_idt &clinit_name=id2string(classname)+".<clinit>:()V";
937+
findit_here.first->second=symbol_table.symbols.count(clinit_name);
938+
}
939+
if(findit_here.first->second)
940+
{
941+
findit_any.first->second=true;
942+
return true;
943+
}
944+
auto findit_symbol=symbol_table.symbols.find(classname);
945+
// Stub class?
946+
if(findit_symbol==symbol_table.symbols.end())
947+
{
948+
warning() << "SKIPPED: " << classname << eom;
949+
return false;
950+
}
951+
const symbolt &class_symbol=symbol_table.lookup(classname);
952+
for(const auto &base : to_class_type(class_symbol.type).bases())
953+
{
954+
if(class_needs_clinit(to_symbol_type(base.type()).get_identifier()))
955+
{
956+
findit_any.first->second=true;
957+
return true;
958+
}
959+
}
960+
return false;
961+
}
962+
963+
/*******************************************************************\
964+
965+
Function: java_bytecode_convert_methodt::get_or_create_clinit_wrapper
966+
967+
Inputs: classname: Class name
968+
969+
Outputs: Returns a symbol_exprt pointing to the given class' clinit
970+
wrapper if one is required, or nil otherwise.
971+
972+
Purpose: Create a ::clinit_wrapper the first time a static initializer
973+
might be called. The wrapper method checks whether static init
974+
has already taken place, calls the actual <clinit> method if
975+
not, and initializes super-classes and interfaces.
976+
977+
\*******************************************************************/
978+
979+
exprt java_bytecode_convert_methodt::get_or_create_clinit_wrapper(
980+
const irep_idt &classname)
981+
{
982+
if(!class_needs_clinit(classname))
983+
return static_cast<const exprt &>(get_nil_irep());
984+
985+
const irep_idt &clinit_wrapper_name=
986+
id2string(classname)+"::clinit_wrapper";
987+
auto findit=symbol_table.symbols.find(clinit_wrapper_name);
988+
if(findit!=symbol_table.symbols.end())
989+
return findit->second.symbol_expr();
990+
991+
// Create the wrapper now:
992+
const irep_idt &already_run_name=
993+
id2string(classname)+"::clinit_already_run";
994+
symbolt already_run_symbol;
995+
already_run_symbol.name=already_run_name;
996+
already_run_symbol.pretty_name=already_run_name;
997+
already_run_symbol.base_name="clinit_already_run";
998+
already_run_symbol.type=bool_typet();
999+
already_run_symbol.value=false_exprt();
1000+
already_run_symbol.is_lvalue=true;
1001+
already_run_symbol.is_state_var=true;
1002+
already_run_symbol.is_static_lifetime=true;
1003+
already_run_symbol.mode=ID_java;
1004+
symbol_table.add(already_run_symbol);
1005+
1006+
equal_exprt check_already_run(
1007+
already_run_symbol.symbol_expr(),
1008+
false_exprt());
1009+
1010+
code_ifthenelset wrapper_body;
1011+
wrapper_body.cond()=check_already_run;
1012+
code_blockt init_body;
1013+
// Note already-run is set *before* calling clinit, in order to prevent
1014+
// recursion in clinit methods.
1015+
code_assignt set_already_run(already_run_symbol.symbol_expr(), true_exprt());
1016+
init_body.move_to_operands(set_already_run);
1017+
const irep_idt &real_clinit_name=id2string(classname)+".<clinit>:()V";
1018+
const symbolt &class_symbol=symbol_table.lookup(classname);
1019+
1020+
auto findsymit=symbol_table.symbols.find(real_clinit_name);
1021+
if(findsymit!=symbol_table.symbols.end())
1022+
{
1023+
code_function_callt call_real_init;
1024+
call_real_init.function()=findsymit->second.symbol_expr();
1025+
init_body.move_to_operands(call_real_init);
1026+
}
1027+
1028+
for(const auto &base : to_class_type(class_symbol.type).bases())
1029+
{
1030+
const auto base_name=to_symbol_type(base.type()).get_identifier();
1031+
exprt base_init_routine=get_or_create_clinit_wrapper(base_name);
1032+
if(base_init_routine.is_nil())
1033+
continue;
1034+
code_function_callt call_base;
1035+
call_base.function()=base_init_routine;
1036+
init_body.move_to_operands(call_base);
1037+
}
1038+
1039+
wrapper_body.then_case()=init_body;
1040+
1041+
symbolt wrapper_method_symbol;
1042+
code_typet wrapper_method_type;
1043+
wrapper_method_type.return_type()=void_typet();
1044+
wrapper_method_symbol.name=clinit_wrapper_name;
1045+
wrapper_method_symbol.pretty_name=clinit_wrapper_name;
1046+
wrapper_method_symbol.base_name="clinit_wrapper";
1047+
wrapper_method_symbol.type=wrapper_method_type;
1048+
wrapper_method_symbol.value=wrapper_body;
1049+
wrapper_method_symbol.mode=ID_java;
1050+
symbol_table.add(wrapper_method_symbol);
1051+
return wrapper_method_symbol.symbol_expr();
1052+
}
1053+
1054+
/*******************************************************************\
1055+
1056+
Function: java_bytecode_convert_methodt::get_clinit_call
1057+
1058+
Inputs: classname: Class name
1059+
1060+
Outputs: Returns a function call to the given class' static initializer
1061+
wrapper if one is needed, or a skip instruction otherwise.
1062+
1063+
Purpose: Each static access to classname should be prefixed with a check
1064+
for necessary static init; this returns a call implementing
1065+
that check.
1066+
1067+
\*******************************************************************/
1068+
1069+
codet java_bytecode_convert_methodt::get_clinit_call(
1070+
const irep_idt &classname)
1071+
{
1072+
exprt callee=get_or_create_clinit_wrapper(classname);
1073+
if(callee.is_nil())
1074+
return code_skipt();
1075+
code_function_callt ret;
1076+
ret.function()=callee;
1077+
return ret;
1078+
}
1079+
1080+
/*******************************************************************\
1081+
9141082
Function: java_bytecode_convert_methodt::convert_instructions
9151083
9161084
Inputs:
@@ -1378,6 +1546,18 @@ codet java_bytecode_convert_methodt::convert_instructions(
13781546

13791547
call.function().add_source_location()=loc;
13801548
c=call;
1549+
1550+
if(!use_this)
1551+
{
1552+
codet clinit_call=get_clinit_call(arg0.get(ID_C_class));
1553+
if(clinit_call.get_statement()!=ID_skip)
1554+
{
1555+
code_blockt ret_block;
1556+
ret_block.move_to_operands(clinit_call);
1557+
ret_block.move_to_operands(c);
1558+
c=std::move(ret_block);
1559+
}
1560+
}
13811561
}
13821562
else if(statement=="return")
13831563
{
@@ -1916,9 +2096,14 @@ codet java_bytecode_convert_methodt::convert_instructions(
19162096
}
19172097
results[0]=java_bytecode_promotion(symbol_expr);
19182098

1919-
// set $assertionDisabled to false
1920-
if(field_name.find("$assertionsDisabled")!=std::string::npos)
2099+
codet clinit_call=get_clinit_call(arg0.get_string(ID_class));
2100+
if(clinit_call.get_statement()!=ID_skip)
2101+
c=clinit_call;
2102+
else if(field_name.find("$assertionsDisabled")!=std::string::npos)
2103+
{
2104+
// set $assertionDisabled to false
19212105
c=code_assignt(symbol_expr, false_exprt());
2106+
}
19222107
}
19232108
else if(statement=="putfield")
19242109
{
@@ -1937,6 +2122,14 @@ codet java_bytecode_convert_methodt::convert_instructions(
19372122
to_symbol_type(arg0.type()).get_identifier());
19382123
}
19392124
c=code_assignt(symbol_expr, op[0]);
2125+
codet clinit_call=get_clinit_call(arg0.get_string(ID_class));
2126+
if(clinit_call.get_statement()!=ID_skip)
2127+
{
2128+
code_blockt ret_block;
2129+
ret_block.move_to_operands(clinit_call);
2130+
ret_block.move_to_operands(c);
2131+
c=std::move(ret_block);
2132+
}
19402133
}
19412134
else if(statement==patternt("?2?")) // i2c etc.
19422135
{
@@ -1955,6 +2148,15 @@ codet java_bytecode_convert_methodt::convert_instructions(
19552148

19562149
const exprt tmp=tmp_variable("new", ref_type);
19572150
c=code_assignt(tmp, java_new_expr);
2151+
codet clinit_call=
2152+
get_clinit_call(to_symbol_type(arg0.type()).get_identifier());
2153+
if(clinit_call.get_statement()!=ID_skip)
2154+
{
2155+
code_blockt ret_block;
2156+
ret_block.move_to_operands(clinit_call);
2157+
ret_block.move_to_operands(c);
2158+
c=std::move(ret_block);
2159+
}
19582160
results[0]=tmp;
19592161
}
19602162
else if(statement=="newarray" ||

src/java_bytecode/java_bytecode_convert_method_class.h

+6
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ class java_bytecode_convert_methodt:public messaget
9494
expanding_vectort<variablest> variables;
9595
std::set<symbol_exprt> used_local_names;
9696
bool method_has_this;
97+
std::map<irep_idt, bool> class_has_clinit_method;
98+
std::map<irep_idt, bool> any_superclass_has_clinit_method;
9799

98100
typedef enum instruction_sizet
99101
{
@@ -221,6 +223,10 @@ class java_bytecode_convert_methodt:public messaget
221223
void check_static_field_stub(
222224
const symbol_exprt &se,
223225
const irep_idt &basename);
226+
227+
bool class_needs_clinit(const irep_idt &classname);
228+
exprt get_or_create_clinit_wrapper(const irep_idt &classname);
229+
codet get_clinit_call(const irep_idt &classname);
224230
};
225231

226232
#endif

0 commit comments

Comments
 (0)