Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional functions for processing Java annotations #2247

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 32 additions & 7 deletions jbmc/src/java_bytecode/java_bytecode_convert_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -875,8 +875,9 @@ void java_bytecode_convert_classt::add_array_types(symbol_tablet &symbol_table)

bool java_bytecode_convert_classt::is_overlay_method(const methodt &method)
{
return java_bytecode_parse_treet::does_annotation_exist(
method.annotations, ID_overlay_method);
return java_bytecode_parse_treet::find_annotation(
method.annotations, ID_overlay_method)
.has_value();
}

bool java_bytecode_convert_class(
Expand Down Expand Up @@ -1015,24 +1016,48 @@ static void find_and_replace_parameters(
/// \param annotations: The java_annotationt collection to populate
void convert_annotations(
const java_bytecode_parse_treet::annotationst &parsed_annotations,
std::vector<java_annotationt> &annotations)
std::vector<java_annotationt> &java_annotations)
{
for(const auto &annotation : parsed_annotations)
{
annotations.emplace_back(annotation.type);
java_annotations.emplace_back(annotation.type);
std::vector<java_annotationt::valuet> &values =
annotations.back().get_values();
java_annotations.back().get_values();
std::transform(
annotation.element_value_pairs.begin(),
annotation.element_value_pairs.end(),
std::back_inserter(values),
[](const decltype(annotation.element_value_pairs)::value_type &value)
{
[](const decltype(annotation.element_value_pairs)::value_type &value) {
return java_annotationt::valuet(value.element_name, value.value);
});
}
}

/// Convert java annotations, e.g. as retrieved from the symbol table, back
/// to type annotationt (inverse of convert_annotations())
/// \param java_annotations: The java_annotationt collection to convert
/// \param annotations: The annotationt collection to populate
void convert_java_annotations(
const std::vector<java_annotationt> &java_annotations,
java_bytecode_parse_treet::annotationst &annotations)
{
for(const auto &java_annotation : java_annotations)
{
annotations.emplace_back(java_bytecode_parse_treet::annotationt());
auto &annotation = annotations.back();
annotation.type = java_annotation.get_type();

std::transform(
java_annotation.get_values().begin(),
java_annotation.get_values().end(),
std::back_inserter(annotation.element_value_pairs),
[](const java_annotationt::valuet &value)
-> java_bytecode_parse_treet::annotationt::element_value_pairt {
return {value.get_name(), value.get_value()};
});
}
}

/// Checks if the class is implicitly generic, i.e., it is an inner class of
/// any generic class. All uses of the implicit generic type parameters within
/// the inner class are updated to point to the type parameters of the
Expand Down
4 changes: 4 additions & 0 deletions jbmc/src/java_bytecode/java_bytecode_convert_class.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ void convert_annotations(
const java_bytecode_parse_treet::annotationst &parsed_annotations,
std::vector<java_annotationt> &annotations);

void convert_java_annotations(
const std::vector<java_annotationt> &java_annotations,
java_bytecode_parse_treet::annotationst &annotations);

void mark_java_implicitly_generic_class_type(
const irep_idt &class_name,
symbol_tablet &symbol_table);
Expand Down
35 changes: 21 additions & 14 deletions jbmc/src/java_bytecode/java_bytecode_parse_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,23 +99,30 @@ void java_bytecode_parse_treet::annotationt::element_value_pairt::output(
out << expr2java(value, ns);
}

bool java_bytecode_parse_treet::does_annotation_exist(
/// Find an annotation given its name
/// \param annotations: A vector of annotationt
/// \param annotation_type_name: An irep_idt representing the name of the
/// annotation class, e.g. java::java.lang.SuppressWarnings
/// \return The first annotation with the given name in annotations if one
/// exists, an empty optionalt otherwise.
optionalt<java_bytecode_parse_treet::annotationt>
java_bytecode_parse_treet::find_annotation(
const annotationst &annotations,
const irep_idt &annotation_type_name)
{
return
std::find_if(
annotations.begin(),
annotations.end(),
[&annotation_type_name](const annotationt &annotation)
{
if(annotation.type.id() != ID_pointer)
return false;
typet type = annotation.type.subtype();
return
type.id() == ID_symbol
&& to_symbol_type(type).get_identifier() == annotation_type_name;
}) != annotations.end();
const auto annotation_it = std::find_if(
annotations.begin(),
annotations.end(),
[&annotation_type_name](const annotationt &annotation) {
if(annotation.type.id() != ID_pointer)
return false;
const typet &type = annotation.type.subtype();
return type.id() == ID_symbol &&
to_symbol_type(type).get_identifier() == annotation_type_name;
});
if(annotation_it == annotations.end())
return {};
return *annotation_it;
}

void java_bytecode_parse_treet::methodt::output(std::ostream &out) const
Expand Down
2 changes: 1 addition & 1 deletion jbmc/src/java_bytecode/java_bytecode_parse_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class java_bytecode_parse_treet

typedef std::vector<annotationt> annotationst;

static bool does_annotation_exist(
static optionalt<annotationt> find_annotation(
const annotationst &annotations,
const irep_idt &annotation_type_name);

Expand Down
5 changes: 3 additions & 2 deletions jbmc/src/java_bytecode/java_class_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,9 @@ optionalt<java_bytecode_parse_treet> java_class_loadert::get_class_from_jar(

static bool is_overlay_class(const java_bytecode_parse_treet::classt &c)
{
return java_bytecode_parse_treet::does_annotation_exist(
c.annotations, ID_overlay_class);
return java_bytecode_parse_treet::find_annotation(
c.annotations, ID_overlay_class)
.has_value();
}

/// Check through all the places class parse trees can appear and returns the
Expand Down
1 change: 1 addition & 0 deletions jbmc/unit/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ SRC += java_bytecode/goto-programs/class_hierarchy_output.cpp \
java_bytecode/goto-programs/class_hierarchy_graph.cpp \
java_bytecode/goto-programs/remove_virtual_functions_without_fallback.cpp \
java_bytecode/java_bytecode_convert_class/convert_abstract_class.cpp \
java_bytecode/java_bytecode_convert_class/convert_java_annotations.cpp \
java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp \
java_bytecode/java_bytecode_parse_generics/parse_generic_class.cpp \
java_bytecode/java_object_factory/gen_nondet_string_init.cpp \
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@MyClassAnnotation(6)
public class ClassWithClassAnnotation {
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
public class ClassWithMethodAnnotation {

@MyMethodAnnotation(methodValue = 11)
public void myMethod() {
}

}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public @interface MyClassAnnotation {
int value();
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public @interface MyMethodAnnotation {
int methodValue();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*******************************************************************\

Module: Unit tests for converting annotations

Author: Diffblue Ltd.

\*******************************************************************/

#include <java-testing-utils/load_java_class.h>
#include <java_bytecode/java_bytecode_convert_class.h>
#include <java_bytecode/java_bytecode_parse_tree.h>
#include <java_bytecode/java_types.h>
#include <testing-utils/catch.hpp>

SCENARIO(
"java_bytecode_convert_annotations",
"[core][java_bytecode][java_bytecode_convert_class]")
{
GIVEN("Some class files in the class path")
{
WHEN("Parsing a class with class-level annotation MyClassAnnotation(6)")
{
const symbol_tablet &new_symbol_table = load_java_class(
"ClassWithClassAnnotation",
"./java_bytecode/java_bytecode_convert_class");

THEN("The annotation should have the correct structure")
{
const symbolt &class_symbol =
*new_symbol_table.lookup("java::ClassWithClassAnnotation");
const std::vector<java_annotationt> &java_annotations =
to_annotated_type(class_symbol.type).get_annotations();
java_bytecode_parse_treet::annotationst annotations;
convert_java_annotations(java_annotations, annotations);
REQUIRE(annotations.size() == 1);
const auto &annotation = annotations.front();
const auto &identifier =
to_symbol_type(annotation.type.subtype()).get_identifier();
REQUIRE(id2string(identifier) == "java::MyClassAnnotation");
const auto &element_value_pair = annotation.element_value_pairs.front();
const auto &element_name = element_value_pair.element_name;
REQUIRE(id2string(element_name) == "value");
const auto &expr = element_value_pair.value;
const auto comp_expr = from_integer(6, java_int_type());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to follow on @mgudemann comment you could comment on this in the WHEN section, for instance:
WHEN("Parsing a class with a class-level annotation MyClassAnnotation(6)") it would make it clear where this 6 comes from.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, I added that for both the class and method annotation.

REQUIRE(expr == comp_expr);
}
}
WHEN("Parsing a class with method-level annotation MyMethodAnnotation(11)")
{
const symbol_tablet &new_symbol_table = load_java_class(
"ClassWithMethodAnnotation",
"./java_bytecode/java_bytecode_convert_class");

THEN("The annotation should have the correct structure")
{
const symbolt &method_symbol = *new_symbol_table.lookup(
"java::ClassWithMethodAnnotation.myMethod:()V");
const std::vector<java_annotationt> &java_annotations =
to_annotated_type(method_symbol.type).get_annotations();
java_bytecode_parse_treet::annotationst annotations;
convert_java_annotations(java_annotations, annotations);
REQUIRE(annotations.size() == 1);
const auto &annotation = annotations.front();
const auto &identifier =
to_symbol_type(annotation.type.subtype()).get_identifier();
REQUIRE(id2string(identifier) == "java::MyMethodAnnotation");
const auto &element_value_pair = annotation.element_value_pairs.front();
const auto &element_name = element_value_pair.element_name;
REQUIRE(id2string(element_name) == "methodValue");
const auto &expr = element_value_pair.value;
const auto &comp_expr = from_integer(11, java_int_type());
REQUIRE(expr == comp_expr);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
java-testing-utils
testing-utils
java_bytecode