Skip to content

Commit 9380275

Browse files
committed
Implemented a specialized version of const_exprt_visitort for codet
1 parent b0cb1ee commit 9380275

File tree

3 files changed

+193
-0
lines changed

3 files changed

+193
-0
lines changed

src/util/code_observer.h

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*******************************************************************\
2+
3+
Module: Misc Utilities
4+
5+
Author: Kurt Degiorgio
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// code_observer.h
11+
12+
/// This file contains a couple of utility classes that should be used when one
13+
/// needs to read all codet's of a particular type from a given symbol
14+
/// table. This saves you from having to manually iterate through the symbol
15+
/// table. These classes essentially implement the observable pattern.
16+
///
17+
/// In general, you first register one or more observers with
18+
/// const_code_observert. The observer is a callback that is
19+
/// associated with a specific irep_idt. This is used to determine for which
20+
/// codet's the observer will be notified. The second step involves calling
21+
/// visit_symbols which will in turn iterate through every codet inside the
22+
/// given symbol-table and notify registered observers only if the id of the
23+
/// codet in question matches the associated id of the observer.
24+
///
25+
/// Example: Examining all codet(ID_function_call)
26+
///
27+
/// \code
28+
///
29+
/// void A::examine_function_calls(const codet &code)
30+
/// {
31+
/// PRECONDITION(code.get_statement()==ID_function_call);
32+
/// ...
33+
/// }
34+
///
35+
/// ...
36+
///
37+
/// const_code_observert code_observer;
38+
/// code_observer.register_observer(ID_function_call,
39+
/// &A::examine_function_calls,
40+
/// this,
41+
/// std::placeholders::_1);
42+
/// code_observer.visit_symbols(symbol_table);
43+
///
44+
/// ...
45+
///
46+
/// \endcode
47+
///
48+
/// Note: each unique irep_idt can only be associated with one observer.
49+
///
50+
/// Note': Currently there is only a const version of the codet observer. This
51+
/// is due to limitations with the symbol table API, specifically the lack of a
52+
/// publicly visible non-const iterator. If the codet needs to be modified then
53+
/// inside the observer callback use symbol_table_baset::get_writeable_ref to
54+
/// get a non-const handle and walk down the tree again.
55+
56+
#ifndef CPROVER_UTIL_CODE_OBSERVER_H
57+
#define CPROVER_UTIL_CODE_OBSERVER_H
58+
59+
#include <unordered_map>
60+
#include <functional>
61+
#include <type_traits>
62+
63+
#include "symbol_table.h"
64+
#include "std_code.h"
65+
66+
/// \brief base class for all code observer implementations
67+
template <class T> class code_observer_baset
68+
{
69+
static_assert(
70+
std::is_base_of<codet, T>::value,
71+
"T is not derived from codet");
72+
73+
public:
74+
virtual ~code_observer_baset() = default;
75+
76+
/// registers an observer that is associated with specific codet id.
77+
/// \param id: id of the codet to be registered
78+
/// \param f: observer callback that is associated with the given, \p id
79+
/// \param args: arguments associated with the given callback, \p f, this
80+
// should include a placeholder object (std::placeholder_1), as the first
81+
// argument.
82+
template <class F, class... Args>
83+
void register_observer(const irep_idt &id, const F &&f, Args &&... args)
84+
{
85+
auto observer = std::bind(f, args...);
86+
code_observers_map.insert({id, observer});
87+
}
88+
89+
protected:
90+
/// notifies (if any) the observer that is registered to receive a callback
91+
/// for the given codet, \p code
92+
/// \param code: will be passed to the registered observer that
93+
/// that has the same associated id
94+
void notify(T &code) const
95+
{
96+
auto it = code_observers_map.find(code.get_statement());
97+
if(it != code_observers_map.end())
98+
it->second(code);
99+
}
100+
101+
private:
102+
using observerst = std::function<void(T &)>;
103+
using code_observer_mapt =
104+
std::unordered_map<const irep_idt, const observerst, irep_id_hash>;
105+
code_observer_mapt code_observers_map;
106+
};
107+
108+
/// \brief codet observer, used to get a callback for every
109+
/// codet of a specfic type from a given symbol-table.
110+
class const_code_observert final : private const_expr_visitort,
111+
public code_observer_baset<const codet>
112+
{
113+
public:
114+
/// iterates through a given symbol table and for every symbol calls its
115+
/// associated expression visitor
116+
/// \param symbol_table: symbol table to visit
117+
void visit_symbols(const symbol_tablet &symbol_table)
118+
{
119+
forall_symbols(it, symbol_table.symbols)
120+
{
121+
it->second.value.visit(*this);
122+
}
123+
}
124+
125+
/// Called by expr_vistort, for every expression found. Expressions that are
126+
/// not of type ID_code are ignored. Otherwise, this function will notify
127+
/// the observer (if any) that is associated with this code type.
128+
/// \param expr: expression
129+
void operator()(const exprt &expr) override
130+
{
131+
if(expr.id() == ID_code)
132+
notify(to_code(expr));
133+
}
134+
};
135+
136+
#endif // CPROVER_UTIL_CODE_OBSERVER_H

unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ SRC += unit_tests.cpp \
4242
util/parameter_indices.cpp \
4343
util/simplify_expr.cpp \
4444
util/symbol_table.cpp \
45+
util/code_observer.cpp \
4546
catch_example.cpp \
4647
# Empty last line
4748

unit/util/code_observer.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*******************************************************************\
2+
3+
Module: Code Observer Tests.
4+
5+
Author: DiffBlue Limited. All rights reserved.
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Unit tests for code_observer.h
11+
12+
#include <testing-utils/catch.hpp>
13+
#include <util/std_types.h>
14+
#include <util/symbol_table.h>
15+
#include <util/code_observer.h>
16+
17+
// Utilities
18+
19+
static void add_codet(
20+
const exprt &value,
21+
const std::string &name,
22+
symbol_tablet &symbol_table)
23+
{
24+
symbolt new_symbol;
25+
new_symbol.name = name;
26+
new_symbol.type = code_typet();
27+
new_symbol.value = value;
28+
symbol_table.add(new_symbol);
29+
}
30+
31+
// Observers
32+
33+
static void count_skips(const codet &code, int &count)
34+
{
35+
REQUIRE(code.id() == ID_code);
36+
REQUIRE(code.get_statement() == ID_skip);
37+
++count;
38+
}
39+
40+
// Tests
41+
42+
TEST_CASE("Count number of codet(ID_SKIP)")
43+
{
44+
symbol_tablet symbol_table;
45+
add_codet(codet(ID_skip), "SKIP1", symbol_table);
46+
add_codet(codet(ID_decl), "DECL", symbol_table);
47+
add_codet(codet(ID_skip), "SKIP2", symbol_table);
48+
49+
int count = 0;
50+
const_code_observert code_observer;
51+
code_observer.register_observer(
52+
ID_skip, &count_skips, std::placeholders::_1, std::ref(count));
53+
code_observer.visit_symbols(symbol_table);
54+
55+
REQUIRE(count == 2);
56+
}

0 commit comments

Comments
 (0)