diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 6cf87ec5827..b5df971bbbb 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,28 @@ +2009-05-27 Tom Tromey + Thiago Jung Bauermann + Phil Muldoon + Paul Pluzhnikov + + * python/python.c (_initialize_python): Call + gdbpy_initialize_types. + (GdbMethods): Add "lookup_type". + * python/python-value.c (value_object) : New field. + (valpy_dealloc): Decref type. + (valpy_new): Initialize type. + (valpy_get_type): New function. + (value_to_value_object): Initialize type. + (valpy_cast): New function. + (value_object_getset): Add "type". + (value_object_methods): Add "cast". + * python/python-internal.h (type_to_type_object): Declare. + (type_object_to_type): Likewise. + (gdbpy_initialize_types): Likewise. + (gdbpy_lookup_type): Declare. + * Makefile.in (SUBDIR_PYTHON_OBS): Add python-type.o. + (SUBDIR_PYTHON_SRCS): Add python-type.c. + (python-type.o): New target. + * python/python-type.c: New file. + 2009-05-27 Tom Tromey Thiago Jung Bauermann Phil Muldoon diff --git a/gdb/Makefile.in b/gdb/Makefile.in index df157bf7211..50e4e8dc2d6 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -268,6 +268,7 @@ SUBDIR_PYTHON_OBS = \ python-frame.o \ python-function.o \ python-objfile.o \ + python-type.o \ python-utils.o \ python-value.o SUBDIR_PYTHON_SRCS = \ @@ -276,6 +277,7 @@ SUBDIR_PYTHON_SRCS = \ python/python-frame.c \ python/python-function.c \ python/python-objfile.c \ + python/python-type.c \ python/python-utils.c \ python/python-value.c SUBDIR_PYTHON_DEPS = @@ -1870,6 +1872,10 @@ python-objfile.o: $(srcdir)/python/python-objfile.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-objfile.c $(POSTCOMPILE) +python-type.o: $(srcdir)/python/python-type.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-type.c + $(POSTCOMPILE) + python-utils.o: $(srcdir)/python/python-utils.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-utils.c $(POSTCOMPILE) diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index c5594ff5e0d..167790027c4 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,10 @@ +2009-05-27 Thiago Jung Bauermann + Tom Tromey + + * gdb.texinfo (Types In Python): New node. + (Values From Inferior): "type" is now an attribute. + (Python API): Update. + 2009-05-27 Tom Tromey Thiago Jung Bauermann Phil Muldoon diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index ba06b420c44..5fccb309e57 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -18516,6 +18516,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown. * Exception Handling:: * Auto-loading:: Automatically loading Python code. * Values From Inferior:: +* Types In Python:: Python representation of types. * Commands In Python:: Implementing new commands in Python. * Functions In Python:: Writing new convenience functions. * Objfiles In Python:: Object files. @@ -18701,17 +18702,22 @@ Again, @code{bar} will also be a @code{gdb.Value} object. The following attributes are provided: @table @code -@defmethod Value address +@defivar Value address If this object is addressable, this read-only attribute holds a @code{gdb.Value} object representing the address. Otherwise, this attribute holds @code{None}. -@end defmethod +@end defivar @cindex optimized out value in Python -@defmethod Value is_optimized_out +@defivar Value is_optimized_out This read-only boolean attribute is true if the compiler optimized out this value, thus it is not available for fetching from the inferior. -@end defmethod +@end defivar + +@defivar Value type +The type of this @code{gdb.Value}. The value of this attribute is a +@code{gdb.Type} object. +@end defivar @end table The following methods are provided: @@ -18766,6 +18772,278 @@ argument to Python's @code{string.decode} method. @end defmethod @end table +@node Types In Python +@subsubsection Types In Python +@cindex types in Python +@cindex Python, working with types + +@tindex gdb.Type +@value{GDBN} represents types from the inferior using the class +@code{gdb.Type}. + +The following type-related functions are available in the @code{gdb} +module: + +@findex gdb.lookup_type +@defun lookup_type name [block] +This function looks up a type by name. @var{name} is the name of the +type to look up. It must be a string. + +Ordinarily, this function will return an instance of @code{gdb.Type}. +If the named type cannot be found, it will throw an exception. +@end defun + +An instance of @code{Type} has the following attributes: + +@table @code +@defivar Type code +The type code for this type. The type code will be one of the +@code{TYPE_CODE_} constants defined below. +@end defivar + +@defivar Type sizeof +The size of this type, in target @code{char} units. Usually, a +target's @code{char} type will be an 8-bit byte. However, on some +unusual platforms, this type may have a different size. +@end defivar + +@defivar Type tag +The tag name for this type. The tag name is the name after +@code{struct}, @code{union}, or @code{enum} in C and C@t{++}; not all +languages have this concept. If this type has no tag name, then +@code{None} is returned. +@end defivar +@end table + +The following methods are provided: + +@table @code +@defmethod Type fields +For structure and union types, this method returns the fields. Range +types have two fields, the minimum and maximum values. Enum types +have one field per enum constant. Function and method types have one +field per parameter. The base types of C@t{++} classes are also +represented as fields. If the type has no fields, or does not fit +into one of these categories, an empty sequence will be returned. + +Each field is an object, with some pre-defined attributes: +@table @code +@item bitpos +This attribute is not available for @code{static} fields (as in +C@t{++} or Java). For non-@code{static} fields, the value is the bit +position of the field. + +@item name +The name of the field, or @code{None} for anonymous fields. + +@item artificial +This is @code{True} if the field is artificial, usually meaning that +it was provided by the compiler and not the user. This attribute is +always provided, and is @code{False} if the field is not artificial. + +@item bitsize +If the field is packed, or is a bitfield, then this will have a +non-zero value, which is the size of the field in bits. Otherwise, +this will be zero; in this case the field's size is given by its type. + +@item type +The type of the field. This is usually an instance of @code{Type}, +but it can be @code{None} in some situations. +@end table +@end defmethod + +@defmethod Type const +Return a new @code{gdb.Type} object which represents a +@code{const}-qualified variant of this type. +@end defmethod + +@defmethod Type volatile +Return a new @code{gdb.Type} object which represents a +@code{volatile}-qualified variant of this type. +@end defmethod + +@defmethod Type unqualified +Return a new @code{gdb.Type} object which represents an unqualified +variant of this type. That is, the result is neither @code{const} nor +@code{volatile}. +@end defmethod + +@defmethod Type reference +Return a new @code{gdb.Type} object which represents a reference to this +type. +@end defmethod + +@defmethod Type strip_typedefs +Return a new @code{gdb.Type} that represents the real type, +after removing all layers of typedefs. +@end defmethod + +@defmethod Type target +Return a new @code{gdb.Type} object which represents the target type +of this type. + +For a pointer type, the target type is the type of the pointed-to +object. For an array type (meaning C-like arrays), the target type is +the type of the elements of the array. For a function or method type, +the target type is the type of the return value. For a complex type, +the target type is the type of the elements. For a typedef, the +target type is the aliased type. + +If the type does not have a target, this method will throw an +exception. +@end defmethod + +@defmethod Type template_argument n +If this @code{gdb.Type} is an instantiation of a template, this will +return a new @code{gdb.Type} which represents the type of the +@var{n}th template argument. + +If this @code{gdb.Type} is not a template type, this will throw an +exception. Ordinarily, only C@t{++} code will have template types. + +@var{name} is searched for globally. +@end defmethod +@end table + + +Each type has a code, which indicates what category this type falls +into. The available type categories are represented by constants +defined in the @code{gdb} module: + +@table @code +@findex TYPE_CODE_PTR +@findex gdb.TYPE_CODE_PTR +@item TYPE_CODE_PTR +The type is a pointer. + +@findex TYPE_CODE_ARRAY +@findex gdb.TYPE_CODE_ARRAY +@item TYPE_CODE_ARRAY +The type is an array. + +@findex TYPE_CODE_STRUCT +@findex gdb.TYPE_CODE_STRUCT +@item TYPE_CODE_STRUCT +The type is a structure. + +@findex TYPE_CODE_UNION +@findex gdb.TYPE_CODE_UNION +@item TYPE_CODE_UNION +The type is a union. + +@findex TYPE_CODE_ENUM +@findex gdb.TYPE_CODE_ENUM +@item TYPE_CODE_ENUM +The type is an enum. + +@findex TYPE_CODE_FLAGS +@findex gdb.TYPE_CODE_FLAGS +@item TYPE_CODE_FLAGS +A bit flags type, used for things such as status registers. + +@findex TYPE_CODE_FUNC +@findex gdb.TYPE_CODE_FUNC +@item TYPE_CODE_FUNC +The type is a function. + +@findex TYPE_CODE_INT +@findex gdb.TYPE_CODE_INT +@item TYPE_CODE_INT +The type is an integer type. + +@findex TYPE_CODE_FLT +@findex gdb.TYPE_CODE_FLT +@item TYPE_CODE_FLT +A floating point type. + +@findex TYPE_CODE_VOID +@findex gdb.TYPE_CODE_VOID +@item TYPE_CODE_VOID +The special type @code{void}. + +@findex TYPE_CODE_SET +@findex gdb.TYPE_CODE_SET +@item TYPE_CODE_SET +A Pascal set type. + +@findex TYPE_CODE_RANGE +@findex gdb.TYPE_CODE_RANGE +@item TYPE_CODE_RANGE +A range type, that is, an integer type with bounds. + +@findex TYPE_CODE_STRING +@findex gdb.TYPE_CODE_STRING +@item TYPE_CODE_STRING +A string type. Note that this is only used for certain languages with +language-defined string types; C strings are not represented this way. + +@findex TYPE_CODE_BITSTRING +@findex gdb.TYPE_CODE_BITSTRING +@item TYPE_CODE_BITSTRING +A string of bits. + +@findex TYPE_CODE_ERROR +@findex gdb.TYPE_CODE_ERROR +@item TYPE_CODE_ERROR +An unknown or erroneous type. + +@findex TYPE_CODE_METHOD +@findex gdb.TYPE_CODE_METHOD +@item TYPE_CODE_METHOD +A method type, as found in C@t{++} or Java. + +@findex TYPE_CODE_METHODPTR +@findex gdb.TYPE_CODE_METHODPTR +@item TYPE_CODE_METHODPTR +A pointer-to-member-function. + +@findex TYPE_CODE_MEMBERPTR +@findex gdb.TYPE_CODE_MEMBERPTR +@item TYPE_CODE_MEMBERPTR +A pointer-to-member. + +@findex TYPE_CODE_REF +@findex gdb.TYPE_CODE_REF +@item TYPE_CODE_REF +A reference type. + +@findex TYPE_CODE_CHAR +@findex gdb.TYPE_CODE_CHAR +@item TYPE_CODE_CHAR +A character type. + +@findex TYPE_CODE_BOOL +@findex gdb.TYPE_CODE_BOOL +@item TYPE_CODE_BOOL +A boolean type. + +@findex TYPE_CODE_COMPLEX +@findex gdb.TYPE_CODE_COMPLEX +@item TYPE_CODE_COMPLEX +A complex float type. + +@findex TYPE_CODE_TYPEDEF +@findex gdb.TYPE_CODE_TYPEDEF +@item TYPE_CODE_TYPEDEF +A typedef to some other type. + +@findex TYPE_CODE_NAMESPACE +@findex gdb.TYPE_CODE_NAMESPACE +@item TYPE_CODE_NAMESPACE +A C@t{++} namespace. + +@findex TYPE_CODE_DECFLOAT +@findex gdb.TYPE_CODE_DECFLOAT +@item TYPE_CODE_DECFLOAT +A decimal floating point type. + +@findex TYPE_CODE_INTERNAL_FUNCTION +@findex gdb.TYPE_CODE_INTERNAL_FUNCTION +@item TYPE_CODE_INTERNAL_FUNCTION +A function internal to @value{GDBN}. This is the type used to represent +convenience functions. +@end table + @node Commands In Python @subsubsection Commands In Python diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index f16b509b383..819867f462d 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -66,17 +66,21 @@ extern PyTypeObject value_object_type; PyObject *gdbpy_history (PyObject *self, PyObject *args); PyObject *gdbpy_frame_stop_reason_string (PyObject *, PyObject *); PyObject *gdbpy_selected_frame (PyObject *self, PyObject *args); +PyObject *gdbpy_lookup_type (PyObject *self, PyObject *args, PyObject *kw); PyObject *value_to_value_object (struct value *v); +PyObject *type_to_type_object (struct type *); PyObject *objfile_to_objfile_object (struct objfile *); PyObject *objfpy_get_printers (PyObject *, void *); struct value *convert_value_from_python (PyObject *obj); +struct type *type_object_to_type (PyObject *obj); void gdbpy_initialize_values (void); void gdbpy_initialize_frames (void); void gdbpy_initialize_commands (void); +void gdbpy_initialize_types (void); void gdbpy_initialize_functions (void); void gdbpy_initialize_objfile (void); diff --git a/gdb/python/python-type.c b/gdb/python/python-type.c new file mode 100644 index 00000000000..577ebd2204f --- /dev/null +++ b/gdb/python/python-type.c @@ -0,0 +1,799 @@ +/* Python interface to types. + + Copyright (C) 2008, 2009 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "defs.h" +#include "value.h" +#include "exceptions.h" +#include "python-internal.h" +#include "charset.h" +#include "gdbtypes.h" +#include "cp-support.h" +#include "demangle.h" +#include "objfiles.h" + +typedef struct pyty_type_object +{ + PyObject_HEAD + struct type *type; + + /* If a Type object is associated with an objfile, it is kept on a + doubly-linked list, rooted in the objfile. This lets us copy the + underlying struct type when the objfile is deleted. */ + struct pyty_type_object *prev; + struct pyty_type_object *next; +} type_object; + +static PyTypeObject type_object_type; + +/* A Field object. */ +typedef struct pyty_field_object +{ + PyObject_HEAD + + /* Dictionary holding our attributes. */ + PyObject *dict; +} field_object; + +static PyTypeObject field_object_type; + +/* This is used to initialize various gdb.TYPE_ constants. */ +struct pyty_code +{ + /* The code. */ + enum type_code code; + /* The name. */ + const char *name; +}; + +#define ENTRY(X) { X, #X } + +static struct pyty_code pyty_codes[] = +{ + ENTRY (TYPE_CODE_PTR), + ENTRY (TYPE_CODE_ARRAY), + ENTRY (TYPE_CODE_STRUCT), + ENTRY (TYPE_CODE_UNION), + ENTRY (TYPE_CODE_ENUM), + ENTRY (TYPE_CODE_FLAGS), + ENTRY (TYPE_CODE_FUNC), + ENTRY (TYPE_CODE_INT), + ENTRY (TYPE_CODE_FLT), + ENTRY (TYPE_CODE_VOID), + ENTRY (TYPE_CODE_SET), + ENTRY (TYPE_CODE_RANGE), + ENTRY (TYPE_CODE_STRING), + ENTRY (TYPE_CODE_BITSTRING), + ENTRY (TYPE_CODE_ERROR), + ENTRY (TYPE_CODE_METHOD), + ENTRY (TYPE_CODE_METHODPTR), + ENTRY (TYPE_CODE_MEMBERPTR), + ENTRY (TYPE_CODE_REF), + ENTRY (TYPE_CODE_CHAR), + ENTRY (TYPE_CODE_BOOL), + ENTRY (TYPE_CODE_COMPLEX), + ENTRY (TYPE_CODE_TYPEDEF), + ENTRY (TYPE_CODE_NAMESPACE), + ENTRY (TYPE_CODE_DECFLOAT), + ENTRY (TYPE_CODE_INTERNAL_FUNCTION), + { TYPE_CODE_UNDEF, NULL } +}; + + + +static void +field_dealloc (PyObject *obj) +{ + field_object *f = (field_object *) obj; + Py_XDECREF (f->dict); + f->ob_type->tp_free (obj); +} + +static PyObject * +field_new (void) +{ + field_object *result = PyObject_New (field_object, &field_object_type); + if (result) + { + result->dict = PyDict_New (); + if (!result->dict) + { + Py_DECREF (result); + result = NULL; + } + } + return (PyObject *) result; +} + + + +/* Return the code for this type. */ +static PyObject * +typy_get_code (PyObject *self, void *closure) +{ + struct type *type = ((type_object *) self)->type; + return PyInt_FromLong (TYPE_CODE (type)); +} + +/* Helper function for typy_fields which converts a single field to a + dictionary. Returns NULL on error. */ +static PyObject * +convert_field (struct type *type, int field) +{ + PyObject *result = field_new (); + PyObject *arg; + + if (!result) + return NULL; + + if (!field_is_static (&TYPE_FIELD (type, field))) + { + arg = PyLong_FromLong (TYPE_FIELD_BITPOS (type, field)); + if (!arg) + goto fail; + + if (PyObject_SetAttrString (result, "bitpos", arg) < 0) + goto failarg; + } + + if (TYPE_FIELD_NAME (type, field)) + arg = PyString_FromString (TYPE_FIELD_NAME (type, field)); + else + { + arg = Py_None; + Py_INCREF (arg); + } + if (!arg) + goto fail; + if (PyObject_SetAttrString (result, "name", arg) < 0) + goto failarg; + + arg = TYPE_FIELD_ARTIFICIAL (type, field) ? Py_True : Py_False; + Py_INCREF (arg); + if (PyObject_SetAttrString (result, "artificial", arg) < 0) + goto failarg; + + arg = PyLong_FromLong (TYPE_FIELD_BITSIZE (type, field)); + if (!arg) + goto fail; + if (PyObject_SetAttrString (result, "bitsize", arg) < 0) + goto failarg; + + /* A field can have a NULL type in some situations. */ + if (TYPE_FIELD_TYPE (type, field) == NULL) + { + arg = Py_None; + Py_INCREF (arg); + } + else + arg = type_to_type_object (TYPE_FIELD_TYPE (type, field)); + if (!arg) + goto fail; + if (PyObject_SetAttrString (result, "type", arg) < 0) + goto failarg; + + return result; + + failarg: + Py_DECREF (arg); + fail: + Py_DECREF (result); + return NULL; +} + +/* Return a sequence of all fields. Each field is a dictionary with + some pre-defined keys. */ +static PyObject * +typy_fields (PyObject *self, PyObject *args) +{ + PyObject *result; + int i; + struct type *type = ((type_object *) self)->type; + + /* We would like to make a tuple here, make fields immutable, and + then memoize the result (and perhaps make Field.type() lazy). + However, that can lead to cycles. */ + result = PyList_New (0); + + for (i = 0; i < TYPE_NFIELDS (type); ++i) + { + PyObject *dict = convert_field (type, i); + if (!dict) + { + Py_DECREF (result); + return NULL; + } + if (PyList_Append (result, dict)) + { + Py_DECREF (dict); + Py_DECREF (result); + return NULL; + } + } + + return result; +} + +/* Return the type's tag, or None. */ +static PyObject * +typy_get_tag (PyObject *self, void *closure) +{ + struct type *type = ((type_object *) self)->type; + if (!TYPE_TAG_NAME (type)) + Py_RETURN_NONE; + return PyString_FromString (TYPE_TAG_NAME (type)); +} + +/* Return the type, stripped of typedefs. */ +static PyObject * +typy_strip_typedefs (PyObject *self, PyObject *args) +{ + struct type *type = ((type_object *) self)->type; + + return type_to_type_object (check_typedef (type)); +} + +/* Return a Type object which represents a pointer to SELF. */ +static PyObject * +typy_pointer (PyObject *self, PyObject *args) +{ + struct type *type = ((type_object *) self)->type; + volatile struct gdb_exception except; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + type = lookup_pointer_type (type); + } + GDB_PY_HANDLE_EXCEPTION (except); + + return type_to_type_object (type); +} + +/* Return a Type object which represents a reference to SELF. */ +static PyObject * +typy_reference (PyObject *self, PyObject *args) +{ + struct type *type = ((type_object *) self)->type; + volatile struct gdb_exception except; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + type = lookup_reference_type (type); + } + GDB_PY_HANDLE_EXCEPTION (except); + + return type_to_type_object (type); +} + +/* Return a Type object which represents the target type of SELF. */ +static PyObject * +typy_target (PyObject *self, PyObject *args) +{ + struct type *type = ((type_object *) self)->type; + + if (!TYPE_TARGET_TYPE (type)) + { + PyErr_SetString (PyExc_RuntimeError, "type does not have a target"); + return NULL; + } + + return type_to_type_object (TYPE_TARGET_TYPE (type)); +} + +/* Return a const-qualified type variant. */ +static PyObject * +typy_const (PyObject *self, PyObject *args) +{ + struct type *type = ((type_object *) self)->type; + volatile struct gdb_exception except; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + type = make_cv_type (1, 0, type, NULL); + } + GDB_PY_HANDLE_EXCEPTION (except); + + return type_to_type_object (type); +} + +/* Return a volatile-qualified type variant. */ +static PyObject * +typy_volatile (PyObject *self, PyObject *args) +{ + struct type *type = ((type_object *) self)->type; + volatile struct gdb_exception except; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + type = make_cv_type (0, 1, type, NULL); + } + GDB_PY_HANDLE_EXCEPTION (except); + + return type_to_type_object (type); +} + +/* Return an unqualified type variant. */ +static PyObject * +typy_unqualified (PyObject *self, PyObject *args) +{ + struct type *type = ((type_object *) self)->type; + volatile struct gdb_exception except; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + type = make_cv_type (0, 0, type, NULL); + } + GDB_PY_HANDLE_EXCEPTION (except); + + return type_to_type_object (type); +} + +/* Return the size of the type represented by SELF, in bytes. */ +static PyObject * +typy_get_sizeof (PyObject *self, void *closure) +{ + struct type *type = ((type_object *) self)->type; + volatile struct gdb_exception except; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + check_typedef (type); + } + /* Ignore exceptions. */ + + return PyLong_FromLong (TYPE_LENGTH (type)); +} + +static struct type * +typy_lookup_typename (char *type_name) +{ + struct type *type = NULL; + volatile struct gdb_exception except; + TRY_CATCH (except, RETURN_MASK_ALL) + { + if (!strncmp (type_name, "struct ", 7)) + type = lookup_struct (type_name + 7, NULL); + else if (!strncmp (type_name, "union ", 6)) + type = lookup_union (type_name + 6, NULL); + else if (!strncmp (type_name, "enum ", 5)) + type = lookup_enum (type_name + 5, NULL); + else + type = lookup_typename (type_name, NULL, 0); + } + if (except.reason < 0) + { + PyErr_Format (except.reason == RETURN_QUIT + ? PyExc_KeyboardInterrupt : PyExc_RuntimeError, + "%s", except.message); + return NULL; + } + + return type; +} + +static struct type * +typy_lookup_type (struct demangle_component *demangled) +{ + struct type *type; + char *type_name; + enum demangle_component_type demangled_type; + + /* Save the type: typy_lookup_type() may (indirectly) overwrite + memory pointed by demangled. */ + demangled_type = demangled->type; + + if (demangled_type == DEMANGLE_COMPONENT_POINTER + || demangled_type == DEMANGLE_COMPONENT_REFERENCE + || demangled_type == DEMANGLE_COMPONENT_CONST + || demangled_type == DEMANGLE_COMPONENT_VOLATILE) + { + type = typy_lookup_type (demangled->u.s_binary.left); + if (! type) + return NULL; + + switch (demangled_type) + { + case DEMANGLE_COMPONENT_REFERENCE: + return lookup_reference_type (type); + case DEMANGLE_COMPONENT_POINTER: + return lookup_pointer_type (type); + case DEMANGLE_COMPONENT_CONST: + return make_cv_type (1, 0, type, NULL); + case DEMANGLE_COMPONENT_VOLATILE: + return make_cv_type (0, 1, type, NULL); + } + } + + type_name = cp_comp_to_string (demangled, 10); + type = typy_lookup_typename (type_name); + xfree (type_name); + + return type; +} + +static PyObject * +typy_template_argument (PyObject *self, PyObject *args) +{ + int i, argno, n_pointers; + struct type *type = ((type_object *) self)->type; + struct demangle_component *demangled; + const char *err; + struct type *argtype; + + if (! PyArg_ParseTuple (args, "i", &argno)) + return NULL; + + type = check_typedef (type); + if (TYPE_CODE (type) == TYPE_CODE_REF) + type = check_typedef (TYPE_TARGET_TYPE (type)); + + if (TYPE_NAME (type) == NULL) + { + PyErr_SetString (PyExc_RuntimeError, "null type name"); + return NULL; + } + + /* Note -- this is not thread-safe. */ + demangled = cp_demangled_name_to_comp (TYPE_NAME (type), &err); + if (! demangled) + { + PyErr_SetString (PyExc_RuntimeError, err); + return NULL; + } + + /* Strip off component names. */ + while (demangled->type == DEMANGLE_COMPONENT_QUAL_NAME + || demangled->type == DEMANGLE_COMPONENT_LOCAL_NAME) + demangled = demangled->u.s_binary.right; + + if (demangled->type != DEMANGLE_COMPONENT_TEMPLATE) + { + PyErr_SetString (PyExc_RuntimeError, "type is not a template"); + return NULL; + } + + /* Skip from the template to the arguments. */ + demangled = demangled->u.s_binary.right; + + for (i = 0; demangled && i < argno; ++i) + demangled = demangled->u.s_binary.right; + + if (! demangled) + { + PyErr_Format (PyExc_RuntimeError, "no argument %d in template", + argno); + return NULL; + } + + argtype = typy_lookup_type (demangled->u.s_binary.left); + if (! argtype) + return NULL; + + return type_to_type_object (argtype); +} + +static PyObject * +typy_str (PyObject *self) +{ + volatile struct gdb_exception except; + char *thetype = NULL; + PyObject *result; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + struct cleanup *old_chain; + struct ui_file *stb; + long length; + + stb = mem_fileopen (); + old_chain = make_cleanup_ui_file_delete (stb); + + type_print (type_object_to_type (self), "", stb, -1); + + thetype = ui_file_xstrdup (stb, &length); + do_cleanups (old_chain); + } + if (except.reason < 0) + { + xfree (thetype); + GDB_PY_HANDLE_EXCEPTION (except); + } + + result = PyUnicode_Decode (thetype, strlen (thetype), host_charset (), NULL); + xfree (thetype); + + return result; +} + + + +static const struct objfile_data *typy_objfile_data_key; + +static void +clean_up_objfile_types (struct objfile *objfile, void *datum) +{ + type_object *obj = datum; + htab_t copied_types; + struct cleanup *cleanup; + PyGILState_STATE state; + + /* This prevents another thread from freeing the objects we're + operating on. */ + state = PyGILState_Ensure (); + cleanup = make_cleanup_py_restore_gil (&state); + + copied_types = create_copied_types_hash (objfile); + + while (obj) + { + type_object *next = obj->next; + + htab_empty (copied_types); + + obj->type = copy_type_recursive (objfile, obj->type, copied_types); + + obj->next = NULL; + obj->prev = NULL; + + obj = next; + } + + htab_delete (copied_types); + + do_cleanups (cleanup); +} + +static void +set_type (type_object *obj, struct type *type) +{ + obj->type = type; + obj->prev = NULL; + if (type && TYPE_OBJFILE (type)) + { + struct objfile *objfile = TYPE_OBJFILE (type); + + obj->next = objfile_data (objfile, typy_objfile_data_key); + if (obj->next) + obj->next->prev = obj; + set_objfile_data (objfile, typy_objfile_data_key, obj); + } + else + obj->next = NULL; +} + +static void +typy_dealloc (PyObject *obj) +{ + type_object *type = (type_object *) obj; + + if (type->prev) + type->prev->next = type->next; + else if (type->type && TYPE_OBJFILE (type->type)) + { + /* Must reset head of list. */ + struct objfile *objfile = TYPE_OBJFILE (type->type); + if (objfile) + set_objfile_data (objfile, typy_objfile_data_key, type->next); + } + if (type->next) + type->next->prev = type->prev; + + type->ob_type->tp_free (type); +} + +/* Create a new Type referring to TYPE. */ +PyObject * +type_to_type_object (struct type *type) +{ + type_object *type_obj; + + type_obj = PyObject_New (type_object, &type_object_type); + if (type_obj) + set_type (type_obj, type); + + return (PyObject *) type_obj; +} + +struct type * +type_object_to_type (PyObject *obj) +{ + if (! PyObject_TypeCheck (obj, &type_object_type)) + return NULL; + return ((type_object *) obj)->type; +} + + + +/* Implementation of gdb.lookup_type. */ +PyObject * +gdbpy_lookup_type (PyObject *self, PyObject *args, PyObject *kw) +{ + static char *keywords[] = { "name", NULL }; + char *type_name = NULL; + struct type *type = NULL; + + if (! PyArg_ParseTupleAndKeywords (args, kw, "s", keywords, &type_name)) + return NULL; + + type = typy_lookup_typename (type_name); + if (! type) + return NULL; + + return (PyObject *) type_to_type_object (type); +} + +void +gdbpy_initialize_types (void) +{ + int i; + + typy_objfile_data_key + = register_objfile_data_with_cleanup (clean_up_objfile_types); + + if (PyType_Ready (&type_object_type) < 0) + return; + if (PyType_Ready (&field_object_type) < 0) + return; + + for (i = 0; pyty_codes[i].name; ++i) + { + if (PyModule_AddIntConstant (gdb_module, + /* Cast needed for Python 2.4. */ + (char *) pyty_codes[i].name, + pyty_codes[i].code) < 0) + return; + } + + Py_INCREF (&type_object_type); + PyModule_AddObject (gdb_module, "Type", (PyObject *) &type_object_type); + + Py_INCREF (&field_object_type); + PyModule_AddObject (gdb_module, "Field", (PyObject *) &field_object_type); +} + + + +static PyGetSetDef type_object_getset[] = +{ + { "code", typy_get_code, NULL, + "The code for this type.", NULL }, + { "sizeof", typy_get_sizeof, NULL, + "The size of this type, in bytes.", NULL }, + { "tag", typy_get_tag, NULL, + "The tag name for this type, or None.", NULL }, + { NULL } +}; + +static PyMethodDef type_object_methods[] = +{ + { "const", typy_const, METH_NOARGS, + "const () -> Type\n\ +Return a const variant of this type." }, + { "fields", typy_fields, METH_NOARGS, + "field () -> list\n\ +Return a sequence holding all the fields of this type.\n\ +Each field is a dictionary." }, + { "pointer", typy_pointer, METH_NOARGS, + "pointer () -> Type\n\ +Return a type of pointer to this type." }, + { "reference", typy_reference, METH_NOARGS, + "reference () -> Type\n\ +Return a type of reference to this type." }, + { "strip_typedefs", typy_strip_typedefs, METH_NOARGS, + "strip_typedefs () -> Type\n\ +Return a type formed by stripping this type of all typedefs."}, + { "target", typy_target, METH_NOARGS, + "target () -> Type\n\ +Return the target type of this type." }, + { "template_argument", typy_template_argument, METH_VARARGS, + "template_argument (arg) -> Type\n\ +Return the type of a template argument." }, + { "unqualified", typy_unqualified, METH_NOARGS, + "unqualified () -> Type\n\ +Return a variant of this type without const or volatile attributes." }, + { "volatile", typy_volatile, METH_NOARGS, + "volatile () -> Type\n\ +Return a volatile variant of this type" }, + { NULL } +}; + +static PyTypeObject type_object_type = +{ + PyObject_HEAD_INIT (NULL) + 0, /*ob_size*/ + "gdb.Type", /*tp_name*/ + sizeof (type_object), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + typy_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + typy_str, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /*tp_flags*/ + "GDB type object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + type_object_methods, /* tp_methods */ + 0, /* tp_members */ + type_object_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +static PyTypeObject field_object_type = +{ + PyObject_HEAD_INIT (NULL) + 0, /*ob_size*/ + "gdb.Field", /*tp_name*/ + sizeof (field_object), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + field_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /*tp_flags*/ + "GDB field object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + offsetof (field_object, dict), /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; diff --git a/gdb/python/python-value.c b/gdb/python/python-value.c index 5faa281843b..76f5cde000b 100644 --- a/gdb/python/python-value.c +++ b/gdb/python/python-value.c @@ -59,6 +59,7 @@ typedef struct { PyObject_HEAD struct value *value; PyObject *address; + PyObject *type; } value_object; /* Called by the Python interpreter when deallocating a value object. */ @@ -77,6 +78,11 @@ valpy_dealloc (PyObject *obj) Py_DECREF (self->address); } + if (self->type) + { + Py_DECREF (self->type); + } + self->ob_type->tp_free (self); } @@ -111,6 +117,7 @@ valpy_new (PyTypeObject *subtype, PyObject *args, PyObject *keywords) value_obj->value = value; value_obj->address = NULL; + value_obj->type = NULL; release_value (value); value_prepend_to_list (&values_in_python, value); @@ -161,6 +168,24 @@ valpy_get_address (PyObject *self, void *closure) return val_obj->address; } +/* Return type of the value. */ +static PyObject * +valpy_get_type (PyObject *self, void *closure) +{ + value_object *obj = (value_object *) self; + if (!obj->type) + { + obj->type = type_to_type_object (value_type (obj->value)); + if (!obj->type) + { + obj->type = Py_None; + Py_INCREF (obj->type); + } + } + Py_INCREF (obj->type); + return obj->type; +} + /* Implementation of gdb.Value.string ([encoding] [, errors]) -> string Return Unicode string with value contents. If ENCODING is not given, the string is assumed to be encoded in the target's charset. */ @@ -195,6 +220,34 @@ valpy_string (PyObject *self, PyObject *args, PyObject *kw) return unicode; } +/* Cast a value to a given type. */ +static PyObject * +valpy_cast (PyObject *self, PyObject *args) +{ + PyObject *type_obj; + struct type *type; + struct value *res_val = NULL; /* Initialize to appease gcc warning. */ + volatile struct gdb_exception except; + + if (! PyArg_ParseTuple (args, "O", &type_obj)) + return NULL; + + type = type_object_to_type (type_obj); + if (! type) + { + PyErr_SetString (PyExc_RuntimeError, "argument must be a Type"); + return NULL; + } + + TRY_CATCH (except, RETURN_MASK_ALL) + { + res_val = value_cast (type, ((value_object *) self)->value); + } + GDB_PY_HANDLE_EXCEPTION (except); + + return value_to_value_object (res_val); +} + static Py_ssize_t valpy_length (PyObject *self) { @@ -744,6 +797,7 @@ value_to_value_object (struct value *val) { val_obj->value = val; val_obj->address = NULL; + val_obj->type = NULL; release_value (val); value_prepend_to_list (&values_in_python, val); } @@ -855,16 +909,20 @@ gdbpy_initialize_values (void) values_in_python = NULL; } + + static PyGetSetDef value_object_getset[] = { { "address", valpy_get_address, NULL, "The address of the value.", NULL }, { "is_optimized_out", valpy_get_is_optimized_out, NULL, "Boolean telling whether the value is optimized out (i.e., not available).", NULL }, + { "type", valpy_get_type, NULL, "Type of the value.", NULL }, {NULL} /* Sentinel */ }; static PyMethodDef value_object_methods[] = { + { "cast", valpy_cast, METH_VARARGS, "Cast the value to the supplied type." }, { "dereference", valpy_dereference, METH_NOARGS, "Dereferences the value." }, { "string", (PyCFunction) valpy_string, METH_VARARGS | METH_KEYWORDS, "string ([encoding] [, errors]) -> string\n\ diff --git a/gdb/python/python.c b/gdb/python/python.c index 78a23c7d716..701a931e544 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -552,6 +552,7 @@ Enables or disables auto-loading of Python code when an object is opened."), gdbpy_initialize_frames (); gdbpy_initialize_commands (); gdbpy_initialize_functions (); + gdbpy_initialize_types (); gdbpy_initialize_objfile (); PyRun_SimpleString ("import gdb"); @@ -618,6 +619,11 @@ Return the selected frame object." }, "stop_reason_string (Integer) -> String.\n\ Return a string explaining unwind stop reason." }, + { "lookup_type", (PyCFunction) gdbpy_lookup_type, + METH_VARARGS | METH_KEYWORDS, + "lookup_type (name [, block]) -> type\n\ +Return a Type corresponding to the given name." }, + { "write", gdbpy_write, METH_VARARGS, "Write a string using gdb's filtered stream." }, { "flush", gdbpy_flush, METH_NOARGS, diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index ff1cfea2525..818b2f75775 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,18 @@ +2009-05-27 Thiago Jung Bauermann + Tom Tromey + Pedro Alves + Paul Pluzhnikov + + * gdb.python/python-template.exp: New file. + * gdb.python/python-template.cc: New file. + * gdb.python/python.exp (gdb_py_test_multiple): Add two objfile + tests. + * gdb.python/python-value.exp (py_objfile_tests): New proc. + Call it. + (test_value_after_death): New proc. + * gdb.python/python-value.c (PTR): New typedef. + (main): New variable 'x'. + 2009-05-27 Tom Tromey * gdb.python/python.exp (gdb_py_test_multiple): Add two objfile diff --git a/gdb/testsuite/gdb.python/python-template.cc b/gdb/testsuite/gdb.python/python-template.cc new file mode 100644 index 00000000000..bd6a212e70d --- /dev/null +++ b/gdb/testsuite/gdb.python/python-template.cc @@ -0,0 +1,30 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2008 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +template +struct Foo { +}; + +#ifndef TYPE +#define TYPE int +#endif + +int main() +{ + Foo foo; + return 0; // break here +} diff --git a/gdb/testsuite/gdb.python/python-template.exp b/gdb/testsuite/gdb.python/python-template.exp new file mode 100644 index 00000000000..1ace5d6e2fa --- /dev/null +++ b/gdb/testsuite/gdb.python/python-template.exp @@ -0,0 +1,75 @@ +# Copyright (C) 2008, 2009 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This file is part of the GDB testsuite. It tests the mechanism +# exposing values to Python. + +if $tracelevel then { + strace $tracelevel +} + +set testfile "python-template" +set srcfile ${testfile}.cc +set binfile ${objdir}/${subdir}/${testfile} +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable \ + {debug c++}] != "" } { + untested "Couldn't compile ${srcfile}" + return -1 +} + +# Start with a fresh gdb. + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir + +gdb_test_multiple "python print 23" "verify python support" { + -re "not supported.*$gdb_prompt $" { + unsupported "python support is disabled" + return -1 + } + -re "$gdb_prompt $" {} +} + +proc test_template_arg {type} { + global testfile srcdir subdir srcfile binfile + if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ + executable \ + [list debug c++ additional_flags="-DTYPE=$type"]] != "" } { + untested $type + return -1 + } + gdb_load ${binfile} + if ![runto_main ] then { + perror "couldn't run to breakpoint" + return + } + # There is no executable code in main(), so we are where we want to be + gdb_test "print foo" "" + gdb_test "python foo = gdb.history(0)" "" + + # Replace '*' with '\*' in regex. + regsub -all {\*} $type {\*} t + gdb_test "python print foo.type.template_argument(0)" $t $type +} + +test_template_arg "const int" +test_template_arg "volatile int" +test_template_arg "const int &" +test_template_arg "volatile int &" +test_template_arg "volatile int * const" +test_template_arg "volatile int * const *" +test_template_arg "const int * volatile" +test_template_arg "const int * volatile * const * volatile *" diff --git a/gdb/testsuite/gdb.python/python-value.c b/gdb/testsuite/gdb.python/python-value.c index 17e5c622a22..092c5207433 100644 --- a/gdb/testsuite/gdb.python/python-value.c +++ b/gdb/testsuite/gdb.python/python-value.c @@ -33,19 +33,21 @@ enum e TWO = 2 }; +typedef struct s *PTR; + enum e evalue = TWO; int main (int argc, char *argv[]) { + char *cp = argv[0]; /* Prevent gcc from optimizing argv[] out. */ struct s s; union u u; + PTR x = &s; s.a = 3; s.b = 5; u.a = 7; - argv[0][0] = 'a'; /* Just to avoid getting argv optimized out. */ - return 0; /* break to inspect struct and union */ } diff --git a/gdb/testsuite/gdb.python/python-value.exp b/gdb/testsuite/gdb.python/python-value.exp index 93e9d893d0c..20333f6cd67 100644 --- a/gdb/testsuite/gdb.python/python-value.exp +++ b/gdb/testsuite/gdb.python/python-value.exp @@ -246,6 +246,33 @@ proc test_objfiles {} { "pretty_printers attribute must be a list.*Error while executing Python code." } +proc test_value_after_death {} { + # Construct a type while the inferior is still running. + gdb_py_test_silent_cmd "python ptrtype = gdb.lookup_type('PTR')" \ + "create PTR type" 1 + + # Kill the inferior and remove the symbols. + gdb_test "kill" "" "kill the inferior" \ + "Kill the program being debugged. .y or n. $" \ + "y" + gdb_test "file" "" "Discard the symbols" \ + "Discard symbol table from.*y or n. $" \ + "y" + + # Now create a value using that type. Relies on arg0, created by + # test_value_in_inferior. + gdb_py_test_silent_cmd "python castval = arg0.cast(ptrtype.pointer())" \ + "cast arg0 to PTR" 1 + + # Make sure the type is deleted. + gdb_py_test_silent_cmd "python ptrtype = None" \ + "delete PTR type" 1 + + # Now see if the value's type is still valid. + gdb_test "python print castval.type" "PTR ." \ + "print value's type" +} + # Start with a fresh gdb. gdb_exit @@ -275,3 +302,4 @@ if ![runto_main] then { } test_value_in_inferior +test_value_after_death