Skip to content

Commit a1e9d00

Browse files
author
svorenova
committed
Updating and extending utility functions
1 parent 2964910 commit a1e9d00

File tree

2 files changed

+300
-12
lines changed

2 files changed

+300
-12
lines changed

unit/testing-utils/require_goto_statements.cpp

+275-11
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
#include <algorithm>
1313
#include <util/expr_iterator.h>
14+
#include <goto-programs/goto_functions.h>
15+
#include <java_bytecode/java_types.h>
1416

1517
/// Expand value of a function to include all child codets
1618
/// \param function_value: The value of the function (e.g. got by looking up
@@ -33,18 +35,38 @@ require_goto_statements::get_all_statements(const exprt &function_value)
3335
return statements;
3436
}
3537

36-
/// Find assignment statements of the form \p structure_name.\component_name =
38+
/// \param symbol_table Symbol table for the test
39+
/// \return All codet statements of the __CPROVER_start function
40+
const std::vector<codet>
41+
require_goto_statements::require_entry_point_statements(
42+
const symbol_tablet &symbol_table)
43+
{
44+
// Retrieve __CPROVER_start
45+
const symbolt *entry_point_function =
46+
symbol_table.lookup(goto_functionst::entry_point());
47+
REQUIRE(entry_point_function != nullptr);
48+
return get_all_statements(entry_point_function->value);
49+
}
50+
51+
/// Find assignment statements of the form:
52+
/// - \p structure_name
53+
/// .@\p superclass_name.\p component_name = (if it's a component inherited
54+
/// from the superclass)
55+
/// - \p structure_name.\p component_name = (otherwise)
3756
/// \param statements: The statements to look through
3857
/// \param structure_name: The name of variable of type struct
39-
/// \param component_name: The name of the component that should be assigned
58+
/// \param superclass_name: The name of the superclass (if given)
59+
/// \param component_name: The name of the component of the superclass that
60+
/// should be assigned
4061
/// \return: All the assignments to that component.
41-
std::vector<code_assignt>
62+
require_goto_statements::pointer_assignment_locationt
4263
require_goto_statements::find_struct_component_assignments(
4364
const std::vector<codet> &statements,
4465
const irep_idt &structure_name,
66+
const optionalt<irep_idt> &superclass_name,
4567
const irep_idt &component_name)
4668
{
47-
std::vector<code_assignt> component_assignments;
69+
pointer_assignment_locationt locations;
4870

4971
for(const auto &assignment : statements)
5072
{
@@ -54,19 +76,69 @@ require_goto_statements::find_struct_component_assignments(
5476

5577
if(code_assign.lhs().id() == ID_member)
5678
{
57-
const auto &member_expr = to_member_expr(code_assign.lhs());
58-
const auto &symbol = member_expr.symbol();
79+
const member_exprt &member_expr = to_member_expr(code_assign.lhs());
5980

60-
if(
61-
symbol.get_identifier() == structure_name &&
62-
member_expr.get_component_name() == component_name)
81+
if(superclass_name.has_value())
6382
{
64-
component_assignments.push_back(code_assign);
83+
// Structure of the expression:
84+
// member_exprt member_expr:
85+
// - component name: \p component_name
86+
// - operand (component of): member_exprt superclass_expr:
87+
// - component name: @\p superclass_name
88+
// - operand (component of): symbol for \p structure_name
89+
if(
90+
member_expr.get_component_name() == component_name &&
91+
member_expr.compound().id() == ID_member)
92+
{
93+
const member_exprt &superclass_expr =
94+
to_member_expr(member_expr.op0());
95+
const irep_idt supercomponent_name =
96+
"@" + id2string(superclass_name.value());
97+
98+
if(
99+
superclass_expr.get_component_name() == supercomponent_name &&
100+
superclass_expr.symbol().get_identifier() == structure_name)
101+
{
102+
if(
103+
code_assign.rhs() ==
104+
null_pointer_exprt(to_pointer_type(code_assign.lhs().type())))
105+
{
106+
locations.null_assignment = code_assign;
107+
}
108+
else
109+
{
110+
locations.non_null_assignments.push_back(code_assign);
111+
}
112+
}
113+
}
114+
}
115+
else
116+
{
117+
// Structure of the expression:
118+
// member_exprt member_expr:
119+
// - component name: \p component_name
120+
// - operand (component of): symbol for \p structure_name
121+
if(
122+
member_expr.op().id() == ID_symbol &&
123+
member_expr.symbol().get_identifier() == structure_name &&
124+
member_expr.get_component_name() == component_name)
125+
{
126+
if(
127+
code_assign.rhs() ==
128+
null_pointer_exprt(to_pointer_type(code_assign.lhs().type())))
129+
{
130+
locations.null_assignment = code_assign;
131+
}
132+
else
133+
{
134+
locations.non_null_assignments.push_back(code_assign);
135+
}
136+
}
65137
}
66138
}
67139
}
68140
}
69-
return component_assignments;
141+
return locations;
70142
}
71143

72144
/// For a given variable name, gets the assignments to it in the provided
@@ -131,3 +203,195 @@ const code_declt &require_goto_statements::require_declaration_of_name(
131203
}
132204
throw no_decl_found_exceptiont(variable_name.c_str());
133205
}
206+
207+
/// Checks that the component of the structure (possibly inherited from
208+
/// the superclass) is assigned an object of the given type.
209+
/// \param structure_name The name the variable
210+
/// \param superclass_name The name of its superclass (if given)
211+
/// \param component_name The name of the field of the superclass
212+
/// \param component_type_name The name of the required type of the field
213+
/// \param typecast_name The name of the type to which the object is cast (if
214+
/// there is a typecast)
215+
/// \param entry_point_instructions The statements to look through
216+
/// \return The identifier of the variable assigned to the field
217+
const irep_idt &require_goto_statements::require_struct_component_assignment(
218+
const irep_idt &structure_name,
219+
const optionalt<irep_idt> &superclass_name,
220+
const irep_idt &component_name,
221+
const irep_idt &component_type_name,
222+
const optionalt<irep_idt> &typecast_name,
223+
const std::vector<codet> &entry_point_instructions)
224+
{
225+
// First we need to find the assignments to the component belonging to
226+
// the structure_name object
227+
const auto &component_assignments =
228+
require_goto_statements::find_struct_component_assignments(
229+
entry_point_instructions,
230+
structure_name,
231+
superclass_name,
232+
component_name);
233+
REQUIRE(component_assignments.non_null_assignments.size() == 1);
234+
235+
// We are expecting that the resulting statement can be of two forms:
236+
// 1. structure_name.(@superclass_name if given).component =
237+
// (struct cast_type_name *) tmp_object_factory$1;
238+
// followed by a direct assignment like this:
239+
// tmp_object_factory$1 = &tmp_object_factory$2;
240+
// 2. structure_name.component = &tmp_object_factory$1;
241+
exprt component_assignment_rhs_expr =
242+
component_assignments.non_null_assignments[0].rhs();
243+
244+
// If the rhs is a typecast (case 1 above), deconstruct it to get the name of
245+
// the variable and find the assignment to it
246+
if(component_assignment_rhs_expr.id() == ID_typecast)
247+
{
248+
const auto &component_assignment_rhs =
249+
to_typecast_expr(component_assignment_rhs_expr);
250+
251+
// Check the type we are casting to
252+
if(typecast_name.has_value())
253+
{
254+
REQUIRE(component_assignment_rhs.type().id() == ID_pointer);
255+
REQUIRE(
256+
to_symbol_type(
257+
to_pointer_type(component_assignment_rhs.type()).subtype())
258+
.get(ID_identifier) == typecast_name.value());
259+
}
260+
261+
const auto &component_reference_tmp_name =
262+
to_symbol_expr(component_assignment_rhs.op()).get_identifier();
263+
const auto &component_reference_assignments =
264+
require_goto_statements::find_pointer_assignments(
265+
component_reference_tmp_name, entry_point_instructions)
266+
.non_null_assignments;
267+
REQUIRE(component_reference_assignments.size() == 1);
268+
component_assignment_rhs_expr = component_reference_assignments[0].rhs();
269+
}
270+
271+
// The rhs assigns an address of a variable, get its name
272+
const auto &component_reference_assignment_rhs =
273+
to_address_of_expr(component_assignment_rhs_expr);
274+
const auto &component_tmp_name =
275+
to_symbol_expr(component_reference_assignment_rhs.op()).get_identifier();
276+
277+
// After we have found the declaration of the final assignment's
278+
// right hand side, then we want to identify that the type
279+
// is the one we expect, e.g.:
280+
// struct java.lang.Integer { __CPROVER_string @class_identifier;
281+
// boolean @lock; } tmp_object_factory$2;
282+
const auto &component_declaration =
283+
require_goto_statements::require_declaration_of_name(
284+
component_tmp_name, entry_point_instructions);
285+
REQUIRE(component_declaration.symbol().type().id() == ID_struct);
286+
const auto &component_struct =
287+
to_struct_type(component_declaration.symbol().type());
288+
REQUIRE(component_struct.get(ID_name) == component_type_name);
289+
290+
return component_tmp_name;
291+
}
292+
293+
/// Checks that the array component of the structure (possibly inherited from
294+
/// the superclass) is assigned an array with given element type.
295+
/// \param structure_name The name the variable
296+
/// \param superclass_name The name of its superclass (if given)
297+
/// \param array_component_name The name of the array field of the superclass
298+
/// \param array_type_name The type of the array, e.g., java::array[reference]
299+
/// \param array_component_element_type_name The element type of the array
300+
/// \param entry_point_instructions The statements to look through
301+
/// \return The identifier of the variable assigned to the field
302+
const irep_idt &
303+
require_goto_statements::require_struct_array_component_assignment(
304+
const irep_idt &structure_name,
305+
const optionalt<irep_idt> &superclass_name,
306+
const irep_idt &array_component_name,
307+
const irep_idt &array_type_name,
308+
const irep_idt &array_component_element_type_name,
309+
const std::vector<codet> &entry_point_instructions)
310+
{
311+
// First we need to find the assignments to the component belonging to
312+
// the structure_name object
313+
const auto &component_assignments =
314+
require_goto_statements::find_struct_component_assignments(
315+
entry_point_instructions,
316+
structure_name,
317+
superclass_name,
318+
array_component_name);
319+
REQUIRE(component_assignments.non_null_assignments.size() == 1);
320+
321+
// We are expecting that the resulting statement is of form:
322+
// structure_name.array_component_name = (struct array_type_name *)
323+
// tmp_object_factory$1;
324+
const exprt &component_assignment_rhs_expr =
325+
component_assignments.non_null_assignments[0].rhs();
326+
327+
// The rhs is a typecast, deconstruct it to get the name of the variable
328+
// and find the assignment to it
329+
PRECONDITION(component_assignment_rhs_expr.id() == ID_typecast);
330+
const auto &component_assignment_rhs =
331+
to_typecast_expr(component_assignment_rhs_expr);
332+
const auto &component_reference_tmp_name =
333+
to_symbol_expr(component_assignment_rhs.op()).get_identifier();
334+
335+
// Check the type we are casting to matches the array type
336+
REQUIRE(component_assignment_rhs.type().id() == ID_pointer);
337+
REQUIRE(
338+
to_symbol_type(to_pointer_type(component_assignment_rhs.type()).subtype())
339+
.get(ID_identifier) == array_type_name);
340+
341+
// Get the tmp_object name and find assignments to it, there should be only
342+
// one that assigns the correct array through side effect
343+
const auto &component_reference_assignments =
344+
require_goto_statements::find_pointer_assignments(
345+
component_reference_tmp_name, entry_point_instructions);
346+
REQUIRE(component_reference_assignments.non_null_assignments.size() == 1);
347+
const exprt &component_reference_assignment_rhs_expr =
348+
component_reference_assignments.non_null_assignments[0].rhs();
349+
350+
// The rhs is side effect
351+
PRECONDITION(component_reference_assignment_rhs_expr.id() == ID_side_effect);
352+
const auto &array_component_reference_assignment_rhs =
353+
to_side_effect_expr(component_reference_assignment_rhs_expr);
354+
355+
// The type of the side effect is an array with the correct element type
356+
PRECONDITION(
357+
array_component_reference_assignment_rhs.type().id() == ID_pointer);
358+
const typet &array =
359+
to_pointer_type(array_component_reference_assignment_rhs.type()).subtype();
360+
PRECONDITION(is_java_array_tag(array.get(ID_identifier)));
361+
REQUIRE(array.get(ID_identifier) == array_type_name);
362+
363+
return component_reference_tmp_name;
364+
}
365+
366+
/// Checks that the input argument (of method under test) with given name is
367+
/// assigned a single non-null object in the entry point function.
368+
/// \param argument_name Name of the input argument of method under test
369+
/// \param entry_point_statements The statements to look through
370+
/// \return The identifier of the variable assigned to the input argument
371+
const irep_idt &
372+
require_goto_statements::require_entry_point_argument_assignment(
373+
const irep_idt &argument_name,
374+
const std::vector<codet> &entry_point_statements)
375+
{
376+
// Trace the creation of the object that is being supplied as the input
377+
// argument to the function under test
378+
const pointer_assignment_locationt &argument_assignments =
379+
find_pointer_assignments(
380+
id2string(goto_functionst::entry_point()) + "::" +
381+
id2string(argument_name),
382+
entry_point_statements);
383+
384+
// There should be at most one assignment to it
385+
REQUIRE(argument_assignments.non_null_assignments.size() == 1);
386+
387+
// The following finds the name of the tmp object that gets assigned
388+
// to the input argument. There must be one such assignment only,
389+
// and usually looks like this:
390+
// argument_name = &tmp_object_factory$1;
391+
const auto &argument_assignment =
392+
argument_assignments.non_null_assignments[0];
393+
const auto &argument_tmp_name =
394+
to_symbol_expr(to_address_of_expr(argument_assignment.rhs()).object())
395+
.get_identifier();
396+
return argument_tmp_name;
397+
}

unit/testing-utils/require_goto_statements.h

+25-1
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,44 @@ class no_decl_found_exceptiont : public std::exception
4747
std::string _varname;
4848
};
4949

50-
std::vector<code_assignt> find_struct_component_assignments(
50+
pointer_assignment_locationt find_struct_component_assignments(
5151
const std::vector<codet> &statements,
5252
const irep_idt &structure_name,
53+
const optionalt<irep_idt> &superclass_name,
5354
const irep_idt &component_name);
5455

5556
std::vector<codet> get_all_statements(const exprt &function_value);
5657

58+
const std::vector<codet>
59+
require_entry_point_statements(const symbol_tablet &symbol_table);
60+
5761
pointer_assignment_locationt find_pointer_assignments(
5862
const irep_idt &pointer_name,
5963
const std::vector<codet> &instructions);
6064

6165
const code_declt &require_declaration_of_name(
6266
const irep_idt &variable_name,
6367
const std::vector<codet> &entry_point_instructions);
68+
69+
const irep_idt &require_struct_component_assignment(
70+
const irep_idt &structure_name,
71+
const optionalt<irep_idt> &superclass_name,
72+
const irep_idt &component_name,
73+
const irep_idt &component_type_name,
74+
const optionalt<irep_idt> &typecast_name,
75+
const std::vector<codet> &entry_point_instructions);
76+
77+
const irep_idt &require_struct_array_component_assignment(
78+
const irep_idt &structure_name,
79+
const optionalt<irep_idt> &superclass_name,
80+
const irep_idt &array_component_name,
81+
const irep_idt &array_type_name,
82+
const irep_idt &array_component_element_type_name,
83+
const std::vector<codet> &entry_point_instructions);
84+
85+
const irep_idt &require_entry_point_argument_assignment(
86+
const irep_idt &argument_name,
87+
const std::vector<codet> &entry_point_statements);
6488
}
6589

6690
#endif // CPROVER_TESTING_UTILS_REQUIRE_GOTO_STATEMENTS_H

0 commit comments

Comments
 (0)