root/usr/src/tools/smatch/src/smatch_expressions.c
#include "smatch.h"
#include "smatch_extra.h"

DECLARE_ALLOCATOR(sname);
__ALLOCATOR(struct expression, "temporary expr", tmp_expression);

static struct position get_cur_pos(void)
{
        static struct position pos;
        static struct position none;
        struct expression *expr;
        struct statement *stmt;

        expr = last_ptr_list((struct ptr_list *)big_expression_stack);
        stmt = last_ptr_list((struct ptr_list *)big_statement_stack);
        if (expr)
                pos = expr->pos;
        else if (stmt)
                pos = stmt->pos;
        else
                pos = none;
        return pos;
}

struct expression *alloc_tmp_expression(struct position pos, int type)
{
        struct expression *expr;

        expr = __alloc_tmp_expression(0);
        expr->smatch_flags |= Tmp;
        expr->type = type;
        expr->pos = pos;
        return expr;
}

void free_tmp_expressions(void)
{
        clear_tmp_expression_alloc();
}

struct expression *zero_expr(void)
{
        struct expression *zero;

        zero = alloc_tmp_expression(get_cur_pos(), EXPR_VALUE);
        zero->value = 0;
        zero->ctype = &int_ctype;
        return zero;
}

struct expression *value_expr(long long val)
{
        struct expression *expr;

        if (!val)
                return zero_expr();

        expr = alloc_tmp_expression(get_cur_pos(), EXPR_VALUE);
        expr->value = val;
        expr->ctype = &llong_ctype;
        return expr;
}

struct expression *member_expression(struct expression *deref, int op, struct ident *member)
{
        struct expression *expr;

        expr = alloc_tmp_expression(deref->pos, EXPR_DEREF);
        expr->op = op;
        expr->deref = deref;
        expr->member = member;
        expr->member_offset = -1;
        return expr;
}

struct expression *preop_expression(struct expression *expr, int op)
{
        struct expression *preop;

        preop = alloc_tmp_expression(expr->pos, EXPR_PREOP);
        preop->unop = expr;
        preop->op = op;
        return preop;
}

struct expression *deref_expression(struct expression *expr)
{
        if (expr->type == EXPR_BINOP)
                expr = preop_expression(expr, '(');
        return preop_expression(expr, '*');
}

struct expression *assign_expression(struct expression *left, int op, struct expression *right)
{
        struct expression *expr;

        if (!right)
                return NULL;

        /* FIXME: make this a tmp expression. */
        expr = alloc_expression(right->pos, EXPR_ASSIGNMENT);
        expr->op = op;
        expr->left = left;
        expr->right = right;
        return expr;
}

struct expression *binop_expression(struct expression *left, int op, struct expression *right)
{
        struct expression *expr;

        expr = alloc_tmp_expression(right->pos, EXPR_BINOP);
        expr->op = op;
        expr->left = left;
        expr->right = right;
        return expr;
}

struct expression *array_element_expression(struct expression *array, struct expression *offset)
{
        struct expression *expr;

        expr = binop_expression(array, '+', offset);
        return deref_expression(expr);
}

struct expression *symbol_expression(struct symbol *sym)
{
        struct expression *expr;

        expr = alloc_tmp_expression(sym->pos, EXPR_SYMBOL);
        expr->symbol = sym;
        expr->symbol_name = sym->ident;
        return expr;
}

struct expression *compare_expression(struct expression *left, int op, struct expression *right)
{
        struct expression *expr;

        expr = alloc_tmp_expression(get_cur_pos(), EXPR_COMPARE);
        expr->op = op;
        expr->left = left;
        expr->right = right;
        return expr;
}

struct expression *string_expression(char *str)
{
        struct expression *ret;
        struct string *string;
        int len;

        len = strlen(str) + 1;
        string = (void *)__alloc_sname(4 + len);
        string->length = len;
        string->immutable = 0;
        memcpy(string->data, str, len);

        ret = alloc_tmp_expression(get_cur_pos(), EXPR_STRING);
        ret->wide = 0;
        ret->string = string;

        return ret;
}

struct expression *call_expression(struct expression *fn, struct expression_list *args)
{
        struct expression *expr;

        expr = alloc_tmp_expression(fn->pos, EXPR_CALL);
        expr->fn = fn;
        expr->args = args;

        return expr;
}

static struct expression *get_expression_from_base_and_str(struct expression *base, const char *addition)
{
        struct expression *ret = NULL;
        struct token *token, *prev, *end;
        char *alloc;

        if (addition[0] == '\0')
                return base;

        alloc = alloc_string_newline(addition);

        token = tokenize_buffer(alloc, strlen(alloc), &end);
        if (!token)
                goto free;
        if (token_type(token) != TOKEN_STREAMBEGIN)
                goto free;
        token = token->next;

        ret = base;
        while (token_type(token) == TOKEN_SPECIAL &&
               (token->special == SPECIAL_DEREFERENCE || token->special == '.')) {
                prev = token;
                token = token->next;
                if (token_type(token) != TOKEN_IDENT)
                        goto free;
                switch (prev->special) {
                case SPECIAL_DEREFERENCE:
                        ret = deref_expression(ret);
                        ret = member_expression(ret, '*', token->ident);
                        break;
                case '.':
                        ret = member_expression(ret, '.', token->ident);
                        break;
                default:
                        goto free;
                }
                token = token->next;
        }

        if (token_type(token) != TOKEN_STREAMEND)
                goto free;

free:
        free_string(alloc);

        return ret;
}

struct expression *gen_expression_from_name_sym(const char *name, struct symbol *sym)
{
        struct expression *base;
        int skip = 0;
        struct expression *ret;

        if (!name || !sym)
                return NULL;

        base = symbol_expression(sym);
        while (name[skip] != '\0' && name[skip] != '.' && name[skip] != '-')
                skip++;

        ret = get_expression_from_base_and_str(base, name + skip);
        if (ret) {
                char *new = expr_to_str(ret);

                /*
                 * FIXME: this sometimes changes "foo->bar.a.b->c" into
                 * "foo->bar.a.b.c".  I don't know why...  :(
                 *
                 */
                if (!new || strcmp(name, new) != 0)
                        return NULL;
        }
        return ret;
}

struct expression *gen_expression_from_key(struct expression *arg, const char *key)
{
        struct expression *ret;
        struct token *token, *prev, *end;
        const char *p = key;
        char buf[4095];
        char *alloc;
        size_t len;

        /* The idea is that we can parse either $0->foo or $->foo */
        if (key[0] != '$')
                return NULL;
        p++;
        while (*p >= '0' && *p <= '9')
                p++;
        len = snprintf(buf, sizeof(buf), "%s\n", p);
        alloc = alloc_string(buf);

        token = tokenize_buffer(alloc, len, &end);
        if (!token)
                return NULL;
        if (token_type(token) != TOKEN_STREAMBEGIN)
                return NULL;
        token = token->next;

        ret = arg;
        while (token_type(token) == TOKEN_SPECIAL &&
               (token->special == SPECIAL_DEREFERENCE || token->special == '.')) {
                prev = token;
                token = token->next;
                if (token_type(token) != TOKEN_IDENT)
                        return NULL;
                ret = deref_expression(ret);
                ret = member_expression(ret,
                                        (prev->special == SPECIAL_DEREFERENCE) ? '*' : '.',
                                        token->ident);
                token = token->next;
        }

        if (token_type(token) != TOKEN_STREAMEND)
                return NULL;

        return ret;
}

void expr_set_parent_expr(struct expression *expr, struct expression *parent)
{
        if (!expr)
                return;
        if (parent && parent->smatch_flags & Tmp)
                return;

        expr->parent = (unsigned long)parent | 0x1UL;
}

void expr_set_parent_stmt(struct expression *expr, struct statement *parent)
{
        if (!expr)
                return;
        expr->parent = (unsigned long)parent;
}

struct expression *expr_get_parent_expr(struct expression *expr)
{
        if (!expr)
                return NULL;
        if (!(expr->parent & 0x1UL))
                return NULL;
        return (struct expression *)(expr->parent & ~0x1UL);
}

struct statement *expr_get_parent_stmt(struct expression *expr)
{
        if (!expr)
                return NULL;
        if (expr->parent & 0x1UL)
                return NULL;
        return (struct statement *)expr->parent;
}