drgn/libdrgn/type.c
Omar Sandoval dcddaa2cc1 libdrgn: revamp hash table API
This makes several improvements to the hash table API.

The first two changes make things more general in order to be consistent
with the upcoming binary search tree API:

- Items are renamed to entries.
- Positions are renamed to iterators.
- hash_table_empty() is added.

One change makes the definition API more convenient:

- It is no longer necessary to pass the types into
  DEFINE_HASH_{MAP,SET}_FUNCTIONS().

A few changes take some good ideas from the C++ STL:

- hash_table_insert() now fails on duplicates instead of overwriting.
- hash_table_delete_iterator() returns the next iterator.
- hash_table_next() returns an iterator instead of modifying it.

One change reduces memory usage:

- The lower-level DEFINE_HASH_TABLE() is cleaned up and exposed as an
  alternative to DEFINE_HASH_MAP() and DEFINE_HASH_SET(). This allows us
  to get rid of the duplicated key where a hash map value already embeds
  the key (the DWARF index file table) and gets rid of the need to make
  a dummy hash set entry to do a search (the pointer and array type
  caches).
2019-05-24 17:48:05 -07:00

882 lines
25 KiB
C

#include <string.h>
#include "internal.h"
#include "hash_table.h"
#include "language.h"
#include "type.h"
#include "type_index.h"
const char * const drgn_type_kind_spelling[] = {
[DRGN_TYPE_VOID] = "void",
[DRGN_TYPE_INT] = "int",
[DRGN_TYPE_BOOL] = "bool",
[DRGN_TYPE_FLOAT] = "float",
[DRGN_TYPE_COMPLEX] = "complex",
[DRGN_TYPE_STRUCT] = "struct",
[DRGN_TYPE_UNION] = "union",
[DRGN_TYPE_ENUM] = "enum",
[DRGN_TYPE_TYPEDEF] = "typedef",
[DRGN_TYPE_POINTER] = "pointer",
[DRGN_TYPE_ARRAY] = "array",
[DRGN_TYPE_FUNCTION] = "function",
};
const char * const * const
drgn_primitive_type_spellings[DRGN_PRIMITIVE_TYPE_NUM] = {
[DRGN_C_TYPE_VOID] = (const char * []){ "void", NULL, },
[DRGN_C_TYPE_CHAR] = (const char * []){ "char", NULL, },
[DRGN_C_TYPE_SIGNED_CHAR] = (const char * []){
"signed char", "char signed", NULL,
},
[DRGN_C_TYPE_UNSIGNED_CHAR] = (const char * []){
"unsigned char", "char unsigned", NULL,
},
[DRGN_C_TYPE_SHORT] = (const char * []){
"short", "signed short", "short signed", "short int",
"int short", "signed short int", "signed int short",
"short signed int", "short int signed", "int signed short",
"int short signed", NULL,
},
[DRGN_C_TYPE_UNSIGNED_SHORT] = (const char * []){
"unsigned short", "short unsigned", "unsigned short int",
"unsigned int short", "short unsigned int",
"short int unsigned", "int unsigned short",
"int short unsigned", NULL,
},
[DRGN_C_TYPE_INT] = (const char * []){
"int", "signed", "signed int", "int signed", NULL,
},
[DRGN_C_TYPE_UNSIGNED_INT] = (const char * []){
"unsigned int", "int unsigned", "unsigned", NULL,
},
[DRGN_C_TYPE_LONG] = (const char * []){
"long", "signed long", "long signed", "long int", "int long",
"signed long int", "signed int long", "long signed int",
"long int signed", "int signed long", "int long signed", NULL,
},
[DRGN_C_TYPE_UNSIGNED_LONG] = (const char * []){
"unsigned long", "long unsigned", "unsigned long int",
"unsigned int long", "long unsigned int", "long int unsigned",
"int unsigned long", "int long unsigned", NULL,
},
[DRGN_C_TYPE_LONG_LONG] = (const char * []){
"long long", "signed long long", "long signed long",
"long long signed", "long long int", "long int long",
"int long long", "signed long long int", "signed long int long",
"signed int long long", "long signed long int",
"long signed int long", "long long signed int",
"long long int signed", "long int signed long",
"long int long signed", "int signed long long",
"int long signed long", "int long long signed", NULL,
},
[DRGN_C_TYPE_UNSIGNED_LONG_LONG] = (const char * []){
"unsigned long long", "long unsigned long",
"long long unsigned", "unsigned long long int",
"unsigned long int long", "unsigned int long long",
"long unsigned long int", "long unsigned int long",
"long long unsigned int", "long long int unsigned",
"long int unsigned long", "long int long unsigned",
"int unsigned long long", "int long unsigned long",
"int long long unsigned", NULL,
},
[DRGN_C_TYPE_BOOL] = (const char * []){ "_Bool", NULL, },
[DRGN_C_TYPE_FLOAT] = (const char * []){ "float", NULL, },
[DRGN_C_TYPE_DOUBLE] = (const char * []){ "double", NULL, },
[DRGN_C_TYPE_LONG_DOUBLE] = (const char * []){
"long double", "double long", NULL,
},
[DRGN_C_TYPE_SIZE_T] = (const char * []){ "size_t", NULL, },
[DRGN_C_TYPE_PTRDIFF_T] = (const char * []){ "ptrdiff_t", NULL, },
};
const enum drgn_type_kind
drgn_primitive_type_kind[DRGN_PRIMITIVE_TYPE_NUM + 1] = {
[DRGN_C_TYPE_CHAR] = DRGN_TYPE_INT,
[DRGN_C_TYPE_SIGNED_CHAR] = DRGN_TYPE_INT,
[DRGN_C_TYPE_UNSIGNED_CHAR] = DRGN_TYPE_INT,
[DRGN_C_TYPE_SHORT] = DRGN_TYPE_INT,
[DRGN_C_TYPE_UNSIGNED_SHORT] = DRGN_TYPE_INT,
[DRGN_C_TYPE_INT] = DRGN_TYPE_INT,
[DRGN_C_TYPE_UNSIGNED_INT] = DRGN_TYPE_INT,
[DRGN_C_TYPE_LONG] = DRGN_TYPE_INT,
[DRGN_C_TYPE_UNSIGNED_LONG] = DRGN_TYPE_INT,
[DRGN_C_TYPE_LONG_LONG] = DRGN_TYPE_INT,
[DRGN_C_TYPE_UNSIGNED_LONG_LONG] = DRGN_TYPE_INT,
[DRGN_C_TYPE_BOOL] = DRGN_TYPE_BOOL,
[DRGN_C_TYPE_FLOAT] = DRGN_TYPE_FLOAT,
[DRGN_C_TYPE_DOUBLE] = DRGN_TYPE_FLOAT,
[DRGN_C_TYPE_LONG_DOUBLE] = DRGN_TYPE_FLOAT,
[DRGN_C_TYPE_SIZE_T] = DRGN_TYPE_TYPEDEF,
[DRGN_C_TYPE_PTRDIFF_T] = DRGN_TYPE_TYPEDEF,
[DRGN_C_TYPE_VOID] = DRGN_TYPE_VOID,
[DRGN_NOT_PRIMITIVE_TYPE] = -1,
};
/** Return whether a primitive type is always a signed integer type. */
static inline bool
drgn_primitive_type_is_signed(enum drgn_primitive_type primitive)
{
switch (primitive) {
case DRGN_C_TYPE_SIGNED_CHAR:
case DRGN_C_TYPE_SHORT:
case DRGN_C_TYPE_INT:
case DRGN_C_TYPE_LONG:
case DRGN_C_TYPE_LONG_LONG:
case DRGN_C_TYPE_PTRDIFF_T:
return true;
default:
return false;
}
}
void drgn_type_thunk_free(struct drgn_type_thunk *thunk)
{
thunk->free_fn(thunk);
}
struct drgn_error *drgn_lazy_type_evaluate(struct drgn_lazy_type *lazy_type,
struct drgn_qualified_type *qualified_type)
{
if (drgn_lazy_type_is_evaluated(lazy_type)) {
qualified_type->type = lazy_type->type;
qualified_type->qualifiers = lazy_type->qualifiers;
} else {
struct drgn_error *err;
struct drgn_type_thunk *thunk_ptr = lazy_type->thunk;
struct drgn_type_thunk thunk = *thunk_ptr;
err = thunk.evaluate_fn(thunk_ptr, qualified_type);
if (err)
return err;
drgn_lazy_type_init_evaluated(lazy_type, qualified_type->type,
qualified_type->qualifiers);
thunk.free_fn(thunk_ptr);
}
return NULL;
}
void drgn_lazy_type_deinit(struct drgn_lazy_type *lazy_type)
{
if (!drgn_lazy_type_is_evaluated(lazy_type))
drgn_type_thunk_free(lazy_type->thunk);
}
LIBDRGN_PUBLIC struct drgn_error *
drgn_member_type(struct drgn_type_member *member,
struct drgn_qualified_type *ret)
{
return drgn_lazy_type_evaluate(&member->type, ret);
}
LIBDRGN_PUBLIC struct drgn_error *
drgn_parameter_type(struct drgn_type_parameter *parameter,
struct drgn_qualified_type *ret)
{
return drgn_lazy_type_evaluate(&parameter->type, ret);
}
LIBDRGN_PUBLIC struct drgn_type drgn_void_type = {
{ .kind = DRGN_TYPE_VOID, .primitive = DRGN_C_TYPE_VOID, },
};
void drgn_int_type_init(struct drgn_type *type, const char *name, uint64_t size,
bool is_signed)
{
enum drgn_primitive_type primitive;
assert(name);
type->_private.kind = DRGN_TYPE_INT;
type->_private.is_complete = true;
primitive = c_parse_specifier_list(name);
if (drgn_primitive_type_kind[primitive] == DRGN_TYPE_INT &&
(primitive == DRGN_C_TYPE_CHAR ||
is_signed == drgn_primitive_type_is_signed(primitive))) {
type->_private.primitive = primitive;
type->_private.name =
drgn_primitive_type_spellings[primitive][0];
} else {
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.name = name;
}
type->_private.size = size;
type->_private.is_signed = is_signed;
}
void drgn_bool_type_init(struct drgn_type *type, const char *name,
uint64_t size)
{
assert(name);
type->_private.kind = DRGN_TYPE_BOOL;
type->_private.is_complete = true;
if (c_parse_specifier_list(name) == DRGN_C_TYPE_BOOL) {
type->_private.primitive = DRGN_C_TYPE_BOOL;
type->_private.name =
drgn_primitive_type_spellings[DRGN_C_TYPE_BOOL][0];
} else {
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.name = name;
}
type->_private.size = size;
}
void drgn_float_type_init(struct drgn_type *type, const char *name,
uint64_t size)
{
enum drgn_primitive_type primitive;
assert(name);
type->_private.kind = DRGN_TYPE_FLOAT;
type->_private.is_complete = true;
primitive = c_parse_specifier_list(name);
if (drgn_primitive_type_kind[primitive] == DRGN_TYPE_FLOAT) {
type->_private.primitive = primitive;
type->_private.name =
drgn_primitive_type_spellings[primitive][0];
} else {
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.name = name;
}
type->_private.size = size;
}
void drgn_complex_type_init(struct drgn_type *type, const char *name,
uint64_t size, struct drgn_type *real_type)
{
assert(name);
assert(real_type);
assert(drgn_type_kind(real_type) == DRGN_TYPE_FLOAT ||
drgn_type_kind(real_type) == DRGN_TYPE_INT);
type->_private.kind = DRGN_TYPE_COMPLEX;
type->_private.is_complete = true;
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.name = name;
type->_private.size = size;
type->_private.type = real_type;
type->_private.qualifiers = 0;
}
void drgn_struct_type_init(struct drgn_type *type, const char *tag,
uint64_t size, size_t num_members)
{
type->_private.kind = DRGN_TYPE_STRUCT;
type->_private.is_complete = true;
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.tag = tag;
type->_private.size = size;
type->_private.num_members = num_members;
}
void drgn_struct_type_init_incomplete(struct drgn_type *type, const char *tag)
{
type->_private.kind = DRGN_TYPE_STRUCT;
type->_private.is_complete = false;
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.tag = tag;
type->_private.size = 0;
type->_private.num_members = 0;
}
void drgn_union_type_init(struct drgn_type *type, const char *tag,
uint64_t size, size_t num_members)
{
type->_private.kind = DRGN_TYPE_UNION;
type->_private.is_complete = true;
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.tag = tag;
type->_private.size = size;
type->_private.num_members = num_members;
}
void drgn_union_type_init_incomplete(struct drgn_type *type, const char *tag)
{
type->_private.kind = DRGN_TYPE_UNION;
type->_private.is_complete = false;
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.tag = tag;
type->_private.size = 0;
type->_private.num_members = 0;
}
void drgn_enum_type_init(struct drgn_type *type, const char *tag,
struct drgn_type *compatible_type,
size_t num_enumerators)
{
assert(drgn_type_kind(compatible_type) == DRGN_TYPE_INT);
type->_private.kind = DRGN_TYPE_ENUM;
type->_private.is_complete = true;
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.tag = tag;
type->_private.type = compatible_type;
type->_private.qualifiers = 0;
type->_private.num_enumerators = num_enumerators;
}
void drgn_enum_type_init_incomplete(struct drgn_type *type, const char *tag)
{
type->_private.kind = DRGN_TYPE_ENUM;
type->_private.is_complete = false;
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.tag = tag;
type->_private.type = NULL;
type->_private.qualifiers = 0;
type->_private.num_enumerators = 0;
}
void drgn_typedef_type_init(struct drgn_type *type, const char *name,
struct drgn_qualified_type aliased_type)
{
type->_private.kind = DRGN_TYPE_TYPEDEF;
type->_private.is_complete = drgn_type_is_complete(aliased_type.type);
if (strcmp(name, "size_t") == 0)
type->_private.primitive = DRGN_C_TYPE_SIZE_T;
else if (strcmp(name, "ptrdiff_t") == 0)
type->_private.primitive = DRGN_C_TYPE_PTRDIFF_T;
else
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.name = name;
type->_private.type = aliased_type.type;
type->_private.qualifiers = aliased_type.qualifiers;
}
void drgn_pointer_type_init(struct drgn_type *type, uint64_t size,
struct drgn_qualified_type referenced_type)
{
type->_private.kind = DRGN_TYPE_POINTER;
type->_private.is_complete = true;
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.size = size;
type->_private.type = referenced_type.type;
type->_private.qualifiers = referenced_type.qualifiers;
}
void drgn_array_type_init(struct drgn_type *type, uint64_t length,
struct drgn_qualified_type element_type)
{
type->_private.kind = DRGN_TYPE_ARRAY;
type->_private.is_complete = true;
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.length = length;
type->_private.type = element_type.type;
type->_private.qualifiers = element_type.qualifiers;
}
void drgn_array_type_init_incomplete(struct drgn_type *type,
struct drgn_qualified_type element_type)
{
type->_private.kind = DRGN_TYPE_ARRAY;
type->_private.is_complete = false;
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.length = 0;
type->_private.type = element_type.type;
type->_private.qualifiers = element_type.qualifiers;
}
void drgn_function_type_init(struct drgn_type *type,
struct drgn_qualified_type return_type,
size_t num_parameters, bool is_variadic)
{
type->_private.kind = DRGN_TYPE_FUNCTION;
type->_private.is_complete = true;
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.type = return_type.type;
type->_private.qualifiers = return_type.qualifiers;
type->_private.num_parameters = num_parameters;
type->_private.is_variadic = is_variadic;
}
struct drgn_type_pair {
struct drgn_type *a;
struct drgn_type *b;
};
static struct hash_pair
hash_pair_drgn_type_pair(const struct drgn_type_pair *pair)
{
return hash_pair_from_avalanching_hash(hash_combine((uintptr_t)pair->a,
(uintptr_t)pair->b));
}
static bool drgn_type_pair_eq(const struct drgn_type_pair *a,
const struct drgn_type_pair *b)
{
return a->a == b->a && a->b == b->b;
}
DEFINE_HASH_SET(drgn_type_pair_set, struct drgn_type_pair,
hash_pair_drgn_type_pair, drgn_type_pair_eq)
static struct drgn_error *drgn_type_eq_impl(struct drgn_type *a,
struct drgn_type *b,
struct drgn_type_pair_set *set,
bool *ret);
static struct drgn_error *drgn_qualified_type_eq_impl(struct drgn_qualified_type *a,
struct drgn_qualified_type *b,
struct drgn_type_pair_set *set,
bool *ret)
{
if (a->qualifiers != b->qualifiers) {
*ret = false;
return NULL;
}
return drgn_type_eq_impl(a->type, b->type, set, ret);
}
static struct drgn_error *drgn_type_members_eq(struct drgn_type *a,
struct drgn_type *b,
struct drgn_type_pair_set *set,
bool *ret)
{
struct drgn_type_member *members_a, *members_b;
size_t num_a, num_b, i;
num_a = drgn_type_num_members(a);
num_b = drgn_type_num_members(b);
if (num_a != num_b)
goto out_false;
members_a = drgn_type_members(a);
members_b = drgn_type_members(b);
for (i = 0; i < num_a; i++) {
struct drgn_qualified_type type_a, type_b;
struct drgn_error *err;
if (members_a[i].bit_offset != members_b[i].bit_offset ||
members_a[i].bit_field_size != members_b[i].bit_field_size)
goto out_false;
if (!members_a[i].name && !members_b[i].name)
continue;
if (!members_a[i].name || !members_b[i].name ||
strcmp(members_a[i].name, members_b[i].name) != 0)
goto out_false;
err = drgn_member_type(&members_a[i], &type_a);
if (err)
return err;
err = drgn_member_type(&members_b[i], &type_b);
if (err)
return err;
err = drgn_qualified_type_eq_impl(&type_a, &type_b, set, ret);
if (err)
return err;
if (!*ret)
return NULL;
}
*ret = true;
return NULL;
out_false:
*ret = false;
return NULL;
}
static bool drgn_type_enumerators_eq(struct drgn_type *a, struct drgn_type *b)
{
const struct drgn_type_enumerator *enumerators_a, *enumerators_b;
size_t num_a, num_b, i;
num_a = drgn_type_num_enumerators(a);
num_b = drgn_type_num_enumerators(b);
if (num_a != num_b)
return false;
enumerators_a = drgn_type_enumerators(a);
enumerators_b = drgn_type_enumerators(b);
for (i = 0; i < num_a; i++) {
if (strcmp(enumerators_a[i].name, enumerators_b[i].name) != 0)
return false;
if (enumerators_a[i].uvalue != enumerators_b[i].uvalue)
return false;
}
return true;
}
static struct drgn_error *drgn_type_parameters_eq(struct drgn_type *a,
struct drgn_type *b,
struct drgn_type_pair_set *set,
bool *ret)
{
struct drgn_type_parameter *parameters_a, *parameters_b;
size_t num_a, num_b, i;
num_a = drgn_type_num_parameters(a);
num_b = drgn_type_num_parameters(b);
if (num_a != num_b)
goto out_false;
parameters_a = drgn_type_parameters(a);
parameters_b = drgn_type_parameters(b);
for (i = 0; i < num_a; i++) {
struct drgn_qualified_type type_a, type_b;
struct drgn_error *err;
if (!parameters_a[i].name && !parameters_b[i].name)
continue;
if (!parameters_a[i].name || !parameters_b[i].name ||
strcmp(parameters_a[i].name, parameters_b[i].name) != 0)
goto out_false;
err = drgn_parameter_type(&parameters_a[i], &type_a);
if (err)
return err;
err = drgn_parameter_type(&parameters_b[i], &type_b);
if (err)
return err;
err = drgn_qualified_type_eq_impl(&type_a, &type_b, set, ret);
if (err)
return err;
if (!*ret)
return NULL;
}
*ret = true;
return NULL;
out_false:
*ret = false;
return NULL;
}
static struct drgn_error *drgn_type_eq_impl(struct drgn_type *a,
struct drgn_type *b,
struct drgn_type_pair_set *set,
bool *ret)
{
struct drgn_type_pair pair = { a, b };
struct drgn_error *err;
struct hash_pair hp;
if (drgn_type_pair_set_size(set) >= 1000) {
return drgn_error_create(DRGN_ERROR_RECURSION,
"maximum type comparison depth exceeded");
}
/*
* Handle cycles by assuming that types which we are already comparing
* are equal.
*/
hp = drgn_type_pair_set_hash(&pair);
switch (drgn_type_pair_set_insert_hashed(set, &pair, hp, NULL)) {
case 1:
break;
case 0:
*ret = true;
return NULL;
case -1:
return &drgn_enomem;
}
if (a == b)
goto out_true;
if (!a || !b)
goto out_false;
if (drgn_type_kind(a) != drgn_type_kind(b) ||
drgn_type_is_complete(a) != drgn_type_is_complete(b))
goto out_false;
if (drgn_type_has_name(a) &&
strcmp(drgn_type_name(a), drgn_type_name(b)) != 0)
goto out_false;
if (drgn_type_has_tag(a)) {
const char *tag_a, *tag_b;
tag_a = drgn_type_tag(a);
tag_b = drgn_type_tag(b);
if ((!tag_a != !tag_b) || (tag_a && strcmp(tag_a, tag_b) != 0))
goto out_false;
}
if (drgn_type_has_size(a) && drgn_type_size(a) != drgn_type_size(b))
goto out_false;
if (drgn_type_has_length(a) &&
drgn_type_length(a) != drgn_type_length(b))
goto out_false;
if (drgn_type_has_is_signed(a) &&
drgn_type_is_signed(a) != drgn_type_is_signed(b))
goto out_false;
if (drgn_type_has_type(a)) {
struct drgn_qualified_type type_a, type_b;
type_a = drgn_type_type(a);
type_b = drgn_type_type(b);
err = drgn_qualified_type_eq_impl(&type_a, &type_b, set, ret);
if (err || !*ret)
goto out;
}
if (drgn_type_has_members(a)) {
err = drgn_type_members_eq(a, b, set, ret);
if (err || !*ret)
goto out;
}
if (drgn_type_has_enumerators(a) && !drgn_type_enumerators_eq(a, b))
goto out_false;
if (drgn_type_has_parameters(a)) {
err = drgn_type_parameters_eq(a, b, set, ret);
if (err || !*ret)
goto out;
}
if (drgn_type_has_is_variadic(a) &&
drgn_type_is_variadic(a) != drgn_type_is_variadic(b))
goto out_false;
out_true:
*ret = true;
err = NULL;
goto out;
out_false:
*ret = false;
err = NULL;
out:
drgn_type_pair_set_delete_hashed(set, &pair, hp);
return err;
}
LIBDRGN_PUBLIC struct drgn_error *drgn_type_eq(struct drgn_type *a,
struct drgn_type *b, bool *ret)
{
struct drgn_type_pair_set set;
struct drgn_error *err;
drgn_type_pair_set_init(&set);
err = drgn_type_eq_impl(a, b, &set, ret);
drgn_type_pair_set_deinit(&set);
return err;
}
LIBDRGN_PUBLIC struct drgn_error *
drgn_qualified_type_eq(struct drgn_qualified_type a,
struct drgn_qualified_type b, bool *ret)
{
if (a.qualifiers != b.qualifiers) {
*ret = false;
return NULL;
}
return drgn_type_eq(a.type, b.type, ret);
}
LIBDRGN_PUBLIC struct drgn_error *
drgn_pretty_print_type_name(struct drgn_qualified_type qualified_type,
char **ret)
{
return drgn_language_c.pretty_print_type_name(qualified_type, ret);
}
LIBDRGN_PUBLIC struct drgn_error *
drgn_pretty_print_type(struct drgn_qualified_type qualified_type, char **ret)
{
return drgn_language_c.pretty_print_type(qualified_type, ret);
}
bool drgn_type_is_integer(struct drgn_type *type)
{
switch (drgn_type_kind(type)) {
case DRGN_TYPE_INT:
case DRGN_TYPE_BOOL:
case DRGN_TYPE_ENUM:
return true;
case DRGN_TYPE_TYPEDEF:
return drgn_type_is_integer(drgn_type_type(type).type);
default:
return false;
}
}
bool drgn_type_is_arithmetic(struct drgn_type *type)
{
switch (drgn_type_kind(type)) {
case DRGN_TYPE_INT:
case DRGN_TYPE_BOOL:
case DRGN_TYPE_FLOAT:
case DRGN_TYPE_ENUM:
return true;
case DRGN_TYPE_TYPEDEF:
return drgn_type_is_arithmetic(drgn_type_type(type).type);
default:
return false;
}
}
bool drgn_type_is_scalar(struct drgn_type *type)
{
switch (drgn_type_kind(type)) {
case DRGN_TYPE_INT:
case DRGN_TYPE_BOOL:
case DRGN_TYPE_FLOAT:
case DRGN_TYPE_ENUM:
case DRGN_TYPE_POINTER:
return true;
case DRGN_TYPE_TYPEDEF:
return drgn_type_is_scalar(drgn_type_type(type).type);
default:
return false;
}
}
LIBDRGN_PUBLIC struct drgn_error *drgn_type_sizeof(struct drgn_type *type,
uint64_t *ret)
{
struct drgn_error *err;
switch (drgn_type_kind(type)) {
case DRGN_TYPE_INT:
case DRGN_TYPE_BOOL:
case DRGN_TYPE_FLOAT:
case DRGN_TYPE_COMPLEX:
case DRGN_TYPE_POINTER:
*ret = drgn_type_size(type);
return NULL;
case DRGN_TYPE_STRUCT:
if (!drgn_type_is_complete(type)) {
return drgn_error_create(DRGN_ERROR_TYPE,
"cannot get size of incomplete structure type");
}
*ret = drgn_type_size(type);
return NULL;
case DRGN_TYPE_UNION:
if (!drgn_type_is_complete(type)) {
return drgn_error_create(DRGN_ERROR_TYPE,
"cannot get size of incomplete union type");
}
*ret = drgn_type_size(type);
return NULL;
case DRGN_TYPE_ENUM:
if (!drgn_type_is_complete(type)) {
return drgn_error_create(DRGN_ERROR_TYPE,
"cannot get size of incomplete enumerated type");
}
/* fallthrough */
case DRGN_TYPE_TYPEDEF:
return drgn_type_sizeof(drgn_type_type(type).type, ret);
case DRGN_TYPE_ARRAY:
if (!drgn_type_is_complete(type)) {
return drgn_error_create(DRGN_ERROR_TYPE,
"cannot get size of incomplete array type");
}
err = drgn_type_sizeof(drgn_type_type(type).type, ret);
if (err)
return err;
if (__builtin_mul_overflow(*ret, drgn_type_length(type), ret)) {
return drgn_error_create(DRGN_ERROR_OVERFLOW,
"type size is too large");
}
return NULL;
case DRGN_TYPE_VOID:
return drgn_error_create(DRGN_ERROR_TYPE,
"cannot get size of void type");
case DRGN_TYPE_FUNCTION:
return drgn_error_create(DRGN_ERROR_TYPE,
"cannot get size of function type");
}
DRGN_UNREACHABLE();
}
struct drgn_error *drgn_type_bit_size(struct drgn_type *type, uint64_t *ret)
{
struct drgn_error *err;
err = drgn_type_sizeof(type, ret);
if (err)
return err;
if (__builtin_mul_overflow(*ret, 8U, ret)) {
return drgn_error_create(DRGN_ERROR_OVERFLOW,
"type bit size is too large");
}
return NULL;
}
enum drgn_object_kind drgn_type_object_kind(struct drgn_type *type)
{
switch (drgn_type_kind(type)) {
case DRGN_TYPE_INT:
return (drgn_type_is_signed(type) ? DRGN_OBJECT_SIGNED :
DRGN_OBJECT_UNSIGNED);
case DRGN_TYPE_BOOL:
case DRGN_TYPE_POINTER:
return DRGN_OBJECT_UNSIGNED;
case DRGN_TYPE_FLOAT:
return DRGN_OBJECT_FLOAT;
case DRGN_TYPE_COMPLEX:
return DRGN_OBJECT_BUFFER;
case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION:
case DRGN_TYPE_ARRAY:
return (drgn_type_is_complete(type) ? DRGN_OBJECT_BUFFER :
DRGN_OBJECT_INCOMPLETE_BUFFER);
case DRGN_TYPE_ENUM:
if (!drgn_type_is_complete(type))
return DRGN_OBJECT_INCOMPLETE_INTEGER;
/* fallthrough */
case DRGN_TYPE_TYPEDEF:
return drgn_type_object_kind(drgn_type_type(type).type);
case DRGN_TYPE_VOID:
case DRGN_TYPE_FUNCTION:
return DRGN_OBJECT_NONE;
}
DRGN_UNREACHABLE();
}
struct drgn_error *drgn_type_error(const char *format, struct drgn_type *type)
{
struct drgn_qualified_type qualified_type = { type };
return drgn_qualified_type_error(format, qualified_type);
}
struct drgn_error *
drgn_qualified_type_error(const char *format,
struct drgn_qualified_type qualified_type)
{
struct drgn_error *err;
char *name;
err = drgn_pretty_print_type_name(qualified_type, &name);
if (err)
return err;
err = drgn_error_format(DRGN_ERROR_TYPE, format, name);
free(name);
return err;
}
struct drgn_error *drgn_error_incomplete_type(const char *format,
struct drgn_type *type)
{
switch (drgn_type_kind(drgn_underlying_type(type))) {
case DRGN_TYPE_STRUCT:
return drgn_error_format(DRGN_ERROR_TYPE, format,
"incomplete structure");
case DRGN_TYPE_UNION:
return drgn_error_format(DRGN_ERROR_TYPE, format,
"incomplete union");
case DRGN_TYPE_ENUM:
return drgn_error_format(DRGN_ERROR_TYPE, format,
"incomplete enumerated");
case DRGN_TYPE_ARRAY:
return drgn_error_format(DRGN_ERROR_TYPE, format,
"incomplete array");
case DRGN_TYPE_FUNCTION:
return drgn_error_format(DRGN_ERROR_TYPE, format, "function");
case DRGN_TYPE_VOID:
return drgn_error_format(DRGN_ERROR_TYPE, format, "void");
default:
DRGN_UNREACHABLE();
}
}
struct drgn_error *drgn_error_member_not_found(struct drgn_type *type,
const char *member_name)
{
struct drgn_error *err;
struct drgn_qualified_type qualified_type = { type };
char *name;
err = drgn_pretty_print_type_name(qualified_type, &name);
if (err)
return err;
err = drgn_error_format(DRGN_ERROR_LOOKUP, "'%s' has no member '%s'",
name, member_name);
free(name);
return err;
}