Skip to content

Commit 286fcc5

Browse files
committed
Lazy loading: load static initializers for needed classes. Fixes #846
This adds a wrapper for the needed-methods and needed-classes objects used to accumulate dependencies during lazy loading, such that the first time a class is noted needed, its static initializer if any is also queued for elaboration. The test case is borrowed from @reuk.
1 parent 340a5ff commit 286fcc5

11 files changed

+190
-47
lines changed
312 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class Problem {
2+
private static final Object[] DEFAULT = {};
3+
private Object data;
4+
5+
Problem() {
6+
this.data = DEFAULT;
7+
}
8+
9+
void checkInvariant() {
10+
assert data != null;
11+
}
12+
}
13+
14+
public class Main {
15+
public static void main(String[] args) {
16+
new Problem().checkInvariant();
17+
}
18+
}
631 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
CORE
2+
Main.class
3+
--lazy-methods
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
VERIFICATION SUCCESSFUL

src/java_bytecode/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
SRC = bytecode_info.cpp \
2+
ci_lazy_methods.cpp \
23
expr2java.cpp \
34
jar_file.cpp \
45
java_bytecode_convert_class.cpp \

src/java_bytecode/ci_lazy_methods.cpp

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*******************************************************************\
2+
3+
Module: Context-insensitive lazy methods container
4+
5+
Author: Chris Smowton, [email protected]
6+
7+
\*******************************************************************/
8+
9+
#include <string>
10+
11+
#include "ci_lazy_methods.h"
12+
13+
/*******************************************************************\
14+
15+
Function: ci_lazy_methodst::add_needed_method
16+
17+
Inputs: `method_symbol_name`: method name; must exist in symbol table.
18+
19+
Outputs:
20+
21+
Purpose: Notes `method_symbol_name` is referenced from some reachable
22+
function, and should therefore be elaborated.
23+
24+
\*******************************************************************/
25+
26+
void ci_lazy_methodst::add_needed_method(const irep_idt &method_symbol_name)
27+
{
28+
needed_methods.push_back(method_symbol_name);
29+
}
30+
31+
/*******************************************************************\
32+
33+
Function: java_bytecode_parsert::parse
34+
35+
Inputs: `class_symbol_name`: class name; must exist in symbol table.
36+
37+
Outputs: Returns true if `class_symbol_name` is new (not seen before).
38+
39+
Purpose: Notes class `class_symbol_name` will be instantiated, or
40+
a static field belonging to it will be accessed. Also notes
41+
that its static initializer is therefore reachable.
42+
43+
\*******************************************************************/
44+
45+
bool ci_lazy_methodst::add_needed_class(const irep_idt &class_symbol_name)
46+
{
47+
if(!needed_classes.insert(class_symbol_name).second)
48+
return false;
49+
const irep_idt clinit_name(id2string(class_symbol_name)+".<clinit>:()V");
50+
if(symbol_table.symbols.count(clinit_name))
51+
add_needed_method(clinit_name);
52+
return true;
53+
}

src/java_bytecode/ci_lazy_methods.h

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*******************************************************************\
2+
3+
Module: Context-insensitive lazy methods container
4+
5+
Author: Chris Smowton, [email protected]
6+
7+
\*******************************************************************/
8+
9+
#ifndef CPROVER_JAVA_BYTECODE_CI_LAZY_METHODS_H
10+
#define CPROVER_JAVA_BYTECODE_CI_LAZY_METHODS_H
11+
12+
#include <vector>
13+
#include <set>
14+
#include <util/symbol_table.h>
15+
16+
class ci_lazy_methodst
17+
{
18+
public:
19+
ci_lazy_methodst(
20+
std::vector<irep_idt> &_needed_methods,
21+
std::set<irep_idt> &_needed_classes,
22+
symbol_tablet &_symbol_table):
23+
needed_methods(_needed_methods),
24+
needed_classes(_needed_classes),
25+
symbol_table(_symbol_table)
26+
{}
27+
28+
void add_needed_method(const irep_idt &);
29+
// Returns true if new
30+
bool add_needed_class(const irep_idt &);
31+
32+
private:
33+
// needed_methods is a vector because it's used as a work-list
34+
// which is periodically cleared. It can't be relied upon to
35+
// contain all methods that have previously been elaborated.
36+
// It should be changed to a set if we develop the need to use
37+
// it that way.
38+
std::vector<irep_idt> &needed_methods;
39+
// needed_classes on the other hand is a true set of every class
40+
// found so far, so we can use a membership test to avoid
41+
// repeatedly exploring a class hierarchy.
42+
std::set<irep_idt> &needed_classes;
43+
symbol_tablet &symbol_table;
44+
};
45+
46+
#endif

src/java_bytecode/java_bytecode_convert_method.cpp

+23-13
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,7 @@ static void gather_symbol_live_ranges(
824824

825825
/*******************************************************************\
826826
827-
Function: java_bytecode_convert_methodt::convert_instructions
827+
Function: get_bytecode_type_width
828828
829829
Inputs:
830830
@@ -841,6 +841,18 @@ static unsigned get_bytecode_type_width(const typet &ty)
841841
return ty.get_unsigned_int(ID_width);
842842
}
843843

844+
/*******************************************************************\
845+
846+
Function: java_bytecode_convert_methodt::convert_instructions
847+
848+
Inputs:
849+
850+
Outputs:
851+
852+
Purpose:
853+
854+
\*******************************************************************/
855+
844856
codet java_bytecode_convert_methodt::convert_instructions(
845857
const methodt &method,
846858
const code_typet &method_type)
@@ -1165,8 +1177,8 @@ codet java_bytecode_convert_methodt::convert_instructions(
11651177
if(as_string(arg0.get(ID_identifier))
11661178
.find("<init>")!=std::string::npos)
11671179
{
1168-
if(needed_classes)
1169-
needed_classes->insert(classname);
1180+
if(lazy_methods)
1181+
lazy_methods->add_needed_class(classname);
11701182
code_type.set(ID_constructor, true);
11711183
}
11721184
else
@@ -1257,8 +1269,8 @@ codet java_bytecode_convert_methodt::convert_instructions(
12571269
{
12581270
// static binding
12591271
call.function()=symbol_exprt(arg0.get(ID_identifier), arg0.type());
1260-
if(needed_methods)
1261-
needed_methods->push_back(arg0.get(ID_identifier));
1272+
if(lazy_methods)
1273+
lazy_methods->add_needed_method(arg0.get(ID_identifier));
12621274
}
12631275

12641276
call.function().add_source_location()=loc;
@@ -1794,9 +1806,9 @@ codet java_bytecode_convert_methodt::convert_instructions(
17941806
symbol_exprt symbol_expr(arg0.type());
17951807
const auto &field_name=arg0.get_string(ID_component_name);
17961808
symbol_expr.set_identifier(arg0.get_string(ID_class)+"."+field_name);
1797-
if(needed_classes && arg0.type().id()==ID_symbol)
1809+
if(lazy_methods && arg0.type().id()==ID_symbol)
17981810
{
1799-
needed_classes->insert(
1811+
lazy_methods->add_needed_class(
18001812
to_symbol_type(arg0.type()).get_identifier());
18011813
}
18021814
results[0]=java_bytecode_promotion(symbol_expr);
@@ -1816,9 +1828,9 @@ codet java_bytecode_convert_methodt::convert_instructions(
18161828
symbol_exprt symbol_expr(arg0.type());
18171829
const auto &field_name=arg0.get_string(ID_component_name);
18181830
symbol_expr.set_identifier(arg0.get_string(ID_class)+"."+field_name);
1819-
if(needed_classes && arg0.type().id()==ID_symbol)
1831+
if(lazy_methods && arg0.type().id()==ID_symbol)
18201832
{
1821-
needed_classes->insert(
1833+
lazy_methods->add_needed_class(
18221834
to_symbol_type(arg0.type()).get_identifier());
18231835
}
18241836
c=code_assignt(symbol_expr, op[0]);
@@ -2413,15 +2425,13 @@ void java_bytecode_convert_method(
24132425
symbol_tablet &symbol_table,
24142426
message_handlert &message_handler,
24152427
size_t max_array_length,
2416-
safe_pointer<std::vector<irep_idt> > needed_methods,
2417-
safe_pointer<std::set<irep_idt> > needed_classes)
2428+
safe_pointer<ci_lazy_methodst> lazy_methods)
24182429
{
24192430
java_bytecode_convert_methodt java_bytecode_convert_method(
24202431
symbol_table,
24212432
message_handler,
24222433
max_array_length,
2423-
needed_methods,
2424-
needed_classes);
2434+
lazy_methods);
24252435

24262436
java_bytecode_convert_method(class_symbol, method);
24272437
}

src/java_bytecode/java_bytecode_convert_method.h

+3-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Author: Daniel Kroening, [email protected]
1414
#include <util/safe_pointer.h>
1515

1616
#include "java_bytecode_parse_tree.h"
17+
#include "ci_lazy_methods.h"
1718

1819
class class_hierarchyt;
1920

@@ -23,10 +24,8 @@ void java_bytecode_convert_method(
2324
symbol_tablet &symbol_table,
2425
message_handlert &message_handler,
2526
size_t max_array_length,
26-
safe_pointer<std::vector<irep_idt> > needed_methods,
27-
safe_pointer<std::set<irep_idt> > needed_classes);
27+
safe_pointer<ci_lazy_methodst> lazy_methods);
2828

29-
// Must provide both the optional parameters or neither.
3029
inline void java_bytecode_convert_method(
3130
const symbolt &class_symbol,
3231
const java_bytecode_parse_treet::methodt &method,
@@ -40,8 +39,7 @@ inline void java_bytecode_convert_method(
4039
symbol_table,
4140
message_handler,
4241
max_array_length,
43-
safe_pointer<std::vector<irep_idt> >::create_null(),
44-
safe_pointer<std::set<irep_idt> >::create_null());
42+
safe_pointer<ci_lazy_methodst>::create_null());
4543
}
4644

4745
void java_bytecode_convert_method_lazy(

src/java_bytecode/java_bytecode_convert_method_class.h

+4-6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Author: Daniel Kroening, [email protected]
1717
#include <analyses/cfg_dominators.h>
1818
#include "java_bytecode_parse_tree.h"
1919
#include "java_bytecode_convert_class.h"
20+
#include "ci_lazy_methods.h"
2021

2122
#include <vector>
2223
#include <list>
@@ -31,13 +32,11 @@ class java_bytecode_convert_methodt:public messaget
3132
symbol_tablet &_symbol_table,
3233
message_handlert &_message_handler,
3334
size_t _max_array_length,
34-
safe_pointer<std::vector<irep_idt> > _needed_methods,
35-
safe_pointer<std::set<irep_idt> > _needed_classes):
35+
safe_pointer<ci_lazy_methodst> _lazy_methods):
3636
messaget(_message_handler),
3737
symbol_table(_symbol_table),
3838
max_array_length(_max_array_length),
39-
needed_methods(_needed_methods),
40-
needed_classes(_needed_classes)
39+
lazy_methods(_lazy_methods)
4140
{
4241
}
4342

@@ -55,8 +54,7 @@ class java_bytecode_convert_methodt:public messaget
5554
protected:
5655
symbol_tablet &symbol_table;
5756
const size_t max_array_length;
58-
safe_pointer<std::vector<irep_idt> > needed_methods;
59-
safe_pointer<std::set<irep_idt> > needed_classes;
57+
safe_pointer<ci_lazy_methodst> lazy_methods;
6058

6159
irep_idt method_id;
6260
irep_idt current_method;

0 commit comments

Comments
 (0)