Skip to content

Commit e86e2a0

Browse files
committed
Java: infer opaque type fields before method conversion
Before, opaque types' fields were inferred after a method is converted to codet (for example, if we converted a method that accessed field A.x and type A didn't have a field x yet, we would add it at this stage). This was problematic when converting functions on demand, as we might create instances of an opaque type but then realise upon converting a later method that it ought to have had more fields than we realised. This commit changes our strategy to infer the types earlier, before any methods are converted. This may lead to unnecessary work, but the pass executes very quickly even if thousands of classes are loaded, so I expect the penalty to be minimal compared to the advantage that the layout of types is fixed and will not change under the feet of subsequent passes.
1 parent bfd4f50 commit e86e2a0

File tree

2 files changed

+69
-15
lines changed

2 files changed

+69
-15
lines changed

src/java_bytecode/java_bytecode_language.cpp

+69
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,67 @@ bool java_bytecode_languaget::parse(
195195
return false;
196196
}
197197

198+
/// Infer fields that must exist on opaque types from field accesses against
199+
/// them. Note that we don't yet try to infer inheritence between opaque types
200+
/// here, so we may introduce the same field onto a type and its ancestor
201+
/// without realising that is in fact the same field, inherited from that
202+
/// ancestor. This can lead to incorrect results when opaque types are cast
203+
/// to other opaque types and their fields do not alias as intended.
204+
/// \param parse_tree: class parse tree
205+
/// \param symbol_table: global symbol table
206+
static void infer_opaque_type_fields(
207+
const java_bytecode_parse_treet &parse_tree,
208+
symbol_tablet &symbol_table)
209+
{
210+
namespacet ns(symbol_table);
211+
for(const auto &method : parse_tree.parsed_class.methods)
212+
{
213+
for(const auto &instruction : method.instructions)
214+
{
215+
if(instruction.statement == "getfield" ||
216+
instruction.statement == "putfield")
217+
{
218+
const exprt &fieldref = instruction.args[0];
219+
const symbolt *class_symbol =
220+
symbol_table.lookup(fieldref.get(ID_class));
221+
INVARIANT(
222+
class_symbol != nullptr, "all field types should have been loaded");
223+
224+
const class_typet *class_type = &to_class_type(class_symbol->type);
225+
irep_idt class_symbol_id = fieldref.get(ID_class);
226+
const irep_idt &component_name = fieldref.get(ID_component_name);
227+
while(!class_type->has_component(component_name))
228+
{
229+
if(class_type->get_bool(ID_incomplete_class))
230+
{
231+
// Accessing a field of an incomplete (opaque) type.
232+
symbolt &writable_class_symbol =
233+
symbol_table.get_writeable_ref(class_symbol_id);
234+
auto &add_to_components =
235+
to_struct_type(writable_class_symbol.type).components();
236+
add_to_components.push_back(
237+
struct_typet::componentt(component_name, fieldref.type()));
238+
add_to_components.back().set_base_name(component_name);
239+
add_to_components.back().set_pretty_name(component_name);
240+
break;
241+
}
242+
else
243+
{
244+
// Not present here: check the superclass.
245+
INVARIANT(
246+
class_type->bases().size() != 0,
247+
"class missing an expected field should have a superclass");
248+
const symbol_typet &superclass_type =
249+
to_symbol_type(class_type->bases()[0].type());
250+
class_symbol_id = superclass_type.get_identifier();
251+
class_type = &to_class_type(ns.follow(superclass_type));
252+
}
253+
}
254+
}
255+
}
256+
}
257+
}
258+
198259
bool java_bytecode_languaget::typecheck(
199260
symbol_tablet &symbol_table,
200261
const std::string &module)
@@ -248,6 +309,14 @@ bool java_bytecode_languaget::typecheck(
248309
}
249310
}
250311

312+
// Infer fields on opaque types based on the method instructions just loaded.
313+
// For example, if we don't have bytecode for field x of class A, but we can
314+
// see an int-typed getfield instruction referring to it, add that field now.
315+
for(const auto &c : java_class_loader.class_map)
316+
{
317+
infer_opaque_type_fields(c.second, symbol_table);
318+
}
319+
251320
// Now incrementally elaborate methods
252321
// that are reachable from this entry point.
253322
if(lazy_methods_mode==LAZY_METHODS_MODE_CONTEXT_INSENSITIVE)

src/java_bytecode/java_bytecode_typecheck_expr.cpp

-15
Original file line numberDiff line numberDiff line change
@@ -307,21 +307,6 @@ void java_bytecode_typecheckt::typecheck_expr_member(member_exprt &expr)
307307
struct_typet::componentst &components=
308308
struct_type.components();
309309

310-
if(struct_type.get_bool(ID_incomplete_class))
311-
{
312-
// member doesn't exist. In this case struct_type should be an opaque
313-
// stub, and we'll add the member to it.
314-
symbolt &symbol_table_type=symbol_table.get_writeable_ref(
315-
"java::"+id2string(struct_type.get_tag()));
316-
auto &add_to_components=
317-
to_struct_type(symbol_table_type.type).components();
318-
add_to_components
319-
.push_back(struct_typet::componentt(component_name, expr.type()));
320-
add_to_components.back().set_base_name(component_name);
321-
add_to_components.back().set_pretty_name(component_name);
322-
return;
323-
}
324-
325310
if(components.empty())
326311
break;
327312

0 commit comments

Comments
 (0)