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:
Amlan Nayak 2019-11-14 17:12:47 -08:00 committed by Omar Sandoval
parent b49f773fe6
commit 0df2152307
20 changed files with 527 additions and 96 deletions

View File

@ -1112,6 +1112,10 @@ Types
Union type. Union type.
.. attribute:: CLASS
Class type.
.. attribute:: ENUM .. attribute:: ENUM
Enumerated type. 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, Create a new union type. It has kind :attr:`TypeKind.UNION`. Otherwise,
this is the same as :func:`struct_type()`. 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) .. function:: enum_type(tag, type, enumerators, qualifiers=None)
Create a new enumerated type. It has kind :attr:`TypeKind.ENUM`. Create a new enumerated type. It has kind :attr:`TypeKind.ENUM`.

View File

@ -67,6 +67,7 @@ from _drgn import (
array_type, array_type,
bool_type, bool_type,
cast, cast,
class_type,
complex_type, complex_type,
container_of, container_of,
enum_type, enum_type,
@ -110,6 +111,7 @@ __all__ = [
'array_type', 'array_type',
'bool_type', 'bool_type',
'cast', 'cast',
'class_type',
'complex_type', 'complex_type',
'container_of', 'container_of',
'enum_type', 'enum_type',

View File

@ -291,6 +291,8 @@ enum drgn_type_kind {
DRGN_TYPE_STRUCT, DRGN_TYPE_STRUCT,
/** Union type. */ /** Union type. */
DRGN_TYPE_UNION, DRGN_TYPE_UNION,
/** Class type. */
DRGN_TYPE_CLASS,
/** Enumerated type. */ /** Enumerated type. */
DRGN_TYPE_ENUM, DRGN_TYPE_ENUM,
/** Type definition (a.k.a.\ alias) type. */ /** Type definition (a.k.a.\ alias) type. */
@ -332,7 +334,7 @@ enum drgn_primitive_type {
*/ */
} __attribute__((packed)); } __attribute__((packed));
/** Member of a structure or union type. */ /** Member of a structure, union, or class type. */
struct drgn_type_member { struct drgn_type_member {
/** /**
* Type of the 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). * 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, * 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. * underlying type is one of those. Otherwise, it is always @c true.
*/ */
static inline bool drgn_type_is_complete(struct drgn_type *type) 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, * 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) 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_COMPLEX ||
kind == DRGN_TYPE_STRUCT || kind == DRGN_TYPE_STRUCT ||
kind == DRGN_TYPE_UNION || kind == DRGN_TYPE_UNION ||
kind == DRGN_TYPE_CLASS ||
kind == DRGN_TYPE_POINTER); kind == DRGN_TYPE_POINTER);
} }
/** Get whether a type has a size. @sa drgn_type_kind_has_size() */ /** 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 * Get whether a kind of type has a tag. This is true for structure, union,
* enumerated types. * class, and enumerated types.
*/ */
static inline bool drgn_type_kind_has_tag(enum drgn_type_kind kind) static inline bool drgn_type_kind_has_tag(enum drgn_type_kind kind)
{ {
return (kind == DRGN_TYPE_STRUCT || return (kind == DRGN_TYPE_STRUCT ||
kind == DRGN_TYPE_UNION || kind == DRGN_TYPE_UNION ||
kind == DRGN_TYPE_CLASS ||
kind == DRGN_TYPE_ENUM); kind == DRGN_TYPE_ENUM);
} }
/** Get whether a type has a tag. @sa drgn_type_kind_has_tag() */ /** 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 * Get whether a kind of type has members. This is true for structure, union,
* types. * and class types.
*/ */
static inline bool drgn_type_kind_has_members(enum drgn_type_kind kind) 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() */ /** Get whether a type has members. @sa drgn_type_kind_has_members() */
static inline bool drgn_type_has_members(struct drgn_type *type) static inline bool drgn_type_has_members(struct drgn_type *type)
@ -1405,9 +1411,9 @@ enum drgn_object_kind {
/** /**
* Memory buffer. * Memory buffer.
* *
* This is used for objects with a complex, structure, union, or array * This is used for objects with a complex, structure, union, class, or
* type. The value is a buffer of the contents of that object's memory * array type. The value is a buffer of the contents of that object's
* in the program. * memory in the program.
*/ */
DRGN_OBJECT_BUFFER, DRGN_OBJECT_BUFFER,
/** /**
@ -1440,7 +1446,7 @@ enum drgn_object_kind {
* Incomplete buffer value. * Incomplete buffer value.
* *
* This is used for reference objects with an incomplete structure, * This is used for reference objects with an incomplete structure,
* union, or array type. * union, class, or array type.
*/ */
DRGN_OBJECT_INCOMPLETE_BUFFER = -2, 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[out] res Returned member. May be the same as @p obj.
* @param[in] obj Object. * @param[in] obj Object.

View File

@ -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 * DW_TAG_structure_type, DW_TAG_union_type, DW_TAG_class_type, and
* incomplete (i.e., have a DW_AT_declaration of true). This tries to find the * DW_TAG_enumeration_type can be incomplete (i.e., have a DW_AT_declaration of
* complete type. If it succeeds, it returns NULL. If it can't find a complete * true). This tries to find the complete type. If it succeeds, it returns NULL.
* type, it returns a DRGN_ERROR_STOP error. Otherwise, it returns an error. * If it can't find a complete type, it returns a DRGN_ERROR_STOP error.
* Otherwise, it returns an error.
*/ */
static struct drgn_error * static struct drgn_error *
drgn_dwarf_info_cache_find_complete(struct drgn_dwarf_info_cache *dicache, 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 * static struct drgn_error *
drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache, 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_type **ret, bool *should_free)
{ {
struct drgn_error *err; struct drgn_error *err;
struct drgn_type *type; struct drgn_type *type;
const char *dw_tag_str;
uint64_t dw_tag;
Dwarf_Attribute attr_mem; Dwarf_Attribute attr_mem;
Dwarf_Attribute *attr; Dwarf_Attribute *attr;
const char *tag; const char *tag;
@ -558,28 +561,43 @@ drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache,
bool little_endian; bool little_endian;
int r; 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); attr = dwarf_attr_integrate(die, DW_AT_name, &attr_mem);
if (attr) { if (attr) {
tag = dwarf_formstring(attr); tag = dwarf_formstring(attr);
if (!tag) if (!tag) {
return drgn_error_format(DRGN_ERROR_OTHER, return drgn_error_format(DRGN_ERROR_OTHER,
"DW_TAG_%s_type has invalid DW_AT_name", "%s has invalid DW_AT_name",
is_struct ? "structure" : "union"); dw_tag_str);
}
} else { } else {
tag = NULL; tag = NULL;
} }
if (dwarf_flag(die, DW_AT_declaration, &declaration)) { if (dwarf_flag(die, DW_AT_declaration, &declaration)) {
return drgn_error_format(DRGN_ERROR_OTHER, return drgn_error_format(DRGN_ERROR_OTHER,
"DW_TAG_%s_type has invalid DW_AT_declaration", "%s has invalid DW_AT_declaration",
is_struct ? "structure" : "union"); dw_tag_str);
} }
if (declaration && tag) { if (declaration && tag) {
err = drgn_dwarf_info_cache_find_complete(dicache, err = drgn_dwarf_info_cache_find_complete(dicache,
is_struct ? dw_tag, tag, ret);
DW_TAG_structure_type :
DW_TAG_union_type,
tag, ret);
if (!err) { if (!err) {
*should_free = false; *should_free = false;
return NULL; return NULL;
@ -594,10 +612,19 @@ drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache,
return &drgn_enomem; return &drgn_enomem;
if (declaration) { if (declaration) {
if (is_struct) switch (kind) {
case DRGN_TYPE_STRUCT:
drgn_struct_type_init_incomplete(type, tag); drgn_struct_type_init_incomplete(type, tag);
else break;
case DRGN_TYPE_UNION:
drgn_union_type_init_incomplete(type, tag); 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; *ret = type;
return NULL; return NULL;
} }
@ -605,8 +632,8 @@ drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache,
size = dwarf_bytesize(die); size = dwarf_bytesize(die);
if (size == -1) { if (size == -1) {
err = drgn_error_format(DRGN_ERROR_OTHER, err = drgn_error_format(DRGN_ERROR_OTHER,
"DW_TAG_%s_type has missing or invalid DW_AT_byte_size", "%s has missing or invalid DW_AT_byte_size",
is_struct ? "structure" : "union"); dw_tag_str);
goto err; goto err;
} }
@ -645,8 +672,13 @@ drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache,
sizeof(struct drgn_type_member)); sizeof(struct drgn_type_member));
} }
if (is_struct) { if (kind == DRGN_TYPE_UNION) {
drgn_struct_type_init(type, tag, size, num_members); 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 * Flexible array members are only allowed as the last member of
* a structure with more than one named member. We defaulted * 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; thunk->can_be_incomplete_array = true;
} }
} }
} else {
drgn_union_type_init(type, tag, size, num_members);
} }
*ret = type; *ret = type;
return NULL; 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); err = drgn_base_type_from_dwarf(dicache, die, &ret->type);
break; break;
case DW_TAG_structure_type: 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, &ret->type,
&entry.value.should_free); &entry.value.should_free);
break; break;
case DW_TAG_union_type: case DW_TAG_union_type:
err = drgn_compound_type_from_dwarf(dicache, die, false, err = drgn_compound_type_from_dwarf(dicache, die,
&ret->type, 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); &entry.value.should_free);
break; break;
case DW_TAG_enumeration_type: 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: case DRGN_TYPE_UNION:
tag = DW_TAG_union_type; tag = DW_TAG_union_type;
break; break;
case DRGN_TYPE_CLASS:
tag = DW_TAG_class_type;
break;
case DRGN_TYPE_ENUM: case DRGN_TYPE_ENUM:
tag = DW_TAG_enumeration_type; tag = DW_TAG_enumeration_type;
break; break;

View File

@ -115,6 +115,9 @@ c_append_tagged_name(struct drgn_qualified_type qualified_type, size_t indent,
case DRGN_TYPE_UNION: case DRGN_TYPE_UNION:
keyword = "union"; keyword = "union";
break; break;
case DRGN_TYPE_CLASS:
keyword = "class";
break;
case DRGN_TYPE_ENUM: case DRGN_TYPE_ENUM:
keyword = "enum"; keyword = "enum";
break; break;
@ -338,6 +341,7 @@ c_declare_variable(struct drgn_qualified_type qualified_type,
return c_declare_basic(qualified_type, name, indent, sb); return c_declare_basic(qualified_type, name, indent, sb);
case DRGN_TYPE_STRUCT: case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION: case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
case DRGN_TYPE_ENUM: case DRGN_TYPE_ENUM:
return c_declare_tagged(qualified_type, name, indent, sb); return c_declare_tagged(qualified_type, name, indent, sb);
case DRGN_TYPE_POINTER: 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); return c_declare_basic(qualified_type, NULL, indent, sb);
case DRGN_TYPE_STRUCT: case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION: case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
return c_define_compound(qualified_type, indent, sb); return c_define_compound(qualified_type, indent, sb);
case DRGN_TYPE_ENUM: case DRGN_TYPE_ENUM:
return c_define_enum(qualified_type, indent, sb); 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; struct drgn_object member;
if (!drgn_type_is_complete(underlying_type)) { 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, return drgn_error_format(DRGN_ERROR_TYPE,
"cannot format incomplete %s object", "cannot format incomplete %s object",
drgn_type_kind(underlying_type) == keyword);
DRGN_TYPE_STRUCT ? "struct" : "union");
} }
if (!string_builder_appendc(sb, '{')) 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"); "complex object formatting is not implemented");
case DRGN_TYPE_STRUCT: case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION: case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
return c_pretty_print_compound_object(obj, underlying_type, return c_pretty_print_compound_object(obj, underlying_type,
indent, indent,
multi_line_columns, sb); multi_line_columns, sb);

View File

@ -1040,6 +1040,7 @@ struct drgn_error *drgn_object_truthiness(const struct drgn_object *obj,
switch (drgn_type_kind(underlying_type)) { switch (drgn_type_kind(underlying_type)) {
case DRGN_TYPE_STRUCT: case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION: case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
err = drgn_object_compound_truthiness(obj, err = drgn_object_compound_truthiness(obj,
underlying_type); underlying_type);
if (!err || err->code == DRGN_ERROR_STOP) { 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); underlying_type = drgn_underlying_type(obj->type);
if (drgn_type_kind(underlying_type) != DRGN_TYPE_POINTER) { 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); obj->type);
} }

View File

@ -36,10 +36,10 @@
/** /**
* Get the truthiness of an object. * Get the truthiness of an object.
* *
* For a signed, unsigned, or floating-point value, this is true iff the value * For a signed, unsigned, or floating-point values, this is true iff the value
* is non-zero. For structures and unions, this is true iff it is true for any * is non-zero. For structures, unions, and classes, this is true iff it is true
* of its members. For arrays, this is true iff it is true for any of its * for any of its members. For arrays, this is true iff it is true for any of
* elements. * its elements.
*/ */
struct drgn_error *drgn_object_truthiness(const struct drgn_object *obj, struct drgn_error *drgn_object_truthiness(const struct drgn_object *obj,
bool *ret); bool *ret);

View File

@ -178,6 +178,7 @@ DrgnType *float_type(PyObject *self, PyObject *args, PyObject *kwds);
DrgnType *complex_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 *struct_type(PyObject *self, PyObject *args, PyObject *kwds);
DrgnType *union_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 *enum_type(PyObject *self, PyObject *args, PyObject *kwds);
DrgnType *typedef_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); DrgnType *pointer_type(PyObject *self, PyObject *args, PyObject *kwds);

View File

@ -101,6 +101,8 @@ static PyMethodDef drgn_methods[] = {
drgn_struct_type_DOC}, drgn_struct_type_DOC},
{"union_type", (PyCFunction)union_type, METH_VARARGS | METH_KEYWORDS, {"union_type", (PyCFunction)union_type, METH_VARARGS | METH_KEYWORDS,
drgn_union_type_DOC}, 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, {"enum_type", (PyCFunction)enum_type, METH_VARARGS | METH_KEYWORDS,
drgn_enum_type_DOC}, drgn_enum_type_DOC},
{"typedef_type", (PyCFunction)typedef_type, {"typedef_type", (PyCFunction)typedef_type,

View File

@ -303,6 +303,7 @@ static int serialize_py_object(struct drgn_program *prog, char *buf,
return -1; return -1;
case DRGN_TYPE_STRUCT: case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION: case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
return serialize_compound_value(prog, buf, buf_bit_size, return serialize_compound_value(prog, buf, buf_bit_size,
bit_offset, value_obj, bit_offset, value_obj,
type, little_endian); type, little_endian);
@ -587,8 +588,7 @@ static PyObject *DrgnObject_compound_value(struct drgn_object *obj,
if (!drgn_type_is_complete(underlying_type)) { if (!drgn_type_is_complete(underlying_type)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"cannot get value of incomplete %s", "cannot get value of incomplete %s",
drgn_type_kind(underlying_type) == drgn_type_kind_spelling[drgn_type_kind(underlying_type)]);
DRGN_TYPE_STRUCT ? "struct" : "union");
return NULL; return NULL;
} }
@ -742,6 +742,7 @@ static PyObject *DrgnObject_value_impl(struct drgn_object *obj)
return NULL; return NULL;
case DRGN_TYPE_STRUCT: case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION: case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
return DrgnObject_compound_value(obj, underlying_type); return DrgnObject_compound_value(obj, underlying_type);
case DRGN_TYPE_ARRAY: case DRGN_TYPE_ARRAY:
return DrgnObject_array_value(obj, underlying_type); return DrgnObject_array_value(obj, underlying_type);
@ -1424,8 +1425,8 @@ static PyObject *DrgnObject_getattro(DrgnObject *self, PyObject *attr_name)
Py_CLEAR(res); Py_CLEAR(res);
if (err->code == DRGN_ERROR_TYPE) { if (err->code == DRGN_ERROR_TYPE) {
/* /*
* If the object isn't a structure or union, raise the * If the object doesn't have a compound type,
* original AttributeError. * raise the original AttributeError.
*/ */
PyErr_Restore(exc_type, exc_value, exc_traceback); PyErr_Restore(exc_type, exc_value, exc_traceback);
return NULL; return NULL;
@ -1616,7 +1617,7 @@ static PyMethodDef DrgnObject_methods[] = {
{"__floor__", (PyCFunction)DrgnObject_floor, METH_NOARGS}, {"__floor__", (PyCFunction)DrgnObject_floor, METH_NOARGS},
{"__ceil__", (PyCFunction)DrgnObject_ceil, METH_NOARGS}, {"__ceil__", (PyCFunction)DrgnObject_ceil, METH_NOARGS},
{"__dir__", (PyCFunction)DrgnObject_dir, 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, {"__format__", (PyCFunction)DrgnObject_format, METH_O,
"Object formatter."}, "Object formatter."},
{}, {},

View File

@ -691,10 +691,9 @@ static int append_field(PyObject *parts, bool *first, const char *format, ...)
_Py_IDENTIFIER(DrgnType_Repr); _Py_IDENTIFIER(DrgnType_Repr);
/* /*
* We only want to print compound types (structure and union types) one level * We only want to print compound types one level deep in order to avoid very
* deep in order to avoid very deep recursion. Return 0 if this is the first * deep recursion. Return 0 if this is the first level, 1 if this is a deeper
* level, 1 if this is a deeper level (and thus we shouldn't print more * level (and thus we shouldn't print more members), and -1 on error.
* members), and -1 on error.
*/ */
static int DrgnType_ReprEnter(DrgnType *self) static int DrgnType_ReprEnter(DrgnType *self)
{ {
@ -1250,7 +1249,8 @@ out:
static DrgnType *compound_type(PyObject *tag_obj, PyObject *size_obj, static DrgnType *compound_type(PyObject *tag_obj, PyObject *size_obj,
PyObject *members_obj, PyObject *members_obj,
enum drgn_qualifiers qualifiers, bool is_struct) enum drgn_qualifiers qualifiers,
enum drgn_type_kind kind)
{ {
const char *tag; const char *tag;
DrgnType *type_obj = NULL; DrgnType *type_obj = NULL;
@ -1268,7 +1268,7 @@ static DrgnType *compound_type(PyObject *tag_obj, PyObject *size_obj,
} else { } else {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"%s_type() tag must be str or None", "%s_type() tag must be str or None",
is_struct ? "struct" : "union"); drgn_type_kind_spelling[kind]);
return NULL; return NULL;
} }
@ -1276,7 +1276,7 @@ static DrgnType *compound_type(PyObject *tag_obj, PyObject *size_obj,
if (size_obj != Py_None) { if (size_obj != Py_None) {
PyErr_Format(PyExc_ValueError, PyErr_Format(PyExc_ValueError,
"incomplete %s type must not have size", "incomplete %s type must not have size",
is_struct ? "structure" : "union"); drgn_type_kind_spelling[kind]);
return NULL; return NULL;
} }
type_obj = DrgnType_new(qualifiers, 0, 0); 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) { if (size_obj == Py_None) {
PyErr_Format(PyExc_ValueError, "%s type must have size", PyErr_Format(PyExc_ValueError, "%s type must have size",
is_struct ? "structure" : "union"); drgn_type_kind_spelling[kind]);
return NULL; return NULL;
} }
@ -1335,17 +1335,35 @@ static DrgnType *compound_type(PyObject *tag_obj, PyObject *size_obj,
goto err; goto err;
if (members_obj == Py_None) { if (members_obj == Py_None) {
if (is_struct) switch (kind) {
case DRGN_TYPE_STRUCT:
drgn_struct_type_init_incomplete(type_obj->type, tag); drgn_struct_type_init_incomplete(type_obj->type, tag);
else break;
case DRGN_TYPE_UNION:
drgn_union_type_init_incomplete(type_obj->type, tag); 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 { } else {
if (is_struct) { switch (kind) {
case DRGN_TYPE_STRUCT:
drgn_struct_type_init(type_obj->type, tag, size, drgn_struct_type_init(type_obj->type, tag, size,
num_members); num_members);
} else { break;
case DRGN_TYPE_UNION:
drgn_union_type_init(type_obj->type, tag, size, drgn_union_type_init(type_obj->type, tag, size,
num_members); 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; return type_obj;
@ -1371,7 +1389,8 @@ DrgnType *struct_type(PyObject *self, PyObject *args, PyObject *kwds)
&qualifiers)) &qualifiers))
return NULL; 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) DrgnType *union_type(PyObject *self, PyObject *args, PyObject *kwds)
@ -1388,7 +1407,26 @@ DrgnType *union_type(PyObject *self, PyObject *args, PyObject *kwds)
&qualifiers)) &qualifiers))
return NULL; 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, static int unpack_enumerator(DrgnType *type_obj, PyObject *enumerators_seq,

View File

@ -14,6 +14,7 @@ const char * const drgn_type_kind_spelling[] = {
[DRGN_TYPE_COMPLEX] = "complex", [DRGN_TYPE_COMPLEX] = "complex",
[DRGN_TYPE_STRUCT] = "struct", [DRGN_TYPE_STRUCT] = "struct",
[DRGN_TYPE_UNION] = "union", [DRGN_TYPE_UNION] = "union",
[DRGN_TYPE_CLASS] = "class",
[DRGN_TYPE_ENUM] = "enum", [DRGN_TYPE_ENUM] = "enum",
[DRGN_TYPE_TYPEDEF] = "typedef", [DRGN_TYPE_TYPEDEF] = "typedef",
[DRGN_TYPE_POINTER] = "pointer", [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; 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, void drgn_enum_type_init(struct drgn_type *type, const char *tag,
struct drgn_type *compatible_type, struct drgn_type *compatible_type,
size_t num_enumerators) size_t num_enumerators)
@ -717,8 +739,14 @@ LIBDRGN_PUBLIC struct drgn_error *drgn_type_sizeof(struct drgn_type *type,
uint64_t *ret) uint64_t *ret)
{ {
struct drgn_error *err; 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_INT:
case DRGN_TYPE_BOOL: case DRGN_TYPE_BOOL:
case DRGN_TYPE_FLOAT: 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); *ret = drgn_type_size(type);
return NULL; return NULL;
case DRGN_TYPE_STRUCT: 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: case DRGN_TYPE_UNION:
if (!drgn_type_is_complete(type)) { case DRGN_TYPE_CLASS:
return drgn_error_create(DRGN_ERROR_TYPE,
"cannot get size of incomplete union type");
}
*ret = drgn_type_size(type); *ret = drgn_type_size(type);
return NULL; return NULL;
case DRGN_TYPE_ENUM: 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: case DRGN_TYPE_TYPEDEF:
return drgn_type_sizeof(drgn_type_type(type).type, ret); return drgn_type_sizeof(drgn_type_type(type).type, ret);
case DRGN_TYPE_ARRAY: 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); err = drgn_type_sizeof(drgn_type_type(type).type, ret);
if (err) if (err)
return err; return err;
@ -801,6 +810,7 @@ enum drgn_object_kind drgn_type_object_kind(struct drgn_type *type)
return DRGN_OBJECT_BUFFER; return DRGN_OBJECT_BUFFER;
case DRGN_TYPE_STRUCT: case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION: case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
case DRGN_TYPE_ARRAY: case DRGN_TYPE_ARRAY:
return (drgn_type_is_complete(type) ? DRGN_OBJECT_BUFFER : return (drgn_type_is_complete(type) ? DRGN_OBJECT_BUFFER :
DRGN_OBJECT_INCOMPLETE_BUFFER); DRGN_OBJECT_INCOMPLETE_BUFFER);
@ -849,6 +859,9 @@ struct drgn_error *drgn_error_incomplete_type(const char *format,
case DRGN_TYPE_UNION: case DRGN_TYPE_UNION:
return drgn_error_format(DRGN_ERROR_TYPE, format, return drgn_error_format(DRGN_ERROR_TYPE, format,
"incomplete union"); "incomplete union");
case DRGN_TYPE_CLASS:
return drgn_error_format(DRGN_ERROR_TYPE, format,
"incomplete class");
case DRGN_TYPE_ENUM: case DRGN_TYPE_ENUM:
return drgn_error_format(DRGN_ERROR_TYPE, format, return drgn_error_format(DRGN_ERROR_TYPE, format,
"incomplete enumerated"); "incomplete enumerated");

View File

@ -34,7 +34,7 @@
* Lazily-evaluated types. * Lazily-evaluated types.
* *
* The graph of types in a program can be very deep (and often cyclical), so * 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. * 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); 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. * 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). * 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 * This may be @c false for structure, union, class, and enum types. Otherwise,
* always true. * it is always true.
*/ */
static inline bool drgn_type_is_anonymous(struct drgn_type *type) static inline bool drgn_type_is_anonymous(struct drgn_type *type)
{ {
switch (drgn_type_kind(type)) { switch (drgn_type_kind(type)) {
case DRGN_TYPE_STRUCT: case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION: case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
case DRGN_TYPE_ENUM: case DRGN_TYPE_ENUM:
return !drgn_type_tag(type); return !drgn_type_tag(type);
default: default:

View File

@ -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: * 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 * 2. The type hasn't been cached, which means we need to cache it and
* check again. * check again.
* 3. The type has already been cached, which means the member doesn't * 3. The type has already been cached, which means the member doesn't
* exist. * exist.
*/ */
if (!drgn_type_has_members(key.type)) { 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); type);
} }
cached_hp = drgn_type_set_hash(&key.type); cached_hp = drgn_type_set_hash(&key.type);

View File

@ -178,7 +178,8 @@ drgn_type_index_find_primitive(struct drgn_type_index *tindex,
* drgn_language::find_type(). * drgn_language::find_type().
* *
* @param[in] kind Kind of type to find. Must be @ref DRGN_TYPE_STRUCT, @ref * @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 Name of the type.
* @param[in] name_len Length of @p name in bytes. * @param[in] name_len Length of @p name in bytes.
* @param[in] filename See @ref drgn_type_index_find(). * @param[in] filename See @ref drgn_type_index_find().

View File

@ -11,6 +11,7 @@ from drgn import (
Program, Program,
Type, Type,
TypeKind, TypeKind,
class_type,
enum_type, enum_type,
float_type, float_type,
int_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, ( point_type = struct_type('point', 8, (
(int_type('int', 4, True), 'x', 0), (int_type('int', 4, True), 'x', 0),
(int_type('int', 4, True), 'y', 32), (int_type('int', 4, True), 'y', 32),

View File

@ -8,6 +8,7 @@ from drgn import (
Program, Program,
Qualifiers, Qualifiers,
array_type, array_type,
class_type,
complex_type, complex_type,
enum_type, enum_type,
float_type, float_type,
@ -19,7 +20,14 @@ from drgn import (
union_type, union_type,
void_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.dwarf import DW_AT, DW_ATE, DW_FORM, DW_TAG
from tests.dwarfwriter import compile_dwarf, DwarfDie, DwarfAttrib from tests.dwarfwriter import compile_dwarf, DwarfDie, DwarfAttrib
@ -709,6 +717,109 @@ class TestTypes(unittest.TestCase):
self.type_from_dwarf, dies) self.type_from_dwarf, dies)
dies[0].attribs.insert(1, size) 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): def test_lazy_cycle(self):
dies = [ dies = [
DwarfDie( DwarfDie(

View File

@ -3,21 +3,22 @@ import operator
import unittest import unittest
from drgn import ( from drgn import (
Qualifiers,
array_type, array_type,
bool_type, bool_type,
class_type,
complex_type, complex_type,
enum_type, enum_type,
float_type, float_type,
function_type, function_type,
int_type, int_type,
pointer_type, pointer_type,
Qualifiers,
struct_type, struct_type,
typedef_type, typedef_type,
union_type, union_type,
void_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 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, self.assertQualifiedTypeName('union <anonymous>', False, union_type,
None) 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): def test_enum(self):
self.assertQualifiedTypeName('enum color', True, enum_type, 'color', self.assertQualifiedTypeName('enum color', True, enum_type, 'color',
None, None), None, None),
@ -279,6 +286,14 @@ const union foo {
unsigned char a[4]; unsigned char a[4];
}""") }""")
def test_class(self):
self.assertPrettyPrint(coord_type, """\
class coord {
int x;
int y;
int z;
}""")
def test_enum(self): def test_enum(self):
t = enum_type('color', int_type('unsigned int', 4, False), ( t = enum_type('color', int_type('unsigned int', 4, False), (
('RED', 0), ('RED', 0),

View File

@ -26,6 +26,7 @@ from tests import (
MockMemorySegment, MockMemorySegment,
ObjectTestCase, ObjectTestCase,
color_type, color_type,
coord_type,
line_segment_type, line_segment_type,
mock_program, mock_program,
option_type, option_type,
@ -1200,7 +1201,7 @@ class TestCOperators(ObjectTestCase):
r'container_of\(\) argument must be a pointer', r'container_of\(\) argument must be a pointer',
container_of, obj[0], point_type, 'x') 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'), container_of, obj, obj.type_, 'x'),
type_ = struct_type('foo', 16, ( type_ = struct_type('foo', 16, (
@ -1771,7 +1772,7 @@ class TestGenericOperators(ObjectTestCase):
self.assertEqual(obj.y, Object(self.prog, 'int', value=1)) self.assertEqual(obj.y, Object(self.prog, 'int', value=1))
obj = 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') obj.member_, 'x')
self.assertRaisesRegex(AttributeError, 'no attribute', getattr, obj, self.assertRaisesRegex(AttributeError, 'no attribute', getattr, obj,
'x') 'x')

View File

@ -6,6 +6,7 @@ from drgn import (
TypeKind, TypeKind,
array_type, array_type,
bool_type, bool_type,
class_type,
complex_type, complex_type,
enum_type, enum_type,
float_type, float_type,
@ -366,6 +367,144 @@ class TestType(unittest.TestCase):
(int_type('unsigned int', 4, False), 'y', 0, 4), (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): def test_enum(self):
t = enum_type('color', int_type('unsigned int', 4, False), t = enum_type('color', int_type('unsigned int', 4, False),
(('RED', 0), ('GREEN', 1), ('BLUE', 2))) (('RED', 0), ('GREEN', 1), ('BLUE', 2)))