Skip to content

Load classes from opaque calls [TG-3657] #2335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
7 changes: 7 additions & 0 deletions jbmc/regression/jbmc/lazyloading_opaquereturn/Bar.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
public class Bar {
public int x;

public void cproverNondetInitialize() {

}
}
Binary file not shown.
5 changes: 5 additions & 0 deletions jbmc/regression/jbmc/lazyloading_opaquereturn/Baz.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public class Baz {
public void cproverNondetInitialize() {

}
}
Binary file not shown.
11 changes: 11 additions & 0 deletions jbmc/regression/jbmc/lazyloading_opaquereturn/Foo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
public class Foo {
public Bar subType;

public Baz[] arraySubtype;

public Gen<GenFiller> genSubtype;

public void cproverNondetInitialize() {

}
}
Binary file not shown.
3 changes: 3 additions & 0 deletions jbmc/regression/jbmc/lazyloading_opaquereturn/Gen.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public class Gen<T> {
T t;
}
Binary file not shown.
7 changes: 7 additions & 0 deletions jbmc/regression/jbmc/lazyloading_opaquereturn/GenFiller.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
public class GenFiller {
public int i;

public void cproverNondetInitialize() {

}

6 changes: 6 additions & 0 deletions jbmc/regression/jbmc/lazyloading_opaquereturn/Opaque.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
public class Opaque
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this folder missing the script that deletes the opaque class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No - the Opaque.class is not checked in unlike the other .class files, do you think including a build script is worthwhile? I think it is in general not done but that's isn't an argument against starting.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed that it was not included and was thinking of java-compile-tests.

{
static Foo opaqueMethod() {
return null;
}
}
Binary file not shown.
5 changes: 5 additions & 0 deletions jbmc/regression/jbmc/lazyloading_opaquereturn/TestClass.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public class TestClass {
public static void testFunction() {
int x = Opaque.opaqueMethod().subType.x;
}
}
13 changes: 13 additions & 0 deletions jbmc/regression/jbmc/lazyloading_opaquereturn/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
CORE symex-driven-lazy-loading-expected-failure
TestClass.class
--function TestClass.testFunction --lazy-methods --verbosity 10
CI lazy methods: elaborate java::Foo\.cproverNondetInitialize:\(\)V
CI lazy methods: elaborate java::Bar\.cproverNondetInitialize:\(\)V
CI lazy methods: elaborate java::Baz\.cproverNondetInitialize:\(\)V
CI lazy methods: elaborate java::GenFiller\.cproverNondetInitialize:\(\)V
--
--
Testing that the return type of an opaque method is correctly elaborated. The
cproverNondetInitialize should be loaded for all classes where an instance might
be seen.
This doesn't work under symex-driven lazy loading because it is incompatible with --lazy-methods
151 changes: 9 additions & 142 deletions jbmc/src/java_bytecode/ci_lazy_methods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ bool ci_lazy_methodst::operator()(
{
std::unordered_set<irep_idt> initial_callable_methods;
ci_lazy_methods_neededt initial_lazy_methods(
initial_callable_methods, instantiated_classes, symbol_table);
initial_callable_methods,
instantiated_classes,
symbol_table,
pointer_type_selector);
initialize_instantiated_classes(
methods_to_convert_later, namespacet(symbol_table), initial_lazy_methods);
methods_to_convert_later.insert(
Expand Down Expand Up @@ -174,17 +177,6 @@ bool ci_lazy_methodst::operator()(
symbol_table);
}

// cproverNondetInitialize has to be force-loaded for mocks to return valid
// objects (objects which satisfy invariants specified in the
// cproverNondetInitialize method)
for(const auto &class_name : instantiated_classes)
{
const irep_idt cprover_validate =
id2string(class_name) + ".cproverNondetInitialize:()V";
if(symbol_table.symbols.count(cprover_validate))
methods_already_populated.insert(cprover_validate);
}

// Remove symbols for methods that were declared but never used:
symbol_tablet keep_symbols;
// Manually keep @inflight_exception, as it is unused at this stage
Expand Down Expand Up @@ -298,7 +290,10 @@ ci_lazy_methodst::convert_and_analyze_method(
// Note this wraps *references* to methods_to_convert_later &
// instantiated_classes
ci_lazy_methods_neededt needed_methods(
methods_to_convert_later, instantiated_classes, symbol_table);
methods_to_convert_later,
instantiated_classes,
symbol_table,
pointer_type_selector);

const bool could_not_convert_function =
method_converter(method_name, needed_methods);
Expand Down Expand Up @@ -430,8 +425,7 @@ void ci_lazy_methodst::initialize_instantiated_classes(
if(param.type().id()==ID_pointer)
{
const pointer_typet &original_pointer=to_pointer_type(param.type());
initialize_all_instantiated_classes_from_pointer(
original_pointer, ns, needed_lazy_methods);
needed_lazy_methods.add_all_needed_classes(original_pointer);
}
}
}
Expand All @@ -448,75 +442,6 @@ void ci_lazy_methodst::initialize_instantiated_classes(
needed_lazy_methods.add_needed_class("java::" + id2string(id));
}

/// Build up list of methods for types for a pointer and any types it
/// might be subsituted for. See
/// `initialize_instantiated_classes` for more details.
/// \param pointer_type: The type to gather methods for.
/// \param ns: global namespace
/// \param [out] needed_lazy_methods: Populated with all Java reference types
/// whose references may be passed, directly or indirectly, to any of the
/// functions in `entry_points`
void ci_lazy_methodst::initialize_all_instantiated_classes_from_pointer(
const pointer_typet &pointer_type,
const namespacet &ns,
ci_lazy_methods_neededt &needed_lazy_methods)
{
initialize_instantiated_classes_from_pointer(
pointer_type,
ns,
needed_lazy_methods);

// TODO we should be passing here a map that maps generic parameters
// to concrete types in the current context TG-2664
const pointer_typet &subbed_pointer_type =
pointer_type_selector.convert_pointer_type(pointer_type, {}, ns);

if(subbed_pointer_type!=pointer_type)
{
initialize_instantiated_classes_from_pointer(
subbed_pointer_type, ns, needed_lazy_methods);
}
}

/// Build up list of methods for types for a specific pointer type. See
/// `initialize_instantiated_classes` for more details.
/// \param pointer_type: The type to gather methods for.
/// \param ns: global namespace
/// \param [out] needed_lazy_methods: Populated with all Java reference types
/// whose references may be passed, directly or indirectly, to any of the
/// functions in `entry_points`
void ci_lazy_methodst::initialize_instantiated_classes_from_pointer(
const pointer_typet &pointer_type,
const namespacet &ns,
ci_lazy_methods_neededt &needed_lazy_methods)
{
const symbol_typet &class_type=to_symbol_type(pointer_type.subtype());
const auto &param_classid=class_type.get_identifier();

// Note here: different arrays may have different element types, so we should
// explore again even if we've seen this classid before in the array case.
if(needed_lazy_methods.add_needed_class(param_classid) ||
is_java_array_tag(param_classid))
{
gather_field_types(pointer_type.subtype(), ns, needed_lazy_methods);
}

if(is_java_generic_type(pointer_type))
{
// Assume if this is a generic like X<A, B, C>, then any concrete parameters
// will at some point be instantiated.
const auto &generic_args =
to_java_generic_type(pointer_type).generic_type_arguments();
for(const auto &generic_arg : generic_args)
{
if(!is_java_generic_parameter(generic_arg))
{
initialize_instantiated_classes_from_pointer(
generic_arg, ns, needed_lazy_methods);
}
}
}
}

/// Get places where virtual functions are called.
/// \param e: expression tree to search
Expand Down Expand Up @@ -611,64 +536,6 @@ void ci_lazy_methodst::gather_needed_globals(
gather_needed_globals(*opit, symbol_table, needed);
}

/// See param needed_lazy_methods
/// \param class_type: root of class tree to search
/// \param ns: global namespace
/// \param [out] needed_lazy_methods: Popualted with all Java reference types
/// reachable starting at `class_type`. For example if `class_type` is
/// `symbol_typet("java::A")` and A has a B field, then `B` (but not `A`) will
/// noted as a needed class.
void ci_lazy_methodst::gather_field_types(
const typet &class_type,
const namespacet &ns,
ci_lazy_methods_neededt &needed_lazy_methods)
{
const auto &underlying_type=to_struct_type(ns.follow(class_type));
if(is_java_array_tag(underlying_type.get_tag()))
{
// If class_type is not a symbol this may be a reference array,
// but we can't tell what type.
if(class_type.id() == ID_symbol)
{
const typet &element_type =
java_array_element_type(to_symbol_type(class_type));
if(element_type.id() == ID_pointer)
{
// This is a reference array -- mark its element type available.
initialize_all_instantiated_classes_from_pointer(
to_pointer_type(element_type), ns, needed_lazy_methods);
}
}
}
else
{
for(const auto &field : underlying_type.components())
{
if(field.type().id() == ID_struct || field.type().id() == ID_symbol)
gather_field_types(field.type(), ns, needed_lazy_methods);
else if(field.type().id() == ID_pointer)
{
if(field.type().subtype().id() == ID_symbol)
{
initialize_all_instantiated_classes_from_pointer(
to_pointer_type(field.type()), ns, needed_lazy_methods);
}
else
{
// If raw structs were possible this would lead to missed
// dependencies, as both array element and specialised generic type
// information cannot be obtained in this case.
// We should therefore only be skipping pointers such as the uint16t*
// in our internal String representation.
INVARIANT(
field.type().subtype().id() != ID_struct,
"struct types should be referred to by symbol at this stage");
}
}
}
}
}

/// Find a virtual callee, if one is defined and the callee type is known to
/// exist.
/// \param instantiated_classes: set of classes that can be instantiated.
Expand Down
15 changes: 0 additions & 15 deletions jbmc/src/java_bytecode/ci_lazy_methods.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,6 @@ class ci_lazy_methodst:public messaget
const namespacet &ns,
ci_lazy_methods_neededt &needed_lazy_methods);

void initialize_all_instantiated_classes_from_pointer(
const pointer_typet &pointer_type,
const namespacet &ns,
ci_lazy_methods_neededt &needed_lazy_methods);

void initialize_instantiated_classes_from_pointer(
const pointer_typet &pointer_type,
const namespacet &ns,
ci_lazy_methods_neededt &needed_lazy_methods);

void gather_virtual_callsites(
const exprt &e,
std::unordered_set<exprt, irep_hash> &result);
Expand All @@ -146,11 +136,6 @@ class ci_lazy_methodst:public messaget
const symbol_tablet &symbol_table,
symbol_tablet &needed);

void gather_field_types(
const typet &class_type,
const namespacet &ns,
ci_lazy_methods_neededt &needed_lazy_methods);

irep_idt get_virtual_method_target(
const std::unordered_set<irep_idt> &instantiated_classes,
const irep_idt &call_basename,
Expand Down
Loading