Skip to content

Commit c09e970

Browse files
authored
Merge pull request #5355 from smowton/smowton/feature/string-type-lambdas
Preserve lambda handles declared on string types
2 parents a1f60ea + f022757 commit c09e970

File tree

10 files changed

+101
-37
lines changed

10 files changed

+101
-37
lines changed
Binary file not shown.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
public class Test {
2+
3+
public static void testme() {
4+
assert String.usesLambdas(1) == 2;
5+
assert false; // Check we got here
6+
}
7+
8+
}
Binary file not shown.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package java.lang;
2+
3+
public class String {
4+
5+
interface FunctionalIf {
6+
public int f(int x);
7+
}
8+
9+
public static int usesLambdas(int x) {
10+
FunctionalIf lambda = (n -> n + 1);
11+
return lambda.f(x);
12+
}
13+
14+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
Test
3+
--function Test.testme --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar`
4+
line 4 assertion at file Test\.java line 4 function java::Test\.testme:\(\)V bytecode-index 9: SUCCESS
5+
line 5 assertion at file Test\.java line 5 function java::Test\.testme:\(\)V bytecode-index 15: FAILURE
6+
^EXIT=10$
7+
^SIGNAL=0$
8+
--
9+
--
10+
This checks that a lambda functions work when defined on a string type (String, StringBuilder, StringBuffer,
11+
CharSequence, i.e. the types that the string solver may define or redefine)
12+
We use a dummy String type because the typical example in the wild, CharSequence.chars(), requires much
13+
more of the JDK than the tiny fragment present in jbmc's models library at present.

jbmc/src/java_bytecode/java_string_library_preprocess.cpp

Lines changed: 62 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Date: April 2017
2020
#include <util/allocate_objects.h>
2121
#include <util/arith_tools.h>
2222
#include <util/c_types.h>
23+
#include <util/expr_initializer.h>
2324
#include <util/fresh_symbol.h>
2425
#include <util/refined_string_type.h>
2526
#include <util/std_code.h>
@@ -188,6 +189,17 @@ java_string_library_preprocesst::get_string_type_base_classes(
188189

189190
std::vector<irep_idt> bases;
190191
bases.reserve(3);
192+
193+
// StringBuilder and StringBuffer derive from AbstractStringBuilder;
194+
// other String types (String and CharSequence) derive directly from Object.
195+
if(
196+
class_name == "java.lang.StringBuilder" ||
197+
class_name == "java.lang.StringBuffer")
198+
bases.push_back("java.lang.AbstractStringBuilder");
199+
else
200+
bases.push_back("java.lang.Object");
201+
202+
// Interfaces:
191203
if(class_name != "java.lang.CharSequence")
192204
{
193205
bases.push_back("java.io.Serializable");
@@ -196,10 +208,6 @@ java_string_library_preprocesst::get_string_type_base_classes(
196208
if(class_name == "java.lang.String")
197209
bases.push_back("java.lang.Comparable");
198210

199-
if(class_name == "java.lang.StringBuilder" ||
200-
class_name == "java.lang.StringBuffer")
201-
bases.push_back("java.lang.AbstractStringBuilder");
202-
203211
return bases;
204212
}
205213

@@ -209,36 +217,55 @@ java_string_library_preprocesst::get_string_type_base_classes(
209217
void java_string_library_preprocesst::add_string_type(
210218
const irep_idt &class_name, symbol_tablet &symbol_table)
211219
{
212-
java_class_typet string_type;
213-
string_type.set_tag(class_name);
214-
string_type.set_name("java::" + id2string(class_name));
220+
irep_idt class_symbol_name = "java::" + id2string(class_name);
221+
symbolt tmp_string_symbol;
222+
tmp_string_symbol.name = class_symbol_name;
223+
symbolt *string_symbol = nullptr;
224+
bool already_exists = symbol_table.move(tmp_string_symbol, string_symbol);
225+
226+
if(already_exists)
227+
{
228+
// A library has already defined this type -- we'll replace its
229+
// components with those required for internal string modelling, but
230+
// otherwise leave it alone.
231+
to_java_class_type(string_symbol->type).components().clear();
232+
}
233+
else
234+
{
235+
// No definition of this type exists -- define it as it usually occurs in
236+
// the JDK:
237+
java_class_typet new_string_type;
238+
new_string_type.set_tag(class_name);
239+
new_string_type.set_name(class_symbol_name);
240+
new_string_type.set_access(ID_public);
241+
242+
std::vector<irep_idt> bases = get_string_type_base_classes(class_name);
243+
for(const irep_idt &base_name : bases)
244+
new_string_type.add_base(
245+
struct_tag_typet("java::" + id2string(base_name)));
246+
247+
string_symbol->base_name = id2string(class_name);
248+
string_symbol->pretty_name = id2string(class_name);
249+
string_symbol->type = new_string_type;
250+
string_symbol->is_type = true;
251+
string_symbol->mode = ID_java;
252+
}
253+
254+
auto &string_type = to_java_class_type(string_symbol->type);
255+
215256
string_type.components().resize(3);
216-
string_type.components()[0].set_name("@java.lang.Object");
217-
string_type.components()[0].set_pretty_name("@java.lang.Object");
218-
string_type.components()[0].type() =
219-
struct_tag_typet("java::java.lang.Object");
257+
const struct_tag_typet &supertype = string_type.bases().front().type();
258+
irep_idt supertype_component_name =
259+
"@" + id2string(supertype.get_identifier()).substr(6);
260+
string_type.components()[0].set_name(supertype_component_name);
261+
string_type.components()[0].set_pretty_name(supertype_component_name);
262+
string_type.components()[0].type() = supertype;
220263
string_type.components()[1].set_name("length");
221264
string_type.components()[1].set_pretty_name("length");
222265
string_type.components()[1].type()=string_length_type();
223266
string_type.components()[2].set_name("data");
224267
string_type.components()[2].set_pretty_name("data");
225268
string_type.components()[2].type() = pointer_type(java_char_type());
226-
string_type.set_access(ID_public);
227-
string_type.add_base(struct_tag_typet("java::java.lang.Object"));
228-
229-
std::vector<irep_idt> bases = get_string_type_base_classes(class_name);
230-
for(const irep_idt &base_name : bases)
231-
string_type.add_base(struct_tag_typet("java::" + id2string(base_name)));
232-
233-
symbolt tmp_string_symbol;
234-
tmp_string_symbol.name="java::"+id2string(class_name);
235-
symbolt *string_symbol=nullptr;
236-
symbol_table.move(tmp_string_symbol, string_symbol);
237-
string_symbol->base_name=id2string(class_name);
238-
string_symbol->pretty_name=id2string(class_name);
239-
string_symbol->type=string_type;
240-
string_symbol->is_type=true;
241-
string_symbol->mode = ID_java;
242269
}
243270

244271
/// calls string_refine_preprocesst::process_operands with a list of parameters.
@@ -776,14 +803,15 @@ codet java_string_library_preprocesst::code_assign_components_to_java_string(
776803

777804
if(is_constructor)
778805
{
779-
// A String has a field Object with @clsid = String
780-
struct_tag_typet jlo_tag("java::java.lang.Object");
781-
struct_exprt jlo_init({}, jlo_tag);
782-
irep_idt clsid = get_tag(lhs.type().subtype());
806+
// Initialise the supertype with the appropriate classid:
783807
namespacet ns(symbol_table);
784-
java_root_class_init(jlo_init, ns.follow_tag(jlo_tag), clsid);
785-
786-
struct_exprt struct_rhs({jlo_init, rhs_length, rhs_array}, deref.type());
808+
const struct_typet &lhs_type = to_struct_type(ns.follow(deref.type()));
809+
auto zero_base_object = *zero_initializer(
810+
lhs_type.components().front().type(), source_locationt{}, ns);
811+
set_class_identifier(
812+
to_struct_expr(zero_base_object), ns, to_struct_tag_type(deref.type()));
813+
struct_exprt struct_rhs(
814+
{zero_base_object, rhs_length, rhs_array}, deref.type());
787815
return code_assignt(checked_dereference(lhs), struct_rhs);
788816
}
789817
else

jbmc/unit/java_bytecode/java_object_factory/gen_nondet_string_init.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ SCENARIO(
4343

4444
// Add java.lang.String to symbol table
4545
java_string_library_preprocesst preprocess;
46+
preprocess.initialize_known_type_table();
4647
preprocess.add_string_type("java.lang.String", symbol_table);
4748
namespacet ns(symbol_table);
4849

jbmc/unit/java_bytecode/java_string_library_preprocess/convert_exprt_to_string_exprt.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ TEST_CASE("Convert exprt to string exprt")
3939
namespacet ns(symbol_table);
4040
code_blockt code;
4141
java_string_library_preprocesst preprocess;
42+
preprocess.initialize_known_type_table();
4243
preprocess.add_string_type("java.lang.String", symbol_table);
4344
struct_tag_typet java_string_type("java::java.lang.String");
4445
symbol_exprt expr("a", pointer_type(java_string_type));

src/goto-symex/complexity_limiter.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ bool complexity_limitert::are_loop_children_too_complicated(
9898
// complicated, we make sure we don't execute it the other 17 times. But as
9999
// soon as we're running the loop again via a different context it gets a
100100
// chance to redeem itself.
101-
optionalt<std::reference_wrapper<lexical_loopst::loopt>> loop_to_blacklist;
101+
lexical_loopst::loopt *loop_to_blacklist = nullptr;
102102
for(auto frame_iter = current_call_stack.rbegin();
103103
frame_iter != current_call_stack.rend();
104104
++frame_iter)
@@ -121,8 +121,7 @@ bool complexity_limitert::are_loop_children_too_complicated(
121121
sum_complexity += loop_info.children_too_complex;
122122
if(sum_complexity > max_loops_complexity)
123123
{
124-
loop_to_blacklist =
125-
std::reference_wrapper<lexical_loopst::loopt>(loop_info.loop);
124+
loop_to_blacklist = &loop_info.loop;
126125
}
127126
}
128127
}

0 commit comments

Comments
 (0)