Skip to content

Commit 2345adc

Browse files
author
owen-jones-diffblue
authored
Merge pull request #4864 from owen-jones-diffblue/dynamically_allocate_java_entry_point_arguments
[TG-8445] Dynamically allocate arguments to java entry point
2 parents 7eb2f2c + 2c36f47 commit 2345adc

File tree

11 files changed

+161
-67
lines changed

11 files changed

+161
-67
lines changed

jbmc/regression/jbmc/internal1/test.desc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ activate-multi-line-match
55
^EXIT=10$
66
^SIGNAL=0$
77
"identifier": "__CPROVER_initialize",\n *"sourceLocation": [{][}]\n *[}],\n *"hidden": false,\n *"internal": true,\n *"stepType": "function-call",
8-
"internal": true,\n *"lhs": "tmp_object_factory[$][0-9]+",
8+
"internal": true,\n *"lhs": "malloc_site[$][0-9]+",
99
"internal": false,\n *"sourceLocation": [{]\n *"file": "com/diffblue/javatest/nestedobjects/subpackage/Order.java",\n *"function": "java::com.diffblue.javatest.nestedobjects.subpackage.Order.setItem:[(]Lcom/diffblue/javatest/nestedobjects/subpackage/Item;[)]Z",\n *"line": "21"\n *[}],\n *"stepType": "function-call",
10-
"internal": false,\n *"lhs": "this",\n *"mode": "java",\n *"sourceLocation": [{]\n *"file": "com/diffblue/javatest/nestedobjects/subpackage/Order.java",\n *"function": "java::com.diffblue.javatest.nestedobjects.subpackage.Order.setItem:[(]Lcom/diffblue/javatest/nestedobjects/subpackage/Item;[)]Z",
10+
"internal": true,\n *"lhs": "this",\n *"mode": "java",\n *"sourceLocation": [{]\n *"file": "com/diffblue/javatest/nestedobjects/subpackage/Order.java",\n *"function": "java::com.diffblue.javatest.nestedobjects.subpackage.Order.setItem:[(]Lcom/diffblue/javatest/nestedobjects/subpackage/Item;[)]Z",
1111
"internal": false,\n *"lhs": "item",\n *"mode": "java",\n *"sourceLocation": [{]\n *"file": "com/diffblue/javatest/nestedobjects/subpackage/Order.java",\n *"function": "java::com.diffblue.javatest.nestedobjects.subpackage.Order.setItem:[(]Lcom/diffblue/javatest/nestedobjects/subpackage/Item;[)]Z",
1212
--
1313
^warning: ignoring
Binary file not shown.
Binary file not shown.
Binary file not shown.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
public class Test {
2+
3+
public static class A {
4+
public int getInteger() {
5+
return 0;
6+
}
7+
}
8+
9+
public static class B extends A {
10+
public int getInteger() {
11+
return 1;
12+
}
13+
}
14+
15+
public static void test(A a) {
16+
// Need to instantiate B to make sure it's loaded
17+
B dummyB = new B();
18+
19+
int zero = a.getInteger();
20+
assert(zero == 2);
21+
}
22+
23+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
CORE
2+
Test.class
3+
--function Test.test --show-vcc
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
--
7+
java::Test\$B\.getInteger
8+
^warning: ignoring
9+
--
10+
Symex should have the values of all class_identifier fields in its constant
11+
propagation map, so that it can run efficiently and create a simpler equation.
12+
If an argument to the entrypoint is nondet-initialised using automatic local
13+
initialisation then symex thinks its class identifier might be uninitialised,
14+
and hence could be anything, because that's what happens in the null case (but
15+
we never access the class identifier in this case). Hence we have switched to
16+
dynamic allocation for this situation.

jbmc/src/java_bytecode/java_entry_point.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ std::pair<code_blockt, std::vector<exprt>> java_build_arguments(
351351
init_code,
352352
symbol_table,
353353
factory_parameters,
354-
lifetimet::AUTOMATIC_LOCAL,
354+
lifetimet::DYNAMIC,
355355
function.location,
356356
pointer_type_selector,
357357
message_handler);

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

Lines changed: 100 additions & 51 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
@@ -462,26 +513,24 @@ require_goto_statements::require_entry_point_argument_assignment(
462513
{
463514
// Trace the creation of the object that is being supplied as the input
464515
// argument to the function under test
465-
const pointer_assignment_locationt &argument_assignments =
516+
const pointer_assignment_locationt argument_assignments =
466517
find_pointer_assignments(
467-
id2string(goto_functionst::entry_point()) + "::" +
468-
id2string(argument_name),
518+
id2string(goto_functionst::entry_point()) +
519+
"::" + id2string(argument_name),
469520
entry_point_statements);
470521

471522
// There should be at most one assignment to it
472523
REQUIRE(argument_assignments.non_null_assignments.size() == 1);
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)