@@ -223,7 +223,8 @@ static void infer_opaque_type_fields(
223223 namespacet ns (symbol_table);
224224 for (const auto &method : parse_tree.parsed_class .methods )
225225 {
226- for (const auto &instruction : method.instructions )
226+ for (const java_bytecode_parse_treet::instructiont &instruction :
227+ method.instructions )
227228 {
228229 if (instruction.statement == " getfield" ||
229230 instruction.statement == " putfield" )
@@ -359,7 +360,8 @@ static void generate_constant_global_variables(
359360{
360361 for (auto &method : parse_tree.parsed_class .methods )
361362 {
362- for (auto &instruction : method.instructions )
363+ for (java_bytecode_parse_treet::instructiont &instruction :
364+ method.instructions )
363365 {
364366 // ldc* instructions are Java bytecode "load constant" ops, which can
365367 // retrieve a numeric constant, String literal, or Class literal.
@@ -381,6 +383,87 @@ static void generate_constant_global_variables(
381383 }
382384}
383385
386+ // / Add a stub global symbol to the symbol table, initialising pointer-typed
387+ // / symbols with null and primitive-typed ones with an arbitrary (nondet) value.
388+ // / \param symbol_table: table to add to
389+ // / \param symbol_id: new symbol fully-qualified identifier
390+ // / \param symbol_basename: new symbol basename
391+ // / \param symbol_type: new symbol type
392+ // / \param class_id: class id that directly encloses this static field
393+ static void create_stub_global_symbol (
394+ symbol_table_baset &symbol_table,
395+ const irep_idt &symbol_id,
396+ const irep_idt &symbol_basename,
397+ const typet &symbol_type,
398+ const irep_idt &class_id)
399+ {
400+ symbolt new_symbol;
401+ new_symbol.is_static_lifetime = true ;
402+ new_symbol.is_lvalue = true ;
403+ new_symbol.is_state_var = true ;
404+ new_symbol.name = symbol_id;
405+ new_symbol.base_name = symbol_basename;
406+ new_symbol.type = symbol_type;
407+ new_symbol.type .set (ID_C_class, class_id);
408+ new_symbol.pretty_name = new_symbol.name ;
409+ new_symbol.mode = ID_java;
410+ new_symbol.is_type = false ;
411+ // If pointer-typed, initialise to null and a static initialiser will be
412+ // created to initialise on first reference. If primitive-typed, specify
413+ // nondeterministic initialisation by setting a nil value.
414+ if (symbol_type.id () == ID_pointer)
415+ new_symbol.value = null_pointer_exprt (to_pointer_type (symbol_type));
416+ else
417+ new_symbol.value .make_nil ();
418+ bool add_failed = symbol_table.add (new_symbol);
419+ INVARIANT (
420+ !add_failed, " caller should have checked symbol not already in table" );
421+ }
422+
423+ // / Search for getstatic and putstatic instructions in a class' bytecode and
424+ // / create stub symbols for any static fields that aren't already in the symbol
425+ // / table. The new symbols are null-initialised for reference-typed globals /
426+ // / static fields, and nondet-initialised for primitives.
427+ // / \param parse_tree: class bytecode
428+ // / \param symbol_table: symbol table; may gain new symbols
429+ static void create_stub_global_symbols (
430+ const java_bytecode_parse_treet &parse_tree,
431+ symbol_table_baset &symbol_table)
432+ {
433+ namespacet ns (symbol_table);
434+ for (const auto &method : parse_tree.parsed_class .methods )
435+ {
436+ for (const java_bytecode_parse_treet::instructiont &instruction :
437+ method.instructions )
438+ {
439+ if (instruction.statement == " getstatic" ||
440+ instruction.statement == " putstatic" )
441+ {
442+ INVARIANT (
443+ instruction.args .size () > 0 ,
444+ " get/putstatic should have at least one argument" );
445+ irep_idt component = instruction.args [0 ].get_string (ID_component_name);
446+ INVARIANT (
447+ !component.empty (), " get/putstatic should specify a component" );
448+ irep_idt class_id = instruction.args [0 ].get_string (ID_class);
449+ INVARIANT (
450+ !class_id.empty (), " get/putstatic should specify a class" );
451+ irep_idt identifier = id2string (class_id) + " ." + id2string (component);
452+
453+ if (!symbol_table.has_symbol (identifier))
454+ {
455+ create_stub_global_symbol (
456+ symbol_table,
457+ identifier,
458+ component,
459+ instruction.args [0 ].type (),
460+ class_id);
461+ }
462+ }
463+ }
464+ }
465+ }
466+
384467bool java_bytecode_languaget::typecheck (
385468 symbol_tablet &symbol_table,
386469 const std::string &module )
@@ -477,10 +560,31 @@ bool java_bytecode_languaget::typecheck(
477560 << " String or Class constant symbols"
478561 << messaget::eom;
479562
563+ // For each reference to a stub global (that is, a global variable declared on
564+ // a class we don't have bytecode for, and therefore don't know the static
565+ // initialiser for), create a synthetic static initialiser (clinit method)
566+ // to nondet initialise it.
567+ // Note this must be done before making static initialiser wrappers below, as
568+ // this makes a Classname.clinit method, then the next pass makes a wrapper
569+ // that ensures it is only run once, and that static initialisation happens
570+ // in class-graph topological order.
571+
572+ {
573+ journalling_symbol_tablet symbol_table_journal =
574+ journalling_symbol_tablet::wrap (symbol_table);
575+ for (const auto &c : java_class_loader.class_map )
576+ {
577+ create_stub_global_symbols (c.second , symbol_table_journal);
578+ }
579+
580+ stub_global_initializer_factory.create_stub_global_initializer_symbols (
581+ symbol_table, symbol_table_journal.get_inserted (), synthetic_methods);
582+ }
583+
480584 // For each class that will require a static initializer wrapper, create a
481585 // function named package.classname::clinit_wrapper, and a corresponding
482586 // global tracking whether it has run or not:
483- create_static_initializer_wrappers (symbol_table);
587+ create_static_initializer_wrappers (symbol_table, synthetic_methods );
484588
485589 // Now incrementally elaborate methods
486590 // that are reachable from this entry point.
@@ -495,24 +599,12 @@ bool java_bytecode_languaget::typecheck(
495599 journalling_symbol_tablet journalling_symbol_table =
496600 journalling_symbol_tablet::wrap (symbol_table);
497601
602+ // Convert all synthetic methods:
603+ for (const auto &function_id_and_type : synthetic_methods)
498604 {
499- // Convert all static initialisers:
500- std::vector<irep_idt> static_initialisers;
501-
502- // Careful not to add symbols while iterating over the symbol table!
503- for (const auto &symbol : symbol_table.symbols )
504- {
505- if (is_clinit_wrapper_function (symbol.second .name ))
506- static_initialisers.push_back (symbol.second .name );
507- }
508-
509- for (const auto &static_init_wrapper_name : static_initialisers)
510- {
511- convert_single_method (
512- static_init_wrapper_name, journalling_symbol_table);
513- }
605+ convert_single_method (
606+ function_id_and_type.first , journalling_symbol_table);
514607 }
515-
516608 // Convert all methods for which we have bytecode now
517609 for (const auto &method_sig : method_bytecode)
518610 {
@@ -714,6 +806,8 @@ bool java_bytecode_languaget::convert_single_method(
714806 // Get bytecode for specified function if we have it
715807 method_bytecodet::opt_reft cmb = method_bytecode.get (function_id);
716808
809+ synthetic_methods_mapt::iterator synthetic_method_it;
810+
717811 // Check if have a string solver implementation
718812 if (string_preprocess.implements_function (function_id))
719813 {
@@ -735,11 +829,29 @@ bool java_bytecode_languaget::convert_single_method(
735829 symbol.value = generated_code;
736830 return false ;
737831 }
738- else if (is_clinit_wrapper_function (function_id))
832+ else if (
833+ (synthetic_method_it = synthetic_methods.find (function_id)) !=
834+ synthetic_methods.end ())
739835 {
836+ // Synthetic method (i.e. one generated by the Java frontend and which
837+ // doesn't occur in the source bytecode):
740838 symbolt &symbol = symbol_table.get_writeable_ref (function_id);
741- symbol.value = get_clinit_wrapper_body (function_id, symbol_table);
742- // Notify lazy methods of other static init functions called:
839+ switch (synthetic_method_it->second )
840+ {
841+ case synthetic_method_typet::STATIC_INITIALIZER_WRAPPER:
842+ symbol.value = get_clinit_wrapper_body (function_id, symbol_table);
843+ break ;
844+ case synthetic_method_typet::STUB_CLASS_STATIC_INITIALIZER:
845+ symbol.value =
846+ stub_global_initializer_factory.get_stub_initializer_body (
847+ function_id,
848+ symbol_table,
849+ object_factory_parameters,
850+ get_pointer_type_selector ());
851+ break ;
852+ }
853+ // Notify lazy methods of static calls made from the newly generated
854+ // function:
743855 notify_static_method_calls (symbol.value , needed_lazy_methods);
744856 return false ;
745857 }
0 commit comments