Skip to content

Commit 02744e6

Browse files
authored
Merge pull request #3822 from thk123/assignment-consistency
Finished applying fixes for jbmc for assignment consistency
2 parents 9f1f3f5 + f0c520e commit 02744e6

File tree

11 files changed

+195
-131
lines changed

11 files changed

+195
-131
lines changed

jbmc/regression/jbmc/remove_virtual_function_typecast/test.desc

+2
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ VirtualFunctions.class
1010
--
1111
--
1212
This doesn't work under symex-driven lazy loading because it is incompatible with lazy-methods (default)
13+
Note the space before the checks for virtual function call on a and b, this
14+
verifies there is no cast.

jbmc/src/java_bytecode/java_bytecode_convert_method.cpp

+89-37
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,17 @@ Author: Daniel Kroening, [email protected]
3636

3737
#include <goto-programs/cfg.h>
3838
#include <goto-programs/class_hierarchy.h>
39+
#include <goto-programs/remove_returns.h>
3940
#include <goto-programs/resolve_inherited_component.h>
4041

4142
#include <analyses/cfg_dominators.h>
4243
#include <analyses/uncaught_exceptions_analysis.h>
4344

44-
#include <limits>
4545
#include <algorithm>
4646
#include <functional>
47-
#include <unordered_set>
47+
#include <limits>
4848
#include <regex>
49+
#include <unordered_set>
4950

5051
/// Given a string of the format '?blah?', will return true when compared
5152
/// against a string that matches appart from any characters that are '?'
@@ -428,11 +429,14 @@ void java_bytecode_convert_methodt::convert(
428429
id2string(class_symbol.name)+"."+id2string(m.name)+":"+m.descriptor;
429430
method_id=method_identifier;
430431

431-
symbolt &method_symbol=*symbol_table.get_writeable(method_identifier);
432-
433432
// Obtain a std::vector of java_method_typet::parametert objects from the
434433
// (function) type of the symbol
435-
java_method_typet method_type = to_java_method_type(method_symbol.type);
434+
// Don't try to hang on to this reference into the symbol table, as we're
435+
// about to create symbols for the method's parameters, which would invalidate
436+
// the reference. Instead, copy the type here, set it up, then assign it back
437+
// to the symbol later.
438+
java_method_typet method_type =
439+
to_java_method_type(symbol_table.lookup_ref(method_identifier).type);
436440
method_type.set(ID_C_class, class_symbol.name);
437441
method_type.set_is_final(m.is_final);
438442
method_return_type = method_type.return_type();
@@ -555,6 +559,8 @@ void java_bytecode_convert_methodt::convert(
555559
param_index+=java_local_variable_slots(param.type());
556560
}
557561

562+
symbolt &method_symbol = symbol_table.get_writeable_ref(method_identifier);
563+
558564
// The parameter slots detected in this function should agree with what
559565
// java_parameter_count() thinks about this method
560566
INVARIANT(
@@ -641,20 +647,39 @@ static irep_idt get_if_cmp_operator(const irep_idt &stmt)
641647
throw "unhandled java comparison instruction";
642648
}
643649

644-
static member_exprt to_member(const exprt &pointer, const exprt &fieldref)
650+
static member_exprt
651+
to_member(const exprt &pointer, const exprt &fieldref, const namespacet &ns)
645652
{
646653
struct_tag_typet class_type(fieldref.get(ID_class));
647654

648-
const typecast_exprt pointer2(pointer, java_reference_type(class_type));
655+
const exprt typed_pointer =
656+
typecast_exprt::conditional_cast(pointer, java_reference_type(class_type));
657+
658+
const irep_idt &component_name = fieldref.get(ID_component_name);
649659

650-
dereference_exprt obj_deref=checked_dereference(pointer2, class_type);
660+
exprt accessed_object = checked_dereference(typed_pointer, class_type);
661+
662+
// The field access is described as being against a particular type, but it
663+
// may in fact belong to any ancestor type.
664+
while(1)
665+
{
666+
struct_typet object_type =
667+
to_struct_type(ns.follow(accessed_object.type()));
668+
auto component = object_type.get_component(component_name);
651669

652-
const member_exprt member_expr(
653-
obj_deref,
654-
fieldref.get(ID_component_name),
655-
fieldref.type());
670+
if(component.is_not_nil())
671+
return member_exprt(accessed_object, component);
656672

657-
return member_expr;
673+
// Component not present: check the immediate parent type.
674+
675+
const auto &components = object_type.components();
676+
INVARIANT(
677+
components.size() != 0,
678+
"infer_opaque_type_fields should guarantee that a member access has a "
679+
"corresponding field");
680+
681+
accessed_object = member_exprt(accessed_object, components.front());
682+
}
658683
}
659684

660685
/// Find all goto statements in 'repl' that target 'old_label' and redirect them
@@ -1530,7 +1555,7 @@ code_blockt java_bytecode_convert_methodt::convert_instructions(
15301555
else if(statement=="getfield")
15311556
{
15321557
PRECONDITION(op.size() == 1 && results.size() == 1);
1533-
results[0]=java_bytecode_promotion(to_member(op[0], arg0));
1558+
results[0] = java_bytecode_promotion(to_member(op[0], arg0, ns));
15341559
}
15351560
else if(statement=="getstatic")
15361561
{
@@ -2070,34 +2095,63 @@ void java_bytecode_convert_methodt::convert_invoke(
20702095
const bool is_virtual(
20712096
statement == "invokevirtual" || statement == "invokeinterface");
20722097

2098+
const irep_idt &invoked_method_id = arg0.get(ID_identifier);
2099+
INVARIANT(
2100+
!invoked_method_id.empty(),
2101+
"invoke statement arg0 must have an identifier");
2102+
2103+
auto method_symbol = symbol_table.symbols.find(invoked_method_id);
2104+
2105+
// Use the most precise type available: the actual symbol has generic info,
2106+
// whereas the type given by the invoke instruction doesn't and is therefore
2107+
// less accurate.
2108+
if(method_symbol != symbol_table.symbols.end())
2109+
{
2110+
const auto &restored_type =
2111+
original_return_type(symbol_table, invoked_method_id);
2112+
// Note the number of parameters might change here due to constructors using
2113+
// invokespecial will have zero arguments (the `this` is added below)
2114+
// but the symbol for the <init> will have the this parameter.
2115+
INVARIANT(
2116+
to_java_method_type(arg0.type()).return_type().id() ==
2117+
restored_type.return_type().id(),
2118+
"Function return type must not change in kind");
2119+
arg0.type() = restored_type;
2120+
}
2121+
2122+
// Note arg0 and arg0.type() are subject to many side-effects in this method,
2123+
// then finally used to populate the call instruction.
20732124
java_method_typet &method_type = to_java_method_type(arg0.type());
2125+
20742126
java_method_typet::parameterst &parameters(method_type.parameters());
20752127

20762128
if(use_this)
20772129
{
2130+
irep_idt classname = arg0.get(ID_C_class);
2131+
20782132
if(parameters.empty() || !parameters[0].get_this())
20792133
{
2080-
irep_idt classname = arg0.get(ID_C_class);
20812134
typet thistype = struct_tag_typet(classname);
2082-
// Note invokespecial is used for super-method calls as well as
2083-
// constructors.
2084-
if(statement == "invokespecial")
2085-
{
2086-
if(is_constructor(arg0.get(ID_identifier)))
2087-
{
2088-
if(needed_lazy_methods)
2089-
needed_lazy_methods->add_needed_class(classname);
2090-
method_type.set_is_constructor();
2091-
}
2092-
else
2093-
method_type.set(ID_java_super_method_call, true);
2094-
}
20952135
reference_typet object_ref_type = java_reference_type(thistype);
20962136
java_method_typet::parametert this_p(object_ref_type);
20972137
this_p.set_this();
20982138
this_p.set_base_name(ID_this);
20992139
parameters.insert(parameters.begin(), this_p);
21002140
}
2141+
2142+
// Note invokespecial is used for super-method calls as well as
2143+
// constructors.
2144+
if(statement == "invokespecial")
2145+
{
2146+
if(is_constructor(invoked_method_id))
2147+
{
2148+
if(needed_lazy_methods)
2149+
needed_lazy_methods->add_needed_class(classname);
2150+
method_type.set_is_constructor();
2151+
}
2152+
else
2153+
method_type.set(ID_java_super_method_call, true);
2154+
}
21012155
}
21022156

21032157
code_function_callt call;
@@ -2160,18 +2214,17 @@ void java_bytecode_convert_methodt::convert_invoke(
21602214
// accessible from the original caller, but not from the generated test.
21612215
// Therefore we must assume that the method is not accessible.
21622216
// We set opaque methods as final to avoid assuming they can be overridden.
2163-
irep_idt id = arg0.get(ID_identifier);
21642217
if(
2165-
symbol_table.symbols.find(id) == symbol_table.symbols.end() &&
2218+
method_symbol == symbol_table.symbols.end() &&
21662219
!(is_virtual &&
21672220
is_method_inherited(arg0.get(ID_C_class), arg0.get(ID_component_name))))
21682221
{
21692222
symbolt symbol;
2170-
symbol.name = id;
2223+
symbol.name = invoked_method_id;
21712224
symbol.base_name = arg0.get(ID_C_base_name);
21722225
symbol.pretty_name = id2string(arg0.get(ID_C_class)).substr(6) + "." +
21732226
id2string(symbol.base_name) + "()";
2174-
symbol.type = arg0.type();
2227+
symbol.type = method_type;
21752228
symbol.type.set(ID_access, ID_private);
21762229
to_java_method_type(symbol.type).set_is_final(true);
21772230
symbol.value.make_nil();
@@ -2196,10 +2249,10 @@ void java_bytecode_convert_methodt::convert_invoke(
21962249
else
21972250
{
21982251
// static binding
2199-
call.function() = symbol_exprt(arg0.get(ID_identifier), arg0.type());
2252+
call.function() = symbol_exprt(invoked_method_id, method_type);
22002253
if(needed_lazy_methods)
22012254
{
2202-
needed_lazy_methods->add_needed_method(arg0.get(ID_identifier));
2255+
needed_lazy_methods->add_needed_method(invoked_method_id);
22032256
// Calling a static method causes static initialization:
22042257
needed_lazy_methods->add_needed_class(arg0.get(ID_C_class));
22052258
}
@@ -2543,7 +2596,7 @@ code_blockt java_bytecode_convert_methodt::convert_putfield(
25432596
block,
25442597
bytecode_write_typet::FIELD,
25452598
arg0.get(ID_component_name));
2546-
block.add(code_assignt(to_member(op[0], arg0), op[1]));
2599+
block.add(code_assignt(to_member(op[0], arg0, ns), op[1]));
25472600
return block;
25482601
}
25492602

@@ -2918,8 +2971,7 @@ optionalt<exprt> java_bytecode_convert_methodt::convert_invoke_dynamic(
29182971
if(return_type.id() == ID_empty)
29192972
return {};
29202973

2921-
const auto value =
2922-
zero_initializer(return_type, location, namespacet(symbol_table));
2974+
const auto value = zero_initializer(return_type, location, ns);
29232975
if(!value.has_value())
29242976
{
29252977
error().source_location = location;

jbmc/src/java_bytecode/java_bytecode_convert_method_class.h

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class java_bytecode_convert_methodt:public messaget
4343
bool threading_support)
4444
: messaget(_message_handler),
4545
symbol_table(symbol_table),
46+
ns(symbol_table),
4647
max_array_length(_max_array_length),
4748
throw_assertion_error(throw_assertion_error),
4849
threading_support(threading_support),
@@ -69,6 +70,7 @@ class java_bytecode_convert_methodt:public messaget
6970

7071
protected:
7172
symbol_table_baset &symbol_table;
73+
namespacet ns;
7274
const size_t max_array_length;
7375
const bool throw_assertion_error;
7476
const bool threading_support;

jbmc/src/java_bytecode/java_bytecode_typecheck.h

-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ class java_bytecode_typecheckt:public typecheckt
6969
void typecheck_code(codet &);
7070
void typecheck_type(typet &);
7171
void typecheck_expr_symbol(symbol_exprt &);
72-
void typecheck_expr_member(member_exprt &);
7372
void typecheck_expr_java_new(side_effect_exprt &);
7473
void typecheck_expr_java_new_array(side_effect_exprt &);
7574

jbmc/src/java_bytecode/java_bytecode_typecheck_expr.cpp

-45
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@ void java_bytecode_typecheckt::typecheck_expr(exprt &expr)
5252
else if(statement==ID_java_new_array)
5353
typecheck_expr_java_new_array(to_side_effect_expr(expr));
5454
}
55-
else if(expr.id()==ID_member)
56-
typecheck_expr_member(to_member_expr(expr));
5755
}
5856

5957
void java_bytecode_typecheckt::typecheck_expr_java_new(side_effect_exprt &expr)
@@ -120,46 +118,3 @@ void java_bytecode_typecheckt::typecheck_expr_symbol(symbol_exprt &expr)
120118
expr.type()=symbol.type;
121119
}
122120
}
123-
124-
void java_bytecode_typecheckt::typecheck_expr_member(member_exprt &expr)
125-
{
126-
// The member might be in a parent class or an opaque class, which we resolve
127-
// here.
128-
const irep_idt component_name=expr.get_component_name();
129-
130-
while(1)
131-
{
132-
typet base_type(ns.follow(expr.struct_op().type()));
133-
134-
if(base_type.id()!=ID_struct)
135-
break; // give up
136-
137-
struct_typet &struct_type=
138-
to_struct_type(base_type);
139-
140-
if(struct_type.has_component(component_name))
141-
return; // done
142-
143-
// look at parent
144-
struct_typet::componentst &components=
145-
struct_type.components();
146-
147-
if(components.empty())
148-
break;
149-
150-
const struct_typet::componentt &c=components.front();
151-
152-
member_exprt m(expr.struct_op(), c.get_name(), c.type());
153-
m.add_source_location()=expr.source_location();
154-
155-
expr.struct_op()=m;
156-
}
157-
158-
warning().source_location=expr.source_location();
159-
warning() << "failed to find field `"
160-
<< component_name << "` in class hierarchy" << eom;
161-
162-
// We replace by a non-det of same type
163-
side_effect_expr_nondett nondet(expr.type(), expr.source_location());
164-
expr.swap(nondet);
165-
}

jbmc/src/java_bytecode/java_pointer_casts.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,12 @@ exprt make_clean_pointer_cast(
118118
return bare_ptr;
119119

120120
exprt superclass_ptr=bare_ptr;
121+
// Looking at base types discards generic qualifiers (because those are
122+
// recorded on the pointer, not the pointee), so it may still be necessary
123+
// to use a cast to reintroduce the qualifier (for example, the base might
124+
// be recorded as a List, when we're looking for a List<E>)
121125
if(find_superclass_with_type(superclass_ptr, target_base, ns))
122-
return superclass_ptr;
126+
return typecast_exprt::conditional_cast(superclass_ptr, target_type);
123127

124128
return typecast_exprt(bare_ptr, target_type);
125129
}

0 commit comments

Comments
 (0)