aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Vrany <jan.vrany@labware.com>2023-10-10 11:22:56 +0100
committerJan Vrany <jan.vrany@labware.com>2023-10-10 11:22:56 +0100
commit80a3485f81303639c7212a15cf9e453a830913f8 (patch)
treee168d3d01d61f88374c73c921768ac071d19ca25
parent1fb3cdd87ec61715a5684925fb6d6a6cf53bb97c (diff)
downloadgdb-80a3485f81303639c7212a15cf9e453a830913f8.zip
gdb-80a3485f81303639c7212a15cf9e453a830913f8.tar.gz
gdb-80a3485f81303639c7212a15cf9e453a830913f8.tar.bz2
gdb/python: generalize serialize_mi_result()
This commit generalizes serialize_mi_result() to make usable in different contexts than printing result of custom MI command. To do so, the check whether passed Python object is a dictionary has been moved to the caller - at the very least, different uses require different error messages. Also it has been renamed to serialize_mi_results() to better match GDB/MI output syntax (see corresponding section in documentation, in particular rules 'result-record' and 'async-output'. Since it is now more generic function, it has been moved to py-mi.c. This is a preparation for implementing Python support for sending custom MI async events. Approved-By: Andrew Burgess <aburgess@redhat.com>
-rw-r--r--gdb/python/py-mi.c159
-rw-r--r--gdb/python/py-micmd.c185
-rw-r--r--gdb/python/python-internal.h13
3 files changed, 181 insertions, 176 deletions
diff --git a/gdb/python/py-mi.c b/gdb/python/py-mi.c
index 66dc6fb..36bcb6c 100644
--- a/gdb/python/py-mi.c
+++ b/gdb/python/py-mi.c
@@ -296,3 +296,162 @@ gdbpy_execute_mi_command (PyObject *self, PyObject *args, PyObject *kw)
return uiout.result ();
}
+
+/* Convert KEY_OBJ into a string that can be used as a field name in MI
+ output. KEY_OBJ must be a Python string object, and must only contain
+ characters suitable for use as an MI field name.
+
+ If KEY_OBJ is not a string, or if KEY_OBJ contains invalid characters,
+ then an error is thrown. Otherwise, KEY_OBJ is converted to a string
+ and returned. */
+
+static gdb::unique_xmalloc_ptr<char>
+py_object_to_mi_key (PyObject *key_obj)
+{
+ /* The key must be a string. */
+ if (!PyUnicode_Check (key_obj))
+ {
+ gdbpy_ref<> key_repr (PyObject_Repr (key_obj));
+ gdb::unique_xmalloc_ptr<char> key_repr_string;
+ if (key_repr != nullptr)
+ key_repr_string = python_string_to_target_string (key_repr.get ());
+ if (key_repr_string == nullptr)
+ gdbpy_handle_exception ();
+
+ gdbpy_error (_("non-string object used as key: %s"),
+ key_repr_string.get ());
+ }
+
+ gdb::unique_xmalloc_ptr<char> key_string
+ = python_string_to_target_string (key_obj);
+ if (key_string == nullptr)
+ gdbpy_handle_exception ();
+
+ /* Predicate function, returns true if NAME is a valid field name for use
+ in MI result output, otherwise, returns false. */
+ auto is_valid_key_name = [] (const char *name) -> bool
+ {
+ gdb_assert (name != nullptr);
+
+ if (*name == '\0' || !isalpha (*name))
+ return false;
+
+ for (; *name != '\0'; ++name)
+ if (!isalnum (*name) && *name != '_' && *name != '-')
+ return false;
+
+ return true;
+ };
+
+ if (!is_valid_key_name (key_string.get ()))
+ {
+ if (*key_string.get () == '\0')
+ gdbpy_error (_("Invalid empty key in MI result"));
+ else
+ gdbpy_error (_("Invalid key in MI result: %s"), key_string.get ());
+ }
+
+ return key_string;
+}
+
+/* Serialize RESULT and print it in MI format to the current_uiout.
+ FIELD_NAME is used as the name of this result field.
+
+ RESULT can be a dictionary, a sequence, an iterator, or an object that
+ can be converted to a string, these are converted to the matching MI
+ output format (dictionaries as tuples, sequences and iterators as lists,
+ and strings as named fields).
+
+ If anything goes wrong while formatting the output then an error is
+ thrown.
+
+ This function is the recursive inner core of serialize_mi_result, and
+ should only be called from that function. */
+
+static void
+serialize_mi_result_1 (PyObject *result, const char *field_name)
+{
+ struct ui_out *uiout = current_uiout;
+
+ if (PyDict_Check (result))
+ {
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+ ui_out_emit_tuple tuple_emitter (uiout, field_name);
+ while (PyDict_Next (result, &pos, &key, &value))
+ {
+ gdb::unique_xmalloc_ptr<char> key_string
+ (py_object_to_mi_key (key));
+ serialize_mi_result_1 (value, key_string.get ());
+ }
+ }
+ else if (PySequence_Check (result) && !PyUnicode_Check (result))
+ {
+ ui_out_emit_list list_emitter (uiout, field_name);
+ Py_ssize_t len = PySequence_Size (result);
+ if (len == -1)
+ gdbpy_handle_exception ();
+ for (Py_ssize_t i = 0; i < len; ++i)
+ {
+ gdbpy_ref<> item (PySequence_ITEM (result, i));
+ if (item == nullptr)
+ gdbpy_handle_exception ();
+ serialize_mi_result_1 (item.get (), nullptr);
+ }
+ }
+ else if (PyIter_Check (result))
+ {
+ gdbpy_ref<> item;
+ ui_out_emit_list list_emitter (uiout, field_name);
+ while (true)
+ {
+ item.reset (PyIter_Next (result));
+ if (item == nullptr)
+ {
+ if (PyErr_Occurred () != nullptr)
+ gdbpy_handle_exception ();
+ break;
+ }
+ serialize_mi_result_1 (item.get (), nullptr);
+ }
+ }
+ else
+ {
+ if (PyLong_Check (result))
+ {
+ int overflow = 0;
+ gdb_py_longest val = gdb_py_long_as_long_and_overflow (result,
+ &overflow);
+ if (PyErr_Occurred () != nullptr)
+ gdbpy_handle_exception ();
+ if (overflow == 0)
+ {
+ uiout->field_signed (field_name, val);
+ return;
+ }
+ /* Fall through to the string case on overflow. */
+ }
+
+ gdb::unique_xmalloc_ptr<char> string (gdbpy_obj_to_string (result));
+ if (string == nullptr)
+ gdbpy_handle_exception ();
+ uiout->field_string (field_name, string.get ());
+ }
+}
+
+/* See python-internal.h. */
+
+void
+serialize_mi_results (PyObject *results)
+{
+ gdb_assert (PyDict_Check (results));
+
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+ while (PyDict_Next (results, &pos, &key, &value))
+ {
+ gdb::unique_xmalloc_ptr<char> key_string
+ (py_object_to_mi_key (key));
+ serialize_mi_result_1 (value, key_string.get ());
+ }
+}
diff --git a/gdb/python/py-micmd.c b/gdb/python/py-micmd.c
index 01fc606..0153f84 100644
--- a/gdb/python/py-micmd.c
+++ b/gdb/python/py-micmd.c
@@ -173,178 +173,6 @@ extern PyTypeObject micmdpy_object_type
static PyObject *invoke_cst;
-/* Convert KEY_OBJ into a string that can be used as a field name in MI
- output. KEY_OBJ must be a Python string object, and must only contain
- characters suitable for use as an MI field name.
-
- If KEY_OBJ is not a string, or if KEY_OBJ contains invalid characters,
- then an error is thrown. Otherwise, KEY_OBJ is converted to a string
- and returned. */
-
-static gdb::unique_xmalloc_ptr<char>
-py_object_to_mi_key (PyObject *key_obj)
-{
- /* The key must be a string. */
- if (!PyUnicode_Check (key_obj))
- {
- gdbpy_ref<> key_repr (PyObject_Repr (key_obj));
- gdb::unique_xmalloc_ptr<char> key_repr_string;
- if (key_repr != nullptr)
- key_repr_string = python_string_to_target_string (key_repr.get ());
- if (key_repr_string == nullptr)
- gdbpy_handle_exception ();
-
- gdbpy_error (_("non-string object used as key: %s"),
- key_repr_string.get ());
- }
-
- gdb::unique_xmalloc_ptr<char> key_string
- = python_string_to_target_string (key_obj);
- if (key_string == nullptr)
- gdbpy_handle_exception ();
-
- /* Predicate function, returns true if NAME is a valid field name for use
- in MI result output, otherwise, returns false. */
- auto is_valid_key_name = [] (const char *name) -> bool
- {
- gdb_assert (name != nullptr);
-
- if (*name == '\0' || !isalpha (*name))
- return false;
-
- for (; *name != '\0'; ++name)
- if (!isalnum (*name) && *name != '_' && *name != '-')
- return false;
-
- return true;
- };
-
- if (!is_valid_key_name (key_string.get ()))
- {
- if (*key_string.get () == '\0')
- gdbpy_error (_("Invalid empty key in MI result"));
- else
- gdbpy_error (_("Invalid key in MI result: %s"), key_string.get ());
- }
-
- return key_string;
-}
-
-/* Serialize RESULT and print it in MI format to the current_uiout.
- FIELD_NAME is used as the name of this result field.
-
- RESULT can be a dictionary, a sequence, an iterator, or an object that
- can be converted to a string, these are converted to the matching MI
- output format (dictionaries as tuples, sequences and iterators as lists,
- and strings as named fields).
-
- If anything goes wrong while formatting the output then an error is
- thrown.
-
- This function is the recursive inner core of serialize_mi_result, and
- should only be called from that function. */
-
-static void
-serialize_mi_result_1 (PyObject *result, const char *field_name)
-{
- struct ui_out *uiout = current_uiout;
-
- if (PyDict_Check (result))
- {
- PyObject *key, *value;
- Py_ssize_t pos = 0;
- ui_out_emit_tuple tuple_emitter (uiout, field_name);
- while (PyDict_Next (result, &pos, &key, &value))
- {
- gdb::unique_xmalloc_ptr<char> key_string
- (py_object_to_mi_key (key));
- serialize_mi_result_1 (value, key_string.get ());
- }
- }
- else if (PySequence_Check (result) && !PyUnicode_Check (result))
- {
- ui_out_emit_list list_emitter (uiout, field_name);
- Py_ssize_t len = PySequence_Size (result);
- if (len == -1)
- gdbpy_handle_exception ();
- for (Py_ssize_t i = 0; i < len; ++i)
- {
- gdbpy_ref<> item (PySequence_ITEM (result, i));
- if (item == nullptr)
- gdbpy_handle_exception ();
- serialize_mi_result_1 (item.get (), nullptr);
- }
- }
- else if (PyIter_Check (result))
- {
- gdbpy_ref<> item;
- ui_out_emit_list list_emitter (uiout, field_name);
- while (true)
- {
- item.reset (PyIter_Next (result));
- if (item == nullptr)
- {
- if (PyErr_Occurred () != nullptr)
- gdbpy_handle_exception ();
- break;
- }
- serialize_mi_result_1 (item.get (), nullptr);
- }
- }
- else
- {
- if (PyLong_Check (result))
- {
- int overflow = 0;
- gdb_py_longest val = gdb_py_long_as_long_and_overflow (result,
- &overflow);
- if (PyErr_Occurred () != nullptr)
- gdbpy_handle_exception ();
- if (overflow == 0)
- {
- uiout->field_signed (field_name, val);
- return;
- }
- /* Fall through to the string case on overflow. */
- }
-
- gdb::unique_xmalloc_ptr<char> string (gdbpy_obj_to_string (result));
- if (string == nullptr)
- gdbpy_handle_exception ();
- uiout->field_string (field_name, string.get ());
- }
-}
-
-/* Serialize RESULT and print it in MI format to the current_uiout.
-
- This function handles the top-level result initially returned from the
- invoke method of the Python command implementation. At the top-level
- the result must be a dictionary. The values within this dictionary can
- be a wider range of types. Handling the values of the top-level
- dictionary is done by serialize_mi_result_1, see that function for more
- details.
-
- If anything goes wrong while parsing and printing the MI output then an
- error is thrown. */
-
-static void
-serialize_mi_result (PyObject *result)
-{
- /* At the top-level, the result must be a dictionary. */
-
- if (!PyDict_Check (result))
- gdbpy_error (_("Result from invoke must be a dictionary"));
-
- PyObject *key, *value;
- Py_ssize_t pos = 0;
- while (PyDict_Next (result, &pos, &key, &value))
- {
- gdb::unique_xmalloc_ptr<char> key_string
- (py_object_to_mi_key (key));
- serialize_mi_result_1 (value, key_string.get ());
- }
-}
-
/* Called when the MI command is invoked. PARSE contains the parsed
command line arguments from the user. */
@@ -381,14 +209,19 @@ mi_command_py::invoke (struct mi_parse *parse) const
gdb_assert (this->m_pyobj != nullptr);
gdb_assert (PyErr_Occurred () == nullptr);
- gdbpy_ref<> result
+ gdbpy_ref<> results
(PyObject_CallMethodObjArgs ((PyObject *) this->m_pyobj.get (), invoke_cst,
argobj.get (), nullptr));
- if (result == nullptr)
+ if (results == nullptr)
gdbpy_handle_exception ();
- if (result != Py_None)
- serialize_mi_result (result.get ());
+ if (results != Py_None)
+ {
+ /* At the top-level, the results must be a dictionary. */
+ if (!PyDict_Check (results.get ()))
+ gdbpy_error (_("Result from invoke must be a dictionary"));
+ serialize_mi_results (results.get ());
+ }
}
/* See declaration above. */
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 9321737..60b795f 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -486,6 +486,19 @@ struct gdbarch *arch_object_to_gdbarch (PyObject *obj);
extern PyObject *gdbpy_execute_mi_command (PyObject *self, PyObject *args,
PyObject *kw);
+/* Serialize RESULTS and print it in MI format to the current_uiout.
+
+ This function handles the top-level results passed as a dictionary.
+ The caller is responsible for ensuring that. The values within this
+ dictionary can be a wider range of types. Handling the values of the top-level
+ dictionary is done by serialize_mi_result_1, see that function for more
+ details.
+
+ If anything goes wrong while parsing and printing the MI output then an
+ error is thrown. */
+
+extern void serialize_mi_results (PyObject *results);
+
/* 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