1 /*
   2  * Copyright (C) 2013 Oracle.
   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 ALLOCATOR(var_sym, "var_sym structs");
  21 
  22 struct var_sym *alloc_var_sym(const char *var, struct symbol *sym)
  23 {
  24         struct var_sym *tmp;
  25 
  26         tmp = __alloc_var_sym(0);
  27         tmp->var = alloc_string(var);
  28         tmp->sym = sym;
  29         return tmp;
  30 }
  31 
  32 struct var_sym_list *expr_to_vsl(struct expression *expr)
  33 {
  34         struct expression *unop;
  35         struct var_sym_list *ret = NULL;
  36         char *var;
  37         struct symbol *sym;
  38 
  39         expr = strip_expr(expr);
  40         if (!expr)
  41                 return NULL;
  42 
  43         if ((expr->type == EXPR_PREOP && expr->op == '*')) {
  44                 unop = strip_expr(expr->unop);
  45 
  46                 if (unop->type == EXPR_SYMBOL)
  47                         goto one_var;
  48                 return expr_to_vsl(unop);
  49         }
  50 
  51         if (expr->type == EXPR_BINOP ||
  52             expr->type == EXPR_LOGICAL ||
  53             expr->type == EXPR_COMPARE) {
  54                 struct var_sym_list *left, *right;
  55 
  56                 left = expr_to_vsl(expr->left);
  57                 right = expr_to_vsl(expr->right);
  58                 ret = combine_var_sym_lists(left, right);
  59                 free_var_syms_and_list(&left);
  60                 free_var_syms_and_list(&right);
  61                 return ret;
  62         }
  63 
  64         if (expr->type == EXPR_DEREF)
  65                 return expr_to_vsl(expr->deref);
  66 
  67 one_var:
  68         var = expr_to_var_sym(expr, &sym);
  69         if (!var || !sym) {
  70                 free_string(var);
  71                 return NULL;
  72         }
  73         add_var_sym(&ret, var, sym);
  74         return ret;
  75 }
  76 
  77 int cmp_var_sym(const struct var_sym *a, const struct var_sym *b)
  78 {
  79         int ret;
  80 
  81         if (a == b)
  82                 return 0;
  83         if (!b)
  84                 return -1;
  85         if (!a)
  86                 return 1;
  87 
  88         ret = strcmp(a->var, b->var);
  89         if (ret < 0)
  90                 return -1;
  91         if (ret > 0)
  92                 return 1;
  93 
  94         if (!b->sym && a->sym)
  95                 return -1;
  96         if (!a->sym && b->sym)
  97                 return 1;
  98         if (a->sym < b->sym)
  99                 return -1;
 100         if (a->sym > b->sym)
 101                 return 1;
 102 
 103         return 0;
 104 }
 105 
 106 void add_var_sym(struct var_sym_list **list, const char *var, struct symbol *sym)
 107 {
 108         struct var_sym *tmp, *new;
 109 
 110         if (in_var_sym_list(*list, var, sym))
 111                 return;
 112         new = alloc_var_sym(var, sym);
 113 
 114         FOR_EACH_PTR(*list, tmp) {
 115                 if (cmp_var_sym(tmp, new) < 0)
 116                         continue;
 117                 else if (cmp_var_sym(tmp, new) == 0) {
 118                         return;
 119                 } else {
 120                         INSERT_CURRENT(new, tmp);
 121                         return;
 122                 }
 123         } END_FOR_EACH_PTR(tmp);
 124         add_ptr_list(list, new);
 125 }
 126 
 127 void add_var_sym_expr(struct var_sym_list **list, struct expression *expr)
 128 {
 129         char *var;
 130         struct symbol *sym;
 131 
 132         var = expr_to_var_sym(expr, &sym);
 133         if (!var || !sym)
 134                 goto free;
 135         add_var_sym(list, var, sym);
 136 free:
 137         free_string(var);
 138 }
 139 
 140 static void free_var_sym(struct var_sym *vs)
 141 {
 142         free_string(vs->var);
 143         __free_var_sym(vs);
 144 }
 145 
 146 void del_var_sym(struct var_sym_list **list, const char *var, struct symbol *sym)
 147 {
 148         struct var_sym *tmp;
 149 
 150         FOR_EACH_PTR(*list, tmp) {
 151                 if (tmp->sym == sym && strcmp(tmp->var, var) == 0) {
 152                         DELETE_CURRENT_PTR(tmp);
 153                         free_var_sym(tmp);
 154                         return;
 155                 }
 156         } END_FOR_EACH_PTR(tmp);
 157 }
 158 
 159 int in_var_sym_list(struct var_sym_list *list, const char *var, struct symbol *sym)
 160 {
 161         struct var_sym *tmp;
 162 
 163         FOR_EACH_PTR(list, tmp) {
 164                 if (tmp->sym == sym && strcmp(tmp->var, var) == 0)
 165                         return 1;
 166         } END_FOR_EACH_PTR(tmp);
 167         return 0;
 168 }
 169 
 170 struct var_sym_list *clone_var_sym_list(struct var_sym_list *from_vsl)
 171 {
 172         struct var_sym *tmp, *clone_vs;
 173         struct var_sym_list *to_vsl = NULL;
 174 
 175         FOR_EACH_PTR(from_vsl, tmp) {
 176                 clone_vs = alloc_var_sym(tmp->var, tmp->sym);
 177                 add_ptr_list(&to_vsl, clone_vs);
 178         } END_FOR_EACH_PTR(tmp);
 179         return to_vsl;
 180 }
 181 
 182 void merge_var_sym_list(struct var_sym_list **dest, struct var_sym_list *src)
 183 {
 184         struct var_sym *tmp;
 185 
 186         FOR_EACH_PTR(src, tmp) {
 187                 add_var_sym(dest, tmp->var, tmp->sym);
 188         } END_FOR_EACH_PTR(tmp);
 189 }
 190 
 191 struct var_sym_list *combine_var_sym_lists(struct var_sym_list *one, struct var_sym_list *two)
 192 {
 193         struct var_sym_list *to_vsl;
 194 
 195         to_vsl = clone_var_sym_list(one);
 196         merge_var_sym_list(&to_vsl, two);
 197         return to_vsl;
 198 }
 199 
 200 int var_sym_lists_equiv(struct var_sym_list *one, struct var_sym_list *two)
 201 {
 202         struct var_sym *one_tmp, *two_tmp;
 203 
 204         if (one == two)
 205                 return 1;
 206 
 207         if (ptr_list_size((struct ptr_list *)one) != ptr_list_size((struct ptr_list *)two))
 208                 return 0;
 209 
 210         PREPARE_PTR_LIST(one, one_tmp);
 211         PREPARE_PTR_LIST(two, two_tmp);
 212         for (;;) {
 213                 if (!one_tmp && !two_tmp)
 214                         return 1;
 215                 if (one_tmp->sym != two_tmp->sym)
 216                         return 0;
 217                 if (strcmp(one_tmp->var, two_tmp->var) != 0)
 218                         return 0;
 219                 NEXT_PTR_LIST(one_tmp);
 220                 NEXT_PTR_LIST(two_tmp);
 221         }
 222         FINISH_PTR_LIST(two_tmp);
 223         FINISH_PTR_LIST(one_tmp);
 224 
 225         return 1;
 226 }
 227 
 228 void free_var_sym_list(struct var_sym_list **list)
 229 {
 230         __free_ptr_list((struct ptr_list **)list);
 231 }
 232 
 233 void free_var_syms_and_list(struct var_sym_list **list)
 234 {
 235         struct var_sym *tmp;
 236 
 237         FOR_EACH_PTR(*list, tmp) {
 238                 free_var_sym(tmp);
 239         } END_FOR_EACH_PTR(tmp);
 240         free_var_sym_list(list);
 241 }
 242