Skip to content

Commit db4f688

Browse files
authored
Merge pull request diffblue#497 from diffblue/jd/feature/autowire_detection
Make java-class-info generate additional information for DI generation
2 parents a59f709 + 4b4c343 commit db4f688

File tree

2 files changed

+145
-4
lines changed

2 files changed

+145
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export interface AnnotationConfig {
2+
beans: Beans;
3+
}
4+
5+
export interface Beans {
6+
[name: string]: Bean;
7+
}
8+
9+
export interface Bean {
10+
fields: AnnotatedFields;
11+
}
12+
13+
export interface AnnotatedFields {
14+
[name: string]: AnnotatedField;
15+
}
16+
17+
export interface AnnotatedField {
18+
type: string;
19+
}

src/java-class-info/java_class_info.cpp

+126-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include <java_bytecode/java_bytecode_parser.h>
99

1010
#include <boost/filesystem.hpp>
11+
#include <java_bytecode/java_types.h>
12+
#include <utility>
1113

1214
class cmdline_optionst final:
1315
public parse_options_baset
@@ -22,9 +24,12 @@ class cmdline_optionst final:
2224
ui_message_handlert ui_message_handler;
2325
};
2426

25-
cmdline_optionst::cmdline_optionst(int argc, const char **argv):
26-
parse_options_baset("(version)(in-json):(out-json):", argc, argv),
27-
ui_message_handler(cmdline, "java-class-info " JAVA_CLASS_INFO_VERSION)
27+
cmdline_optionst::cmdline_optionst(int argc, const char **argv)
28+
: parse_options_baset(
29+
"(version)(in-json):(out-json):(out-di-json):",
30+
argc,
31+
argv),
32+
ui_message_handler(cmdline, "java-class-info " JAVA_CLASS_INFO_VERSION)
2833
{}
2934

3035
void cmdline_optionst::help()
@@ -48,6 +53,41 @@ void cmdline_optionst::help()
4853
" will be written.\n";
4954
}
5055

56+
bool has_annotation_in_list(
57+
const std::vector<java_bytecode_parse_treet::annotationt> &annotations,
58+
const std::set<irep_idt> &names_to_match)
59+
{
60+
if(annotations.empty())
61+
{
62+
return false;
63+
}
64+
65+
std::vector<irep_idt> annotation_names;
66+
std::transform(
67+
annotations.begin(),
68+
annotations.end(),
69+
std::inserter(annotation_names, annotation_names.end()),
70+
[](const java_bytecode_parse_treet::annotationt &annotation) {
71+
INVARIANT(
72+
annotation.type.id() == ID_pointer,
73+
"Annotation type should be pointer.");
74+
return annotation.type.subtype().get(ID_C_base_name);
75+
});
76+
77+
// Set_intersection requires a sort to have been done.
78+
std::sort(annotation_names.begin(), annotation_names.end());
79+
80+
std::set<irep_idt> matching_annotation_names;
81+
std::set_intersection(
82+
annotation_names.begin(),
83+
annotation_names.end(),
84+
names_to_match.begin(),
85+
names_to_match.end(),
86+
std::inserter(matching_annotation_names, matching_annotation_names.end()));
87+
88+
return !matching_annotation_names.empty();
89+
}
90+
5191
int cmdline_optionst::doit()
5292
{
5393
messaget msg(ui_message_handler);
@@ -104,9 +144,32 @@ int cmdline_optionst::doit()
104144
return -6;
105145
}
106146

147+
struct annotated_fieldt
148+
{
149+
annotated_fieldt(irep_idt name, std::string type)
150+
: name(name), type(std::move(type))
151+
{
152+
}
153+
154+
irep_idt name;
155+
std::string type;
156+
};
157+
158+
struct beant
159+
{
160+
explicit beant(irep_idt name) : name(name)
161+
{
162+
}
163+
164+
irep_idt name;
165+
std::vector<annotated_fieldt> fields;
166+
};
167+
168+
const std::string di_out_path = cmdline.get_value("out-di-json");
169+
bool enable_di_scan = !di_out_path.empty();
107170
json_objectt results;
108171
json_arrayt errors;
109-
172+
std::vector<beant> beans;
110173
int ret_code=0;
111174
for(auto const &json_array_element : cfg.array)
112175
{
@@ -198,6 +261,35 @@ int cmdline_optionst::doit()
198261
}
199262
class_json=class_props;
200263
}
264+
265+
if(enable_di_scan)
266+
{
267+
static std::set<irep_idt> bean_annotations = {
268+
"org.springframework.stereotype.Component"
269+
};
270+
271+
static std::set<irep_idt> autowire_annotation = {
272+
"org.springframework.beans.factory.annotation.Autowired"
273+
};
274+
275+
const java_bytecode_parse_treet::classt &parsed_class =
276+
parse_tree->parsed_class;
277+
if(has_annotation_in_list(parsed_class.annotations, bean_annotations))
278+
{
279+
beant bean(parsed_class.name);
280+
for(const java_bytecode_parse_treet::fieldt &field :
281+
parsed_class.fields)
282+
{
283+
if(has_annotation_in_list(field.annotations, autowire_annotation))
284+
bean.fields.emplace_back(
285+
field.name,
286+
field.signature.has_value() ? field.signature.value()
287+
: field.descriptor);
288+
}
289+
290+
beans.push_back(std::move(bean));
291+
}
292+
}
201293
}
202294
catch(...)
203295
{
@@ -238,6 +330,36 @@ int cmdline_optionst::doit()
238330
result["errors"]=errors;
239331
ofile << result;
240332

333+
if(enable_di_scan)
334+
{
335+
std::ofstream di_filename(di_out_path);
336+
if(!di_filename)
337+
{
338+
msg.error()
339+
<< "ERROR: Cannot open the output JSON file '"
340+
<< di_out_path
341+
<< "' for writing."
342+
<< messaget::eom;
343+
return -8;
344+
}
345+
346+
json_objectt root;
347+
json_objectt &json_beans = root["beans"].make_object();
348+
for(const beant &bean : beans)
349+
{
350+
json_objectt &json_bean = json_beans[id2string(bean.name)].make_object();
351+
json_objectt &json_fields = json_bean["fields"].make_object();
352+
for(const annotated_fieldt &field : bean.fields)
353+
{
354+
json_objectt &json_field =
355+
json_fields[id2string(field.name)].make_object();
356+
json_field["type"] = json_stringt(field.type);
357+
}
358+
}
359+
360+
di_filename << root;
361+
}
362+
241363
return ret_code;
242364
}
243365

0 commit comments

Comments
 (0)