Skip to content

Commit 37539f2

Browse files
committed
JCI: don't load method instructions
This speeds loading 40000 classes up by around 3x.
1 parent df5e3b8 commit 37539f2

File tree

6 files changed

+120
-10
lines changed

6 files changed

+120
-10
lines changed

jbmc/src/java_bytecode/java_bytecode_parser.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@ Author: Daniel Kroening, kroening@kroening.com
3232
class java_bytecode_parsert:public parsert
3333
{
3434
public:
35-
java_bytecode_parsert()
35+
explicit java_bytecode_parsert(bool skip_instructions)
36+
: skip_instructions(skip_instructions)
3637
{
37-
get_bytecodes();
38+
if(!skip_instructions)
39+
get_bytecodes();
3840
}
3941

4042
virtual bool parse();
@@ -78,6 +80,7 @@ class java_bytecode_parsert:public parsert
7880
};
7981

8082
std::vector<bytecodet> bytecodes;
83+
bool skip_instructions = false;
8184

8285
pool_entryt &pool_entry(u2 index)
8386
{
@@ -1215,7 +1218,7 @@ void java_bytecode_parsert::rmethod_attribute(methodt &method)
12151218

12161219
irep_idt attribute_name=pool_entry(attribute_name_index).s;
12171220

1218-
if(attribute_name=="Code")
1221+
if(attribute_name=="Code" && !skip_instructions)
12191222
{
12201223
UNUSED_u2(max_stack);
12211224
UNUSED_u2(max_locals);
@@ -1861,9 +1864,12 @@ void java_bytecode_parsert::rmethod(classt &parsed_class)
18611864
}
18621865

18631866
optionalt<java_bytecode_parse_treet>
1864-
java_bytecode_parse(std::istream &istream, message_handlert &message_handler)
1867+
java_bytecode_parse(
1868+
std::istream &istream,
1869+
message_handlert &message_handler,
1870+
bool skip_instructions)
18651871
{
1866-
java_bytecode_parsert java_bytecode_parser;
1872+
java_bytecode_parsert java_bytecode_parser(skip_instructions);
18671873
java_bytecode_parser.in=&istream;
18681874
java_bytecode_parser.set_message_handler(message_handler);
18691875

@@ -1878,7 +1884,10 @@ java_bytecode_parse(std::istream &istream, message_handlert &message_handler)
18781884
}
18791885

18801886
optionalt<java_bytecode_parse_treet>
1881-
java_bytecode_parse(const std::string &file, message_handlert &message_handler)
1887+
java_bytecode_parse(
1888+
const std::string &file,
1889+
message_handlert &message_handler,
1890+
bool skip_instructions)
18821891
{
18831892
std::ifstream in(file, std::ios::binary);
18841893

@@ -1890,7 +1899,7 @@ java_bytecode_parse(const std::string &file, message_handlert &message_handler)
18901899
return {};
18911900
}
18921901

1893-
return java_bytecode_parse(in, message_handler);
1902+
return java_bytecode_parse(in, message_handler, skip_instructions);
18941903
}
18951904

18961905
/// Parses the local variable type table of a method. The LVTT holds generic

jbmc/src/java_bytecode/java_bytecode_parser.h

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,28 @@ Author: Daniel Kroening, kroening@kroening.com
1616

1717
struct java_bytecode_parse_treet;
1818

19+
/// Attempt to parse a Java class from the given file.
20+
/// \param file: file to load from
21+
/// \param msg: gets log messages
22+
/// \param skip_instructions: if true, the loaded class's methods will all be
23+
/// empty. Saves time and memory for consumers that only want signature info.
24+
/// \return parse tree, or Empty on failure
1925
optionalt<java_bytecode_parse_treet>
20-
java_bytecode_parse(const std::string &file, class message_handlert &);
21-
26+
java_bytecode_parse(
27+
const std::string &file,
28+
class message_handlert &msg,
29+
bool skip_instructions = false);
30+
31+
/// Attempt to parse a Java class from the given stream
32+
/// \param stream: stream to load from
33+
/// \param msg: gets log messages
34+
/// \param skip_instructions: if true, the loaded class's methods will all be
35+
/// empty. Saves time and memory for consumers that only want signature info.
36+
/// \return parse tree, or Empty on failure
2237
optionalt<java_bytecode_parse_treet>
23-
java_bytecode_parse(std::istream &, class message_handlert &);
38+
java_bytecode_parse(
39+
std::istream &stream,
40+
class message_handlert &msg,
41+
bool skip_instructions = false);
2442

2543
#endif // CPROVER_JAVA_BYTECODE_JAVA_BYTECODE_PARSER_H

jbmc/unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ SRC += java_bytecode/ci_lazy_methods/lazy_load_lambdas.cpp \
3636
java_bytecode/java_bytecode_parse_generics/parse_lvtt_generic_local_vars.cpp \
3737
java_bytecode/java_bytecode_parse_lambdas/java_bytecode_convert_class_lambda_method_handles.cpp \
3838
java_bytecode/java_bytecode_parse_lambdas/java_bytecode_parse_lambda_method_table.cpp \
39+
java_bytecode/java_bytecode_parser/parse_class_without_instructions.cpp \
3940
java_bytecode/java_bytecode_parser/parse_java_annotations.cpp \
4041
java_bytecode/java_bytecode_parser/parse_java_attributes.cpp \
4142
java_bytecode/java_bytecode_parser/parse_java_class.cpp \
Binary file not shown.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
public class Trivial {
2+
3+
int x;
4+
public Trivial() { x = 1; }
5+
public void f() { x++; };
6+
7+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*******************************************************************\
2+
3+
Module: Unit tests to parse a class without its methods' instructions
4+
5+
Author: Diffblue Ltd.
6+
7+
\*******************************************************************/
8+
9+
#include <java-testing-utils/load_java_class.h>
10+
#include <java_bytecode/java_bytecode_parse_tree.h>
11+
#include <java_bytecode/java_bytecode_parser.h>
12+
#include <util/message.h>
13+
14+
#include <testing-utils/catch.hpp>
15+
16+
SCENARIO(
17+
"java_bytecode_parse_class_without_instructions",
18+
"[core][java_bytecode][java_bytecode_parser]")
19+
{
20+
null_message_handlert null_out;
21+
22+
WHEN("Loading a class without instructions")
23+
{
24+
auto loaded =
25+
java_bytecode_parse(
26+
"./java_bytecode/java_bytecode_parser/Trivial.class", null_out, true);
27+
THEN("Loading should succeed")
28+
{
29+
REQUIRE(loaded);
30+
const auto &loaded_class = *loaded;
31+
32+
THEN("It should have two methods")
33+
{
34+
REQUIRE(loaded_class.parsed_class.methods.size() == 2);
35+
36+
const auto &methodone = *loaded_class.parsed_class.methods.begin();
37+
const auto &methodtwo =
38+
*std::next(loaded_class.parsed_class.methods.begin());
39+
40+
THEN("Neither method should have instructions")
41+
{
42+
REQUIRE(methodone.instructions.size() == 0);
43+
REQUIRE(methodtwo.instructions.size() == 0);
44+
}
45+
}
46+
}
47+
}
48+
49+
WHEN("Loading the same class normally")
50+
{
51+
auto loaded =
52+
java_bytecode_parse(
53+
"./java_bytecode/java_bytecode_parser/Trivial.class", null_out, false);
54+
THEN("Loading should succeed")
55+
{
56+
REQUIRE(loaded);
57+
const auto &loaded_class = *loaded;
58+
59+
THEN("It should have two methods")
60+
{
61+
REQUIRE(loaded_class.parsed_class.methods.size() == 2);
62+
63+
const auto &methodone = *loaded_class.parsed_class.methods.begin();
64+
const auto &methodtwo =
65+
*std::next(loaded_class.parsed_class.methods.begin());
66+
67+
THEN("Both methods should have instructions")
68+
{
69+
REQUIRE(methodone.instructions.size() != 0);
70+
REQUIRE(methodtwo.instructions.size() != 0);
71+
}
72+
}
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)