Skip to content

Commit 22afc5c

Browse files
cesaroDegiorgio
authored andcommitted
Fixes wrong invocation order for static initializers
The Java Virtual Machine Specification, section 5.5, p. 359 defines that the static initializers of both the parent class or interfaces of a given class must be invoked **before** the class initializer of the class. They were previously being invoked in the opposite order. Two Regression tests added to enforce this behaviour.
1 parent 5c3997d commit 22afc5c

File tree

8 files changed

+106
-13
lines changed

8 files changed

+106
-13
lines changed
276 Bytes
Binary file not shown.
261 Bytes
Binary file not shown.
272 Bytes
Binary file not shown.
Binary file not shown.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
public class static_init_order
2+
{
3+
// as per the Java Virtual Machine Specification,
4+
// section 5.5, p. 35 we expect the static initializer of
5+
// the parent class to be called before the static initializer
6+
// of the class in question.
7+
//
8+
// The following tests will verify the aforementioned behaviour.
9+
10+
public static void test1()
11+
{
12+
C object2 = new C();
13+
B object = new B();
14+
if(object2.x != 20)
15+
// order of init is: C.clint, B.clint, A.clint
16+
// This should not be reachable as expected order
17+
// should be: C.clint, A.clint, B.clint.
18+
assert(false);
19+
}
20+
21+
public static void test2()
22+
{
23+
C object2 = new C();
24+
B object = new B();
25+
// Assertion is expected to fail,
26+
// as the only way for object2.x
27+
// to be assigned a value of 10 is through
28+
// the following incorrect ordering of init calls:
29+
// C.clint, B.clint, A.clint
30+
assert(object2.x == 10);
31+
}
32+
}
33+
34+
class C
35+
{
36+
public static int x = 0;
37+
}
38+
39+
class A
40+
{
41+
static {
42+
C.x=10;
43+
}
44+
}
45+
46+
class B extends A
47+
{
48+
static {
49+
C.x=20;
50+
}
51+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
static_init_order.class
3+
--function static_init_order.test1 --trace
4+
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
^VERIFICATION SUCCESSFUL$
8+
--
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
static_init_order.class
3+
--function static_init_order.test2
4+
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
^VERIFICATION FAILED$
8+
--

src/java_bytecode/java_bytecode_convert_method.cpp

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -984,13 +984,33 @@ exprt java_bytecode_convert_methodt::get_or_create_clinit_wrapper(
984984
if(!class_needs_clinit(classname))
985985
return static_cast<const exprt &>(get_nil_irep());
986986

987+
// if the symbol table already contains the clinit_wrapper() function, return
988+
// it
987989
const irep_idt &clinit_wrapper_name=
988990
id2string(classname)+"::clinit_wrapper";
989991
auto findit=symbol_table.symbols.find(clinit_wrapper_name);
990992
if(findit!=symbol_table.symbols.end())
991993
return findit->second.symbol_expr();
992994

993-
// Create the wrapper now:
995+
// Otherwise, assume that class C extends class C' and implements interfaces
996+
// I1, ..., In. We now create the following function (possibly recursively
997+
// creating the clinit_wrapper functions for C' and I1, ..., In):
998+
//
999+
// java::C::clinit_wrapper()
1000+
// {
1001+
// if (java::C::clinit_already_run == false)
1002+
// {
1003+
// java::C::clinit_already_run = true; // before recursive calls!
1004+
//
1005+
// java::C'::clinit_wrapper();
1006+
// java::I1::clinit_wrapper();
1007+
// java::I2::clinit_wrapper();
1008+
// // ...
1009+
// java::In::clinit_wrapper();
1010+
//
1011+
// java::C::<clinit>();
1012+
// }
1013+
// }
9941014
const irep_idt &already_run_name=
9951015
id2string(classname)+"::clinit_already_run";
9961016
symbolt already_run_symbol;
@@ -1009,24 +1029,20 @@ exprt java_bytecode_convert_methodt::get_or_create_clinit_wrapper(
10091029
already_run_symbol.symbol_expr(),
10101030
false_exprt());
10111031

1032+
// the entire body of the function is an if-then-else
10121033
code_ifthenelset wrapper_body;
1034+
1035+
// add the condition to the if
10131036
wrapper_body.cond()=check_already_run;
1037+
1038+
// add the "already-run = false" statement
10141039
code_blockt init_body;
1015-
// Note already-run is set *before* calling clinit, in order to prevent
1016-
// recursion in clinit methods.
10171040
code_assignt set_already_run(already_run_symbol.symbol_expr(), true_exprt());
10181041
init_body.move_to_operands(set_already_run);
1019-
const irep_idt &real_clinit_name=id2string(classname)+".<clinit>:()V";
1020-
const symbolt &class_symbol=*symbol_table.lookup(classname);
1021-
1022-
auto findsymit=symbol_table.symbols.find(real_clinit_name);
1023-
if(findsymit!=symbol_table.symbols.end())
1024-
{
1025-
code_function_callt call_real_init;
1026-
call_real_init.function()=findsymit->second.symbol_expr();
1027-
init_body.move_to_operands(call_real_init);
1028-
}
10291042

1043+
// iterate through the base types and add recursive calls to the
1044+
// clinit_wrapper()
1045+
const symbolt &class_symbol=*symbol_table.lookup(classname);
10301046
for(const auto &base : to_class_type(class_symbol.type).bases())
10311047
{
10321048
const auto base_name=to_symbol_type(base.type()).get_identifier();
@@ -1038,8 +1054,18 @@ exprt java_bytecode_convert_methodt::get_or_create_clinit_wrapper(
10381054
init_body.move_to_operands(call_base);
10391055
}
10401056

1057+
// call java::C::<clinit>(), if the class has one static initializer
1058+
const irep_idt &real_clinit_name=id2string(classname)+".<clinit>:()V";
1059+
auto find_sym_it=symbol_table.symbols.find(real_clinit_name);
1060+
if(find_sym_it!=symbol_table.symbols.end())
1061+
{
1062+
code_function_callt call_real_init;
1063+
call_real_init.function()=find_sym_it->second.symbol_expr();
1064+
init_body.move_to_operands(call_real_init);
1065+
}
10411066
wrapper_body.then_case()=init_body;
10421067

1068+
// insert symbol in the symbol table
10431069
symbolt wrapper_method_symbol;
10441070
code_typet wrapper_method_type;
10451071
wrapper_method_type.return_type()=void_typet();

0 commit comments

Comments
 (0)