mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-01-12 12:16:04 +08:00
1d506c26d9
This commit is the result of the following actions: - Running gdb/copyright.py to update all of the copyright headers to include 2024, - Manually updating a few files the copyright.py script told me to update, these files had copyright headers embedded within the file, - Regenerating gdbsupport/Makefile.in to refresh it's copyright date, - Using grep to find other files that still mentioned 2023. If these files were updated last year from 2022 to 2023 then I've updated them this year to 2024. I'm sure I've probably missed some dates. Feel free to fix them up as you spot them.
405 lines
10 KiB
C
405 lines
10 KiB
C
/* Python interface to MI commands
|
|
|
|
Copyright (C) 2023-2024 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 "py-uiout.h"
|
|
#include "utils.h"
|
|
#include "ui.h"
|
|
#include "interps.h"
|
|
#include "target.h"
|
|
#include "mi/mi-parse.h"
|
|
#include "mi/mi-console.h"
|
|
#include "mi/mi-interp.h"
|
|
|
|
void
|
|
py_ui_out::add_field (const char *name, const gdbpy_ref<> &obj)
|
|
{
|
|
if (obj == nullptr)
|
|
{
|
|
m_error.emplace ();
|
|
return;
|
|
}
|
|
|
|
object_desc &desc = current ();
|
|
if (desc.type == ui_out_type_list)
|
|
{
|
|
if (PyList_Append (desc.obj.get (), obj.get ()) < 0)
|
|
m_error.emplace ();
|
|
}
|
|
else
|
|
{
|
|
if (PyDict_SetItemString (desc.obj.get (), name, obj.get ()) < 0)
|
|
m_error.emplace ();
|
|
}
|
|
}
|
|
|
|
void
|
|
py_ui_out::do_begin (ui_out_type type, const char *id)
|
|
{
|
|
if (m_error.has_value ())
|
|
return;
|
|
|
|
gdbpy_ref<> new_obj (type == ui_out_type_list
|
|
? PyList_New (0)
|
|
: PyDict_New ());
|
|
if (new_obj == nullptr)
|
|
{
|
|
m_error.emplace ();
|
|
return;
|
|
}
|
|
|
|
object_desc new_desc;
|
|
if (id != nullptr)
|
|
new_desc.field_name = id;
|
|
new_desc.obj = std::move (new_obj);
|
|
new_desc.type = type;
|
|
|
|
m_objects.push_back (std::move (new_desc));
|
|
}
|
|
|
|
void
|
|
py_ui_out::do_end (ui_out_type type)
|
|
{
|
|
if (m_error.has_value ())
|
|
return;
|
|
|
|
object_desc new_obj = std::move (current ());
|
|
m_objects.pop_back ();
|
|
add_field (new_obj.field_name.c_str (), new_obj.obj);
|
|
}
|
|
|
|
void
|
|
py_ui_out::do_field_signed (int fldno, int width, ui_align align,
|
|
const char *fldname, LONGEST value)
|
|
{
|
|
if (m_error.has_value ())
|
|
return;
|
|
|
|
gdbpy_ref<> val = gdb_py_object_from_longest (value);
|
|
add_field (fldname, val);
|
|
}
|
|
|
|
void
|
|
py_ui_out::do_field_unsigned (int fldno, int width, ui_align align,
|
|
const char *fldname, ULONGEST value)
|
|
{
|
|
if (m_error.has_value ())
|
|
return;
|
|
|
|
gdbpy_ref<> val = gdb_py_object_from_ulongest (value);
|
|
add_field (fldname, val);
|
|
}
|
|
|
|
void
|
|
py_ui_out::do_field_string (int fldno, int width, ui_align align,
|
|
const char *fldname, const char *string,
|
|
const ui_file_style &style)
|
|
{
|
|
if (m_error.has_value ())
|
|
return;
|
|
|
|
gdbpy_ref<> val = host_string_to_python_string (string);
|
|
add_field (fldname, val);
|
|
}
|
|
|
|
void
|
|
py_ui_out::do_field_fmt (int fldno, int width, ui_align align,
|
|
const char *fldname, const ui_file_style &style,
|
|
const char *format, va_list args)
|
|
{
|
|
if (m_error.has_value ())
|
|
return;
|
|
|
|
std::string str = string_vprintf (format, args);
|
|
do_field_string (fldno, width, align, fldname, str.c_str (), style);
|
|
}
|
|
|
|
/* Implementation of the gdb.execute_mi command. */
|
|
|
|
PyObject *
|
|
gdbpy_execute_mi_command (PyObject *self, PyObject *args, PyObject *kw)
|
|
{
|
|
gdb::unique_xmalloc_ptr<char> mi_command;
|
|
std::vector<gdb::unique_xmalloc_ptr<char>> arg_strings;
|
|
|
|
Py_ssize_t n_args = PyTuple_Size (args);
|
|
if (n_args < 0)
|
|
return nullptr;
|
|
|
|
for (Py_ssize_t i = 0; i < n_args; ++i)
|
|
{
|
|
/* Note this returns a borrowed reference. */
|
|
PyObject *arg = PyTuple_GetItem (args, i);
|
|
if (arg == nullptr)
|
|
return nullptr;
|
|
gdb::unique_xmalloc_ptr<char> str = python_string_to_host_string (arg);
|
|
if (str == nullptr)
|
|
return nullptr;
|
|
if (i == 0)
|
|
mi_command = std::move (str);
|
|
else
|
|
arg_strings.push_back (std::move (str));
|
|
}
|
|
|
|
py_ui_out uiout;
|
|
|
|
try
|
|
{
|
|
scoped_restore save_uiout = make_scoped_restore (¤t_uiout, &uiout);
|
|
auto parser = std::make_unique<mi_parse> (std::move (mi_command),
|
|
std::move (arg_strings));
|
|
mi_execute_command (parser.get ());
|
|
}
|
|
catch (const gdb_exception &except)
|
|
{
|
|
gdbpy_convert_exception (except);
|
|
return nullptr;
|
|
}
|
|
|
|
return uiout.result ().release ();
|
|
}
|
|
|
|
/* 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 ());
|
|
}
|
|
}
|
|
|
|
/* See python-internal.h. */
|
|
|
|
PyObject *
|
|
gdbpy_notify_mi (PyObject *self, PyObject *args, PyObject *kwargs)
|
|
{
|
|
static const char *keywords[] = { "name", "data", nullptr };
|
|
char *name = nullptr;
|
|
PyObject *data = Py_None;
|
|
|
|
if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "s|O", keywords,
|
|
&name, &data))
|
|
return nullptr;
|
|
|
|
/* Validate notification name. */
|
|
const int name_len = strlen (name);
|
|
if (name_len == 0)
|
|
{
|
|
PyErr_SetString (PyExc_ValueError, _("MI notification name is empty."));
|
|
return nullptr;
|
|
}
|
|
for (int i = 0; i < name_len; i++)
|
|
{
|
|
if (!isalnum (name[i]) && name[i] != '-')
|
|
{
|
|
PyErr_Format
|
|
(PyExc_ValueError,
|
|
_("MI notification name contains invalid character: %c."),
|
|
name[i]);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
/* Validate additional data. */
|
|
if (!(data == Py_None || PyDict_Check (data)))
|
|
{
|
|
PyErr_Format
|
|
(PyExc_ValueError,
|
|
_("MI notification data must be either None or a dictionary, not %s"),
|
|
Py_TYPE (data)->tp_name);
|
|
return nullptr;
|
|
}
|
|
|
|
SWITCH_THRU_ALL_UIS ()
|
|
{
|
|
struct mi_interp *mi = as_mi_interp (top_level_interpreter ());
|
|
|
|
if (mi == nullptr)
|
|
continue;
|
|
|
|
target_terminal::scoped_restore_terminal_state term_state;
|
|
target_terminal::ours_for_output ();
|
|
|
|
gdb_printf (mi->event_channel, "%s", name);
|
|
if (data != Py_None)
|
|
{
|
|
ui_out *mi_uiout = mi->interp_ui_out ();
|
|
ui_out_redirect_pop redir (mi_uiout, mi->event_channel);
|
|
scoped_restore restore_uiout
|
|
= make_scoped_restore (¤t_uiout, mi_uiout);
|
|
|
|
serialize_mi_results (data);
|
|
}
|
|
gdb_flush (mi->event_channel);
|
|
}
|
|
|
|
Py_RETURN_NONE;
|
|
}
|