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 "scope.h"
  19 #include "smatch.h"
  20 #include "smatch_slist.h"
  21 #include "smatch_extra.h"
  22 
  23 static int my_id;
  24 
  25 /*
  26  * I'm going to store the states of local data at the end of each function.
  27  * Then at the end of the file, I'll combine the possible range lists for
  28  * each state and store the value in the on-disk database.
  29  *
  30  * One issue is that when I read the data back from the in-memory database at
  31  * the end of the file, then I don't have access to type information.  I'll just
  32  * cast everything to "long long" for now, I guess.  We'll see how that works.
  33  */
  34 
  35 static char *db_vals;
  36 static int get_vals(void *unused, int argc, char **argv, char **azColName)
  37 {
  38         db_vals = alloc_string(argv[0]);
  39         return 0;
  40 }
  41 
  42 static int is_array_symbol(struct expression *expr)
  43 {
  44         struct symbol *type;
  45 
  46         if (!expr || expr->type != EXPR_SYMBOL)
  47                 return 0;
  48         type = get_type(expr);
  49         if (!type)
  50                 return 0;
  51         if (type->type == SYM_ARRAY)
  52                 return 1;
  53         return 0;
  54 }
  55 
  56 int get_local_rl(struct expression *expr, struct range_list **rl)
  57 {
  58         char *name;
  59         struct range_list *tmp;
  60 
  61         if (!is_static(expr))
  62                 return 0;
  63         if (is_array_symbol(expr))
  64                 return 0;
  65         name = expr_to_var(expr);
  66         if (!name)
  67                 return 0;
  68 
  69         db_vals = NULL;
  70         run_sql(get_vals, NULL,
  71                 "select value from local_values where file = '%s' and variable = '%s';",
  72                 get_filename(), name);
  73         free_string(name);
  74         if (!db_vals)
  75                 return 0;
  76         str_to_rl(&llong_ctype, db_vals, &tmp);
  77         *rl = cast_rl(get_type(expr), tmp);
  78         free_string(db_vals);
  79 
  80         return 1;
  81 }
  82 
  83 int get_local_max_helper(struct expression *expr, sval_t *sval)
  84 {
  85         struct range_list *rl;
  86 
  87         if (!get_local_rl(expr, &rl))
  88                 return 0;
  89         *sval = rl_max(rl);
  90         return 1;
  91 }
  92 
  93 int get_local_min_helper(struct expression *expr, sval_t *sval)
  94 {
  95         struct range_list *rl;
  96 
  97         if (!get_local_rl(expr, &rl))
  98                 return 0;
  99         *sval = rl_min(rl);
 100         return 1;
 101 }
 102 
 103 static struct smatch_state *unmatched_state(struct sm_state *sm)
 104 {
 105         return alloc_estate_empty();
 106 }
 107 
 108 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
 109 {
 110         struct smatch_state *old;
 111         struct smatch_state *new;
 112 
 113         if (!sym || !(sym->ctype.modifiers & MOD_STATIC))
 114                 return;
 115         old = get_state(my_id, name, sym);
 116         if (old)
 117                 new = merge_estates(old, state);
 118         else
 119                 new = state;
 120         set_state(my_id, name, sym, new);
 121 }
 122 
 123 static void process_states(void)
 124 {
 125         struct sm_state *sm;
 126         struct smatch_state *extra;
 127         struct range_list *rl;
 128 
 129         FOR_EACH_SM(__get_cur_stree(), sm) {
 130                 if (sm->owner != my_id)
 131                         continue;
 132                 extra = get_state(SMATCH_EXTRA, sm->name, sm->sym);
 133                 if (extra && estate_rl(extra))
 134                         rl = rl_intersection(estate_rl(sm->state), estate_rl(extra));
 135                 else
 136                         rl = estate_rl(sm->state);
 137                 rl = cast_rl(&llong_ctype, rl);
 138                 mem_sql(NULL, NULL,
 139                         "insert into local_values values ('%s', '%s', '%s', %lu);",
 140                         get_filename(), sm->name, show_rl(rl),
 141                         (unsigned long)sm->sym);
 142         } END_FOR_EACH_SM(sm);
 143 }
 144 
 145 static int get_initial_value_sym(struct symbol *sym, char *name, sval_t *sval)
 146 {
 147         struct expression *expr_symbol, *deref, *tmp;
 148         char *member_name;
 149 
 150         if (!sym)
 151                 return 0;
 152 
 153         if (!sym->initializer) {
 154                 *sval = sval_type_val(&llong_ctype, 0);
 155                 return 1;
 156         }
 157         if (sym->initializer->type != EXPR_INITIALIZER)
 158                 return get_value(sym->initializer, sval);
 159 
 160         expr_symbol = symbol_expression(sym);
 161         FOR_EACH_PTR(sym->initializer->expr_list, tmp) {
 162                 if (tmp->type != EXPR_IDENTIFIER) /* how to handle arrays?? */
 163                         continue;
 164                 deref = member_expression(expr_symbol, '.', tmp->expr_ident);
 165                 member_name = expr_to_var(deref);
 166                 if (!member_name)
 167                         continue;
 168                 if (strcmp(name, member_name) == 0) {
 169                         free_string(member_name);
 170                         return get_value(tmp->ident_expression, sval);
 171                 }
 172                 free_string(member_name);
 173         } END_FOR_EACH_PTR(tmp);
 174 
 175         return 0;
 176 }
 177 
 178 static char *cur_name;
 179 static struct symbol *cur_symbol;
 180 static struct range_list *cur_rl;
 181 static void add_current_local(void)
 182 {
 183         sval_t initial;
 184 
 185         if (!get_initial_value_sym(cur_symbol, cur_name, &initial)) {
 186                 free_string(cur_name);
 187                 cur_name = NULL;
 188                 cur_rl = NULL;
 189                 return;
 190         }
 191         add_range(&cur_rl, initial, initial);
 192         if (!is_whole_rl(cur_rl))
 193                 sql_insert_local_values(cur_name, show_rl(cur_rl));
 194         free_string(cur_name);
 195         cur_name = NULL;
 196         cur_rl = NULL;
 197 }
 198 
 199 static int save_final_values(void *unused, int argc, char **argv, char **azColName)
 200 {
 201         char *name = argv[0];
 202         char *sym_str = argv[1];
 203         char *value = argv[2];
 204         struct range_list *rl;
 205 
 206         if (!cur_name) {
 207                 cur_name = alloc_string(name);
 208                 cur_symbol = (struct symbol *)strtoul(sym_str, NULL, 10);
 209         } else if (strcmp(cur_name, name) != 0) {
 210                 add_current_local();
 211                 cur_name = alloc_string(name);
 212                 cur_symbol = (struct symbol *)strtoul(sym_str, NULL, 10);
 213                 cur_rl = NULL;
 214         }
 215 
 216         str_to_rl(&llong_ctype, value, &rl);
 217         cur_rl = rl_union(cur_rl, rl);
 218 
 219         return 0;
 220 }
 221 
 222 static void match_end_file(struct symbol_list *sym_list)
 223 {
 224         mem_sql(save_final_values, NULL,
 225                 "select distinct variable, symbol, value from local_values order by variable;");
 226         if (cur_name)
 227                 add_current_local();
 228 }
 229 
 230 void register_local_values(int id)
 231 {
 232         my_id = id;
 233 
 234         if (!option_info)
 235                 return;
 236 
 237         set_dynamic_states(my_id);
 238         add_extra_mod_hook(&extra_mod_hook);
 239         add_unmatched_state_hook(my_id, &unmatched_state);
 240         add_merge_hook(my_id, &merge_estates);
 241         all_return_states_hook(&process_states);
 242         add_hook(match_end_file, END_FILE_HOOK);
 243         mem_sql(NULL, NULL, "alter table local_values add column symbol integer;");
 244 }