@@ -12,6 +12,7 @@ Author: Diffblue Ltd.
1212#include < util/arith_tools.h>
1313#include < util/c_types.h>
1414#include < util/exception_utils.h>
15+ #include < util/prefix.h>
1516#include < util/std_code.h>
1617#include < util/std_expr.h>
1718#include < util/string2int.h>
@@ -72,14 +73,24 @@ struct function_call_harness_generatort::implt
7273 void ensure_harness_does_not_already_exist ();
7374 // / Update the goto-model with the new harness function.
7475 void add_harness_function_to_goto_model (code_blockt function_body);
75- // / declare local variables for each of the parameters of the entry function
76+ // / Declare local variables for each of the parameters of the entry function
7677 // / and return them
7778 code_function_callt::argumentst declare_arguments (code_blockt &function_body);
78- // / write initialisation code for each of the arguments into function_body,
79+ // / Write initialisation code for each of the arguments into function_body,
7980 // / then insert a call to the entry function with the arguments
8081 void call_function (
8182 const code_function_callt::argumentst &arguments,
8283 code_blockt &function_body);
84+ // / For function parameters that are pointers to functions we want to
85+ // / be able to specify whether or not they can be NULL. To disambiguate
86+ // / this specification from that for a global variable of the same name,
87+ // / we prepend the name of the function to the parameter name. However,
88+ // / what is actually being initialised in the implementation is not the
89+ // / parameter itself, but a corresponding function argument (local variable
90+ // / of the harness function). We need a mapping from function parameter
91+ // / name to function argument names, and this is what this function does.
92+ std::unordered_set<irep_idt>
93+ map_function_parameters_to_function_argument_names ();
8394};
8495
8596function_call_harness_generatort::function_call_harness_generatort (
@@ -173,6 +184,30 @@ void function_call_harness_generatort::handle_option(
173184 {
174185 p_impl->recursive_initialization_config .arguments_may_be_equal = true ;
175186 }
187+ else if (option == COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT)
188+ {
189+ std::transform (
190+ values.begin (),
191+ values.end (),
192+ std::inserter (
193+ p_impl->recursive_initialization_config
194+ .potential_null_function_pointers ,
195+ p_impl->recursive_initialization_config .potential_null_function_pointers
196+ .end ()),
197+ [](const std::string &opt) -> irep_idt { return irep_idt{opt}; });
198+ }
199+ else if (option == COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT)
200+ {
201+ std::transform (
202+ values.begin (),
203+ values.end (),
204+ std::inserter (
205+ p_impl->recursive_initialization_config
206+ .potential_null_function_pointers ,
207+ p_impl->recursive_initialization_config .potential_null_function_pointers
208+ .end ()),
209+ [](const std::string &opt) -> irep_idt { return irep_idt{opt}; });
210+ }
176211 else
177212 {
178213 throw invalid_command_line_argument_exceptiont{
@@ -214,6 +249,8 @@ void function_call_harness_generatort::implt::generate(
214249 recursive_initialization_config.variables_that_hold_array_sizes .insert (
215250 pair.second );
216251 }
252+ recursive_initialization_config.potential_null_function_pointers =
253+ map_function_parameters_to_function_argument_names ();
217254 recursive_initialization_config.pointers_to_treat_as_cstrings =
218255 function_arguments_to_treat_as_cstrings;
219256 recursive_initialization = util_make_unique<recursive_initializationt>(
@@ -269,7 +306,7 @@ void function_call_harness_generatort::implt::generate_initialisation_code_for(
269306void function_call_harness_generatort::validate_options (
270307 const goto_modelt &goto_model)
271308{
272- if (p_impl->function == ID_empty )
309+ if (p_impl->function == ID_empty_string )
273310 throw invalid_command_line_argument_exceptiont{
274311 " required parameter entry function not set" ,
275312 " --" FUNCTION_HARNESS_GENERATOR_FUNCTION_OPT};
@@ -283,8 +320,17 @@ void function_call_harness_generatort::validate_options(
283320 " --" COMMON_HARNESS_GENERATOR_MAX_ARRAY_SIZE_OPT};
284321 }
285322
286- auto function_to_call = goto_model.symbol_table .lookup_ref (p_impl->function );
287- auto ftype = to_code_type (function_to_call.type );
323+ const auto function_to_call_pointer =
324+ goto_model.symbol_table .lookup (p_impl->function );
325+ if (function_to_call_pointer == nullptr )
326+ {
327+ throw invalid_command_line_argument_exceptiont{
328+ " entry function `" + id2string (p_impl->function ) +
329+ " ' does not exist in the symbol table" ,
330+ " --" FUNCTION_HARNESS_GENERATOR_FUNCTION_OPT};
331+ }
332+ const auto &function_to_call = *function_to_call_pointer;
333+ const auto &ftype = to_code_type (function_to_call.type );
288334 for (auto const &equal_cluster : p_impl->function_parameters_to_treat_equal )
289335 {
290336 for (auto const &pointer_id : equal_cluster)
@@ -325,6 +371,43 @@ void function_call_harness_generatort::validate_options(
325371 }
326372 }
327373 }
374+
375+ const namespacet ns{goto_model.symbol_table };
376+
377+ // Make sure all function pointers that the user asks are nullable are
378+ // present in the symbol table.
379+ for (const auto &nullable :
380+ p_impl->recursive_initialization_config .potential_null_function_pointers )
381+ {
382+ const auto &function_pointer_symbol_pointer =
383+ goto_model.symbol_table .lookup (nullable);
384+
385+ if (function_pointer_symbol_pointer == nullptr )
386+ {
387+ throw invalid_command_line_argument_exceptiont{
388+ " nullable function pointer `" + id2string (nullable) +
389+ " ' not found in symbol table" ,
390+ " --" COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT};
391+ }
392+
393+ const auto &function_pointer_type =
394+ ns.follow (function_pointer_symbol_pointer->type );
395+
396+ if (!can_cast_type<pointer_typet>(function_pointer_type))
397+ {
398+ throw invalid_command_line_argument_exceptiont{
399+ " `" + id2string (nullable) + " ' is not a pointer" ,
400+ " --" COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT};
401+ }
402+
403+ if (!can_cast_type<code_typet>(
404+ to_pointer_type (function_pointer_type).subtype ()))
405+ {
406+ throw invalid_command_line_argument_exceptiont{
407+ " `" + id2string (nullable) + " ' is not pointing to a function" ,
408+ " --" COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT};
409+ }
410+ }
328411}
329412
330413const symbolt &
@@ -469,3 +552,25 @@ void function_call_harness_generatort::implt::call_function(
469552
470553 function_body.add (std::move (function_call));
471554}
555+
556+ std::unordered_set<irep_idt> function_call_harness_generatort::implt::
557+ map_function_parameters_to_function_argument_names ()
558+ {
559+ std::unordered_set<irep_idt> nullables;
560+ for (const auto &nullable :
561+ recursive_initialization_config.potential_null_function_pointers )
562+ {
563+ const auto &nullable_name = id2string (nullable);
564+ const auto &function_prefix = id2string (function) + " ::" ;
565+ if (has_prefix (nullable_name, function_prefix))
566+ {
567+ nullables.insert (
568+ " __goto_harness::" + nullable_name.substr (function_prefix.size ()));
569+ }
570+ else
571+ {
572+ nullables.insert (nullable_name);
573+ }
574+ }
575+ return nullables;
576+ }
0 commit comments