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
add_field(const char * name,const gdbpy_ref<> & obj)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
do_begin(ui_out_type type,const char * id)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
do_end(ui_out_type type)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
do_field_signed(int fldno,int width,ui_align align,const char * fldname,LONGEST value)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
do_field_unsigned(int fldno,int width,ui_align align,const char * fldname,ULONGEST value)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
do_field_string(int fldno,int width,ui_align align,const char * fldname,const char * string,const ui_file_style & style)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
do_field_fmt(int fldno,int width,ui_align align,const char * fldname,const ui_file_style & style,const char * format,va_list args)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 *
gdbpy_execute_mi_command(PyObject * self,PyObject * args,PyObject * kw)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>
py_object_to_mi_key(PyObject * key_obj)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
serialize_mi_result_1(PyObject * result,const char * field_name)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
serialize_mi_results(PyObject * results)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 *
gdbpy_notify_mi(PyObject * self,PyObject * args,PyObject * kwargs)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