#include "smatch.h"
#include "smatch_slist.h"
struct symbol *get_real_base_type(struct symbol *sym)
{
struct symbol *ret;
if (!sym)
return NULL;
if (sym->type == SYM_BASETYPE)
return sym;
ret = get_base_type(sym);
if (!ret)
return NULL;
if (ret->type == SYM_RESTRICT || ret->type == SYM_NODE)
return get_real_base_type(ret);
return ret;
}
int type_bytes(struct symbol *type)
{
int bits;
if (type && type->type == SYM_ARRAY)
return array_bytes(type);
bits = type_bits(type);
if (bits < 0)
return 0;
return bits_to_bytes(bits);
}
int array_bytes(struct symbol *type)
{
if (!type || type->type != SYM_ARRAY)
return 0;
return bits_to_bytes(type->bit_size);
}
static struct symbol *get_binop_type(struct expression *expr)
{
struct symbol *left, *right;
left = get_type(expr->left);
if (!left)
return NULL;
if (expr->op == SPECIAL_LEFTSHIFT ||
expr->op == SPECIAL_RIGHTSHIFT) {
if (type_positive_bits(left) < 31)
return &int_ctype;
return left;
}
right = get_type(expr->right);
if (!right)
return NULL;
if (type_is_fp(left)) {
if (type_is_fp(right)) {
if (type_bits(left) > type_bits(right))
return left;
return right;
}
return left;
}
if (type_is_fp(right)) {
if (type_is_fp(left)) {
if (type_bits(right) > type_bits(left))
return right;
return left;
}
return right;
}
if (expr->op == '-' &&
(is_ptr_type(left) && is_ptr_type(right)))
return ssize_t_ctype;
if (left->type == SYM_PTR || left->type == SYM_ARRAY)
return left;
if (right->type == SYM_PTR || right->type == SYM_ARRAY)
return right;
if (type_positive_bits(left) < 31 && type_positive_bits(right) < 31)
return &int_ctype;
if (type_positive_bits(left) > type_positive_bits(right))
return left;
return right;
}
static struct symbol *get_type_symbol(struct expression *expr)
{
if (!expr || expr->type != EXPR_SYMBOL || !expr->symbol)
return NULL;
return get_real_base_type(expr->symbol);
}
static struct symbol *get_member_symbol(struct symbol_list *symbol_list, struct ident *member)
{
struct symbol *tmp, *sub;
FOR_EACH_PTR(symbol_list, tmp) {
if (!tmp->ident) {
sub = get_real_base_type(tmp);
sub = get_member_symbol(sub->symbol_list, member);
if (sub)
return sub;
continue;
}
if (tmp->ident == member)
return tmp;
} END_FOR_EACH_PTR(tmp);
return NULL;
}
static struct symbol *get_symbol_from_deref(struct expression *expr)
{
struct ident *member;
struct symbol *sym;
if (!expr || expr->type != EXPR_DEREF)
return NULL;
member = expr->member;
sym = get_type(expr->deref);
if (!sym) {
return NULL;
}
if (sym->type == SYM_PTR)
sym = get_real_base_type(sym);
sym = get_member_symbol(sym->symbol_list, member);
if (!sym)
return NULL;
return get_real_base_type(sym);
}
static struct symbol *handle__builtin_choose_expr(struct expression *expr)
{
struct expression *const_expr, *expr1, *expr2;
sval_t sval;
const_expr = get_argument_from_call_expr(expr->args, 0);
expr1 = get_argument_from_call_expr(expr->args, 1);
expr2 = get_argument_from_call_expr(expr->args, 2);
if (!get_value(const_expr, &sval) || !expr1 || !expr2)
return NULL;
if (sval.value)
return get_type(expr1);
else
return get_type(expr2);
}
static struct symbol *get_return_type(struct expression *expr)
{
struct symbol *tmp;
if (sym_name_is("__builtin_choose_expr", expr->fn))
return handle__builtin_choose_expr(expr);
tmp = get_type(expr->fn);
if (!tmp)
return NULL;
if (tmp->type != SYM_FN)
tmp = get_base_type(tmp);
return get_real_base_type(tmp);
}
static struct symbol *get_expr_stmt_type(struct statement *stmt)
{
if (stmt->type != STMT_COMPOUND)
return NULL;
stmt = last_ptr_list((struct ptr_list *)stmt->stmts);
if (stmt->type == STMT_LABEL)
stmt = stmt->label_statement;
if (stmt->type != STMT_EXPRESSION)
return NULL;
return get_type(stmt->expression);
}
static struct symbol *get_select_type(struct expression *expr)
{
struct symbol *one, *two;
one = get_type(expr->cond_true);
two = get_type(expr->cond_false);
if (!one || !two)
return NULL;
if (type_positive_bits(one) > type_positive_bits(two))
return one;
return two;
}
struct symbol *get_pointer_type(struct expression *expr)
{
struct symbol *sym;
sym = get_type(expr);
if (!sym)
return NULL;
if (sym->type == SYM_NODE) {
sym = get_real_base_type(sym);
if (!sym)
return NULL;
}
if (sym->type != SYM_PTR && sym->type != SYM_ARRAY)
return NULL;
return get_real_base_type(sym);
}
static struct symbol *fake_pointer_sym(struct expression *expr)
{
struct symbol *sym;
struct symbol *base;
sym = alloc_symbol(expr->pos, SYM_PTR);
expr = expr->unop;
base = get_type(expr);
if (!base)
return NULL;
sym->ctype.base_type = base;
return sym;
}
static struct symbol *get_type_helper(struct expression *expr)
{
struct symbol *ret;
expr = strip_parens(expr);
if (!expr)
return NULL;
if (expr->ctype)
return expr->ctype;
switch (expr->type) {
case EXPR_STRING:
ret = &string_ctype;
break;
case EXPR_SYMBOL:
ret = get_type_symbol(expr);
break;
case EXPR_DEREF:
ret = get_symbol_from_deref(expr);
break;
case EXPR_PREOP:
case EXPR_POSTOP:
if (expr->op == '&')
ret = fake_pointer_sym(expr);
else if (expr->op == '*')
ret = get_pointer_type(expr->unop);
else
ret = get_type(expr->unop);
break;
case EXPR_ASSIGNMENT:
ret = get_type(expr->left);
break;
case EXPR_CAST:
case EXPR_FORCE_CAST:
case EXPR_IMPLIED_CAST:
ret = get_real_base_type(expr->cast_type);
break;
case EXPR_COMPARE:
case EXPR_BINOP:
ret = get_binop_type(expr);
break;
case EXPR_CALL:
ret = get_return_type(expr);
break;
case EXPR_STATEMENT:
ret = get_expr_stmt_type(expr->statement);
break;
case EXPR_CONDITIONAL:
case EXPR_SELECT:
ret = get_select_type(expr);
break;
case EXPR_SIZEOF:
ret = &ulong_ctype;
break;
case EXPR_LOGICAL:
ret = &int_ctype;
break;
case EXPR_OFFSETOF:
ret = &ulong_ctype;
break;
default:
return NULL;
}
if (ret && ret->type == SYM_TYPEOF)
ret = get_type(ret->initializer);
expr->ctype = ret;
return ret;
}
static struct symbol *get_final_type_helper(struct expression *expr)
{
expr = strip_parens(expr);
if (!expr)
return NULL;
if (expr->type == EXPR_COMPARE)
return &int_ctype;
return NULL;
}
struct symbol *get_type(struct expression *expr)
{
return get_type_helper(expr);
}
struct symbol *get_final_type(struct expression *expr)
{
struct symbol *ret;
ret = get_final_type_helper(expr);
if (ret)
return ret;
return get_type_helper(expr);
}
struct symbol *get_promoted_type(struct symbol *left, struct symbol *right)
{
struct symbol *ret = &int_ctype;
if (type_positive_bits(left) > type_positive_bits(ret))
ret = left;
if (type_positive_bits(right) > type_positive_bits(ret))
ret = right;
if (type_is_ptr(left))
ret = left;
if (type_is_ptr(right))
ret = right;
return ret;
}
int type_signed(struct symbol *base_type)
{
if (!base_type)
return 0;
if (base_type->ctype.modifiers & MOD_SIGNED)
return 1;
return 0;
}
int expr_unsigned(struct expression *expr)
{
struct symbol *sym;
sym = get_type(expr);
if (!sym)
return 0;
if (type_unsigned(sym))
return 1;
return 0;
}
int expr_signed(struct expression *expr)
{
struct symbol *sym;
sym = get_type(expr);
if (!sym)
return 0;
if (type_signed(sym))
return 1;
return 0;
}
int returns_unsigned(struct symbol *sym)
{
if (!sym)
return 0;
sym = get_base_type(sym);
if (!sym || sym->type != SYM_FN)
return 0;
sym = get_base_type(sym);
return type_unsigned(sym);
}
int is_pointer(struct expression *expr)
{
return type_is_ptr(get_type(expr));
}
int returns_pointer(struct symbol *sym)
{
if (!sym)
return 0;
sym = get_base_type(sym);
if (!sym || sym->type != SYM_FN)
return 0;
sym = get_base_type(sym);
if (sym && sym->type == SYM_PTR)
return 1;
return 0;
}
static sval_t fp_max(struct symbol *type)
{
sval_t ret = { .type = type };
if (type == &float_ctype)
ret.fvalue = FLT_MAX;
else if (type == &double_ctype)
ret.dvalue = DBL_MAX;
else
ret.ldvalue = LDBL_MAX;
return ret;
}
sval_t sval_type_max(struct symbol *base_type)
{
sval_t ret;
if (type_is_fp(base_type))
return fp_max(base_type);
if (!base_type || !type_bits(base_type))
base_type = &llong_ctype;
ret.type = base_type;
ret.value = (~0ULL) >> (64 - type_positive_bits(base_type));
return ret;
}
static sval_t fp_min(struct symbol *type)
{
sval_t ret = { .type = type };
if (type == &float_ctype)
ret.fvalue = -FLT_MAX;
else if (type == &double_ctype)
ret.dvalue = -DBL_MAX;
else
ret.ldvalue = -LDBL_MAX;
return ret;
}
sval_t sval_type_min(struct symbol *base_type)
{
sval_t ret;
if (type_is_fp(base_type))
return fp_min(base_type);
if (!base_type || !type_bits(base_type))
base_type = &llong_ctype;
ret.type = base_type;
if (type_unsigned(base_type) || is_ptr_type(base_type)) {
ret.value = 0;
return ret;
}
ret.value = (~0ULL) << type_positive_bits(base_type);
return ret;
}
int nr_bits(struct expression *expr)
{
struct symbol *type;
type = get_type(expr);
if (!type)
return 0;
return type_bits(type);
}
int is_void_pointer(struct expression *expr)
{
struct symbol *type;
type = get_type(expr);
if (!type || type->type != SYM_PTR)
return 0;
type = get_real_base_type(type);
if (type == &void_ctype)
return 1;
return 0;
}
int is_char_pointer(struct expression *expr)
{
struct symbol *type;
type = get_type(expr);
if (!type || type->type != SYM_PTR)
return 0;
type = get_real_base_type(type);
if (type == &char_ctype)
return 1;
return 0;
}
int is_string(struct expression *expr)
{
expr = strip_expr(expr);
if (!expr || expr->type != EXPR_STRING)
return 0;
if (expr->string)
return 1;
return 0;
}
bool is_struct_ptr(struct symbol *type)
{
if (!type || type->type != SYM_PTR)
return false;
type = get_real_base_type(type);
if (!type || type->type != SYM_STRUCT)
return false;
return true;
}
int is_static(struct expression *expr)
{
char *name;
struct symbol *sym;
int ret = 0;
name = expr_to_str_sym(expr, &sym);
if (!name || !sym)
goto free;
if (sym->ctype.modifiers & MOD_STATIC)
ret = 1;
free:
free_string(name);
return ret;
}
bool is_local_variable(struct expression *expr)
{
struct symbol *sym;
if (!expr || expr->type != EXPR_SYMBOL || !expr->symbol)
return false;
sym = expr->symbol;
if (!(sym->ctype.modifiers & MOD_TOPLEVEL))
return true;
return false;
}
int types_equiv(struct symbol *one, struct symbol *two)
{
if (!one && !two)
return 1;
if (!one || !two)
return 0;
if (one->type != two->type)
return 0;
if (one->type == SYM_PTR)
return types_equiv(get_real_base_type(one), get_real_base_type(two));
if (type_positive_bits(one) != type_positive_bits(two))
return 0;
return 1;
}
bool type_fits(struct symbol *type, struct symbol *test)
{
if (!type || !test)
return false;
if (type == test)
return true;
if (type_bits(test) > type_bits(type))
return false;
if (type_signed(test) && !type_signed(type))
return false;
if (type_positive_bits(test) > type_positive_bits(type))
return false;
return true;
}
int fn_static(void)
{
return !!(cur_func_sym->ctype.modifiers & MOD_STATIC);
}
const char *global_static(void)
{
if (cur_func_sym->ctype.modifiers & MOD_STATIC)
return "static";
else
return "global";
}
struct symbol *cur_func_return_type(void)
{
struct symbol *sym;
sym = get_real_base_type(cur_func_sym);
if (!sym || sym->type != SYM_FN)
return NULL;
sym = get_real_base_type(sym);
return sym;
}
struct symbol *get_arg_type(struct expression *fn, int arg)
{
struct symbol *fn_type;
struct symbol *tmp;
struct symbol *arg_type;
int i;
fn_type = get_type(fn);
if (!fn_type)
return NULL;
if (fn_type->type == SYM_PTR)
fn_type = get_real_base_type(fn_type);
if (fn_type->type != SYM_FN)
return NULL;
i = 0;
FOR_EACH_PTR(fn_type->arguments, tmp) {
arg_type = get_real_base_type(tmp);
if (i == arg) {
return arg_type;
}
i++;
} END_FOR_EACH_PTR(tmp);
return NULL;
}
static struct symbol *get_member_from_string(struct symbol_list *symbol_list, const char *name)
{
struct symbol *tmp, *sub;
int chunk_len;
if (strncmp(name, ".", 1) == 0)
name += 1;
else if (strncmp(name, "->", 2) == 0)
name += 2;
FOR_EACH_PTR(symbol_list, tmp) {
if (!tmp->ident) {
sub = get_real_base_type(tmp);
sub = get_member_from_string(sub->symbol_list, name);
if (sub)
return sub;
continue;
}
if (strcmp(tmp->ident->name, name) == 0)
return tmp;
chunk_len = tmp->ident->len;
if (strncmp(tmp->ident->name, name, chunk_len) == 0 &&
(name[chunk_len] == '.' || name[chunk_len] == '-')) {
sub = get_real_base_type(tmp);
if (sub->type == SYM_PTR)
sub = get_real_base_type(sub);
return get_member_from_string(sub->symbol_list, name + chunk_len);
}
} END_FOR_EACH_PTR(tmp);
return NULL;
}
struct symbol *get_member_type_from_key(struct expression *expr, const char *key)
{
struct symbol *sym;
int star = 0;
int i;
if (strcmp(key, "$") == 0)
return get_type(expr);
if (strcmp(key, "*$") == 0) {
sym = get_type(expr);
if (!sym || sym->type != SYM_PTR)
return NULL;
return get_real_base_type(sym);
}
sym = get_type(expr);
if (!sym)
return NULL;
if (sym->type == SYM_PTR)
sym = get_real_base_type(sym);
while (*key == '*') {
key++;
star++;
}
if (*key != '$')
return NULL;
key++;
sym = get_member_from_string(sym->symbol_list, key);
if (!sym)
return NULL;
if (sym->type == SYM_RESTRICT || sym->type == SYM_NODE)
sym = get_real_base_type(sym);
for (i = 0; i < star; i++) {
if (!sym || sym->type != SYM_PTR)
return NULL;
sym = get_real_base_type(sym);
}
return sym;
}
struct symbol *get_arg_type_from_key(struct expression *fn, int param, struct expression *arg, const char *key)
{
struct symbol *type;
if (!key)
return NULL;
if (strcmp(key, "$") == 0)
return get_arg_type(fn, param);
if (strcmp(key, "*$") == 0) {
type = get_arg_type(fn, param);
if (!type || type->type != SYM_PTR)
return NULL;
return get_real_base_type(type);
}
return get_member_type_from_key(arg, key);
}
int is_struct(struct expression *expr)
{
struct symbol *type;
type = get_type(expr);
if (type && type->type == SYM_STRUCT)
return 1;
return 0;
}
static struct {
struct symbol *sym;
const char *name;
} base_types[] = {
{&bool_ctype, "bool"},
{&void_ctype, "void"},
{&type_ctype, "type"},
{&char_ctype, "char"},
{&schar_ctype, "schar"},
{&uchar_ctype, "uchar"},
{&short_ctype, "short"},
{&sshort_ctype, "sshort"},
{&ushort_ctype, "ushort"},
{&int_ctype, "int"},
{&sint_ctype, "sint"},
{&uint_ctype, "uint"},
{&long_ctype, "long"},
{&slong_ctype, "slong"},
{&ulong_ctype, "ulong"},
{&llong_ctype, "llong"},
{&sllong_ctype, "sllong"},
{&ullong_ctype, "ullong"},
{&lllong_ctype, "lllong"},
{&slllong_ctype, "slllong"},
{&ulllong_ctype, "ulllong"},
{&float_ctype, "float"},
{&double_ctype, "double"},
{&ldouble_ctype, "ldouble"},
{&string_ctype, "string"},
{&ptr_ctype, "ptr"},
{&lazy_ptr_ctype, "lazy_ptr"},
{&incomplete_ctype, "incomplete"},
{&label_ctype, "label"},
{&bad_ctype, "bad"},
{&null_ctype, "null"},
};
static const char *base_type_str(struct symbol *sym)
{
int i;
for (i = 0; i < ARRAY_SIZE(base_types); i++) {
if (sym == base_types[i].sym)
return base_types[i].name;
}
return "<unknown>";
}
static int type_str_helper(char *buf, int size, struct symbol *type)
{
int n;
if (!type)
return snprintf(buf, size, "<null type>");
if (type->type == SYM_BASETYPE) {
return snprintf(buf, size, "%s", base_type_str(type));
} else if (type->type == SYM_PTR) {
type = get_real_base_type(type);
n = type_str_helper(buf, size, type);
if (n > size)
return n;
return n + snprintf(buf + n, size - n, "*");
} else if (type->type == SYM_ARRAY) {
type = get_real_base_type(type);
n = type_str_helper(buf, size, type);
if (n > size)
return n;
return n + snprintf(buf + n, size - n, "[]");
} else if (type->type == SYM_STRUCT) {
return snprintf(buf, size, "struct %s", type->ident ? type->ident->name : "");
} else if (type->type == SYM_UNION) {
if (type->ident)
return snprintf(buf, size, "union %s", type->ident->name);
else
return snprintf(buf, size, "anonymous union");
} else if (type->type == SYM_FN) {
struct symbol *arg, *return_type, *arg_type;
int i;
return_type = get_real_base_type(type);
n = type_str_helper(buf, size, return_type);
if (n > size)
return n;
n += snprintf(buf + n, size - n, "(*)(");
if (n > size)
return n;
i = 0;
FOR_EACH_PTR(type->arguments, arg) {
if (i++)
n += snprintf(buf + n, size - n, ", ");
if (n > size)
return n;
arg_type = get_real_base_type(arg);
n += type_str_helper(buf + n, size - n, arg_type);
if (n > size)
return n;
} END_FOR_EACH_PTR(arg);
return n + snprintf(buf + n, size - n, ")");
} else if (type->type == SYM_NODE) {
n = snprintf(buf, size, "node {");
if (n > size)
return n;
type = get_real_base_type(type);
n += type_str_helper(buf + n, size - n, type);
if (n > size)
return n;
return n + snprintf(buf + n, size - n, "}");
} else if (type->type == SYM_ENUM) {
return snprintf(buf, size, "enum %s", type->ident ? type->ident->name : "<unknown>");
} else {
return snprintf(buf, size, "<type %d>", type->type);
}
}
char *type_to_str(struct symbol *type)
{
static char buf[256];
buf[0] = '\0';
type_str_helper(buf, sizeof(buf), type);
return buf;
}