root/usr/src/tools/smatch/src/smatch_hooks.c
/*
 * Copyright (C) 2006 Dan Carpenter.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
 */

#include "smatch.h"

enum data_type {
        NO_DATA,
        EXPR_PTR,
        STMT_PTR,
        SYMBOL_PTR,
        SYM_LIST_PTR,
};

struct hook_container {
        int hook_type;
        int owner;
        void *fn;
};
ALLOCATOR(hook_container, "hook functions");
DECLARE_PTR_LIST(hook_func_list, struct hook_container);

typedef void (expr_func)(struct expression *expr);
typedef void (stmt_func)(struct statement *stmt);
typedef void (sym_func)(struct symbol *sym);
typedef void (sym_list_func)(struct symbol_list *sym_list);

static struct hook_func_list *merge_funcs;
static struct hook_func_list *unmatched_state_funcs;
static struct hook_func_list *hook_array[NUM_HOOKS] = {};
static const enum data_type data_types[NUM_HOOKS] = {
        [EXPR_HOOK] = EXPR_PTR,
        [EXPR_HOOK_AFTER] = EXPR_PTR,
        [STMT_HOOK] = STMT_PTR,
        [STMT_HOOK_AFTER] = STMT_PTR,
        [SYM_HOOK] = EXPR_PTR,
        [STRING_HOOK] = EXPR_PTR,
        [DECLARATION_HOOK] = SYMBOL_PTR,
        [ASSIGNMENT_HOOK] = EXPR_PTR,
        [ASSIGNMENT_HOOK_AFTER] = EXPR_PTR,
        [RAW_ASSIGNMENT_HOOK] = EXPR_PTR,
        [GLOBAL_ASSIGNMENT_HOOK] = EXPR_PTR,
        [CALL_ASSIGNMENT_HOOK] = EXPR_PTR,
        [MACRO_ASSIGNMENT_HOOK] = EXPR_PTR,
        [BINOP_HOOK] = EXPR_PTR,
        [OP_HOOK] = EXPR_PTR,
        [LOGIC_HOOK] = EXPR_PTR,
        [PRELOOP_HOOK] = STMT_PTR,
        [CONDITION_HOOK] = EXPR_PTR,
        [SELECT_HOOK] = EXPR_PTR,
        [WHOLE_CONDITION_HOOK] = EXPR_PTR,
        [FUNCTION_CALL_HOOK] = EXPR_PTR,
        [CALL_HOOK_AFTER_INLINE] = EXPR_PTR,
        [FUNCTION_CALL_HOOK_AFTER_DB] = EXPR_PTR,
        [DEREF_HOOK] = EXPR_PTR,
        [CASE_HOOK] = NO_DATA,
        [ASM_HOOK] = STMT_PTR,
        [CAST_HOOK] = EXPR_PTR,
        [SIZEOF_HOOK] = EXPR_PTR,
        [BASE_HOOK] = SYMBOL_PTR,
        [FUNC_DEF_HOOK] = SYMBOL_PTR,
        [AFTER_DEF_HOOK] = SYMBOL_PTR,
        [END_FUNC_HOOK] = SYMBOL_PTR,
        [AFTER_FUNC_HOOK] = SYMBOL_PTR,
        [RETURN_HOOK] = EXPR_PTR,
        [INLINE_FN_START] = EXPR_PTR,
        [INLINE_FN_END] = EXPR_PTR,
        [END_FILE_HOOK] = SYM_LIST_PTR,
};

void (**pre_merge_hooks)(struct sm_state *cur, struct sm_state *other);

struct scope_container {
        void *fn;
        void *data;
};
ALLOCATOR(scope_container, "scope hook functions");
DECLARE_PTR_LIST(scope_hook_list, struct scope_container);
DECLARE_PTR_LIST(scope_hook_stack, struct scope_hook_list);
static struct scope_hook_stack *scope_hooks;

void add_hook(void *func, enum hook_type type)
{
        struct hook_container *container = __alloc_hook_container(0);

        container->hook_type = type;
        container->fn = func;

        add_ptr_list(&hook_array[type], container);
}

void add_merge_hook(int client_id, merge_func_t *func)
{
        struct hook_container *container = __alloc_hook_container(0);
        container->owner = client_id;
        container->fn = func;
        add_ptr_list(&merge_funcs, container);
}

void add_unmatched_state_hook(int client_id, unmatched_func_t *func)
{
        struct hook_container *container = __alloc_hook_container(0);
        container->owner = client_id;
        container->fn = func;
        add_ptr_list(&unmatched_state_funcs, container);
}

void add_pre_merge_hook(int client_id, void (*hook)(struct sm_state *cur, struct sm_state *other))
{
        pre_merge_hooks[client_id] = hook;
}

static void pass_expr_to_client(void *fn, void *data)
{
        ((expr_func *)fn)((struct expression *)data);
}

static void pass_stmt_to_client(void *fn, void *data)
{
        ((stmt_func *)fn)((struct statement *)data);
}

static void pass_sym_to_client(void *fn, void *data)
{
        ((sym_func *)fn)((struct symbol *)data);
}

static void pass_sym_list_to_client(void *fn, void *data)
{
        ((sym_list_func *)fn)((struct symbol_list *)data);
}

void __pass_to_client(void *data, enum hook_type type)
{
        struct hook_container *container;

        FOR_EACH_PTR(hook_array[type], container) {
                switch (data_types[type]) {
                case EXPR_PTR:
                        pass_expr_to_client(container->fn, data);
                        break;
                case STMT_PTR:
                        pass_stmt_to_client(container->fn, data);
                        break;
                case SYMBOL_PTR:
                        pass_sym_to_client(container->fn, data);
                        break;
                case SYM_LIST_PTR:
                        pass_sym_list_to_client(container->fn, data);
                        break;
                }
        } END_FOR_EACH_PTR(container);
}

void __pass_case_to_client(struct expression *switch_expr,
                           struct range_list *rl)
{
        typedef void (case_func)(struct expression *switch_expr,
                                 struct range_list *rl);
        struct hook_container *container;

        FOR_EACH_PTR(hook_array[CASE_HOOK], container) {
                ((case_func *)container->fn)(switch_expr, rl);
        } END_FOR_EACH_PTR(container);
}

int __has_merge_function(int client_id)
{
        struct hook_container *tmp;

        FOR_EACH_PTR(merge_funcs, tmp) {
                if (tmp->owner == client_id)
                        return 1;
        } END_FOR_EACH_PTR(tmp);
        return 0;
}

struct smatch_state *__client_merge_function(int owner,
                                             struct smatch_state *s1,
                                             struct smatch_state *s2)
{
        struct smatch_state *tmp_state;
        struct hook_container *tmp;

        /* Pass NULL states first and the rest alphabetically by name */
        if (!s2 || (s1 && strcmp(s2->name, s1->name) < 0)) {
                tmp_state = s1;
                s1 = s2;
                s2 = tmp_state;
        }

        FOR_EACH_PTR(merge_funcs, tmp) {
                if (tmp->owner == owner)
                        return ((merge_func_t *)tmp->fn)(s1, s2);
        } END_FOR_EACH_PTR(tmp);
        return &undefined;
}

struct smatch_state *__client_unmatched_state_function(struct sm_state *sm)
{
        struct hook_container *tmp;

        FOR_EACH_PTR(unmatched_state_funcs, tmp) {
                if (tmp->owner == sm->owner)
                        return ((unmatched_func_t *)tmp->fn)(sm);
        } END_FOR_EACH_PTR(tmp);
        return &undefined;
}

void call_pre_merge_hook(struct sm_state *cur, struct sm_state *other)
{
        if (cur->owner >= num_checks)
                return;

        if (pre_merge_hooks[cur->owner])
                pre_merge_hooks[cur->owner](cur, other);
}

static struct scope_hook_list *pop_scope_hook_list(struct scope_hook_stack **stack)
{
        struct scope_hook_list *hook_list;

        hook_list = last_ptr_list((struct ptr_list *)*stack);
        delete_ptr_list_last((struct ptr_list **)stack);
        return hook_list;
}

static void push_scope_hook_list(struct scope_hook_stack **stack, struct scope_hook_list *l)
{
        add_ptr_list(stack, l);
}

void add_scope_hook(scope_hook *fn, void *data)
{
        struct scope_hook_list *hook_list;
        struct scope_container *new;

        if (!scope_hooks)
                return;
        hook_list = pop_scope_hook_list(&scope_hooks);
        new = __alloc_scope_container(0);
        new->fn = fn;
        new->data = data;
        add_ptr_list(&hook_list, new);
        push_scope_hook_list(&scope_hooks, hook_list);
}

void __push_scope_hooks(void)
{
        push_scope_hook_list(&scope_hooks, NULL);
}

void __call_scope_hooks(void)
{
        struct scope_hook_list *hook_list;
        struct scope_container *tmp;

        if (!scope_hooks)
                return;

        hook_list = pop_scope_hook_list(&scope_hooks);
        FOR_EACH_PTR(hook_list, tmp) {
                ((scope_hook *)tmp->fn)(tmp->data);
                __free_scope_container(tmp);
        } END_FOR_EACH_PTR(tmp);
}

void allocate_hook_memory(void)
{
        pre_merge_hooks = malloc(num_checks * sizeof(*pre_merge_hooks));
        memset(pre_merge_hooks, 0, num_checks * sizeof(*pre_merge_hooks));
}