Skip to content

Commit 1b86b27

Browse files
authored
Merge pull request diffblue#1687 from smowton/smowton/feature/class-hierarchy-dot
Class hierarchy: add DOT output, unit tests
2 parents fd2bf6a + 8fa42b3 commit 1b86b27

18 files changed

+132
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
CORE
2+
main.c
3+
--class-hierarchy --dot
4+
digraph class_hierarchy
5+
^EXIT=0$
6+
^SIGNAL=0$
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
void main()
3+
{
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CORE
2+
main.c
3+
--class-hierarchy
4+
^EXIT=0$
5+
^SIGNAL=0$

src/goto-instrument/goto_instrument_parse_options.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Author: Daniel Kroening, [email protected]
2121
#include <util/json.h>
2222
#include <util/exit_codes.h>
2323

24+
#include <goto-programs/class_hierarchy.h>
2425
#include <goto-programs/goto_convert_functions.h>
2526
#include <goto-programs/remove_function_pointers.h>
2627
#include <goto-programs/remove_virtual_functions.h>
@@ -675,6 +676,18 @@ int goto_instrument_parse_optionst::doit()
675676
return 0;
676677
}
677678

679+
if(cmdline.isset("class-hierarchy"))
680+
{
681+
class_hierarchyt hierarchy;
682+
hierarchy(goto_model.symbol_table);
683+
if(cmdline.isset("dot"))
684+
hierarchy.output_dot(std::cout);
685+
else
686+
hierarchy.output(std::cout);
687+
688+
return 0;
689+
}
690+
678691
if(cmdline.isset("dot"))
679692
{
680693
namespacet ns(goto_model.symbol_table);
@@ -1474,6 +1487,7 @@ void goto_instrument_parse_optionst::help()
14741487
" --call-graph show graph of function calls\n"
14751488
// NOLINTNEXTLINE(whitespace/line_length)
14761489
" --reachable-call-graph show graph of function calls potentially reachable from main function\n"
1490+
" --class-hierarchy show class hierarchy\n"
14771491
"\n"
14781492
"Safety checks:\n"
14791493
" --no-assertions ignore user assertions\n"

src/goto-instrument/goto_instrument_parse_options.h

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Author: Daniel Kroening, [email protected]
4444
"(max-var):(max-po-trans):(ignore-arrays)" \
4545
"(cfg-kill)(no-dependencies)(force-loop-duplication)" \
4646
"(call-graph)(reachable-call-graph)" \
47+
"(class-hierarchy)" \
4748
"(no-po-rendering)(render-cluster-file)(render-cluster-function)" \
4849
"(nondet-volatile)(isr):" \
4950
"(stack-depth):(nondet-static)" \

src/goto-programs/class_hierarchy.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,23 @@ void class_hierarchyt::output(std::ostream &out) const
9999
<< ch << '\n';
100100
}
101101
}
102+
103+
/// Output class hierarchy in Graphviz DOT format
104+
/// \param ostr: stream to write DOT to
105+
void class_hierarchyt::output_dot(std::ostream &ostr) const
106+
{
107+
ostr << "digraph class_hierarchy {\n"
108+
<< " rankdir=BT;\n"
109+
<< " node [fontsize=12 shape=box];\n";
110+
for(const auto &c : class_map)
111+
{
112+
for(const auto &ch : c.second.parents)
113+
{
114+
ostr << " \"" << c.first << "\" -> "
115+
<< "\"" << ch << "\" "
116+
<< " [arrowhead=\"vee\"];"
117+
<< "\n";
118+
}
119+
}
120+
ostr << "}\n";
121+
}

src/goto-programs/class_hierarchy.h

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class class_hierarchyt
5252
}
5353

5454
void output(std::ostream &) const;
55+
void output_dot(std::ostream &) const;
5556

5657
protected:
5758
void get_children_trans_rec(const irep_idt &, idst &) const;

unit/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ SRC += unit_tests.cpp \
1717
analyses/does_remove_const/does_type_preserve_const_correctness.cpp \
1818
analyses/does_remove_const/is_type_at_least_as_const_as.cpp \
1919
goto-programs/goto_trace_output.cpp \
20+
goto-programs/class_hierarchy_output.cpp \
2021
java_bytecode/java_bytecode_convert_class/convert_abstract_class.cpp \
2122
java_bytecode/java_bytecode_parse_generics/parse_generic_class.cpp \
2223
java_bytecode/java_object_factory/gen_nondet_string_init.cpp \
286 Bytes
Binary file not shown.

unit/goto-programs/HierarchyTest.java

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
public class HierarchyTest {
2+
// These fields exist only so the classloader will load all test classes:
3+
HierarchyTestGrandchild field1;
4+
HierarchyTestChild2 field2;
5+
}
6+
7+
class HierarchyTestChild1 extends HierarchyTest {}
8+
9+
class HierarchyTestChild2 extends HierarchyTest {}
10+
11+
class HierarchyTestGrandchild extends HierarchyTestChild1
12+
implements HierarchyTestInterface1, HierarchyTestInterface2 {}
203 Bytes
Binary file not shown.
203 Bytes
Binary file not shown.
275 Bytes
Binary file not shown.
127 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
interface HierarchyTestInterface1 {}
127 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
interface HierarchyTestInterface2 {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*******************************************************************\
2+
3+
Module: Unit tests for class_hierarchyt output functions
4+
5+
Author: Diffblue Limited. All rights reserved.
6+
7+
\*******************************************************************/
8+
9+
#include <testing-utils/catch.hpp>
10+
#include <testing-utils/load_java_class.h>
11+
12+
#include <goto-programs/class_hierarchy.h>
13+
14+
#include <iostream>
15+
#include <sstream>
16+
17+
void require_parent_child_relationship(
18+
const std::string &parent_raw,
19+
const std::string &child_raw,
20+
const std::string &output,
21+
const std::string &output_dot)
22+
{
23+
std::string parent = "java::" + parent_raw;
24+
std::string child = "java::" + child_raw;
25+
26+
std::stringstream
27+
plain_child_expectation, plain_parent_expectation, dot_expectation;
28+
29+
plain_child_expectation << "Child of " << parent << ": " << child;
30+
plain_parent_expectation << "Parent of " << child << ": " << parent;
31+
dot_expectation << "\"" << child << "\" -> \"" << parent << "\"";
32+
33+
REQUIRE(output.find(plain_child_expectation.str()) != std::string::npos);
34+
REQUIRE(output.find(plain_parent_expectation.str()) != std::string::npos);
35+
REQUIRE(output_dot.find(dot_expectation.str()) != std::string::npos);
36+
}
37+
38+
SCENARIO(
39+
"Output a simple class hierarchy"
40+
"[core][goto-programs][class_hierarchy]")
41+
{
42+
symbol_tablet symbol_table =
43+
load_java_class("HierarchyTest", "goto-programs/");
44+
class_hierarchyt hierarchy;
45+
46+
std::stringstream output_stream;
47+
std::stringstream output_dot_stream;
48+
49+
hierarchy(symbol_table);
50+
hierarchy.output(output_stream);
51+
hierarchy.output_dot(output_dot_stream);
52+
53+
std::string output = output_stream.str();
54+
std::string output_dot = output_dot_stream.str();
55+
56+
require_parent_child_relationship(
57+
"HierarchyTest", "HierarchyTestChild1", output, output_dot);
58+
require_parent_child_relationship(
59+
"HierarchyTest", "HierarchyTestChild2", output, output_dot);
60+
require_parent_child_relationship(
61+
"HierarchyTestChild1", "HierarchyTestGrandchild", output, output_dot);
62+
require_parent_child_relationship(
63+
"HierarchyTestInterface1", "HierarchyTestGrandchild", output, output_dot);
64+
require_parent_child_relationship(
65+
"HierarchyTestInterface2", "HierarchyTestGrandchild", output, output_dot);
66+
}

0 commit comments

Comments
 (0)