Skip to content

Commit 20cd882

Browse files
authored
Merge pull request #4495 from antlechner/antonia/stubbed-string-constructors
Fix stubbed String constructor special case
2 parents 4bf05c8 + 9bc1a6e commit 20cd882

File tree

4 files changed

+82
-68
lines changed

4 files changed

+82
-68
lines changed
Binary file not shown.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import java.lang.String;
2+
3+
public class Test {
4+
5+
public static void test() {
6+
char[] c1 = new char[] { 'j', 'a', 'v', 'a' };
7+
String s = new String (c1);
8+
assert s.charAt(0) == 'j';
9+
}
10+
11+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
Test.class
3+
--function Test.test --show-properties
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
--
7+
--
8+
This test checks that for a stubbed String constructor in the method to verify
9+
(such a constructor will be stubbed if no model for String is loaded), we still
10+
generate properties for this method.
11+
Note that verification would fail in this case because we don't know the correct
12+
implementation of the constructor.

jbmc/src/java_bytecode/java_object_factory.cpp

Lines changed: 59 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -794,77 +794,68 @@ void java_object_factoryt::gen_nondet_struct_init(
794794
// * Not if the object has already been initialised by our caller, in which
795795
// case they will set `skip_classid`
796796
// * Not if we're re-initializing an existing object (i.e. update_in_place)
797-
798-
bool skip_special_string_fields = false;
799-
800-
if(!is_sub &&
801-
!skip_classid &&
802-
update_in_place != update_in_placet::MUST_UPDATE_IN_PLACE)
797+
// * Always if it has a string type. Strings should not be partially updated,
798+
// and the `length` and `data` components of string types need to be
799+
// generated differently from object fields in the general case, see
800+
// \ref java_object_factoryt::initialize_nondet_string_fields.
801+
802+
const bool is_char_sequence =
803+
java_string_library_preprocesst::implements_java_char_sequence(struct_type);
804+
const bool has_length_and_data =
805+
struct_type.has_component("length") && struct_type.has_component("data");
806+
const bool is_string_type = is_char_sequence && has_length_and_data;
807+
const bool has_string_input_values =
808+
!object_factory_parameters.string_input_values.empty();
809+
810+
if(is_string_type && has_string_input_values && !skip_classid)
811+
{ // We're dealing with a string and we should set fixed input values.
812+
// We create a switch statement where each case is an assignment
813+
// of one of the fixed input strings to the input variable in question
814+
const alternate_casest cases = get_string_input_values_code(
815+
expr, object_factory_parameters.string_input_values, symbol_table);
816+
assignments.add(generate_nondet_switch(
817+
id2string(object_factory_parameters.function_id),
818+
cases,
819+
java_int_type(),
820+
ID_java,
821+
location,
822+
symbol_table));
823+
}
824+
else if(
825+
(!is_sub && !skip_classid &&
826+
update_in_place != update_in_placet::MUST_UPDATE_IN_PLACE) ||
827+
is_string_type)
803828
{
804-
class_identifier = struct_tag;
829+
// Add an initial all-zero write. Most of the fields of this will be
830+
// overwritten, but it helps to have a whole-structure write that analysis
831+
// passes can easily recognise leaves no uninitialised state behind.
805832

806-
const bool is_char_sequence =
807-
java_string_library_preprocesst
808-
::implements_java_char_sequence(struct_type);
809-
const bool has_length_and_data =
810-
struct_type.has_component("length") && struct_type.has_component("data");
811-
const bool has_string_input_values =
812-
!object_factory_parameters.string_input_values.empty();
813-
814-
if(is_char_sequence && has_length_and_data && has_string_input_values)
815-
{ // We're dealing with a string and we should set fixed input values.
816-
skip_special_string_fields = true;
817-
818-
// We create a switch statement where each case is an assignment
819-
// of one of the fixed input strings to the input variable in question
820-
821-
const alternate_casest cases =
822-
get_string_input_values_code(
823-
expr,
824-
object_factory_parameters.string_input_values,
825-
symbol_table);
826-
assignments.add(generate_nondet_switch(
827-
id2string(object_factory_parameters.function_id),
828-
cases,
829-
java_int_type(),
830-
ID_java,
833+
// This code mirrors the `remove_java_new` pass:
834+
auto initial_object = zero_initializer(expr.type(), source_locationt(), ns);
835+
CHECK_RETURN(initial_object.has_value());
836+
class_identifier = struct_tag;
837+
const irep_idt qualified_clsid = "java::" + id2string(class_identifier);
838+
set_class_identifier(
839+
to_struct_expr(*initial_object), ns, struct_tag_typet(qualified_clsid));
840+
841+
// If the initialised type is a special-cased String type (one with length
842+
// and data fields introduced by string-library preprocessing), initialise
843+
// those fields with nondet values
844+
if(is_string_type)
845+
{ // We're dealing with a string
846+
initialize_nondet_string_fields(
847+
to_struct_expr(*initial_object),
848+
assignments,
849+
object_factory_parameters.min_nondet_string_length,
850+
object_factory_parameters.max_nondet_string_length,
831851
location,
832-
symbol_table));
833-
}
834-
else
835-
{
836-
// Add an initial all-zero write. Most of the fields of this will be
837-
// overwritten, but it helps to have a whole-structure write that analysis
838-
// passes can easily recognise leaves no uninitialised state behind.
839-
840-
// This code mirrors the `remove_java_new` pass:
841-
auto initial_object =
842-
zero_initializer(expr.type(), source_locationt(), ns);
843-
CHECK_RETURN(initial_object.has_value());
844-
const irep_idt qualified_clsid = "java::" + id2string(class_identifier);
845-
set_class_identifier(
846-
to_struct_expr(*initial_object), ns, struct_tag_typet(qualified_clsid));
847-
848-
// If the initialised type is a special-cased String type (one with length
849-
// and data fields introduced by string-library preprocessing), initialise
850-
// those fields with nondet values
851-
if(is_char_sequence && has_length_and_data)
852-
{ // We're dealing with a string
853-
skip_special_string_fields = true;
854-
initialize_nondet_string_fields(
855-
to_struct_expr(*initial_object),
856-
assignments,
857-
object_factory_parameters.min_nondet_string_length,
858-
object_factory_parameters.max_nondet_string_length,
859-
location,
860-
object_factory_parameters.function_id,
861-
symbol_table,
862-
object_factory_parameters.string_printable,
863-
allocate_objects);
864-
}
865-
866-
assignments.add(code_assignt(expr, *initial_object));
852+
object_factory_parameters.function_id,
853+
symbol_table,
854+
object_factory_parameters.string_printable,
855+
allocate_objects);
867856
}
857+
858+
assignments.add(code_assignt(expr, *initial_object));
868859
}
869860

870861
for(const auto &component : components)
@@ -890,7 +881,7 @@ void java_object_factoryt::gen_nondet_struct_init(
890881
code.add_source_location() = location;
891882
assignments.add(code);
892883
}
893-
else if(skip_special_string_fields && (name == "length" || name == "data"))
884+
else if(is_string_type && (name == "length" || name == "data"))
894885
{
895886
// In this case these were set up above.
896887
continue;

0 commit comments

Comments
 (0)