1 /*
   2  * Copyright (C) 2006 Dan Carpenter.
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU General Public License
   6  * as published by the Free Software Foundation; either version 2
   7  * of the License, or (at your option) any later version.
   8  *
   9  * This program 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/copyleft/gpl.txt
  16  */
  17 
  18 #include "smatch.h"
  19 
  20 enum data_type {
  21         NO_DATA,
  22         EXPR_PTR,
  23         STMT_PTR,
  24         SYMBOL_PTR,
  25         SYM_LIST_PTR,
  26 };
  27 
  28 struct hook_container {
  29         int hook_type;
  30         int owner;
  31         void *fn;
  32 };
  33 ALLOCATOR(hook_container, "hook functions");
  34 DECLARE_PTR_LIST(hook_func_list, struct hook_container);
  35 
  36 typedef void (expr_func)(struct expression *expr);
  37 typedef void (stmt_func)(struct statement *stmt);
  38 typedef void (sym_func)(struct symbol *sym);
  39 typedef void (sym_list_func)(struct symbol_list *sym_list);
  40 
  41 static struct hook_func_list *merge_funcs;
  42 static struct hook_func_list *unmatched_state_funcs;
  43 static struct hook_func_list *hook_array[NUM_HOOKS] = {};
  44 static const enum data_type data_types[NUM_HOOKS] = {
  45         [EXPR_HOOK] = EXPR_PTR,
  46         [EXPR_HOOK_AFTER] = EXPR_PTR,
  47         [STMT_HOOK] = STMT_PTR,
  48         [STMT_HOOK_AFTER] = STMT_PTR,
  49         [SYM_HOOK] = EXPR_PTR,
  50         [STRING_HOOK] = EXPR_PTR,
  51         [DECLARATION_HOOK] = SYMBOL_PTR,
  52         [ASSIGNMENT_HOOK] = EXPR_PTR,
  53         [ASSIGNMENT_HOOK_AFTER] = EXPR_PTR,
  54         [RAW_ASSIGNMENT_HOOK] = EXPR_PTR,
  55         [GLOBAL_ASSIGNMENT_HOOK] = EXPR_PTR,
  56         [CALL_ASSIGNMENT_HOOK] = EXPR_PTR,
  57         [MACRO_ASSIGNMENT_HOOK] = EXPR_PTR,
  58         [BINOP_HOOK] = EXPR_PTR,
  59         [OP_HOOK] = EXPR_PTR,
  60         [LOGIC_HOOK] = EXPR_PTR,
  61         [PRELOOP_HOOK] = STMT_PTR,
  62         [CONDITION_HOOK] = EXPR_PTR,
  63         [SELECT_HOOK] = EXPR_PTR,
  64         [WHOLE_CONDITION_HOOK] = EXPR_PTR,
  65         [FUNCTION_CALL_HOOK] = EXPR_PTR,
  66         [CALL_HOOK_AFTER_INLINE] = EXPR_PTR,
  67         [FUNCTION_CALL_HOOK_AFTER_DB] = EXPR_PTR,
  68         [DEREF_HOOK] = EXPR_PTR,
  69         [CASE_HOOK] = NO_DATA,
  70         [ASM_HOOK] = STMT_PTR,
  71         [CAST_HOOK] = EXPR_PTR,
  72         [SIZEOF_HOOK] = EXPR_PTR,
  73         [BASE_HOOK] = SYMBOL_PTR,
  74         [FUNC_DEF_HOOK] = SYMBOL_PTR,
  75         [AFTER_DEF_HOOK] = SYMBOL_PTR,
  76         [END_FUNC_HOOK] = SYMBOL_PTR,
  77         [AFTER_FUNC_HOOK] = SYMBOL_PTR,
  78         [RETURN_HOOK] = EXPR_PTR,
  79         [INLINE_FN_START] = EXPR_PTR,
  80         [INLINE_FN_END] = EXPR_PTR,
  81         [END_FILE_HOOK] = SYM_LIST_PTR,
  82 };
  83 
  84 void (**pre_merge_hooks)(struct sm_state *cur, struct sm_state *other);
  85 
  86 struct scope_container {
  87         void *fn;
  88         void *data;
  89 };
  90 ALLOCATOR(scope_container, "scope hook functions");
  91 DECLARE_PTR_LIST(scope_hook_list, struct scope_container);
  92 DECLARE_PTR_LIST(scope_hook_stack, struct scope_hook_list);
  93 static struct scope_hook_stack *scope_hooks;
  94 
  95 void add_hook(void *func, enum hook_type type)
  96 {
  97         struct hook_container *container = __alloc_hook_container(0);
  98 
  99         container->hook_type = type;
 100         container->fn = func;
 101 
 102         add_ptr_list(&hook_array[type], container);
 103 }
 104 
 105 void add_merge_hook(int client_id, merge_func_t *func)
 106 {
 107         struct hook_container *container = __alloc_hook_container(0);
 108         container->owner = client_id;
 109         container->fn = func;
 110         add_ptr_list(&merge_funcs, container);
 111 }
 112 
 113 void add_unmatched_state_hook(int client_id, unmatched_func_t *func)
 114 {
 115         struct hook_container *container = __alloc_hook_container(0);
 116         container->owner = client_id;
 117         container->fn = func;
 118         add_ptr_list(&unmatched_state_funcs, container);
 119 }
 120 
 121 void add_pre_merge_hook(int client_id, void (*hook)(struct sm_state *cur, struct sm_state *other))
 122 {
 123         pre_merge_hooks[client_id] = hook;
 124 }
 125 
 126 static void pass_expr_to_client(void *fn, void *data)
 127 {
 128         ((expr_func *)fn)((struct expression *)data);
 129 }
 130 
 131 static void pass_stmt_to_client(void *fn, void *data)
 132 {
 133         ((stmt_func *)fn)((struct statement *)data);
 134 }
 135 
 136 static void pass_sym_to_client(void *fn, void *data)
 137 {
 138         ((sym_func *)fn)((struct symbol *)data);
 139 }
 140 
 141 static void pass_sym_list_to_client(void *fn, void *data)
 142 {
 143         ((sym_list_func *)fn)((struct symbol_list *)data);
 144 }
 145 
 146 void __pass_to_client(void *data, enum hook_type type)
 147 {
 148         struct hook_container *container;
 149 
 150         FOR_EACH_PTR(hook_array[type], container) {
 151                 switch (data_types[type]) {
 152                 case EXPR_PTR:
 153                         pass_expr_to_client(container->fn, data);
 154                         break;
 155                 case STMT_PTR:
 156                         pass_stmt_to_client(container->fn, data);
 157                         break;
 158                 case SYMBOL_PTR:
 159                         pass_sym_to_client(container->fn, data);
 160                         break;
 161                 case SYM_LIST_PTR:
 162                         pass_sym_list_to_client(container->fn, data);
 163                         break;
 164                 }
 165         } END_FOR_EACH_PTR(container);
 166 }
 167 
 168 void __pass_case_to_client(struct expression *switch_expr,
 169                            struct range_list *rl)
 170 {
 171         typedef void (case_func)(struct expression *switch_expr,
 172                                  struct range_list *rl);
 173         struct hook_container *container;
 174 
 175         FOR_EACH_PTR(hook_array[CASE_HOOK], container) {
 176                 ((case_func *)container->fn)(switch_expr, rl);
 177         } END_FOR_EACH_PTR(container);
 178 }
 179 
 180 int __has_merge_function(int client_id)
 181 {
 182         struct hook_container *tmp;
 183 
 184         FOR_EACH_PTR(merge_funcs, tmp) {
 185                 if (tmp->owner == client_id)
 186                         return 1;
 187         } END_FOR_EACH_PTR(tmp);
 188         return 0;
 189 }
 190 
 191 struct smatch_state *__client_merge_function(int owner,
 192                                              struct smatch_state *s1,
 193                                              struct smatch_state *s2)
 194 {
 195         struct smatch_state *tmp_state;
 196         struct hook_container *tmp;
 197 
 198         /* Pass NULL states first and the rest alphabetically by name */
 199         if (!s2 || (s1 && strcmp(s2->name, s1->name) < 0)) {
 200                 tmp_state = s1;
 201                 s1 = s2;
 202                 s2 = tmp_state;
 203         }
 204 
 205         FOR_EACH_PTR(merge_funcs, tmp) {
 206                 if (tmp->owner == owner)
 207                         return ((merge_func_t *)tmp->fn)(s1, s2);
 208         } END_FOR_EACH_PTR(tmp);
 209         return &undefined;
 210 }
 211 
 212 struct smatch_state *__client_unmatched_state_function(struct sm_state *sm)
 213 {
 214         struct hook_container *tmp;
 215 
 216         FOR_EACH_PTR(unmatched_state_funcs, tmp) {
 217                 if (tmp->owner == sm->owner)
 218                         return ((unmatched_func_t *)tmp->fn)(sm);
 219         } END_FOR_EACH_PTR(tmp);
 220         return &undefined;
 221 }
 222 
 223 void call_pre_merge_hook(struct sm_state *cur, struct sm_state *other)
 224 {
 225         if (cur->owner >= num_checks)
 226                 return;
 227 
 228         if (pre_merge_hooks[cur->owner])
 229                 pre_merge_hooks[cur->owner](cur, other);
 230 }
 231 
 232 static struct scope_hook_list *pop_scope_hook_list(struct scope_hook_stack **stack)
 233 {
 234         struct scope_hook_list *hook_list;
 235 
 236         hook_list = last_ptr_list((struct ptr_list *)*stack);
 237         delete_ptr_list_last((struct ptr_list **)stack);
 238         return hook_list;
 239 }
 240 
 241 static void push_scope_hook_list(struct scope_hook_stack **stack, struct scope_hook_list *l)
 242 {
 243         add_ptr_list(stack, l);
 244 }
 245 
 246 void add_scope_hook(scope_hook *fn, void *data)
 247 {
 248         struct scope_hook_list *hook_list;
 249         struct scope_container *new;
 250 
 251         if (!scope_hooks)
 252                 return;
 253         hook_list = pop_scope_hook_list(&scope_hooks);
 254         new = __alloc_scope_container(0);
 255         new->fn = fn;
 256         new->data = data;
 257         add_ptr_list(&hook_list, new);
 258         push_scope_hook_list(&scope_hooks, hook_list);
 259 }
 260 
 261 void __push_scope_hooks(void)
 262 {
 263         push_scope_hook_list(&scope_hooks, NULL);
 264 }
 265 
 266 void __call_scope_hooks(void)
 267 {
 268         struct scope_hook_list *hook_list;
 269         struct scope_container *tmp;
 270 
 271         if (!scope_hooks)
 272                 return;
 273 
 274         hook_list = pop_scope_hook_list(&scope_hooks);
 275         FOR_EACH_PTR(hook_list, tmp) {
 276                 ((scope_hook *)tmp->fn)(tmp->data);
 277                 __free_scope_container(tmp);
 278         } END_FOR_EACH_PTR(tmp);
 279 }
 280 
 281 void allocate_hook_memory(void)
 282 {
 283         pre_merge_hooks = malloc(num_checks * sizeof(*pre_merge_hooks));
 284         memset(pre_merge_hooks, 0, num_checks * sizeof(*pre_merge_hooks));
 285 }
 286