-
Notifications
You must be signed in to change notification settings - Fork 37
/
Copy pathcall.h
298 lines (258 loc) · 10.8 KB
/
call.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
/*
* (C) Copyright 2004-2024 Diomidis Spinellis
*
* This file is part of CScout.
*
* CScout is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CScout is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CScout. If not, see <http://www.gnu.org/licenses/>.
*
*
* Function call graph information
*
*/
#ifndef CALL_
#define CALL_
#include "funmetrics.h"
#include "tokid.h"
#include "fchar.h"
#include "token.h"
#include "filedetails.h"
class FCall;
class Sql;
class Id;
class Ctoken;
class Pltoken;
/*
* Generic call information of a called/calling entity.
* The calls can be to/from macros or functions.
* We identify four types of calls:
* 1. Macro calls macro
* Function like macro-expansion while expanding a macro
* 2. Macro calls function
* parse.y type.cpp call register_call(id->get_fcall())
* Function call with the identifier token being part of a
* (function-like) macro body
* 3. Function calls function
* parse.y type.cpp call register_call(id->get_fcall())
* Function call with the identifier token not part of a
* (function-like) macro body
* 4. Function calls macro
* Function-like macro expanded while processing a function body
* This scheme does not handle:
* macros that expand to functions
* functions containing function names that are object-like macros
* macro names consisting of multiple parts
*/
class Call {
public:
// A unique full identifier of a macro or function name
typedef vector <Eclass *> name_identifier;
private:
// Container for storing called and calling functions
typedef set <Call *> fun_container;
/*
* When processing the program a Call * is stored with each Id
* This allows accurate lookup of calls within a linkage unit.
* However, once a linkage unit goes out of scope, we need further
* help to reunite functions from varying projects and also locate
* function declarations when processing the source code. A function
* can be identified well enough by using the Tokid of its declaration.
* However, the same Tokid can through token pasting be used for
* declaring multiple functions. Therefore we use a multimap, and
* as a second step when we lookup a function we compare the
* corresponding tokens.
*/
typedef multimap <Tokid, Call *> fun_map;
string name; // Function's name
fun_container call; // Functions this function calls
fun_container caller; // Functions that call this function
unsigned char visited; // For calculating transitive closures (bit mask or boolean)
bool printed; // For printing a graph's nodes
FcharContext begin, end; // Span of definition
FunMetrics pre_cpp_metrics; // Metrics for this function, before cpp
FunMetrics post_cpp_metrics; // Metrics for this function, after cpp
int curr_stmt_nesting; // Current level of nesting
static int macro_nesting; // Level of nesting through macro tokens
void add_call(Call *f) { call.insert(f); }
void add_caller(Call *f) { caller.insert(f); }
// All known macros
static map<name_identifier, Call *> macros;
protected:
static fun_map all; // Set of all functions
static Call *current_fun; // Function currently being parsed
static stack<Call *> nesting; // Nested function definitions
/*
* A token (almost) uniquely identifying the call entity.
* (See fun_map comment for more details on the "almost".)
* Examples:
* Function's first declaration
* (could also be reference if implicitly declared)
* Macro's definition
*/
Token token;
public:
// Called when outside a function / macro body scope
static void unset_current_fun();
// The current function makes a call to id
static void register_call(const Id *id);
// The current function makes a call to f
static void register_call(Call *f);
// The current function (token t) makes a call to f
static void register_call(const Token &t, const Id *id);
// The current function (tokid t) makes a call to f
static void register_call(Tokid t, Call *f);
// A call from from to to
static void register_call(Call *from, Call *to);
// Clear the visit flags for all functions
static void clear_visit_flags();
static void clear_print_flags();
// Interface for iterating through all functions
typedef fun_map::const_iterator const_fmap_iterator_type;
static const_fmap_iterator_type fbegin() { return all.begin(); }
static const_fmap_iterator_type fend() { return all.end(); }
static int fsize() { return all.size(); }
static const fun_map &functions() { return all; }
// Get a call site for a given Token
static Call *get_call(const Token &t);
// Get a call sites for a given Tokid
static pair <const_fmap_iterator_type, const_fmap_iterator_type> get_calls(Tokid t);
// Dump the data in SQL format
static void dumpSql(Sql *db, ostream &of);
// Populate a map from ECs to macros
static void populate_macro_map();
// Return a macro corresponding to the specified name
static Call* get_macro(const name_identifier &name) {
auto it = macros.find(name);
return it == macros.end() ? nullptr : it->second;
}
const string &get_name() const { return name; }
bool contains(Eclass *e) const;
// Interface for iterating through calls and callers
typedef fun_container::const_iterator const_fiterator_type;
const_fiterator_type call_begin() const { return call.begin(); }
const_fiterator_type call_end() const { return call.end(); }
const_fiterator_type caller_begin() const { return caller.begin(); }
const_fiterator_type caller_end() const { return caller.end(); }
int get_num_call() const { return call.size(); }
int get_num_caller() const { return caller.size(); }
void set_visited() { visited = true; }
// Bit-or the specified visit flag
void set_visited(unsigned char v) { visited |= v; }
bool is_visited() const { return (bool)visited; }
bool is_visited(unsigned short visit_id) const { return (bool)(visited & visit_id); }
unsigned char get_visited() const { return visited; }
void set_printed() { printed = true; }
bool is_printed() const { return printed; }
// Mark the function's span
void mark_begin();
FcharContext get_begin() const { return begin; }
// Mark the function's span and add it to the corresponding file
void mark_end();
FcharContext get_end() const { return end; }
// Return true if the span represents a file region
bool is_span_valid() const;
// Return a reference to the pre-cpp Metrics class
FunMetrics &get_pre_cpp_metrics() { return pre_cpp_metrics; }
// Return a reference to the pre-cpp Metrics class
const FunMetrics &get_pre_cpp_const_metrics() const { return pre_cpp_metrics; }
// Return a reference to the post-cpp Metrics class
FunMetrics &get_post_cpp_metrics() { return post_cpp_metrics; }
// Return a reference to the post-cpp Metrics class
const FunMetrics &get_post_cpp_const_metrics() const { return post_cpp_metrics; }
// Return a token for the given object
const Token &get_token() const {return token; }
// Return a tokid for the given object
Tokid get_tokid() const {return token.get_parts_begin()->get_tokid(); }
// Return the function's file-id
Fileid get_fileid() const {return token.get_parts_begin()->get_tokid().get_fileid(); }
// Return true if the function is defined
virtual bool is_defined() const = 0;
// Return true if the function is declared
virtual bool is_declared() const = 0;
// Return the name of the entity's type
virtual const string & entity_type_name() const = 0;
// Return true if the function is static (e.g. a macro or a static C function)
virtual bool is_file_scoped() const = 0;
virtual bool is_cfun() const = 0;
virtual bool is_macro() const = 0;
virtual Tokid get_definition() const = 0;
// Return an entry's identifying site
Tokid get_site() const {
if (is_defined())
return get_definition();
else
return get_tokid();
}
// Set number of arguments
static inline void set_pre_cpp_num_args(int n) {
if (current_fun && !current_fun->pre_cpp_metrics.is_processed())
current_fun->pre_cpp_metrics.set_metric(FunMetrics::em_nmparam, n);
}
// Process a token destined for preprocessing
static inline void process_pre_cpp_token(const Pltoken &t, Metrics::e_metric keyword_metric) {
if (current_fun && !current_fun->pre_cpp_metrics.is_processed())
current_fun->pre_cpp_metrics.process_token(t, keyword_metric);
}
// Process a token destined for C-proper parsing
static inline void process_post_cpp_token(const Ctoken &t, Metrics::e_metric keyword_metric) {
if (current_fun && !current_fun->post_cpp_metrics.is_processed()) {
current_fun->post_cpp_metrics.process_token(t, keyword_metric);
}
}
// Queue for later processing an identifier destined for C-proper parsing
static inline void queue_post_cpp_identifier(const Ctoken &t) {
if (current_fun && !current_fun->post_cpp_metrics.is_processed()) {
current_fun->post_cpp_metrics.queue_identifier(t);
}
}
// Call the specified metrics function for the current function
static inline void call_pre_cpp_metrics(void (Metrics::*fun)()) {
if (current_fun && !current_fun->pre_cpp_metrics.is_processed())
(current_fun->pre_cpp_metrics.*fun)();
}
// Call the specified metrics function for the current function
static inline void call_post_cpp_metrics(void (Metrics::*fun)()) {
if (current_fun && !current_fun->pre_cpp_metrics.is_processed())
(current_fun->post_cpp_metrics.*fun)();
}
// Add the specified value to the specified metric
static inline void add_pre_cpp_metric(int metric, int value) {
if (current_fun && !current_fun->pre_cpp_metrics.is_processed())
current_fun->pre_cpp_metrics.add_metric(metric, value);
}
// The following three methods must always be called as a group
// See if we have started nesting through macro-expanded tokens
static void check_macro_nesting(const Ctoken &t);
// Increase the current function's level of nesting
static inline void increase_nesting() {
if (current_fun && !current_fun->post_cpp_metrics.is_processed() &&
!macro_nesting) {
current_fun->curr_stmt_nesting += 1;
current_fun->post_cpp_metrics.update_nesting(current_fun->curr_stmt_nesting);
Filedetails::get_post_cpp_metrics(Fchar::get_fileid()).update_nesting(current_fun->curr_stmt_nesting);
}
}
// Decrease the current function's level of nesting
static inline void decrease_nesting() {
if (!current_fun || current_fun->post_cpp_metrics.is_processed())
return;
if (macro_nesting)
macro_nesting--;
else
current_fun->curr_stmt_nesting--;
}
// ctor; never call it if the call for t already exists
Call(const string &s, const Token &t);
virtual ~Call() {}
};
#endif // CALL_