Home | History | Annotate | Line # | Download | only in python
py-mi.c revision 1.1.1.1
      1 /* Python interface to MI commands
      2 
      3    Copyright (C) 2023-2024 Free Software Foundation, Inc.
      4 
      5    This file is part of GDB.
      6 
      7    This program is free software; you can redistribute it and/or modify
      8    it under the terms of the GNU General Public License as published by
      9    the Free Software Foundation; either version 3 of the License, or
     10    (at your option) any later version.
     11 
     12    This program is distributed in the hope that it will be useful,
     13    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15    GNU General Public License for more details.
     16 
     17    You should have received a copy of the GNU General Public License
     18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     19 
     20 #include "python-internal.h"
     21 #include "py-uiout.h"
     22 #include "utils.h"
     23 #include "ui.h"
     24 #include "interps.h"
     25 #include "target.h"
     26 #include "mi/mi-parse.h"
     27 #include "mi/mi-console.h"
     28 #include "mi/mi-interp.h"
     29 
     30 void
     31 py_ui_out::add_field (const char *name, const gdbpy_ref<> &obj)
     32 {
     33   if (obj == nullptr)
     34     {
     35       m_error.emplace ();
     36       return;
     37     }
     38 
     39   object_desc &desc = current ();
     40   if (desc.type == ui_out_type_list)
     41     {
     42       if (PyList_Append (desc.obj.get (), obj.get ()) < 0)
     43 	m_error.emplace ();
     44     }
     45   else
     46     {
     47       if (PyDict_SetItemString (desc.obj.get (), name, obj.get ()) < 0)
     48 	m_error.emplace ();
     49     }
     50 }
     51 
     52 void
     53 py_ui_out::do_begin (ui_out_type type, const char *id)
     54 {
     55   if (m_error.has_value ())
     56     return;
     57 
     58   gdbpy_ref<> new_obj (type == ui_out_type_list
     59 		       ? PyList_New (0)
     60 		       : PyDict_New ());
     61   if (new_obj == nullptr)
     62     {
     63       m_error.emplace ();
     64       return;
     65     }
     66 
     67   object_desc new_desc;
     68   if (id != nullptr)
     69     new_desc.field_name = id;
     70   new_desc.obj = std::move (new_obj);
     71   new_desc.type = type;
     72 
     73   m_objects.push_back (std::move (new_desc));
     74 }
     75 
     76 void
     77 py_ui_out::do_end (ui_out_type type)
     78 {
     79   if (m_error.has_value ())
     80     return;
     81 
     82   object_desc new_obj = std::move (current ());
     83   m_objects.pop_back ();
     84   add_field (new_obj.field_name.c_str (), new_obj.obj);
     85 }
     86 
     87 void
     88 py_ui_out::do_field_signed (int fldno, int width, ui_align align,
     89 			    const char *fldname, LONGEST value)
     90 {
     91   if (m_error.has_value ())
     92     return;
     93 
     94   gdbpy_ref<> val = gdb_py_object_from_longest (value);
     95   add_field (fldname, val);
     96 }
     97 
     98 void
     99 py_ui_out::do_field_unsigned (int fldno, int width, ui_align align,
    100 			    const char *fldname, ULONGEST value)
    101 {
    102   if (m_error.has_value ())
    103     return;
    104 
    105   gdbpy_ref<> val = gdb_py_object_from_ulongest (value);
    106   add_field (fldname, val);
    107 }
    108 
    109 void
    110 py_ui_out::do_field_string (int fldno, int width, ui_align align,
    111 			    const char *fldname, const char *string,
    112 			    const ui_file_style &style)
    113 {
    114   if (m_error.has_value ())
    115     return;
    116 
    117   gdbpy_ref<> val = host_string_to_python_string (string);
    118   add_field (fldname, val);
    119 }
    120 
    121 void
    122 py_ui_out::do_field_fmt (int fldno, int width, ui_align align,
    123 			 const char *fldname, const ui_file_style &style,
    124 			 const char *format, va_list args)
    125 {
    126   if (m_error.has_value ())
    127     return;
    128 
    129   std::string str = string_vprintf (format, args);
    130   do_field_string (fldno, width, align, fldname, str.c_str (), style);
    131 }
    132 
    133 /* Implementation of the gdb.execute_mi command.  */
    134 
    135 PyObject *
    136 gdbpy_execute_mi_command (PyObject *self, PyObject *args, PyObject *kw)
    137 {
    138   gdb::unique_xmalloc_ptr<char> mi_command;
    139   std::vector<gdb::unique_xmalloc_ptr<char>> arg_strings;
    140 
    141   Py_ssize_t n_args = PyTuple_Size (args);
    142   if (n_args < 0)
    143     return nullptr;
    144 
    145   for (Py_ssize_t i = 0; i < n_args; ++i)
    146     {
    147       /* Note this returns a borrowed reference.  */
    148       PyObject *arg = PyTuple_GetItem (args, i);
    149       if (arg == nullptr)
    150 	return nullptr;
    151       gdb::unique_xmalloc_ptr<char> str = python_string_to_host_string (arg);
    152       if (str == nullptr)
    153 	return nullptr;
    154       if (i == 0)
    155 	mi_command = std::move (str);
    156       else
    157 	arg_strings.push_back (std::move (str));
    158     }
    159 
    160   py_ui_out uiout;
    161 
    162   try
    163     {
    164       scoped_restore save_uiout = make_scoped_restore (&current_uiout, &uiout);
    165       auto parser = std::make_unique<mi_parse> (std::move (mi_command),
    166 						std::move (arg_strings));
    167       mi_execute_command (parser.get ());
    168     }
    169   catch (const gdb_exception &except)
    170     {
    171       gdbpy_convert_exception (except);
    172       return nullptr;
    173     }
    174 
    175   return uiout.result ().release ();
    176 }
    177 
    178 /* Convert KEY_OBJ into a string that can be used as a field name in MI
    179    output.  KEY_OBJ must be a Python string object, and must only contain
    180    characters suitable for use as an MI field name.
    181 
    182    If KEY_OBJ is not a string, or if KEY_OBJ contains invalid characters,
    183    then an error is thrown.  Otherwise, KEY_OBJ is converted to a string
    184    and returned.  */
    185 
    186 static gdb::unique_xmalloc_ptr<char>
    187 py_object_to_mi_key (PyObject *key_obj)
    188 {
    189   /* The key must be a string.  */
    190   if (!PyUnicode_Check (key_obj))
    191     {
    192       gdbpy_ref<> key_repr (PyObject_Repr (key_obj));
    193       gdb::unique_xmalloc_ptr<char> key_repr_string;
    194       if (key_repr != nullptr)
    195 	key_repr_string = python_string_to_target_string (key_repr.get ());
    196       if (key_repr_string == nullptr)
    197 	gdbpy_handle_exception ();
    198 
    199       gdbpy_error (_("non-string object used as key: %s"),
    200 		   key_repr_string.get ());
    201     }
    202 
    203   gdb::unique_xmalloc_ptr<char> key_string
    204     = python_string_to_target_string (key_obj);
    205   if (key_string == nullptr)
    206     gdbpy_handle_exception ();
    207 
    208   /* Predicate function, returns true if NAME is a valid field name for use
    209      in MI result output, otherwise, returns false.  */
    210   auto is_valid_key_name = [] (const char *name) -> bool
    211   {
    212     gdb_assert (name != nullptr);
    213 
    214     if (*name == '\0' || !isalpha (*name))
    215       return false;
    216 
    217     for (; *name != '\0'; ++name)
    218       if (!isalnum (*name) && *name != '_' && *name != '-')
    219 	return false;
    220 
    221     return true;
    222   };
    223 
    224   if (!is_valid_key_name (key_string.get ()))
    225     {
    226       if (*key_string.get () == '\0')
    227 	gdbpy_error (_("Invalid empty key in MI result"));
    228       else
    229 	gdbpy_error (_("Invalid key in MI result: %s"), key_string.get ());
    230     }
    231 
    232   return key_string;
    233 }
    234 
    235 /* Serialize RESULT and print it in MI format to the current_uiout.
    236    FIELD_NAME is used as the name of this result field.
    237 
    238    RESULT can be a dictionary, a sequence, an iterator, or an object that
    239    can be converted to a string, these are converted to the matching MI
    240    output format (dictionaries as tuples, sequences and iterators as lists,
    241    and strings as named fields).
    242 
    243    If anything goes wrong while formatting the output then an error is
    244    thrown.
    245 
    246    This function is the recursive inner core of serialize_mi_result, and
    247    should only be called from that function.  */
    248 
    249 static void
    250 serialize_mi_result_1 (PyObject *result, const char *field_name)
    251 {
    252   struct ui_out *uiout = current_uiout;
    253 
    254   if (PyDict_Check (result))
    255     {
    256       PyObject *key, *value;
    257       Py_ssize_t pos = 0;
    258       ui_out_emit_tuple tuple_emitter (uiout, field_name);
    259       while (PyDict_Next (result, &pos, &key, &value))
    260 	{
    261 	  gdb::unique_xmalloc_ptr<char> key_string
    262 	    (py_object_to_mi_key (key));
    263 	  serialize_mi_result_1 (value, key_string.get ());
    264 	}
    265     }
    266   else if (PySequence_Check (result) && !PyUnicode_Check (result))
    267     {
    268       ui_out_emit_list list_emitter (uiout, field_name);
    269       Py_ssize_t len = PySequence_Size (result);
    270       if (len == -1)
    271 	gdbpy_handle_exception ();
    272       for (Py_ssize_t i = 0; i < len; ++i)
    273 	{
    274 	  gdbpy_ref<> item (PySequence_ITEM (result, i));
    275 	  if (item == nullptr)
    276 	    gdbpy_handle_exception ();
    277 	  serialize_mi_result_1 (item.get (), nullptr);
    278 	}
    279     }
    280   else if (PyIter_Check (result))
    281     {
    282       gdbpy_ref<> item;
    283       ui_out_emit_list list_emitter (uiout, field_name);
    284       while (true)
    285 	{
    286 	  item.reset (PyIter_Next (result));
    287 	  if (item == nullptr)
    288 	    {
    289 	      if (PyErr_Occurred () != nullptr)
    290 		gdbpy_handle_exception ();
    291 	      break;
    292 	    }
    293 	  serialize_mi_result_1 (item.get (), nullptr);
    294 	}
    295     }
    296   else
    297     {
    298       if (PyLong_Check (result))
    299 	{
    300 	  int overflow = 0;
    301 	  gdb_py_longest val = gdb_py_long_as_long_and_overflow (result,
    302 								 &overflow);
    303 	  if (PyErr_Occurred () != nullptr)
    304 	    gdbpy_handle_exception ();
    305 	  if (overflow == 0)
    306 	    {
    307 	      uiout->field_signed (field_name, val);
    308 	      return;
    309 	    }
    310 	  /* Fall through to the string case on overflow.  */
    311 	}
    312 
    313       gdb::unique_xmalloc_ptr<char> string (gdbpy_obj_to_string (result));
    314       if (string == nullptr)
    315 	gdbpy_handle_exception ();
    316       uiout->field_string (field_name, string.get ());
    317     }
    318 }
    319 
    320 /* See python-internal.h.  */
    321 
    322 void
    323 serialize_mi_results (PyObject *results)
    324 {
    325   gdb_assert (PyDict_Check (results));
    326 
    327   PyObject *key, *value;
    328   Py_ssize_t pos = 0;
    329   while (PyDict_Next (results, &pos, &key, &value))
    330     {
    331       gdb::unique_xmalloc_ptr<char> key_string
    332 	(py_object_to_mi_key (key));
    333       serialize_mi_result_1 (value, key_string.get ());
    334     }
    335 }
    336 
    337 /* See python-internal.h.  */
    338 
    339 PyObject *
    340 gdbpy_notify_mi (PyObject *self, PyObject *args, PyObject *kwargs)
    341 {
    342   static const char *keywords[] = { "name", "data", nullptr };
    343   char *name = nullptr;
    344   PyObject *data = Py_None;
    345 
    346   if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "s|O", keywords,
    347 					&name, &data))
    348     return nullptr;
    349 
    350   /* Validate notification name.  */
    351   const int name_len = strlen (name);
    352   if (name_len == 0)
    353     {
    354       PyErr_SetString (PyExc_ValueError, _("MI notification name is empty."));
    355       return nullptr;
    356     }
    357   for (int i = 0; i < name_len; i++)
    358     {
    359       if (!isalnum (name[i]) && name[i] != '-')
    360 	{
    361 	  PyErr_Format
    362 	    (PyExc_ValueError,
    363 	     _("MI notification name contains invalid character: %c."),
    364 	     name[i]);
    365 	  return nullptr;
    366 	}
    367     }
    368 
    369   /* Validate additional data.  */
    370   if (!(data == Py_None || PyDict_Check (data)))
    371     {
    372       PyErr_Format
    373 	(PyExc_ValueError,
    374 	 _("MI notification data must be either None or a dictionary, not %s"),
    375 	 Py_TYPE (data)->tp_name);
    376       return nullptr;
    377     }
    378 
    379   SWITCH_THRU_ALL_UIS ()
    380     {
    381       struct mi_interp *mi = as_mi_interp (top_level_interpreter ());
    382 
    383       if (mi == nullptr)
    384 	continue;
    385 
    386       target_terminal::scoped_restore_terminal_state term_state;
    387       target_terminal::ours_for_output ();
    388 
    389       gdb_printf (mi->event_channel, "%s", name);
    390       if (data != Py_None)
    391 	{
    392 	  ui_out *mi_uiout = mi->interp_ui_out ();
    393 	  ui_out_redirect_pop redir (mi_uiout, mi->event_channel);
    394 	  scoped_restore restore_uiout
    395 	    = make_scoped_restore (&current_uiout, mi_uiout);
    396 
    397 	  serialize_mi_results (data);
    398 	}
    399       gdb_flush (mi->event_channel);
    400     }
    401 
    402   Py_RETURN_NONE;
    403 }
    404