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 (¤t_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 (¤t_uiout, mi_uiout);
396
397 serialize_mi_results (data);
398 }
399 gdb_flush (mi->event_channel);
400 }
401
402 Py_RETURN_NONE;
403 }
404