Skip to content

Commit fd6e195

Browse files
author
Thomas Kiley
authored
Merge pull request diffblue#1718 from cesaro/concurrency-team-small-fixes
Adding concurrency to Java
2 parents e6fe617 + 22afc5c commit fd6e195

File tree

12 files changed

+182
-52
lines changed

12 files changed

+182
-52
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ src/ansi-c/library/converter
117117
src/ansi-c/library/converter.exe
118118
src/util/irep_ids_convert
119119
src/util/irep_ids_convert.exe
120+
build/
120121

121122
*.pyc
122123

276 Bytes
Binary file not shown.
261 Bytes
Binary file not shown.
272 Bytes
Binary file not shown.
Binary file not shown.
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+
}
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+
--
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/ansi-c/expr2c.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -2968,6 +2968,12 @@ std::string expr2ct::convert_code(
29682968
if(statement=="unlock")
29692969
return convert_code_unlock(src, indent);
29702970

2971+
if(statement==ID_atomic_begin)
2972+
return indent_str(indent)+"__CPROVER_atomic_begin();";
2973+
2974+
if(statement==ID_atomic_end)
2975+
return indent_str(indent)+"__CPROVER_atomic_end();";
2976+
29712977
if(statement==ID_function_call)
29722978
return convert_code_function_call(to_code_function_call(src), indent);
29732979

src/goto-programs/goto_convert.cpp

+64-39
Original file line numberDiff line numberDiff line change
@@ -344,23 +344,33 @@ void goto_convertt::convert_label(
344344

345345
goto_programt tmp;
346346

347-
// magic thread creation label?
347+
// magic thread creation label.
348+
// The "__CPROVER_ASYNC_" signals the start of a sequence of instructions
349+
// that can be executed in parallel, i.e, a new thread.
348350
if(has_prefix(id2string(label), "__CPROVER_ASYNC_"))
349351
{
350-
// this is like a START_THREAD
351-
codet tmp_code(ID_start_thread);
352-
tmp_code.copy_to_operands(code.op0());
353-
tmp_code.add_source_location()=code.source_location();
354-
convert(tmp_code, tmp);
352+
// the body of the thread is expected to be
353+
// in the operand.
354+
INVARIANT(code.op0().is_not_nil(),
355+
"op0 in magic thread creation label is null");
356+
357+
// replace the magic thread creation label with a
358+
// thread block (START_THREAD...END_THREAD).
359+
code_blockt thread_body;
360+
thread_body.add(to_code(code.op0()));
361+
thread_body.add_source_location()=code.source_location();
362+
generate_thread_block(thread_body, dest);
355363
}
356364
else
365+
{
357366
convert(to_code(code.op0()), tmp);
358367

359-
goto_programt::targett target=tmp.instructions.begin();
360-
dest.destructive_append(tmp);
368+
goto_programt::targett target=tmp.instructions.begin();
369+
dest.destructive_append(tmp);
361370

362-
targets.labels.insert({label, {target, targets.destructor_stack}});
363-
target->labels.push_front(label);
371+
targets.labels.insert({label, {target, targets.destructor_stack}});
372+
target->labels.push_front(label);
373+
}
364374
}
365375

366376
void goto_convertt::convert_gcc_local_label(
@@ -1539,39 +1549,14 @@ void goto_convertt::convert_start_thread(
15391549
const codet &code,
15401550
goto_programt &dest)
15411551
{
1542-
if(code.operands().size()!=1)
1543-
{
1544-
error().source_location=code.find_source_location();
1545-
error() << "start_thread expects one operand" << eom;
1546-
throw 0;
1547-
}
1548-
15491552
goto_programt::targett start_thread=
15501553
dest.add_instruction(START_THREAD);
1551-
15521554
start_thread->source_location=code.source_location();
1555+
start_thread->code=code;
15531556

1554-
{
1555-
// start_thread label;
1556-
// goto tmp;
1557-
// label: op0-code
1558-
// end_thread
1559-
// tmp: skip
1560-
1561-
goto_programt::targett goto_instruction=dest.add_instruction(GOTO);
1562-
goto_instruction->guard=true_exprt();
1563-
goto_instruction->source_location=code.source_location();
1564-
1565-
goto_programt tmp;
1566-
convert(to_code(code.op0()), tmp);
1567-
goto_programt::targett end_thread=tmp.add_instruction(END_THREAD);
1568-
end_thread->source_location=code.source_location();
1569-
1570-
start_thread->targets.push_back(tmp.instructions.begin());
1571-
dest.destructive_append(tmp);
1572-
goto_instruction->targets.push_back(dest.add_instruction(SKIP));
1573-
dest.instructions.back().source_location=code.source_location();
1574-
}
1557+
// remember it to do target later
1558+
targets.gotos.push_back(
1559+
std::make_pair(start_thread, targets.destructor_stack));
15751560
}
15761561

15771562
void goto_convertt::convert_end_thread(
@@ -2242,3 +2227,43 @@ void goto_convert(
22422227

22432228
::goto_convert(to_code(symbol.value), symbol_table, dest, message_handler);
22442229
}
2230+
2231+
/// Generates the necessary goto-instructions to represent a thread-block.
2232+
/// Specifically, the following instructions are generated:
2233+
///
2234+
/// A: START_THREAD : C
2235+
/// B: GOTO Z
2236+
/// C: SKIP
2237+
/// D: {THREAD BODY}
2238+
/// E: END_THREAD
2239+
/// Z: SKIP
2240+
///
2241+
/// \param thread_body: sequence of codet's that can be executed
2242+
/// in parallel.
2243+
/// \param [out] dest: resulting goto-instructions.
2244+
void goto_convertt::generate_thread_block(
2245+
const code_blockt &thread_body,
2246+
goto_programt &dest)
2247+
{
2248+
goto_programt preamble, body, postamble;
2249+
2250+
goto_programt::targett c=body.add_instruction();
2251+
c->make_skip();
2252+
convert(thread_body, body);
2253+
2254+
goto_programt::targett e=postamble.add_instruction(END_THREAD);
2255+
e->source_location=thread_body.source_location();
2256+
goto_programt::targett z=postamble.add_instruction();
2257+
z->make_skip();
2258+
2259+
goto_programt::targett a=preamble.add_instruction(START_THREAD);
2260+
a->source_location=thread_body.source_location();
2261+
a->targets.push_back(c);
2262+
goto_programt::targett b=preamble.add_instruction();
2263+
b->source_location=thread_body.source_location();
2264+
b->make_goto(z);
2265+
2266+
dest.destructive_append(preamble);
2267+
dest.destructive_append(body);
2268+
dest.destructive_append(postamble);
2269+
}

src/goto-programs/goto_convert_class.h

+5
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,11 @@ class goto_convertt:public messaget
502502
const irep_idt &id,
503503
std::list<exprt> &dest);
504504

505+
// START_THREAD; ... END_THREAD;
506+
void generate_thread_block(
507+
const code_blockt &thread_body,
508+
goto_programt &dest);
509+
505510
//
506511
// misc
507512
//

src/java_bytecode/java_bytecode_convert_method.cpp

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

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

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

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

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

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

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

0 commit comments

Comments
 (0)