mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-23 09:43:06 +00:00
Add basic class type support
This implements the first step at supporting C++: class types. In particular, this adds a new drgn_type_kind, DRGN_TYPE_CLASS, and support for parsing DW_TAG_class_type from DWARF. Although classes are not valid in C, this adds support for pretty printing them, for completeness.
This commit is contained in:
parent
b49f773fe6
commit
0df2152307
@ -1112,6 +1112,10 @@ Types
|
||||
|
||||
Union type.
|
||||
|
||||
.. attribute:: CLASS
|
||||
|
||||
Class type.
|
||||
|
||||
.. attribute:: ENUM
|
||||
|
||||
Enumerated type.
|
||||
@ -1274,6 +1278,11 @@ can be used just like types obtained from :meth:`Program.type()`.
|
||||
Create a new union type. It has kind :attr:`TypeKind.UNION`. Otherwise,
|
||||
this is the same as :func:`struct_type()`.
|
||||
|
||||
.. function:: class_type(tag, size, members, qualifiers=None)
|
||||
|
||||
Create a new class type. It has kind :attr:`TypeKind.CLASS`. Otherwise,
|
||||
this is the same as :func:`struct_type()`.
|
||||
|
||||
.. function:: enum_type(tag, type, enumerators, qualifiers=None)
|
||||
|
||||
Create a new enumerated type. It has kind :attr:`TypeKind.ENUM`.
|
||||
|
@ -67,6 +67,7 @@ from _drgn import (
|
||||
array_type,
|
||||
bool_type,
|
||||
cast,
|
||||
class_type,
|
||||
complex_type,
|
||||
container_of,
|
||||
enum_type,
|
||||
@ -110,6 +111,7 @@ __all__ = [
|
||||
'array_type',
|
||||
'bool_type',
|
||||
'cast',
|
||||
'class_type',
|
||||
'complex_type',
|
||||
'container_of',
|
||||
'enum_type',
|
||||
|
@ -291,6 +291,8 @@ enum drgn_type_kind {
|
||||
DRGN_TYPE_STRUCT,
|
||||
/** Union type. */
|
||||
DRGN_TYPE_UNION,
|
||||
/** Class type. */
|
||||
DRGN_TYPE_CLASS,
|
||||
/** Enumerated type. */
|
||||
DRGN_TYPE_ENUM,
|
||||
/** Type definition (a.k.a.\ alias) type. */
|
||||
@ -332,7 +334,7 @@ enum drgn_primitive_type {
|
||||
*/
|
||||
} __attribute__((packed));
|
||||
|
||||
/** Member of a structure or union type. */
|
||||
/** Member of a structure, union, or class type. */
|
||||
struct drgn_type_member {
|
||||
/**
|
||||
* Type of the member.
|
||||
@ -456,7 +458,7 @@ drgn_type_primitive(struct drgn_type *type)
|
||||
* Get whether a type is complete (i.e., the type definition is known).
|
||||
*
|
||||
* This is always @c false for the void type. It may be @c false for structure,
|
||||
* union, enumerated, and array types, as well as typedef types where the
|
||||
* union, class, enumerated, and array types, as well as typedef types where the
|
||||
* underlying type is one of those. Otherwise, it is always @c true.
|
||||
*/
|
||||
static inline bool drgn_type_is_complete(struct drgn_type *type)
|
||||
@ -492,7 +494,7 @@ static inline const char *drgn_type_name(struct drgn_type *type)
|
||||
|
||||
/**
|
||||
* Get whether a kind of type has a size. This is true for integer, boolean,
|
||||
* floating-point, complex, structure, union, and pointer types.
|
||||
* floating-point, complex, structure, union, class, and pointer types.
|
||||
*/
|
||||
static inline bool drgn_type_kind_has_size(enum drgn_type_kind kind)
|
||||
{
|
||||
@ -502,6 +504,7 @@ static inline bool drgn_type_kind_has_size(enum drgn_type_kind kind)
|
||||
kind == DRGN_TYPE_COMPLEX ||
|
||||
kind == DRGN_TYPE_STRUCT ||
|
||||
kind == DRGN_TYPE_UNION ||
|
||||
kind == DRGN_TYPE_CLASS ||
|
||||
kind == DRGN_TYPE_POINTER);
|
||||
}
|
||||
/** Get whether a type has a size. @sa drgn_type_kind_has_size() */
|
||||
@ -542,13 +545,14 @@ static inline bool drgn_type_is_signed(struct drgn_type *type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether a kind of type has a tag. This is true for structure, union, and
|
||||
* enumerated types.
|
||||
* Get whether a kind of type has a tag. This is true for structure, union,
|
||||
* class, and enumerated types.
|
||||
*/
|
||||
static inline bool drgn_type_kind_has_tag(enum drgn_type_kind kind)
|
||||
{
|
||||
return (kind == DRGN_TYPE_STRUCT ||
|
||||
kind == DRGN_TYPE_UNION ||
|
||||
kind == DRGN_TYPE_CLASS ||
|
||||
kind == DRGN_TYPE_ENUM);
|
||||
}
|
||||
/** Get whether a type has a tag. @sa drgn_type_kind_has_tag() */
|
||||
@ -571,12 +575,14 @@ static inline void *drgn_type_payload(struct drgn_type *type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether a kind of type has members. This is true for structure and union
|
||||
* types.
|
||||
* Get whether a kind of type has members. This is true for structure, union,
|
||||
* and class types.
|
||||
*/
|
||||
static inline bool drgn_type_kind_has_members(enum drgn_type_kind kind)
|
||||
{
|
||||
return kind == DRGN_TYPE_STRUCT || kind == DRGN_TYPE_UNION;
|
||||
return (kind == DRGN_TYPE_STRUCT ||
|
||||
kind == DRGN_TYPE_UNION ||
|
||||
kind == DRGN_TYPE_CLASS);
|
||||
}
|
||||
/** Get whether a type has members. @sa drgn_type_kind_has_members() */
|
||||
static inline bool drgn_type_has_members(struct drgn_type *type)
|
||||
@ -1405,9 +1411,9 @@ enum drgn_object_kind {
|
||||
/**
|
||||
* Memory buffer.
|
||||
*
|
||||
* This is used for objects with a complex, structure, union, or array
|
||||
* type. The value is a buffer of the contents of that object's memory
|
||||
* in the program.
|
||||
* This is used for objects with a complex, structure, union, class, or
|
||||
* array type. The value is a buffer of the contents of that object's
|
||||
* memory in the program.
|
||||
*/
|
||||
DRGN_OBJECT_BUFFER,
|
||||
/**
|
||||
@ -1440,7 +1446,7 @@ enum drgn_object_kind {
|
||||
* Incomplete buffer value.
|
||||
*
|
||||
* This is used for reference objects with an incomplete structure,
|
||||
* union, or array type.
|
||||
* union, class, or array type.
|
||||
*/
|
||||
DRGN_OBJECT_INCOMPLETE_BUFFER = -2,
|
||||
/**
|
||||
@ -2198,7 +2204,7 @@ drgn_object_dereference(struct drgn_object *res, const struct drgn_object *obj)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a member of a structure or union @ref drgn_object (@c .).
|
||||
* Get a member of a structure, union, or class @ref drgn_object (@c .).
|
||||
*
|
||||
* @param[out] res Returned member. May be the same as @p obj.
|
||||
* @param[in] obj Object.
|
||||
|
@ -341,10 +341,11 @@ drgn_base_type_from_dwarf(struct drgn_dwarf_info_cache *dicache, Dwarf_Die *die,
|
||||
}
|
||||
|
||||
/*
|
||||
* DW_TAG_structure_type, DW_TAG_union_type, and DW_TAG_enumeration_type can be
|
||||
* incomplete (i.e., have a DW_AT_declaration of true). This tries to find the
|
||||
* complete type. If it succeeds, it returns NULL. If it can't find a complete
|
||||
* type, it returns a DRGN_ERROR_STOP error. Otherwise, it returns an error.
|
||||
* DW_TAG_structure_type, DW_TAG_union_type, DW_TAG_class_type, and
|
||||
* DW_TAG_enumeration_type can be incomplete (i.e., have a DW_AT_declaration of
|
||||
* true). This tries to find the complete type. If it succeeds, it returns NULL.
|
||||
* If it can't find a complete type, it returns a DRGN_ERROR_STOP error.
|
||||
* Otherwise, it returns an error.
|
||||
*/
|
||||
static struct drgn_error *
|
||||
drgn_dwarf_info_cache_find_complete(struct drgn_dwarf_info_cache *dicache,
|
||||
@ -543,11 +544,13 @@ static struct drgn_error *parse_member(struct drgn_dwarf_info_cache *dicache,
|
||||
|
||||
static struct drgn_error *
|
||||
drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache,
|
||||
Dwarf_Die *die, bool is_struct,
|
||||
Dwarf_Die *die, enum drgn_type_kind kind,
|
||||
struct drgn_type **ret, bool *should_free)
|
||||
{
|
||||
struct drgn_error *err;
|
||||
struct drgn_type *type;
|
||||
const char *dw_tag_str;
|
||||
uint64_t dw_tag;
|
||||
Dwarf_Attribute attr_mem;
|
||||
Dwarf_Attribute *attr;
|
||||
const char *tag;
|
||||
@ -558,28 +561,43 @@ drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache,
|
||||
bool little_endian;
|
||||
int r;
|
||||
|
||||
switch (kind) {
|
||||
case DRGN_TYPE_STRUCT:
|
||||
dw_tag_str = "DW_TAG_structure_type";
|
||||
dw_tag = DW_TAG_structure_type;
|
||||
break;
|
||||
case DRGN_TYPE_UNION:
|
||||
dw_tag_str = "DW_TAG_union_type";
|
||||
dw_tag = DW_TAG_union_type;
|
||||
break;
|
||||
case DRGN_TYPE_CLASS:
|
||||
dw_tag_str = "DW_TAG_class_type";
|
||||
dw_tag = DW_TAG_class_type;
|
||||
break;
|
||||
default:
|
||||
DRGN_UNREACHABLE();
|
||||
}
|
||||
|
||||
attr = dwarf_attr_integrate(die, DW_AT_name, &attr_mem);
|
||||
if (attr) {
|
||||
tag = dwarf_formstring(attr);
|
||||
if (!tag)
|
||||
if (!tag) {
|
||||
return drgn_error_format(DRGN_ERROR_OTHER,
|
||||
"DW_TAG_%s_type has invalid DW_AT_name",
|
||||
is_struct ? "structure" : "union");
|
||||
"%s has invalid DW_AT_name",
|
||||
dw_tag_str);
|
||||
}
|
||||
} else {
|
||||
tag = NULL;
|
||||
}
|
||||
|
||||
if (dwarf_flag(die, DW_AT_declaration, &declaration)) {
|
||||
return drgn_error_format(DRGN_ERROR_OTHER,
|
||||
"DW_TAG_%s_type has invalid DW_AT_declaration",
|
||||
is_struct ? "structure" : "union");
|
||||
"%s has invalid DW_AT_declaration",
|
||||
dw_tag_str);
|
||||
}
|
||||
if (declaration && tag) {
|
||||
err = drgn_dwarf_info_cache_find_complete(dicache,
|
||||
is_struct ?
|
||||
DW_TAG_structure_type :
|
||||
DW_TAG_union_type,
|
||||
tag, ret);
|
||||
dw_tag, tag, ret);
|
||||
if (!err) {
|
||||
*should_free = false;
|
||||
return NULL;
|
||||
@ -594,10 +612,19 @@ drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache,
|
||||
return &drgn_enomem;
|
||||
|
||||
if (declaration) {
|
||||
if (is_struct)
|
||||
switch (kind) {
|
||||
case DRGN_TYPE_STRUCT:
|
||||
drgn_struct_type_init_incomplete(type, tag);
|
||||
else
|
||||
break;
|
||||
case DRGN_TYPE_UNION:
|
||||
drgn_union_type_init_incomplete(type, tag);
|
||||
break;
|
||||
case DRGN_TYPE_CLASS:
|
||||
drgn_class_type_init_incomplete(type, tag);
|
||||
break;
|
||||
default:
|
||||
DRGN_UNREACHABLE();
|
||||
}
|
||||
*ret = type;
|
||||
return NULL;
|
||||
}
|
||||
@ -605,8 +632,8 @@ drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache,
|
||||
size = dwarf_bytesize(die);
|
||||
if (size == -1) {
|
||||
err = drgn_error_format(DRGN_ERROR_OTHER,
|
||||
"DW_TAG_%s_type has missing or invalid DW_AT_byte_size",
|
||||
is_struct ? "structure" : "union");
|
||||
"%s has missing or invalid DW_AT_byte_size",
|
||||
dw_tag_str);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -645,8 +672,13 @@ drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache,
|
||||
sizeof(struct drgn_type_member));
|
||||
}
|
||||
|
||||
if (is_struct) {
|
||||
if (kind == DRGN_TYPE_UNION) {
|
||||
drgn_union_type_init(type, tag, size, num_members);
|
||||
} else {
|
||||
if (kind == DRGN_TYPE_STRUCT)
|
||||
drgn_struct_type_init(type, tag, size, num_members);
|
||||
else
|
||||
drgn_class_type_init(type, tag, size, num_members);
|
||||
/*
|
||||
* Flexible array members are only allowed as the last member of
|
||||
* a structure with more than one named member. We defaulted
|
||||
@ -671,8 +703,6 @@ drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache,
|
||||
thunk->can_be_incomplete_array = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
drgn_union_type_init(type, tag, size, num_members);
|
||||
}
|
||||
*ret = type;
|
||||
return NULL;
|
||||
@ -1289,13 +1319,19 @@ drgn_type_from_dwarf_internal(struct drgn_dwarf_info_cache *dicache,
|
||||
err = drgn_base_type_from_dwarf(dicache, die, &ret->type);
|
||||
break;
|
||||
case DW_TAG_structure_type:
|
||||
err = drgn_compound_type_from_dwarf(dicache, die, true,
|
||||
err = drgn_compound_type_from_dwarf(dicache, die,
|
||||
DRGN_TYPE_STRUCT,
|
||||
&ret->type,
|
||||
&entry.value.should_free);
|
||||
break;
|
||||
case DW_TAG_union_type:
|
||||
err = drgn_compound_type_from_dwarf(dicache, die, false,
|
||||
&ret->type,
|
||||
err = drgn_compound_type_from_dwarf(dicache, die,
|
||||
DRGN_TYPE_UNION, &ret->type,
|
||||
&entry.value.should_free);
|
||||
break;
|
||||
case DW_TAG_class_type:
|
||||
err = drgn_compound_type_from_dwarf(dicache, die,
|
||||
DRGN_TYPE_CLASS, &ret->type,
|
||||
&entry.value.should_free);
|
||||
break;
|
||||
case DW_TAG_enumeration_type:
|
||||
@ -1375,6 +1411,9 @@ struct drgn_error *drgn_dwarf_type_find(enum drgn_type_kind kind,
|
||||
case DRGN_TYPE_UNION:
|
||||
tag = DW_TAG_union_type;
|
||||
break;
|
||||
case DRGN_TYPE_CLASS:
|
||||
tag = DW_TAG_class_type;
|
||||
break;
|
||||
case DRGN_TYPE_ENUM:
|
||||
tag = DW_TAG_enumeration_type;
|
||||
break;
|
||||
|
@ -115,6 +115,9 @@ c_append_tagged_name(struct drgn_qualified_type qualified_type, size_t indent,
|
||||
case DRGN_TYPE_UNION:
|
||||
keyword = "union";
|
||||
break;
|
||||
case DRGN_TYPE_CLASS:
|
||||
keyword = "class";
|
||||
break;
|
||||
case DRGN_TYPE_ENUM:
|
||||
keyword = "enum";
|
||||
break;
|
||||
@ -338,6 +341,7 @@ c_declare_variable(struct drgn_qualified_type qualified_type,
|
||||
return c_declare_basic(qualified_type, name, indent, sb);
|
||||
case DRGN_TYPE_STRUCT:
|
||||
case DRGN_TYPE_UNION:
|
||||
case DRGN_TYPE_CLASS:
|
||||
case DRGN_TYPE_ENUM:
|
||||
return c_declare_tagged(qualified_type, name, indent, sb);
|
||||
case DRGN_TYPE_POINTER:
|
||||
@ -488,6 +492,7 @@ c_define_type(struct drgn_qualified_type qualified_type, size_t indent,
|
||||
return c_declare_basic(qualified_type, NULL, indent, sb);
|
||||
case DRGN_TYPE_STRUCT:
|
||||
case DRGN_TYPE_UNION:
|
||||
case DRGN_TYPE_CLASS:
|
||||
return c_define_compound(qualified_type, indent, sb);
|
||||
case DRGN_TYPE_ENUM:
|
||||
return c_define_enum(qualified_type, indent, sb);
|
||||
@ -714,10 +719,24 @@ c_pretty_print_compound_object(const struct drgn_object *obj,
|
||||
struct drgn_object member;
|
||||
|
||||
if (!drgn_type_is_complete(underlying_type)) {
|
||||
const char *keyword;
|
||||
|
||||
switch (drgn_type_kind(underlying_type)) {
|
||||
case DRGN_TYPE_STRUCT:
|
||||
keyword = "struct";
|
||||
break;
|
||||
case DRGN_TYPE_UNION:
|
||||
keyword = "union";
|
||||
break;
|
||||
case DRGN_TYPE_CLASS:
|
||||
keyword = "class";
|
||||
break;
|
||||
default:
|
||||
DRGN_UNREACHABLE();
|
||||
}
|
||||
return drgn_error_format(DRGN_ERROR_TYPE,
|
||||
"cannot format incomplete %s object",
|
||||
drgn_type_kind(underlying_type) ==
|
||||
DRGN_TYPE_STRUCT ? "struct" : "union");
|
||||
keyword);
|
||||
}
|
||||
|
||||
if (!string_builder_appendc(sb, '{'))
|
||||
@ -1246,6 +1265,7 @@ c_pretty_print_object_impl(const struct drgn_object *obj, bool cast,
|
||||
"complex object formatting is not implemented");
|
||||
case DRGN_TYPE_STRUCT:
|
||||
case DRGN_TYPE_UNION:
|
||||
case DRGN_TYPE_CLASS:
|
||||
return c_pretty_print_compound_object(obj, underlying_type,
|
||||
indent,
|
||||
multi_line_columns, sb);
|
||||
|
@ -1040,6 +1040,7 @@ struct drgn_error *drgn_object_truthiness(const struct drgn_object *obj,
|
||||
switch (drgn_type_kind(underlying_type)) {
|
||||
case DRGN_TYPE_STRUCT:
|
||||
case DRGN_TYPE_UNION:
|
||||
case DRGN_TYPE_CLASS:
|
||||
err = drgn_object_compound_truthiness(obj,
|
||||
underlying_type);
|
||||
if (!err || err->code == DRGN_ERROR_STOP) {
|
||||
@ -1375,7 +1376,7 @@ struct drgn_error *drgn_object_member_dereference(struct drgn_object *res,
|
||||
|
||||
underlying_type = drgn_underlying_type(obj->type);
|
||||
if (drgn_type_kind(underlying_type) != DRGN_TYPE_POINTER) {
|
||||
return drgn_type_error("'%s' is not a pointer to a structure or union",
|
||||
return drgn_type_error("'%s' is not a pointer to a structure, union, or class",
|
||||
obj->type);
|
||||
}
|
||||
|
||||
|
@ -36,10 +36,10 @@
|
||||
/**
|
||||
* Get the truthiness of an object.
|
||||
*
|
||||
* For a signed, unsigned, or floating-point value, this is true iff the value
|
||||
* is non-zero. For structures and unions, this is true iff it is true for any
|
||||
* of its members. For arrays, this is true iff it is true for any of its
|
||||
* elements.
|
||||
* For a signed, unsigned, or floating-point values, this is true iff the value
|
||||
* is non-zero. For structures, unions, and classes, this is true iff it is true
|
||||
* for any of its members. For arrays, this is true iff it is true for any of
|
||||
* its elements.
|
||||
*/
|
||||
struct drgn_error *drgn_object_truthiness(const struct drgn_object *obj,
|
||||
bool *ret);
|
||||
|
@ -178,6 +178,7 @@ DrgnType *float_type(PyObject *self, PyObject *args, PyObject *kwds);
|
||||
DrgnType *complex_type(PyObject *self, PyObject *args, PyObject *kwds);
|
||||
DrgnType *struct_type(PyObject *self, PyObject *args, PyObject *kwds);
|
||||
DrgnType *union_type(PyObject *self, PyObject *args, PyObject *kwds);
|
||||
DrgnType *class_type(PyObject *self, PyObject *args, PyObject *kwds);
|
||||
DrgnType *enum_type(PyObject *self, PyObject *args, PyObject *kwds);
|
||||
DrgnType *typedef_type(PyObject *self, PyObject *args, PyObject *kwds);
|
||||
DrgnType *pointer_type(PyObject *self, PyObject *args, PyObject *kwds);
|
||||
|
@ -101,6 +101,8 @@ static PyMethodDef drgn_methods[] = {
|
||||
drgn_struct_type_DOC},
|
||||
{"union_type", (PyCFunction)union_type, METH_VARARGS | METH_KEYWORDS,
|
||||
drgn_union_type_DOC},
|
||||
{"class_type", (PyCFunction)class_type, METH_VARARGS | METH_KEYWORDS,
|
||||
drgn_class_type_DOC},
|
||||
{"enum_type", (PyCFunction)enum_type, METH_VARARGS | METH_KEYWORDS,
|
||||
drgn_enum_type_DOC},
|
||||
{"typedef_type", (PyCFunction)typedef_type,
|
||||
|
@ -303,6 +303,7 @@ static int serialize_py_object(struct drgn_program *prog, char *buf,
|
||||
return -1;
|
||||
case DRGN_TYPE_STRUCT:
|
||||
case DRGN_TYPE_UNION:
|
||||
case DRGN_TYPE_CLASS:
|
||||
return serialize_compound_value(prog, buf, buf_bit_size,
|
||||
bit_offset, value_obj,
|
||||
type, little_endian);
|
||||
@ -587,8 +588,7 @@ static PyObject *DrgnObject_compound_value(struct drgn_object *obj,
|
||||
if (!drgn_type_is_complete(underlying_type)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"cannot get value of incomplete %s",
|
||||
drgn_type_kind(underlying_type) ==
|
||||
DRGN_TYPE_STRUCT ? "struct" : "union");
|
||||
drgn_type_kind_spelling[drgn_type_kind(underlying_type)]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -742,6 +742,7 @@ static PyObject *DrgnObject_value_impl(struct drgn_object *obj)
|
||||
return NULL;
|
||||
case DRGN_TYPE_STRUCT:
|
||||
case DRGN_TYPE_UNION:
|
||||
case DRGN_TYPE_CLASS:
|
||||
return DrgnObject_compound_value(obj, underlying_type);
|
||||
case DRGN_TYPE_ARRAY:
|
||||
return DrgnObject_array_value(obj, underlying_type);
|
||||
@ -1424,8 +1425,8 @@ static PyObject *DrgnObject_getattro(DrgnObject *self, PyObject *attr_name)
|
||||
Py_CLEAR(res);
|
||||
if (err->code == DRGN_ERROR_TYPE) {
|
||||
/*
|
||||
* If the object isn't a structure or union, raise the
|
||||
* original AttributeError.
|
||||
* If the object doesn't have a compound type,
|
||||
* raise the original AttributeError.
|
||||
*/
|
||||
PyErr_Restore(exc_type, exc_value, exc_traceback);
|
||||
return NULL;
|
||||
@ -1616,7 +1617,7 @@ static PyMethodDef DrgnObject_methods[] = {
|
||||
{"__floor__", (PyCFunction)DrgnObject_floor, METH_NOARGS},
|
||||
{"__ceil__", (PyCFunction)DrgnObject_ceil, METH_NOARGS},
|
||||
{"__dir__", (PyCFunction)DrgnObject_dir, METH_NOARGS,
|
||||
"dir() implementation which includes structure and union members."},
|
||||
"dir() implementation which includes structure, union, and class members."},
|
||||
{"__format__", (PyCFunction)DrgnObject_format, METH_O,
|
||||
"Object formatter."},
|
||||
{},
|
||||
|
@ -691,10 +691,9 @@ static int append_field(PyObject *parts, bool *first, const char *format, ...)
|
||||
_Py_IDENTIFIER(DrgnType_Repr);
|
||||
|
||||
/*
|
||||
* We only want to print compound types (structure and union types) one level
|
||||
* deep in order to avoid very deep recursion. Return 0 if this is the first
|
||||
* level, 1 if this is a deeper level (and thus we shouldn't print more
|
||||
* members), and -1 on error.
|
||||
* We only want to print compound types one level deep in order to avoid very
|
||||
* deep recursion. Return 0 if this is the first level, 1 if this is a deeper
|
||||
* level (and thus we shouldn't print more members), and -1 on error.
|
||||
*/
|
||||
static int DrgnType_ReprEnter(DrgnType *self)
|
||||
{
|
||||
@ -1250,7 +1249,8 @@ out:
|
||||
|
||||
static DrgnType *compound_type(PyObject *tag_obj, PyObject *size_obj,
|
||||
PyObject *members_obj,
|
||||
enum drgn_qualifiers qualifiers, bool is_struct)
|
||||
enum drgn_qualifiers qualifiers,
|
||||
enum drgn_type_kind kind)
|
||||
{
|
||||
const char *tag;
|
||||
DrgnType *type_obj = NULL;
|
||||
@ -1268,7 +1268,7 @@ static DrgnType *compound_type(PyObject *tag_obj, PyObject *size_obj,
|
||||
} else {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%s_type() tag must be str or None",
|
||||
is_struct ? "struct" : "union");
|
||||
drgn_type_kind_spelling[kind]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1276,7 +1276,7 @@ static DrgnType *compound_type(PyObject *tag_obj, PyObject *size_obj,
|
||||
if (size_obj != Py_None) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"incomplete %s type must not have size",
|
||||
is_struct ? "structure" : "union");
|
||||
drgn_type_kind_spelling[kind]);
|
||||
return NULL;
|
||||
}
|
||||
type_obj = DrgnType_new(qualifiers, 0, 0);
|
||||
@ -1290,7 +1290,7 @@ static DrgnType *compound_type(PyObject *tag_obj, PyObject *size_obj,
|
||||
|
||||
if (size_obj == Py_None) {
|
||||
PyErr_Format(PyExc_ValueError, "%s type must have size",
|
||||
is_struct ? "structure" : "union");
|
||||
drgn_type_kind_spelling[kind]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1335,17 +1335,35 @@ static DrgnType *compound_type(PyObject *tag_obj, PyObject *size_obj,
|
||||
goto err;
|
||||
|
||||
if (members_obj == Py_None) {
|
||||
if (is_struct)
|
||||
switch (kind) {
|
||||
case DRGN_TYPE_STRUCT:
|
||||
drgn_struct_type_init_incomplete(type_obj->type, tag);
|
||||
else
|
||||
break;
|
||||
case DRGN_TYPE_UNION:
|
||||
drgn_union_type_init_incomplete(type_obj->type, tag);
|
||||
break;
|
||||
case DRGN_TYPE_CLASS:
|
||||
drgn_class_type_init_incomplete(type_obj->type, tag);
|
||||
break;
|
||||
default:
|
||||
DRGN_UNREACHABLE();
|
||||
}
|
||||
} else {
|
||||
if (is_struct) {
|
||||
switch (kind) {
|
||||
case DRGN_TYPE_STRUCT:
|
||||
drgn_struct_type_init(type_obj->type, tag, size,
|
||||
num_members);
|
||||
} else {
|
||||
break;
|
||||
case DRGN_TYPE_UNION:
|
||||
drgn_union_type_init(type_obj->type, tag, size,
|
||||
num_members);
|
||||
break;
|
||||
case DRGN_TYPE_CLASS:
|
||||
drgn_class_type_init(type_obj->type, tag, size,
|
||||
num_members);
|
||||
break;
|
||||
default:
|
||||
DRGN_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
return type_obj;
|
||||
@ -1371,7 +1389,8 @@ DrgnType *struct_type(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
&qualifiers))
|
||||
return NULL;
|
||||
|
||||
return compound_type(tag_obj, size_obj, members_obj, qualifiers, true);
|
||||
return compound_type(tag_obj, size_obj, members_obj, qualifiers,
|
||||
DRGN_TYPE_STRUCT);
|
||||
}
|
||||
|
||||
DrgnType *union_type(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
@ -1388,7 +1407,26 @@ DrgnType *union_type(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
&qualifiers))
|
||||
return NULL;
|
||||
|
||||
return compound_type(tag_obj, size_obj, members_obj, qualifiers, false);
|
||||
return compound_type(tag_obj, size_obj, members_obj, qualifiers,
|
||||
DRGN_TYPE_UNION);
|
||||
}
|
||||
|
||||
DrgnType *class_type(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
static char *keywords[] = { "tag", "size", "members", "qualifiers", NULL, };
|
||||
PyObject *tag_obj;
|
||||
PyObject *size_obj = Py_None;
|
||||
PyObject *members_obj = Py_None;
|
||||
unsigned char qualifiers = 0;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOO&:class_type",
|
||||
keywords, &tag_obj, &size_obj,
|
||||
&members_obj, qualifiers_converter,
|
||||
&qualifiers))
|
||||
return NULL;
|
||||
|
||||
return compound_type(tag_obj, size_obj, members_obj, qualifiers,
|
||||
DRGN_TYPE_CLASS);
|
||||
}
|
||||
|
||||
static int unpack_enumerator(DrgnType *type_obj, PyObject *enumerators_seq,
|
||||
|
@ -14,6 +14,7 @@ const char * const drgn_type_kind_spelling[] = {
|
||||
[DRGN_TYPE_COMPLEX] = "complex",
|
||||
[DRGN_TYPE_STRUCT] = "struct",
|
||||
[DRGN_TYPE_UNION] = "union",
|
||||
[DRGN_TYPE_CLASS] = "class",
|
||||
[DRGN_TYPE_ENUM] = "enum",
|
||||
[DRGN_TYPE_TYPEDEF] = "typedef",
|
||||
[DRGN_TYPE_POINTER] = "pointer",
|
||||
@ -297,6 +298,27 @@ void drgn_union_type_init_incomplete(struct drgn_type *type, const char *tag)
|
||||
type->_private.num_members = 0;
|
||||
}
|
||||
|
||||
void drgn_class_type_init(struct drgn_type *type, const char *tag,
|
||||
uint64_t size, size_t num_members)
|
||||
{
|
||||
type->_private.kind = DRGN_TYPE_CLASS;
|
||||
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_class_type_init_incomplete(struct drgn_type *type, const char *tag)
|
||||
{
|
||||
type->_private.kind = DRGN_TYPE_CLASS;
|
||||
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)
|
||||
@ -717,8 +739,14 @@ LIBDRGN_PUBLIC struct drgn_error *drgn_type_sizeof(struct drgn_type *type,
|
||||
uint64_t *ret)
|
||||
{
|
||||
struct drgn_error *err;
|
||||
enum drgn_type_kind kind = drgn_type_kind(type);
|
||||
|
||||
switch (drgn_type_kind(type)) {
|
||||
if (!drgn_type_is_complete(type)) {
|
||||
return drgn_error_format(DRGN_ERROR_TYPE,
|
||||
"cannot get size of incomplete %s type",
|
||||
drgn_type_kind_spelling[kind]);
|
||||
}
|
||||
switch (kind) {
|
||||
case DRGN_TYPE_INT:
|
||||
case DRGN_TYPE_BOOL:
|
||||
case DRGN_TYPE_FLOAT:
|
||||
@ -727,33 +755,14 @@ LIBDRGN_PUBLIC struct drgn_error *drgn_type_sizeof(struct drgn_type *type,
|
||||
*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");
|
||||
}
|
||||
case DRGN_TYPE_CLASS:
|
||||
*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;
|
||||
@ -801,6 +810,7 @@ enum drgn_object_kind drgn_type_object_kind(struct drgn_type *type)
|
||||
return DRGN_OBJECT_BUFFER;
|
||||
case DRGN_TYPE_STRUCT:
|
||||
case DRGN_TYPE_UNION:
|
||||
case DRGN_TYPE_CLASS:
|
||||
case DRGN_TYPE_ARRAY:
|
||||
return (drgn_type_is_complete(type) ? DRGN_OBJECT_BUFFER :
|
||||
DRGN_OBJECT_INCOMPLETE_BUFFER);
|
||||
@ -849,6 +859,9 @@ struct drgn_error *drgn_error_incomplete_type(const char *format,
|
||||
case DRGN_TYPE_UNION:
|
||||
return drgn_error_format(DRGN_ERROR_TYPE, format,
|
||||
"incomplete union");
|
||||
case DRGN_TYPE_CLASS:
|
||||
return drgn_error_format(DRGN_ERROR_TYPE, format,
|
||||
"incomplete class");
|
||||
case DRGN_TYPE_ENUM:
|
||||
return drgn_error_format(DRGN_ERROR_TYPE, format,
|
||||
"incomplete enumerated");
|
||||
|
@ -34,7 +34,7 @@
|
||||
* Lazily-evaluated types.
|
||||
*
|
||||
* The graph of types in a program can be very deep (and often cyclical), so
|
||||
* drgn lazily evaluates the types of structure/union members and function
|
||||
* drgn lazily evaluates the types of compound type members and function
|
||||
* parameters.
|
||||
*
|
||||
* @{
|
||||
@ -297,6 +297,30 @@ void drgn_union_type_init(struct drgn_type *type, const char *tag,
|
||||
*/
|
||||
void drgn_union_type_init_incomplete(struct drgn_type *type, const char *tag);
|
||||
|
||||
/**
|
||||
* Initialize a class type.
|
||||
* @param[out] type Type to initialize. This must have @c num_members @ref
|
||||
* drgn_type_memer%s allocated after it. Similar to struct type, the members
|
||||
* must be initialized with @ref drgn_type_member_init() (either before or after
|
||||
* this function is called).
|
||||
* @param[in] tag Name of the type.
|
||||
* @param[in] size Size of the type in bytes.
|
||||
* @param[in] num_members The number of members in the type.
|
||||
*/
|
||||
void drgn_class_type_init(struct drgn_type *type, const char *tag,
|
||||
uint64_t size, size_t num_members);
|
||||
|
||||
/**
|
||||
* Initialize an incomplete class type.
|
||||
*
|
||||
* @c size and @c num_members are set to zero and @c is_complete is set to @c
|
||||
* false.
|
||||
*
|
||||
* @param[out] type Type to initialize.
|
||||
* @param[int] tag Name of the type.
|
||||
*/
|
||||
void drgn_class_type_init_incomplete(struct drgn_type *type, const char *tag);
|
||||
|
||||
/**
|
||||
* Initialize a signed enumerator of a type.
|
||||
*
|
||||
@ -518,14 +542,15 @@ static inline bool drgn_enum_type_is_signed(struct drgn_type *type)
|
||||
/**
|
||||
* Get whether a type is anonymous (i.e., the type has no name).
|
||||
*
|
||||
* This may be @c false for structure, union, and enum types. Otherwise, it is
|
||||
* always true.
|
||||
* This may be @c false for structure, union, class, and enum types. Otherwise,
|
||||
* it is always true.
|
||||
*/
|
||||
static inline bool drgn_type_is_anonymous(struct drgn_type *type)
|
||||
{
|
||||
switch (drgn_type_kind(type)) {
|
||||
case DRGN_TYPE_STRUCT:
|
||||
case DRGN_TYPE_UNION:
|
||||
case DRGN_TYPE_CLASS:
|
||||
case DRGN_TYPE_ENUM:
|
||||
return !drgn_type_tag(type);
|
||||
default:
|
||||
|
@ -574,14 +574,15 @@ struct drgn_error *drgn_type_index_find_member(struct drgn_type_index *tindex,
|
||||
/*
|
||||
* Cache miss. One of the following is true:
|
||||
*
|
||||
* 1. The type isn't a structure or union, which is a type error.
|
||||
* 1. The type isn't a structure, union, or class, which is a type
|
||||
* error.
|
||||
* 2. The type hasn't been cached, which means we need to cache it and
|
||||
* check again.
|
||||
* 3. The type has already been cached, which means the member doesn't
|
||||
* exist.
|
||||
*/
|
||||
if (!drgn_type_has_members(key.type)) {
|
||||
return drgn_type_error("'%s' is not a structure or union",
|
||||
return drgn_type_error("'%s' is not a structure, union, or class",
|
||||
type);
|
||||
}
|
||||
cached_hp = drgn_type_set_hash(&key.type);
|
||||
|
@ -178,7 +178,8 @@ drgn_type_index_find_primitive(struct drgn_type_index *tindex,
|
||||
* drgn_language::find_type().
|
||||
*
|
||||
* @param[in] kind Kind of type to find. Must be @ref DRGN_TYPE_STRUCT, @ref
|
||||
* DRGN_TYPE_UNION, @ref DRGN_TYPE_ENUM, or @ref DRGN_TYPE_TYPEDEF.
|
||||
* DRGN_TYPE_UNION, @ref DRGN_TYPE_CLASS, @ref DRGN_TYPE_ENUM, or @ref
|
||||
* DRGN_TYPE_TYPEDEF.
|
||||
* @param[in] name Name of the type.
|
||||
* @param[in] name_len Length of @p name in bytes.
|
||||
* @param[in] filename See @ref drgn_type_index_find().
|
||||
|
@ -11,6 +11,7 @@ from drgn import (
|
||||
Program,
|
||||
Type,
|
||||
TypeKind,
|
||||
class_type,
|
||||
enum_type,
|
||||
float_type,
|
||||
int_type,
|
||||
@ -20,6 +21,11 @@ from drgn import (
|
||||
)
|
||||
|
||||
|
||||
coord_type = class_type('coord', 12, (
|
||||
(int_type('int', 4, True), 'x', 0),
|
||||
(int_type('int', 4, True), 'y', 32),
|
||||
(int_type('int', 4, True), 'z', 64),
|
||||
))
|
||||
point_type = struct_type('point', 8, (
|
||||
(int_type('int', 4, True), 'x', 0),
|
||||
(int_type('int', 4, True), 'y', 32),
|
||||
|
@ -8,6 +8,7 @@ from drgn import (
|
||||
Program,
|
||||
Qualifiers,
|
||||
array_type,
|
||||
class_type,
|
||||
complex_type,
|
||||
enum_type,
|
||||
float_type,
|
||||
@ -19,7 +20,14 @@ from drgn import (
|
||||
union_type,
|
||||
void_type,
|
||||
)
|
||||
from tests import ObjectTestCase, color_type, option_type, pid_type, point_type
|
||||
from tests import (
|
||||
ObjectTestCase,
|
||||
color_type,
|
||||
coord_type,
|
||||
option_type,
|
||||
pid_type,
|
||||
point_type,
|
||||
)
|
||||
from tests.dwarf import DW_AT, DW_ATE, DW_FORM, DW_TAG
|
||||
from tests.dwarfwriter import compile_dwarf, DwarfDie, DwarfAttrib
|
||||
|
||||
@ -709,6 +717,109 @@ class TestTypes(unittest.TestCase):
|
||||
self.type_from_dwarf, dies)
|
||||
dies[0].attribs.insert(1, size)
|
||||
|
||||
def test_class(self):
|
||||
dies = [
|
||||
DwarfDie(
|
||||
DW_TAG.class_type,
|
||||
[
|
||||
DwarfAttrib(DW_AT.name, DW_FORM.string, 'coord'),
|
||||
DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 12),
|
||||
],
|
||||
[
|
||||
DwarfDie(
|
||||
DW_TAG.member,
|
||||
[
|
||||
DwarfAttrib(DW_AT.name, DW_FORM.string, 'x'),
|
||||
DwarfAttrib(DW_AT.data_member_location, DW_FORM.data1, 0),
|
||||
DwarfAttrib(DW_AT.type, DW_FORM.ref4, 1),
|
||||
],
|
||||
),
|
||||
DwarfDie(
|
||||
DW_TAG.member,
|
||||
[
|
||||
DwarfAttrib(DW_AT.name, DW_FORM.string, 'y'),
|
||||
DwarfAttrib(DW_AT.data_member_location, DW_FORM.data1, 4),
|
||||
DwarfAttrib(DW_AT.type, DW_FORM.ref4, 1),
|
||||
],
|
||||
),
|
||||
DwarfDie(
|
||||
DW_TAG.member,
|
||||
[
|
||||
DwarfAttrib(DW_AT.name, DW_FORM.string, 'z'),
|
||||
DwarfAttrib(DW_AT.data_member_location, DW_FORM.data1, 8),
|
||||
DwarfAttrib(DW_AT.type, DW_FORM.ref4, 1),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
int_die,
|
||||
]
|
||||
|
||||
self.assertFromDwarf(dies, coord_type)
|
||||
|
||||
tag = dies[0].attribs.pop(0)
|
||||
self.assertFromDwarf(
|
||||
dies, class_type(None, coord_type.size, coord_type.members))
|
||||
dies[0].attribs.insert(0, tag)
|
||||
|
||||
children = list(dies[0].children)
|
||||
dies[0].children.clear()
|
||||
self.assertFromDwarf(
|
||||
dies, class_type('coord', coord_type.size, ()))
|
||||
size = dies[0].attribs.pop(1)
|
||||
dies[0].attribs.append(DwarfAttrib(DW_AT.declaration, DW_FORM.flag_present, True))
|
||||
self.assertFromDwarf(dies, class_type('coord'))
|
||||
del dies[0].attribs[-1]
|
||||
dies[0].attribs.insert(1, size)
|
||||
dies[0].children.extend(children)
|
||||
|
||||
name = dies[0].children[0].attribs.pop(0)
|
||||
self.assertFromDwarf(
|
||||
dies,
|
||||
class_type('coord', coord_type.size, (
|
||||
(int_type('int', 4, True), None, 0),
|
||||
(int_type('int', 4, True), 'y', 32),
|
||||
(int_type('int', 4, True), 'z', 64),
|
||||
)))
|
||||
dies[0].children[0].attribs.insert(0, name)
|
||||
|
||||
tag = dies[0].attribs.pop(0)
|
||||
dies[0].attribs.insert(0, DwarfAttrib(DW_AT.name, DW_FORM.data1, 0))
|
||||
self.assertRaisesRegex(Exception,
|
||||
'DW_TAG_class_type has invalid DW_AT_name',
|
||||
self.type_from_dwarf, dies)
|
||||
dies[0].attribs[0] = tag
|
||||
|
||||
size = dies[0].attribs.pop(1)
|
||||
self.assertRaisesRegex(Exception,
|
||||
'DW_TAG_class_type has missing or invalid DW_AT_byte_size',
|
||||
self.type_from_dwarf, dies)
|
||||
dies[0].attribs.insert(1, size)
|
||||
|
||||
name = dies[0].children[0].attribs.pop(0)
|
||||
dies[0].children[0].attribs.insert(0, DwarfAttrib(DW_AT.name, DW_FORM.data1, 0))
|
||||
self.assertRaisesRegex(Exception,
|
||||
'DW_TAG_member has invalid DW_AT_name',
|
||||
self.type_from_dwarf, dies)
|
||||
dies[0].children[0].attribs[0] = name
|
||||
|
||||
location = dies[0].children[0].attribs[1]
|
||||
dies[0].children[0].attribs[1] = DwarfAttrib(DW_AT.data_member_location, DW_FORM.string, 'foo')
|
||||
self.assertRaisesRegex(Exception,
|
||||
'DW_TAG_member has invalid DW_AT_data_member_location',
|
||||
self.type_from_dwarf, dies)
|
||||
dies[0].children[0].attribs[1] = location
|
||||
|
||||
type_ = dies[0].children[0].attribs.pop(2)
|
||||
self.assertRaisesRegex(Exception,
|
||||
'DW_TAG_member is missing DW_AT_type',
|
||||
self.type_from_dwarf, dies)
|
||||
dies[0].children[0].attribs.insert(2, DwarfAttrib(DW_AT.type, DW_FORM.string, 'foo'))
|
||||
self.assertRaisesRegex(Exception,
|
||||
'DW_TAG_member has invalid DW_AT_type',
|
||||
self.type_from_dwarf, dies)
|
||||
dies[0].children[0].attribs[2] = type_
|
||||
|
||||
def test_lazy_cycle(self):
|
||||
dies = [
|
||||
DwarfDie(
|
||||
|
@ -3,21 +3,22 @@ import operator
|
||||
import unittest
|
||||
|
||||
from drgn import (
|
||||
Qualifiers,
|
||||
array_type,
|
||||
bool_type,
|
||||
class_type,
|
||||
complex_type,
|
||||
enum_type,
|
||||
float_type,
|
||||
function_type,
|
||||
int_type,
|
||||
pointer_type,
|
||||
Qualifiers,
|
||||
struct_type,
|
||||
typedef_type,
|
||||
union_type,
|
||||
void_type,
|
||||
)
|
||||
from tests import point_type
|
||||
from tests import coord_type, point_type
|
||||
from tests.libdrgn import C_TOKEN, drgn_lexer_c, Lexer
|
||||
|
||||
|
||||
@ -78,6 +79,12 @@ class TestPrettyPrintTypeName(unittest.TestCase):
|
||||
self.assertQualifiedTypeName('union <anonymous>', False, union_type,
|
||||
None)
|
||||
|
||||
def test_class(self):
|
||||
self.assertQualifiedTypeName('class coord', True, class_type,
|
||||
'coord')
|
||||
self.assertQualifiedTypeName('class <anonymous>', False, class_type,
|
||||
None)
|
||||
|
||||
def test_enum(self):
|
||||
self.assertQualifiedTypeName('enum color', True, enum_type, 'color',
|
||||
None, None),
|
||||
@ -279,6 +286,14 @@ const union foo {
|
||||
unsigned char a[4];
|
||||
}""")
|
||||
|
||||
def test_class(self):
|
||||
self.assertPrettyPrint(coord_type, """\
|
||||
class coord {
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
}""")
|
||||
|
||||
def test_enum(self):
|
||||
t = enum_type('color', int_type('unsigned int', 4, False), (
|
||||
('RED', 0),
|
||||
|
@ -26,6 +26,7 @@ from tests import (
|
||||
MockMemorySegment,
|
||||
ObjectTestCase,
|
||||
color_type,
|
||||
coord_type,
|
||||
line_segment_type,
|
||||
mock_program,
|
||||
option_type,
|
||||
@ -1200,7 +1201,7 @@ class TestCOperators(ObjectTestCase):
|
||||
r'container_of\(\) argument must be a pointer',
|
||||
container_of, obj[0], point_type, 'x')
|
||||
|
||||
self.assertRaisesRegex(TypeError, 'not a structure or union',
|
||||
self.assertRaisesRegex(TypeError, 'not a structure, union, or class',
|
||||
container_of, obj, obj.type_, 'x'),
|
||||
|
||||
type_ = struct_type('foo', 16, (
|
||||
@ -1771,7 +1772,7 @@ class TestGenericOperators(ObjectTestCase):
|
||||
self.assertEqual(obj.y, Object(self.prog, 'int', value=1))
|
||||
|
||||
obj = Object(self.prog, 'int', value=1)
|
||||
self.assertRaisesRegex(TypeError, "'int' is not a structure or union",
|
||||
self.assertRaisesRegex(TypeError, "'int' is not a structure, union, or class",
|
||||
obj.member_, 'x')
|
||||
self.assertRaisesRegex(AttributeError, 'no attribute', getattr, obj,
|
||||
'x')
|
||||
|
@ -6,6 +6,7 @@ from drgn import (
|
||||
TypeKind,
|
||||
array_type,
|
||||
bool_type,
|
||||
class_type,
|
||||
complex_type,
|
||||
enum_type,
|
||||
float_type,
|
||||
@ -366,6 +367,144 @@ class TestType(unittest.TestCase):
|
||||
(int_type('unsigned int', 4, False), 'y', 0, 4),
|
||||
))
|
||||
|
||||
def test_class(self):
|
||||
t = class_type('coord', 12, (
|
||||
(int_type('int', 4, True), 'x', 0),
|
||||
(int_type('int', 4, True), 'y', 32),
|
||||
(int_type('int', 4, True), 'z', 64),
|
||||
))
|
||||
self.assertEqual(t.kind, TypeKind.CLASS)
|
||||
self.assertIsNone(t.primitive)
|
||||
self.assertEqual(t.tag, 'coord')
|
||||
self.assertEqual(t.size, 12)
|
||||
self.assertEqual(t.members, (
|
||||
(int_type('int', 4, True), 'x', 0, 0),
|
||||
(int_type('int', 4, True), 'y', 32, 0),
|
||||
(int_type('int', 4, True), 'z', 64, 0),
|
||||
))
|
||||
self.assertTrue(t.is_complete())
|
||||
|
||||
self.assertEqual(t, class_type('coord', 12, (
|
||||
(int_type('int', 4, True), 'x', 0),
|
||||
(int_type('int', 4, True), 'y', 32),
|
||||
(int_type('int', 4, True), 'z', 64),
|
||||
)))
|
||||
# Different tag.
|
||||
self.assertNotEqual(t, class_type('crd', 12, (
|
||||
(int_type('int', 4, True), 'x', 0),
|
||||
(int_type('int', 4, True), 'y', 32),
|
||||
(int_type('int', 4, True), 'z', 64),
|
||||
)))
|
||||
# Different size.
|
||||
self.assertNotEqual(t, class_type('coord', 16, (
|
||||
(int_type('int', 4, True), 'x', 0),
|
||||
(int_type('int', 4, True), 'y', 32),
|
||||
(int_type('int', 4, True), 'z', 64),
|
||||
)))
|
||||
# One is anonymous.
|
||||
self.assertNotEqual(t, class_type(None, 12, (
|
||||
(int_type('int', 4, True), 'x', 0),
|
||||
(int_type('int', 4, True), 'y', 32),
|
||||
(int_type('int', 4, True), 'z', 64),
|
||||
)))
|
||||
# Different members.
|
||||
self.assertNotEqual(t, class_type('coord', 12, (
|
||||
(int_type('long', 8, True), 'x', 0),
|
||||
(int_type('long', 8, True), 'y', 64),
|
||||
(int_type('long', 8, True), 'z', 128),
|
||||
)))
|
||||
# Different number of members.
|
||||
self.assertNotEqual(t, class_type('coord', 12, (
|
||||
(int_type('int', 4, True), 'x', 0),
|
||||
(int_type('int', 4, True), 'y', 32),
|
||||
)))
|
||||
# One member is anonymous.
|
||||
self.assertNotEqual(t, class_type('coord', 8, (
|
||||
(int_type('int', 4, True), 'x', 0, 0),
|
||||
(int_type('int', 4, True), None, 32, 0),
|
||||
(int_type('int', 4, True), 'z', 64, 0),
|
||||
)))
|
||||
# One is incomplete.
|
||||
self.assertNotEqual(t, class_type('coord'))
|
||||
|
||||
self.assertEqual(repr(t), "class_type(tag='coord', size=12, members=((int_type(name='int', size=4, is_signed=True), 'x', 0, 0), (int_type(name='int', size=4, is_signed=True), 'y', 32, 0), (int_type(name='int', size=4, is_signed=True), 'z', 64, 0)))")
|
||||
self.assertEqual(sizeof(t), 12)
|
||||
|
||||
t = class_type(None, 12, (
|
||||
(int_type('int', 4, True), 'x', 0),
|
||||
(int_type('int', 4, True), 'y', 32),
|
||||
(int_type('int', 4, True), 'z', 64),
|
||||
))
|
||||
self.assertEqual(t.kind, TypeKind.CLASS)
|
||||
self.assertIsNone(t.primitive)
|
||||
self.assertIsNone(t.tag)
|
||||
self.assertEqual(t.size, 12)
|
||||
self.assertEqual(t.members, (
|
||||
(int_type('int', 4, True), 'x', 0, 0),
|
||||
(int_type('int', 4, True), 'y', 32, 0),
|
||||
(int_type('int', 4, True), 'z', 64, 0),
|
||||
))
|
||||
self.assertTrue(t.is_complete())
|
||||
|
||||
t = class_type('color', 0, ())
|
||||
self.assertEqual(t.kind, TypeKind.CLASS)
|
||||
self.assertIsNone(t.primitive)
|
||||
self.assertEqual(t.tag, 'color')
|
||||
self.assertEqual(t.size, 0)
|
||||
self.assertEqual(t.members, ())
|
||||
self.assertTrue(t.is_complete())
|
||||
self.assertEqual(repr(t), "class_type(tag='color', size=0, members=())")
|
||||
|
||||
t = class_type('color')
|
||||
self.assertEqual(t.kind, TypeKind.CLASS)
|
||||
self.assertIsNone(t.primitive)
|
||||
self.assertEqual(t.tag, 'color')
|
||||
self.assertIsNone(t.size)
|
||||
self.assertIsNone(t.members)
|
||||
self.assertFalse(t.is_complete())
|
||||
self.assertEqual(repr(t), "class_type(tag='color', size=None, members=None)")
|
||||
|
||||
t = class_type(None, None, None)
|
||||
self.assertEqual(t.kind, TypeKind.CLASS)
|
||||
self.assertIsNone(t.primitive)
|
||||
self.assertEqual(t.tag, None)
|
||||
self.assertIsNone(t.size)
|
||||
self.assertIsNone(t.members)
|
||||
self.assertFalse(t.is_complete())
|
||||
self.assertEqual(repr(t), "class_type(tag=None, size=None, members=None)")
|
||||
|
||||
self.assertRaises(TypeError, class_type, 4)
|
||||
self.assertRaisesRegex(ValueError, 'must not have size', class_type,
|
||||
'coord', 12, None)
|
||||
self.assertRaisesRegex(ValueError, 'must have size', class_type,
|
||||
'coord', None, ())
|
||||
self.assertRaisesRegex(TypeError, 'must be sequence or None',
|
||||
class_type, 'coord', 12, 4)
|
||||
self.assertRaisesRegex(TypeError, 'must be.*sequence', class_type,
|
||||
'coord', 12, (4))
|
||||
self.assertRaisesRegex(ValueError, 'must be.*sequence', class_type,
|
||||
'coord', 12, ((),))
|
||||
self.assertRaisesRegex(TypeError, 'must be string or None',
|
||||
class_type, 'coord', 12,
|
||||
((int_type('int', 4, True), 4, 0),))
|
||||
self.assertRaisesRegex(TypeError, 'must be integer', class_type,
|
||||
'coord', 12,
|
||||
((int_type('int', 4, True), 'x', None),))
|
||||
self.assertRaisesRegex(TypeError, 'must be Type', class_type, 'coord',
|
||||
12, ((None, 'x', 0),))
|
||||
|
||||
# Bit size.
|
||||
t = class_type('coord', 12, (
|
||||
(int_type('int', 4, True), 'x', 0, 4),
|
||||
(int_type('int', 4, True), 'y', 32, 4),
|
||||
(int_type('int', 4, True), 'z', 64, 4),
|
||||
))
|
||||
self.assertEqual(t.members, (
|
||||
(int_type('int', 4, True), 'x', 0, 4),
|
||||
(int_type('int', 4, True), 'y', 32, 4),
|
||||
(int_type('int', 4, True), 'z', 64, 4),
|
||||
))
|
||||
|
||||
def test_enum(self):
|
||||
t = enum_type('color', int_type('unsigned int', 4, False),
|
||||
(('RED', 0), ('GREEN', 1), ('BLUE', 2)))
|
||||
|
Loading…
Reference in New Issue
Block a user