Skip to content

Commit bebaf01

Browse files
author
Owen
committed
Make java generics unit tests work again
They inspect the exact code produced very closely, so they need to be updated to expect code with dynamic allocations instead of automatic local allocations in some places
1 parent 4c6c4a2 commit bebaf01

File tree

4 files changed

+116
-61
lines changed

4 files changed

+116
-61
lines changed

jbmc/unit/java-testing-utils/require_goto_statements.cpp

Lines changed: 97 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ require_goto_statements::find_struct_component_assignments(
7272
const irep_idt &component_name,
7373
const symbol_tablet &symbol_table)
7474
{
75-
pointer_assignment_locationt locations;
75+
pointer_assignment_locationt locations{};
7676

7777
for(const auto &assignment : statements)
7878
{
@@ -106,8 +106,8 @@ require_goto_statements::find_struct_component_assignments(
106106
ode.build(superclass_expr, ns);
107107
if(
108108
superclass_expr.get_component_name() == supercomponent_name &&
109-
to_symbol_expr(ode.root_object()).get_identifier() ==
110-
structure_name)
109+
to_symbol_expr(to_dereference_expr(ode.root_object()).pointer())
110+
.get_identifier() == structure_name)
111111
{
112112
if(
113113
code_assign.rhs() ==
@@ -133,10 +133,9 @@ require_goto_statements::find_struct_component_assignments(
133133
const namespacet ns(symbol_table);
134134
ode.build(member_expr, ns);
135135
if(
136-
ode.root_object().id() == ID_symbol &&
137-
to_symbol_expr(ode.root_object()).get_identifier() ==
138-
structure_name &&
139-
member_expr.get_component_name() == component_name)
136+
member_expr.get_component_name() == component_name &&
137+
to_symbol_expr(to_dereference_expr(ode.root_object()).pointer())
138+
.get_identifier() == structure_name)
140139
{
141140
if(
142141
code_assign.rhs() ==
@@ -295,6 +294,73 @@ const code_declt &require_goto_statements::require_declaration_of_name(
295294
throw no_decl_found_exceptiont(variable_name.c_str());
296295
}
297296

297+
/// Get the unique non-null expression assigned to a symbol. The symbol may have
298+
/// many null assignments, but only one non-null assignment.
299+
/// \param entry_point_instructions: A vector of instructions
300+
/// \param symbol_identifier: The identifier of the symbol we are considering
301+
/// \return The unique non-null expression assigned to the symbol
302+
const exprt &get_unique_non_null_expression_assigned_to_symbol(
303+
const std::vector<codet> &entry_point_instructions,
304+
const irep_idt &symbol_identifier)
305+
{
306+
const auto &assignments = require_goto_statements::find_pointer_assignments(
307+
symbol_identifier, entry_point_instructions)
308+
.non_null_assignments;
309+
REQUIRE(assignments.size() == 1);
310+
return assignments[0].rhs();
311+
}
312+
313+
/// Get the unique symbol assigned to a symbol, if one exists. There must be
314+
/// a unique non-null assignment to the symbol, and it is either another symbol,
315+
/// in which case we return that symbol expression, or something else, which
316+
/// case we return a null pointer.
317+
/// \param entry_point_instructions: A vector of instructions
318+
/// \param symbol_identifier: The identifier of the symbol
319+
/// \return The unique symbol assigned to \p input_symbol_identifier, or a null
320+
/// pointer if no symbols are assigned to it
321+
const symbol_exprt *try_get_unique_symbol_assigned_to_symbol(
322+
const std::vector<codet> &entry_point_instructions,
323+
const irep_idt &symbol_identifier)
324+
{
325+
const auto &expr = get_unique_non_null_expression_assigned_to_symbol(
326+
entry_point_instructions, symbol_identifier);
327+
328+
return expr_try_dynamic_cast<symbol_exprt>(skip_typecast(expr));
329+
}
330+
331+
/// Follow the chain of non-null assignments until we find a symbol that
332+
/// hasn't ever had another symbol assigned to it. For example, if this code is
333+
/// ```
334+
/// a = 5 + g(7)
335+
/// b = a
336+
/// c = b
337+
/// ```
338+
/// then given input c we return a.
339+
/// \param entry_point_instructions: A vector of instructions
340+
/// \param input_symbol_identifier: The identifier of the symbol we are
341+
/// currently considering
342+
/// \return The identifier of the symbol which is (possibly indirectly) assigned
343+
/// to \p input_symbol_identifier and which does not have any symbol assigned
344+
/// to it
345+
static const irep_idt &
346+
get_ultimate_source_symbol(
347+
const std::vector<codet> &entry_point_instructions,
348+
const irep_idt &input_symbol_identifier)
349+
{
350+
const symbol_exprt *symbol_assigned_to_input_symbol =
351+
try_get_unique_symbol_assigned_to_symbol(
352+
entry_point_instructions, input_symbol_identifier);
353+
354+
if(symbol_assigned_to_input_symbol)
355+
{
356+
return get_ultimate_source_symbol(
357+
entry_point_instructions,
358+
symbol_assigned_to_input_symbol->get_identifier());
359+
}
360+
361+
return input_symbol_identifier;
362+
}
363+
298364
/// Checks that the component of the structure (possibly inherited from
299365
/// the superclass) is assigned an object of the given type.
300366
/// \param structure_name: The name the variable
@@ -305,7 +371,9 @@ const code_declt &require_goto_statements::require_declaration_of_name(
305371
/// there is a typecast)
306372
/// \param entry_point_instructions: The statements to look through
307373
/// \param symbol_table: A symbol table to enable type lookups
308-
/// \return The identifier of the variable assigned to the field
374+
/// \return The identifier of the ultimate source symbol assigned to the field,
375+
/// which will be used for future calls to
376+
/// `require_struct_component_assignment`.
309377
const irep_idt &require_goto_statements::require_struct_component_assignment(
310378
const irep_idt &structure_name,
311379
const optionalt<irep_idt> &superclass_name,
@@ -331,48 +399,31 @@ const irep_idt &require_goto_statements::require_struct_component_assignment(
331399
<< structure_name);
332400
REQUIRE(component_assignments.non_null_assignments.size() == 1);
333401

334-
// We are expecting that the resulting statement can be of the form:
335-
// 1. structure_name.(@superclass_name if given).component =
336-
// (optional type cast *) tmp_object_factory$1;
337-
// followed by a direct assignment like this:
338-
// tmp_object_factory$1 = &tmp_object_factory$2;
339-
// 2. structure_name.component = (optional cast *)&tmp_object_factory$1
340-
exprt component_assignment_rhs_expr =
341-
skip_typecast(component_assignments.non_null_assignments[0].rhs());
342-
343-
// If the rhs is not an address of must be in case 1
344-
if(!can_cast_expr<address_of_exprt>(component_assignment_rhs_expr))
345-
{
346-
const auto &component_reference_tmp_name =
347-
to_symbol_expr(component_assignment_rhs_expr).get_identifier();
348-
const auto &component_reference_assignments =
349-
require_goto_statements::find_pointer_assignments(
350-
component_reference_tmp_name, entry_point_instructions)
351-
.non_null_assignments;
352-
REQUIRE(component_reference_assignments.size() == 1);
353-
component_assignment_rhs_expr =
354-
skip_typecast(component_reference_assignments[0].rhs());
355-
}
402+
// We are expecting the non-null assignment to be of the form:
403+
// malloc_site->(@superclass_name if given.)field =
404+
// (possible typecast) malloc_site$0;
405+
const symbol_exprt *rhs_symbol_expr = expr_try_dynamic_cast<symbol_exprt>(
406+
skip_typecast(component_assignments.non_null_assignments[0].rhs()));
407+
REQUIRE(rhs_symbol_expr);
356408

357-
// The rhs assigns an address of a variable, get its name
358-
const auto &component_reference_assignment_rhs =
359-
to_address_of_expr(component_assignment_rhs_expr);
360-
const auto &component_tmp_name =
361-
to_symbol_expr(component_reference_assignment_rhs.op()).get_identifier();
409+
const irep_idt &symbol_identifier = get_ultimate_source_symbol(
410+
entry_point_instructions, rhs_symbol_expr->get_identifier());
362411

363412
// After we have found the declaration of the final assignment's
364413
// right hand side, then we want to identify that the type
365414
// is the one we expect, e.g.:
366-
// struct java.lang.Integer tmp_object_factory$2;
415+
// struct java.lang.Integer *malloc_site$0;
367416
const auto &component_declaration =
368417
require_goto_statements::require_declaration_of_name(
369-
component_tmp_name, entry_point_instructions);
370-
REQUIRE(component_declaration.symbol().type().id() == ID_struct_tag);
418+
symbol_identifier, entry_point_instructions);
419+
const typet &component_type =
420+
to_pointer_type(component_declaration.symbol().type()).subtype();
421+
REQUIRE(component_type.id() == ID_struct_tag);
371422
const auto &component_struct =
372-
ns.follow_tag(to_struct_tag_type(component_declaration.symbol().type()));
423+
ns.follow_tag(to_struct_tag_type(component_type));
373424
REQUIRE(component_struct.get(ID_name) == component_type_name);
374425

375-
return component_tmp_name;
426+
return symbol_identifier;
376427
}
377428

378429
/// Checks that the array component of the structure (possibly inherited from
@@ -473,15 +524,13 @@ require_goto_statements::require_entry_point_argument_assignment(
473524

474525
// The following finds the name of the tmp object that gets assigned
475526
// to the input argument. There must be one such assignment only,
476-
// and usually looks like this:
477-
// argument_name = &tmp_object_factory$1;
527+
// and it usually looks like this:
528+
// argument_name = tmp_object_factory$1;
478529
const auto &argument_assignment =
479530
argument_assignments.non_null_assignments[0];
480-
const auto &argument_tmp_name =
481-
to_symbol_expr(
482-
to_address_of_expr(skip_typecast(argument_assignment.rhs())).object())
483-
.get_identifier();
484-
return argument_tmp_name;
531+
const symbol_exprt &argument_symbol =
532+
to_symbol_expr(skip_typecast(argument_assignment.rhs()));
533+
return argument_symbol.get_identifier();
485534
}
486535

487536
/// Verify that a collection of statements contains a function call to a
@@ -512,6 +561,6 @@ std::vector<code_function_callt> require_goto_statements::find_function_calls(
512561
}
513562
}
514563
}
515-
}
564+
}
516565
return function_calls;
517566
}

jbmc/unit/java_bytecode/goto_program_generics/generic_bases_test.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,8 @@ SCENARIO(
442442
"Ignore generics for incomplete and non-generic bases",
443443
"[core][goto_program_generics][generic_bases_test]")
444444
{
445+
config.ansi_c.set_LP64();
446+
445447
GIVEN(
446448
"A class extending a generic class with unsupported class signature (thus"
447449
" not marked as generic)")
@@ -469,17 +471,16 @@ SCENARIO(
469471
// We trace the creation of the object that is being supplied as
470472
// the input to the method under test. There must be one non-null
471473
// assignment only, and usually looks like this:
472-
// this = &tmp_object_factory$1;
474+
// this = malloc_site;
473475
const irep_idt &this_tmp_name =
474476
require_goto_statements::require_entry_point_argument_assignment(
475477
ID_this, entry_point_code);
476478

477479
THEN("Object 'this' created has unspecialized inherited field")
478480
{
479-
480-
// &tmp_object_factory$2;
481-
// struct java.lang.Object { __CPROVER_string @class_identifier; }
482-
// tmp_object_factory$2;
481+
// Check that entry_point_code contains an instruction of the form
482+
// malloc_site->@UnsupportedWrapper1.field = <symbol>;
483+
// where <symbol> has type `struct java.lang.Object *`
483484
require_goto_statements::require_struct_component_assignment(
484485
this_tmp_name,
485486
{"UnsupportedWrapper1"},

jbmc/unit/java_bytecode/goto_program_generics/generic_parameters_test.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ SCENARIO(
2121
"Instantiate generic parameters to methods or fields used within",
2222
"[core][goto_program_generics][generic_parameters_test]")
2323
{
24+
config.ansi_c.set_LP64();
25+
2426
GIVEN("A class with a generic field")
2527
{
2628
const symbol_tablet &symbol_table = load_java_class(
@@ -316,8 +318,8 @@ SCENARIO(
316318

317319
// Trace the assignments back to the declaration of the generic type
318320
// and verify that it is what we expect.
319-
const auto &tmp_object_struct_tag =
320-
to_struct_tag_type(tmp_object_declaration.symbol().type());
321+
const auto &tmp_object_struct_tag = to_struct_tag_type(
322+
to_pointer_type(tmp_object_declaration.symbol().type()).subtype());
321323
REQUIRE(tmp_object_struct_tag.get_identifier() == "java::Wrapper");
322324

323325
THEN("Object 'v' has field 'field' of type IWrapper")
@@ -366,8 +368,8 @@ SCENARIO(
366368

367369
// Trace the assignments back to the declaration of the generic type
368370
// and verify that it is what we expect.
369-
const auto &tmp_object_struct_tag =
370-
to_struct_tag_type(tmp_object_declaration.symbol().type());
371+
const auto &tmp_object_struct_tag = to_struct_tag_type(
372+
to_pointer_type(tmp_object_declaration.symbol().type()).subtype());
371373
REQUIRE(tmp_object_struct_tag.get_identifier() == "java::Wrapper");
372374

373375
THEN(
@@ -416,8 +418,8 @@ SCENARIO(
416418

417419
// Trace the assignments back to the declaration of the generic type
418420
// and verify that it is what we expect.
419-
const auto &tmp_object_struct_tag =
420-
to_struct_tag_type(tmp_object_declaration.symbol().type());
421+
const auto &tmp_object_struct_tag = to_struct_tag_type(
422+
to_pointer_type(tmp_object_declaration.symbol().type()).subtype());
421423
REQUIRE(
422424
tmp_object_struct_tag.get_identifier() ==
423425
"java::GenericFields$GenericInnerOuter$Outer");
@@ -481,8 +483,8 @@ SCENARIO(
481483

482484
// Trace the assignments back to the declaration of the generic type
483485
// and verify that it is what we expect.
484-
const auto &tmp_object_struct_tag =
485-
to_struct_tag_type(tmp_object_declaration.symbol().type());
486+
const auto &tmp_object_struct_tag = to_struct_tag_type(
487+
to_pointer_type(tmp_object_declaration.symbol().type()).subtype());
486488
REQUIRE(
487489
tmp_object_struct_tag.get_identifier() ==
488490
"java::GenericFields$GenericRewriteParameter$A");

jbmc/unit/java_bytecode/goto_program_generics/mutually_recursive_generics.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ Author: Diffblue Ltd.
1010
#include <java-testing-utils/require_goto_statements.h>
1111
#include <java-testing-utils/require_type.h>
1212
#include <testing-utils/use_catch.h>
13+
#include <util/config.h>
1314

1415
SCENARIO(
1516
"Generics class with mutually recursive_generic parameters",
1617
"[core][java_bytecode][goto_programs_generics]")
1718
{
19+
config.ansi_c.set_LP64();
20+
1821
const symbol_tablet &symbol_table = load_java_class(
1922
"MutuallyRecursiveGenerics",
2023
"./java_bytecode/goto_program_generics",

0 commit comments

Comments
 (0)