11
11
12
12
#include < algorithm>
13
13
#include < util/expr_iterator.h>
14
+ #include < goto-programs/goto_functions.h>
15
+ #include < java_bytecode/java_types.h>
14
16
15
17
// / Expand value of a function to include all child codets
16
18
// / \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)
33
35
return statements;
34
36
}
35
37
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)
37
56
// / \param statements: The statements to look through
38
57
// / \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
40
61
// / \return: All the assignments to that component.
41
- std::vector<code_assignt>
62
+ require_goto_statements::pointer_assignment_locationt
42
63
require_goto_statements::find_struct_component_assignments (
43
64
const std::vector<codet> &statements,
44
65
const irep_idt &structure_name,
66
+ const optionalt<irep_idt> &superclass_name,
45
67
const irep_idt &component_name)
46
68
{
47
- std::vector<code_assignt> component_assignments ;
69
+ pointer_assignment_locationt locations ;
48
70
49
71
for (const auto &assignment : statements)
50
72
{
@@ -54,19 +76,69 @@ require_goto_statements::find_struct_component_assignments(
54
76
55
77
if (code_assign.lhs ().id () == ID_member)
56
78
{
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 ());
59
80
60
- if (
61
- symbol.get_identifier () == structure_name &&
62
- member_expr.get_component_name () == component_name)
81
+ if (superclass_name.has_value ())
63
82
{
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
+ }
65
137
}
66
138
}
67
139
}
68
140
}
69
- return component_assignments ;
141
+ return locations ;
70
142
}
71
143
72
144
// / 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(
131
203
}
132
204
throw no_decl_found_exceptiont (variable_name.c_str ());
133
205
}
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
+ }
0 commit comments