Skip to content

Commit 34a3d85

Browse files
Merge pull request diffblue#2247 from antlechner/antonia/annotation-conversion
Additional functions for processing Java annotations
2 parents 70da606 + 3a7135a commit 34a3d85

16 files changed

+157
-24
lines changed

jbmc/src/java_bytecode/java_bytecode_convert_class.cpp

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -875,8 +875,9 @@ void java_bytecode_convert_classt::add_array_types(symbol_tablet &symbol_table)
875875

876876
bool java_bytecode_convert_classt::is_overlay_method(const methodt &method)
877877
{
878-
return java_bytecode_parse_treet::does_annotation_exist(
879-
method.annotations, ID_overlay_method);
878+
return java_bytecode_parse_treet::find_annotation(
879+
method.annotations, ID_overlay_method)
880+
.has_value();
880881
}
881882

882883
bool java_bytecode_convert_class(
@@ -1015,24 +1016,48 @@ static void find_and_replace_parameters(
10151016
/// \param annotations: The java_annotationt collection to populate
10161017
void convert_annotations(
10171018
const java_bytecode_parse_treet::annotationst &parsed_annotations,
1018-
std::vector<java_annotationt> &annotations)
1019+
std::vector<java_annotationt> &java_annotations)
10191020
{
10201021
for(const auto &annotation : parsed_annotations)
10211022
{
1022-
annotations.emplace_back(annotation.type);
1023+
java_annotations.emplace_back(annotation.type);
10231024
std::vector<java_annotationt::valuet> &values =
1024-
annotations.back().get_values();
1025+
java_annotations.back().get_values();
10251026
std::transform(
10261027
annotation.element_value_pairs.begin(),
10271028
annotation.element_value_pairs.end(),
10281029
std::back_inserter(values),
1029-
[](const decltype(annotation.element_value_pairs)::value_type &value)
1030-
{
1030+
[](const decltype(annotation.element_value_pairs)::value_type &value) {
10311031
return java_annotationt::valuet(value.element_name, value.value);
10321032
});
10331033
}
10341034
}
10351035

1036+
/// Convert java annotations, e.g. as retrieved from the symbol table, back
1037+
/// to type annotationt (inverse of convert_annotations())
1038+
/// \param java_annotations: The java_annotationt collection to convert
1039+
/// \param annotations: The annotationt collection to populate
1040+
void convert_java_annotations(
1041+
const std::vector<java_annotationt> &java_annotations,
1042+
java_bytecode_parse_treet::annotationst &annotations)
1043+
{
1044+
for(const auto &java_annotation : java_annotations)
1045+
{
1046+
annotations.emplace_back(java_bytecode_parse_treet::annotationt());
1047+
auto &annotation = annotations.back();
1048+
annotation.type = java_annotation.get_type();
1049+
1050+
std::transform(
1051+
java_annotation.get_values().begin(),
1052+
java_annotation.get_values().end(),
1053+
std::back_inserter(annotation.element_value_pairs),
1054+
[](const java_annotationt::valuet &value)
1055+
-> java_bytecode_parse_treet::annotationt::element_value_pairt {
1056+
return {value.get_name(), value.get_value()};
1057+
});
1058+
}
1059+
}
1060+
10361061
/// Checks if the class is implicitly generic, i.e., it is an inner class of
10371062
/// any generic class. All uses of the implicit generic type parameters within
10381063
/// the inner class are updated to point to the type parameters of the

jbmc/src/java_bytecode/java_bytecode_convert_class.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ void convert_annotations(
3333
const java_bytecode_parse_treet::annotationst &parsed_annotations,
3434
std::vector<java_annotationt> &annotations);
3535

36+
void convert_java_annotations(
37+
const std::vector<java_annotationt> &java_annotations,
38+
java_bytecode_parse_treet::annotationst &annotations);
39+
3640
void mark_java_implicitly_generic_class_type(
3741
const irep_idt &class_name,
3842
symbol_tablet &symbol_table);

jbmc/src/java_bytecode/java_bytecode_parse_tree.cpp

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -99,23 +99,30 @@ void java_bytecode_parse_treet::annotationt::element_value_pairt::output(
9999
out << expr2java(value, ns);
100100
}
101101

102-
bool java_bytecode_parse_treet::does_annotation_exist(
102+
/// Find an annotation given its name
103+
/// \param annotations: A vector of annotationt
104+
/// \param annotation_type_name: An irep_idt representing the name of the
105+
/// annotation class, e.g. java::java.lang.SuppressWarnings
106+
/// \return The first annotation with the given name in annotations if one
107+
/// exists, an empty optionalt otherwise.
108+
optionalt<java_bytecode_parse_treet::annotationt>
109+
java_bytecode_parse_treet::find_annotation(
103110
const annotationst &annotations,
104111
const irep_idt &annotation_type_name)
105112
{
106-
return
107-
std::find_if(
108-
annotations.begin(),
109-
annotations.end(),
110-
[&annotation_type_name](const annotationt &annotation)
111-
{
112-
if(annotation.type.id() != ID_pointer)
113-
return false;
114-
typet type = annotation.type.subtype();
115-
return
116-
type.id() == ID_symbol
117-
&& to_symbol_type(type).get_identifier() == annotation_type_name;
118-
}) != annotations.end();
113+
const auto annotation_it = std::find_if(
114+
annotations.begin(),
115+
annotations.end(),
116+
[&annotation_type_name](const annotationt &annotation) {
117+
if(annotation.type.id() != ID_pointer)
118+
return false;
119+
const typet &type = annotation.type.subtype();
120+
return type.id() == ID_symbol &&
121+
to_symbol_type(type).get_identifier() == annotation_type_name;
122+
});
123+
if(annotation_it == annotations.end())
124+
return {};
125+
return *annotation_it;
119126
}
120127

121128
void java_bytecode_parse_treet::methodt::output(std::ostream &out) const

jbmc/src/java_bytecode/java_bytecode_parse_tree.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class java_bytecode_parse_treet
5454

5555
typedef std::vector<annotationt> annotationst;
5656

57-
static bool does_annotation_exist(
57+
static optionalt<annotationt> find_annotation(
5858
const annotationst &annotations,
5959
const irep_idt &annotation_type_name);
6060

jbmc/src/java_bytecode/java_class_loader.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,9 @@ optionalt<java_bytecode_parse_treet> java_class_loadert::get_class_from_jar(
103103

104104
static bool is_overlay_class(const java_bytecode_parse_treet::classt &c)
105105
{
106-
return java_bytecode_parse_treet::does_annotation_exist(
107-
c.annotations, ID_overlay_class);
106+
return java_bytecode_parse_treet::find_annotation(
107+
c.annotations, ID_overlay_class)
108+
.has_value();
108109
}
109110

110111
/// Check through all the places class parse trees can appear and returns the

jbmc/unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ SRC += java_bytecode/goto-programs/class_hierarchy_output.cpp \
99
java_bytecode/goto-programs/class_hierarchy_graph.cpp \
1010
java_bytecode/goto-programs/remove_virtual_functions_without_fallback.cpp \
1111
java_bytecode/java_bytecode_convert_class/convert_abstract_class.cpp \
12+
java_bytecode/java_bytecode_convert_class/convert_java_annotations.cpp \
1213
java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp \
1314
java_bytecode/java_bytecode_parse_generics/parse_generic_class.cpp \
1415
java_bytecode/java_object_factory/gen_nondet_string_init.cpp \
Binary file not shown.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@MyClassAnnotation(6)
2+
public class ClassWithClassAnnotation {
3+
}
Binary file not shown.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
public class ClassWithMethodAnnotation {
2+
3+
@MyMethodAnnotation(methodValue = 11)
4+
public void myMethod() {
5+
}
6+
7+
}
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 MyClassAnnotation {
2+
int 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+
public @interface MyMethodAnnotation {
2+
int methodValue();
3+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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+
SCENARIO(
16+
"java_bytecode_convert_annotations",
17+
"[core][java_bytecode][java_bytecode_convert_class]")
18+
{
19+
GIVEN("Some class files in the class path")
20+
{
21+
WHEN("Parsing a class with class-level annotation MyClassAnnotation(6)")
22+
{
23+
const symbol_tablet &new_symbol_table = load_java_class(
24+
"ClassWithClassAnnotation",
25+
"./java_bytecode/java_bytecode_convert_class");
26+
27+
THEN("The annotation should have the correct structure")
28+
{
29+
const symbolt &class_symbol =
30+
*new_symbol_table.lookup("java::ClassWithClassAnnotation");
31+
const std::vector<java_annotationt> &java_annotations =
32+
to_annotated_type(class_symbol.type).get_annotations();
33+
java_bytecode_parse_treet::annotationst annotations;
34+
convert_java_annotations(java_annotations, annotations);
35+
REQUIRE(annotations.size() == 1);
36+
const auto &annotation = annotations.front();
37+
const auto &identifier =
38+
to_symbol_type(annotation.type.subtype()).get_identifier();
39+
REQUIRE(id2string(identifier) == "java::MyClassAnnotation");
40+
const auto &element_value_pair = annotation.element_value_pairs.front();
41+
const auto &element_name = element_value_pair.element_name;
42+
REQUIRE(id2string(element_name) == "value");
43+
const auto &expr = element_value_pair.value;
44+
const auto comp_expr = from_integer(6, java_int_type());
45+
REQUIRE(expr == comp_expr);
46+
}
47+
}
48+
WHEN("Parsing a class with method-level annotation MyMethodAnnotation(11)")
49+
{
50+
const symbol_tablet &new_symbol_table = load_java_class(
51+
"ClassWithMethodAnnotation",
52+
"./java_bytecode/java_bytecode_convert_class");
53+
54+
THEN("The annotation should have the correct structure")
55+
{
56+
const symbolt &method_symbol = *new_symbol_table.lookup(
57+
"java::ClassWithMethodAnnotation.myMethod:()V");
58+
const std::vector<java_annotationt> &java_annotations =
59+
to_annotated_type(method_symbol.type).get_annotations();
60+
java_bytecode_parse_treet::annotationst annotations;
61+
convert_java_annotations(java_annotations, annotations);
62+
REQUIRE(annotations.size() == 1);
63+
const auto &annotation = annotations.front();
64+
const auto &identifier =
65+
to_symbol_type(annotation.type.subtype()).get_identifier();
66+
REQUIRE(id2string(identifier) == "java::MyMethodAnnotation");
67+
const auto &element_value_pair = annotation.element_value_pairs.front();
68+
const auto &element_name = element_value_pair.element_name;
69+
REQUIRE(id2string(element_name) == "methodValue");
70+
const auto &expr = element_value_pair.value;
71+
const auto &comp_expr = from_integer(11, java_int_type());
72+
REQUIRE(expr == comp_expr);
73+
}
74+
}
75+
}
76+
}
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

0 commit comments

Comments
 (0)