Skip to content

Commit 993735b

Browse files
author
Matthias Güdemann
authored
Merge pull request diffblue#2507 from mgudemann/bugfix/java/keep-typecasts-on-stack
Leave typecasts on variables on stack
2 parents d10e44d + a63212e commit 993735b

File tree

4 files changed

+128
-27
lines changed

4 files changed

+128
-27
lines changed
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
.class stack_typecast
2+
.super java/lang/Object
3+
4+
.field public position I
5+
.field public buffer [B
6+
7+
.method public <init>()V
8+
aload_0
9+
invokevirtual java/lang/Object/<init>()V
10+
return
11+
.end method
12+
13+
.method public f()I
14+
.limit stack 6
15+
.limit locals 1
16+
.var 0 is this stack_typecast from begin to end
17+
.line 0
18+
begin:
19+
20+
sipush 255
21+
22+
aload_0
23+
getfield stack_typecast/buffer [B
24+
25+
aload_0
26+
dup
27+
getfield stack_typecast/position I
28+
dup_x1
29+
iconst_1
30+
iadd
31+
putfield stack_typecast/position I
32+
baload
33+
iand
34+
end:
35+
ireturn
36+
37+
.end method
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
CORE
2+
stack_typecast.class
3+
--cover location --function stack_typecast.f
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
covered \(100.0%\)
7+
--
8+
--
9+
This tests that there is no invariant violation when modifying the values on the
10+
stack. in this case the `position` variable is used as index in an array via the
11+
Java equivalent of
12+
13+
buffer[position++];
14+
15+
The code pushes position, duplicates it and the modifies the stack
16+
entry. Before, the stack entry generation removed typecasts which gave a typing
17+
problem with the `iand` operator

jbmc/src/java_bytecode/java_bytecode_convert_method.cpp

+74-27
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Author: Daniel Kroening, [email protected]
3333
#include <util/std_expr.h>
3434
#include <util/string2int.h>
3535
#include <util/string_constant.h>
36+
#include <util/threeval.h>
3637

3738
#include <goto-programs/cfg.h>
3839
#include <goto-programs/class_hierarchy.h>
@@ -3176,44 +3177,90 @@ irep_idt java_bytecode_convert_methodt::get_static_field(
31763177
return inherited_method.get_full_component_identifier();
31773178
}
31783179

3179-
/// create temporary variables if a write instruction can have undesired side-
3180-
/// effects
3180+
/// Create temporary variables if a write instruction can have undesired side-
3181+
/// effects.
3182+
/// \param tmp_var_prefix: The prefix string to use for new temporary variables
3183+
/// \param tmp_var_type: The type of the temporary variable.
3184+
/// \param[out] block: The code block the assignment is added to if required.
3185+
/// \param write_type: The enumeration type of the write instruction.
3186+
/// \param identifier: The identifier of the symbol in the write instruction.
31813187
void java_bytecode_convert_methodt::save_stack_entries(
31823188
const std::string &tmp_var_prefix,
31833189
const typet &tmp_var_type,
31843190
code_blockt &block,
31853191
const bytecode_write_typet write_type,
31863192
const irep_idt &identifier)
31873193
{
3194+
const std::function<bool(
3195+
const std::function<tvt(const exprt &expr)>, const exprt &expr)>
3196+
entry_matches = [&entry_matches](
3197+
const std::function<tvt(const exprt &expr)> predicate,
3198+
const exprt &expr) {
3199+
const tvt &tvres = predicate(expr);
3200+
if(tvres.is_unknown())
3201+
{
3202+
return std::any_of(
3203+
expr.operands().begin(),
3204+
expr.operands().end(),
3205+
[&predicate, &entry_matches](const exprt &expr) {
3206+
return entry_matches(predicate, expr);
3207+
});
3208+
}
3209+
else
3210+
{
3211+
return tvres.is_true();
3212+
}
3213+
};
3214+
3215+
// Function that checks whether the expression accesses a member with the
3216+
// given identifier name. These accesses are created in the case of `iinc`, or
3217+
// non-array `?store` instructions.
3218+
const std::function<tvt(const exprt &expr)> has_member_entry = [&identifier](
3219+
const exprt &expr) {
3220+
const auto member_expr = expr_try_dynamic_cast<member_exprt>(expr);
3221+
return !member_expr ? tvt::unknown()
3222+
: tvt(member_expr->get_component_name() == identifier);
3223+
};
3224+
3225+
// Function that checks whether the expression is a symbol with the given
3226+
// identifier name. These accesses are created in the case of `putstatic` or
3227+
// `putfield` instructions.
3228+
const std::function<tvt(const exprt &expr)> is_symbol_entry =
3229+
[&identifier](const exprt &expr) {
3230+
const auto symbol_expr = expr_try_dynamic_cast<symbol_exprt>(expr);
3231+
return !symbol_expr ? tvt::unknown()
3232+
: tvt(symbol_expr->get_identifier() == identifier);
3233+
};
3234+
3235+
// Function that checks whether the expression is a dereference
3236+
// expression. These accesses are created in `?astore` array write
3237+
// instructions.
3238+
const std::function<tvt(const exprt &expr)> is_dereference_entry =
3239+
[](const exprt &expr) {
3240+
const auto dereference_expr =
3241+
expr_try_dynamic_cast<dereference_exprt>(expr);
3242+
return !dereference_expr ? tvt::unknown() : tvt(true);
3243+
};
3244+
31883245
for(auto &stack_entry : stack)
31893246
{
3190-
// remove typecasts if existing
3191-
while(stack_entry.id()==ID_typecast)
3192-
stack_entry=to_typecast_expr(stack_entry).op();
3193-
3194-
// variables or static fields and symbol -> save symbols with same id
3195-
if((write_type==bytecode_write_typet::VARIABLE ||
3196-
write_type==bytecode_write_typet::STATIC_FIELD) &&
3197-
stack_entry.id()==ID_symbol)
3247+
bool replace = false;
3248+
switch(write_type)
3249+
{
3250+
case bytecode_write_typet::VARIABLE:
3251+
case bytecode_write_typet::STATIC_FIELD:
3252+
replace = entry_matches(is_symbol_entry, stack_entry);
3253+
break;
3254+
case bytecode_write_typet::ARRAY_REF:
3255+
replace = entry_matches(is_dereference_entry, stack_entry);
3256+
break;
3257+
case bytecode_write_typet::FIELD:
3258+
replace = entry_matches(has_member_entry, stack_entry);
3259+
break;
3260+
}
3261+
if(replace)
31983262
{
3199-
const symbol_exprt &var=to_symbol_expr(stack_entry);
3200-
if(var.get_identifier()==identifier)
3201-
create_stack_tmp_var(tmp_var_prefix, tmp_var_type, block, stack_entry);
3202-
}
3203-
3204-
// array reference and dereference -> save all references on the stack
3205-
else if(write_type==bytecode_write_typet::ARRAY_REF &&
3206-
stack_entry.id()==ID_dereference)
32073263
create_stack_tmp_var(tmp_var_prefix, tmp_var_type, block, stack_entry);
3208-
3209-
// field and member access -> compare component name
3210-
else if(write_type==bytecode_write_typet::FIELD &&
3211-
stack_entry.id()==ID_member)
3212-
{
3213-
const irep_idt &entry_id=
3214-
to_member_expr(stack_entry).get_component_name();
3215-
if(entry_id==identifier)
3216-
create_stack_tmp_var(tmp_var_prefix, tmp_var_type, block, stack_entry);
32173264
}
32183265
}
32193266
}

0 commit comments

Comments
 (0)