Skip to content

Commit d64e00b

Browse files
authored
Merge pull request diffblue#220 from diffblue/feature/introducing_java-class-info_tool
SEC-68 : Introducing java-class-info executable.
2 parents c7afca6 + 3ab1fc9 commit d64e00b

File tree

3 files changed

+288
-0
lines changed

3 files changed

+288
-0
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ add_subdirectory(summaries)
2020
add_subdirectory(taint-analysis)
2121
add_subdirectory(taint-slicer)
2222
add_subdirectory(util)
23+
add_subdirectory(java-class-info)
2324

src/java-class-info/CMakeLists.txt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
add_executable(java-class-info
2+
java_class_info.cpp
3+
)
4+
5+
find_package(Git)
6+
if(GIT_FOUND)
7+
execute_process(
8+
COMMAND "${GIT_EXECUTABLE}" "describe" "--tags" "--always" "--long"
9+
OUTPUT_VARIABLE git_repo_version
10+
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
11+
OUTPUT_STRIP_TRAILING_WHITESPACE
12+
)
13+
message(STATUS "Executable is version ${git_repo_version}")
14+
else()
15+
message(FATAL_ERROR "Git is not installed, cannot generate version string")
16+
endif()
17+
18+
target_compile_definitions(java-class-info
19+
PRIVATE "-DJAVA_CLASS_INFO_VERSION=\"${git_repo_version}\""
20+
USE_BOOST=1
21+
)
22+
23+
generic_includes(java-class-info)
24+
target_link_libraries(java-class-info
25+
# cbmc includes
26+
java_bytecode
27+
langapi
28+
json
29+
util
30+
)
31+
32+
install(TARGETS java-class-info DESTINATION bin)
33+
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
#include <util/ui_message.h>
2+
#include <util/parse_options.h>
3+
#include <util/file_util.h>
4+
#include <json/json_parser.h>
5+
#include <iostream>
6+
#include <fstream>
7+
#include <java_bytecode/java_bytecode_parse_tree.h>
8+
#include <java_bytecode/java_bytecode_parser.h>
9+
10+
class cmdline_optionst final:
11+
public parse_options_baset
12+
{
13+
public:
14+
cmdline_optionst(int argc, const char **argv);
15+
16+
void help() override;
17+
int doit() override;
18+
19+
private:
20+
ui_message_handlert ui_message_handler;
21+
};
22+
23+
cmdline_optionst::cmdline_optionst(int argc, const char **argv):
24+
parse_options_baset("(version)(in-json):(out-json):", argc, argv),
25+
ui_message_handler(cmdline, "java-class-info " JAVA_CLASS_INFO_VERSION)
26+
{}
27+
28+
void cmdline_optionst::help()
29+
{
30+
std::cout <<
31+
"java-class-info v." JAVA_CLASS_INFO_VERSION " - Copyright (C) 2017\n\n"
32+
"Usages:\n"
33+
" java-class-info --help\n"
34+
" java-class-info --version\n"
35+
" java-class-info --in-json <file> --out-json <file>\n\n"
36+
"Options: Explanation:\n"
37+
"--help: Prints this help message.\n"
38+
"--version: Prints id of Git commit this tool was\n"
39+
" built from.\n"
40+
"--in-json: A pathname of an input JSON file comprising\n"
41+
" of an array of pathnames of Java class\n"
42+
" files whose properties should be read.\n"
43+
"--out-json: A pathname of an output JSON file into\n"
44+
" which the properties of the all Java class\n"
45+
" files (listed in JSON file; see --in-json)\n"
46+
" will be written.\n";
47+
}
48+
49+
int cmdline_optionst::doit()
50+
{
51+
messaget msg(ui_message_handler);
52+
53+
if(cmdline.isset("version"))
54+
{
55+
msg.status() << JAVA_CLASS_INFO_VERSION << messaget::eom;
56+
return 0;
57+
}
58+
if(!cmdline.isset("in-json"))
59+
{
60+
msg.error()
61+
<< "ERROR: The option --in-json was not specified."
62+
<< messaget::eom;
63+
return -1;
64+
}
65+
const std::string in_json_pathname=cmdline.get_value("in-json");
66+
if(!fileutl_file_exists(in_json_pathname))
67+
{
68+
msg.error()
69+
<< "ERROR: The input file '" << in_json_pathname
70+
<< "' does not exist." << messaget::eom;
71+
return -2;
72+
}
73+
jsont cfg;
74+
if(parse_json(in_json_pathname, ui_message_handler, cfg))
75+
{
76+
msg.error()
77+
<< "ERROR: The input file '" << in_json_pathname
78+
<< "' is not a valid JSON file." << messaget::eom;
79+
return -3;
80+
}
81+
if(!cfg.is_array())
82+
{
83+
msg.error()
84+
<< "ERROR: The input JSON file '"
85+
<< in_json_pathname
86+
<< "' does not consists of one array of strings (paths to Java "
87+
<< "class files)." << messaget::eom;
88+
return -4;
89+
}
90+
if(!cmdline.isset("out-json"))
91+
{
92+
msg.error()
93+
<< "ERROR: The option --out-json was not specified." << messaget::eom;
94+
return -5;
95+
}
96+
const std::string out_json_pathname=cmdline.get_value("out-json");
97+
if(fileutl_is_directory(out_json_pathname))
98+
{
99+
msg.error()
100+
<< "ERROR: The output path '" << out_json_pathname
101+
<< "' references an existing directory." << messaget::eom;
102+
return -6;
103+
}
104+
105+
json_objectt results;
106+
json_arrayt errors;
107+
108+
int ret_code=0;
109+
for(auto const &json_array_element : cfg.array)
110+
{
111+
if(!json_array_element.is_string())
112+
{
113+
std::stringstream sstr;
114+
sstr
115+
<< "Encountered a non-string element in the array of paths "
116+
<< "in the input JSON file.";
117+
msg.warning()
118+
<< "WARNING: "
119+
<< sstr.str()
120+
<< " Skipping this file."
121+
<< messaget::eom;
122+
errors.push_back(json_stringt(sstr.str()));
123+
ret_code=-7;
124+
continue;
125+
}
126+
127+
const std::string &class_file_pathname=json_array_element.value;
128+
if(!fileutl_file_exists(class_file_pathname))
129+
{
130+
std::stringstream sstr;
131+
sstr
132+
<< "Java class file '" << class_file_pathname
133+
<< "' specified in '" << in_json_pathname
134+
<< "' does not exist.";
135+
msg.warning()
136+
<< "WARNING: "
137+
<< sstr.str()
138+
<< " Skipping this file."
139+
<< messaget::eom;
140+
errors.push_back(json_stringt(sstr.str()));
141+
ret_code=-7;
142+
continue;
143+
}
144+
145+
jsont class_json;
146+
try
147+
{
148+
java_bytecode_parse_treet parse_tree;
149+
if(java_bytecode_parse(
150+
class_file_pathname,
151+
parse_tree,
152+
ui_message_handler))
153+
{
154+
std::stringstream sstr;
155+
sstr
156+
<< "The load of Java class file '"
157+
<< class_file_pathname
158+
<< "' specified in '"
159+
<< in_json_pathname
160+
<< "' has failed (by error return code from "
161+
<< "'java_bytecode_parse').";
162+
msg.warning()
163+
<< "WARNING: "
164+
<< sstr.str()
165+
<< " Skipping this file."
166+
<< messaget::eom;
167+
class_json=json_stringt(sstr.str());
168+
ret_code=-7;
169+
}
170+
else
171+
{
172+
INVARIANT(parse_tree.loading_successful,
173+
"False returned from 'java_bytecode_parse' should imply "
174+
"'parse_tree.loading_successful' is True.");
175+
176+
json_objectt class_props;
177+
{
178+
class_props["name"]=
179+
json_stringt(id2string(parse_tree.parsed_class.name));
180+
class_props["extends"]=
181+
json_stringt(id2string(parse_tree.parsed_class.extends));
182+
{
183+
json_arrayt interfaces;
184+
for(auto const &interface : parse_tree.parsed_class.implements)
185+
interfaces.push_back(json_stringt(id2string(interface)));
186+
class_props["implements"]=interfaces;
187+
}
188+
class_props["is_abstract"]=jsont::json_boolean(
189+
parse_tree.parsed_class.is_abstract);
190+
class_props["is_public"]=jsont::json_boolean(
191+
parse_tree.parsed_class.is_public);
192+
class_props["is_private"]=jsont::json_boolean(
193+
parse_tree.parsed_class.is_private);
194+
class_props["is_protected"]=jsont::json_boolean(
195+
parse_tree.parsed_class.is_protected);
196+
class_props["is_enum"]=jsont::json_boolean(
197+
parse_tree.parsed_class.is_enum);
198+
}
199+
class_json=class_props;
200+
}
201+
}
202+
catch(...)
203+
{
204+
std::stringstream sstr;
205+
sstr
206+
<< "The load of Java class file '"
207+
<< class_file_pathname
208+
<< "' specified in '"
209+
<< in_json_pathname
210+
<< "' has failed (by throwing an exception)."
211+
<< "";
212+
msg.warning()
213+
<< "WARNING: "
214+
<< sstr.str()
215+
<< " Skipping this file."
216+
<< messaget::eom;
217+
class_json=json_stringt(sstr.str());
218+
ret_code=-7;
219+
}
220+
if(class_json.is_string())
221+
errors.push_back(class_json);
222+
else
223+
results[class_file_pathname]=class_json;
224+
}
225+
226+
std::ofstream ofile(out_json_pathname);
227+
if(!ofile)
228+
{
229+
msg.error()
230+
<< "ERROR: Cannot open the output JSON file '"
231+
<< out_json_pathname
232+
<< "' for writing."
233+
<< messaget::eom;
234+
return -8;
235+
}
236+
json_objectt result;
237+
result["results"]=results;
238+
result["errors"]=errors;
239+
ofile << result;
240+
241+
return ret_code;
242+
}
243+
244+
#ifdef _MSC_VER
245+
int wmain(int argc, const wchar_t **argv_wide)
246+
{
247+
const char **argv=narrow_argv(argc, argv_wide);
248+
#else
249+
int main(int argc, const char **argv)
250+
{
251+
#endif
252+
cmdline_optionst options(argc, argv);
253+
return options.main();
254+
}

0 commit comments

Comments
 (0)