Skip to content

Commit cdaf0e7

Browse files
Enable parsing of annotations on parameters
1 parent 4ffc2a4 commit cdaf0e7

13 files changed

+162
-0
lines changed

jbmc/src/java_bytecode/java_bytecode_parse_tree.h

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

101+
std::vector<annotationst> parameter_annotations;
102+
101103
struct exceptiont
102104
{
103105
exceptiont()

jbmc/src/java_bytecode/java_bytecode_parser.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,16 @@ 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+
if(method.parameter_annotations.size() < parameter_count)
1252+
method.parameter_annotations.resize(parameter_count);
1253+
for(u2 param_no = 0; param_no < parameter_count; ++param_no)
1254+
rRuntimeAnnotation_attribute(method.parameter_annotations[param_no]);
1255+
}
12461256
else if(attribute_name == "Exceptions")
12471257
{
12481258
method.throws_exception_table = rexceptions_attribute();
Binary file not shown.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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(@ParameterAnnotation @RuntimeParameterAnnotation int p)
39+
{
40+
}
41+
}
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: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,61 @@
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+
const std::set<irep_idt> &expectedAnnotations)
47+
{
48+
REQUIRE(annotations.size() == expectedAnnotations.size());
49+
std::set<irep_idt> annotation_names;
50+
std::transform(
51+
annotations.begin(),
52+
annotations.end(),
53+
std::inserter(annotation_names, annotation_names.end()),
54+
[&annotations](const java_bytecode_parse_treet::annotationt &annotation)
55+
{
56+
return get_base_name(
57+
require_type::require_pointer(annotations[0].type, {}).subtype());
58+
});
59+
auto result = std::mismatch(
60+
annotation_names.begin(),
61+
annotation_names.end(),
62+
expectedAnnotations.begin());
63+
REQUIRE(result.first == annotation_names.end());
64+
}
1465

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

0 commit comments

Comments
 (0)