/* This file is part of GNU cflow Copyright (C) 1997, 2005-2007, 2009-2010, 2014-2016 Sergey Poznyakoff GNU cflow 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. GNU cflow 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 this program. If not, see . */ #include #include #include static Hash_table *symbol_table; static struct linked_list *static_symbol_list; static struct linked_list *auto_symbol_list; static struct linked_list *static_func_list; static void append_symbol(struct linked_list **plist, Symbol *sp) { if (sp->entry) { linked_list_unlink(sp->entry->list, sp->entry); sp->entry = NULL; } if (!data_in_list(sp, *plist)) { linked_list_append(plist, sp); sp->entry = (*plist)->tail; } } struct table_entry { Symbol *sym; }; /* Calculate the hash of a string. */ static size_t hash_symbol_hasher(void const *data, size_t n_buckets) { struct table_entry const *t = data; if (!t->sym) return ((size_t) data) % n_buckets; return hash_string(t->sym->name, n_buckets); } /* Compare two strings for equality. */ static bool hash_symbol_compare(void const *data1, void const *data2) { struct table_entry const *t1 = data1; struct table_entry const *t2 = data2; return t1->sym && t2->sym && strcmp(t1->sym->name, t2->sym->name) == 0; } Symbol * lookup(const char *name) { Symbol s, *sym; struct table_entry t, *tp; if (!symbol_table) return NULL; s.name = (char*) name; t.sym = &s; tp = hash_lookup(symbol_table, &t); if (tp) { sym = tp->sym; while (sym->type == SymToken && sym->flag == symbol_alias) sym = sym->alias; } else sym = NULL; return sym; } /* Install a new symbol `NAME'. If UNIT_LOCAL is set, this symbol can be local to the current compilation unit. */ Symbol * install(char *name, int flags) { Symbol *sym; struct table_entry *tp, *ret; sym = xmalloc(sizeof(*sym)); memset(sym, 0, sizeof(*sym)); sym->type = SymUndefined; sym->name = name; tp = xmalloc(sizeof(*tp)); tp->sym = sym; if (((flags & INSTALL_CHECK_LOCAL) && canonical_filename && strcmp(filename, canonical_filename)) || (flags & INSTALL_UNIT_LOCAL)) { sym->flag = symbol_temp; append_symbol(&static_symbol_list, sym); } else sym->flag = symbol_none; if (! ((symbol_table || (symbol_table = hash_initialize (0, 0, hash_symbol_hasher, hash_symbol_compare, 0))) && (ret = hash_insert (symbol_table, tp)))) xalloc_die (); if (ret != tp) { if (flags & INSTALL_OVERWRITE) { free(sym); free(tp); return ret->sym; } if (ret->sym->type != SymUndefined) sym->next = ret->sym; ret->sym = sym; free(tp); } sym->owner = ret; return sym; } void ident_change_storage(Symbol *sp, enum storage storage) { if (sp->storage == storage) return; if (sp->storage == StaticStorage) /* FIXME */; switch (storage) { case StaticStorage: append_symbol(&static_symbol_list, sp); break; case AutoStorage: append_symbol(&auto_symbol_list, sp); break; default: break; } sp->storage = storage; } Symbol * install_ident(char *name, enum storage storage) { Symbol *sp; sp = install(name, storage != AutoStorage ? INSTALL_CHECK_LOCAL : INSTALL_DEFAULT); sp->type = SymIdentifier; sp->arity = -1; sp->storage = ExternStorage; sp->decl = NULL; sp->source = NULL; sp->def_line = -1; sp->ref_line = NULL; sp->caller = sp->callee = NULL; sp->level = -1; ident_change_storage(sp, storage); return sp; } /* Unlink the symbol from the table entry */ static void unlink_symbol(Symbol *sym) { Symbol *s, *prev = NULL; struct table_entry *tp = sym->owner; for (s = tp->sym; s; ) { Symbol *next = s->next; if (s == sym) { if (prev) prev->next = next; else tp->sym = next; break; } else prev = s; s = next; } sym->owner = NULL; } /* Unlink and free the first symbol from the table entry */ static void delete_symbol(Symbol *sym) { unlink_symbol(sym); /* The symbol could have been referenced even if it is static in -i^s mode. See tests/static.at for details. */ if (sym->ref_line == NULL && !(reverse_tree && sym->callee)) { linked_list_destroy(&sym->ref_line); linked_list_destroy(&sym->caller); linked_list_destroy(&sym->callee); free(sym); } } /* Delete from the symbol table all static symbols defined in the current source. If the user requested static symbols in the listing, the symbol is not deleted, as it may have been referenced by other symbols. Instead, it is unlinked from its table entry. NOTE: This takes advantage of the fact that install() uses LIFO strategy, so we don't have to check the name of the source where the symbol was defined. */ static void static_free(void *data) { Symbol *sym = data; struct table_entry *t = sym->owner; if (!t) return; if (sym->flag == symbol_temp) delete_symbol(sym); else { unlink_symbol(sym); if (symbol_is_function(sym)) linked_list_append(&static_func_list, sym); } } void delete_statics() { if (static_symbol_list) { static_symbol_list->free_data = static_free; linked_list_destroy(&static_symbol_list); } } /* See NOTE above */ /* Delete from the symbol table all auto variables with given nesting level. */ int delete_level_autos(void *data, void *call_data) { int level = *(int*)call_data; Symbol *s = data; if (s->level == level) { delete_symbol(s); return 1; } return 0; } int delete_level_statics(void *data, void *call_data) { int level = *(int*)call_data; Symbol *s = data; if (s->level == level) { unlink_symbol(s); return 1; } return 0; } void delete_autos(int level) { linked_list_iterate(&auto_symbol_list, delete_level_autos, &level); linked_list_iterate(&static_symbol_list, delete_level_statics, &level); } struct collect_data { Symbol **sym; int (*sel)(Symbol *p); size_t index; }; static bool collect_processor(void *data, void *proc_data) { struct table_entry *t = data; struct collect_data *cd = proc_data; Symbol *s; for (s = t->sym; s; s = s->next) { if (cd->sel(s)) { if (cd->sym) cd->sym[cd->index] = s; cd->index++; } } return true; } size_t collect_symbols(Symbol ***return_sym, int (*sel)(Symbol *p), size_t reserved_slots) { struct collect_data cdata; cdata.sym = NULL; cdata.index = 0; cdata.sel = sel; hash_do_for_each (symbol_table, collect_processor, &cdata); cdata.sym = calloc(cdata.index + reserved_slots, sizeof(*cdata.sym)); if (!cdata.sym) xalloc_die(); cdata.index = 0; hash_do_for_each (symbol_table, collect_processor, &cdata); *return_sym = cdata.sym; return cdata.index; } size_t collect_functions(Symbol ***return_sym) { Symbol **symbols; size_t num, snum; struct linked_list_entry *p; /* Count static functions */ snum = 0; if (static_func_list) for (p = linked_list_head(static_func_list); p; p = p->next) snum++; /* Collect global functions */ num = collect_symbols(&symbols, symbol_is_function, snum); /* Collect static functions */ if (snum) for (p = linked_list_head(static_func_list); p; p = p->next) symbols[num++] = p->data; *return_sym = symbols; return num; } /* Special handling for function parameters */ int delete_parms_itr(void *data, void *call_data) { int level = *(int*)call_data; Symbol *s = data; struct table_entry *t = s->owner; if (!t) return 1; if (s->type == SymIdentifier && s->storage == AutoStorage && s->flag == symbol_parm && s->level > level) { delete_symbol(s); return 1; } return 0; } /* Delete all parameters with parameter nesting level greater than LEVEL */ void delete_parms(int level) { linked_list_iterate(&auto_symbol_list, delete_parms_itr, &level); } /* Redeclare all saved parameters as automatic variables with the given nesting level */ void move_parms(int level) { struct linked_list_entry *p; for (p = linked_list_head(auto_symbol_list); p; p = p->next) { Symbol *s = p->data; if (s->type == SymIdentifier && s->storage == AutoStorage && s->flag == symbol_parm) { s->level = level; s->flag = symbol_none; } } }