Skip to content

Commit 679d9b8

Browse files
committed
Java frontend: create static initialiser globals ahead of time
This creates the global variables that indicate whether a static initialiser has already run during java_bytecode_languaget::typecheck, rather than introducing them during method conversion as before. This means that symex-driven lazy loading will be able to rely on the globals being mentioned in __CPROVER_initialize, even if the first method to reference the static initialiser is converted after _initialize has been created.
1 parent 4a93a29 commit 679d9b8

8 files changed

+334
-186
lines changed

src/java_bytecode/CMakeLists.txt

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ add_subdirectory(library)
77

88
add_library(java_bytecode ${sources} ${headers} )
99
add_dependencies(java_bytecode core_models_files)
10-
target_sources(java_bytecode PUBLIC ${sources} ${headers})
1110

1211
generic_includes(java_bytecode)
1312

src/java_bytecode/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ SRC = bytecode_info.cpp \
2323
java_object_factory.cpp \
2424
java_pointer_casts.cpp \
2525
java_root_class.cpp \
26+
java_static_initializers.cpp \
2627
java_string_library_preprocess.cpp \
2728
java_string_literals.cpp \
2829
java_types.cpp \

src/java_bytecode/ci_lazy_methods.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ bool ci_lazy_methodst::operator()(
111111
std::set<irep_idt> needed_classes;
112112

113113
{
114-
std::vector<irep_idt> needed_clinits;
114+
std::vector<irep_idt> initial_needed_methods;
115115
ci_lazy_methods_neededt initial_lazy_methods(
116-
needed_clinits,
116+
initial_needed_methods,
117117
needed_classes,
118118
symbol_table);
119119
initialize_needed_classes(
@@ -122,8 +122,8 @@ bool ci_lazy_methodst::operator()(
122122
initial_lazy_methods);
123123
method_worklist2.insert(
124124
method_worklist2.end(),
125-
needed_clinits.begin(),
126-
needed_clinits.end());
125+
initial_needed_methods.begin(),
126+
initial_needed_methods.end());
127127
}
128128

129129
std::set<irep_idt> methods_already_populated;

src/java_bytecode/ci_lazy_methods_needed.cpp

+2-4
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,12 @@ bool ci_lazy_methods_neededt::add_needed_class(
3434
{
3535
if(!needed_classes.insert(class_symbol_name).second)
3636
return false;
37-
const std::string &class_name_string = id2string(class_symbol_name);
38-
const irep_idt clinit_name(class_name_string + ".<clinit>:()V");
39-
if(symbol_table.symbols.count(clinit_name))
40-
add_needed_method(clinit_name);
4137

38+
const std::string &class_name_string = id2string(class_symbol_name);
4239
const irep_idt cprover_validate(
4340
class_name_string + ".cproverNondetInitialize:()V");
4441
if(symbol_table.symbols.count(cprover_validate))
4542
add_needed_method(cprover_validate);
43+
4644
return true;
4745
}

src/java_bytecode/java_bytecode_convert_method.cpp

+11-155
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Author: Daniel Kroening, [email protected]
1616
#include "java_bytecode_convert_method.h"
1717
#include "java_bytecode_convert_method_class.h"
1818
#include "bytecode_info.h"
19+
#include "java_static_initializers.h"
1920
#include "java_string_library_preprocess.h"
2021
#include "java_types.h"
2122
#include "java_utils.h"
@@ -930,156 +931,6 @@ void java_bytecode_convert_methodt::check_static_field_stub(
930931
}
931932
}
932933

933-
/// Determine whether a `new` or static access against `classname` should be
934-
/// prefixed with a static initialization check.
935-
/// \param classname: Class name
936-
/// \return Returns true if the given class or one of its parents has a static
937-
/// initializer
938-
bool java_bytecode_convert_methodt::class_needs_clinit(
939-
const irep_idt &classname)
940-
{
941-
auto findit_any=any_superclass_has_clinit_method.insert({classname, false});
942-
if(!findit_any.second)
943-
return findit_any.first->second;
944-
945-
auto findit_here=class_has_clinit_method.insert({classname, false});
946-
if(findit_here.second)
947-
{
948-
const irep_idt &clinit_name=id2string(classname)+".<clinit>:()V";
949-
findit_here.first->second=symbol_table.symbols.count(clinit_name);
950-
}
951-
if(findit_here.first->second)
952-
{
953-
findit_any.first->second=true;
954-
return true;
955-
}
956-
const auto maybe_symbol=symbol_table.lookup(classname);
957-
// Stub class?
958-
if(!maybe_symbol)
959-
{
960-
warning() << "SKIPPED: " << classname << eom;
961-
return false;
962-
}
963-
const symbolt &class_symbol=*maybe_symbol;
964-
for(const auto &base : to_class_type(class_symbol.type).bases())
965-
{
966-
if(class_needs_clinit(to_symbol_type(base.type()).get_identifier()))
967-
{
968-
findit_any.first->second=true;
969-
return true;
970-
}
971-
}
972-
return false;
973-
}
974-
975-
/// Create a ::clinit_wrapper the first time a static initializer might be
976-
/// called. The wrapper method checks whether static init has already taken
977-
/// place, calls the actual <clinit> method if not, and initializes super-
978-
/// classes and interfaces.
979-
/// \param classname: Class name
980-
/// \return Returns a symbol_exprt pointing to the given class' clinit wrapper
981-
/// if one is required, or nil otherwise.
982-
exprt java_bytecode_convert_methodt::get_or_create_clinit_wrapper(
983-
const irep_idt &classname)
984-
{
985-
if(!class_needs_clinit(classname))
986-
return static_cast<const exprt &>(get_nil_irep());
987-
988-
// if the symbol table already contains the clinit_wrapper() function, return
989-
// it
990-
const irep_idt &clinit_wrapper_name=
991-
id2string(classname)+"::clinit_wrapper";
992-
auto findit=symbol_table.symbols.find(clinit_wrapper_name);
993-
if(findit!=symbol_table.symbols.end())
994-
return findit->second.symbol_expr();
995-
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-
// }
1015-
const irep_idt &already_run_name=
1016-
id2string(classname)+"::clinit_already_run";
1017-
symbolt already_run_symbol;
1018-
already_run_symbol.name=already_run_name;
1019-
already_run_symbol.pretty_name=already_run_name;
1020-
already_run_symbol.base_name="clinit_already_run";
1021-
already_run_symbol.type=bool_typet();
1022-
already_run_symbol.value=false_exprt();
1023-
already_run_symbol.is_lvalue=true;
1024-
already_run_symbol.is_state_var=true;
1025-
already_run_symbol.is_static_lifetime=true;
1026-
already_run_symbol.mode=ID_java;
1027-
symbol_table.add(already_run_symbol);
1028-
1029-
equal_exprt check_already_run(
1030-
already_run_symbol.symbol_expr(),
1031-
false_exprt());
1032-
1033-
// the entire body of the function is an if-then-else
1034-
code_ifthenelset wrapper_body;
1035-
1036-
// add the condition to the if
1037-
wrapper_body.cond()=check_already_run;
1038-
1039-
// add the "already-run = false" statement
1040-
code_blockt init_body;
1041-
code_assignt set_already_run(already_run_symbol.symbol_expr(), true_exprt());
1042-
init_body.move_to_operands(set_already_run);
1043-
1044-
// iterate through the base types and add recursive calls to the
1045-
// clinit_wrapper()
1046-
const symbolt &class_symbol=*symbol_table.lookup(classname);
1047-
for(const auto &base : to_class_type(class_symbol.type).bases())
1048-
{
1049-
const auto base_name=to_symbol_type(base.type()).get_identifier();
1050-
exprt base_init_routine=get_or_create_clinit_wrapper(base_name);
1051-
if(base_init_routine.is_nil())
1052-
continue;
1053-
code_function_callt call_base;
1054-
call_base.function()=base_init_routine;
1055-
init_body.move_to_operands(call_base);
1056-
}
1057-
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-
}
1067-
wrapper_body.then_case()=init_body;
1068-
1069-
// insert symbol in the symbol table
1070-
symbolt wrapper_method_symbol;
1071-
code_typet wrapper_method_type;
1072-
wrapper_method_type.return_type()=void_typet();
1073-
wrapper_method_symbol.name=clinit_wrapper_name;
1074-
wrapper_method_symbol.pretty_name=clinit_wrapper_name;
1075-
wrapper_method_symbol.base_name="clinit_wrapper";
1076-
wrapper_method_symbol.type=wrapper_method_type;
1077-
wrapper_method_symbol.value=wrapper_body;
1078-
wrapper_method_symbol.mode=ID_java;
1079-
symbol_table.add(wrapper_method_symbol);
1080-
return wrapper_method_symbol.symbol_expr();
1081-
}
1082-
1083934
/// Each static access to classname should be prefixed with a check for
1084935
/// necessary static init; this returns a call implementing that check.
1085936
/// \param classname: Class name
@@ -1088,12 +939,17 @@ exprt java_bytecode_convert_methodt::get_or_create_clinit_wrapper(
1088939
codet java_bytecode_convert_methodt::get_clinit_call(
1089940
const irep_idt &classname)
1090941
{
1091-
exprt callee=get_or_create_clinit_wrapper(classname);
1092-
if(callee.is_nil())
942+
auto findit = symbol_table.symbols.find(clinit_wrapper_name(classname));
943+
if(findit == symbol_table.symbols.end())
1093944
return code_skipt();
1094-
code_function_callt ret;
1095-
ret.function()=callee;
1096-
return ret;
945+
else
946+
{
947+
code_function_callt ret;
948+
ret.function() = findit->second.symbol_expr();
949+
if(needed_lazy_methods)
950+
needed_lazy_methods->add_needed_method(findit->second.name);
951+
return ret;
952+
}
1097953
}
1098954

1099955
static unsigned get_bytecode_type_width(const typet &ty)

src/java_bytecode/java_bytecode_language.cpp

+69-22
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Author: Daniel Kroening, [email protected]
3131
#include "java_bytecode_parser.h"
3232
#include "java_class_loader.h"
3333
#include "java_string_literals.h"
34+
#include "java_static_initializers.h"
3435
#include "java_utils.h"
3536
#include <java_bytecode/ci_lazy_methods.h>
3637
#include <java_bytecode/generate_java_generic_type.h>
@@ -476,6 +477,11 @@ bool java_bytecode_languaget::typecheck(
476477
<< " String or Class constant symbols"
477478
<< messaget::eom;
478479

480+
// For each class that will require a static initializer wrapper, create a
481+
// function named package.classname::clinit_wrapper, and a corresponding
482+
// global tracking whether it has run or not:
483+
create_static_initializer_wrappers(symbol_table);
484+
479485
// Now incrementally elaborate methods
480486
// that are reachable from this entry point.
481487
if(lazy_methods_mode==LAZY_METHODS_MODE_CONTEXT_INSENSITIVE)
@@ -488,6 +494,25 @@ bool java_bytecode_languaget::typecheck(
488494
{
489495
journalling_symbol_tablet journalling_symbol_table =
490496
journalling_symbol_tablet::wrap(symbol_table);
497+
498+
{
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+
}
514+
}
515+
491516
// Convert all methods for which we have bytecode now
492517
for(const auto &method_sig : method_bytecode)
493518
{
@@ -633,6 +658,39 @@ void java_bytecode_languaget::convert_lazy_method(
633658
symbol_table, get_message_handler(), string_refinement_enabled);
634659
}
635660

661+
/// Notify ci_lazy_methods, if present, of any static function calls made by
662+
/// the given function body.
663+
/// \param function_body: function body code
664+
/// \param needed_lazy_methods: optional ci_lazy_method_neededt interface. If
665+
/// not set, this is a no-op; otherwise, its add_needed_method function will
666+
/// be called for each function call in `function_body`.
667+
static void notify_static_method_calls(
668+
const exprt &function_body,
669+
optionalt<ci_lazy_methods_neededt> needed_lazy_methods)
670+
{
671+
if(needed_lazy_methods)
672+
{
673+
for(const_depth_iteratort it = function_body.depth_cbegin();
674+
it != function_body.depth_cend();
675+
++it)
676+
{
677+
if(it->id() == ID_code)
678+
{
679+
const auto fn_call = expr_try_dynamic_cast<code_function_callt>(*it);
680+
if(!fn_call)
681+
continue;
682+
// Only support non-virtual function calls for now, if string solver
683+
// starts to introduce virtual function calls then we will need to
684+
// duplicate the behavior of java_bytecode_convert_method where it
685+
// handles the invokevirtual instruction
686+
const symbol_exprt &fn_sym =
687+
expr_dynamic_cast<symbol_exprt>(fn_call->function());
688+
needed_lazy_methods->add_needed_method(fn_sym.get_identifier());
689+
}
690+
}
691+
}
692+
}
693+
636694
/// \brief Convert a method (one whose type is known but whose body hasn't
637695
/// been converted) but don't run typecheck, etc
638696
/// \remarks Amends the symbol table entry for function `function_id`, which
@@ -673,32 +731,21 @@ bool java_bytecode_languaget::convert_single_method(
673731
generated_code.is_not_nil(), "Couldn't retrieve code for string method");
674732
// String solver can make calls to functions that haven't yet been seen.
675733
// Add these to the needed_lazy_methods collection
676-
if(needed_lazy_methods)
677-
{
678-
for(const_depth_iteratort it = generated_code.depth_cbegin();
679-
it != generated_code.depth_cend();
680-
++it)
681-
{
682-
if(it->id() == ID_code)
683-
{
684-
const auto fn_call = expr_try_dynamic_cast<code_function_callt>(*it);
685-
if(!fn_call)
686-
continue;
687-
// Only support non-virtual function calls for now, if string solver
688-
// starts to introduce virtual function calls then we will need to
689-
// duplicate the behavior of java_bytecode_convert_method where it
690-
// handles the invokevirtual instruction
691-
const symbol_exprt &fn_sym =
692-
expr_dynamic_cast<symbol_exprt>(fn_call->function());
693-
needed_lazy_methods->add_needed_method(fn_sym.get_identifier());
694-
}
695-
}
696-
}
734+
notify_static_method_calls(generated_code, needed_lazy_methods);
697735
symbol.value = generated_code;
698736
return false;
699737
}
738+
else if(is_clinit_wrapper_function(function_id))
739+
{
740+
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:
743+
notify_static_method_calls(symbol.value, needed_lazy_methods);
744+
return false;
745+
}
700746

701-
// No string solver implementation, check if have bytecode for it
747+
// No string solver or static init wrapper implementation;
748+
// check if have bytecode for it
702749
if(cmb)
703750
{
704751
java_bytecode_convert_method(

0 commit comments

Comments
 (0)