Skip to content

Commit 77185fd

Browse files
author
Thomas Kiley
authored
Merge pull request #2607 from jeannielynnmoulton/jeannie/ParseThrownExceptions2
[TG-4345] Parse thrown exceptions in java
2 parents 713d3fe + 655248a commit 77185fd

17 files changed

+250
-28
lines changed

jbmc/src/java_bytecode/java_bytecode_convert_class.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ void java_bytecode_convert_classt::convert(
280280
class_type.set_is_static_class(c.is_static_class);
281281
class_type.set_is_anonymous_class(c.is_anonymous_class);
282282
class_type.set_outer_class(c.outer_class);
283+
class_type.set_super_class(c.super_class);
283284
if(c.is_enum)
284285
{
285286
if(max_array_length != 0 && c.enum_elements > max_array_length)
@@ -303,9 +304,9 @@ void java_bytecode_convert_classt::convert(
303304
else
304305
class_type.set_access(ID_default);
305306

306-
if(!c.extends.empty())
307+
if(!c.super_class.empty())
307308
{
308-
const symbol_typet base("java::" + id2string(c.extends));
309+
const symbol_typet base("java::" + id2string(c.super_class));
309310

310311
// if the superclass is generic then the class has the superclass reference
311312
// including the generic info in its signature
@@ -323,7 +324,7 @@ void java_bytecode_convert_classt::convert(
323324
}
324325
catch(const unsupported_java_class_signature_exceptiont &e)
325326
{
326-
warning() << "Superclass: " << c.extends << " of class: " << c.name
327+
warning() << "Superclass: " << c.super_class << " of class: " << c.name
327328
<< "\n could not parse signature: " << superclass_ref.value()
328329
<< "\n " << e.what()
329330
<< "\n ignoring that the superclass is generic" << eom;
@@ -336,9 +337,9 @@ void java_bytecode_convert_classt::convert(
336337
}
337338
class_typet::componentt base_class_field;
338339
base_class_field.type() = class_type.bases().at(0).type();
339-
base_class_field.set_name("@"+id2string(c.extends));
340-
base_class_field.set_base_name("@"+id2string(c.extends));
341-
base_class_field.set_pretty_name("@"+id2string(c.extends));
340+
base_class_field.set_name("@" + id2string(c.super_class));
341+
base_class_field.set_base_name("@" + id2string(c.super_class));
342+
base_class_field.set_pretty_name("@" + id2string(c.super_class));
342343
class_type.components().push_back(base_class_field);
343344
}
344345

@@ -561,7 +562,7 @@ void java_bytecode_convert_classt::convert(
561562
}
562563

563564
// is this a root class?
564-
if(c.extends.empty())
565+
if(c.super_class.empty())
565566
java_root_class(*class_symbol);
566567
}
567568

jbmc/src/java_bytecode/java_bytecode_convert_method.cpp

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -444,14 +444,14 @@ void java_bytecode_convert_methodt::convert(
444444

445445
// Obtain a std::vector of code_typet::parametert objects from the
446446
// (function) type of the symbol
447-
code_typet code_type = to_code_type(method_symbol.type);
448-
code_type.set(ID_C_class, class_symbol.name);
449-
method_return_type=code_type.return_type();
450-
code_typet::parameterst &parameters=code_type.parameters();
447+
java_method_typet method_type = to_java_method_type(method_symbol.type);
448+
method_type.set(ID_C_class, class_symbol.name);
449+
method_return_type = method_type.return_type();
450+
code_typet::parameterst &parameters = method_type.parameters();
451451

452-
// Determine the number of local variable slots used by the JVM to maintan the
453-
// formal parameters
454-
slots_for_parameters=java_method_parameter_slots(code_type);
452+
// Determine the number of local variable slots used by the JVM to maintain
453+
// the formal parameters
454+
slots_for_parameters = java_method_parameter_slots(method_type);
455455

456456
debug() << "Generating codet: class "
457457
<< class_symbol.name << ", method "
@@ -587,7 +587,10 @@ void java_bytecode_convert_methodt::convert(
587587
method_symbol.location=m.source_location;
588588
method_symbol.location.set_function(method_identifier);
589589

590-
const std::string signature_string = pretty_signature(code_type);
590+
for(const auto &exception_name : m.throws_exception_table)
591+
method_type.add_throws_exceptions(exception_name);
592+
593+
const std::string signature_string = pretty_signature(method_type);
591594

592595
// Set up the pretty name for the method entry in the symbol table.
593596
// The pretty name of a constructor includes the base name of the class
@@ -601,7 +604,7 @@ void java_bytecode_convert_methodt::convert(
601604
method_symbol.pretty_name =
602605
id2string(class_symbol.pretty_name) + signature_string;
603606
INVARIANT(
604-
code_type.get_is_constructor(),
607+
method_type.get_is_constructor(),
605608
"Member type should have already been marked as a constructor");
606609
}
607610
else
@@ -610,14 +613,13 @@ void java_bytecode_convert_methodt::convert(
610613
id2string(m.base_name) + signature_string;
611614
}
612615

613-
method_symbol.type = code_type;
614-
615-
current_method=method_symbol.name;
616-
method_has_this=code_type.has_this();
616+
method_symbol.type = method_type;
617+
current_method = method_symbol.name;
618+
method_has_this = method_type.has_this();
617619
if((!m.is_abstract) && (!m.is_native))
618620
method_symbol.value = convert_instructions(
619621
m,
620-
code_type,
622+
method_type,
621623
method_symbol.name,
622624
to_java_class_type(class_symbol.type).lambda_method_handles());
623625
}

jbmc/src/java_bytecode/java_bytecode_parse_tree.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ void java_bytecode_parse_treet::classt::output(std::ostream &out) const
3838
}
3939

4040
out << "class " << name;
41-
if(!extends.empty())
42-
out << " extends " << extends;
41+
if(!super_class.empty())
42+
out << " extends " << super_class;
4343
out << " {" << '\n';
4444

4545
for(fieldst::const_iterator

jbmc/src/java_bytecode/java_bytecode_parse_tree.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ struct java_bytecode_parse_treet
114114
typedef std::vector<exceptiont> exception_tablet;
115115
exception_tablet exception_table;
116116

117+
std::vector<irep_idt> throws_exception_table;
118+
117119
struct local_variablet
118120
{
119121
irep_idt name;
@@ -197,7 +199,7 @@ struct java_bytecode_parse_treet
197199
classt &operator=(classt &&) = default;
198200
#endif
199201

200-
irep_idt name, extends;
202+
irep_idt name, super_class;
201203
bool is_abstract=false;
202204
bool is_enum=false;
203205
bool is_public=false, is_protected=false, is_private=false;

jbmc/src/java_bytecode/java_bytecode_parser.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ class java_bytecode_parsert:public parsert
121121
void rmethod(classt &parsed_class);
122122
void
123123
rinner_classes_attribute(classt &parsed_class, const u4 &attribute_length);
124+
std::vector<irep_idt> rexceptions_attribute();
124125
void rclass_attribute(classt &parsed_class);
125126
void rRuntimeAnnotation_attribute(annotationst &);
126127
void rRuntimeAnnotation(annotationt &);
@@ -503,8 +504,7 @@ void java_bytecode_parsert::rClassFile()
503504
constant(this_class).type().get(ID_C_base_name);
504505

505506
if(super_class!=0)
506-
parsed_class.extends=
507-
constant(super_class).type().get(ID_C_base_name);
507+
parsed_class.super_class = constant(super_class).type().get(ID_C_base_name);
508508

509509
rinterfaces(parsed_class);
510510
rfields(parsed_class);
@@ -1243,6 +1243,10 @@ void java_bytecode_parsert::rmethod_attribute(methodt &method)
12431243
{
12441244
rRuntimeAnnotation_attribute(method.annotations);
12451245
}
1246+
else if(attribute_name == "Exceptions")
1247+
{
1248+
method.throws_exception_table = rexceptions_attribute();
1249+
}
12461250
else
12471251
skip_bytes(attribute_length);
12481252
}
@@ -1655,6 +1659,26 @@ void java_bytecode_parsert::rinner_classes_attribute(
16551659
}
16561660
}
16571661

1662+
/// Corresponds to the element_value structure
1663+
/// Described in Java 8 specification 4.7.5
1664+
/// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.5
1665+
/// Parses the Exceptions attribute for the current method,
1666+
/// and returns a vector of exceptions.
1667+
std::vector<irep_idt> java_bytecode_parsert::rexceptions_attribute()
1668+
{
1669+
u2 number_of_exceptions = read_u2();
1670+
1671+
std::vector<irep_idt> exceptions;
1672+
for(size_t i = 0; i < number_of_exceptions; i++)
1673+
{
1674+
u2 exception_index_table = read_u2();
1675+
const irep_idt exception_name =
1676+
constant(exception_index_table).type().get(ID_C_base_name);
1677+
exceptions.push_back(exception_name);
1678+
}
1679+
return exceptions;
1680+
}
1681+
16581682
void java_bytecode_parsert::rclass_attribute(classt &parsed_class)
16591683
{
16601684
u2 attribute_name_index=read_u2();

jbmc/src/java_bytecode/java_types.h

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,26 @@ class java_class_typet:public class_typet
121121
return set(ID_is_inner_class, is_inner_class);
122122
}
123123

124-
const irep_idt get_outer_class() const
124+
const irep_idt &get_outer_class() const
125125
{
126126
return get(ID_outer_class);
127127
}
128128

129-
void set_outer_class(irep_idt outer_class)
129+
void set_outer_class(const irep_idt &outer_class)
130130
{
131131
return set(ID_outer_class, outer_class);
132132
}
133133

134+
const irep_idt &get_super_class() const
135+
{
136+
return get(ID_super_class);
137+
}
138+
139+
void set_super_class(const irep_idt &super_class)
140+
{
141+
return set(ID_super_class, super_class);
142+
}
143+
134144
const bool get_is_static_class() const
135145
{
136146
return get_bool(ID_is_static);
@@ -232,6 +242,35 @@ inline bool can_cast_type<java_class_typet>(const typet &type)
232242
return can_cast_type<class_typet>(type);
233243
}
234244

245+
class java_method_typet : public code_typet
246+
{
247+
public:
248+
const std::vector<irep_idt> throws_exceptions() const
249+
{
250+
std::vector<irep_idt> exceptions;
251+
for(const auto &e : find(ID_exceptions_thrown_list).get_sub())
252+
exceptions.push_back(e.id());
253+
return exceptions;
254+
}
255+
256+
void add_throws_exceptions(irep_idt exception)
257+
{
258+
add(ID_exceptions_thrown_list).get_sub().push_back(irept(exception));
259+
}
260+
};
261+
262+
inline const java_method_typet &to_java_method_type(const typet &type)
263+
{
264+
PRECONDITION(type.id() == ID_code);
265+
return static_cast<const java_method_typet &>(type);
266+
}
267+
268+
inline java_method_typet &to_java_method_type(typet &type)
269+
{
270+
PRECONDITION(type.id() == ID_code);
271+
return static_cast<java_method_typet &>(type);
272+
}
273+
235274
typet java_int_type();
236275
typet java_long_type();
237276
typet java_short_type();

jbmc/unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ SRC += java_bytecode/goto-programs/class_hierarchy_output.cpp \
1212
java_bytecode/java_bytecode_convert_class/convert_java_annotations.cpp \
1313
java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp \
1414
java_bytecode/java_bytecode_parse_generics/parse_generic_class.cpp \
15+
java_bytecode/java_bytecode_parser/parse_java_class.cpp \
1516
java_bytecode/java_bytecode_parser/parse_java_attributes.cpp \
1617
java_bytecode/java_object_factory/gen_nondet_string_init.cpp \
1718
java_bytecode/java_bytecode_parse_lambdas/java_bytecode_parse_lambda_method_table.cpp \
Binary file not shown.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
public class ChildClass extends ParentClass {
2+
}
3+
4+
class ParentClass extends GrandparentClass {
5+
}
6+
7+
class GrandparentClass {
8+
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import java.io.*;
2+
3+
public class ThrowsExceptions {
4+
5+
public static void test() throws CustomException, IOException {
6+
StringReader sr = new StringReader("");
7+
sr.read();
8+
throw new CustomException();
9+
}
10+
11+
public static void testNoExceptions() {
12+
StringReader sr = new StringReader("");
13+
}
14+
15+
}
16+
17+
class CustomException extends Exception {
18+
}

jbmc/unit/java_bytecode/java_bytecode_parser/parse_java_attributes.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <java-testing-utils/load_java_class.h>
1010
#include <java_bytecode/java_bytecode_convert_class.h>
11+
#include <java_bytecode/java_bytecode_convert_method.h>
1112
#include <java_bytecode/java_bytecode_parse_tree.h>
1213
#include <java_bytecode/java_types.h>
1314
#include <testing-utils/catch.hpp>
@@ -596,4 +597,45 @@ SCENARIO(
596597
}
597598
}
598599
}
600+
601+
GIVEN("A method that may or may not throw exceptions")
602+
{
603+
const symbol_tablet &new_symbol_table = load_java_class(
604+
"ThrowsExceptions", "./java_bytecode/java_bytecode_parser");
605+
WHEN("Parsing the exceptions attribute for a method that throws exceptions")
606+
{
607+
THEN("We should be able to get the list of exceptions it throws")
608+
{
609+
const symbolt &method_symbol =
610+
new_symbol_table.lookup_ref("java::ThrowsExceptions.test:()V");
611+
const java_method_typet method =
612+
to_java_method_type(method_symbol.type);
613+
const std::vector<irep_idt> exceptions = method.throws_exceptions();
614+
REQUIRE(exceptions.size() == 2);
615+
REQUIRE(
616+
std::find(
617+
exceptions.begin(),
618+
exceptions.end(),
619+
irept("CustomException").id()) != exceptions.end());
620+
REQUIRE(
621+
std::find(
622+
exceptions.begin(),
623+
exceptions.end(),
624+
irept("java.io.IOException").id()) != exceptions.end());
625+
}
626+
}
627+
WHEN(
628+
"Parsing the exceptions attribute for a method that throws no exceptions")
629+
{
630+
THEN("We should be able to get the list of exceptions it throws")
631+
{
632+
const symbolt &method_symbol = new_symbol_table.lookup_ref(
633+
"java::ThrowsExceptions.testNoExceptions:()V");
634+
const java_method_typet method =
635+
to_java_method_type(method_symbol.type);
636+
const std::vector<irep_idt> exceptions = method.throws_exceptions();
637+
REQUIRE(exceptions.size() == 0);
638+
}
639+
}
640+
}
599641
}

0 commit comments

Comments
 (0)