2424#include < goto-instrument/cover.h>
2525
2626#include < iostream>
27+ #include < java-testing-utils/load_java_class.h>
2728
2829void validate_method_removal (
2930 std::list<goto_programt::instructiont> instructions)
3031{
3132 bool method_removed = true , replacement_nondet_exists = false ;
3233
33- // Quick loop to check that our method call has been replaced.
34+ // Loop over our instructions to make sure the nondet java method call has
35+ // been removed and that we can find an assignment/return with a nondet
36+ // as it's right-hand side.
3437 for (const auto &inst : instructions)
3538 {
39+ // Check that our NONDET(<type>) exists on a rhs somewhere.
3640 if (inst.is_assign ())
3741 {
3842 const code_assignt &assignment = to_code_assign (inst.code );
@@ -46,6 +50,21 @@ void validate_method_removal(
4650 }
4751 }
4852
53+ if (inst.is_return ())
54+ {
55+ const code_returnt &ret_expr = to_code_return (inst.code );
56+ if (ret_expr.return_value ().id () == ID_side_effect)
57+ {
58+ const side_effect_exprt &see =
59+ to_side_effect_expr (ret_expr.return_value ());
60+ if (see.get_statement () == ID_nondet)
61+ {
62+ replacement_nondet_exists = true ;
63+ }
64+ }
65+ }
66+
67+ // And check to see that our nondet method call has been removed.
4968 if (inst.is_function_call ())
5069 {
5170 const code_function_callt &function_call =
@@ -71,56 +90,146 @@ void validate_method_removal(
7190 REQUIRE (replacement_nondet_exists);
7291}
7392
74- TEST_CASE (
75- " Load class with a generated java nondet method call, run remove returns "
76- " both before and after the nondet statements have been removed, check "
77- " results are as expected." ,
93+ void load_and_test_method (
94+ const std::string &method_signature,
95+ goto_functionst &functions,
96+ journalling_symbol_tablet &symbol_table)
97+ {
98+ // Find the method under test.
99+ const std::string function_name = " java::Main." + method_signature;
100+ goto_functionst::goto_functiont &goto_function =
101+ functions.function_map .at (function_name);
102+
103+ goto_model_functiont model_function (
104+ symbol_table, functions, function_name, goto_function);
105+
106+ // Emulate some of the passes that we'd normally do before replace_java_nondet
107+ // is called.
108+ remove_instanceof (goto_function, symbol_table, null_message_handler);
109+
110+ remove_virtual_functions (model_function);
111+
112+ // Then test both situations.
113+ THEN (
114+ " Code should work when remove returns is called before "
115+ " replace_java_nondet." )
116+ {
117+ remove_returns (model_function, [](const irep_idt &) { return false ; });
118+
119+ replace_java_nondet (model_function);
120+
121+ validate_method_removal (goto_function.body .instructions );
122+ }
123+
124+ THEN (
125+ " Code should work when remove returns is called after "
126+ " replace_java_nondet." )
127+ {
128+ replace_java_nondet (model_function);
129+
130+ remove_returns (model_function, [](const irep_idt &) { return false ; });
131+
132+ validate_method_removal (goto_function.body .instructions );
133+ }
134+ }
135+
136+ SCENARIO (
137+ " Testing replace_java_nondet correctly replaces CProver.nondet method calls." ,
78138 " [core][java_bytecode][replace_nondet]" )
79139{
80- GIVEN (" A class with a call to CProver.nondetWithoutNull() " )
140+ GIVEN (" A class that holds nondet calls. " )
81141 {
82- symbol_tablet raw_symbol_table = load_java_class (
83- " Main" , " ./java_bytecode/java_replace_nondet" , " Main.replaceNondet" );
142+ // Load our main class.
143+ symbol_tablet raw_symbol_table =
144+ load_java_class (" Main" , " ./java_bytecode/java_replace_nondet" );
84145
85146 journalling_symbol_tablet symbol_table =
86147 journalling_symbol_tablet::wrap (raw_symbol_table);
87148
149+ // Convert bytecode into goto.
88150 goto_functionst functions;
89151 goto_convert (symbol_table, functions, null_message_handler);
90152
91- const std::string function_name = " java::Main.replaceNondet:()V" ;
92- goto_functionst::goto_functiont &goto_function =
93- functions.function_map .at (function_name);
94-
95- goto_model_functiont model_function (
96- symbol_table, functions, function_name, goto_function);
97-
98- remove_instanceof (goto_function, symbol_table, null_message_handler);
99-
100- remove_virtual_functions (model_function);
101-
102- WHEN (" Remove returns is called before java nondet." )
153+ WHEN (" A method assigns a local Object variable with nondetWithoutNull." )
103154 {
104- remove_returns (model_function, [](const irep_idt &) { return false ; });
155+ load_and_test_method (
156+ " replaceNondetAssignment:()V" , functions, symbol_table);
157+ }
105158
106- replace_java_nondet (functions);
159+ WHEN (
160+ " A method assigns an Integer variable with nondetWithoutNull. Causes "
161+ " implicit cast." )
162+ {
163+ load_and_test_method (
164+ " replaceNondetAssignmentImplicitCast:()V" , functions, symbol_table);
165+ }
107166
108- THEN (" The nondet method call should have been removed." )
109- {
110- validate_method_removal (goto_function.body .instructions );
111- }
167+ WHEN (
168+ " A method assigns an Integer variable with nondetWithoutNull. Uses "
169+ " explicit cast." )
170+ {
171+ load_and_test_method (
172+ " replaceNondetAssignmentExplicitCast:()V" , functions, symbol_table);
112173 }
113174
114- WHEN (" Remove returns is called after java nondet ." )
175+ WHEN (" A method directly returns a nonDetWithoutNull of type Object ." )
115176 {
116- replace_java_nondet (functions);
177+ load_and_test_method (
178+ " replaceNondetReturn:()Ljava/lang/Object;" , functions, symbol_table);
179+ }
117180
118- remove_returns (model_function, [](const irep_idt &) { return false ; });
181+ WHEN (
182+ " A method directly returns a nonDetWithoutNull of type Integer. Causes "
183+ " implicit cast." )
184+ {
185+ load_and_test_method (
186+ " replaceNondetReturnWithImplicitCast:()Ljava/lang/Integer;" ,
187+ functions,
188+ symbol_table);
189+ }
119190
120- THEN (" The nondet method call should have been removed." )
121- {
122- validate_method_removal (goto_function.body .instructions );
123- }
191+ WHEN (
192+ " A method directly returns a nonDetWithoutNull of type Integer. Uses "
193+ " explicit cast." )
194+ {
195+ load_and_test_method (
196+ " replaceNondetReturnWithExplicitCast:()Ljava/lang/Integer;" ,
197+ functions,
198+ symbol_table);
124199 }
200+
201+ // These currently cause an abort, issue detailed in https://github.com/diffblue/cbmc/issues/2281.
202+
203+ // WHEN(
204+ // "A method directly returns a nonDetWithoutNull +3 with explicit int cast.")
205+ // {
206+ // load_and_test_method("replaceNondetReturnAddition:()Ljava/lang/Integer;", functions, symbol_table);
207+ // }
208+
209+ // WHEN(
210+ // "A method assigns an int variable with nondetWithoutNull. Causes "
211+ // "unboxing.")
212+ // {
213+ // load_and_test_method("replaceNondetAssignmentUnbox:()V", functions, symbol_table);
214+ // }
215+
216+ // WHEN(
217+ // "A method assigns an int variable with nondetWithoutNull +3 with explicit cast.")
218+ // {
219+ // load_and_test_method("replaceNondetAssignmentAddition:()V", functions, symbol_table);
220+ // }
221+
222+ // WHEN(
223+ // "A method that calls nondetWithoutNull() without assigning the return value.")
224+ // {
225+ // load_and_test_method("replaceNondetUnused:()V", functions, symbol_table);
226+ // }
227+
228+ // WHEN(
229+ // "A method directly returns a nonDetWithoutNull of type int. Causes "
230+ // "unboxing.")
231+ // {
232+ // load_and_test_method("replaceNondetReturnUnboxed:()I", functions, symbol_table);
233+ // }
125234 }
126- }
235+ }
0 commit comments