Skip to content

Commit e74e1d8

Browse files
author
Thomas Kiley
authored
Merge pull request #1489 from svorenova/tg-865
Resolving signature/descriptor missmatch
2 parents 62675bb + 09431fd commit e74e1d8

14 files changed

+276
-56
lines changed

src/java_bytecode/java_bytecode_convert_class.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,8 @@ void java_bytecode_convert_classt::convert(const classt &c)
203203
*class_symbol,
204204
method_identifier,
205205
method,
206-
symbol_table);
206+
symbol_table,
207+
get_message_handler());
207208
lazy_methods[method_identifier]=std::make_pair(class_symbol, &method);
208209
}
209210

src/java_bytecode/java_bytecode_convert_method.cpp

+73-2
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,69 @@ const exprt java_bytecode_convert_methodt::variable(
248248
}
249249
}
250250

251+
/// Returns the member type for a method, based on signature or descriptor
252+
/// \param descriptor
253+
/// descriptor of the method
254+
/// \param signature
255+
/// signature of the method
256+
/// \param class_name
257+
/// string containing the name of the corresponding class
258+
/// \param method_name
259+
/// string containing the name of the method
260+
/// \param message_handler
261+
/// message handler to collect warnings
262+
/// \return
263+
/// the constructed member type
264+
typet member_type_lazy(const std::string &descriptor,
265+
const optionalt<std::string> &signature,
266+
const std::string &class_name,
267+
const std::string &method_name,
268+
message_handlert &message_handler)
269+
{
270+
// In order to construct the method type, we can either use signature or
271+
// descriptor. Since only signature contains the generics info, we want to
272+
// use signature whenever possible. We revert to using descriptor if (1) the
273+
// signature does not exist, (2) an unsupported generics are present or
274+
// (3) in the special case when the number of parameters in the descriptor
275+
// does not match the number of parameters in the signature - e.g. for
276+
// certain types of inner classes and enums (see unit tests for examples).
277+
278+
messaget message(message_handler);
279+
280+
typet member_type_from_descriptor=java_type_from_string(descriptor);
281+
INVARIANT(member_type_from_descriptor.id()==ID_code, "Must be code type");
282+
if(signature.has_value())
283+
{
284+
try
285+
{
286+
typet member_type_from_signature=java_type_from_string(
287+
signature.value(),
288+
class_name);
289+
INVARIANT(member_type_from_signature.id()==ID_code, "Must be code type");
290+
if(to_code_type(member_type_from_signature).parameters().size()==
291+
to_code_type(member_type_from_descriptor).parameters().size())
292+
{
293+
return member_type_from_signature;
294+
}
295+
else
296+
{
297+
message.warning() << "method: " << class_name << "." << method_name
298+
<< "\n signature: " << signature.value() << "\n descriptor: "
299+
<< descriptor << "\n different number of parameters, reverting to "
300+
"descriptor" << message.eom;
301+
}
302+
}
303+
catch(unsupported_java_class_signature_exceptiont &e)
304+
{
305+
message.warning() << "method: " << class_name << "." << method_name
306+
<< "\n could not parse signature: " << signature.value() << "\n "
307+
<< e.what() << "\n" << " reverting to descriptor: "
308+
<< descriptor << message.eom;
309+
}
310+
}
311+
return member_type_from_descriptor;
312+
}
313+
251314
/// This creates a method symbol in the symtab, but doesn't actually perform
252315
/// method conversion just yet. The caller should call
253316
/// java_bytecode_convert_method later to give the symbol/method a body.
@@ -256,14 +319,22 @@ const exprt java_bytecode_convert_methodt::variable(
256319
/// (e.g. "x.y.z.f:(I)")
257320
/// `m`: parsed method object to convert
258321
/// `symbol_table`: global symbol table (will be modified)
322+
/// `message_handler`: message handler to collect warnings
259323
void java_bytecode_convert_method_lazy(
260324
const symbolt &class_symbol,
261325
const irep_idt &method_identifier,
262326
const java_bytecode_parse_treet::methodt &m,
263-
symbol_tablet &symbol_table)
327+
symbol_tablet &symbol_table,
328+
message_handlert &message_handler)
264329
{
265330
symbolt method_symbol;
266-
typet member_type=java_type_from_string(m.descriptor);
331+
332+
typet member_type=member_type_lazy(
333+
m.descriptor,
334+
m.signature,
335+
id2string(class_symbol.name),
336+
id2string(m.base_name),
337+
message_handler);
267338

268339
method_symbol.name=method_identifier;
269340
method_symbol.base_name=m.base_name;

src/java_bytecode/java_bytecode_convert_method.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ void java_bytecode_convert_method_lazy(
5353
const symbolt &class_symbol,
5454
const irep_idt &method_identifier,
5555
const java_bytecode_parse_treet::methodt &,
56-
symbol_tablet &symbol_table);
56+
symbol_tablet &symbol_table,
57+
message_handlert &);
5758

5859
#endif // CPROVER_JAVA_BYTECODE_JAVA_BYTECODE_CONVERT_METHOD_H
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
public abstract class AbstractGenericClass<T>
2+
{
3+
4+
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
public class Outer<T>
2+
{
3+
private class Inner
4+
{
5+
private final AbstractGenericClass<T> u;
6+
7+
Inner (AbstractGenericClass<T> t)
8+
{
9+
this.u = t;
10+
}
11+
}
12+
13+
private enum InnerEnum
14+
{
15+
16+
}
17+
}

unit/java_bytecode/java_bytecode_parse_generics/parse_derived_generic_class.cpp

+3-6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <java_bytecode/java_bytecode_language.h>
1818
#include <iostream>
1919
#include <testing-utils/load_java_class.h>
20+
#include <testing-utils/require_symbol.h>
2021

2122
SCENARIO(
2223
"java_bytecode_parse_derived_generic_class",
@@ -33,12 +34,8 @@ SCENARIO(
3334
REQUIRE(new_symbol_table.has_symbol(class_prefix));
3435

3536
const symbolt &derived_symbol=new_symbol_table.lookup_ref(class_prefix);
36-
REQUIRE(derived_symbol.is_type);
37-
const typet &derived_type=derived_symbol.type;
38-
REQUIRE(derived_type.id()==ID_struct);
39-
const class_typet &class_type=to_class_type(derived_type);
40-
REQUIRE(class_type.is_class());
41-
REQUIRE_FALSE(class_type.get_bool(ID_incomplete_class));
37+
const class_typet &derived_class_type=
38+
require_symbol::require_complete_class(derived_symbol);
4239

4340
// TODO(tkiley): Currently we do not support extracting information
4441
// about the base classes generic information.

unit/java_bytecode/java_bytecode_parse_generics/parse_generic_class.cpp

+41-46
Original file line numberDiff line numberDiff line change
@@ -174,57 +174,52 @@ SCENARIO(
174174
.has_symbol("java::generics$bound_element.f:()Ljava/lang/Number;"));
175175

176176
// TODO: methods should have generic return type (the tests needs to be
177-
// extended), reintroduce when the issue of signature/descriptor for methods is
178-
// resolved
179-
// THEN("The method should have generic return type")
180-
// {
181-
// const symbolt &method_symbol=
182-
// new_symbol_table
183-
// .lookup("java::generics$bound_element.f:()Ljava/lang/Number;")
184-
// .value().get();
185-
// const typet &symbol_type=method_symbol.type;
186-
//
187-
// REQUIRE(symbol_type.id()==ID_code);
188-
//
189-
// const code_typet &code=to_code_type(symbol_type);
190-
// }
177+
// extended)
178+
THEN("The method should have generic return type")
179+
{
180+
const symbolt &method_symbol=
181+
new_symbol_table
182+
.lookup_ref("java::generics$bound_element.f:()Ljava/lang/Number;");
183+
const typet &symbol_type=method_symbol.type;
184+
185+
REQUIRE(symbol_type.id()==ID_code);
186+
187+
const code_typet &code=to_code_type(symbol_type);
188+
}
191189

192190
REQUIRE(
193191
new_symbol_table
194192
.has_symbol("java::generics$bound_element.g:(Ljava/lang/Number;)V"));
193+
THEN("The method should have a generic parameter.")
194+
{
195+
const symbolt &method_symbol=
196+
new_symbol_table
197+
.lookup_ref("java::generics$bound_element.g:(Ljava/lang/Number;)V");
198+
const typet &symbol_type=method_symbol.type;
195199

196-
// TODO: methods are not recognized as generic, reintroduce when
197-
// the issue of signature/descriptor for methods is resolved
198-
// THEN("The method should have a generic parameter.")
199-
// {
200-
// const symbolt &method_symbol=
201-
// new_symbol_table
202-
// .lookup("java::generics$bound_element.g:(Ljava/lang/Number;)V");
203-
// const typet &symbol_type=method_symbol.type;
204-
//
205-
// REQUIRE(symbol_type.id()==ID_code);
206-
//
207-
// const code_typet &code=to_code_type(symbol_type);
208-
//
209-
// bool found=false;
210-
// for(const auto &p : code.parameters())
211-
// {
212-
// if(p.get_identifier()==
213-
// "java::generics$bound_element.g:(Ljava/lang/Number;)V::e")
214-
// {
215-
// found=true;
216-
// const typet &t=p.type();
217-
// REQUIRE(is_java_generic_parameter(p.type()));
218-
// const java_generic_parametert &gen_type=
219-
// to_java_generic_parameter(p.type());
220-
// const symbol_typet &type_var=gen_type.type_variable();
221-
// REQUIRE(type_var.get_identifier()==
222-
// "java::generics$bound_element::NUM");
223-
// break;
224-
// }
225-
// }
226-
// REQUIRE(found);
227-
// }
200+
REQUIRE(symbol_type.id()==ID_code);
201+
202+
const code_typet &code=to_code_type(symbol_type);
203+
204+
bool found=false;
205+
for(const auto &p : code.parameters())
206+
{
207+
if(p.get_identifier()==
208+
"java::generics$bound_element.g:(Ljava/lang/Number;)V::e")
209+
{
210+
found=true;
211+
const typet &t=p.type();
212+
REQUIRE(is_java_generic_parameter(p.type()));
213+
const java_generic_parametert &gen_type=
214+
to_java_generic_parameter(p.type());
215+
const symbol_typet &type_var=gen_type.type_variable();
216+
REQUIRE(type_var.get_identifier()==
217+
"java::generics$bound_element::NUM");
218+
break;
219+
}
220+
}
221+
REQUIRE(found);
222+
}
228223
}
229224
}
230225
GIVEN("A class with multiple bounds")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*******************************************************************\
2+
3+
Module: Unit tests for parsing generic classes
4+
5+
Author: DiffBlue Limited. All rights reserved.
6+
7+
\*******************************************************************/
8+
9+
#include <testing-utils/catch.hpp>
10+
11+
#include <memory>
12+
13+
#include <util/config.h>
14+
#include <util/language.h>
15+
#include <java_bytecode/java_bytecode_language.h>
16+
#include <iostream>
17+
#include <testing-utils/load_java_class.h>
18+
#include <testing-utils/require_symbol.h>
19+
20+
SCENARIO(
21+
"java_bytecode_parse_signature_descriptor_mismatch",
22+
"[core][java_bytecode][java_bytecode_parse_generics]")
23+
{
24+
const symbol_tablet &new_symbol_table=
25+
load_java_class(
26+
"Outer",
27+
"./java_bytecode/java_bytecode_parse_generics");
28+
29+
const std::string class_prefix="java::Outer";
30+
REQUIRE(new_symbol_table.has_symbol(class_prefix));
31+
32+
const std::string inner_prefix=class_prefix+"$Inner";
33+
THEN("There is a (complete) symbol for the inner class Inner")
34+
{
35+
REQUIRE(new_symbol_table.has_symbol(inner_prefix));
36+
37+
const symbolt &inner_symbol=new_symbol_table.lookup_ref(inner_prefix);
38+
const class_typet &inner_class_type=
39+
require_symbol::require_complete_class(inner_symbol);
40+
}
41+
42+
THEN("There is a symbol for the constructor of the inner class with correct"
43+
" descriptor")
44+
{
45+
const std::string func_name=".<init>";
46+
const std::string func_descriptor=":(LOuter;LAbstractGenericClass;)V";
47+
const std::string process_func_name=
48+
inner_prefix+func_name+func_descriptor;
49+
REQUIRE(new_symbol_table.has_symbol(process_func_name));
50+
51+
const code_typet func_code=
52+
to_code_type(new_symbol_table.lookup_ref(process_func_name).type);
53+
REQUIRE(func_code.parameters().size()==3);
54+
}
55+
56+
const std::string inner_enum_prefix=class_prefix+"$InnerEnum";
57+
THEN("There is a (complete) symbol for the inner enum InnerEnum")
58+
{
59+
REQUIRE(new_symbol_table.has_symbol(inner_enum_prefix));
60+
61+
const symbolt &inner_enum_symbol=
62+
new_symbol_table.lookup_ref(inner_enum_prefix);
63+
const class_typet &inner_enum_class_type=
64+
require_symbol::require_complete_class(inner_enum_symbol);
65+
}
66+
67+
THEN("There is a symbol for the constructor of the inner enum with correct"
68+
" number of parameters")
69+
{
70+
const std::string func_name=".<init>";
71+
const std::string func_descriptor=":(Ljava/lang/String;I)V";
72+
const std::string process_func_name=
73+
inner_enum_prefix+func_name+func_descriptor;
74+
REQUIRE(new_symbol_table.has_symbol(process_func_name));
75+
76+
const code_typet func_code=
77+
to_code_type(new_symbol_table.lookup_ref(process_func_name).type);
78+
REQUIRE(func_code.parameters().size()==3);
79+
}
80+
}

unit/testing-utils/require_symbol.cpp

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*******************************************************************\
2+
3+
Module: Unit test utilities
4+
5+
Author: DiffBlue Limited. All rights reserved.
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Helper functions for requiring properties of symbols
11+
12+
#include "require_symbol.h"
13+
14+
#include <testing-utils/catch.hpp>
15+
16+
const class_typet &require_symbol::require_complete_class(
17+
const symbolt &class_symbol)
18+
{
19+
REQUIRE(class_symbol.is_type);
20+
21+
const typet &class_symbol_type=class_symbol.type;
22+
REQUIRE(class_symbol_type.id()==ID_struct);
23+
24+
const class_typet &class_class_type=to_class_type(class_symbol_type);
25+
REQUIRE(class_class_type.is_class());
26+
REQUIRE_FALSE(class_class_type.get_bool(ID_incomplete_class));
27+
28+
return class_class_type;
29+
}

unit/testing-utils/require_symbol.h

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*******************************************************************\
2+
3+
Module: Unit test utilities
4+
5+
Author: DiffBlue Limited. All rights reserved.
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Helper functions for requiring properties of symbols
11+
12+
#include <util/symbol.h>
13+
#include <util/std_types.h>
14+
15+
#ifndef CPROVER_TESTING_UTILS_REQUIRE_SYMBOL_H
16+
#define CPROVER_TESTING_UTILS_REQUIRE_SYMBOL_H
17+
18+
// NOLINTNEXTLINE(readability/namespace)
19+
namespace require_symbol
20+
{
21+
const class_typet &require_complete_class(
22+
const symbolt &class_symbol);
23+
}
24+
25+
#endif // CPROVER_TESTING_UTILS_REQUIRE_SYMBOL_H

0 commit comments

Comments
 (0)