Skip to content

Commit 47cd22c

Browse files
committed
Add support for Java annotations with Class value
Java annotations can store element-value pairs, where each value is of type String, Enum, Class, annotation, array or a primitive type. The Class case was among the cases that did not have support in the java bytecode parser before. For minimal support (without storing any reference to java.lang.Class), we can just store the name of the type as retrieved from the constant pool in a symbol_exprt.
1 parent 3a7135a commit 47cd22c

11 files changed

+115
-2
lines changed

jbmc/src/java_bytecode/java_bytecode_parser.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1509,6 +1509,9 @@ void java_bytecode_parsert::relement_value_pairs(
15091509
}
15101510
}
15111511

1512+
/// Corresponds to the element_value structure
1513+
/// Described in Java 8 specification 4.7.16.1
1514+
/// https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.16.1
15121515
void java_bytecode_parsert::relement_value_pair(
15131516
annotationt::element_value_pairt &element_value_pair)
15141517
{
@@ -1526,8 +1529,8 @@ void java_bytecode_parsert::relement_value_pair(
15261529

15271530
case 'c':
15281531
{
1529-
UNUSED u2 class_info_index=read_u2();
1530-
// todo: class
1532+
u2 class_info_index = read_u2();
1533+
element_value_pair.value = symbol_exprt(pool_entry(class_info_index).s);
15311534
}
15321535
break;
15331536

Binary file not shown.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public @interface AnnotationWithClassType {
2+
Class<?> value();
3+
}
Binary file not shown.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@AnnotationWithClassType(java.lang.String.class)
2+
public class ClassWithClassTypeAnnotation {
3+
}
Binary file not shown.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@AnnotationWithClassType(byte.class)
2+
public class ClassWithPrimitiveTypeAnnotation {
3+
}
Binary file not shown.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@AnnotationWithClassType(void.class)
2+
public class ClassWithVoidTypeAnnotation {
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
java-testing-utils
2+
testing-utils
3+
java_bytecode
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*******************************************************************\
2+
3+
Module: Unit tests for converting annotations
4+
5+
Author: Diffblue Ltd.
6+
7+
\*******************************************************************/
8+
9+
#include <java-testing-utils/load_java_class.h>
10+
#include <java_bytecode/java_bytecode_convert_class.h>
11+
#include <java_bytecode/java_bytecode_parse_tree.h>
12+
#include <java_bytecode/java_types.h>
13+
#include <testing-utils/catch.hpp>
14+
15+
// See
16+
// https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.16.1
17+
SCENARIO(
18+
"java_bytecode_parse_annotations",
19+
"[core][java_bytecode][java_bytecode_parser]")
20+
{
21+
GIVEN("Some class files in the class path")
22+
{
23+
WHEN(
24+
"Parsing an annotation with Class value specified to non-primitive "
25+
"(java.lang.String)")
26+
{
27+
const symbol_tablet &new_symbol_table = load_java_class(
28+
"ClassWithClassTypeAnnotation", "./java_bytecode/java_bytecode_parser");
29+
30+
THEN("The annotation should store the correct type (String)")
31+
{
32+
const symbolt &class_symbol =
33+
*new_symbol_table.lookup("java::ClassWithClassTypeAnnotation");
34+
const std::vector<java_annotationt> &java_annotations =
35+
to_annotated_type(class_symbol.type).get_annotations();
36+
java_bytecode_parse_treet::annotationst annotations;
37+
convert_java_annotations(java_annotations, annotations);
38+
REQUIRE(annotations.size() == 1);
39+
const auto &annotation = annotations.front();
40+
const auto &element_value_pair = annotation.element_value_pairs.front();
41+
const auto &id =
42+
to_symbol_expr(element_value_pair.value).get_identifier();
43+
const auto &java_type = java_type_from_string(id2string(id));
44+
const std::string &class_name =
45+
id2string(to_symbol_type(java_type.subtype()).get_identifier());
46+
REQUIRE(id2string(class_name) == "java::java.lang.String");
47+
}
48+
}
49+
WHEN("Parsing an annotation with Class value specified to primitive (byte)")
50+
{
51+
const symbol_tablet &new_symbol_table = load_java_class(
52+
"ClassWithPrimitiveTypeAnnotation",
53+
"./java_bytecode/java_bytecode_parser");
54+
55+
THEN("The annotation should store the correct type (byte)")
56+
{
57+
const symbolt &class_symbol =
58+
*new_symbol_table.lookup("java::ClassWithPrimitiveTypeAnnotation");
59+
const std::vector<java_annotationt> &java_annotations =
60+
to_annotated_type(class_symbol.type).get_annotations();
61+
java_bytecode_parse_treet::annotationst annotations;
62+
convert_java_annotations(java_annotations, annotations);
63+
REQUIRE(annotations.size() == 1);
64+
const auto &annotation = annotations.front();
65+
const auto &element_value_pair = annotation.element_value_pairs.front();
66+
const auto &id =
67+
to_symbol_expr(element_value_pair.value).get_identifier();
68+
const auto &java_type = java_type_from_string(id2string(id));
69+
REQUIRE(java_type == java_byte_type());
70+
}
71+
}
72+
WHEN("Parsing an annotation with Class value specified to void")
73+
{
74+
const symbol_tablet &new_symbol_table = load_java_class(
75+
"ClassWithVoidTypeAnnotation", "./java_bytecode/java_bytecode_parser");
76+
77+
THEN("The annotation should store the correct type (void)")
78+
{
79+
const symbolt &class_symbol =
80+
*new_symbol_table.lookup("java::ClassWithVoidTypeAnnotation");
81+
const std::vector<java_annotationt> &java_annotations =
82+
to_annotated_type(class_symbol.type).get_annotations();
83+
java_bytecode_parse_treet::annotationst annotations;
84+
convert_java_annotations(java_annotations, annotations);
85+
REQUIRE(annotations.size() == 1);
86+
const auto &annotation = annotations.front();
87+
const auto &element_value_pair = annotation.element_value_pairs.front();
88+
const auto &id =
89+
to_symbol_expr(element_value_pair.value).get_identifier();
90+
const auto &java_type = java_type_from_string(id2string(id));
91+
REQUIRE(java_type == void_type());
92+
}
93+
}
94+
}
95+
}

0 commit comments

Comments
 (0)