17
17
#include < util/arith_tools.h>
18
18
19
19
// / 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.
21
22
// /
22
23
// / According to the JVM Spec document (section 5.5), the JVM needs to
23
24
// / maintain, for every class initializer, a state indicating whether the
33
34
// / class is ready to be used, or it has errored"
34
35
// /
35
36
// / 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.
37
40
enum class clinit_statest
38
41
{
39
42
NOT_INIT,
@@ -98,6 +101,15 @@ static symbolt add_new_symbol(
98
101
return new_symbol;
99
102
}
100
103
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
+
101
113
// / Get name of the real static initializer for a given class. Doesn't check
102
114
// / if a static initializer actually exists.
103
115
// / \param class_name: class symbol name
@@ -165,6 +177,40 @@ gen_clinit_eqexpr(const exprt &expr, const clinit_statest state)
165
177
return equal_exprt (expr, init_s);
166
178
}
167
179
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
+
168
214
// / Checks whether a static initializer wrapper is needed for a given class,
169
215
// / i.e. if the given class or any superclass has a static initializer.
170
216
// / \param class_name: class symbol name
@@ -199,32 +245,51 @@ static bool needs_clinit_wrapper(
199
245
// / \param synthetic_methods: synthetic method type map. The new clinit wrapper
200
246
// / symbol will be recorded, such that we get a callback to produce its body
201
247
// / if and when required.
248
+ // / \param thread_safe: if true state variables required to make the
249
+ // / clinit_wrapper thread safe will be created.
202
250
static void create_clinit_wrapper_symbols (
203
251
const irep_idt &class_name,
204
252
symbol_tablet &symbol_table,
205
- synthetic_methods_mapt &synthetic_methods)
253
+ synthetic_methods_mapt &synthetic_methods,
254
+ const bool thread_safe)
206
255
{
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);
209
260
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 );
220
271
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
+ }
228
293
229
294
// Create symbol for the "clinit_wrapper"
230
295
symbolt wrapper_method_symbol;
@@ -254,6 +319,8 @@ static void create_clinit_wrapper_symbols(
254
319
" clinit wrapper" );
255
320
}
256
321
322
+ // / Thread safe version of the static initialiser.
323
+ // /
257
324
// / Produces the static initialiser wrapper body for the given function. This
258
325
// / static initializer implements (a simplification of) the algorithm defined
259
326
// / in Section 5.5 of the JVM Specs. This function, or wrapper, checks whether
@@ -319,7 +386,7 @@ static void create_clinit_wrapper_symbols(
319
386
// / name created by `create_clinit_wrapper_symbols`)
320
387
// / \param symbol_table: global symbol table
321
388
// / \return the body of the static initialiser wrapper
322
- codet get_clinit_wrapper_body (
389
+ codet get_thread_safe_clinit_wrapper_body (
323
390
const irep_idt &function_id,
324
391
symbol_table_baset &symbol_table)
325
392
{
@@ -470,34 +537,11 @@ codet get_clinit_wrapper_body(
470
537
//
471
538
// java::C::<clinit>();
472
539
//
473
- code_blockt init_body;
474
540
{
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);
499
544
}
500
- function_body.append (init_body);
501
545
502
546
// ATOMIC_START
503
547
// C::__CPROVER_PREFIX_clinit_state = INIT_COMPLETE;
@@ -516,14 +560,77 @@ codet get_clinit_wrapper_body(
516
560
return function_body;
517
561
}
518
562
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
+
519
623
// / Create static initializer wrappers for all classes that need them.
520
624
// / \param symbol_table: global symbol table
521
625
// / \param synthetic_methods: synthetic methods map. Will be extended noting
522
626
// / that any wrapper belongs to this code, and so `get_clinit_wrapper_body`
523
627
// / 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.
524
630
void create_static_initializer_wrappers (
525
631
symbol_tablet &symbol_table,
526
- synthetic_methods_mapt &synthetic_methods)
632
+ synthetic_methods_mapt &synthetic_methods,
633
+ const bool thread_safe)
527
634
{
528
635
// Top-sort the class hierarchy, such that we visit parents before children,
529
636
// and can so identify parents that need static initialisation by whether we
@@ -539,7 +646,7 @@ void create_static_initializer_wrappers(
539
646
if (needs_clinit_wrapper (class_identifier, symbol_table))
540
647
{
541
648
create_clinit_wrapper_symbols (
542
- class_identifier, symbol_table, synthetic_methods);
649
+ class_identifier, symbol_table, synthetic_methods, thread_safe );
543
650
}
544
651
}
545
652
}
0 commit comments