Skip to content

Commit 1b86b27

Browse files
authored
Merge pull request #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
Lines changed: 6 additions & 0 deletions
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$
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
void main()
3+
{
4+
}
Lines changed: 5 additions & 0 deletions
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

Lines changed: 14 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 20 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 12 additions & 0 deletions
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.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
interface HierarchyTestInterface1 {}
127 Bytes
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
interface HierarchyTestInterface2 {}
Lines changed: 66 additions & 0 deletions
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)