Skip to content

Commit 287f0b7

Browse files
Merge pull request #20 from diffblue/feature/string-format
CProverString.format definition and String.format model using it
2 parents d92026c + 077484f commit 287f0b7

File tree

2 files changed

+81
-5
lines changed

2 files changed

+81
-5
lines changed

src/main/java/java/lang/String.java

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3884,6 +3884,50 @@ public char[] toCharArray() {
38843884
return result;
38853885
}
38863886

3887+
/**
3888+
* Helper function for the {@code format} function.
3889+
* Serialize potential String.format argument to a String.
3890+
* The returned String is never null.
3891+
*/
3892+
static String cproverFormatArgument(Object obj) {
3893+
if (obj == null) {
3894+
return "null";
3895+
}
3896+
if (obj instanceof String) {
3897+
return (String) obj;
3898+
}
3899+
3900+
// All primitive types are cast to a long
3901+
long longValue = 0;
3902+
if (obj instanceof Integer) {
3903+
longValue = (Integer) obj;
3904+
} else if (obj instanceof Long) {
3905+
longValue = (Long) obj;
3906+
} else if (obj instanceof Float) {
3907+
longValue = (long) ((Float) obj).doubleValue();
3908+
} else if (obj instanceof Double) {
3909+
longValue = (long) ((Double) obj).doubleValue();
3910+
} else if (obj instanceof Character) {
3911+
char charValue = (Character) obj;
3912+
longValue = (long) charValue;
3913+
} else if (obj instanceof Byte) {
3914+
byte byteValue = ((Byte) obj);
3915+
longValue = (long) byteValue;
3916+
} else if (obj instanceof Boolean) {
3917+
longValue = ((Boolean) obj) ? 1 : 0;
3918+
} else {
3919+
return CProver.nondetWithoutNull("");
3920+
}
3921+
3922+
// The long value is encoded using a string of 4 characters
3923+
StringBuilder builder = new StringBuilder();
3924+
builder.append((char) (longValue >> 48 & 0xFFFF));
3925+
builder.append((char) (longValue >> 32 & 0xFFFF));
3926+
builder.append((char) (longValue >> 16 & 0xFFFF));
3927+
builder.append((char) (longValue & 0xFFFF));
3928+
return builder.toString();
3929+
}
3930+
38873931
/**
38883932
* Returns a formatted string using the specified format string and
38893933
* arguments.
@@ -3942,13 +3986,32 @@ public char[] toCharArray() {
39423986
* <li>
39433987
* having 5 arguments or more makes the solver slow
39443988
* </li>
3989+
* <li>
3990+
* the string "null" is interpreted the same way {@code null} would be by
3991+
* the JDK String.format function, which is correct for the %s format
3992+
* specifier but not %b for instance.
3993+
* </li>
39453994
* </ul>
39463995
* @diffblue.untested
39473996
*/
39483997
public static String format(String format, Object... args) {
3949-
// DIFFBLUE MODEL LIBRARY This is treated internally in CBMC
3950-
return CProver.nondetWithoutNullForNotModelled();
39513998
// return new Formatter().format(format, args).toString();
3999+
// DIFFBLUE MODEL LIBRARY
4000+
if (args.length > 10) {
4001+
return CProver.nondetWithoutNull("");
4002+
}
4003+
String arg0 = args.length > 0 ? cproverFormatArgument(args[0]) : "";
4004+
String arg1 = args.length > 1 ? cproverFormatArgument(args[1]) : "";
4005+
String arg2 = args.length > 2 ? cproverFormatArgument(args[2]) : "";
4006+
String arg3 = args.length > 3 ? cproverFormatArgument(args[3]) : "";
4007+
String arg4 = args.length > 4 ? cproverFormatArgument(args[4]) : "";
4008+
String arg5 = args.length > 5 ? cproverFormatArgument(args[5]) : "";
4009+
String arg6 = args.length > 6 ? cproverFormatArgument(args[6]) : "";
4010+
String arg7 = args.length > 7 ? cproverFormatArgument(args[7]) : "";
4011+
String arg8 = args.length > 8 ? cproverFormatArgument(args[8]) : "";
4012+
String arg9 = args.length > 9 ? cproverFormatArgument(args[9]) : "";
4013+
return CProverString.format(format, arg0, arg1, arg2, arg3, arg4, arg5,
4014+
arg6, arg7, arg8, arg9);
39524015
}
39534016

39544017
/**
@@ -3988,12 +4051,12 @@ public static String format(String format, Object... args) {
39884051
* @see java.util.Formatter
39894052
* @since 1.5
39904053
*
3991-
* @diffblue.noSupport
4054+
* @diffblue.limitedSupport The locale argument is ignored and it has the
4055+
* same limitations as {@link #format(String, Object...)}.
39924056
*/
39934057
public static String format(Locale l, String format, Object... args) {
39944058
// return new Formatter(l).format(format, args).toString();
3995-
CProver.notModelled();
3996-
return CProver.nondetWithoutNullForNotModelled();
4059+
return format(format, args);
39974060
}
39984061

39994062
/**

src/main/java/org/cprover/CProverString.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,4 +410,17 @@ public static String ofCharArray(char value[], int offset, int count) {
410410
}
411411
return builder.toString();
412412
}
413+
414+
/**
415+
* Format a string according to the given {@param formatString}.
416+
* Unlike the JDK version, all arguments are given by strings, there is
417+
* a fixed number of them and they should not be null.
418+
* This fixed number corresponds to the constant {@code MAX_FORMAT_ARGS} defined in
419+
* {@code java_string_library_preprocess.h} from JBMC.
420+
*/
421+
public static String format(
422+
String formatString, String arg0, String arg1, String arg2, String arg3,
423+
String arg4, String arg5, String arg6, String arg7, String arg8, String arg9) {
424+
return CProver.nondetWithoutNullForNotModelled();
425+
}
413426
}

0 commit comments

Comments
 (0)