Skip to content

Commit f69b244

Browse files
authored
Merge pull request #2625 from smowton/smowton/feature/value-set-accuracy
Value set: handle with, array and struct expressions more accurately
2 parents 730b3e2 + 943d60c commit f69b244

File tree

7 files changed

+451
-43
lines changed

7 files changed

+451
-43
lines changed
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
public class Cart {
2+
3+
class Product {
4+
public String size;
5+
public Category cat;
6+
}
7+
8+
class Category {
9+
public String name;
10+
}
11+
12+
public boolean checkTax4(Product product, String s) {
13+
product.size="abc";
14+
return s.startsWith(product.cat.name);
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
Cart.class
3+
--cp `../../../../scripts/format_classpath.sh . ../../../src/java_bytecode/library/core-models.jar` --trace --cover location --java-max-vla-length 96 --refine-strings --java-unwind-enum-static --max-nondet-string-length 200 --unwind 4 Cart.class --function Cart.checkTax4 --string-printable
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
checkTax4:.*: SATISFIED
7+
--
8+
checkTax4:.*bytecode-index 8.*: FAILED
9+
dec_solve: current index set is empty, this should not happen
10+
--
11+
This checks that assigning to a field of an object (which generates a WITH expression in this case) doesn't result
12+
in conflating the String-typed and non-String-typed fields of the Cart class. When they are conflated we get
13+
warnings from the string solver and missing coverage.

src/pointer-analysis/value_set.cpp

+108-43
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,30 @@ void value_sett::get_value_set(
344344
get_value_set_rec(tmp, dest, "", tmp.type(), ns);
345345
}
346346

347+
/// Check if 'suffix' starts with 'field'.
348+
/// Suffix is delimited by periods and '[]' (array access) tokens, so we're
349+
/// looking for ".field($|[]|.)"
350+
static bool suffix_starts_with_field(
351+
const std::string &suffix, const std::string &field)
352+
{
353+
return
354+
!suffix.empty() &&
355+
suffix[0] == '.' &&
356+
suffix.compare(1, field.length(), field) == 0 &&
357+
(suffix.length() == field.length() + 1 ||
358+
suffix[field.length() + 1] == '.' ||
359+
suffix[field.length() + 1] == '[');
360+
}
361+
362+
static std::string strip_first_field_from_suffix(
363+
const std::string &suffix, const std::string &field)
364+
{
365+
INVARIANT(
366+
suffix_starts_with_field(suffix, field),
367+
"suffix should start with " + field);
368+
return suffix.substr(field.length() + 1);
369+
}
370+
347371
void value_sett::get_value_set_rec(
348372
const exprt &expr,
349373
object_mapt &dest,
@@ -711,72 +735,113 @@ void value_sett::get_value_set_rec(
711735
}
712736
else if(expr.id()==ID_struct)
713737
{
738+
const auto &struct_components = to_struct_type(expr_type).components();
739+
INVARIANT(
740+
struct_components.size() == expr.operands().size(),
741+
"struct expression should have an operand per component");
742+
auto component_iter = struct_components.begin();
743+
bool found_component = false;
744+
714745
// a struct constructor, which may contain addresses
746+
715747
forall_operands(it, expr)
716-
get_value_set_rec(*it, dest, suffix, original_type, ns);
748+
{
749+
const std::string &component_name =
750+
id2string(component_iter->get_name());
751+
if(suffix_starts_with_field(suffix, component_name))
752+
{
753+
std::string remaining_suffix =
754+
strip_first_field_from_suffix(suffix, component_name);
755+
get_value_set_rec(*it, dest, remaining_suffix, original_type, ns);
756+
found_component = true;
757+
}
758+
++component_iter;
759+
}
760+
761+
if(!found_component)
762+
{
763+
// Struct field doesn't appear as expected -- this has probably been
764+
// cast from an incompatible type. Conservatively assume all fields may
765+
// be of interest.
766+
forall_operands(it, expr)
767+
get_value_set_rec(*it, dest, suffix, original_type, ns);
768+
}
717769
}
718770
else if(expr.id()==ID_with)
719771
{
720-
assert(expr.operands().size()==3);
721-
722-
// this is the array/struct
723-
object_mapt tmp_map0;
724-
get_value_set_rec(expr.op0(), tmp_map0, suffix, original_type, ns);
772+
const with_exprt &with_expr = to_with_expr(expr);
725773

726-
// this is the update value -- note NO SUFFIX
727-
object_mapt tmp_map2;
728-
get_value_set_rec(expr.op2(), tmp_map2, "", original_type, ns);
729-
730-
if(expr_type.id()==ID_struct)
774+
// If the suffix is empty we're looking for the whole struct:
775+
// default to combining both options as below.
776+
if(expr_type.id() == ID_struct && !suffix.empty())
731777
{
732-
#if 0
733-
const object_map_dt &object_map0=tmp_map0.read();
734-
irep_idt component_name=expr.op1().get(ID_component_name);
735-
736-
bool insert=true;
737-
738-
for(object_map_dt::const_iterator
739-
it=object_map0.begin();
740-
it!=object_map0.end();
741-
it++)
778+
irep_idt component_name = with_expr.where().get(ID_component_name);
779+
if(suffix_starts_with_field(suffix, id2string(component_name)))
742780
{
743-
const exprt &e=to_expr(it);
744-
745-
if(e.id()==ID_member &&
746-
e.get(ID_component_name)==component_name)
747-
{
748-
if(insert)
749-
{
750-
dest.write().insert(tmp_map2.read().begin(), tmp_map2.read().end());
751-
insert=false;
752-
}
753-
}
754-
else
755-
dest.write().insert(*it);
781+
// Looking for the member overwritten by this WITH expression
782+
std::string remaining_suffix =
783+
strip_first_field_from_suffix(suffix, id2string(component_name));
784+
get_value_set_rec(
785+
with_expr.new_value(), dest, remaining_suffix, original_type, ns);
786+
}
787+
else if(to_struct_type(expr_type).has_component(component_name))
788+
{
789+
// Looking for a non-overwritten member, look through this expression
790+
get_value_set_rec(with_expr.old(), dest, suffix, original_type, ns);
791+
}
792+
else
793+
{
794+
// Member we're looking for is not defined in this struct -- this
795+
// must be a reinterpret cast of some sort. Default to conservatively
796+
// assuming either operand might be involved.
797+
get_value_set_rec(expr.op0(), dest, suffix, original_type, ns);
798+
get_value_set_rec(expr.op2(), dest, "", original_type, ns);
756799
}
757-
#else
758-
// Should be more precise! We only want "suffix"
759-
make_union(dest, tmp_map0);
760-
make_union(dest, tmp_map2);
761-
#endif
800+
}
801+
else if(expr_type.id() == ID_array && !suffix.empty())
802+
{
803+
std::string new_value_suffix;
804+
if(has_prefix(suffix, "[]"))
805+
new_value_suffix = suffix.substr(2);
806+
807+
// Otherwise use a blank suffix on the assumption anything involved with
808+
// the new value might be interesting.
809+
810+
get_value_set_rec(with_expr.old(), dest, suffix, original_type, ns);
811+
get_value_set_rec(
812+
with_expr.new_value(), dest, new_value_suffix, original_type, ns);
762813
}
763814
else
764815
{
765-
make_union(dest, tmp_map0);
766-
make_union(dest, tmp_map2);
816+
// Something else-- the suffixes used here are a rough guess at best,
817+
// so this is imprecise.
818+
get_value_set_rec(expr.op0(), dest, suffix, original_type, ns);
819+
get_value_set_rec(expr.op2(), dest, "", original_type, ns);
767820
}
768821
}
769822
else if(expr.id()==ID_array)
770823
{
771824
// an array constructor, possibly containing addresses
825+
std::string new_suffix = suffix;
826+
if(has_prefix(suffix, "[]"))
827+
new_suffix = suffix.substr(2);
828+
// Otherwise we're probably reinterpreting some other type -- try persisting
829+
// with the current suffix for want of a better idea.
830+
772831
forall_operands(it, expr)
773-
get_value_set_rec(*it, dest, suffix, original_type, ns);
832+
get_value_set_rec(*it, dest, new_suffix, original_type, ns);
774833
}
775834
else if(expr.id()==ID_array_of)
776835
{
777836
// an array constructor, possibly containing an address
837+
std::string new_suffix = suffix;
838+
if(has_prefix(suffix, "[]"))
839+
new_suffix = suffix.substr(2);
840+
// Otherwise we're probably reinterpreting some other type -- try persisting
841+
// with the current suffix for want of a better idea.
842+
778843
assert(expr.operands().size()==1);
779-
get_value_set_rec(expr.op0(), dest, suffix, original_type, ns);
844+
get_value_set_rec(expr.op0(), dest, new_suffix, original_type, ns);
780845
}
781846
else if(expr.id()==ID_dynamic_object)
782847
{

0 commit comments

Comments
 (0)