Skip to content

Commit 93b3e9c

Browse files
Merge pull request #2752 from NathanJPhillips/feature/parse-parameter-annotations
Enable parsing of annotations on parameters
2 parents 78d22fb + 03b7bf2 commit 93b3e9c

13 files changed

+177
-0
lines changed

jbmc/src/java_bytecode/java_bytecode_parse_tree.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@ struct java_bytecode_parse_treet
9898
return instructions.back();
9999
}
100100

101+
/// Java annotations that were applied to parameters of this method
102+
/// \remarks Each element in the vector corresponds to the annotations on
103+
/// the parameter of this method with the matching index. A parameter that
104+
/// does not have annotations can have an entry in this vector that is an
105+
/// empty annotationst. Trailing parameters that have no annotations may be
106+
/// entirely omitted from this vector.
107+
std::vector<annotationst> parameter_annotations;
108+
101109
struct exceptiont
102110
{
103111
exceptiont()

jbmc/src/java_bytecode/java_bytecode_parser.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,21 @@ void java_bytecode_parsert::rmethod_attribute(methodt &method)
12431243
{
12441244
rRuntimeAnnotation_attribute(method.annotations);
12451245
}
1246+
else if(
1247+
attribute_name == "RuntimeInvisibleParameterAnnotations" ||
1248+
attribute_name == "RuntimeVisibleParameterAnnotations")
1249+
{
1250+
u1 parameter_count = read_u1();
1251+
// There may be attributes for both runtime-visiible and rutime-invisible
1252+
// annotations, the length of either array may be longer than the other as
1253+
// trailing parameters without annotations are omitted.
1254+
// Extend our parameter_annotations if this one is longer than the one
1255+
// previously recorded (if any).
1256+
if(method.parameter_annotations.size() < parameter_count)
1257+
method.parameter_annotations.resize(parameter_count);
1258+
for(u2 param_no = 0; param_no < parameter_count; ++param_no)
1259+
rRuntimeAnnotation_attribute(method.parameter_annotations[param_no]);
1260+
}
12461261
else if(attribute_name == "Exceptions")
12471262
{
12481263
method.throws_exception_table = rexceptions_attribute();
Binary file not shown.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
@interface ClassAnnotation {
2+
}
3+
4+
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
5+
@interface RuntimeClassAnnotation {
6+
}
7+
8+
@interface FieldAnnotation {
9+
}
10+
11+
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
12+
@interface RuntimeFieldAnnotation {
13+
}
14+
15+
@interface MethodAnnotation {
16+
}
17+
18+
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
19+
@interface RuntimeMethodAnnotation {
20+
}
21+
22+
@interface ParameterAnnotation {
23+
}
24+
25+
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
26+
@interface RuntimeParameterAnnotation {
27+
}
28+
29+
@ClassAnnotation
30+
@RuntimeClassAnnotation
31+
public class AnnotationsEverywhere {
32+
@FieldAnnotation
33+
@RuntimeFieldAnnotation
34+
public int x;
35+
36+
@MethodAnnotation
37+
@RuntimeMethodAnnotation
38+
public void foo(
39+
@RuntimeParameterAnnotation int p,
40+
@ParameterAnnotation int q)
41+
{
42+
}
43+
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

jbmc/unit/java_bytecode/java_bytecode_parser/parse_java_annotations.cpp

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,60 @@
77
\*******************************************************************/
88

99
#include <java-testing-utils/load_java_class.h>
10+
#include <java-testing-utils/require_type.h>
1011
#include <java_bytecode/java_bytecode_convert_class.h>
1112
#include <java_bytecode/java_bytecode_parse_tree.h>
1213
#include <java_bytecode/java_types.h>
1314
#include <testing-utils/catch.hpp>
15+
#include <testing-utils/free_form_cmdline.h>
16+
#include <testing-utils/message.h>
17+
18+
class test_java_bytecode_languaget : public java_bytecode_languaget
19+
{
20+
public:
21+
std::vector<irep_idt> get_parsed_class_names()
22+
{
23+
std::vector<irep_idt> parsed_class_names;
24+
for(const auto &named_class
25+
: java_class_loader.get_class_with_overlays_map())
26+
{
27+
parsed_class_names.push_back(named_class.first);
28+
}
29+
return parsed_class_names;
30+
}
31+
32+
java_class_loadert::parse_tree_with_overlayst &get_parse_trees_for_class(
33+
const irep_idt &class_name)
34+
{
35+
return java_class_loader.get_class_with_overlays_map().at(class_name);
36+
}
37+
};
38+
39+
static irep_idt get_base_name(const typet &type)
40+
{
41+
return type.get(ID_C_base_name);
42+
}
43+
44+
static void require_matching_annotations(
45+
const java_bytecode_parse_treet::annotationst &annotations,
46+
std::vector<irep_idt> expected_annotations)
47+
{
48+
std::vector<irep_idt> annotation_names;
49+
std::transform(
50+
annotations.begin(),
51+
annotations.end(),
52+
std::back_inserter(annotation_names),
53+
[](const java_bytecode_parse_treet::annotationt &annotation)
54+
{
55+
return get_base_name(
56+
require_type::require_pointer(annotation.type, {}).subtype());
57+
});
58+
std::sort(annotation_names.begin(), annotation_names.end());
59+
std::sort(expected_annotations.begin(), expected_annotations.end());
60+
REQUIRE_THAT(
61+
annotation_names,
62+
Catch::Matchers::Equals(expected_annotations));
63+
}
1464

1565
// See
1666
// https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.16.1
@@ -116,5 +166,66 @@ SCENARIO(
116166
REQUIRE(id2string(another_dave) == "Another Dave");
117167
}
118168
}
169+
WHEN("Parsing a class with annotations everywhere")
170+
{
171+
free_form_cmdlinet command_line;
172+
command_line.add_flag("no-lazy-methods");
173+
command_line.add_flag("no-refine-strings");
174+
test_java_bytecode_languaget language;
175+
language.set_message_handler(null_message_handler);
176+
language.get_language_options(command_line);
177+
178+
std::istringstream java_code_stream("ignored");
179+
language.parse(java_code_stream, "AnnotationsEverywhere.class");
180+
const java_class_loadert::parse_tree_with_overlayst &parse_trees =
181+
language.get_parse_trees_for_class("AnnotationsEverywhere");
182+
REQUIRE(parse_trees.size() == 1);
183+
const java_bytecode_parse_treet::classt &parsed_class =
184+
parse_trees.front().parsed_class;
185+
186+
THEN("Only the correct annotations should be on the class")
187+
{
188+
require_matching_annotations(
189+
parsed_class.annotations,
190+
{ "ClassAnnotation", "RuntimeClassAnnotation" });
191+
}
192+
193+
THEN("Only the correct annotations should be on the field")
194+
{
195+
REQUIRE(parsed_class.fields.size() == 1);
196+
const java_bytecode_parse_treet::fieldt &field =
197+
parsed_class.fields.front();
198+
require_matching_annotations(
199+
field.annotations, { "FieldAnnotation", "RuntimeFieldAnnotation" });
200+
}
201+
202+
auto method_it = std::find_if(
203+
parsed_class.methods.begin(),
204+
parsed_class.methods.end(),
205+
[](const java_bytecode_parse_treet::methodt &method)
206+
{
207+
return method.name == "foo";
208+
});
209+
REQUIRE(method_it != parsed_class.methods.end());
210+
const java_bytecode_parse_treet::methodt &method = *method_it;
211+
212+
THEN("Only the correct annotations should be on the method")
213+
{
214+
require_matching_annotations(
215+
method.annotations,
216+
{ "MethodAnnotation", "RuntimeMethodAnnotation" });
217+
}
218+
219+
THEN("Only the correct annotations should be on the parameter")
220+
{
221+
REQUIRE(method.parameter_annotations.size() == 2);
222+
require_matching_annotations(
223+
method.parameter_annotations[0],
224+
{ "RuntimeParameterAnnotation" });
225+
require_matching_annotations(
226+
method.parameter_annotations[1],
227+
{ "ParameterAnnotation" });
228+
}
229+
}
119230
}
120231
}

0 commit comments

Comments
 (0)