Skip to content

Corrects integer/long conversion to string and add tests for StringBuilder.append(long) #1033

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

7 changes: 5 additions & 2 deletions regression/strings-smoke-tests/java_int_to_string/test.desc
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
CORE
test_int.class
--refine-strings
^EXIT=0$
^EXIT=10$
^SIGNAL=0$
^VERIFICATION SUCCESSFUL$
assertion.* file test_int.java line 6 .* SUCCESS$
assertion.* file test_int.java line 9 .* SUCCESS$
assertion.* file test_int.java line 11 .* FAILURE$
assertion.* file test_int.java line 13 .* FAILURE$
--
Binary file modified regression/strings-smoke-tests/java_int_to_string/test_int.class
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
public class test_int
{
public static void main(/*String[] argv*/)
public static void main(int i)
{
String s = Integer.toString(12);
assert(s.equals("12"));
String t = Integer.toString(-23);
System.out.println(t);
assert(t.equals("-23"));
if(i==1)
assert(!s.equals("12"));
else if(i==2)
assert(!t.equals("-23"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question as above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are already assertions which are supposed to pass above.

}
}
Binary file not shown.
8 changes: 8 additions & 0 deletions regression/strings-smoke-tests/java_value_of_long/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CORE
test.class
--refine-strings
^EXIT=10$
^SIGNAL=0$
assertion.* file test.java line 9 .* SUCCESS$
assertion.* file test.java line 14 .* FAILURE$
--
17 changes: 17 additions & 0 deletions regression/strings-smoke-tests/java_value_of_long/test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
public class test
{
public static void main(int i)
{
long l1=12345678901234L;
if(i == 0)
{
String s = String.valueOf(l1);
assert(s.equals("12345678901234"));
}
else
{
String s = String.valueOf(-l1);
assert(!s.equals("-12345678901234"));
}
}
}
35 changes: 24 additions & 11 deletions src/solvers/refinement/string_constraint_generator_valueof.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,16 +209,17 @@ string_exprt string_constraint_generatort::add_axioms_from_bool(

/// add axioms to say the string corresponds to the result of String.valueOf(I)
/// or String.valueOf(J) java functions applied on the integer expression
/// \par parameters: a signed integer expression, and a maximal size for the
/// string
/// representation
/// \param i: a signed integer expression
/// \param max_size: a maximal size for the string representation
/// \param ref_type: type for refined strings
/// \return a string expression
string_exprt string_constraint_generatort::add_axioms_from_int(
const exprt &i, size_t max_size, const refined_string_typet &ref_type)
{
PRECONDITION(i.type().id()==ID_signedbv);
PRECONDITION(max_size<std::numeric_limits<size_t>::max());
string_exprt res=fresh_string(ref_type);
const typet &type=i.type();
assert(type.id()==ID_signedbv);
exprt ten=from_integer(10, type);
const typet &char_type=ref_type.get_char_type();
const typet &index_type=ref_type.get_index_type();
Expand Down Expand Up @@ -262,12 +263,20 @@ string_exprt string_constraint_generatort::add_axioms_from_int(
not_exprt(equal_exprt(res[1], zero_char)));
axioms.push_back(a4);

assert(max_size<std::numeric_limits<size_t>::max());

for(size_t size=1; size<=max_size; size++)
{
// For each possible size, we add axioms:
// a5 : forall 1 <= j < size. '0' <= res[j] <= '9' && sum == 10 * (sum/10)
// For each possible size, we have:
// sum_0 = starts_with_digit ? res[0]-'0' : 0
// sum_j = 10 * sum_{j-1} + res[i] - '0'
// and add axioms:
// a5 : |res| == size =>
// forall 1 <= j < size.
// is_number && (j >= max_size-2 => no_overflow)
// where is_number := '0' <= res[j] <= '9'
// and no_overflow := sum_{j-1} = (10 * sum_{j-1} / 10)
// && sum_j >= sum_{j - 1}
// (the first part avoid overflows in multiplication and
// the second one in additions)
// a6 : |res| == size && '0' <= res[0] <= '9' => i == sum
// a7 : |res| == size && res[0] == '-' => i == -sum

Expand All @@ -288,7 +297,11 @@ string_exprt string_constraint_generatort::add_axioms_from_int(
binary_relation_exprt(res[j], ID_le, nine_char));
digit_constraints.push_back(is_number);

if(j>=max_size-1)
// An overflow can happen when reaching the last index of a string of
// maximal size which is `max_size` for negative numbers and
// `max_size - 1` for positive numbers because of the abscence of a `-`
// sign.
if(j>=max_size-2)
{
// check for overflows if the size is big
and_exprt no_overflow(
Expand All @@ -299,10 +312,10 @@ string_exprt string_constraint_generatort::add_axioms_from_int(
sum=new_sum;
}

exprt a5=conjunction(digit_constraints);
equal_exprt premise=res.axiom_for_has_length(size);
implies_exprt a5(premise, conjunction(digit_constraints));
axioms.push_back(a5);

equal_exprt premise=res.axiom_for_has_length(size);
implies_exprt a6(
and_exprt(premise, starts_with_digit), equal_exprt(i, sum));
axioms.push_back(a6);
Expand Down