Skip to content

Commit 7537adc

Browse files
Merge pull request #4870 from romainbrenguier/bugfix/aload
Fix type of data_ptr in convert_aload/astore and useless typecasts
2 parents c7d2ef2 + 2e4099e commit 7537adc

File tree

4 files changed

+332
-11
lines changed

4 files changed

+332
-11
lines changed

jbmc/src/java_bytecode/java_bytecode_convert_method.cpp

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,7 +1328,7 @@ code_blockt java_bytecode_convert_methodt::convert_instructions(
13281328
}
13291329
else if(bytecode == patternt("?astore"))
13301330
{
1331-
assert(op.size()==3 && results.empty());
1331+
PRECONDITION(results.empty());
13321332
c = convert_astore(statement, op, i_it->source_location);
13331333
}
13341334
else if(bytecode == patternt("?store") || bytecode == patternt("?store_?"))
@@ -1340,7 +1340,7 @@ code_blockt java_bytecode_convert_methodt::convert_instructions(
13401340
}
13411341
else if(bytecode == patternt("?aload"))
13421342
{
1343-
PRECONDITION(op.size() == 2 && results.size() == 1);
1343+
PRECONDITION(results.size() == 1);
13441344
results[0] = convert_aload(statement, op);
13451345
}
13461346
else if(bytecode == patternt("?load") || bytecode == patternt("?load_?"))
@@ -2851,16 +2851,37 @@ code_blockt java_bytecode_convert_methodt::convert_ret(
28512851
return c;
28522852
}
28532853

2854+
/// Add typecast if necessary to \p expr to make it compatible with array type
2855+
/// corresponding to \p type_char (see \ref java_array_type(const char)).
2856+
/// Character 'b' is used both for `byte` and `boolean` arrays, so if \p expr
2857+
/// is a boolean array and we are using a `b` operation we can skip the
2858+
/// typecast.
2859+
static exprt conditional_array_cast(const exprt &expr, char type_char)
2860+
{
2861+
const auto ref_type =
2862+
type_try_dynamic_cast<java_reference_typet>(expr.type());
2863+
const bool typecast_not_needed =
2864+
ref_type && ((type_char == 'b' && ref_type->subtype().get_identifier() ==
2865+
"java::array[boolean]") ||
2866+
*ref_type == java_array_type(type_char));
2867+
return typecast_not_needed ? expr
2868+
: typecast_exprt(expr, java_array_type(type_char));
2869+
}
2870+
28542871
exprt java_bytecode_convert_methodt::convert_aload(
28552872
const irep_idt &statement,
2856-
const exprt::operandst &op) const
2873+
const exprt::operandst &op)
28572874
{
2875+
PRECONDITION(op.size() == 2);
28582876
const char type_char = statement[0];
2859-
dereference_exprt deref{typecast_exprt{op[0], java_array_type(type_char)}};
2877+
const exprt op_with_right_type = conditional_array_cast(op[0], type_char);
2878+
dereference_exprt deref{op_with_right_type};
28602879
deref.set(ID_java_member_access, true);
28612880

2862-
member_exprt data_ptr(
2863-
deref, "data", pointer_type(java_type_from_char(type_char)));
2881+
auto java_array_type = type_try_dynamic_cast<struct_tag_typet>(deref.type());
2882+
INVARIANT(java_array_type, "Java array type should be a struct_tag_typet");
2883+
member_exprt data_ptr{
2884+
deref, "data", pointer_type(java_array_element_type(*java_array_type))};
28642885
plus_exprt data_plus_offset{std::move(data_ptr), op[1]};
28652886
// tag it so it's easy to identify during instrumentation
28662887
data_plus_offset.set(ID_java_array_access, true);
@@ -2899,12 +2920,16 @@ code_blockt java_bytecode_convert_methodt::convert_astore(
28992920
const exprt::operandst &op,
29002921
const source_locationt &location)
29012922
{
2923+
PRECONDITION(op.size() == 3);
29022924
const char type_char = statement[0];
2903-
dereference_exprt deref{typecast_exprt{op[0], java_array_type(type_char)}};
2925+
const exprt op_with_right_type = conditional_array_cast(op[0], type_char);
2926+
dereference_exprt deref{op_with_right_type};
29042927
deref.set(ID_java_member_access, true);
29052928

2906-
member_exprt data_ptr(
2907-
deref, "data", pointer_type(java_type_from_char(type_char)));
2929+
auto java_array_type = type_try_dynamic_cast<struct_tag_typet>(deref.type());
2930+
INVARIANT(java_array_type, "Java array type should be a struct_tag_typet");
2931+
member_exprt data_ptr{
2932+
deref, "data", pointer_type(java_array_element_type(*java_array_type))};
29082933
plus_exprt data_plus_offset{std::move(data_ptr), op[1]};
29092934
// tag it so it's easy to identify during instrumentation
29102935
data_plus_offset.set(ID_java_array_access, true);

jbmc/src/java_bytecode/java_bytecode_convert_method_class.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,8 +366,8 @@ class java_bytecode_convert_methodt:public messaget
366366
const method_offsett address,
367367
const source_locationt &location);
368368

369-
exprt
370-
convert_aload(const irep_idt &statement, const exprt::operandst &op) const;
369+
static exprt
370+
convert_aload(const irep_idt &statement, const exprt::operandst &op);
371371

372372
code_blockt convert_ret(
373373
const std::vector<method_offsett> &jsr_ret_targets,
@@ -504,5 +504,7 @@ class java_bytecode_convert_methodt:public messaget
504504
const source_locationt &location);
505505

506506
codet convert_pop(const irep_idt &statement, const exprt::operandst &op);
507+
508+
friend class java_bytecode_convert_method_unit_testt;
507509
};
508510
#endif

jbmc/unit/java_bytecode/java_bytecode_convert_method/convert_method.cpp

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Author: Diffblue Limited.
66
77
\*******************************************************************/
88

9+
#include <testing-utils/expr_query.h>
910
#include <testing-utils/use_catch.h>
1011

1112
#include <util/irep.h>
@@ -14,7 +15,9 @@ Author: Diffblue Limited.
1415
#include <java-testing-utils/load_java_class.h>
1516
#include <java-testing-utils/require_type.h>
1617

18+
#include <java_bytecode/java_bytecode_convert_method_class.h>
1719
#include <java_bytecode/java_utils.h>
20+
#include <testing-utils/message.h>
1821

1922
SCENARIO(
2023
"java_bytecode_convert_bridge_method",
@@ -286,3 +289,232 @@ SCENARIO(
286289
}
287290
}
288291
}
292+
293+
/// Allow access to private methods so that they can be unit tested
294+
class java_bytecode_convert_method_unit_testt
295+
{
296+
public:
297+
static exprt
298+
convert_aload(const irep_idt &statement, const exprt::operandst &op)
299+
{
300+
return java_bytecode_convert_methodt::convert_aload(statement, op);
301+
}
302+
303+
static code_blockt convert_astore(
304+
java_bytecode_convert_methodt &converter,
305+
const irep_idt &statement,
306+
const exprt::operandst &op,
307+
const source_locationt &location)
308+
{
309+
return converter.convert_astore(statement, op, location);
310+
}
311+
};
312+
313+
SCENARIO(
314+
"baload byte array",
315+
"[core][java_bytecode][java_bytecode_convert_method][convert_aload]")
316+
{
317+
GIVEN("A byte array")
318+
{
319+
const typet byte_array_type = java_array_type('b');
320+
const symbol_exprt byte_array{"byte_array", byte_array_type};
321+
const exprt offset = from_integer(1, java_int_type());
322+
WHEN("baload is called on the byte array")
323+
{
324+
const exprt result =
325+
java_bytecode_convert_method_unit_testt::convert_aload(
326+
"baload", {byte_array, offset});
327+
THEN("The Result is of the form `(int)(*(byte_array->data + offset))`")
328+
// See \ref java_bytecode_promotion on why we need a typecast to int.
329+
{
330+
const auto query = make_query(result)
331+
.as<typecast_exprt>()[0]
332+
.as<dereference_exprt>()[0]
333+
.as<plus_exprt>();
334+
REQUIRE(query[1].get() == offset);
335+
auto member = query[0].as<member_exprt>();
336+
REQUIRE(member.get().get_component_name() == "data");
337+
REQUIRE(member[0].as<dereference_exprt>()[0].get() == byte_array);
338+
REQUIRE(result.type() == java_int_type());
339+
// byte_array->data has type *byte
340+
REQUIRE(member.get().type() == pointer_type(java_byte_type()));
341+
}
342+
}
343+
}
344+
}
345+
346+
SCENARIO(
347+
"baload boolean array",
348+
"[core][java_bytecode][java_bytecode_convert_method][convert_aload]")
349+
{
350+
GIVEN("A boolean array")
351+
{
352+
const typet boolean_array_type = java_array_type('z');
353+
const symbol_exprt boolean_array{"boolean_array", boolean_array_type};
354+
const exprt offset = from_integer(2, java_int_type());
355+
WHEN("baload is called on the byte array")
356+
{
357+
const exprt result =
358+
java_bytecode_convert_method_unit_testt::convert_aload(
359+
"baload", {boolean_array, offset});
360+
THEN("The result is of the form `(int)(*(boolean_array->data + offset))`")
361+
// See \ref java_bytecode_promotion on why we need a typecast to int.
362+
{
363+
const auto query = make_query(result)
364+
.as<typecast_exprt>()[0]
365+
.as<dereference_exprt>()[0]
366+
.as<plus_exprt>();
367+
REQUIRE(query[1].get() == offset);
368+
REQUIRE(
369+
query[0].as<member_exprt>()[0].as<dereference_exprt>()[0].get() ==
370+
boolean_array);
371+
// boolean_array->data has type *boolean
372+
REQUIRE(
373+
query[0].as<member_exprt>().get().type() ==
374+
pointer_type(java_boolean_type()));
375+
}
376+
}
377+
}
378+
}
379+
380+
SCENARIO(
381+
"iaload int array",
382+
"[core][java_bytecode][java_bytecode_convert_method][convert_aload]")
383+
{
384+
GIVEN("An int array")
385+
{
386+
const typet int_array_type = java_array_type('i');
387+
const symbol_exprt int_array{"int_array", int_array_type};
388+
const exprt offset = from_integer(2, java_int_type());
389+
WHEN("iaload is called on the int array")
390+
{
391+
const exprt result =
392+
java_bytecode_convert_method_unit_testt::convert_aload(
393+
"iaload", {int_array, offset});
394+
THEN("The result is of the form `*(int_array->data + offset)`")
395+
{
396+
const auto query =
397+
make_query(result).as<dereference_exprt>()[0].as<plus_exprt>();
398+
REQUIRE(query[1].get() == offset);
399+
REQUIRE(
400+
query[0].as<member_exprt>()[0].as<dereference_exprt>()[0].get() ==
401+
int_array);
402+
// int_array->data has type *int
403+
REQUIRE(
404+
query[0].as<member_exprt>().get().type() ==
405+
pointer_type(java_int_type()));
406+
}
407+
}
408+
}
409+
}
410+
411+
SCENARIO(
412+
"astore",
413+
"[core][java_bytecode][java_bytecode_convert_method][convert_astore]")
414+
{
415+
symbol_tablet symbol_table;
416+
java_string_library_preprocesst string_preprocess;
417+
const class_hierarchyt class_hierarchy;
418+
java_bytecode_convert_methodt converter{symbol_table,
419+
null_message_handler,
420+
10,
421+
true,
422+
{},
423+
string_preprocess,
424+
class_hierarchy,
425+
false};
426+
427+
GIVEN("An int array")
428+
{
429+
const source_locationt location;
430+
const typet int_array_type = java_array_type('i');
431+
const symbol_exprt int_array{"int_array", int_array_type};
432+
const exprt offset = from_integer(3, java_int_type());
433+
const exprt value = from_integer(4, java_int_type());
434+
WHEN("iastore is called on the int array")
435+
{
436+
const code_blockt result =
437+
java_bytecode_convert_method_unit_testt::convert_astore(
438+
converter, "iastore", {int_array, offset, value}, location);
439+
THEN(
440+
"The result contains 1 statement of the form `*(int_array->data + 3) = "
441+
"4`")
442+
{
443+
REQUIRE(result.statements().size() == 1);
444+
auto query = make_query(result.statements()[0]).as<code_assignt>();
445+
REQUIRE(query[1].get() == value);
446+
auto plus = query[0].as<dereference_exprt>()[0].as<plus_exprt>();
447+
REQUIRE(plus[1].get() == offset);
448+
REQUIRE(
449+
plus[0].as<member_exprt>().get().get_component_name() == "data");
450+
REQUIRE(
451+
plus[0].as<member_exprt>()[0].as<dereference_exprt>()[0].get() ==
452+
int_array);
453+
THEN("int_array->data has type *int")
454+
{
455+
REQUIRE(
456+
plus[0].as<member_exprt>().get().type() ==
457+
pointer_type(java_int_type()));
458+
}
459+
}
460+
}
461+
}
462+
463+
GIVEN("A boolean array")
464+
{
465+
const source_locationt location;
466+
const typet boolean_array_type = java_array_type('z');
467+
const symbol_exprt boolean_array{"boolean_array", boolean_array_type};
468+
const exprt offset = from_integer(3, java_int_type());
469+
const exprt value = from_integer(true, java_boolean_type());
470+
WHEN("bastore is called on the boolean array")
471+
{
472+
const code_blockt result =
473+
java_bytecode_convert_method_unit_testt::convert_astore(
474+
converter, "bastore", {boolean_array, offset, value}, location);
475+
THEN(
476+
"The result contains 1 statement of the form "
477+
"`*(boolean_array->data + 3) = true`")
478+
{
479+
REQUIRE(result.statements().size() == 1);
480+
auto query = make_query(result.statements()[0]).as<code_assignt>();
481+
REQUIRE(query[1].get() == value);
482+
auto plus = query[0].as<dereference_exprt>()[0].as<plus_exprt>();
483+
REQUIRE(plus[1].get() == offset);
484+
REQUIRE(
485+
plus[0].as<member_exprt>().get().get_component_name() == "data");
486+
REQUIRE(
487+
plus[0].as<member_exprt>()[0].as<dereference_exprt>()[0].get() ==
488+
boolean_array);
489+
THEN("boolean_array->data has type *boolean")
490+
{
491+
REQUIRE(
492+
plus[0].as<member_exprt>().get().type() ==
493+
pointer_type(java_boolean_type()));
494+
}
495+
}
496+
}
497+
WHEN("iastore is called on the boolean array")
498+
{
499+
const code_blockt result =
500+
java_bytecode_convert_method_unit_testt::convert_astore(
501+
converter, "iastore", {boolean_array, offset, value}, location);
502+
THEN(
503+
"The result contains 1 statement of the form "
504+
"`*(((int[])boolean_array)->data + offset)`")
505+
{
506+
REQUIRE(result.statements().size() == 1);
507+
REQUIRE(
508+
make_query(result.statements()[0])
509+
.as<code_assignt>()[0]
510+
.as<dereference_exprt>()[0]
511+
.as<plus_exprt>()[0]
512+
.as<member_exprt>()[0]
513+
.as<dereference_exprt>()[0]
514+
.as<typecast_exprt>()
515+
.get()
516+
.type() == java_array_type('i'));
517+
}
518+
}
519+
}
520+
}

0 commit comments

Comments
 (0)