public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 1/1] Add support for gdb.Type initialization from within the Python API
@ 2023-01-05 23:56 dark.ryu.550
  2023-01-06 20:00 ` Simon Marchi
  0 siblings, 1 reply; 10+ messages in thread
From: dark.ryu.550 @ 2023-01-05 23:56 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 30500 bytes --]

This patch adds support for creating types from within the Python API. It
does

so by exposing the `init_*_type` family of functions, defined in
`gdbtypes.h` to 

Python and having them return `gdb.Type` objects connected to the newly
minted

types.

 

These functions are accessible in the root of the gdb module and all require

a reference to a `gdb.Objfile`. Types created from this API are exclusively 

objfile-owned.

 

This patch also adds an extra type - `gdb.FloatFormat` - to support creation
of 

floating point types by letting users control the format from within Python.
It

is missing, however, a way to specify half formats and validation functions.

 

It is important to note that types created using this interface are not

automatically registered as a symbol, and so, types will become unreachable

unless used to create a value that otherwise references it or saved in some
way.

 

The main drawback of using the `init_*_type` family over implementing type 

initialization by hand is that any type that's created gets immediately 

allocated on its owner objfile's obstack, regardless of what its real

lifetime requirements are. The main implication of this is that types that 

become unreachable will leak their memory for the lifetime of the objfile.

 

Keeping track of the initialization of the type by hand would require a 

deeper change to the existing type object infrastructure. A bit too
ambitious

for a first patch, I'd say.

 

if it were to be done though, we would gain the ability to only keep in the 

obstack types that are known to be referenced in some other way - by
allocating 

and copying the data to the obstack as other objects are created that
reference 

it (eg. symbols).

 

 

---

diff --git a/gdb/Makefile.in b/gdb/Makefile.in

index fb4d42c7baa..789f7dce224 100644

--- a/gdb/Makefile.in

+++ b/gdb/Makefile.in

@@ -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 \

diff --git a/gdb/python/py-float-format.c b/gdb/python/py-float-format.c

new file mode 100644

index 00000000000..a1da4dcd3f0

--- /dev/null

+++ b/gdb/python/py-float-format.c

@@ -0,0 +1,278 @@

+/* Accessibility of float format controls from inside the Python API

+

+   Copyright (C) 2008-2022 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"

+

+struct float_format_object

+{

+  PyObject_HEAD

+  struct floatformat format;

+

+  struct floatformat *float_format()

+  {

+    return &this->format;

+  }

+};

+

+int

+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;

+}

+

+#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);
\

+  }

+

+#define INSTANCE_FIELD_SETTER(getter_name, field_name, field_type,
field_conv) \

+  static int
\

+  getter_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;
\

+  }

+

+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;

+}

+

+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;

+}

+

+static bool

+py_to_unsigned_int(PyObject *object, unsigned int *val)

+{

+  if (!PyObject_IsInstance(object, (PyObject*)&PyLong_Type))

+  {

+    PyErr_SetString(PyExc_TypeError, u8"value must be an integer");

+    return false;

+  }

+

+  long native_val = PyLong_AsLong(object);

+  if (native_val > (long)UINT_MAX)

+  {

+    PyErr_SetString(PyExc_ValueError, u8"value is too large");

+    return false;

+  }

+  if (native_val < 0)

+  {

+    PyErr_SetString(PyExc_ValueError, u8"value must not be smaller than
zero");

+    return false;

+  }

+

+  *val = (unsigned int)native_val;

+  return true;

+}

+

+static bool

+py_to_int(PyObject *object, int *val)

+{

+  if (!PyObject_IsInstance(object, (PyObject*)&PyLong_Type))

+  {

+    PyErr_SetString(PyExc_TypeError, u8"value must be an integer");

+    return false;

+  }

+

+  long native_val = PyLong_AsLong(object);

+  if (native_val > (long)INT_MAX)

+  {

+    PyErr_SetString(PyExc_ValueError, u8"value is too large");

+    return false;

+  }

+

+  *val = (int)native_val;

+  return true;

+}

+

+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)

+

+static int

+ffpy_always_valid (const struct floatformat *fmt ATTRIBUTE_UNUSED,

+  const void *from ATTRIBUTE_UNUSED)

+{

+  return 1;

+}

+

+

+static int

+ffpy_init(PyObject *self, PyObject *args, PyObject *kwds)

+{

+  float_format_object *ff = (float_format_object*) self;

+  ff->format = floatformat{};

+  ff->float_format()->name = u8"";

+  ff->float_format()->is_valid = ffpy_always_valid;

+  return 0;

+}

+

+struct floatformat *

+float_format_object_as_float_format (PyObject *self)

+{

+  if (!PyObject_IsInstance(self, (PyObject*)&float_format_object_type))

+    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 a \"true\" 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 }

+};

+

+static PyMethodDef float_format_object_methods[] =

+{

+  { NULL }

+};

+

+static PyNumberMethods float_format_object_as_number = {

+  nullptr,                                                   /* nb_add */

+  nullptr,                                                   /* nb_subtract
*/

+  nullptr,                                                   /* nb_multiply
*/

+  nullptr,                                                   /*
nb_remainder */

+  nullptr,                                                   /* nb_divmod
*/

+  nullptr,                                                   /* nb_power */

+  nullptr,                                                   /* nb_negative
*/

+  nullptr,                                                   /* nb_positive
*/

+  nullptr,                                                   /* nb_absolute
*/

+  nullptr,                                   /* nb_nonzero */

+  nullptr,                                                   /* nb_invert
*/

+  nullptr,                                                   /* nb_lshift
*/

+  nullptr,                                                   /* nb_rshift
*/

+  nullptr,                                                   /* nb_and */

+  nullptr,                                                   /* nb_xor */

+  nullptr,                                                   /* nb_or */

+  nullptr,                                                   /* nb_int */

+  nullptr,                                                   /* reserved */

+  nullptr,                                                   /* nb_float */

+};

+

+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*/

+  &float_format_object_as_number,       /*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 */

+  float_format_object_methods,                               /* 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 */

+};

+

+

diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c

index c278925531b..9b884d4c414 100644

--- a/gdb/python/py-objfile.c

+++ b/gdb/python/py-objfile.c

@@ -704,6 +928,18 @@ objfile_to_objfile_object (struct objfile *objfile)

   return gdbpy_ref<>::new_reference (result);

}

+struct objfile *

+objfile_object_to_objfile (PyObject *self)

+{

+  if (!PyObject_TypeCheck (self, &objfile_object_type))

+    return nullptr;

+

+  auto objfile_object = (struct objfile_object*) self;

+  OBJFPY_REQUIRE_VALID (objfile_object);

+

+  return objfile_object->objfile;

+}

+

int

gdbpy_initialize_objfile (void)

{ 

diff --git a/gdb/python/py-type-init.c b/gdb/python/py-type-init.c

new file mode 100644

index 00000000000..127f6eaa538

--- /dev/null

+++ b/gdb/python/py-type-init.c

@@ -0,0 +1,370 @@

+/* Functionality for creating new types accessible from python.

+

+   Copyright (C) 2008-2022 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"

+

+

+static const char *

+copy_string(struct objfile *objfile, const char *py_str)

+{

+  unsigned int len = strlen(py_str);

+  return obstack_strndup (&objfile->per_bfd->storage_obstack,

+                          py_str,

+                          len);

+}

+

+PyObject *

+gdbpy_init_type (PyObject *self, PyObject *args)

+{

+  PyObject *objfile_object;

+  enum type_code code;

+  int bit_length;

+  const char *py_name;

+

+  if (!PyArg_ParseTuple(args, "Oiis", &objfile_object, &code, &bit_length,
&py_name))

+    return nullptr;

+

+  struct objfile* objfile = objfile_object_to_objfile(objfile_object);

+  if (objfile == nullptr)

+    return nullptr;

+

+  const char *name = copy_string(objfile, py_name);

+  struct type *type;

+  try

+  {

+    type = init_type(objfile, code, bit_length, name);

+    gdb_assert (type != nullptr);

+  }

+  catch(gdb_exception_error& ex)

+  {

+    GDB_PY_HANDLE_EXCEPTION (ex);

+  }

+

+  return type_to_type_object(type);

+}

+

+PyObject *

+gdbpy_init_integer_type (PyObject *self, PyObject *args)

+{

+  PyObject *objfile_object;

+  int bit_size;

+  int unsigned_p;

+  const char* py_name;

+

+  if (!PyArg_ParseTuple(args, "Oips", &objfile_object, &bit_size,
&unsigned_p, &py_name))

+    return nullptr;

+

+  struct objfile* objfile = objfile_object_to_objfile(objfile_object);

+  if (objfile == nullptr)

+    return nullptr;

+

+  const char *name = copy_string(objfile, py_name);

+  struct type *type;

+  try

+  {

+    type = init_integer_type(objfile, 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);

+}

+

+PyObject *

+gdbpy_init_character_type (PyObject *self, PyObject *args)

+{

+

+  PyObject *objfile_object;

+  int bit_size;

+  int unsigned_p;

+  const char* py_name;

+

+  if (!PyArg_ParseTuple(args, "Oips", &objfile_object, &bit_size,
&unsigned_p, &py_name))

+    return nullptr;

+

+  struct objfile* objfile = objfile_object_to_objfile(objfile_object);

+  if (objfile == nullptr)

+    return nullptr;

+

+  const char *name = copy_string(objfile, py_name);

+  struct type *type;

+  try

+  {

+    type = init_character_type(objfile, 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);

+}

+

+PyObject *

+gdbpy_init_boolean_type (PyObject *self, PyObject *args)

+{

+

+  PyObject *objfile_object;

+  int bit_size;

+  int unsigned_p;

+  const char* py_name;

+

+  if (!PyArg_ParseTuple(args, "Oips", &objfile_object, &bit_size,
&unsigned_p, &py_name))

+    return nullptr;

+

+  struct objfile* objfile = objfile_object_to_objfile(objfile_object);

+  if (objfile == nullptr)

+    return nullptr;

+

+  const char *name = copy_string(objfile, py_name);

+  struct type *type;

+  try

+  {

+    type = init_boolean_type(objfile, 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);

+}

+

+PyObject *

+gdbpy_init_float_type (PyObject *self, PyObject *args)

+{

+  PyObject *objfile_object, *float_format_object;

+  const char* py_name;

+

+  if (!PyArg_ParseTuple(args, "OOs", &objfile_object, &float_format_object,
&py_name))

+    return nullptr;

+

+  struct objfile* objfile = objfile_object_to_objfile(objfile_object);

+  if (objfile == nullptr)

+    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(

+    &objfile->objfile_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(objfile->arch());

+  gdb_assert(endianness < 2);

+

+  const struct floatformat* per_endian[2] = { nullptr, nullptr };

+  per_endian[endianness] = ff;

+

+  const char *name = copy_string(objfile, py_name);

+  struct type *type;

+  try

+  {

+    type = init_float_type(objfile, -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);

+}

+

+PyObject *

+gdbpy_init_decfloat_type (PyObject *self, PyObject *args)

+{

+  PyObject *objfile_object;

+  int bit_length;

+  const char* py_name;

+

+  if (!PyArg_ParseTuple(args, "Ois", &objfile_object, &bit_length,
&py_name))

+    return nullptr;

+

+  struct objfile* objfile = objfile_object_to_objfile(objfile_object);

+  if (objfile == nullptr)

+    return nullptr;

+

+  const char *name = copy_string(objfile, py_name);

+  struct type *type;

+  try

+  {

+    type = init_decfloat_type(objfile, bit_length, name);

+    gdb_assert (type != nullptr);

+  }

+  catch(gdb_exception_error& ex)

+  {

+    GDB_PY_HANDLE_EXCEPTION (ex);

+  }

+

+  return type_to_type_object(type);

+}

+

+PyObject *

+gdbpy_can_create_complex_type (PyObject *self, PyObject *args)

+{

+

+  PyObject *type_object;

+

+  if (!PyArg_ParseTuple(args, "O", &type_object))

+    return nullptr;

+

+  struct type* type = type_object_to_type(type_object);

+  if (type == nullptr)

+    return nullptr;

+

+  bool can_create_complex;

+  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;

+}

+

+PyObject *

+gdbpy_init_complex_type (PyObject *self, PyObject *args)

+{

+

+  PyObject *type_object;

+  const char* py_name;

+

+  if (!PyArg_ParseTuple(args, "Os", &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);

+}

+

+PyObject *

+gdbpy_init_pointer_type (PyObject *self, PyObject *args)

+{

+  PyObject *objfile_object, *type_object;

+  int bit_length;

+  const char* py_name;

+

+  if (!PyArg_ParseTuple(args, "OOis", &objfile_object, &type_object,
&bit_length, &py_name))

+    return nullptr;

+

+  struct objfile* objfile = objfile_object_to_objfile(objfile_object);

+  if (objfile == nullptr)

+    return nullptr;

+

+  struct type* type = type_object_to_type(type_object);

+  if (type == nullptr)

+    return nullptr;

+

+  const char *name = copy_string(objfile, py_name);

+  struct type *pointer_type;

+  try

+  {

+    pointer_type = init_pointer_type(objfile, 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);

+}

+

+PyObject *

+gdbpy_init_fixed_point_type (PyObject *self, PyObject *args)

+{

+

+  PyObject *objfile_object;

+  int bit_length;

+  int unsigned_p;

+  const char* py_name;

+

+  if (!PyArg_ParseTuple(args, "Oips", &objfile_object, &bit_length,
&unsigned_p, &py_name))

+    return nullptr;

+

+  struct objfile* objfile = objfile_object_to_objfile(objfile_object);

+  if (objfile == nullptr)

+    return nullptr;

+

+  const char *name = copy_string(objfile, py_name);

+  struct type *type;

+  try

+  {

+    type = init_fixed_point_type(objfile, 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);

+}

+

+

+

diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h

index 06357cc8c0b..3877f8a7ca9 100644

--- a/gdb/python/python-internal.h

+++ b/gdb/python/python-internal.h

@@ -289,6 +289,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

@@ -431,6 +433,17 @@ 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 *gdbpy_init_integer_type (PyObject *self, PyObject *args);

+PyObject *gdbpy_init_character_type (PyObject *self, PyObject *args);

+PyObject *gdbpy_init_boolean_type (PyObject *self, PyObject *args);

+PyObject *gdbpy_init_float_type (PyObject *self, PyObject *args);

+PyObject *gdbpy_init_decfloat_type (PyObject *self, PyObject *args);

+PyObject *gdbpy_can_create_complex_type (PyObject *self, PyObject *args);

+PyObject *gdbpy_init_complex_type (PyObject *self, PyObject *args);

+PyObject *gdbpy_init_pointer_type (PyObject *self, PyObject *args);

+PyObject *gdbpy_init_fixed_point_type (PyObject *self, PyObject *args);

+

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);

@@ -481,6 +494,8 @@ struct symtab *symtab_object_to_symtab (PyObject *obj);

struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);

frame_info_ptr frame_object_to_frame_info (PyObject *frame_obj);

struct gdbarch *arch_object_to_gdbarch (PyObject *obj);

+struct objfile *objfile_object_to_objfile (PyObject *self);

+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

@@ -559,6 +574,8 @@ int gdbpy_initialize_micommands (void)

void gdbpy_finalize_micommands ();

int gdbpy_initialize_disasm ()

   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;

+int gdbpy_initialize_float_format ()

+  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;

 PyMODINIT_FUNC gdbpy_events_mod_func ();

diff --git a/gdb/python/python.c b/gdb/python/python.c

index 4aa24421dec..d852fa448df 100644

--- a/gdb/python/python.c

+++ b/gdb/python/python.c

@@ -2153,7 +2153,8 @@ do_start_initialization ()

       || gdbpy_initialize_membuf () < 0

       || gdbpy_initialize_connection () < 0

       || gdbpy_initialize_tui () < 0

-      || gdbpy_initialize_micommands () < 0)

+      || gdbpy_initialize_micommands () < 0

+      || gdbpy_initialize_float_format() < 0)

     return false;

 #define GDB_PY_DEFINE_EVENT_TYPE(name, py_name, doc, base)         \

@@ -2529,6 +2530,28 @@ Return current recording object." },

     "stop_recording () -> None.\n\

Stop current recording." },

+  /* Type initialization functions. */

+  { "init_type", gdbpy_init_type, METH_VARARGS,

+    "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", gdbpy_init_integer_type, METH_VARARGS,

+    "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", gdbpy_init_character_type, METH_VARARGS,

+    "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", gdbpy_init_boolean_type, METH_VARARGS,

+    "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", gdbpy_init_float_type, METH_VARARGS,

+    "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", gdbpy_init_decfloat_type, METH_VARARGS,

+    "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", gdbpy_can_create_complex_type, METH_VARARGS,

+    "can_create_complex_type (type) -> bool\n\

+    Returns whether a given type can form a new complex type." },

+  { "init_complex_type", gdbpy_init_complex_type, METH_VARARGS,

+    "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", gdbpy_init_pointer_type, METH_VARARGS,

+    "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", gdbpy_init_fixed_point_type, METH_VARARGS,

+    "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\

 


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] Add support for gdb.Type initialization from within the Python API
  2023-01-05 23:56 [PATCH 1/1] Add support for gdb.Type initialization from within the Python API dark.ryu.550
@ 2023-01-06 20:00 ` Simon Marchi
  2023-01-11  0:58   ` [PATCH] Add support for creating new types from " Matheus Branco Borella
  2023-05-26  3:30   ` Matheus Branco Borella
  0 siblings, 2 replies; 10+ messages in thread
From: Simon Marchi @ 2023-01-06 20:00 UTC (permalink / raw)
  To: dark.ryu.550, gdb-patches

Hi,

Unfortunately, I am unable to apply this patch as well, please send it
using git-send-email.

On 1/5/23 18:56, dark.ryu.550--- via Gdb-patches wrote:
> This patch adds support for creating types from within the Python API. It
> does
> 
> so by exposing the `init_*_type` family of functions, defined in
> `gdbtypes.h` to 
> 
> Python and having them return `gdb.Type` objects connected to the newly
> minted
> 
> types.
> 
>  
> 
> These functions are accessible in the root of the gdb module and all require
> 
> a reference to a `gdb.Objfile`. Types created from this API are exclusively 
> 
> objfile-owned.

It would maybe be nice to be able to create arch-owned types too.  For
instance, you could create types just after firing up GDB, without even
having an objfile loaded.  It's not necessary to implement it at the
same time, but does your approach leave us the option to do that at a
later time?

> 
>  
> 
> This patch also adds an extra type - `gdb.FloatFormat` - to support creation
> of 
> 
> floating point types by letting users control the format from within Python.
> It
> 
> is missing, however, a way to specify half formats and validation functions.
> 
>  
> 
> It is important to note that types created using this interface are not
> 
> automatically registered as a symbol, and so, types will become unreachable
> 
> unless used to create a value that otherwise references it or saved in some
> way.
> 
>  
> 
> The main drawback of using the `init_*_type` family over implementing type 
> 
> initialization by hand is that any type that's created gets immediately 
> 
> allocated on its owner objfile's obstack, regardless of what its real
> 
> lifetime requirements are. The main implication of this is that types that 
> 
> become unreachable will leak their memory for the lifetime of the objfile.
>
> Keeping track of the initialization of the type by hand would require a 
> 
> deeper change to the existing type object infrastructure. A bit too
> ambitious
> 
> for a first patch, I'd say.
> 
>  
> 
> if it were to be done though, we would gain the ability to only keep in the 
> 
> obstack types that are known to be referenced in some other way - by
> allocating 
> 
> and copying the data to the obstack as other objects are created that
> reference 
> 
> it (eg. symbols).

I think how you did it is ok, it's better to keep things simple.

Simon

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH] Add support for creating new types from the Python API
  2023-01-06 20:00 ` Simon Marchi
@ 2023-01-11  0:58   ` Matheus Branco Borella
  2023-06-27  3:52     ` [PING] " Matheus Branco Borella
  2023-05-26  3:30   ` Matheus Branco Borella
  1 sibling, 1 reply; 10+ messages in thread
From: Matheus Branco Borella @ 2023-01-11  0:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Matheus Branco Borella

This patch adds support for creating types from within the Python API. It does
so by exposing the `init_*_type` family of functions, defined in `gdbtypes.h` to
Python and having them return `gdb.Type` objects connected to the newly minted
types.

These functions are accessible in the root of the gdb module and all require
a reference to a `gdb.Objfile`. Types created from this API are exclusively
objfile-owned.

This patch also adds an extra type - `gdb.FloatFormat` - to support creation of
floating point types by letting users control the format from within Python. It
is missing, however, a way to specify half formats and validation functions.

It is important to note that types created using this interface are not
automatically registered as a symbol, and so, types will become unreachable
unless used to create a value that otherwise references it or saved in some way.

The main drawback of using the `init_*_type` family over implementing type
initialization by hand is that any type that's created gets immediately
allocated on its owner objfile's obstack, regardless of what its real
lifetime requirements are. The main implication of this is that types that
become unreachable will leak their memory for the lifetime of the objfile.

Keeping track of the initialization of the type by hand would require a
deeper change to the existing type object infrastructure. A bit too ambitious
for a first patch, I'd say.

if it were to be done though, we would gain the ability to only keep in the
obstack types that are known to be referenced in some other way - by allocating
and copying the data to the obstack as other objects are created that reference
it (eg. symbols).
---
 gdb/Makefile.in              |   2 +
 gdb/python/py-float-format.c | 297 +++++++++++++++++++++++++++
 gdb/python/py-objfile.c      |  12 ++
 gdb/python/py-type-init.c    | 388 +++++++++++++++++++++++++++++++++++
 gdb/python/python-internal.h |  17 ++
 gdb/python/python.c          |  44 +++-
 6 files changed, 759 insertions(+), 1 deletion(-)
 create mode 100644 gdb/python/py-float-format.c
 create mode 100644 gdb/python/py-type-init.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index fb4d42c7baa..789f7dce224 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -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 \
diff --git a/gdb/python/py-float-format.c b/gdb/python/py-float-format.c
new file mode 100644
index 00000000000..e517e410899
--- /dev/null
+++ b/gdb/python/py-float-format.c
@@ -0,0 +1,297 @@
+/* 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. */
+
+int
+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;
+}
+
+#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);                                                 \
+  }
+
+#define INSTANCE_FIELD_SETTER(getter_name, field_name, field_type, field_conv) \
+  static int                                                                   \
+  getter_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, u8"value must be an integer");
+    return false;
+  }
+
+  long native_val = PyLong_AsLong(object);
+  if(native_val > (long)INT_MAX)
+  {
+    PyErr_SetString(PyExc_ValueError, u8"value is too large");
+    return false;
+  }
+
+  *val = (int)native_val;
+  return true;
+}
+
+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;
+}
+
+/* Retrieves a pointer to the underlying float format structure. */
+
+struct floatformat *
+float_format_object_as_float_format (PyObject *self)
+{
+  if (!PyObject_IsInstance (self, (PyObject*) &float_format_object_type))
+    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 a \"true\" 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 }
+};
+
+static PyMethodDef float_format_object_methods[] =
+{
+  { NULL }
+};
+
+static PyNumberMethods float_format_object_as_number = {
+  nullptr,             /* nb_add */
+  nullptr,             /* nb_subtract */
+  nullptr,             /* nb_multiply */
+  nullptr,             /* nb_remainder */
+  nullptr,             /* nb_divmod */
+  nullptr,             /* nb_power */
+  nullptr,             /* nb_negative */
+  nullptr,             /* nb_positive */
+  nullptr,             /* nb_absolute */
+  nullptr,             /* nb_nonzero */
+  nullptr,             /* nb_invert */
+  nullptr,             /* nb_lshift */
+  nullptr,             /* nb_rshift */
+  nullptr,             /* nb_and */
+  nullptr,             /* nb_xor */
+  nullptr,             /* nb_or */
+  nullptr,             /* nb_int */
+  nullptr,             /* reserved */
+  nullptr,             /* nb_float */
+};
+
+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*/
+  &float_format_object_as_number, /*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 */
+  float_format_object_methods,    /* 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 */
+};
+
+
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index c278925531b..28a7c9a7873 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -704,6 +704,18 @@ objfile_to_objfile_object (struct objfile *objfile)
   return gdbpy_ref<>::new_reference (result);
 }
 
+struct objfile *
+objfile_object_to_objfile (PyObject *self)
+{
+  if (!PyObject_TypeCheck (self, &objfile_object_type))
+    return nullptr;
+
+  auto objfile_object = (struct objfile_object*) self;
+  OBJFPY_REQUIRE_VALID (objfile_object);
+
+  return objfile_object->objfile;
+}
+
 int
 gdbpy_initialize_objfile (void)
 {
diff --git a/gdb/python/py-type-init.c b/gdb/python/py-type-init.c
new file mode 100644
index 00000000000..f3b6813c3ad
--- /dev/null
+++ b/gdb/python/py-type-init.c
@@ -0,0 +1,388 @@
+/* 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"
+
+
+/* Copies a null-terminated string into an objfile's obstack. */
+
+static const char *
+copy_string (struct objfile *objfile, const char *py_str)
+{
+  unsigned int len = strlen (py_str);
+  return obstack_strndup (&objfile->per_bfd->storage_obstack,
+                          py_str, len);
+}
+
+/* Creates a new type and returns a new gdb.Type associated with it. */
+
+PyObject *
+gdbpy_init_type (PyObject *self, PyObject *args)
+{
+  PyObject *objfile_object;
+  enum type_code code;
+  int bit_length;
+  const char *py_name;
+
+  if(!PyArg_ParseTuple (args, "Oiis", &objfile_object, &code, &bit_length, &py_name))
+    return nullptr;
+
+  struct objfile* objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    return nullptr;
+
+  const char *name = copy_string (objfile, py_name);
+  struct type *type;
+  try
+  {
+    type = init_type (objfile, 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 *objfile_object;
+  int bit_size;
+  int unsigned_p;
+  const char *py_name;
+
+  if (!PyArg_ParseTuple (args, "Oips", &objfile_object, &bit_size, &unsigned_p, &py_name))
+    return nullptr;
+
+  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    return nullptr;
+
+  const char *name = copy_string (objfile, py_name);
+  struct type *type;
+  try
+  {
+    type = init_integer_type (objfile, 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 *objfile_object;
+  int bit_size;
+  int unsigned_p;
+  const char *py_name;
+
+  if (!PyArg_ParseTuple (args, "Oips", &objfile_object, &bit_size, &unsigned_p, &py_name))
+    return nullptr;
+
+  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    return nullptr;
+
+  const char *name = copy_string (objfile, py_name);
+  struct type *type;
+  try
+  {
+    type = init_character_type (objfile, 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 *objfile_object;
+  int bit_size;
+  int unsigned_p;
+  const char *py_name;
+
+  if (!PyArg_ParseTuple (args, "Oips", &objfile_object, &bit_size, &unsigned_p, &py_name))
+    return nullptr;
+
+  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    return nullptr;
+
+  const char *name = copy_string (objfile, py_name);
+  struct type *type;
+  try
+  {
+    type = init_boolean_type (objfile, 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 *objfile_object, *float_format_object;
+  const char *py_name;
+
+  if (!PyArg_ParseTuple (args, "OOs", &objfile_object, &float_format_object, &py_name))
+    return nullptr;
+
+  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    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
+    (&objfile->objfile_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 (objfile->arch());
+  gdb_assert (endianness < BFD_ENDIAN_UNKNOWN);
+
+  const struct floatformat *per_endian[2] = { nullptr, nullptr };
+  per_endian[endianness] = ff;
+
+  const char *name = copy_string (objfile, py_name);
+  struct type *type;
+  try
+  {
+    type = init_float_type (objfile, -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 *objfile_object;
+  int bit_length;
+  const char *py_name;
+
+  if (!PyArg_ParseTuple (args, "Ois", &objfile_object, &bit_length, &py_name))
+    return nullptr;
+
+  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    return nullptr;
+
+  const char *name = copy_string (objfile, py_name);
+  struct type *type;
+  try
+  {
+    type = init_decfloat_type (objfile, 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 *type_object;
+
+  if (!PyArg_ParseTuple (args, "O", &type_object))
+    return nullptr;
+
+  struct type *type = type_object_to_type (type_object);
+  if (type == nullptr)
+    return nullptr;
+
+  bool can_create_complex;
+  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 *type_object;
+  const char *py_name;
+
+  if (!PyArg_ParseTuple (args, "Os", &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 *objfile_object, *type_object;
+  int bit_length;
+  const char *py_name;
+
+  if (!PyArg_ParseTuple (args, "OOis", &objfile_object, &type_object, &bit_length, &py_name))
+    return nullptr;
+
+  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    return nullptr;
+
+  struct type *type = type_object_to_type (type_object);
+  if (type == nullptr)
+    return nullptr;
+
+  const char *name = copy_string (objfile, py_name);
+  struct type *pointer_type;
+  try
+  {
+    pointer_type = init_pointer_type (objfile, 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 *objfile_object;
+  int bit_length;
+  int unsigned_p;
+  const char* py_name;
+
+  if (!PyArg_ParseTuple (args, "Oips", &objfile_object, &bit_length, &unsigned_p, &py_name))
+    return nullptr;
+
+  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    return nullptr;
+
+  const char *name = copy_string (objfile, py_name);
+  struct type *type;
+  try
+  {
+    type = init_fixed_point_type (objfile, 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);
+}
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 06357cc8c0b..3877f8a7ca9 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -289,6 +289,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
@@ -431,6 +433,17 @@ 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 *gdbpy_init_integer_type (PyObject *self, PyObject *args);
+PyObject *gdbpy_init_character_type (PyObject *self, PyObject *args);
+PyObject *gdbpy_init_boolean_type (PyObject *self, PyObject *args);
+PyObject *gdbpy_init_float_type (PyObject *self, PyObject *args);
+PyObject *gdbpy_init_decfloat_type (PyObject *self, PyObject *args);
+PyObject *gdbpy_can_create_complex_type (PyObject *self, PyObject *args);
+PyObject *gdbpy_init_complex_type (PyObject *self, PyObject *args);
+PyObject *gdbpy_init_pointer_type (PyObject *self, PyObject *args);
+PyObject *gdbpy_init_fixed_point_type (PyObject *self, PyObject *args);
+
 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);
@@ -481,6 +494,8 @@ struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
 frame_info_ptr frame_object_to_frame_info (PyObject *frame_obj);
 struct gdbarch *arch_object_to_gdbarch (PyObject *obj);
+struct objfile *objfile_object_to_objfile (PyObject *self);
+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
@@ -559,6 +574,8 @@ int gdbpy_initialize_micommands (void)
 void gdbpy_finalize_micommands ();
 int gdbpy_initialize_disasm ()
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+int gdbpy_initialize_float_format ()
+  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 
 PyMODINIT_FUNC gdbpy_events_mod_func ();
 
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 4aa24421dec..1ed29ff4dea 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -2153,7 +2153,8 @@ do_start_initialization ()
       || gdbpy_initialize_membuf () < 0
       || gdbpy_initialize_connection () < 0
       || gdbpy_initialize_tui () < 0
-      || gdbpy_initialize_micommands () < 0)
+      || gdbpy_initialize_micommands () < 0
+      || gdbpy_initialize_float_format() < 0)
     return false;
 
 #define GDB_PY_DEFINE_EVENT_TYPE(name, py_name, doc, base)	\
@@ -2529,6 +2530,47 @@ Return current recording object." },
     "stop_recording () -> None.\n\
 Stop current recording." },
 
+  /* Type initialization functions. */
+  { "init_type", gdbpy_init_type, METH_VARARGS,
+    "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", gdbpy_init_integer_type, METH_VARARGS,
+    "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", gdbpy_init_character_type, METH_VARARGS,
+    "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", gdbpy_init_boolean_type, METH_VARARGS,
+    "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", gdbpy_init_float_type, METH_VARARGS,
+    "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", gdbpy_init_decfloat_type, METH_VARARGS,
+    "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", gdbpy_can_create_complex_type, METH_VARARGS,
+    "can_create_complex_type (type) -> bool\n\
+     Returns whether a given type can form a new complex type." },
+  { "init_complex_type", gdbpy_init_complex_type, METH_VARARGS,
+    "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", gdbpy_init_pointer_type, METH_VARARGS,
+    "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", gdbpy_init_fixed_point_type, METH_VARARGS,
+   "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\
-- 
2.37.3.windows.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH] Add support for creating new types from the Python API
  2023-01-06 20:00 ` Simon Marchi
  2023-01-11  0:58   ` [PATCH] Add support for creating new types from " Matheus Branco Borella
@ 2023-05-26  3:30   ` Matheus Branco Borella
  2023-08-07 14:53     ` Andrew Burgess
  1 sibling, 1 reply; 10+ messages in thread
From: Matheus Branco Borella @ 2023-05-26  3:30 UTC (permalink / raw)
  To: gdb-patches; +Cc: dark.ryu.550

From: "dark.ryu.550@gmail.com" <dark.ryu.550@gmail.com>

On 1/6/23 20:00, simark@simark.ca:
> Unfortunately, I am unable to apply this patch as well, please send it
> using git-send-email.

Should be all good to go now. I'm sorry for unearthing this patch after it's 
been so long, but I hope it's not (too much of) a problem. I've updated the old 
patch to work with the way symbol allocation is done now, since it changed from 
six months ago, and I've also added a test case for it.

> It would maybe be nice to be able to create arch-owned types too.  For
> instance, you could create types just after firing up GDB, without even
> having an objfile loaded.  It's not necessary to implement it at the
> same time, but does your approach leave us the option to do that at a
> later time?

Hmm, I think it shouldn't be a problem. The way it works now, it already uses
`type_allocator` to do most of the heavy lifting, which can handle both `
objfile`s and `arch`es. I can see a straightforward way to do that in using
keyword arguments (e.g. `objfile=` and `arch=`) to separate the two cases in 
Python and doing a check on the C side for which of the two was used.

---

This patch adds support for creating types from within the Python API. It does
so by exposing the `init_*_type` family of functions, defined in `gdbtypes.h` to
Python and having them return `gdb.Type` objects connected to the newly minted
types.

These functions are accessible in the root of the gdb module and all require
a reference to a `gdb.Objfile`. Types created from this API are exclusively
objfile-owned.

This patch also adds an extra type - `gdb.FloatFormat` - to support creation of
floating point types by letting users control the format from within Python. It
is missing, however, a way to specify half formats and validation functions.

It is important to note that types created using this interface are not
automatically registered as a symbol, and so, types will become unreachable
unless used to create a value that otherwise references it or saved in some way.

The main drawback of using the `init_*_type` family over implementing type
initialization by hand is that any type that's created gets immediately
allocated on its owner objfile's obstack, regardless of what its real
lifetime requirements are. The main implication of this is that types that
become unreachable will leak their memory for the lifetime of the objfile.

Keeping track of the initialization of the type by hand would require a
deeper change to the existing type object infrastructure. A bit too ambitious
for a first patch, I'd say.

if it were to be done though, we would gain the ability to only keep in the
obstack types that are known to be referenced in some other way - by allocating
and copying the data to the obstack as other objects are created that reference
it (eg. symbols).
---
 gdb/Makefile.in                      |   2 +
 gdb/python/py-float-format.c         | 321 +++++++++++++++++++++
 gdb/python/py-objfile.c              |  12 +
 gdb/python/py-type-init.c            | 409 +++++++++++++++++++++++++++
 gdb/python/python-internal.h         |  15 +
 gdb/python/python.c                  |  41 +++
 gdb/testsuite/gdb.python/py-type.exp |  10 +
 7 files changed, 810 insertions(+)
 create mode 100644 gdb/python/py-float-format.c
 create mode 100644 gdb/python/py-type-init.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 14b5dd0bad..108bcea69e 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -431,6 +431,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 \
diff --git a/gdb/python/py-float-format.c b/gdb/python/py-float-format.c
new file mode 100644
index 0000000000..8fe92980f1
--- /dev/null
+++ b/gdb/python/py-float-format.c
@@ -0,0 +1,321 @@
+/* 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);
+
+#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);                                                 \
+  }
+
+#define INSTANCE_FIELD_SETTER(getter_name, field_name, field_type, field_conv) \
+  static int                                                                   \
+  getter_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;
+}
+
+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;
+}
+
+/* Retrieves a pointer to the underlying float format structure. */
+
+struct floatformat *
+float_format_object_as_float_format (PyObject *self)
+{
+  if (!PyObject_IsInstance (self, (PyObject*) &float_format_object_type))
+    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 a \"true\" 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 }
+};
+
+static PyMethodDef float_format_object_methods[] =
+{
+  { NULL }
+};
+
+static PyNumberMethods float_format_object_as_number = {
+  nullptr,             /* nb_add */
+  nullptr,             /* nb_subtract */
+  nullptr,             /* nb_multiply */
+  nullptr,             /* nb_remainder */
+  nullptr,             /* nb_divmod */
+  nullptr,             /* nb_power */
+  nullptr,             /* nb_negative */
+  nullptr,             /* nb_positive */
+  nullptr,             /* nb_absolute */
+  nullptr,             /* nb_nonzero */
+  nullptr,             /* nb_invert */
+  nullptr,             /* nb_lshift */
+  nullptr,             /* nb_rshift */
+  nullptr,             /* nb_and */
+  nullptr,             /* nb_xor */
+  nullptr,             /* nb_or */
+  nullptr,             /* nb_int */
+  nullptr,             /* reserved */
+  nullptr,             /* nb_float */
+};
+
+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*/
+  &float_format_object_as_number, /*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 */
+  float_format_object_methods,    /* 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 */
+};
+
+
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index ad72f3f042..be2121c405 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -704,6 +704,18 @@ objfile_to_objfile_object (struct objfile *objfile)
   return gdbpy_ref<>::new_reference (result);
 }
 
+struct objfile *
+objfile_object_to_objfile (PyObject *self)
+{
+  if (!PyObject_TypeCheck (self, &objfile_object_type))
+    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)
 {
diff --git a/gdb/python/py-type-init.c b/gdb/python/py-type-init.c
new file mode 100644
index 0000000000..a18cce6e51
--- /dev/null
+++ b/gdb/python/py-type-init.c
@@ -0,0 +1,409 @@
+/* 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"
+
+
+/* Copies a null-terminated string into an objfile's obstack. */
+
+static const char *
+copy_string (struct objfile *objfile, const char *py_str)
+{
+  unsigned int len = strlen (py_str);
+  return obstack_strndup (&objfile->per_bfd->storage_obstack,
+                          py_str, len);
+}
+
+/* Creates a new type and returns a new gdb.Type associated with it. */
+
+PyObject *
+gdbpy_init_type (PyObject *self, PyObject *args)
+{
+  PyObject *objfile_object;
+  enum type_code code;
+  int bit_length;
+  const char *py_name;
+
+  if(!PyArg_ParseTuple (args, "Oiis", &objfile_object, &code, 
+                        &bit_length, &py_name))
+    return nullptr;
+
+  struct objfile* objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    return nullptr;
+
+  const char *name = copy_string (objfile, py_name);
+  struct type *type;
+  try
+    {
+      type_allocator allocator (objfile);
+      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 *objfile_object;
+  int bit_size;
+  int unsigned_p;
+  const char *py_name;
+
+  if (!PyArg_ParseTuple (args, "Oips", &objfile_object, &bit_size, 
+                         &unsigned_p, &py_name))
+    return nullptr;
+
+  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    return nullptr;
+
+  const char *name = copy_string (objfile, py_name);
+  struct type *type;
+  try
+    {
+      type_allocator allocator (objfile);
+      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 *objfile_object;
+  int bit_size;
+  int unsigned_p;
+  const char *py_name;
+
+  if (!PyArg_ParseTuple (args, "Oips", &objfile_object, &bit_size, 
+                         &unsigned_p, &py_name))
+    return nullptr;
+
+  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    return nullptr;
+
+  const char *name = copy_string (objfile, py_name);
+  struct type *type;
+  try
+    {
+      type_allocator allocator (objfile);
+      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 *objfile_object;
+  int bit_size;
+  int unsigned_p;
+  const char *py_name;
+
+  if (!PyArg_ParseTuple (args, "Oips", &objfile_object, &bit_size, 
+                         &unsigned_p, &py_name))
+    return nullptr;
+
+  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    return nullptr;
+
+  const char *name = copy_string (objfile, py_name);
+  struct type *type;
+  try
+    {
+      type_allocator allocator (objfile);
+      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 *objfile_object, *float_format_object;
+  const char *py_name;
+
+  if (!PyArg_ParseTuple (args, "OOs", &objfile_object, 
+                         &float_format_object, &py_name))
+    return nullptr;
+
+  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    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
+    (&objfile->objfile_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 (objfile->arch());
+  gdb_assert (endianness < BFD_ENDIAN_UNKNOWN);
+
+  const struct floatformat *per_endian[2] = { nullptr, nullptr };
+  per_endian[endianness] = ff;
+
+  const char *name = copy_string (objfile, py_name);
+  struct type *type;
+  try
+    {
+      type_allocator allocator (objfile);
+      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 *objfile_object;
+  int bit_length;
+  const char *py_name;
+
+  if (!PyArg_ParseTuple (args, "Ois", &objfile_object, &bit_length, &py_name))
+    return nullptr;
+
+  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    return nullptr;
+
+  const char *name = copy_string (objfile, py_name);
+  struct type *type;
+  try
+    {
+      type_allocator allocator (objfile);
+      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 *type_object;
+
+  if (!PyArg_ParseTuple (args, "O", &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 *type_object;
+  const char *py_name;
+
+  if (!PyArg_ParseTuple (args, "Os", &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 *objfile_object, *type_object;
+  int bit_length;
+  const char *py_name;
+
+  if (!PyArg_ParseTuple (args, "OOis", &objfile_object, &type_object, 
+                         &bit_length, &py_name))
+    return nullptr;
+
+  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    return nullptr;
+
+  struct type *type = type_object_to_type (type_object);
+  if (type == nullptr)
+    return nullptr;
+
+  const char *name = copy_string (objfile, py_name);
+  struct type *pointer_type = nullptr;
+  try
+    {
+      type_allocator allocator (objfile);
+      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 *objfile_object;
+  int bit_length;
+  int unsigned_p;
+  const char* py_name;
+
+  if (!PyArg_ParseTuple (args, "Oips", &objfile_object, &bit_length, 
+                         &unsigned_p, &py_name))
+    return nullptr;
+
+  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    return nullptr;
+
+  const char *name = copy_string (objfile, py_name);
+  struct type *type;
+  try
+    {
+      type = init_fixed_point_type (objfile, 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);
+}
+
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index dbd33570a7..73e2e6ce62 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -289,6 +289,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
@@ -431,6 +433,17 @@ 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 *gdbpy_init_integer_type (PyObject *self, PyObject *args);
+PyObject *gdbpy_init_character_type (PyObject *self, PyObject *args);
+PyObject *gdbpy_init_boolean_type (PyObject *self, PyObject *args);
+PyObject *gdbpy_init_float_type (PyObject *self, PyObject *args);
+PyObject *gdbpy_init_decfloat_type (PyObject *self, PyObject *args);
+PyObject *gdbpy_can_create_complex_type (PyObject *self, PyObject *args);
+PyObject *gdbpy_init_complex_type (PyObject *self, PyObject *args);
+PyObject *gdbpy_init_pointer_type (PyObject *self, PyObject *args);
+PyObject *gdbpy_init_fixed_point_type (PyObject *self, PyObject *args);
+
 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);
@@ -480,6 +493,8 @@ struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
 frame_info_ptr frame_object_to_frame_info (PyObject *frame_obj);
 struct gdbarch *arch_object_to_gdbarch (PyObject *obj);
+struct objfile *objfile_object_to_objfile (PyObject *self);
+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
diff --git a/gdb/python/python.c b/gdb/python/python.c
index fd5a920cbd..288c8b355c 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -2521,6 +2521,47 @@ Return current recording object." },
     "stop_recording () -> None.\n\
 Stop current recording." },
 
+  /* Type initialization functions. */
+  { "init_type", gdbpy_init_type, METH_VARARGS,
+    "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", gdbpy_init_integer_type, METH_VARARGS,
+    "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", gdbpy_init_character_type, METH_VARARGS,
+    "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", gdbpy_init_boolean_type, METH_VARARGS,
+    "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", gdbpy_init_float_type, METH_VARARGS,
+    "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", gdbpy_init_decfloat_type, METH_VARARGS,
+    "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", gdbpy_can_create_complex_type, METH_VARARGS,
+    "can_create_complex_type (type) -> bool\n\
+     Returns whether a given type can form a new complex type." },
+  { "init_complex_type", gdbpy_init_complex_type, METH_VARARGS,
+    "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", gdbpy_init_pointer_type, METH_VARARGS,
+    "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", gdbpy_init_fixed_point_type, METH_VARARGS,
+   "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\
diff --git a/gdb/testsuite/gdb.python/py-type.exp b/gdb/testsuite/gdb.python/py-type.exp
index c245d41a1a..aee2b4d60a 100644
--- a/gdb/testsuite/gdb.python/py-type.exp
+++ b/gdb/testsuite/gdb.python/py-type.exp
@@ -388,3 +388,13 @@ if { [build_inferior "${binfile}-cxx" "c++"] == 0 } {
       test_type_equality
   }
 }
+
+# Test python type construction
+gdb_test "python t = gdb.init_type(gdb.objfiles ()\[0\], gdb.TYPE_CODE_INT, 24, 'long short int')" \
+  "" "construct a new type from inside python"
+gdb_test "python print (t.code)" \
+  "8" "check the code for the python-constructed type"
+gdb_test "python print (t.sizeof)" \
+  "3" "check the size for the python-constructed type"
+gdb_test "python print (t.name)" \
+  "long short int" "check the name for the python-constructed type"
-- 
2.40.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PING] Re: [PATCH] Add support for creating new types from the Python API
  2023-01-11  0:58   ` [PATCH] Add support for creating new types from " Matheus Branco Borella
@ 2023-06-27  3:52     ` Matheus Branco Borella
  0 siblings, 0 replies; 10+ messages in thread
From: Matheus Branco Borella @ 2023-06-27  3:52 UTC (permalink / raw)
  To: gdb-patches

Following the contribution checklist, so, pinging this.

On Tue, Jan 10, 2023 at 9:58 PM Matheus Branco Borella
<dark.ryu.550@gmail.com> wrote:
>
> This patch adds support for creating types from within the Python API. It does
> so by exposing the `init_*_type` family of functions, defined in `gdbtypes.h` to
> Python and having them return `gdb.Type` objects connected to the newly minted
> types.
>
> These functions are accessible in the root of the gdb module and all require
> a reference to a `gdb.Objfile`. Types created from this API are exclusively
> objfile-owned.
>
> This patch also adds an extra type - `gdb.FloatFormat` - to support creation of
> floating point types by letting users control the format from within Python. It
> is missing, however, a way to specify half formats and validation functions.
>
> It is important to note that types created using this interface are not
> automatically registered as a symbol, and so, types will become unreachable
> unless used to create a value that otherwise references it or saved in some way.
>
> The main drawback of using the `init_*_type` family over implementing type
> initialization by hand is that any type that's created gets immediately
> allocated on its owner objfile's obstack, regardless of what its real
> lifetime requirements are. The main implication of this is that types that
> become unreachable will leak their memory for the lifetime of the objfile.
>
> Keeping track of the initialization of the type by hand would require a
> deeper change to the existing type object infrastructure. A bit too ambitious
> for a first patch, I'd say.
>
> if it were to be done though, we would gain the ability to only keep in the
> obstack types that are known to be referenced in some other way - by allocating
> and copying the data to the obstack as other objects are created that reference
> it (eg. symbols).
> ---
>  gdb/Makefile.in              |   2 +
>  gdb/python/py-float-format.c | 297 +++++++++++++++++++++++++++
>  gdb/python/py-objfile.c      |  12 ++
>  gdb/python/py-type-init.c    | 388 +++++++++++++++++++++++++++++++++++
>  gdb/python/python-internal.h |  17 ++
>  gdb/python/python.c          |  44 +++-
>  6 files changed, 759 insertions(+), 1 deletion(-)
>  create mode 100644 gdb/python/py-float-format.c
>  create mode 100644 gdb/python/py-type-init.c
>
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index fb4d42c7baa..789f7dce224 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -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 \
> diff --git a/gdb/python/py-float-format.c b/gdb/python/py-float-format.c
> new file mode 100644
> index 00000000000..e517e410899
> --- /dev/null
> +++ b/gdb/python/py-float-format.c
> @@ -0,0 +1,297 @@
> +/* 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. */
> +
> +int
> +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;
> +}
> +
> +#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);                                                 \
> +  }
> +
> +#define INSTANCE_FIELD_SETTER(getter_name, field_name, field_type, field_conv) \
> +  static int                                                                   \
> +  getter_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, u8"value must be an integer");
> +    return false;
> +  }
> +
> +  long native_val = PyLong_AsLong(object);
> +  if(native_val > (long)INT_MAX)
> +  {
> +    PyErr_SetString(PyExc_ValueError, u8"value is too large");
> +    return false;
> +  }
> +
> +  *val = (int)native_val;
> +  return true;
> +}
> +
> +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;
> +}
> +
> +/* Retrieves a pointer to the underlying float format structure. */
> +
> +struct floatformat *
> +float_format_object_as_float_format (PyObject *self)
> +{
> +  if (!PyObject_IsInstance (self, (PyObject*) &float_format_object_type))
> +    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 a \"true\" 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 }
> +};
> +
> +static PyMethodDef float_format_object_methods[] =
> +{
> +  { NULL }
> +};
> +
> +static PyNumberMethods float_format_object_as_number = {
> +  nullptr,             /* nb_add */
> +  nullptr,             /* nb_subtract */
> +  nullptr,             /* nb_multiply */
> +  nullptr,             /* nb_remainder */
> +  nullptr,             /* nb_divmod */
> +  nullptr,             /* nb_power */
> +  nullptr,             /* nb_negative */
> +  nullptr,             /* nb_positive */
> +  nullptr,             /* nb_absolute */
> +  nullptr,             /* nb_nonzero */
> +  nullptr,             /* nb_invert */
> +  nullptr,             /* nb_lshift */
> +  nullptr,             /* nb_rshift */
> +  nullptr,             /* nb_and */
> +  nullptr,             /* nb_xor */
> +  nullptr,             /* nb_or */
> +  nullptr,             /* nb_int */
> +  nullptr,             /* reserved */
> +  nullptr,             /* nb_float */
> +};
> +
> +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*/
> +  &float_format_object_as_number, /*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 */
> +  float_format_object_methods,    /* 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 */
> +};
> +
> +
> diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
> index c278925531b..28a7c9a7873 100644
> --- a/gdb/python/py-objfile.c
> +++ b/gdb/python/py-objfile.c
> @@ -704,6 +704,18 @@ objfile_to_objfile_object (struct objfile *objfile)
>    return gdbpy_ref<>::new_reference (result);
>  }
>
> +struct objfile *
> +objfile_object_to_objfile (PyObject *self)
> +{
> +  if (!PyObject_TypeCheck (self, &objfile_object_type))
> +    return nullptr;
> +
> +  auto objfile_object = (struct objfile_object*) self;
> +  OBJFPY_REQUIRE_VALID (objfile_object);
> +
> +  return objfile_object->objfile;
> +}
> +
>  int
>  gdbpy_initialize_objfile (void)
>  {
> diff --git a/gdb/python/py-type-init.c b/gdb/python/py-type-init.c
> new file mode 100644
> index 00000000000..f3b6813c3ad
> --- /dev/null
> +++ b/gdb/python/py-type-init.c
> @@ -0,0 +1,388 @@
> +/* 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"
> +
> +
> +/* Copies a null-terminated string into an objfile's obstack. */
> +
> +static const char *
> +copy_string (struct objfile *objfile, const char *py_str)
> +{
> +  unsigned int len = strlen (py_str);
> +  return obstack_strndup (&objfile->per_bfd->storage_obstack,
> +                          py_str, len);
> +}
> +
> +/* Creates a new type and returns a new gdb.Type associated with it. */
> +
> +PyObject *
> +gdbpy_init_type (PyObject *self, PyObject *args)
> +{
> +  PyObject *objfile_object;
> +  enum type_code code;
> +  int bit_length;
> +  const char *py_name;
> +
> +  if(!PyArg_ParseTuple (args, "Oiis", &objfile_object, &code, &bit_length, &py_name))
> +    return nullptr;
> +
> +  struct objfile* objfile = objfile_object_to_objfile (objfile_object);
> +  if (objfile == nullptr)
> +    return nullptr;
> +
> +  const char *name = copy_string (objfile, py_name);
> +  struct type *type;
> +  try
> +  {
> +    type = init_type (objfile, 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 *objfile_object;
> +  int bit_size;
> +  int unsigned_p;
> +  const char *py_name;
> +
> +  if (!PyArg_ParseTuple (args, "Oips", &objfile_object, &bit_size, &unsigned_p, &py_name))
> +    return nullptr;
> +
> +  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
> +  if (objfile == nullptr)
> +    return nullptr;
> +
> +  const char *name = copy_string (objfile, py_name);
> +  struct type *type;
> +  try
> +  {
> +    type = init_integer_type (objfile, 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 *objfile_object;
> +  int bit_size;
> +  int unsigned_p;
> +  const char *py_name;
> +
> +  if (!PyArg_ParseTuple (args, "Oips", &objfile_object, &bit_size, &unsigned_p, &py_name))
> +    return nullptr;
> +
> +  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
> +  if (objfile == nullptr)
> +    return nullptr;
> +
> +  const char *name = copy_string (objfile, py_name);
> +  struct type *type;
> +  try
> +  {
> +    type = init_character_type (objfile, 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 *objfile_object;
> +  int bit_size;
> +  int unsigned_p;
> +  const char *py_name;
> +
> +  if (!PyArg_ParseTuple (args, "Oips", &objfile_object, &bit_size, &unsigned_p, &py_name))
> +    return nullptr;
> +
> +  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
> +  if (objfile == nullptr)
> +    return nullptr;
> +
> +  const char *name = copy_string (objfile, py_name);
> +  struct type *type;
> +  try
> +  {
> +    type = init_boolean_type (objfile, 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 *objfile_object, *float_format_object;
> +  const char *py_name;
> +
> +  if (!PyArg_ParseTuple (args, "OOs", &objfile_object, &float_format_object, &py_name))
> +    return nullptr;
> +
> +  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
> +  if (objfile == nullptr)
> +    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
> +    (&objfile->objfile_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 (objfile->arch());
> +  gdb_assert (endianness < BFD_ENDIAN_UNKNOWN);
> +
> +  const struct floatformat *per_endian[2] = { nullptr, nullptr };
> +  per_endian[endianness] = ff;
> +
> +  const char *name = copy_string (objfile, py_name);
> +  struct type *type;
> +  try
> +  {
> +    type = init_float_type (objfile, -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 *objfile_object;
> +  int bit_length;
> +  const char *py_name;
> +
> +  if (!PyArg_ParseTuple (args, "Ois", &objfile_object, &bit_length, &py_name))
> +    return nullptr;
> +
> +  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
> +  if (objfile == nullptr)
> +    return nullptr;
> +
> +  const char *name = copy_string (objfile, py_name);
> +  struct type *type;
> +  try
> +  {
> +    type = init_decfloat_type (objfile, 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 *type_object;
> +
> +  if (!PyArg_ParseTuple (args, "O", &type_object))
> +    return nullptr;
> +
> +  struct type *type = type_object_to_type (type_object);
> +  if (type == nullptr)
> +    return nullptr;
> +
> +  bool can_create_complex;
> +  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 *type_object;
> +  const char *py_name;
> +
> +  if (!PyArg_ParseTuple (args, "Os", &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 *objfile_object, *type_object;
> +  int bit_length;
> +  const char *py_name;
> +
> +  if (!PyArg_ParseTuple (args, "OOis", &objfile_object, &type_object, &bit_length, &py_name))
> +    return nullptr;
> +
> +  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
> +  if (objfile == nullptr)
> +    return nullptr;
> +
> +  struct type *type = type_object_to_type (type_object);
> +  if (type == nullptr)
> +    return nullptr;
> +
> +  const char *name = copy_string (objfile, py_name);
> +  struct type *pointer_type;
> +  try
> +  {
> +    pointer_type = init_pointer_type (objfile, 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 *objfile_object;
> +  int bit_length;
> +  int unsigned_p;
> +  const char* py_name;
> +
> +  if (!PyArg_ParseTuple (args, "Oips", &objfile_object, &bit_length, &unsigned_p, &py_name))
> +    return nullptr;
> +
> +  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
> +  if (objfile == nullptr)
> +    return nullptr;
> +
> +  const char *name = copy_string (objfile, py_name);
> +  struct type *type;
> +  try
> +  {
> +    type = init_fixed_point_type (objfile, 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);
> +}
> diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
> index 06357cc8c0b..3877f8a7ca9 100644
> --- a/gdb/python/python-internal.h
> +++ b/gdb/python/python-internal.h
> @@ -289,6 +289,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
> @@ -431,6 +433,17 @@ 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 *gdbpy_init_integer_type (PyObject *self, PyObject *args);
> +PyObject *gdbpy_init_character_type (PyObject *self, PyObject *args);
> +PyObject *gdbpy_init_boolean_type (PyObject *self, PyObject *args);
> +PyObject *gdbpy_init_float_type (PyObject *self, PyObject *args);
> +PyObject *gdbpy_init_decfloat_type (PyObject *self, PyObject *args);
> +PyObject *gdbpy_can_create_complex_type (PyObject *self, PyObject *args);
> +PyObject *gdbpy_init_complex_type (PyObject *self, PyObject *args);
> +PyObject *gdbpy_init_pointer_type (PyObject *self, PyObject *args);
> +PyObject *gdbpy_init_fixed_point_type (PyObject *self, PyObject *args);
> +
>  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);
> @@ -481,6 +494,8 @@ struct symtab *symtab_object_to_symtab (PyObject *obj);
>  struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
>  frame_info_ptr frame_object_to_frame_info (PyObject *frame_obj);
>  struct gdbarch *arch_object_to_gdbarch (PyObject *obj);
> +struct objfile *objfile_object_to_objfile (PyObject *self);
> +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
> @@ -559,6 +574,8 @@ int gdbpy_initialize_micommands (void)
>  void gdbpy_finalize_micommands ();
>  int gdbpy_initialize_disasm ()
>    CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
> +int gdbpy_initialize_float_format ()
> +  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
>
>  PyMODINIT_FUNC gdbpy_events_mod_func ();
>
> diff --git a/gdb/python/python.c b/gdb/python/python.c
> index 4aa24421dec..1ed29ff4dea 100644
> --- a/gdb/python/python.c
> +++ b/gdb/python/python.c
> @@ -2153,7 +2153,8 @@ do_start_initialization ()
>        || gdbpy_initialize_membuf () < 0
>        || gdbpy_initialize_connection () < 0
>        || gdbpy_initialize_tui () < 0
> -      || gdbpy_initialize_micommands () < 0)
> +      || gdbpy_initialize_micommands () < 0
> +      || gdbpy_initialize_float_format() < 0)
>      return false;
>
>  #define GDB_PY_DEFINE_EVENT_TYPE(name, py_name, doc, base)     \
> @@ -2529,6 +2530,47 @@ Return current recording object." },
>      "stop_recording () -> None.\n\
>  Stop current recording." },
>
> +  /* Type initialization functions. */
> +  { "init_type", gdbpy_init_type, METH_VARARGS,
> +    "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", gdbpy_init_integer_type, METH_VARARGS,
> +    "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", gdbpy_init_character_type, METH_VARARGS,
> +    "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", gdbpy_init_boolean_type, METH_VARARGS,
> +    "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", gdbpy_init_float_type, METH_VARARGS,
> +    "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", gdbpy_init_decfloat_type, METH_VARARGS,
> +    "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", gdbpy_can_create_complex_type, METH_VARARGS,
> +    "can_create_complex_type (type) -> bool\n\
> +     Returns whether a given type can form a new complex type." },
> +  { "init_complex_type", gdbpy_init_complex_type, METH_VARARGS,
> +    "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", gdbpy_init_pointer_type, METH_VARARGS,
> +    "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", gdbpy_init_fixed_point_type, METH_VARARGS,
> +   "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\
> --
> 2.37.3.windows.1
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] Add support for creating new types from the Python API
  2023-05-26  3:30   ` Matheus Branco Borella
@ 2023-08-07 14:53     ` Andrew Burgess
  2023-08-08 21:00       ` [PATCH v2] " Matheus Branco Borella
  0 siblings, 1 reply; 10+ messages in thread
From: Andrew Burgess @ 2023-08-07 14:53 UTC (permalink / raw)
  To: Matheus Branco Borella via Gdb-patches, gdb-patches; +Cc: dark.ryu.550

Matheus Branco Borella via Gdb-patches <gdb-patches@sourceware.org>
writes:

> From: "dark.ryu.550@gmail.com" <dark.ryu.550@gmail.com>
>
> On 1/6/23 20:00, simark@simark.ca:
>> Unfortunately, I am unable to apply this patch as well, please send it
>> using git-send-email.
>
> Should be all good to go now. I'm sorry for unearthing this patch after it's 
> been so long, but I hope it's not (too much of) a problem. I've updated the old 
> patch to work with the way symbol allocation is done now, since it changed from 
> six months ago, and I've also added a test case for it.
>
>> It would maybe be nice to be able to create arch-owned types too.  For
>> instance, you could create types just after firing up GDB, without even
>> having an objfile loaded.  It's not necessary to implement it at the
>> same time, but does your approach leave us the option to do that at a
>> later time?
>
> Hmm, I think it shouldn't be a problem. The way it works now, it already uses
> `type_allocator` to do most of the heavy lifting, which can handle both `
> objfile`s and `arch`es. I can see a straightforward way to do that in using
> keyword arguments (e.g. `objfile=` and `arch=`) to separate the two cases in 
> Python and doing a check on the C side for which of the two was used.
>
> ---
>
> This patch adds support for creating types from within the Python API. It does
> so by exposing the `init_*_type` family of functions, defined in `gdbtypes.h` to
> Python and having them return `gdb.Type` objects connected to the newly minted
> types.
>
> These functions are accessible in the root of the gdb module and all require
> a reference to a `gdb.Objfile`. Types created from this API are exclusively
> objfile-owned.
>
> This patch also adds an extra type - `gdb.FloatFormat` - to support creation of
> floating point types by letting users control the format from within Python. It
> is missing, however, a way to specify half formats and validation functions.
>
> It is important to note that types created using this interface are not
> automatically registered as a symbol, and so, types will become unreachable
> unless used to create a value that otherwise references it or saved in some way.
>
> The main drawback of using the `init_*_type` family over implementing type
> initialization by hand is that any type that's created gets immediately
> allocated on its owner objfile's obstack, regardless of what its real
> lifetime requirements are. The main implication of this is that types that
> become unreachable will leak their memory for the lifetime of the objfile.

I'd soften this from "leak their memory" to "remain live" -- it just
feels like claiming there's a leak here is a little too harsh.

>
> Keeping track of the initialization of the type by hand would require a
> deeper change to the existing type object infrastructure. A bit too ambitious
> for a first patch, I'd say.
>
> if it were to be done though, we would gain the ability to only keep in the
> obstack types that are known to be referenced in some other way - by allocating
> and copying the data to the obstack as other objects are created that reference
> it (eg. symbols).
> ---
>  gdb/Makefile.in                      |   2 +
>  gdb/python/py-float-format.c         | 321 +++++++++++++++++++++
>  gdb/python/py-objfile.c              |  12 +
>  gdb/python/py-type-init.c            | 409 +++++++++++++++++++++++++++
>  gdb/python/python-internal.h         |  15 +
>  gdb/python/python.c                  |  41 +++
>  gdb/testsuite/gdb.python/py-type.exp |  10 +

Before this could be merged we would need at least:

  - Updates to the documentation,
  - A NEWS entry,
  - Significantly more tests.

I have a few observations, but I think it will be easier to review once
there are either some docs and tests that exercise all the parts, as
I'll be able to see how everything is intended to work together without
having to figure it out from the code.

>  7 files changed, 810 insertions(+)
>  create mode 100644 gdb/python/py-float-format.c
>  create mode 100644 gdb/python/py-type-init.c
>
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index 14b5dd0bad..108bcea69e 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -431,6 +431,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 \
> diff --git a/gdb/python/py-float-format.c b/gdb/python/py-float-format.c
> new file mode 100644
> index 0000000000..8fe92980f1
> --- /dev/null
> +++ b/gdb/python/py-float-format.c
> @@ -0,0 +1,321 @@
> +/* 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. */

Throughout this patch you have some lines that are longer than we like
for GDB.  Ideally we keep lines under 80 characters.  I'm not going to
point out every such line.

> +
> +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);
> +
> +#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);                                                 \
> +  }
> +
> +#define INSTANCE_FIELD_SETTER(getter_name, field_name, field_type, field_conv) \
> +  static int                                                                   \
> +  getter_name (PyObject *self, PyObject* value, void *closure)                 \

Probably setter_name would be better here.

Ideally every function and global macro or variable should have a
comment.  I think these would definitely be improved with a comment.

> +  {                                                                            \
> +    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);

The '||' operator should be placed at the start of the new line.  The
line wrapping here is a little too aggressive, better would be:

  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;

Operator at the start of a line again, and use () to encourage
alignment, so:

  *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;
> +}
> +
> +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;
> +}
> +
> +/* Retrieves a pointer to the underlying float format structure. */

Comments for extern functions should be placed in the header file, and a
comment here should just say:

  /* See python/python-internal.h.  */

I know there are lots of counter examples to this practice in
python-internal.h, but they are all older code.  Newer code is expected
to follow the above style -- and there are also examples of this in that
header too.

> +
> +struct floatformat *
> +float_format_object_as_float_format (PyObject *self)
> +{
> +  if (!PyObject_IsInstance (self, (PyObject*) &float_format_object_type))
> +    return nullptr;

I'm not sure this is right.  I believe PyObject_IsInstance can return 1
(if it is an instance), 0 (if it is not an instance), or -1 (on error).

So:

  if (PyObject_IsInstance (self, (PyObject*) &float_format_object_type) <= 0)
    return nullptr;

Might be better.  Except, an exception is only set for the -1 case, and
looking at the user of float_format_object_as_float_format, I think
there is an expectation that an exception will have been set, so you
might want to either always set an exception here, or only set an
exception for the 0 case?

Either way, this is exactly the sort of thing that the comment should be
saying, e.g.

  /* Retrieves a pointer to the float format structure represented by
     SELF, assuming SELF is of type gdb.XXXX.  If SELF is not of the
     correct type then return nullptr and set an exception.  */

Actually, looking at some of the other code, I wonder if the right thing
here is to switch to PyObject_TypeCheck?  This avoids (possibly) calling
back into Python code, and only returns 0/1.  We seem to use _TypeCheck
extensively in other places, is there a requirement for _IsInstance?

> +  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 a \"true\" 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 }
> +};
> +
> +static PyMethodDef float_format_object_methods[] =
> +{
> +  { NULL }
> +};
> +
> +static PyNumberMethods float_format_object_as_number = {
> +  nullptr,             /* nb_add */
> +  nullptr,             /* nb_subtract */
> +  nullptr,             /* nb_multiply */
> +  nullptr,             /* nb_remainder */
> +  nullptr,             /* nb_divmod */
> +  nullptr,             /* nb_power */
> +  nullptr,             /* nb_negative */
> +  nullptr,             /* nb_positive */
> +  nullptr,             /* nb_absolute */
> +  nullptr,             /* nb_nonzero */
> +  nullptr,             /* nb_invert */
> +  nullptr,             /* nb_lshift */
> +  nullptr,             /* nb_rshift */
> +  nullptr,             /* nb_and */
> +  nullptr,             /* nb_xor */
> +  nullptr,             /* nb_or */
> +  nullptr,             /* nb_int */
> +  nullptr,             /* reserved */
> +  nullptr,             /* nb_float */
> +};

I haven't dug into the implications of providing this structure with all
the fields set to nullptr vs just providing nullptr for the tp_as_number
field below.

However, given this is not common in the GDB code, I think, if there is
a reason for doing this, that it would be worth explaining in a comment.

Similarly, you have an empty float_format_object_methods list, in other
places we just set tp_methods to nullptr -- so is there a reason for the
approach you've taken here?

> +
> +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*/
> +  &float_format_object_as_number, /*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 */
> +  float_format_object_methods,    /* 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 */
> +};
> +
> +
> diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
> index ad72f3f042..be2121c405 100644
> --- a/gdb/python/py-objfile.c
> +++ b/gdb/python/py-objfile.c
> @@ -704,6 +704,18 @@ objfile_to_objfile_object (struct objfile *objfile)
>    return gdbpy_ref<>::new_reference (result);
>  }
>  
> +struct objfile *
> +objfile_object_to_objfile (PyObject *self)
> +{
> +  if (!PyObject_TypeCheck (self, &objfile_object_type))
> +    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)
>  {
> diff --git a/gdb/python/py-type-init.c b/gdb/python/py-type-init.c
> new file mode 100644
> index 0000000000..a18cce6e51
> --- /dev/null
> +++ b/gdb/python/py-type-init.c
> @@ -0,0 +1,409 @@
> +/* 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"
> +
> +
> +/* Copies a null-terminated string into an objfile's obstack. */
> +
> +static const char *
> +copy_string (struct objfile *objfile, const char *py_str)
> +{
> +  unsigned int len = strlen (py_str);
> +  return obstack_strndup (&objfile->per_bfd->storage_obstack,
> +                          py_str, len);
> +}
> +
> +/* Creates a new type and returns a new gdb.Type associated with it. */
> +
> +PyObject *
> +gdbpy_init_type (PyObject *self, PyObject *args)
> +{
> +  PyObject *objfile_object;
> +  enum type_code code;
> +  int bit_length;
> +  const char *py_name;
> +
> +  if(!PyArg_ParseTuple (args, "Oiis", &objfile_object, &code, 
> +                        &bit_length, &py_name))
> +    return nullptr;

I'm a huge fan of named arguments, and would like to see all of these
converted to use named arguments.

With an eye to Simon's suggestion, I would be tempted to name the first
argument `owner` maybe?  We could then use PyObject_TypeCheck to decide
if the owner is a gdb.Objfile or a gdb.Architecture.

I agree that supporting gdb.Architecture isn't a requirement for getting
the patch merged, but I don't want to get stuck with an API that doesn't
quite work, so if you were willing to give that a go, that would be great.

> +
> +  struct objfile* objfile = objfile_object_to_objfile (objfile_object);
> +  if (objfile == nullptr)
> +    return nullptr;
> +
> +  const char *name = copy_string (objfile, py_name);
> +  struct type *type;
> +  try
> +    {
> +      type_allocator allocator (objfile);
> +      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 *objfile_object;
> +  int bit_size;
> +  int unsigned_p;
> +  const char *py_name;
> +
> +  if (!PyArg_ParseTuple (args, "Oips", &objfile_object, &bit_size, 
> +                         &unsigned_p, &py_name))
> +    return nullptr;
> +
> +  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
> +  if (objfile == nullptr)
> +    return nullptr;
> +
> +  const char *name = copy_string (objfile, py_name);
> +  struct type *type;
> +  try
> +    {
> +      type_allocator allocator (objfile);
> +      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 *objfile_object;
> +  int bit_size;
> +  int unsigned_p;
> +  const char *py_name;
> +
> +  if (!PyArg_ParseTuple (args, "Oips", &objfile_object, &bit_size, 
> +                         &unsigned_p, &py_name))
> +    return nullptr;
> +
> +  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
> +  if (objfile == nullptr)
> +    return nullptr;
> +
> +  const char *name = copy_string (objfile, py_name);
> +  struct type *type;
> +  try
> +    {
> +      type_allocator allocator (objfile);
> +      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 *objfile_object;
> +  int bit_size;
> +  int unsigned_p;
> +  const char *py_name;
> +
> +  if (!PyArg_ParseTuple (args, "Oips", &objfile_object, &bit_size, 
> +                         &unsigned_p, &py_name))
> +    return nullptr;
> +
> +  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
> +  if (objfile == nullptr)
> +    return nullptr;
> +
> +  const char *name = copy_string (objfile, py_name);
> +  struct type *type;
> +  try
> +    {
> +      type_allocator allocator (objfile);
> +      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 *objfile_object, *float_format_object;
> +  const char *py_name;
> +
> +  if (!PyArg_ParseTuple (args, "OOs", &objfile_object, 
> +                         &float_format_object, &py_name))
> +    return nullptr;
> +
> +  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
> +  if (objfile == nullptr)
> +    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
> +    (&objfile->objfile_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 (objfile->arch());
> +  gdb_assert (endianness < BFD_ENDIAN_UNKNOWN);
> +
> +  const struct floatformat *per_endian[2] = { nullptr, nullptr };
> +  per_endian[endianness] = ff;
> +
> +  const char *name = copy_string (objfile, py_name);
> +  struct type *type;
> +  try
> +    {
> +      type_allocator allocator (objfile);
> +      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 *objfile_object;
> +  int bit_length;
> +  const char *py_name;
> +
> +  if (!PyArg_ParseTuple (args, "Ois", &objfile_object, &bit_length, &py_name))
> +    return nullptr;
> +
> +  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
> +  if (objfile == nullptr)
> +    return nullptr;
> +
> +  const char *name = copy_string (objfile, py_name);
> +  struct type *type;
> +  try
> +    {
> +      type_allocator allocator (objfile);
> +      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 *type_object;
> +
> +  if (!PyArg_ParseTuple (args, "O", &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 *type_object;
> +  const char *py_name;
> +
> +  if (!PyArg_ParseTuple (args, "Os", &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)
> +{

I guess I was a little surprised to see this one.  I'd sort-of expected
that to get a pointer type you'd create a new type and then do:

  pointer_type = gdb.init_*_type(....).pointer()

I guess this code below does allow for different pointer sizes ... and
there's even a FIXME comment in gdbtypes.c pointing out that GDB only
supports a single pointer size, so maybe this is working towards closing
that issue.

But I'm not sure how these pointers would be used ... maybe some tests
will give examples of how this is different to calling
gdb.Type.pointer() then I'll understand...

> +  PyObject *objfile_object, *type_object;
> +  int bit_length;
> +  const char *py_name;
> +
> +  if (!PyArg_ParseTuple (args, "OOis", &objfile_object, &type_object, 
> +                         &bit_length, &py_name))
> +    return nullptr;
> +
> +  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
> +  if (objfile == nullptr)
> +    return nullptr;
> +
> +  struct type *type = type_object_to_type (type_object);
> +  if (type == nullptr)
> +    return nullptr;
> +
> +  const char *name = copy_string (objfile, py_name);
> +  struct type *pointer_type = nullptr;
> +  try
> +    {
> +      type_allocator allocator (objfile);
> +      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 *objfile_object;
> +  int bit_length;
> +  int unsigned_p;
> +  const char* py_name;
> +
> +  if (!PyArg_ParseTuple (args, "Oips", &objfile_object, &bit_length, 
> +                         &unsigned_p, &py_name))
> +    return nullptr;
> +
> +  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
> +  if (objfile == nullptr)
> +    return nullptr;
> +
> +  const char *name = copy_string (objfile, py_name);
> +  struct type *type;
> +  try
> +    {
> +      type = init_fixed_point_type (objfile, 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);
> +}
> +
> diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
> index dbd33570a7..73e2e6ce62 100644
> --- a/gdb/python/python-internal.h
> +++ b/gdb/python/python-internal.h
> @@ -289,6 +289,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
> @@ -431,6 +433,17 @@ 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 *gdbpy_init_integer_type (PyObject *self, PyObject *args);
> +PyObject *gdbpy_init_character_type (PyObject *self, PyObject *args);
> +PyObject *gdbpy_init_boolean_type (PyObject *self, PyObject *args);
> +PyObject *gdbpy_init_float_type (PyObject *self, PyObject *args);
> +PyObject *gdbpy_init_decfloat_type (PyObject *self, PyObject *args);
> +PyObject *gdbpy_can_create_complex_type (PyObject *self, PyObject *args);
> +PyObject *gdbpy_init_complex_type (PyObject *self, PyObject *args);
> +PyObject *gdbpy_init_pointer_type (PyObject *self, PyObject *args);
> +PyObject *gdbpy_init_fixed_point_type (PyObject *self, PyObject *args);
> +
>  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);
> @@ -480,6 +493,8 @@ struct symtab *symtab_object_to_symtab (PyObject *obj);
>  struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
>  frame_info_ptr frame_object_to_frame_info (PyObject *frame_obj);
>  struct gdbarch *arch_object_to_gdbarch (PyObject *obj);
> +struct objfile *objfile_object_to_objfile (PyObject *self);
> +struct floatformat *float_format_object_as_float_format (PyObject *self);

These two should be marked 'extern' and (as I said earlier) should have
a their comments here.

>  
>  /* Convert Python object OBJ to a program_space pointer.  OBJ must be a
>     gdb.Progspace reference.  Return nullptr if the gdb.Progspace is not
> diff --git a/gdb/python/python.c b/gdb/python/python.c
> index fd5a920cbd..288c8b355c 100644
> --- a/gdb/python/python.c
> +++ b/gdb/python/python.c
> @@ -2521,6 +2521,47 @@ Return current recording object." },
>      "stop_recording () -> None.\n\
>  Stop current recording." },
>  
> +  /* Type initialization functions. */
> +  { "init_type", gdbpy_init_type, METH_VARARGS,
> +    "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", gdbpy_init_integer_type, METH_VARARGS,
> +    "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", gdbpy_init_character_type, METH_VARARGS,
> +    "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", gdbpy_init_boolean_type, METH_VARARGS,
> +    "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", gdbpy_init_float_type, METH_VARARGS,
> +    "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", gdbpy_init_decfloat_type, METH_VARARGS,
> +    "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", gdbpy_can_create_complex_type, METH_VARARGS,
> +    "can_create_complex_type (type) -> bool\n\
> +     Returns whether a given type can form a new complex type." },
> +  { "init_complex_type", gdbpy_init_complex_type, METH_VARARGS,
> +    "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", gdbpy_init_pointer_type, METH_VARARGS,
> +    "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", gdbpy_init_fixed_point_type, METH_VARARGS,
> +   "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\
> diff --git a/gdb/testsuite/gdb.python/py-type.exp b/gdb/testsuite/gdb.python/py-type.exp
> index c245d41a1a..aee2b4d60a 100644
> --- a/gdb/testsuite/gdb.python/py-type.exp
> +++ b/gdb/testsuite/gdb.python/py-type.exp
> @@ -388,3 +388,13 @@ if { [build_inferior "${binfile}-cxx" "c++"] == 0 } {
>        test_type_equality
>    }
>  }
> +
> +# Test python type construction
> +gdb_test "python t = gdb.init_type(gdb.objfiles ()\[0\], gdb.TYPE_CODE_INT, 24, 'long short int')" \
> +  "" "construct a new type from inside python"
> +gdb_test "python print (t.code)" \
> +  "8" "check the code for the python-constructed type"

Rather than including '8' in here, I wonder if:

  gdb_test "python print (t.code == gdb.TYPE_CODE_INT)" \
    "True" "check the code for the python-constructed type"

would be better?

Thanks,
Andrew

> +gdb_test "python print (t.sizeof)" \
> +  "3" "check the size for the python-constructed type"
> +gdb_test "python print (t.name)" \
> +  "long short int" "check the name for the python-constructed type"
> -- 
> 2.40.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2] Add support for creating new types from the Python API
  2023-08-07 14:53     ` Andrew Burgess
@ 2023-08-08 21:00       ` Matheus Branco Borella
  2024-01-13  1:37         ` [PATCH v3] " Matheus Branco Borella
  0 siblings, 1 reply; 10+ messages in thread
From: Matheus Branco Borella @ 2023-08-08 21:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: aburgess, disconnect3d, Matheus Branco Borella

Andrew Burgess <aburgess@redhat.com> wrote:
> I'd soften this from "leak their memory" to "remain live" -- it just
> feels like claiming there's a leak here is a little too harsh.

Fair enough. When I wrote that I was coming from the perspective that, while the
memory will get freed eventually, I'd guess that for the average user that will
only happen when GDB exists. So it would be effectively lost anyway. But, yes,
it makes more sense to describe it as remaining live.

> I have a few observations, but I think it will be easier to review once
> there are either some docs and tests that exercise all the parts, as
> I'll be able to see how everything is intended to work together without
> having to figure it out from the code.

I've added a far more complete set of tests, added docs and a news file entry.
Let me know if there's anything else you'd like me to add or something that's
there but need clarifying or changing.

Additionally, I think all of your formatting and styling concerns should have
been addressed in this new version. I might have still missed something though,
since all of my lines came out to less than or equal to 80 characters long in
the original patch, so I could be counting them differently. Just to be safe,
I've made it so that lines in this patch are always strictly less than 80 
characters long.

> Actually, looking at some of the other code, I wonder if the right thing
> here is to switch to PyObject_TypeCheck?

That makes it much more consistent, yes. I've changed to using that.

> I haven't dug into the implications of providing this structure with all
> the fields set to nullptr vs just providing nullptr for the tp_as_number
> field below.

While it wouldn't be a problem, those lists and structures being there was a
consequence of how I'd originally planned to expose things to Python through 
them, but ended up never doing so and forgetting about it. Since they were
unused, I took them out. Thanks for pointing it out.

> But I'm not sure how these pointers would be used ... maybe some tests
> will give examples of how this is different to calling
> gdb.Type.pointer() then I'll understand...

What I had in mind was representing pointers types in environments that have
different pointer types depending on the mode of execution. I'm not sure how I
could test it though, or if this is redundant for that use case.

---
This patch adds support for creating types from within the Python API. It does
so by exposing the `init_*_type` family of functions, defined in `gdbtypes.h` to
Python and having them return `gdb.Type` objects connected to the newly minted
types.

These functions are accessible in the root of the gdb module and all require
a reference to either a `gdb.Objfile` or a `gdb.Architecture`. Types created
from them will be owned by the object passed to the function.

This patch also adds an extra type - `gdb.FloatFormat` - to support creation of
floating point types by letting users control the format from within Python. It
is missing, however, a way to specify half formats and validation functions.

It is important to note that types created using this interface are not
automatically registered as a symbol, and so, types will become unreachable
unless used to create a value that otherwise references it or saved in some way.

The main drawback of using the `init_*_type` family over implementing type
initialization by hand is that any type that's created gets immediately
allocated on its owner's obstack, regardless of what its real lifetime
requirements are. The main implication of this is that types that become
unreachable will remain live for the lifetime of the owner.

Keeping track of the initialization of the type by hand would require a
deeper change to the existing type object infrastructure. A bit too ambitious
for a first patch, I'd say.

If it were to be done though, we would gain the ability to only keep in the
obstack types that are known to be referenced in some other way - by allocating
and copying the data to the obstack as other objects are created that reference
it (eg. symbols).
---
 gdb/Makefile.in                           |   2 +
 gdb/NEWS                                  |  16 +
 gdb/doc/python.texi                       | 237 ++++++++++
 gdb/python/py-float-format.c              | 307 +++++++++++++
 gdb/python/py-objfile.c                   |  17 +
 gdb/python/py-type-init.c                 | 516 ++++++++++++++++++++++
 gdb/python/python-internal.h              |  34 ++
 gdb/python/python.c                       |  50 +++
 gdb/testsuite/gdb.python/py-type-init.c   |  21 +
 gdb/testsuite/gdb.python/py-type-init.exp | 132 ++++++
 10 files changed, 1332 insertions(+)
 create mode 100644 gdb/python/py-float-format.c
 create mode 100644 gdb/python/py-type-init.c
 create mode 100644 gdb/testsuite/gdb.python/py-type-init.c
 create mode 100644 gdb/testsuite/gdb.python/py-type-init.exp

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 14b5dd0bad..108bcea69e 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -431,6 +431,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 \
diff --git a/gdb/NEWS b/gdb/NEWS
index 6aa0d5171f..7d461ff29f 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -170,6 +170,22 @@ info main
      (program-counter) values, and can be used as the frame-id when
      calling gdb.PendingFrame.create_unwind_info.
 
+  ** 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.
+
 *** Changes in GDB 13
 
 * MI version 1 is deprecated, and will be removed in GDB 14.
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 1113591065..3f3a9a2220 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -1665,6 +1665,243 @@ 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} makes available functionality to create new types from
+inside Python.
+
+The following type creation functions are available in the @code{gdb}
+module:
+
+@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 @code{owner}, with the given type code,
+@code{name} and size.
+
+@code{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.
+
+@code{type_code} is one of the @code{TYPE_CODE_} constants defined in
+@xref{Types In Python}.
+
+@code{bit_size} is the size of instances of the newly created type, in
+bits. Currently, accepted values are limited to multiples of 8.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@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 @code{owner}, with the given
+@code{name}, size and signedness.
+
+@code{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.
+
+@code{bit_size} is the size of instances of the newly created type, in
+bits. Currently, accepted values are limited to multiples of 8.
+
+@code{unsigned} is a boolean indicating whether the type corresponds to
+a signed or unsigned value.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@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 @code{owner}, with the given
+@code{name}, size and signedness.
+
+@code{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.
+
+@code{bit_size} is the size of instances of the newly created type, in
+bits. Currently, accepted values are limited to multiples of 8.
+
+@code{unsigned} is a boolean indicating whether the type corresponds to
+a signed or unsigned value.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@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 @code{owner}, with the given
+@code{name}, size and signedness.
+
+@code{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.
+
+@code{bit_size} is the size of instances of the newly created type, in
+bits. Currently, accepted values are limited to multiples of 8.
+
+@code{unsigned} is a boolean indicating whether the type corresponds to
+a signed or unsigned value.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@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
+character type owned by the given @code{owner}, with the given
+@code{name} and @code{format}.
+
+@code{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.
+
+@code{format} is an reference to a @code{gdb.FloatFormat} object, as
+described below.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@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 @code{owner}, with the
+given @code{name} and size.
+
+@code{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.
+
+@code{bit_size} is the size of instances of the newly created type, in
+bits. Currently, accepted values are limited to multiples of 8.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@end defun
+
+@findex gdb.can_create_complex_type
+@defun gdb.can_create_complex_type (type)
+This function returns a boolean indicating whether @code{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 @code{name} based on the given base
+@code{type}.
+
+The newly created type will be owned by the same object as the base
+type that was used to create it.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@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 @code{target} and is owned by the given
+@code{owner}, with the given @code{name} and size.
+
+@code{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.
+
+@code{target} is a @code{gdb.Type} object, corresponding to the type
+that will be pointed to by the newly created pointer type.
+
+@code{bit_size} is the size of instances of the newly created type, in
+bits. Currently, accepted values are limited to multiples of 8.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@end defun
+
+@findex gdb.init_boolean_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 @code{owner}, with the given
+@code{name}, size and signedness.
+
+@code{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.
+
+@code{bit_size} is the size of instances of the newly created type, in
+bits. Currently, accepted values are limited to multiples of 8.
+
+@code{unsigned} is a boolean indicating whether the type corresponds to
+a signed or unsigned value.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@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
diff --git a/gdb/python/py-float-format.c b/gdb/python/py-float-format.c
new file mode 100644
index 0000000000..984b96361a
--- /dev/null
+++ b/gdb/python/py-float-format.c
@@ -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 */
+};
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index ad72f3f042..f440a538c1 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -704,6 +704,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)
 {
diff --git a/gdb/python/py-type-init.c b/gdb/python/py-type-init.c
new file mode 100644
index 0000000000..9c26f29b2c
--- /dev/null
+++ b/gdb/python/py-type-init.c
@@ -0,0 +1,516 @@
+/* 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)
+      return type_allocator (this->owner.objfile);
+
+    /* 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 *objfile_object;
+  int bit_length;
+  int unsigned_p;
+  const char* py_name;
+
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "Oips", keywords,
+					&objfile_object, &bit_length,
+					&unsigned_p, &py_name))
+    return nullptr;
+
+  struct objfile *objfile = objfile_object_to_objfile (objfile_object);
+  if (objfile == nullptr)
+    return nullptr;
+
+  unsigned int len = strlen (py_name);
+  const char *name = obstack_strndup (&objfile->objfile_obstack, py_name, len);
+  struct type *type;
+  try
+    {
+      type = init_fixed_point_type (objfile, 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);
+}
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index dbd33570a7..c355bed212 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -289,6 +289,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
@@ -431,6 +433,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);
@@ -481,6 +503,18 @@ struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
 frame_info_ptr frame_object_to_frame_info (PyObject *frame_obj);
 struct gdbarch *arch_object_to_gdbarch (PyObject *obj);
 
+/* 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
diff --git a/gdb/python/python.c b/gdb/python/python.c
index fd5a920cbd..22a1ca7184 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -2521,6 +2521,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\
diff --git a/gdb/testsuite/gdb.python/py-type-init.c b/gdb/testsuite/gdb.python/py-type-init.c
new file mode 100644
index 0000000000..010e62bd24
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-type-init.c
@@ -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;
+}
diff --git a/gdb/testsuite/gdb.python/py-type-init.exp b/gdb/testsuite/gdb.python/py-type-init.exp
new file mode 100644
index 0000000000..8ef3c2c57a
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-type-init.exp
@@ -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()"
+}
-- 
2.41.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v3] Add support for creating new types from the Python API
  2023-08-08 21:00       ` [PATCH v2] " Matheus Branco Borella
@ 2024-01-13  1:37         ` Matheus Branco Borella
  2024-01-13  7:21           ` Eli Zaretskii
  0 siblings, 1 reply; 10+ messages in thread
From: Matheus Branco Borella @ 2024-01-13  1:37 UTC (permalink / raw)
  To: gdb-patches; +Cc: aburgess, Matheus Branco Borella

I had to walk away from this for a while. I'm pinging it now and I've updated
the code so that it works on master.

This patch adds support for creating types from within the Python API. It does
so by exposing the `init_*_type` family of functions, defined in `gdbtypes.h` to
Python and having them return `gdb.Type` objects connected to the newly minted
types.

These functions are accessible in the root of the gdb module and all require
a reference to either a `gdb.Objfile` or a `gdb.Architecture`. Types created
from them will be owned by the object passed to the function.

This patch also adds an extra type - `gdb.FloatFormat` - to support creation of
floating point types by letting users control the format from within Python. It
is missing, however, a way to specify half formats and validation functions.

It is important to note that types created using this interface are not
automatically registered as a symbol, and so, types will become unreachable
unless used to create a value that otherwise references it or saved in some way.

The main drawback of using the `init_*_type` family over implementing type
initialization by hand is that any type that's created gets immediately
allocated on its owner's obstack, regardless of what its real lifetime
requirements are. The main implication of this is that types that become
unreachable will remain live for the lifetime of the owner.

Keeping track of the initialization of the type by hand would require a
deeper change to the existing type object infrastructure. A bit too ambitious
for a first patch, I'd say.

If it were to be done though, we would gain the ability to only keep in the
obstack types that are known to be referenced in some other way - by allocating
and copying the data to the obstack as other objects are created that reference
it (eg. symbols).
---
 gdb/Makefile.in                           |   2 +
 gdb/NEWS                                  |  16 +
 gdb/doc/python.texi                       | 237 ++++++++++
 gdb/python/py-float-format.c              | 307 +++++++++++++
 gdb/python/py-objfile.c                   |  17 +
 gdb/python/py-type-init.c                 | 520 ++++++++++++++++++++++
 gdb/python/python-internal.h              |  34 ++
 gdb/python/python.c                       |  50 +++
 gdb/testsuite/gdb.python/py-type-init.c   |  21 +
 gdb/testsuite/gdb.python/py-type-init.exp | 132 ++++++
 10 files changed, 1336 insertions(+)
 create mode 100644 gdb/python/py-float-format.c
 create mode 100644 gdb/python/py-type-init.c
 create mode 100644 gdb/testsuite/gdb.python/py-type-init.c
 create mode 100644 gdb/testsuite/gdb.python/py-type-init.exp

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 195f3a2e2d1..50a758c802b 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -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 \
diff --git a/gdb/NEWS b/gdb/NEWS
index 11cd6c0663e..e21cca24422 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -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.
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index d74defeec0c..8c2cd393e0c 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -1743,6 +1743,243 @@ 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} makes available functionality to create new types from
+inside Python.
+
+The following type creation functions are available in the @code{gdb}
+module:
+
+@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 @code{owner}, with the given type code,
+@code{name} and size.
+
+@code{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.
+
+@code{type_code} is one of the @code{TYPE_CODE_} constants defined in
+@xref{Types In Python}.
+
+@code{bit_size} is the size of instances of the newly created type, in
+bits. Currently, accepted values are limited to multiples of 8.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@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 @code{owner}, with the given
+@code{name}, size and signedness.
+
+@code{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.
+
+@code{bit_size} is the size of instances of the newly created type, in
+bits. Currently, accepted values are limited to multiples of 8.
+
+@code{unsigned} is a boolean indicating whether the type corresponds to
+a signed or unsigned value.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@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 @code{owner}, with the given
+@code{name}, size and signedness.
+
+@code{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.
+
+@code{bit_size} is the size of instances of the newly created type, in
+bits. Currently, accepted values are limited to multiples of 8.
+
+@code{unsigned} is a boolean indicating whether the type corresponds to
+a signed or unsigned value.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@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 @code{owner}, with the given
+@code{name}, size and signedness.
+
+@code{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.
+
+@code{bit_size} is the size of instances of the newly created type, in
+bits. Currently, accepted values are limited to multiples of 8.
+
+@code{unsigned} is a boolean indicating whether the type corresponds to
+a signed or unsigned value.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@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
+character type owned by the given @code{owner}, with the given
+@code{name} and @code{format}.
+
+@code{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.
+
+@code{format} is an reference to a @code{gdb.FloatFormat} object, as
+described below.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@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 @code{owner}, with the
+given @code{name} and size.
+
+@code{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.
+
+@code{bit_size} is the size of instances of the newly created type, in
+bits. Currently, accepted values are limited to multiples of 8.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@end defun
+
+@findex gdb.can_create_complex_type
+@defun gdb.can_create_complex_type (type)
+This function returns a boolean indicating whether @code{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 @code{name} based on the given base
+@code{type}.
+
+The newly created type will be owned by the same object as the base
+type that was used to create it.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@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 @code{target} and is owned by the given
+@code{owner}, with the given @code{name} and size.
+
+@code{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.
+
+@code{target} is a @code{gdb.Type} object, corresponding to the type
+that will be pointed to by the newly created pointer type.
+
+@code{bit_size} is the size of instances of the newly created type, in
+bits. Currently, accepted values are limited to multiples of 8.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@end defun
+
+@findex gdb.init_boolean_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 @code{owner}, with the given
+@code{name}, size and signedness.
+
+@code{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.
+
+@code{bit_size} is the size of instances of the newly created type, in
+bits. Currently, accepted values are limited to multiples of 8.
+
+@code{unsigned} is a boolean indicating whether the type corresponds to
+a signed or unsigned value.
+
+This function returns an instance of @code{gdb.Type}, and will throw an
+exception in case of an error.
+@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
diff --git a/gdb/python/py-float-format.c b/gdb/python/py-float-format.c
new file mode 100644
index 00000000000..984b96361a7
--- /dev/null
+++ b/gdb/python/py-float-format.c
@@ -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 */
+};
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index bb5d0d92aba..71d840c3e00 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -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)
 {
diff --git a/gdb/python/py-type-init.c b/gdb/python/py-type-init.c
new file mode 100644
index 00000000000..58f29393413
--- /dev/null
+++ b/gdb/python/py-type-init.c
@@ -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);
+}
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 14e15574685..51e1202d5bd 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -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
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 2ca3c50afd4..1ccba1ca519 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -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\
diff --git a/gdb/testsuite/gdb.python/py-type-init.c b/gdb/testsuite/gdb.python/py-type-init.c
new file mode 100644
index 00000000000..010e62bd248
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-type-init.c
@@ -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;
+}
diff --git a/gdb/testsuite/gdb.python/py-type-init.exp b/gdb/testsuite/gdb.python/py-type-init.exp
new file mode 100644
index 00000000000..8ef3c2c57af
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-type-init.exp
@@ -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()"
+}
-- 
2.40.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] Add support for creating new types from the Python API
  2024-01-13  1:37         ` [PATCH v3] " Matheus Branco Borella
@ 2024-01-13  7:21           ` Eli Zaretskii
  2024-01-16  4:55             ` Matheus Branco Borella
  0 siblings, 1 reply; 10+ messages in thread
From: Eli Zaretskii @ 2024-01-13  7:21 UTC (permalink / raw)
  To: Matheus Branco Borella; +Cc: gdb-patches, aburgess

> From: Matheus Branco Borella <dark.ryu.550@gmail.com>
> Cc: aburgess@redhat.com,
> 	Matheus Branco Borella <dark.ryu.550@gmail.com>
> Date: Fri, 12 Jan 2024 22:37:57 -0300
> 
>  gdb/Makefile.in                           |   2 +
>  gdb/NEWS                                  |  16 +
>  gdb/doc/python.texi                       | 237 ++++++++++
>  gdb/python/py-float-format.c              | 307 +++++++++++++
>  gdb/python/py-objfile.c                   |  17 +
>  gdb/python/py-type-init.c                 | 520 ++++++++++++++++++++++
>  gdb/python/python-internal.h              |  34 ++
>  gdb/python/python.c                       |  50 +++
>  gdb/testsuite/gdb.python/py-type-init.c   |  21 +
>  gdb/testsuite/gdb.python/py-type-init.exp | 132 ++++++
>  10 files changed, 1336 insertions(+)
>  create mode 100644 gdb/python/py-float-format.c
>  create mode 100644 gdb/python/py-type-init.c
>  create mode 100644 gdb/testsuite/gdb.python/py-type-init.c
>  create mode 100644 gdb/testsuite/gdb.python/py-type-init.exp

Thanks.

> diff --git a/gdb/NEWS b/gdb/NEWS
> index 11cd6c0663e..e21cca24422 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -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:
             ^^
Our convention is to leave two spaces between sentences.

> +@node Creating Types In Python
> +@subsubsection Creating Types In Python
> +@cindex creating types in Python
> +@cindex Python, working with types
> +
> +@value{GDBN} makes available functionality to create new types from
> +inside Python.

This is awkward English.  Suggest to reword:

  @value{GDBN} allows creation of new types from Python extensions.

> +The following type creation functions are available in the @code{gdb}
> +module:

  The following functions available in the @code{gdb} module create
  new types:

> +@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 @code{owner}, with the given type code,
> +@code{name} and size.

The arguments should have the @var markup, not @code.  So:

  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 @var{bit_size}.

Likewise in the rest of this section: formal parameters should be
referenced with @var{..}, not @code{..}.

> +@code{owner} must be a reference to either a @code{gdb.Objfile} or a
> +@code{gdb.Architecture} object. These correspond to objfile and
                                 ^^
Two spaces between sentences, here and elsewhere.

> +@code{type_code} is one of the @code{TYPE_CODE_} constants defined in
> +@xref{Types In Python}.
   ^^^^^
This should be @ref, not @xref.  @xref is only used at the beginning
of a sentence.

> +@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 @code{owner}, with the given
> +@code{name}, size and signedness.
> +
> +@code{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.
> +
> +@code{bit_size} is the size of instances of the newly created type, in
> +bits. Currently, accepted values are limited to multiples of 8.
> +
> +@code{unsigned} is a boolean indicating whether the type corresponds to
> +a signed or unsigned value.
> +
> +This function returns an instance of @code{gdb.Type}, and will throw an
> +exception in case of an error.
> +@end defun

There's no need to repeat the same text over and over again, for each
function.  It should be enough to describe these arguments once, and
say that all of the functions accept these arguments.  The only thing
you should say about each function is what data type it creates, all
the rest is similar or identical for all of these functions, and
should be only described once.  If some functions accept arguments
that are unique to them, then describe those unique arguments as part
of the function's description.  Otherwise, just refer to the general
description, which should be at the beginning of the section.

> +@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 @code{target} and is owned by the given
> +@code{owner}, with the given @code{name} and size.
> +
> +@code{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.
> +
> +@code{target} is a @code{gdb.Type} object, corresponding to the type
> +that will be pointed to by the newly created pointer type.
> +
> +@code{bit_size} is the size of instances of the newly created type, in
> +bits. Currently, accepted values are limited to multiples of 8.

Does this mean one can create pointer types of arbitrary length,
regardless of the target architecture?  Aren't pointers limited to
what the underlying system supports?

> +@findex gdb.init_boolean_type
> +@defun gdb.init_fixed_point_type (owner, bit_size, unsigned, name)

Typo in the first line?

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] Add support for creating new types from the Python API
  2024-01-13  7:21           ` Eli Zaretskii
@ 2024-01-16  4:55             ` Matheus Branco Borella
  0 siblings, 0 replies; 10+ messages in thread
From: Matheus Branco Borella @ 2024-01-16  4:55 UTC (permalink / raw)
  To: gdb-patches; +Cc: eli, Matheus Branco Borella

Alright, I've sent a v4 that I believe should address your comments. Thank you
for your time.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2024-01-16  4:58 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-05 23:56 [PATCH 1/1] Add support for gdb.Type initialization from within the Python API dark.ryu.550
2023-01-06 20:00 ` Simon Marchi
2023-01-11  0:58   ` [PATCH] Add support for creating new types from " Matheus Branco Borella
2023-06-27  3:52     ` [PING] " Matheus Branco Borella
2023-05-26  3:30   ` Matheus Branco Borella
2023-08-07 14:53     ` Andrew Burgess
2023-08-08 21:00       ` [PATCH v2] " Matheus Branco Borella
2024-01-13  1:37         ` [PATCH v3] " Matheus Branco Borella
2024-01-13  7:21           ` Eli Zaretskii
2024-01-16  4:55             ` Matheus Branco Borella

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).