diff --git a/jbmc/src/java_bytecode/java_bytecode_convert_class.cpp b/jbmc/src/java_bytecode/java_bytecode_convert_class.cpp index 178dddad5ea..706b61a0ecc 100644 --- a/jbmc/src/java_bytecode/java_bytecode_convert_class.cpp +++ b/jbmc/src/java_bytecode/java_bytecode_convert_class.cpp @@ -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( @@ -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 &annotations) + std::vector &java_annotations) { for(const auto &annotation : parsed_annotations) { - annotations.emplace_back(annotation.type); + java_annotations.emplace_back(annotation.type); std::vector &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_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 diff --git a/jbmc/src/java_bytecode/java_bytecode_convert_class.h b/jbmc/src/java_bytecode/java_bytecode_convert_class.h index 3693290491f..6fad2c8c17a 100644 --- a/jbmc/src/java_bytecode/java_bytecode_convert_class.h +++ b/jbmc/src/java_bytecode/java_bytecode_convert_class.h @@ -33,6 +33,10 @@ void convert_annotations( const java_bytecode_parse_treet::annotationst &parsed_annotations, std::vector &annotations); +void convert_java_annotations( + const std::vector &java_annotations, + java_bytecode_parse_treet::annotationst &annotations); + void mark_java_implicitly_generic_class_type( const irep_idt &class_name, symbol_tablet &symbol_table); diff --git a/jbmc/src/java_bytecode/java_bytecode_parse_tree.cpp b/jbmc/src/java_bytecode/java_bytecode_parse_tree.cpp index 673ed6d22d2..bbd985182fc 100644 --- a/jbmc/src/java_bytecode/java_bytecode_parse_tree.cpp +++ b/jbmc/src/java_bytecode/java_bytecode_parse_tree.cpp @@ -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::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 diff --git a/jbmc/src/java_bytecode/java_bytecode_parse_tree.h b/jbmc/src/java_bytecode/java_bytecode_parse_tree.h index 7d5adcff2d0..4baa6fd1dba 100644 --- a/jbmc/src/java_bytecode/java_bytecode_parse_tree.h +++ b/jbmc/src/java_bytecode/java_bytecode_parse_tree.h @@ -54,7 +54,7 @@ class java_bytecode_parse_treet typedef std::vector annotationst; - static bool does_annotation_exist( + static optionalt find_annotation( const annotationst &annotations, const irep_idt &annotation_type_name); diff --git a/jbmc/src/java_bytecode/java_class_loader.cpp b/jbmc/src/java_bytecode/java_class_loader.cpp index 2541e4eeb76..3104c52e084 100644 --- a/jbmc/src/java_bytecode/java_class_loader.cpp +++ b/jbmc/src/java_bytecode/java_class_loader.cpp @@ -103,8 +103,9 @@ optionalt 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 diff --git a/jbmc/unit/Makefile b/jbmc/unit/Makefile index 4614a36e024..16da78dcda2 100644 --- a/jbmc/unit/Makefile +++ b/jbmc/unit/Makefile @@ -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 \ diff --git a/jbmc/unit/java_bytecode/java_bytecode_convert_class/ClassWithClassAnnotation.class b/jbmc/unit/java_bytecode/java_bytecode_convert_class/ClassWithClassAnnotation.class new file mode 100644 index 00000000000..e24c8e68896 Binary files /dev/null and b/jbmc/unit/java_bytecode/java_bytecode_convert_class/ClassWithClassAnnotation.class differ diff --git a/jbmc/unit/java_bytecode/java_bytecode_convert_class/ClassWithClassAnnotation.java b/jbmc/unit/java_bytecode/java_bytecode_convert_class/ClassWithClassAnnotation.java new file mode 100644 index 00000000000..3bc08c8de17 --- /dev/null +++ b/jbmc/unit/java_bytecode/java_bytecode_convert_class/ClassWithClassAnnotation.java @@ -0,0 +1,3 @@ +@MyClassAnnotation(6) +public class ClassWithClassAnnotation { +} diff --git a/jbmc/unit/java_bytecode/java_bytecode_convert_class/ClassWithMethodAnnotation.class b/jbmc/unit/java_bytecode/java_bytecode_convert_class/ClassWithMethodAnnotation.class new file mode 100644 index 00000000000..4d3c00d3b06 Binary files /dev/null and b/jbmc/unit/java_bytecode/java_bytecode_convert_class/ClassWithMethodAnnotation.class differ diff --git a/jbmc/unit/java_bytecode/java_bytecode_convert_class/ClassWithMethodAnnotation.java b/jbmc/unit/java_bytecode/java_bytecode_convert_class/ClassWithMethodAnnotation.java new file mode 100644 index 00000000000..2efe9cc6cf4 --- /dev/null +++ b/jbmc/unit/java_bytecode/java_bytecode_convert_class/ClassWithMethodAnnotation.java @@ -0,0 +1,7 @@ +public class ClassWithMethodAnnotation { + + @MyMethodAnnotation(methodValue = 11) + public void myMethod() { + } + +} diff --git a/jbmc/unit/java_bytecode/java_bytecode_convert_class/MyClassAnnotation.class b/jbmc/unit/java_bytecode/java_bytecode_convert_class/MyClassAnnotation.class new file mode 100644 index 00000000000..09977233041 Binary files /dev/null and b/jbmc/unit/java_bytecode/java_bytecode_convert_class/MyClassAnnotation.class differ diff --git a/jbmc/unit/java_bytecode/java_bytecode_convert_class/MyClassAnnotation.java b/jbmc/unit/java_bytecode/java_bytecode_convert_class/MyClassAnnotation.java new file mode 100644 index 00000000000..2e9ece03aea --- /dev/null +++ b/jbmc/unit/java_bytecode/java_bytecode_convert_class/MyClassAnnotation.java @@ -0,0 +1,3 @@ +public @interface MyClassAnnotation { + int value(); +} diff --git a/jbmc/unit/java_bytecode/java_bytecode_convert_class/MyMethodAnnotation.class b/jbmc/unit/java_bytecode/java_bytecode_convert_class/MyMethodAnnotation.class new file mode 100644 index 00000000000..7c9b3a73285 Binary files /dev/null and b/jbmc/unit/java_bytecode/java_bytecode_convert_class/MyMethodAnnotation.class differ diff --git a/jbmc/unit/java_bytecode/java_bytecode_convert_class/MyMethodAnnotation.java b/jbmc/unit/java_bytecode/java_bytecode_convert_class/MyMethodAnnotation.java new file mode 100644 index 00000000000..9aa2e0be0e0 --- /dev/null +++ b/jbmc/unit/java_bytecode/java_bytecode_convert_class/MyMethodAnnotation.java @@ -0,0 +1,3 @@ +public @interface MyMethodAnnotation { + int methodValue(); +} diff --git a/jbmc/unit/java_bytecode/java_bytecode_convert_class/convert_java_annotations.cpp b/jbmc/unit/java_bytecode/java_bytecode_convert_class/convert_java_annotations.cpp new file mode 100644 index 00000000000..6ecbe704bf2 --- /dev/null +++ b/jbmc/unit/java_bytecode/java_bytecode_convert_class/convert_java_annotations.cpp @@ -0,0 +1,76 @@ +/*******************************************************************\ + + Module: Unit tests for converting annotations + + Author: Diffblue Ltd. + +\*******************************************************************/ + +#include +#include +#include +#include +#include + +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_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()); + 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_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); + } + } + } +} diff --git a/jbmc/unit/java_bytecode/java_bytecode_convert_class/module_dependencies.txt b/jbmc/unit/java_bytecode/java_bytecode_convert_class/module_dependencies.txt new file mode 100644 index 00000000000..87cf9849bbf --- /dev/null +++ b/jbmc/unit/java_bytecode/java_bytecode_convert_class/module_dependencies.txt @@ -0,0 +1,3 @@ +java-testing-utils +testing-utils +java_bytecode