Skip to content

Commit 742873f

Browse files
committed
Java class literals: init using a library hook when possible
The Java library definition of java.lang.Class, when nondet initialized, can be quite time consuming due to the enumeration constant dictionary's internal array. Instead, let's give the library a chance to set Class constants (literals like MyClass.class) itself. While we're at it we might as well gain the ability to prove some properties of the literals. We currently supply those class attributes that are easy to come by in the parse tree; with a little more effort in the parser IsLocalClass and IsMemberClass could be determined.
1 parent 34b0ac6 commit 742873f

21 files changed

+284
-9
lines changed
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
public @interface ExampleAnnotation {
3+
4+
}
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
public enum ExampleEnum {
3+
4+
}
5+
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
interface ExampleInterface {
3+
4+
}
1.23 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
public class Test {
3+
4+
public static void main() {
5+
6+
assert ExampleAnnotation.class.isAnnotation();
7+
assert ExampleInterface.class.isInterface();
8+
assert ExampleEnum.class.isEnum();
9+
assert ExampleSynthetic.class.isSynthetic();
10+
11+
assert char[].class.isArray();
12+
assert short[].class.isArray();
13+
assert int[].class.isArray();
14+
assert long[].class.isArray();
15+
assert float[].class.isArray();
16+
assert double[].class.isArray();
17+
assert boolean[].class.isArray();
18+
assert Object[].class.isArray();
19+
assert Object[][].class.isArray();
20+
21+
// Note use of '==' not '.equals', as we expect the same exact literal,
22+
// which in jbmc always have the same address.
23+
// Note names of array classes are not tested yet, as arrays' types are
24+
// printed as their raw signature, to be addressed separately.
25+
// Note also primitive types (e.g. int.class) are not addresses here, as
26+
// they are created through box types' static initializers (e.g. Integer
27+
// has a static member TYPE that holds the Class for `int.class`)
28+
29+
assert ExampleAnnotation.class.getName() == "ExampleAnnotation";
30+
assert ExampleInterface.class.getName() == "ExampleInterface";
31+
assert ExampleEnum.class.getName() == "ExampleEnum";
32+
33+
}
34+
35+
}
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package java.lang;
2+
3+
public class Class {
4+
5+
private String name;
6+
7+
private boolean isAnnotation;
8+
private boolean isArray;
9+
private boolean isInterface;
10+
private boolean isSynthetic;
11+
private boolean isLocalClass;
12+
private boolean isMemberClass;
13+
private boolean isEnum;
14+
15+
public void cproverInitializeClassLiteral(
16+
String name,
17+
boolean isAnnotation,
18+
boolean isArray,
19+
boolean isInterface,
20+
boolean isSynthetic,
21+
boolean isLocalClass,
22+
boolean isMemberClass,
23+
boolean isEnum) {
24+
25+
this.name = name;
26+
this.isAnnotation = isAnnotation;
27+
this.isArray = isArray;
28+
this.isInterface = isInterface;
29+
this.isSynthetic = isSynthetic;
30+
this.isLocalClass = isLocalClass;
31+
this.isMemberClass = isMemberClass;
32+
this.isEnum = isEnum;
33+
34+
}
35+
36+
public String getName() { return name; }
37+
38+
public boolean isAnnotation() { return isAnnotation; }
39+
public boolean isArray() { return isArray; }
40+
public boolean isInterface() { return isInterface; }
41+
public boolean isSynthetic() { return isSynthetic; }
42+
public boolean isLocalClass() { return isLocalClass; }
43+
public boolean isMemberClass() { return isMemberClass; }
44+
public boolean isEnum() { return isEnum; }
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
Test.class
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Test.class
3+
--lazy-methods
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
9+
--
10+
lazy-methods is incompatible with symex-driven lazy loading

jbmc/src/java_bytecode/ci_lazy_methods.cpp

+40-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "java_string_library_preprocess.h"
1414
#include "remove_exceptions.h"
1515

16+
#include <util/expr_iterator.h>
1617
#include <util/suffix.h>
1718

1819
#include <goto-programs/resolve_inherited_component.h>
@@ -52,6 +53,31 @@ ci_lazy_methodst::ci_lazy_methodst(
5253
class_hierarchy(symbol_table);
5354
}
5455

56+
/// Checks if an expression refers to any class literals (e.g. MyType.class)
57+
/// These are expressed as ldc instructions in Java bytecode, and as symbols
58+
/// of the form MyType@class_model in GOTO programs.
59+
/// \param expr: expression to check
60+
/// \return true if the expression or any of its subexpressions refer to a
61+
/// class
62+
static bool references_class_model(const exprt &expr)
63+
{
64+
static const symbol_typet class_type("java::java.lang.Class");
65+
66+
for(auto it = expr.depth_begin(); it != expr.depth_end(); ++it)
67+
{
68+
if(can_cast_expr<symbol_exprt>(*it) &&
69+
it->type() == class_type &&
70+
has_suffix(
71+
id2string(to_symbol_expr(*it).get_identifier()),
72+
JAVA_CLASS_MODEL_SUFFIX))
73+
{
74+
return true;
75+
}
76+
}
77+
78+
return false;
79+
}
80+
5581
/// Uses a simple context-insensitive ('ci') analysis to determine which methods
5682
/// may be reachable from the main entry point. In brief, static methods are
5783
/// reachable if we find a callsite in another reachable site, while virtual
@@ -122,6 +148,7 @@ bool ci_lazy_methodst::operator()(
122148

123149
std::unordered_set<irep_idt> methods_already_populated;
124150
std::unordered_set<exprt, irep_hash> virtual_function_calls;
151+
bool class_initializer_seen = false;
125152

126153
bool any_new_classes = true;
127154
while(any_new_classes)
@@ -149,8 +176,19 @@ bool ci_lazy_methodst::operator()(
149176
// Couldn't convert this function
150177
continue;
151178
}
152-
gather_virtual_callsites(
153-
symbol_table.lookup_ref(mname).value, virtual_function_calls);
179+
const exprt &method_body = symbol_table.lookup_ref(mname).value;
180+
181+
gather_virtual_callsites(method_body, virtual_function_calls);
182+
183+
if(!class_initializer_seen && references_class_model(method_body))
184+
{
185+
class_initializer_seen = true;
186+
irep_idt initializer_signature =
187+
get_java_class_literal_initializer_signature();
188+
if(symbol_table.has_symbol(initializer_signature))
189+
methods_to_convert_later.insert(initializer_signature);
190+
}
191+
154192
any_new_methods=true;
155193
}
156194
}

jbmc/src/java_bytecode/java_bytecode_convert_class.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,9 @@ void java_bytecode_convert_classt::convert(
265265
class_type.set_tag(c.name);
266266
class_type.set(ID_base_name, c.name);
267267
class_type.set(ID_abstract, c.is_abstract);
268+
class_type.set(ID_is_annotation, c.is_annotation);
269+
class_type.set(ID_interface, c.is_interface);
270+
class_type.set(ID_synthetic, c.is_synthetic);
268271
class_type.set_final(c.is_final);
269272
if(c.is_enum)
270273
{

jbmc/src/java_bytecode/java_bytecode_language.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ static symbol_exprt get_or_create_class_literal_symbol(
299299
{
300300
symbol_typet java_lang_Class("java::java.lang.Class");
301301
symbol_exprt symbol_expr(
302-
id2string(class_id)+"@class_model",
302+
id2string(class_id) + JAVA_CLASS_MODEL_SUFFIX,
303303
java_lang_Class);
304304
if(!symbol_table.has_symbol(symbol_expr.get_identifier()))
305305
{
@@ -785,7 +785,8 @@ bool java_bytecode_languaget::generate_support_functions(
785785
get_message_handler(),
786786
assume_inputs_non_null,
787787
object_factory_parameters,
788-
get_pointer_type_selector());
788+
get_pointer_type_selector(),
789+
string_refinement_enabled);
789790
}
790791

791792
/// Uses a simple context-insensitive ('ci') analysis to determine which methods

jbmc/src/java_bytecode/java_bytecode_language.h

+2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ enum lazy_methods_modet
6868
LAZY_METHODS_MODE_EXTERNAL_DRIVER
6969
};
7070

71+
#define JAVA_CLASS_MODEL_SUFFIX "@class_model"
72+
7173
class java_bytecode_languaget:public languaget
7274
{
7375
public:

jbmc/src/java_bytecode/java_bytecode_parse_tree.h

+3
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,9 @@ class java_bytecode_parse_treet
204204
bool is_enum=false;
205205
bool is_public=false, is_protected=false, is_private=false;
206206
bool is_final = false;
207+
bool is_interface = false;
208+
bool is_synthetic = false;
209+
bool is_annotation = false;
207210
bool attribute_bootstrapmethods_read = false;
208211
size_t enum_elements=0;
209212

jbmc/src/java_bytecode/java_bytecode_parser.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -451,9 +451,11 @@ bool java_bytecode_parsert::parse()
451451
#define ACC_BRIDGE 0x0040
452452
#define ACC_VARARGS 0x0080
453453
#define ACC_NATIVE 0x0100
454+
#define ACC_INTERFACE 0x0200
454455
#define ACC_ABSTRACT 0x0400
455456
#define ACC_STRICT 0x0800
456457
#define ACC_SYNTHETIC 0x1000
458+
#define ACC_ANNOTATION 0x2000
457459
#define ACC_ENUM 0x4000
458460

459461
#ifdef _MSC_VER
@@ -496,6 +498,9 @@ void java_bytecode_parsert::rClassFile()
496498
parsed_class.is_protected=(access_flags&ACC_PROTECTED)!=0;
497499
parsed_class.is_private=(access_flags&ACC_PRIVATE)!=0;
498500
parsed_class.is_final = (access_flags & ACC_FINAL) != 0;
501+
parsed_class.is_interface = (access_flags & ACC_INTERFACE) != 0;
502+
parsed_class.is_synthetic = (access_flags & ACC_SYNTHETIC) != 0;
503+
parsed_class.is_annotation = (access_flags & ACC_ANNOTATION) != 0;
499504
parsed_class.name=
500505
constant(this_class).type().get(ID_C_base_name);
501506

0 commit comments

Comments
 (0)