Skip to content

Commit 3817341

Browse files
authored
Merge pull request #2237 from smowton/smowton/feature/initialize-class-literals
Java class literals: init using a library hook when possible
2 parents 70f13f3 + 4eda8ad commit 3817341

23 files changed

+288
-9
lines changed
Binary file not shown.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
public @interface ExampleAnnotation {
3+
4+
}
Binary file not shown.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
public enum ExampleEnum {
3+
4+
}
5+
Binary file not shown.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
interface ExampleInterface {
3+
4+
}
Binary file not shown.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
; Compile me with Jasmin 2.1+ (https://sourceforge.net/projects/jasmin/)
2+
3+
.class public synthetic ExampleSynthetic
4+
.super java/lang/Object
1.23 KB
Binary file not shown.
Lines changed: 35 additions & 0 deletions
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.
Lines changed: 45 additions & 0 deletions
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+
}
Lines changed: 8 additions & 0 deletions
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
Lines changed: 10 additions & 0 deletions
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

Lines changed: 40 additions & 2 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 3 additions & 2 deletions
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ class java_bytecode_parse_treet
209209
bool is_enum=false;
210210
bool is_public=false, is_protected=false, is_private=false;
211211
bool is_final = false;
212+
bool is_interface = false;
213+
bool is_synthetic = false;
214+
bool is_annotation = false;
212215
bool attribute_bootstrapmethods_read = false;
213216
size_t enum_elements=0;
214217

jbmc/src/java_bytecode/java_bytecode_parser.cpp

Lines changed: 5 additions & 0 deletions
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)