1717#include < util/arith_tools.h>
1818
1919// / The three states in which a `<clinit>` method for a class can be before,
20- // / after, and during static class initialization.
20+ // / after, and during static class initialization. These states are only used
21+ // / when the thread safe version of the clinit wrapper is generated.
2122// /
2223// / According to the JVM Spec document (section 5.5), the JVM needs to
2324// / maintain, for every class initializer, a state indicating whether the
3334// / class is ready to be used, or it has errored"
3435// /
3536// / The last state corresponds to a fusion of the two original states "ready
36- // / for use" and "initialization error".
37+ // / for use" and "initialization error". The basis for fusing these states is
38+ // / that for simplification reasons both implementations of the clinit wrapper
39+ // / do not handle exceptions, hence the error state is not possible.
3740enum class clinit_statest
3841{
3942 NOT_INIT,
@@ -98,6 +101,15 @@ static symbolt add_new_symbol(
98101 return new_symbol;
99102}
100103
104+ // / Get name of the static-initialization-already-done global variable for a
105+ // / given class.
106+ // / \param class_name: class symbol name
107+ // / \return static initializer wrapper-already run global name
108+ static irep_idt clinit_already_run_variable_name (const irep_idt &class_name)
109+ {
110+ return id2string (class_name) + " ::clinit_already_run" ;
111+ }
112+
101113// / Get name of the real static initializer for a given class. Doesn't check
102114// / if a static initializer actually exists.
103115// / \param class_name: class symbol name
@@ -165,6 +177,40 @@ gen_clinit_eqexpr(const exprt &expr, const clinit_statest state)
165177 return equal_exprt (expr, init_s);
166178}
167179
180+ // / Generates codet that iterates through the base types of the class specified
181+ // / by class_name, C, and recursively adds calls to their clinit wrapper.
182+ // / Finally a call to the clinint wrapper of C is made.
183+ // / \param symbol_table: symbol table
184+ // / \param class_name: name of the class to generate clinit wrapper calls for
185+ // / \param [out] init_body: appended with calls to clinit wrapper
186+ static void clinit_wrapper_do_recursive_calls (
187+ const symbol_tablet &symbol_table,
188+ const irep_idt &class_name,
189+ code_blockt &init_body)
190+ {
191+ const symbolt &class_symbol = symbol_table.lookup_ref (class_name);
192+ for (const auto &base : to_class_type (class_symbol.type ).bases ())
193+ {
194+ const auto base_name = to_symbol_type (base.type ()).get_identifier ();
195+ irep_idt base_init_routine = clinit_wrapper_name (base_name);
196+ auto findit = symbol_table.symbols .find (base_init_routine);
197+ if (findit == symbol_table.symbols .end ())
198+ continue ;
199+ code_function_callt call_base;
200+ call_base.function () = findit->second .symbol_expr ();
201+ init_body.move_to_operands (call_base);
202+ }
203+
204+ const irep_idt &real_clinit_name = clinit_function_name (class_name);
205+ auto find_sym_it = symbol_table.symbols .find (real_clinit_name);
206+ if (find_sym_it != symbol_table.symbols .end ())
207+ {
208+ code_function_callt call_real_init;
209+ call_real_init.function () = find_sym_it->second .symbol_expr ();
210+ init_body.move_to_operands (call_real_init);
211+ }
212+ }
213+
168214// / Checks whether a static initializer wrapper is needed for a given class,
169215// / i.e. if the given class or any superclass has a static initializer.
170216// / \param class_name: class symbol name
@@ -199,32 +245,51 @@ static bool needs_clinit_wrapper(
199245// / \param synthetic_methods: synthetic method type map. The new clinit wrapper
200246// / symbol will be recorded, such that we get a callback to produce its body
201247// / if and when required.
248+ // / \param thread_safe: if true state variables required to make the
249+ // / clinit_wrapper thread safe will be created.
202250static void create_clinit_wrapper_symbols (
203251 const irep_idt &class_name,
204252 symbol_tablet &symbol_table,
205- synthetic_methods_mapt &synthetic_methods)
253+ synthetic_methods_mapt &synthetic_methods,
254+ const bool thread_safe)
206255{
207- exprt not_init_value = from_integer (
208- static_cast <int >(clinit_statest::NOT_INIT), clinit_states_type);
256+ if (thread_safe)
257+ {
258+ exprt not_init_value = from_integer (
259+ static_cast <int >(clinit_statest::NOT_INIT), clinit_states_type);
209260
210- // Create two global static synthetic "fields" for the class "id"
211- // these two variables hold the state of the class initialization algorithm
212- // across calls to the clinit_wrapper
213- add_new_symbol (
214- symbol_table,
215- clinit_state_var_name (class_name),
216- clinit_states_type,
217- not_init_value,
218- false ,
219- true );
261+ // Create two global static synthetic "fields" for the class "id"
262+ // these two variables hold the state of the class initialization algorithm
263+ // across calls to the clinit_wrapper
264+ add_new_symbol (
265+ symbol_table,
266+ clinit_state_var_name (class_name),
267+ clinit_states_type,
268+ not_init_value,
269+ false ,
270+ true );
220271
221- add_new_symbol (
222- symbol_table,
223- clinit_thread_local_state_var_name (class_name),
224- clinit_states_type,
225- not_init_value,
226- true ,
227- true );
272+ add_new_symbol (
273+ symbol_table,
274+ clinit_thread_local_state_var_name (class_name),
275+ clinit_states_type,
276+ not_init_value,
277+ true ,
278+ true );
279+ }
280+ else
281+ {
282+ const irep_idt &already_run_name =
283+ clinit_already_run_variable_name (class_name);
284+
285+ add_new_symbol (
286+ symbol_table,
287+ already_run_name,
288+ bool_typet (),
289+ false_exprt (),
290+ false ,
291+ true );
292+ }
228293
229294 // Create symbol for the "clinit_wrapper"
230295 symbolt wrapper_method_symbol;
@@ -254,6 +319,8 @@ static void create_clinit_wrapper_symbols(
254319 " clinit wrapper" );
255320}
256321
322+ // / Thread safe version of the static initialiser.
323+ // /
257324// / Produces the static initialiser wrapper body for the given function. This
258325// / static initializer implements (a simplification of) the algorithm defined
259326// / in Section 5.5 of the JVM Specs. This function, or wrapper, checks whether
@@ -319,7 +386,7 @@ static void create_clinit_wrapper_symbols(
319386// / name created by `create_clinit_wrapper_symbols`)
320387// / \param symbol_table: global symbol table
321388// / \return the body of the static initialiser wrapper
322- codet get_clinit_wrapper_body (
389+ codet get_thread_safe_clinit_wrapper_body (
323390 const irep_idt &function_id,
324391 symbol_table_baset &symbol_table)
325392{
@@ -470,34 +537,11 @@ codet get_clinit_wrapper_body(
470537 //
471538 // java::C::<clinit>();
472539 //
473- code_blockt init_body;
474540 {
475- // iterate through the base types and add recursive calls to the
476- // clinit_wrapper()
477- const symbolt &class_symbol = symbol_table.lookup_ref (class_name);
478- for (const auto &base : to_class_type (class_symbol.type ).bases ())
479- {
480- const auto base_name = to_symbol_type (base.type ()).get_identifier ();
481- irep_idt base_init_routine = clinit_wrapper_name (base_name);
482- auto findit = symbol_table.symbols .find (base_init_routine);
483- if (findit == symbol_table.symbols .end ())
484- continue ;
485- code_function_callt call_base;
486- call_base.function () = findit->second .symbol_expr ();
487- init_body.move_to_operands (call_base);
488- }
489-
490- // call java::C::<clinit>(), if the class has one static initializer
491- const irep_idt &real_clinit_name = clinit_function_name (class_name);
492- auto find_sym_it = symbol_table.symbols .find (real_clinit_name);
493- if (find_sym_it != symbol_table.symbols .end ())
494- {
495- code_function_callt call_real_init;
496- call_real_init.function () = find_sym_it->second .symbol_expr ();
497- init_body.move_to_operands (call_real_init);
498- }
541+ code_blockt init_body;
542+ clinit_wrapper_do_recursive_calls (symbol_table, class_name, init_body);
543+ function_body.append (init_body);
499544 }
500- function_body.append (init_body);
501545
502546 // ATOMIC_START
503547 // C::__CPROVER_PREFIX_clinit_state = INIT_COMPLETE;
@@ -516,14 +560,77 @@ codet get_clinit_wrapper_body(
516560 return function_body;
517561}
518562
563+ // / Produces the static initialiser wrapper body for the given function.
564+ // / Note: this version of the clinit wrapper is not thread safe.
565+ // / \param function_id: clinit wrapper function id (the wrapper_method_symbol
566+ // / name created by `create_clinit_wrapper_symbols`)
567+ // / \param symbol_table: global symbol table
568+ // / \return the body of the static initialiser wrapper/
569+ codet get_clinit_wrapper_body (
570+ const irep_idt &function_id,
571+ symbol_table_baset &symbol_table)
572+ {
573+ // Assume that class C extends class C' and implements interfaces
574+ // I1, ..., In. We now create the following function (possibly recursively
575+ // creating the clinit_wrapper functions for C' and I1, ..., In):
576+ //
577+ // java::C::clinit_wrapper()
578+ // {
579+ // if (java::C::clinit_already_run == false)
580+ // {
581+ // java::C::clinit_already_run = true; // before recursive calls!
582+ //
583+ // java::C'::clinit_wrapper();
584+ // java::I1::clinit_wrapper();
585+ // java::I2::clinit_wrapper();
586+ // // ...
587+ // java::In::clinit_wrapper();
588+ //
589+ // java::C::<clinit>();
590+ // }
591+ // }
592+ const symbolt &wrapper_method_symbol = symbol_table.lookup_ref (function_id);
593+ irep_idt class_name = wrapper_method_symbol.type .get (ID_C_class);
594+ INVARIANT (
595+ !class_name.empty (), " wrapper function should be annotated with its class" );
596+
597+ const symbolt &already_run_symbol =
598+ symbol_table.lookup_ref (clinit_already_run_variable_name (class_name));
599+
600+ equal_exprt check_already_run (
601+ already_run_symbol.symbol_expr (),
602+ false_exprt ());
603+
604+ // the entire body of the function is an if-then-else
605+ code_ifthenelset wrapper_body;
606+
607+ // add the condition to the if
608+ wrapper_body.cond () = check_already_run;
609+
610+ // add the "already-run = false" statement
611+ code_blockt init_body;
612+ code_assignt set_already_run (already_run_symbol.symbol_expr (), true_exprt ());
613+ init_body.move_to_operands (set_already_run);
614+
615+ clinit_wrapper_do_recursive_calls (symbol_table, class_name, init_body);
616+
617+ wrapper_body.then_case () = init_body;
618+
619+ return wrapper_body;
620+ }
621+
622+
519623// / Create static initializer wrappers for all classes that need them.
520624// / \param symbol_table: global symbol table
521625// / \param synthetic_methods: synthetic methods map. Will be extended noting
522626// / that any wrapper belongs to this code, and so `get_clinit_wrapper_body`
523627// / should be used to produce the method body when required.
628+ // / \param thread_safe: if true state variables required to make the
629+ // / clinit_wrapper thread safe will be created.
524630void create_static_initializer_wrappers (
525631 symbol_tablet &symbol_table,
526- synthetic_methods_mapt &synthetic_methods)
632+ synthetic_methods_mapt &synthetic_methods,
633+ const bool thread_safe)
527634{
528635 // Top-sort the class hierarchy, such that we visit parents before children,
529636 // and can so identify parents that need static initialisation by whether we
@@ -539,7 +646,7 @@ void create_static_initializer_wrappers(
539646 if (needs_clinit_wrapper (class_identifier, symbol_table))
540647 {
541648 create_clinit_wrapper_symbols (
542- class_identifier, symbol_table, synthetic_methods);
649+ class_identifier, symbol_table, synthetic_methods, thread_safe );
543650 }
544651 }
545652}
0 commit comments