1 /* This file is part of GNU cflow
2 Copyright (C) 1997-2019 Sergey Poznyakoff
3
4 GNU cflow is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3 of the License, or
7 (at your option) any later version.
8
9 GNU cflow is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <cflow.h>
18 #include <parser.h>
19 #include <hash.h>
20
21 static Hash_table *symbol_table;
22
23 static struct linked_list *static_symbol_list;
24 static struct linked_list *auto_symbol_list;
25 static struct linked_list *static_func_list;
26 static struct linked_list *unit_local_list;
27
28 static void
append_symbol(struct linked_list ** plist,Symbol * sp)29 append_symbol(struct linked_list **plist, Symbol *sp)
30 {
31 if (sp->entry) {
32 linked_list_unlink(sp->entry->list, sp->entry);
33 sp->entry = NULL;
34 }
35 if (!data_in_list(sp, *plist)) {
36 linked_list_append(plist, sp);
37 sp->entry = (*plist)->tail;
38 }
39 }
40
41 struct table_entry {
42 Symbol *sym;
43 };
44
45 /* Calculate the hash of a string. */
46 static size_t
hash_symbol_hasher(void const * data,size_t n_buckets)47 hash_symbol_hasher(void const *data, size_t n_buckets)
48 {
49 struct table_entry const *t = data;
50 if (!t->sym)
51 return ((size_t) data) % n_buckets;
52 return hash_string(t->sym->name, n_buckets);
53 }
54
55 /* Compare two strings for equality. */
56 static bool
hash_symbol_compare(void const * data1,void const * data2)57 hash_symbol_compare(void const *data1, void const *data2)
58 {
59 struct table_entry const *t1 = data1;
60 struct table_entry const *t2 = data2;
61 return t1->sym && t2->sym && strcmp(t1->sym->name, t2->sym->name) == 0;
62 }
63
64 Symbol *
lookup(const char * name)65 lookup(const char *name)
66 {
67 Symbol s, *sym;
68 struct table_entry t, *tp;
69
70 if (!symbol_table)
71 return NULL;
72 s.name = (char*) name;
73 t.sym = &s;
74 tp = hash_lookup(symbol_table, &t);
75 if (tp) {
76 sym = tp->sym;
77 while (sym->type == SymToken && sym->flag == symbol_alias)
78 sym = sym->alias;
79 } else
80 sym = NULL;
81 return sym;
82 }
83
84 /* Install a new symbol `NAME'. If UNIT_LOCAL is set, this symbol can
85 be local to the current compilation unit. */
86 Symbol *
install(char * name,int flags)87 install(char *name, int flags)
88 {
89 Symbol *sym;
90 struct table_entry *tp, *ret;
91
92 sym = xmalloc(sizeof(*sym));
93 memset(sym, 0, sizeof(*sym));
94 sym->type = SymUndefined;
95 sym->name = name;
96
97 tp = xmalloc(sizeof(*tp));
98 tp->sym = sym;
99
100 if (((flags & INSTALL_CHECK_LOCAL) &&
101 canonical_filename && strcmp(filename, canonical_filename)) ||
102 (flags & INSTALL_UNIT_LOCAL)) {
103 sym->flag = symbol_local;
104 append_symbol(&static_symbol_list, sym);
105 } else
106 sym->flag = symbol_none;
107
108 if (! ((symbol_table
109 || (symbol_table = hash_initialize (0, 0,
110 hash_symbol_hasher,
111 hash_symbol_compare, 0)))
112 && (ret = hash_insert (symbol_table, tp))))
113 xalloc_die ();
114
115 if (ret != tp) {
116 if (flags & INSTALL_OVERWRITE) {
117 free(sym);
118 free(tp);
119 return ret->sym;
120 }
121 if (ret->sym->type != SymUndefined)
122 sym->next = ret->sym;
123 ret->sym = sym;
124 free(tp);
125 }
126 sym->owner = ret;
127 return sym;
128 }
129
130 void
ident_change_storage(Symbol * sp,enum storage storage)131 ident_change_storage(Symbol *sp, enum storage storage)
132 {
133 if (sp->storage == storage)
134 return;
135 if (sp->storage == StaticStorage)
136 /* FIXME */;
137
138 switch (storage) {
139 case StaticStorage:
140 append_symbol(&static_symbol_list, sp);
141 break;
142 case AutoStorage:
143 append_symbol(&auto_symbol_list, sp);
144 break;
145 default:
146 break;
147 }
148 sp->storage = storage;
149 }
150
151 Symbol *
install_ident(char * name,enum storage storage)152 install_ident(char *name, enum storage storage)
153 {
154 Symbol *sp;
155
156 sp = install(name,
157 storage != AutoStorage ?
158 INSTALL_CHECK_LOCAL : INSTALL_DEFAULT);
159 sp->type = SymIdentifier;
160 sp->arity = -1;
161 sp->storage = ExternStorage;
162 sp->decl = NULL;
163 sp->source = NULL;
164 sp->def_line = -1;
165 sp->ref_line = NULL;
166 sp->caller = sp->callee = NULL;
167 sp->level = -1;
168 ident_change_storage(sp, storage);
169 return sp;
170 }
171
172 /* Unlink the symbol from the table entry */
173 static void
unlink_symbol(Symbol * sym)174 unlink_symbol(Symbol *sym)
175 {
176 Symbol *s, *prev = NULL;
177 struct table_entry *tp = sym->owner;
178 for (s = tp->sym; s; ) {
179 Symbol *next = s->next;
180 if (s == sym) {
181 if (prev)
182 prev->next = next;
183 else
184 tp->sym = next;
185 break;
186 } else
187 prev = s;
188 s = next;
189 }
190
191 sym->owner = NULL;
192 }
193
194 /* Unlink and free the first symbol from the table entry */
195 static void
delete_symbol(Symbol * sym)196 delete_symbol(Symbol *sym)
197 {
198 unlink_symbol(sym);
199 /* The symbol could have been referenced even if it is static
200 in -i^s mode. See tests/static.at for details. */
201 if (sym->ref_line == NULL && !(reverse_tree && sym->callee)) {
202 linked_list_destroy(&sym->ref_line);
203 linked_list_destroy(&sym->caller);
204 linked_list_destroy(&sym->callee);
205 free(sym);
206 }
207 }
208
209 /* Delete from the symbol table all static symbols defined in the current
210 source.
211 If the user requested static symbols in the listing, the symbol is
212 not deleted, as it may have been referenced by other symbols. Instead,
213 it is unlinked from its table entry.
214 NOTE: This takes advantage of the fact that install() uses LIFO strategy,
215 so we don't have to check the name of the source where the symbol was
216 defined. */
217
218 static void
static_free(void * data)219 static_free(void *data)
220 {
221 Symbol *sym = data;
222 struct table_entry *t = sym->owner;
223
224 if (!t)
225 return;
226 if (sym->flag == symbol_local) {
227 /* In xref mode, eligible unit-local symbols are retained in
228 unit_local_list for further processing.
229 Otherwise, they are deleted. */
230 if (print_option == PRINT_XREF && include_symbol(sym)) {
231 unlink_symbol(sym);
232 linked_list_append(&unit_local_list, sym);
233 } else {
234 delete_symbol(sym);
235 }
236 } else {
237 unlink_symbol(sym);
238 if (symbol_is_function(sym))
239 linked_list_append(&static_func_list, sym);
240 }
241 }
242
243 void
delete_statics()244 delete_statics()
245 {
246 if (static_symbol_list) {
247 static_symbol_list->free_data = static_free;
248 linked_list_destroy(&static_symbol_list);
249 }
250 }
251
252 /* See NOTE above */
253
254 /* Delete from the symbol table all auto variables with given nesting
255 level. */
256 int
delete_level_autos(void * data,void * call_data)257 delete_level_autos(void *data, void *call_data)
258 {
259 int level = *(int*)call_data;
260 Symbol *s = data;
261 if (s->level == level) {
262 delete_symbol(s);
263 return 1;
264 }
265 return 0;
266 }
267
268 int
delete_level_statics(void * data,void * call_data)269 delete_level_statics(void *data, void *call_data)
270 {
271 int level = *(int*)call_data;
272 Symbol *s = data;
273 if (s->level == level) {
274 unlink_symbol(s);
275 return 1;
276 }
277 return 0;
278 }
279
280 void
delete_autos(int level)281 delete_autos(int level)
282 {
283 linked_list_iterate(&auto_symbol_list, delete_level_autos, &level);
284 linked_list_iterate(&static_symbol_list, delete_level_statics, &level);
285 }
286
287 struct collect_data {
288 Symbol **sym;
289 int (*sel)(Symbol *p);
290 size_t index;
291 };
292
293 static bool
collect_processor(void * data,void * proc_data)294 collect_processor(void *data, void *proc_data)
295 {
296 struct table_entry *t = data;
297 struct collect_data *cd = proc_data;
298 Symbol *s;
299 for (s = t->sym; s; s = s->next) {
300 if (cd->sel(s)) {
301 if (cd->sym)
302 cd->sym[cd->index] = s;
303 cd->index++;
304 }
305 }
306 return true;
307 }
308
309 static int
collect_list_entry(void * item,void * proc_data)310 collect_list_entry(void *item, void *proc_data)
311 {
312 Symbol *s = item;
313 struct collect_data *cd = proc_data;
314 if (cd->sel(s)) {
315 cd->sym[cd->index] = s;
316 cd->index++;
317 }
318 return 0;
319 }
320
321 size_t
collect_symbols(Symbol *** return_sym,int (* sel)(Symbol * p),size_t reserved_slots)322 collect_symbols(Symbol ***return_sym, int (*sel)(Symbol *p),
323 size_t reserved_slots)
324 {
325 struct collect_data cdata;
326 size_t size;
327
328 size = hash_get_n_entries(symbol_table)
329 + linked_list_size(static_func_list)
330 + linked_list_size(unit_local_list);
331 cdata.sym = xcalloc(size + reserved_slots, sizeof(*cdata.sym));
332 cdata.index = 0;
333 cdata.sel = sel;
334 hash_do_for_each(symbol_table, collect_processor, &cdata);
335 linked_list_iterate(&static_func_list, collect_list_entry, &cdata);
336 linked_list_iterate(&unit_local_list, collect_list_entry, &cdata);
337
338 cdata.sym = xrealloc(cdata.sym,
339 (cdata.index + reserved_slots) * sizeof(*cdata.sym));
340 *return_sym = cdata.sym;
341 return cdata.index;
342 }
343
344 size_t
collect_functions(Symbol *** return_sym)345 collect_functions(Symbol ***return_sym)
346 {
347 Symbol **symbols;
348 size_t num, snum;
349 struct linked_list_entry *p;
350
351 /* Count static functions */
352 snum = linked_list_size(static_func_list);
353
354 /* Collect global functions */
355 num = collect_symbols(&symbols, symbol_is_function, snum);
356
357 /* Collect static functions */
358 if (snum)
359 for (p = linked_list_head(static_func_list); p; p = p->next)
360 symbols[num++] = p->data;
361 *return_sym = symbols;
362 return num;
363 }
364
365
366
367 /* Special handling for function parameters */
368
369 int
delete_parms_itr(void * data,void * call_data)370 delete_parms_itr(void *data, void *call_data)
371 {
372 int level = *(int*)call_data;
373 Symbol *s = data;
374 struct table_entry *t = s->owner;
375
376 if (!t)
377 return 1;
378 if (s->type == SymIdentifier && s->storage == AutoStorage
379 && s->flag == symbol_parm && s->level > level) {
380 delete_symbol(s);
381 return 1;
382 }
383 return 0;
384 }
385
386 /* Delete all parameters with parameter nesting level greater than LEVEL */
387 void
delete_parms(int level)388 delete_parms(int level)
389 {
390 linked_list_iterate(&auto_symbol_list, delete_parms_itr, &level);
391 }
392
393 /* Redeclare all saved parameters as automatic variables with the
394 given nesting level */
395 void
move_parms(int level)396 move_parms(int level)
397 {
398 struct linked_list_entry *p;
399
400 for (p = linked_list_head(auto_symbol_list); p; p = p->next) {
401 Symbol *s = p->data;
402
403 if (s->type == SymIdentifier && s->storage == AutoStorage
404 && s->flag == symbol_parm) {
405 s->level = level;
406 s->flag = symbol_none;
407 }
408 }
409 }
410
411