@@ -432,6 +432,8 @@ SUBDIR_PYTHON_SRCS = \
python/py-threadevent.c \
python/py-tui.c \
python/py-type.c \
+ python/py-type-init.c \
+ python/py-float-format.c \
python/py-unwind.c \
python/py-utils.c \
python/py-value.c \
@@ -87,6 +87,22 @@ show remote thread-options-packet
** New function gdb.interrupt(), that interrupts GDB as if the user
typed control-c.
+ ** Functions that allow creation of instances of gdb.Type, and a new
+ class gdb.FloatFormat that may be used to create floating point
+ types. The functions that allow new type creation are:
+ - gdb.init_type: Create a new type given a type code.
+ - gdb.init_integer_type: Create a new integer type.
+ - gdb.init_character_type: Create a new character type.
+ - gdb.init_boolean_type: Create a new boolean type.
+ - gdb.init_float_type: Create a new floating point type.
+ - gdb.init_decfloat_type: Create a new decimal floating point type.
+ - gdb.can_create_complex_type: Whether a type can be used to create a
+ new complex type.
+ - gdb.init_complex_type: Create a new complex type.
+ - gdb.init_pointer_type: Create a new pointer type.
+ * This allows creating pointers of arbitrary size.
+ - gdb.init_fixed_point_type: Create a new fixed point type.
+
* Debugger Adapter Protocol changes
** GDB now emits the "process" event.
@@ -1743,6 +1743,167 @@ A Fortran namelist.
Further support for types is provided in the @code{gdb.types}
Python module (@pxref{gdb.types}).
+
+
+@node Creating Types In Python
+@subsubsection Creating Types In Python
+@cindex creating types in Python
+@cindex Python, working with types
+
+@value{GDBN} allows creation of new types from Python extensions.
+
+The following functions available in the @code{gdb} module create
+new types.
+
+They all return an instance of @code{gdb.Type}, and will throw an
+exception in case of an error, unless stated otherwise. Arguments that
+have the same name behave the same for all functions.
+
+@findex gdb.init_type
+@defun gdb.init_type (owner, type_code, bit_size, name)
+This function creates a new @code{gdb.Type} instance corresponding to a
+type owned by the given @var{owner}, with the given @var{type_code},
+@var{name} and size.
+
+@var{owner} must be a reference to either a @code{gdb.Objfile} or a
+@code{gdb.Architecture} object. These correspond to objfile and
+architecture-owned types, respectively.
+
+@var{type_code} is one of the @code{TYPE_CODE_} constants defined in
+@ref{Types In Python}.
+
+@var{bit_size} is the size of instances of the newly created type, in
+bits. Currently, accepted values are limited to multiples of 8.
+@end defun
+
+@findex gdb.init_integer_type
+@defun gdb.init_integer_type (owner, bit_size, unsigned, name)
+This function creates a new @code{gdb.Type} instance corresponding to an
+integer type owned by the given @var{owner}, with the given
+@var{name}, size and signedness.
+
+@var{unsigned} is a boolean indicating whether the type corresponds to
+a signed or unsigned value.
+
+@end defun
+
+@findex gdb.init_character_type
+@defun gdb.init_character_type (owner, bit_size, unsigned, name)
+This function creates a new @code{gdb.Type} instance corresponding to a
+character type owned by the given @var{owner}, with the given
+@var{name}, size and signedness.
+
+This function
+@end defun
+
+@findex gdb.init_boolean_type
+@defun gdb.init_boolean_type (owner, bit_size, unsigned, name)
+This function creates a new @code{gdb.Type} instance corresponding to a
+boolean type owned by the given @var{owner}, with the given
+@var{name}, size and signedness.
+@end defun
+
+@findex gdb.init_float_type
+@defun gdb.init_float_type (owner, format, name)
+This function creates a new @code{gdb.Type} instance corresponding to a
+floating point type owned by the given @var{owner}, with the given
+@var{name} and @var{format}.
+
+@var{format} is an reference to a @code{gdb.FloatFormat} object, as
+described below.
+@end defun
+
+@findex gdb.init_decfloat_type
+@defun gdb.init_decfloat_type (owner, bit_size, name)
+This function creates a new @code{gdb.Type} instance corresponding to a
+decimal floating point type owned by the given @var{owner}, with the
+given @var{name} and size.
+@end defun
+
+@findex gdb.can_create_complex_type
+@defun gdb.can_create_complex_type (type)
+This function returns a boolean indicating whether @var{type} can be
+used to create a new complex type using the @code{gdb.init_complex_type}
+function.
+@end defun
+
+@findex gdb.init_complex_type
+@defun gdb.init_complex_type (type, name)
+This function creates a new @code{gdb.Type} instance corresponding to a
+complex type with the given @var{name} based on the given base
+@var{type}.
+
+The newly created type will be owned by the same object as the base
+type that was used to create it.
+@end defun
+
+@findex gdb.init_pointer_type
+@defun gdb.init_pointer_type (owner, target, bit_size, name)
+This function creates a new @code{gdb.Type} instance corresponding to a
+pointer type that points to @var{target} and is owned by the given
+@var{owner}, with the given @var{name} and size.
+
+@var{target} is a @code{gdb.Type} object, corresponding to the type
+that will be pointed to by the newly created pointer type.
+@end defun
+
+@findex gdb.init_fixed_point_type
+@defun gdb.init_fixed_point_type (owner, bit_size, unsigned, name)
+This function creates a new @code{gdb.Type} instance corresponding to a
+fixed point type owned by the given @var{owner}, with the given
+@var{name}, size and signedness.
+@end defun
+
+When creating a floating point type through @code{gdb.init_float_type},
+one has to use a @code{gdb.FloatFormat} object. These objects may be
+created with no arguments, and the following attributes may be used to
+defined the format of the desired floating point format:
+
+@defvar FloatFormat.totalsize
+The size of the floating point number, in bits. Currently, accepted
+values are limited to multiples of 8.
+@end defvar
+
+@defvar FloatFormat.sign_start
+The bit offset of the sign bit.
+@end defvar
+
+@defvar FloatFormt.exp_start
+The bit offset of the start of the exponent.
+@end defvar
+
+@defvar FloatFormat.exp_len
+The size of the exponent, in bits.
+@end defvar
+
+@defvar FloatFormat.exp_bias
+Bias added to the written exponent to form the biased exponent.
+@end defvar
+
+@defvar FloatFormat.exp_nan
+Exponent value which indicates NaN.
+@end defvar
+
+@defvar FloatFormat.man_start
+The bit offset of the start of the mantissa.
+@end defvar
+
+@defvar FloatFormat.man_len
+The size of the mantissa, in bits.
+@end defvar
+
+@defvar FloatFormat.intbit
+This is a boolean values that indicates whether the integer bit is part
+of the value or if it is determined implicitly. A value of true
+indicates the former, while a value of false indicates the latter.
+@end defvar
+
+@defvar FloatFormat.name
+The name of the float format. Used internally, for debugging purposes.
+@end defvar
+
+
+
@node Pretty Printing API
@subsubsection Pretty Printing API
@cindex python pretty printing api
new file mode 100644
@@ -0,0 +1,307 @@
+/* Accessibility of float format controls from inside the Python API
+
+ Copyright (C) 2008-2023 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 <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "python-internal.h"
+#include "floatformat.h"
+
+/* Structure backing the float format Python interface. */
+
+struct float_format_object
+{
+ PyObject_HEAD
+ struct floatformat format;
+
+ struct floatformat *float_format ()
+ {
+ return &this->format;
+ }
+};
+
+/* Initializes the float format type and registers it with the Python
+ * interpreter. */
+
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
+gdbpy_initialize_float_format (void)
+{
+ if (PyType_Ready (&float_format_object_type) < 0)
+ return -1;
+
+ if (gdb_pymodule_addobject (gdb_module, "FloatFormat",
+ (PyObject *) &float_format_object_type) < 0)
+ return -1;
+
+ return 0;
+}
+
+GDBPY_INITIALIZE_FILE (gdbpy_initialize_float_format);
+
+/* Creates a function that gets the value of a field of a given name from the
+ * underliying float_format structure in the Python object. */
+
+#define INSTANCE_FIELD_GETTER(getter_name, field_name, field_type, field_conv)\
+ static PyObject * \
+ getter_name (PyObject *self, void *closure) \
+ { \
+ float_format_object *ff = (float_format_object*) self; \
+ field_type value = ff->float_format ()->field_name; \
+ return field_conv (value); \
+ }
+
+/* Creates a function that sets the value of a field of a given name from the
+ * underliying float_format structure in the Python object. */
+
+#define INSTANCE_FIELD_SETTER(setter_name, field_name, field_type, field_conv)\
+ static int \
+ setter_name (PyObject *self, PyObject* value, void *closure) \
+ { \
+ field_type native_value; \
+ if (!field_conv (value, &native_value)) \
+ return -1; \
+ float_format_object *ff = (float_format_object*) self; \
+ ff->float_format ()->field_name = native_value; \
+ return 0; \
+ }
+
+/* Converts from the intbit enum to a Python boolean. */
+
+static PyObject *
+intbit_to_py (enum floatformat_intbit intbit)
+{
+ gdb_assert (intbit == floatformat_intbit_yes
+ || intbit == floatformat_intbit_no);
+
+ if (intbit == floatformat_intbit_no)
+ Py_RETURN_FALSE;
+ else
+ Py_RETURN_TRUE;
+}
+
+/* Converts from a Python boolean to the intbit enum. */
+
+static bool
+py_to_intbit (PyObject *object, enum floatformat_intbit *intbit)
+{
+ if (!PyObject_IsInstance (object, (PyObject*) &PyBool_Type))
+ {
+ PyErr_SetString (PyExc_TypeError, "intbit must be True or False");
+ return false;
+ }
+
+ *intbit = PyObject_IsTrue (object) ? floatformat_intbit_yes
+ : floatformat_intbit_no;
+
+ return true;
+}
+
+/* Converts from a Python integer to a unsigned integer. */
+
+static bool
+py_to_unsigned_int (PyObject *object, unsigned int *val)
+{
+ if (!PyObject_IsInstance (object, (PyObject*) &PyLong_Type))
+ {
+ PyErr_SetString (PyExc_TypeError, "value must be an integer");
+ return false;
+ }
+
+ long native_val = PyLong_AsLong (object);
+ if (native_val > (long) UINT_MAX)
+ {
+ PyErr_SetString (PyExc_ValueError, "value is too large");
+ return false;
+ }
+ if (native_val < 0)
+ {
+ PyErr_SetString (PyExc_ValueError,
+ "value must not be smaller than zero");
+ return false;
+ }
+
+ *val = (unsigned int) native_val;
+ return true;
+}
+
+/* Converts from a Python integer to a signed integer. */
+
+static bool
+py_to_int(PyObject *object, int *val)
+{
+ if(!PyObject_IsInstance(object, (PyObject*)&PyLong_Type))
+ {
+ PyErr_SetString(PyExc_TypeError, "value must be an integer");
+ return false;
+ }
+
+ long native_val = PyLong_AsLong(object);
+ if(native_val > (long)INT_MAX)
+ {
+ PyErr_SetString(PyExc_ValueError, "value is too large");
+ return false;
+ }
+
+ *val = (int)native_val;
+ return true;
+}
+
+/* Instantiate functions for all of the float format fields we'd like to be
+ * able to read and change from our Python object. These will be used later to
+ * define `getset` entries for them. */
+
+INSTANCE_FIELD_GETTER (ffpy_get_totalsize, totalsize,
+ unsigned int, PyLong_FromLong)
+INSTANCE_FIELD_GETTER (ffpy_get_sign_start, sign_start,
+ unsigned int, PyLong_FromLong)
+INSTANCE_FIELD_GETTER (ffpy_get_exp_start, exp_start,
+ unsigned int, PyLong_FromLong)
+INSTANCE_FIELD_GETTER (ffpy_get_exp_len, exp_len,
+ unsigned int, PyLong_FromLong)
+INSTANCE_FIELD_GETTER (ffpy_get_exp_bias, exp_bias, int, PyLong_FromLong)
+INSTANCE_FIELD_GETTER (ffpy_get_exp_nan, exp_nan,
+ unsigned int, PyLong_FromLong)
+INSTANCE_FIELD_GETTER (ffpy_get_man_start, man_start,
+ unsigned int, PyLong_FromLong)
+INSTANCE_FIELD_GETTER (ffpy_get_man_len, man_len,
+ unsigned int, PyLong_FromLong)
+INSTANCE_FIELD_GETTER (ffpy_get_intbit, intbit,
+ enum floatformat_intbit, intbit_to_py)
+INSTANCE_FIELD_GETTER (ffpy_get_name, name,
+ const char *, PyUnicode_FromString)
+
+INSTANCE_FIELD_SETTER (ffpy_set_totalsize, totalsize,
+ unsigned int, py_to_unsigned_int)
+INSTANCE_FIELD_SETTER (ffpy_set_sign_start, sign_start,
+ unsigned int, py_to_unsigned_int)
+INSTANCE_FIELD_SETTER (ffpy_set_exp_start, exp_start,
+ unsigned int, py_to_unsigned_int)
+INSTANCE_FIELD_SETTER (ffpy_set_exp_len, exp_len,
+ unsigned int, py_to_unsigned_int)
+INSTANCE_FIELD_SETTER (ffpy_set_exp_bias, exp_bias, int, py_to_int)
+INSTANCE_FIELD_SETTER (ffpy_set_exp_nan, exp_nan,
+ unsigned int, py_to_unsigned_int)
+INSTANCE_FIELD_SETTER (ffpy_set_man_start, man_start,
+ unsigned int, py_to_unsigned_int)
+INSTANCE_FIELD_SETTER (ffpy_set_man_len, man_len,
+ unsigned int, py_to_unsigned_int)
+INSTANCE_FIELD_SETTER (ffpy_set_intbit, intbit,
+ enum floatformat_intbit, py_to_intbit)
+
+/* Makes sure float formats created from Python always test as valid. */
+
+static int
+ffpy_always_valid (const struct floatformat *fmt ATTRIBUTE_UNUSED,
+ const void *from ATTRIBUTE_UNUSED)
+{
+ return 1;
+}
+
+/* Initializes new float format objects. */
+
+static int
+ffpy_init (PyObject *self,
+ PyObject *args ATTRIBUTE_UNUSED,
+ PyObject *kwds ATTRIBUTE_UNUSED)
+{
+ auto ff = (float_format_object*) self;
+ ff->format = floatformat ();
+ ff->float_format ()->name = "";
+ ff->float_format ()->is_valid = ffpy_always_valid;
+ return 0;
+}
+
+/* See python/python-internal.h. */
+
+struct floatformat *
+float_format_object_as_float_format (PyObject *self)
+{
+ if (!PyObject_TypeCheck (self, &float_format_object_type))
+ {
+ PyErr_SetString(PyExc_TypeError, "expected gdb.FloatFormat");
+ return nullptr;
+ }
+ return ((float_format_object*) self)->float_format ();
+}
+
+static gdb_PyGetSetDef float_format_object_getset[] =
+{
+ { "totalsize", ffpy_get_totalsize, ffpy_set_totalsize,
+ "The total size of the floating point number, in bits.", nullptr },
+ { "sign_start", ffpy_get_sign_start, ffpy_set_sign_start,
+ "The bit offset of the sign bit.", nullptr },
+ { "exp_start", ffpy_get_exp_start, ffpy_set_exp_start,
+ "The bit offset of the start of the exponent.", nullptr },
+ { "exp_len", ffpy_get_exp_len, ffpy_set_exp_len,
+ "The size of the exponent, in bits.", nullptr },
+ { "exp_bias", ffpy_get_exp_bias, ffpy_set_exp_bias,
+ "Bias added to the written exponent to form the biased exponent.",
+ nullptr },
+ { "exp_nan", ffpy_get_exp_nan, ffpy_set_exp_nan,
+ "Exponent value which indicates NaN.", nullptr },
+ { "man_start", ffpy_get_man_start, ffpy_set_man_start,
+ "The bit offset of the start of the mantissa.", nullptr },
+ { "man_len", ffpy_get_man_len, ffpy_set_man_len,
+ "The size of the mantissa, in bits.", nullptr },
+ { "intbit", ffpy_get_intbit, ffpy_set_intbit,
+ "Is the integer bit explicit or implicit?", nullptr },
+ { "name", ffpy_get_name, nullptr,
+ "Internal name for debugging.", nullptr },
+ { nullptr }
+};
+
+PyTypeObject float_format_object_type =
+{
+ PyVarObject_HEAD_INIT (NULL, 0)
+ "gdb.FloatFormat", /*tp_name*/
+ sizeof (float_format_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ nullptr, /*tp_dealloc*/
+ 0, /*tp_print*/
+ nullptr, /*tp_getattr*/
+ nullptr, /*tp_setattr*/
+ nullptr, /*tp_compare*/
+ nullptr, /*tp_repr*/
+ nullptr, /*tp_as_number*/
+ nullptr, /*tp_as_sequence*/
+ nullptr, /*tp_as_mapping*/
+ nullptr, /*tp_hash */
+ nullptr, /*tp_call*/
+ nullptr, /*tp_str*/
+ nullptr, /*tp_getattro*/
+ nullptr, /*tp_setattro*/
+ nullptr, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "GDB float format object", /* tp_doc */
+ nullptr, /* tp_traverse */
+ nullptr, /* tp_clear */
+ nullptr, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ nullptr, /* tp_iter */
+ nullptr, /* tp_iternext */
+ nullptr, /* tp_methods */
+ nullptr, /* tp_members */
+ float_format_object_getset, /* tp_getset */
+ nullptr, /* tp_base */
+ nullptr, /* tp_dict */
+ nullptr, /* tp_descr_get */
+ nullptr, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ ffpy_init, /* tp_init */
+ nullptr, /* tp_alloc */
+ PyType_GenericNew, /* tp_new */
+};
@@ -705,6 +705,23 @@ objfile_to_objfile_object (struct objfile *objfile)
return gdbpy_ref<>::new_reference (result);
}
+/* See python/python-internal.h. */
+
+struct objfile *
+objfile_object_to_objfile (PyObject *self)
+{
+ if (!PyObject_TypeCheck (self, &objfile_object_type))
+ {
+ PyErr_SetString(PyExc_TypeError, "expected gdb.Objfile");
+ return nullptr;
+ }
+
+ auto objfile_object = (struct objfile_object*) self;
+ OBJFPY_REQUIRE_VALID (objfile_object);
+
+ return objfile_object->objfile;
+}
+
static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
gdbpy_initialize_objfile (void)
{
new file mode 100644
@@ -0,0 +1,520 @@
+/* Functionality for creating new types accessible from python.
+
+ Copyright (C) 2008-2023 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 <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "python-internal.h"
+#include "gdbtypes.h"
+#include "floatformat.h"
+#include "objfiles.h"
+#include "gdbsupport/gdb_obstack.h"
+
+
+/* An abstraction covering the objects types that can own a type object. */
+
+class type_storage_owner
+{
+public:
+ /* Creates a new type owner from the given python object. If the object is
+ * of a type that is not supported, the newly created instance will be
+ * marked as invalid and nothing should be done with it. */
+
+ type_storage_owner (PyObject *owner)
+ {
+ if (gdbpy_is_architecture (owner))
+ {
+ this->kind = owner_kind::arch;
+ this->owner.arch = arch_object_to_gdbarch (owner);
+ return;
+ }
+
+ this->kind = owner_kind::objfile;
+ this->owner.objfile = objfile_object_to_objfile (owner);
+ if (this->owner.objfile != nullptr)
+ return;
+
+ this->kind = owner_kind::none;
+ PyErr_SetString(PyExc_TypeError, "unsupported owner type");
+ }
+
+ /* Whether the owner is valid. An owner may not be valid if the type that
+ * was used to create it is not known. Operations must only be done on valid
+ * instances of this class. */
+
+ bool valid ()
+ {
+ return this->kind != owner_kind::none;
+ }
+
+ /* Returns a type allocator that allocates on this owner. */
+
+ type_allocator allocator ()
+ {
+ gdb_assert (this->valid ());
+
+ if (this->kind == owner_kind::arch)
+ return type_allocator (this->owner.arch);
+ else if (this->kind == owner_kind::objfile)
+ {
+ /* Creating types on the gdbarch sets their language to minimal, we
+ * maintain this behavior here. */
+ return type_allocator (this->owner.objfile, language_minimal);
+ }
+
+ /* Should never be reached, but it's better to fail in a safe way than try
+ * to instance the allocator with arbitraty parameters here. */
+ abort ();
+ }
+
+ /* Get a reference to the owner's obstack. */
+
+ obstack *get_obstack ()
+ {
+ gdb_assert (this->valid ());
+
+ if (this->kind == owner_kind::arch)
+ return gdbarch_obstack (this->owner.arch);
+ else if (this->kind == owner_kind::objfile)
+ return &this->owner.objfile->objfile_obstack;
+
+ return nullptr;
+ }
+
+ /* Get a reference to the owner's architecture. */
+
+ struct gdbarch *get_arch ()
+ {
+ gdb_assert (this->valid ());
+
+ if (this->kind == owner_kind::arch)
+ return this->owner.arch;
+ else if (this->kind == owner_kind::objfile)
+ return this->owner.objfile->arch ();
+
+ return nullptr;
+ }
+
+ /* Copy a null-terminated string to the owner's obstack. */
+
+ const char *copy_string (const char *py_str)
+ {
+ gdb_assert (this->valid ());
+
+ unsigned int len = strlen (py_str);
+ return obstack_strndup (this->get_obstack (), py_str, len);
+ }
+
+
+
+private:
+ enum class owner_kind { arch, objfile, none };
+
+ owner_kind kind = owner_kind::none;
+ union {
+ struct gdbarch *arch;
+ struct objfile *objfile;
+ } owner;
+};
+
+/* Creates a new type and returns a new gdb.Type associated with it. */
+
+PyObject *
+gdbpy_init_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+ static const char *keywords[] = { "owner", "type_code", "bit_size", "name",
+ NULL };
+ PyObject *owner_object;
+ enum type_code code;
+ int bit_length;
+ const char *py_name;
+
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "Oiis", keywords, &owner_object,
+ &code, &bit_length, &py_name))
+ return nullptr;
+
+ type_storage_owner owner (owner_object);
+ if (!owner.valid ())
+ return nullptr;
+
+ const char *name = owner.copy_string (py_name);
+ struct type *type;
+ try
+ {
+ type_allocator allocator = owner.allocator ();
+ type = allocator.new_type (code, bit_length, name);
+ gdb_assert (type != nullptr);
+ }
+ catch (gdb_exception_error& ex)
+ {
+ GDB_PY_HANDLE_EXCEPTION (ex);
+ }
+
+ return type_to_type_object (type);
+}
+
+/* Creates a new integer type and returns a new gdb.Type associated with it. */
+
+PyObject *
+gdbpy_init_integer_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+ static const char *keywords[] = { "owner", "bit_size", "unsigned", "name",
+ NULL };
+ PyObject *owner_object;
+ int bit_size;
+ int unsigned_p;
+ const char *py_name;
+
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "Oips", keywords,
+ &owner_object, &bit_size, &unsigned_p,
+ &py_name))
+ return nullptr;
+
+ type_storage_owner owner (owner_object);
+ if (!owner.valid ())
+ return nullptr;
+
+ const char *name = owner.copy_string (py_name);
+ struct type *type;
+ try
+ {
+ type_allocator allocator = owner.allocator ();
+ type = init_integer_type (allocator, bit_size, unsigned_p, name);
+ gdb_assert (type != nullptr);
+ }
+ catch (gdb_exception_error& ex)
+ {
+ GDB_PY_HANDLE_EXCEPTION (ex);
+ }
+
+ return type_to_type_object(type);
+}
+
+/* Creates a new character type and returns a new gdb.Type associated
+ * with it. */
+
+PyObject *
+gdbpy_init_character_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+ static const char *keywords[] = { "owner", "bit_size", "unsigned", "name",
+ NULL };
+ PyObject *owner_object;
+ int bit_size;
+ int unsigned_p;
+ const char *py_name;
+
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "Oips", keywords,
+ &owner_object, &bit_size, &unsigned_p,
+ &py_name))
+ return nullptr;
+
+ type_storage_owner owner (owner_object);
+ if (!owner.valid ())
+ return nullptr;
+
+ const char *name = owner.copy_string (py_name);
+ struct type *type;
+ try
+ {
+ type_allocator allocator = owner.allocator ();
+ type = init_character_type (allocator, bit_size, unsigned_p, name);
+ gdb_assert (type != nullptr);
+ }
+ catch (gdb_exception_error& ex)
+ {
+ GDB_PY_HANDLE_EXCEPTION (ex);
+ }
+
+ return type_to_type_object (type);
+}
+
+/* Creates a new boolean type and returns a new gdb.Type associated with it. */
+
+PyObject *
+gdbpy_init_boolean_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+ static const char *keywords[] = { "owner", "bit_size", "unsigned", "name",
+ NULL };
+ PyObject *owner_object;
+ int bit_size;
+ int unsigned_p;
+ const char *py_name;
+
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "Oips", keywords,
+ &owner_object, &bit_size, &unsigned_p,
+ &py_name))
+ return nullptr;
+
+ type_storage_owner owner (owner_object);
+ if (!owner.valid ())
+ return nullptr;
+
+ const char *name = owner.copy_string (py_name);
+ struct type *type;
+ try
+ {
+ type_allocator allocator = owner.allocator ();
+ type = init_boolean_type (allocator, bit_size, unsigned_p, name);
+ gdb_assert (type != nullptr);
+ }
+ catch (gdb_exception_error& ex)
+ {
+ GDB_PY_HANDLE_EXCEPTION (ex);
+ }
+
+ return type_to_type_object (type);
+}
+
+/* Creates a new float type and returns a new gdb.Type associated with it. */
+
+PyObject *
+gdbpy_init_float_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+ static const char *keywords[] = { "owner", "format", "name", NULL };
+ PyObject *owner_object, *float_format_object;
+ const char *py_name;
+
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "OOs", keywords, &owner_object,
+ &float_format_object, &py_name))
+ return nullptr;
+
+ type_storage_owner owner (owner_object);
+ if (!owner.valid ())
+ return nullptr;
+
+ struct floatformat *local_ff = float_format_object_as_float_format
+ (float_format_object);
+ if (local_ff == nullptr)
+ return nullptr;
+
+ /* Persist a copy of the format in the objfile's obstack. This guarantees
+ * that the format won't outlive the type being created from it and that
+ * changes made to the object used to create this type will not affect it
+ * after creation. */
+ auto ff = OBSTACK_CALLOC (owner.get_obstack (), 1, struct floatformat);
+ memcpy (ff, local_ff, sizeof (struct floatformat));
+
+ /* We only support creating float types in the architecture's endianness, so
+ * make sure init_float_type sees the float format structure we need it to.
+ */
+ enum bfd_endian endianness = gdbarch_byte_order (owner.get_arch ());
+ gdb_assert (endianness < BFD_ENDIAN_UNKNOWN);
+
+ const struct floatformat *per_endian[2] = { nullptr, nullptr };
+ per_endian[endianness] = ff;
+
+ const char *name = owner.copy_string (py_name);
+ struct type *type;
+ try
+ {
+ type_allocator allocator = owner.allocator ();
+ type = init_float_type (allocator, -1, name, per_endian, endianness);
+ gdb_assert (type != nullptr);
+ }
+ catch (gdb_exception_error& ex)
+ {
+ GDB_PY_HANDLE_EXCEPTION (ex);
+ }
+
+ return type_to_type_object (type);
+}
+
+/* Creates a new decimal float type and returns a new gdb.Type
+ * associated with it. */
+
+PyObject *
+gdbpy_init_decfloat_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+ static const char *keywords[] = { "owner", "bit_size", "name", NULL };
+ PyObject *owner_object;
+ int bit_length;
+ const char *py_name;
+
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "Ois", keywords, &owner_object,
+ &bit_length, &py_name))
+ return nullptr;
+
+ type_storage_owner owner (owner_object);
+ if (!owner.valid ())
+ return nullptr;
+
+ const char *name = owner.copy_string (py_name);
+ struct type *type;
+ try
+ {
+ type_allocator allocator = owner.allocator ();
+ type = init_decfloat_type (allocator, bit_length, name);
+ gdb_assert (type != nullptr);
+ }
+ catch (gdb_exception_error& ex)
+ {
+ GDB_PY_HANDLE_EXCEPTION (ex);
+ }
+
+ return type_to_type_object (type);
+}
+
+/* Returns whether a given type can be used to create a complex type. */
+
+PyObject *
+gdbpy_can_create_complex_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+ static const char *keywords[] = { "type", NULL };
+ PyObject *type_object;
+
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "O", keywords,
+ &type_object))
+ return nullptr;
+
+ struct type *type = type_object_to_type (type_object);
+ if (type == nullptr)
+ return nullptr;
+
+ bool can_create_complex = false;
+ try
+ {
+ can_create_complex = can_create_complex_type (type);
+ }
+ catch (gdb_exception_error& ex)
+ {
+ GDB_PY_HANDLE_EXCEPTION (ex);
+ }
+
+ if (can_create_complex)
+ Py_RETURN_TRUE;
+ else
+ Py_RETURN_FALSE;
+}
+
+/* Creates a new complex type and returns a new gdb.Type associated with it. */
+
+PyObject *
+gdbpy_init_complex_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+ static const char *keywords[] = { "type", "name", NULL };
+ PyObject *type_object;
+ const char *py_name;
+
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "Os", keywords, &type_object,
+ &py_name))
+ return nullptr;
+
+ struct type *type = type_object_to_type (type_object);
+ if (type == nullptr)
+ return nullptr;
+
+ obstack *obstack;
+ if (type->is_objfile_owned ())
+ obstack = &type->objfile_owner ()->objfile_obstack;
+ else
+ obstack = gdbarch_obstack (type->arch_owner ());
+
+ unsigned int len = strlen (py_name);
+ const char *name = obstack_strndup (obstack,
+ py_name,
+ len);
+ struct type *complex_type;
+ try
+ {
+ complex_type = init_complex_type (name, type);
+ gdb_assert (complex_type != nullptr);
+ }
+ catch (gdb_exception_error& ex)
+ {
+ GDB_PY_HANDLE_EXCEPTION (ex);
+ }
+
+ return type_to_type_object (complex_type);
+}
+
+/* Creates a new pointer type and returns a new gdb.Type associated with it. */
+
+PyObject *
+gdbpy_init_pointer_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+ static const char *keywords[] = { "owner", "target", "bit_size", "name",
+ NULL };
+ PyObject *owner_object, *type_object;
+ int bit_length;
+ const char *py_name;
+
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "OOis", keywords,
+ &owner_object, &type_object,
+ &bit_length, &py_name))
+ return nullptr;
+
+ struct type *type = type_object_to_type (type_object);
+ if (type == nullptr)
+ return nullptr;
+
+ type_storage_owner owner (owner_object);
+ if (!owner.valid ())
+ return nullptr;
+
+ const char *name = owner.copy_string (py_name);
+ struct type *pointer_type = nullptr;
+ try
+ {
+ type_allocator allocator = owner.allocator ();
+ pointer_type = init_pointer_type (allocator, bit_length, name, type);
+ gdb_assert (type != nullptr);
+ }
+ catch (gdb_exception_error& ex)
+ {
+ GDB_PY_HANDLE_EXCEPTION (ex);
+ }
+
+ return type_to_type_object (pointer_type);
+}
+
+/* Creates a new fixed point type and returns a new gdb.Type associated
+ * with it. */
+
+PyObject *
+gdbpy_init_fixed_point_type (PyObject *self, PyObject *args, PyObject *kw)
+{
+ static const char *keywords[] = { "owner", "bit_size", "unsigned", "name",
+ NULL };
+ PyObject *owner_object;
+ int bit_length;
+ int unsigned_p;
+ const char* py_name;
+
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "Oips", keywords,
+ &owner_object, &bit_length,
+ &unsigned_p, &py_name))
+ return nullptr;
+
+ type_storage_owner owner (owner_object);
+ if (!owner.valid ())
+ return nullptr;
+
+ const char *name = owner.copy_string (py_name);
+ struct type *type;
+ try
+ {
+ type_allocator allocator = owner.allocator ();
+ type = init_fixed_point_type (allocator, bit_length, unsigned_p, name);
+ gdb_assert (type != nullptr);
+ }
+ catch (gdb_exception_error& ex)
+ {
+ GDB_PY_HANDLE_EXCEPTION (ex);
+ }
+
+ return type_to_type_object (type);
+}
@@ -291,6 +291,8 @@ extern PyTypeObject frame_object_type
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("frame_object");
extern PyTypeObject thread_object_type
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("thread_object");
+extern PyTypeObject float_format_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("float_format");
/* Ensure that breakpoint_object_type is initialized and return true. If
breakpoint_object_type can't be initialized then set a suitable Python
@@ -433,6 +435,26 @@ gdb::unique_xmalloc_ptr<char> gdbpy_parse_command_name
PyObject *gdbpy_register_tui_window (PyObject *self, PyObject *args,
PyObject *kw);
+PyObject *gdbpy_init_type (PyObject *self, PyObject *args, PyObject *kw);
+PyObject *gdbpy_init_integer_type (PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *gdbpy_init_character_type (PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *gdbpy_init_boolean_type (PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *gdbpy_init_float_type (PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *gdbpy_init_decfloat_type (PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *gdbpy_can_create_complex_type (PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *gdbpy_init_complex_type (PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *gdbpy_init_pointer_type (PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *gdbpy_init_fixed_point_type (PyObject *self, PyObject *args,
+ PyObject *kw);
+
PyObject *symtab_and_line_to_sal_object (struct symtab_and_line sal);
PyObject *symtab_to_symtab_object (struct symtab *symtab);
PyObject *symbol_to_symbol_object (struct symbol *sym);
@@ -504,6 +526,18 @@ extern void serialize_mi_results (PyObject *results);
extern PyObject *gdbpy_notify_mi (PyObject *self, PyObject *args,
PyObject *kw);
+/* Retrieves a pointer to the underlying float format structure. Expects an
+ * instance of gdb.Objfile for SELF. If SELF is of an incompatible type,
+ * returns nullptr and raises a Python exception. */
+
+extern struct objfile *objfile_object_to_objfile (PyObject *self);
+
+/* Retrieves a pointer to the underlying float format structure. Expects an
+ * instance of gdb.FloatFormat for SELF. If SELF is of an incompatible type,
+ * returns nullptr and raises a Python exception. */
+
+extern struct floatformat *float_format_object_as_float_format (PyObject *self);
+
/* Convert Python object OBJ to a program_space pointer. OBJ must be a
gdb.Progspace reference. Return nullptr if the gdb.Progspace is not
valid (see gdb.Progspace.is_valid), otherwise return the program_space
@@ -2626,6 +2626,56 @@ Return current recording object." },
"stop_recording () -> None.\n\
Stop current recording." },
+ /* Type initialization functions. */
+ { "init_type", (PyCFunction) gdbpy_init_type, METH_VARARGS | METH_KEYWORDS,
+ "init_type (objfile, type_code, bit_length, name) -> type\n\
+ Creates a new type with the given bit length and type code, owned\
+ by the given objfile." },
+ { "init_integer_type", (PyCFunction) gdbpy_init_integer_type,
+ METH_VARARGS | METH_KEYWORDS,
+ "init_integer_type (objfile, bit_length, unsigned, name) -> type\n\
+ Creates a new integer type with the given bit length and \
+ signedness, owned by the given objfile." },
+ { "init_character_type", (PyCFunction) gdbpy_init_character_type,
+ METH_VARARGS | METH_KEYWORDS,
+ "init_character_type (objfile, bit_length, unsigned, name) -> type\n\
+ Creates a new character type with the given bit length and \
+ signedness, owned by the given objfile." },
+ { "init_boolean_type", (PyCFunction) gdbpy_init_boolean_type,
+ METH_VARARGS | METH_KEYWORDS,
+ "init_boolean_type (objfile, bit_length, unsigned, name) -> type\n\
+ Creates a new boolean type with the given bit length and \
+ signedness, owned by the given objfile." },
+ { "init_float_type", (PyCFunction) gdbpy_init_float_type,
+ METH_VARARGS | METH_KEYWORDS,
+ "init_float_type (objfile, float_format, name) -> type\n\
+ Creates a new floating point type with the given bit length and \
+ format, owned by the given objfile." },
+ { "init_decfloat_type", (PyCFunction) gdbpy_init_decfloat_type,
+ METH_VARARGS | METH_KEYWORDS,
+ "init_decfloat_type (objfile, bit_length, name) -> type\n\
+ Creates a new decimal float type with the given bit length,\
+ owned by the given objfile." },
+ { "can_create_complex_type", (PyCFunction) gdbpy_can_create_complex_type,
+ METH_VARARGS | METH_KEYWORDS,
+ "can_create_complex_type (type) -> bool\n\
+ Returns whether a given type can form a new complex type." },
+ { "init_complex_type", (PyCFunction) gdbpy_init_complex_type,
+ METH_VARARGS | METH_KEYWORDS,
+ "init_complex_type (base_type, name) -> type\n\
+ Creates a new complex type whose components belong to the\
+ given type, owned by the given objfile." },
+ { "init_pointer_type", (PyCFunction) gdbpy_init_pointer_type,
+ METH_VARARGS | METH_KEYWORDS,
+ "init_pointer_type (objfile, target_type, bit_length, name) -> type\n\
+ Creates a new pointer type with the given bit length, pointing\
+ to the given target type, and owned by the given objfile." },
+ { "init_fixed_point_type", (PyCFunction) gdbpy_init_fixed_point_type,
+ METH_VARARGS | METH_KEYWORDS,
+ "init_fixed_point_type (objfile, bit_length, unsigned, name) -> type\n\
+ Creates a new fixed point type with the given bit length and\
+ signedness, owned by the given objfile." },
+
{ "lookup_type", (PyCFunction) gdbpy_lookup_type,
METH_VARARGS | METH_KEYWORDS,
"lookup_type (name [, block]) -> type\n\
new file mode 100644
@@ -0,0 +1,21 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2009-2023 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 <http://www.gnu.org/licenses/>. */
+
+int main ()
+{
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,132 @@
+# Copyright (C) 2009-2023 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 <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite. It tests the mechanism
+# of creating new types from within Python.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+# Build inferior to language specification.
+proc build_inferior {exefile lang} {
+ global srcdir subdir srcfile testfile hex
+
+ if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${exefile}" executable "debug $lang"] != "" } {
+ untested "failed to compile in $lang mode"
+ return -1
+ }
+
+ return 0
+}
+
+# Restart GDB.
+proc restart_gdb {exefile} {
+ clean_restart $exefile
+
+ if {![runto_main]} {
+ return
+ }
+}
+
+# Tests the basic values of a type.
+proc test_type_basic {owner t code sizeof name} {
+ gdb_test "python print(${t}.code == ${code})" \
+ "True" "check the code for the python-constructed type (${owner}/${name})"
+ gdb_test "python print(${t}.sizeof == ${sizeof})" \
+ "True" "check the size for the python-constructed type (${owner}/${name})"
+ gdb_test "python print(${t}.name == ${name})" \
+ "True" "check the name for the python-constructed type (${owner}/${name})"
+}
+
+# Runs the tests for a given owner object.
+proc for_owner {owner} {
+ # Simple direct type creation.
+ gdb_test "python t = gdb.init_type(${owner}, gdb.TYPE_CODE_INT, 24, 'long short int')" \
+ "" "construct a new type from inside python (${owner})"
+ test_type_basic $owner "t" "gdb.TYPE_CODE_INT" "3" "'long short int'"
+
+ # Integer type creation.
+ gdb_test "python t = gdb.init_integer_type(${owner}, 24, True, 'test_int_t')" \
+ "" "construct a new integer type from inside python (${owner})"
+ test_type_basic $owner "t" "gdb.TYPE_CODE_INT" "3" "'test_int_t'"
+
+ # Character type creation.
+ gdb_test "python t = gdb.init_character_type(${owner}, 24, True, 'test_char_t')" \
+ "" "construct a new character type from inside python (${owner})"
+ test_type_basic $owner "t" "gdb.TYPE_CODE_CHAR" "3" "'test_char_t'"
+
+ # Boolean type creation.
+ gdb_test "python t = gdb.init_boolean_type(${owner}, 24, True, 'test_bool_t')" \
+ "" "construct a new boolean type from inside python (${owner})"
+ test_type_basic $owner "t" "gdb.TYPE_CODE_BOOL" "3" "'test_bool_t'"
+
+ # Float type creation.
+ gdb_test "python f = gdb.FloatFormat()" "" "create a float format object (${owner})"
+ gdb_test "python f.totalsize = 32" "" "set totalsize for the float format (${owner})"
+ gdb_test "python f.sign_start = 31" "" "set sign_start for the float format (${owner})"
+ gdb_test "python f.exp_start = 23" "" "set exp_start for the float format (${owner})"
+ gdb_test "python f.exp_len = 8" "" "set exp_len for the float format (${owner})"
+ gdb_test "python f.exp_bias = 0" "" "set exp_bias for the float format (${owner})"
+ gdb_test "python f.exp_nan = 0xff" "" "set exp_nan for the float format (${owner})"
+ gdb_test "python f.man_start = 0" "" "set man_start for the float format (${owner})"
+ gdb_test "python f.man_len = 22" "" "set man_len for the float format (${owner})"
+ gdb_test "python f.intbit = False" "" "set intbit for the float format (${owner})"
+ gdb_test "python f.name = 'test_float_fmt'" "" "set name for the float format (${owner})"
+
+ gdb_test "python ft = gdb.init_float_type(${owner}, f, 'test_float_t')" \
+ "" "construct a new float type from inside python (${owner})"
+ test_type_basic $owner "ft" "gdb.TYPE_CODE_FLT" "4" "'test_float_t'"
+
+ # Decfloat type creation.
+ gdb_test "python t = gdb.init_decfloat_type(${owner}, 24, 'test_decfloat_t')" \
+ "" "construct a new decfloat type from inside python (${owner})"
+ test_type_basic $owner "t" "gdb.TYPE_CODE_DECFLOAT" "3" "'test_decfloat_t'"
+
+ # Test complex type.
+ gdb_test "python print(gdb.can_create_complex_type(ft))" "True" \
+ "check whether the float type we created can be the basis for a complex (${owner})"
+
+ gdb_test "python t = gdb.init_complex_type(ft, 'test_complex_t')" \
+ "" "construct a new complex type from inside python (${owner})"
+ test_type_basic $owner "t" "gdb.TYPE_CODE_COMPLEX" "8" "'test_complex_t'"
+
+ # Create a 24-bit pointer to our floating point type.
+ gdb_test "python t = gdb.init_pointer_type(${owner}, ft, 24, 'test_pointer_t')" \
+ "" "construct a new pointer type from inside python (${owner})"
+ test_type_basic $owner "t" "gdb.TYPE_CODE_PTR" "3" "'test_pointer_t'"
+}
+
+# Run the tests.
+if { [build_inferior "${binfile}" "c"] == 0 } {
+ restart_gdb "${binfile}"
+
+ # Skip all tests if Python scripting is not enabled.
+ if { ![allow_python_tests] } { continue }
+
+ # Test objfile-owned type construction
+ for_owner "gdb.objfiles()\[0\]"
+
+ # Objfile-owned fixed point type creation.
+ #
+ # Currently, these cannot be owned by architectures, so we have to
+ # test them separately.
+ gdb_test "python t = gdb.init_fixed_point_type(gdb.objfiles()\[0\], 24, True, 'test_fixed_t')" \
+ "" "construct a new fixed point type from inside python (gdb.objfile()\[0\])"
+ test_type_basic "gdb.objfile()\[0\]" "t" "gdb.TYPE_CODE_FIXED_POINT" "3" "'test_fixed_t'"
+
+ # Test arch-owned type construction
+ for_owner "gdb.inferiors()\[0\].architecture()"
+}