#include "smatch.h"
#include "smatch_slist.h"
#include "smatch_extra.h"
ALLOCATOR(relation, "related variables");
static struct relation *alloc_relation(const char *name, struct symbol *sym)
{
struct relation *tmp;
tmp = __alloc_relation(0);
tmp->name = alloc_string(name);
tmp->sym = sym;
return tmp;
}
struct related_list *clone_related_list(struct related_list *related)
{
struct relation *rel;
struct related_list *to_list = NULL;
FOR_EACH_PTR(related, rel) {
add_ptr_list(&to_list, rel);
} END_FOR_EACH_PTR(rel);
return to_list;
}
static int cmp_relation(struct relation *a, struct relation *b)
{
int ret;
if (a == b)
return 0;
if (a->sym > b->sym)
return -1;
if (a->sym < b->sym)
return 1;
ret = strcmp(a->name, b->name);
if (ret)
return ret;
return 0;
}
struct related_list *get_shared_relations(struct related_list *one,
struct related_list *two)
{
struct related_list *ret = NULL;
struct relation *one_rel;
struct relation *two_rel;
PREPARE_PTR_LIST(one, one_rel);
PREPARE_PTR_LIST(two, two_rel);
for (;;) {
if (!one_rel || !two_rel)
break;
if (cmp_relation(one_rel, two_rel) < 0) {
NEXT_PTR_LIST(one_rel);
} else if (cmp_relation(one_rel, two_rel) == 0) {
add_ptr_list(&ret, one_rel);
NEXT_PTR_LIST(one_rel);
NEXT_PTR_LIST(two_rel);
} else {
NEXT_PTR_LIST(two_rel);
}
}
FINISH_PTR_LIST(two_rel);
FINISH_PTR_LIST(one_rel);
return ret;
}
static void add_related(struct related_list **rlist, const char *name, struct symbol *sym)
{
struct relation *rel;
struct relation *new;
struct relation tmp = {
.name = (char *)name,
.sym = sym
};
FOR_EACH_PTR(*rlist, rel) {
if (cmp_relation(rel, &tmp) < 0)
continue;
if (cmp_relation(rel, &tmp) == 0)
return;
new = alloc_relation(name, sym);
INSERT_CURRENT(new, rel);
return;
} END_FOR_EACH_PTR(rel);
new = alloc_relation(name, sym);
add_ptr_list(rlist, new);
}
static struct related_list *del_related(struct smatch_state *state, const char *name, struct symbol *sym)
{
struct relation *tmp;
struct relation remove = {
.name = (char *)name,
.sym = sym,
};
struct related_list *ret = NULL;
FOR_EACH_PTR(estate_related(state), tmp) {
if (cmp_relation(tmp, &remove) != 0)
add_ptr_list(&ret, tmp);
} END_FOR_EACH_PTR(tmp);
return ret;
}
void remove_from_equiv(const char *name, struct symbol *sym)
{
struct sm_state *orig_sm;
struct relation *rel;
struct smatch_state *state;
struct related_list *to_update;
orig_sm = get_sm_state(SMATCH_EXTRA, name, sym);
if (!orig_sm || !get_dinfo(orig_sm->state)->related)
return;
state = clone_estate(orig_sm->state);
to_update = del_related(state, name, sym);
FOR_EACH_PTR(to_update, rel) {
struct sm_state *old_sm, *new_sm;
old_sm = get_sm_state(SMATCH_EXTRA, rel->name, rel->sym);
if (!old_sm)
continue;
new_sm = clone_sm(old_sm);
get_dinfo(new_sm->state)->related = to_update;
__set_sm(new_sm);
} END_FOR_EACH_PTR(rel);
}
void remove_from_equiv_expr(struct expression *expr)
{
char *name;
struct symbol *sym;
name = expr_to_var_sym(expr, &sym);
if (!name || !sym)
goto free;
remove_from_equiv(name, sym);
free:
free_string(name);
}
void set_related(struct smatch_state *estate, struct related_list *rlist)
{
if (!estate_related(estate) && !rlist)
return;
get_dinfo(estate)->related = rlist;
}
void set_equiv(struct expression *left, struct expression *right)
{
struct sm_state *right_sm, *left_sm, *other_sm;
struct relation *rel;
char *left_name;
struct symbol *left_sym;
struct related_list *rlist;
char *other_name;
struct symbol *other_sym;
left_name = expr_to_var_sym(left, &left_sym);
if (!left_name || !left_sym)
goto free;
other_name = get_other_name_sym(left_name, left_sym, &other_sym);
right_sm = get_sm_state_expr(SMATCH_EXTRA, right);
if (!right_sm) {
struct range_list *rl;
if (!get_implied_rl(right, &rl))
rl = alloc_whole_rl(get_type(right));
right_sm = set_state_expr(SMATCH_EXTRA, right, alloc_estate_rl(rl));
}
if (!right_sm)
goto free;
left_sm = clone_sm(right_sm);
left_sm->name = alloc_string(left_name);
left_sm->sym = left_sym;
left_sm->state = clone_estate_cast(get_type(left), right_sm->state);
set_extra_mod_helper(left_name, left_sym, left, left_sm->state);
__set_sm(left_sm);
if (other_name && other_sym) {
other_sm = clone_sm(right_sm);
other_sm->name = alloc_string(other_name);
other_sm->sym = other_sym;
other_sm->state = clone_estate_cast(get_type(left), left_sm->state);
set_extra_mod_helper(other_name, other_sym, NULL, other_sm->state);
__set_sm(other_sm);
}
rlist = clone_related_list(estate_related(right_sm->state));
add_related(&rlist, right_sm->name, right_sm->sym);
add_related(&rlist, left_name, left_sym);
if (other_name && other_sym)
add_related(&rlist, other_name, other_sym);
FOR_EACH_PTR(rlist, rel) {
struct sm_state *old_sm, *new_sm;
old_sm = get_sm_state(SMATCH_EXTRA, rel->name, rel->sym);
if (!old_sm)
continue;
new_sm = clone_sm(old_sm);
new_sm->state = clone_estate(old_sm->state);
get_dinfo(new_sm->state)->related = rlist;
__set_sm(new_sm);
} END_FOR_EACH_PTR(rel);
free:
free_string(left_name);
}
void set_equiv_state_expr(int id, struct expression *expr, struct smatch_state *state)
{
struct relation *rel;
struct smatch_state *estate;
estate = get_state_expr(SMATCH_EXTRA, expr);
if (!estate)
return;
FOR_EACH_PTR(get_dinfo(estate)->related, rel) {
set_state(id, rel->name, rel->sym, state);
} END_FOR_EACH_PTR(rel);
}