Skip to content

Commit d4d4a9a

Browse files
committed
Tolerate stub static fields defined on non-stub types
Normally this situation (a reference to static field A.x, when neither A nor any of its ancestors either define a field x or are opaque stubs) would be an error, probably indicating a class version mismatch, but at present some of our library models intentionally exploit this behaviour to obtain a nondet- initialised field. Therefore for the time being we tolerate the situation, initialising such fields in __CPROVER_initialize, as we cannot attach a synthetic clinit method to a non-stub type.
1 parent 873e1f6 commit d4d4a9a

6 files changed

+76
-23
lines changed

src/java_bytecode/java_bytecode_language.cpp

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -390,12 +390,17 @@ static void generate_constant_global_variables(
390390
/// \param symbol_basename: new symbol basename
391391
/// \param symbol_type: new symbol type
392392
/// \param class_id: class id that directly encloses this static field
393+
/// \param force_nondet_init: if true, always leave the symbol's value nil so it
394+
/// gets nondet initialised during __CPROVER_initialize. Otherwise, pointer-
395+
/// typed globals are initialised null and we expect a synthetic clinit method
396+
/// to be created later.
393397
static void create_stub_global_symbol(
394398
symbol_table_baset &symbol_table,
395399
const irep_idt &symbol_id,
396400
const irep_idt &symbol_basename,
397401
const typet &symbol_type,
398-
const irep_idt &class_id)
402+
const irep_idt &class_id,
403+
bool force_nondet_init)
399404
{
400405
symbolt new_symbol;
401406
new_symbol.is_static_lifetime = true;
@@ -415,7 +420,7 @@ static void create_stub_global_symbol(
415420
// If pointer-typed, initialise to null and a static initialiser will be
416421
// created to initialise on first reference. If primitive-typed, specify
417422
// nondeterministic initialisation by setting a nil value.
418-
if(symbol_type.id() == ID_pointer)
423+
if(symbol_type.id() == ID_pointer && !force_nondet_init)
419424
new_symbol.value = null_pointer_exprt(to_pointer_type(symbol_type));
420425
else
421426
new_symbol.value.make_nil();
@@ -454,7 +459,7 @@ static irep_idt get_any_incomplete_ancestor(
454459
classes_to_check.end(), parents.begin(), parents.end());
455460
}
456461

457-
INVARIANT(false, "input class id should have some incomplete ancestor");
462+
return irep_idt();
458463
}
459464

460465
/// Search for getstatic and putstatic instructions in a class' bytecode and
@@ -464,10 +469,13 @@ static irep_idt get_any_incomplete_ancestor(
464469
/// \param parse_tree: class bytecode
465470
/// \param symbol_table: symbol table; may gain new symbols
466471
/// \param class_hierarchy: global class hierarchy
472+
/// \param log: message handler used to log warnings when stub static fields are
473+
/// found belonging to non-stub classes.
467474
static void create_stub_global_symbols(
468475
const java_bytecode_parse_treet &parse_tree,
469476
symbol_table_baset &symbol_table,
470-
const class_hierarchyt &class_hierarchy)
477+
const class_hierarchyt &class_hierarchy,
478+
messaget &log)
471479
{
472480
namespacet ns(symbol_table);
473481
for(const auto &method : parse_tree.parsed_class.methods)
@@ -507,6 +515,28 @@ static void create_stub_global_symbols(
507515
get_any_incomplete_ancestor(
508516
class_id, symbol_table, class_hierarchy);
509517

518+
// If there are no incomplete ancestors to ascribe the missing field
519+
// to, we must have an incomplete model of a class or simply a
520+
// version mismatch of some kind. Normally this would be an error, but
521+
// our models library currently triggers this error in some cases
522+
// (notably java.lang.System, which is missing System.in/out/err).
523+
// Therefore for this case we ascribe the missing field to the class
524+
// it was directly referenced from, and fall back to initialising the
525+
// field in __CPROVER_initialize, rather than try to create or augment
526+
// a clinit method for a non-stub class.
527+
528+
bool no_incomplete_ancestors = add_to_class_id.empty();
529+
if(no_incomplete_ancestors)
530+
{
531+
add_to_class_id = class_id;
532+
533+
// TODO forbid this again once the models library has been checked
534+
// for missing static fields.
535+
log.warning() << "Stub static field " << component << " found for "
536+
<< "non-stub type " << class_id << ". In future this "
537+
<< "will be a fatal error." << messaget::eom;
538+
}
539+
510540
irep_idt identifier =
511541
id2string(add_to_class_id) + "." + id2string(component);
512542

@@ -515,7 +545,8 @@ static void create_stub_global_symbols(
515545
identifier,
516546
component,
517547
instruction.args[0].type(),
518-
add_to_class_id);
548+
add_to_class_id,
549+
no_incomplete_ancestors);
519550
}
520551
}
521552
}
@@ -637,7 +668,7 @@ bool java_bytecode_languaget::typecheck(
637668
for(const auto &c : java_class_loader.class_map)
638669
{
639670
create_stub_global_symbols(
640-
c.second, symbol_table_journal, class_hierarchy);
671+
c.second, symbol_table_journal, class_hierarchy, *this);
641672
}
642673

643674
stub_global_initializer_factory.create_stub_global_initializer_symbols(

src/java_bytecode/java_entry_point.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ static void java_static_lifetime_init(
118118
allow_null=false;
119119
if(allow_null && is_java_string_literal_id(nameid))
120120
allow_null=false;
121+
if(allow_null && is_non_null_library_global(nameid))
122+
allow_null = false;
121123
}
122124
gen_nondet_init(
123125
sym.symbol_expr(),

src/java_bytecode/java_static_initializers.cpp

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Author: Chris Smowton, [email protected]
88

99
#include "java_static_initializers.h"
1010
#include "java_object_factory.h"
11+
#include "java_utils.h"
1112
#include <goto-programs/class_hierarchy.h>
1213
#include <util/std_types.h>
1314
#include <util/std_expr.h>
@@ -266,8 +267,20 @@ void stub_global_initializer_factoryt::create_stub_global_initializer_symbols(
266267
// Populate map from class id -> stub globals:
267268
for(const irep_idt &stub_global : stub_globals_set)
268269
{
270+
const symbolt &global_symbol = symbol_table.lookup_ref(stub_global);
271+
if(global_symbol.value.is_nil())
272+
{
273+
// This symbol is already nondet-initialised during __CPROVER_initialize
274+
// (generated in java_static_lifetime_init). In future this will only
275+
// be the case for primitive-typed fields, but for now reference-typed
276+
// fields can also be treated this way in the exceptional case that they
277+
// belong to a non-stub class. Skip the field, as it does not need a
278+
// synthetic static initializer.
279+
continue;
280+
}
281+
269282
const irep_idt class_id =
270-
symbol_table.lookup_ref(stub_global).type.get(ID_C_class);
283+
global_symbol.type.get(ID_C_class);
271284
INVARIANT(
272285
!class_id.empty(),
273286
"static field should be annotated with its defining class");
@@ -283,6 +296,9 @@ void stub_global_initializer_factoryt::create_stub_global_initializer_symbols(
283296
{
284297
const irep_idt static_init_name = clinit_function_name(it->first);
285298

299+
INVARIANT(
300+
symbol_table.lookup_ref(it->first).type.get_bool(ID_incomplete_class),
301+
"only incomplete classes should be given synthetic static initialisers");
286302
INVARIANT(
287303
!symbol_table.has_symbol(static_init_name),
288304
"a class cannot be both incomplete, and so have stub static fields, and "
@@ -315,20 +331,6 @@ void stub_global_initializer_factoryt::create_stub_global_initializer_symbols(
315331
}
316332
}
317333

318-
/// Check if a symbol is a well-known non-null global
319-
/// \param symbolid: symbol id to check
320-
/// \return true if this static field is known never to be null
321-
static bool is_non_null_library_global(const irep_idt &symbolid)
322-
{
323-
static const std::unordered_set<irep_idt, irep_id_hash> non_null_globals =
324-
{
325-
"java::java.lang.System.out",
326-
"java::java.lang.System.err",
327-
"java::java.lang.System.in"
328-
};
329-
return non_null_globals.count(symbolid);
330-
}
331-
332334
/// Create the body of a synthetic static initializer (clinit method),
333335
/// which initialise stub globals in the same manner as
334336
/// java_static_lifetime_init, only delayed until first reference by virtue of

src/java_bytecode/java_static_initializers.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ codet get_clinit_wrapper_body(
3131
class stub_global_initializer_factoryt
3232
{
3333
/// Maps class symbols onto the stub globals that belong to them
34-
std::unordered_multimap<irep_idt, irep_idt, irep_id_hash>
35-
stub_globals_by_class;
34+
typedef std::unordered_multimap<irep_idt, irep_idt, irep_id_hash>
35+
stub_globals_by_classt;
36+
stub_globals_by_classt stub_globals_by_class;
3637

3738
public:
3839
void create_stub_global_initializer_symbols(

src/java_bytecode/java_utils.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Author: Daniel Kroening, [email protected]
1818
#include <util/string_utils.h>
1919

2020
#include <set>
21+
#include <unordered_set>
2122

2223
bool java_is_array_type(const typet &type)
2324
{
@@ -413,3 +414,17 @@ resolve_inherited_componentt::inherited_componentt get_inherited_component(
413414
return resolve_inherited_componentt::inherited_componentt();
414415
}
415416
}
417+
418+
/// Check if a symbol is a well-known non-null global
419+
/// \param symbolid: symbol id to check
420+
/// \return true if this static field is known never to be null
421+
bool is_non_null_library_global(const irep_idt &symbolid)
422+
{
423+
static const std::unordered_set<irep_idt, irep_id_hash> non_null_globals =
424+
{
425+
"java::java.lang.System.out",
426+
"java::java.lang.System.err",
427+
"java::java.lang.System.in"
428+
};
429+
return non_null_globals.count(symbolid);
430+
}

src/java_bytecode/java_utils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,6 @@ resolve_inherited_componentt::inherited_componentt get_inherited_component(
106106
const class_hierarchyt &class_hierarchy,
107107
bool include_interfaces);
108108

109+
bool is_non_null_library_global(const irep_idt &);
110+
109111
#endif // CPROVER_JAVA_BYTECODE_JAVA_UTILS_H

0 commit comments

Comments
 (0)