forked from Imagelibrary/binutils-gdb
gdb/python: allow Python TUI windows to be replaced
The documentation for gdb.register_window_type says:
"... It's an error to try to replace one of the built-in windows,
but other window types can be replaced. ..."
I take this to mean that if I imported a Python script like this:
gdb.register_window_type('my_window', FactoryFunction)
Then GDB would have a new TUI window 'my_window', which could be
created by calling FactoryFunction(). If I then, in the same GDB
session imported a script which included:
gdb.register_window_type('my_window', UpdatedFactoryFunction)
Then GDB would replace the old 'my_window' factory with my new one,
GDB would now call UpdatedFactoryFunction().
This is pretty useful in practice, as it allows users to iterate on
their window implementation within a single GDB session.
However, right now, this is not how GDB operates. The second call to
register_window_type is basically ignored and the old window factory
is retained.
This is because in tui_register_window (tui/tui-layout.c) we use
std::unordered_map::emplace to insert the new factory function, and
emplace doesn't replace an existing element in an unordered_map.
In this commit, before the emplace call, I now search for an already
existing element, and delete any matching element from the map, the
emplace call will then add the new factory function.
Reviewed-By: Tom Tromey <tom@tromey.com>
This commit is contained in:
73
gdb/testsuite/gdb.python/tui-window-factory.exp
Normal file
73
gdb/testsuite/gdb.python/tui-window-factory.exp
Normal file
@@ -0,0 +1,73 @@
|
||||
# Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
# Test that GDB correctly deallocates the window factory object (a)
|
||||
# when a window factory is replaced, and (b) during GDB shutdown.
|
||||
#
|
||||
# This test also ensures that when a new window is registered (via the
|
||||
# Python API) with the same name as an existing window, then the
|
||||
# previous window is replaced.
|
||||
|
||||
load_lib gdb-python.exp
|
||||
|
||||
tuiterm_env
|
||||
|
||||
clean_restart
|
||||
|
||||
require allow_tui_tests allow_python_tests
|
||||
|
||||
set pyfile [gdb_remote_download host \
|
||||
${srcdir}/${subdir}/${gdb_test_file_name}.py]
|
||||
|
||||
Term::clean_restart 24 80
|
||||
Term::prepare_for_tui
|
||||
|
||||
gdb_test "source ${pyfile}" "Python script imported" \
|
||||
"import python scripts"
|
||||
|
||||
gdb_test "python register_window_factory('msg_1')" \
|
||||
"Entering TestWindowFactory\\.__init__: msg_1"
|
||||
|
||||
gdb_test "python register_window_factory('msg_2')" \
|
||||
[multi_line \
|
||||
"Entering TestWindowFactory\\.__init__: msg_2" \
|
||||
"Entering TestWindowFactory\\.__del__: msg_1"]
|
||||
|
||||
gdb_test_no_output "tui new-layout test test_window 1 cmd 1 status 1"
|
||||
|
||||
# Load the custom window layout and ensure that the correct window
|
||||
# factory was used.
|
||||
with_test_prefix "msg_2" {
|
||||
Term::command_no_prompt_prefix "layout test"
|
||||
Term::check_box_contents "check test_window box" 0 0 80 15 \
|
||||
"TestWindow \\(msg_2\\)"
|
||||
}
|
||||
|
||||
# Replace the existing window factory with a new one, then switch
|
||||
# layouts so that GDB recreates the window, and check that the new
|
||||
# window factory was used.
|
||||
with_test_prefix "msg_3" {
|
||||
Term::command "python register_window_factory('msg_3')"
|
||||
Term::check_region_contents "check for python output" \
|
||||
0 18 80 2 \
|
||||
[multi_line \
|
||||
"Entering TestWindowFactory.__init__: msg_3\\s+" \
|
||||
"Entering TestWindowFactory.__del__: msg_2"]
|
||||
Term::command "layout src"
|
||||
Term::command "layout test"
|
||||
|
||||
Term::check_box_contents "check test_window box" 0 0 80 15 \
|
||||
"TestWindow \\(msg_3\\)"
|
||||
}
|
||||
48
gdb/testsuite/gdb.python/tui-window-factory.py
Normal file
48
gdb/testsuite/gdb.python/tui-window-factory.py
Normal file
@@ -0,0 +1,48 @@
|
||||
# Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
# 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/>.
|
||||
|
||||
|
||||
class TestWindow:
|
||||
def __init__(self, tui_win, msg):
|
||||
self.msg = msg
|
||||
self.tui_win = tui_win
|
||||
print("Entering TestWindow.__init__: %s" % self.msg)
|
||||
|
||||
def render(self):
|
||||
self.tui_win.erase()
|
||||
self.tui_win.write("TestWindow (%s)" % self.msg)
|
||||
|
||||
def __del__(self):
|
||||
print("Entering TestWindow.__del__: %s" % self.msg)
|
||||
|
||||
|
||||
class TestWindowFactory:
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
print("Entering TestWindowFactory.__init__: %s" % self.msg)
|
||||
|
||||
def __call__(self, tui_win):
|
||||
print("Entering TestWindowFactory.__call__: %s" % self.msg)
|
||||
return TestWindow(tui_win, self.msg)
|
||||
|
||||
def __del__(self):
|
||||
print("Entering TestWindowFactory.__del__: %s" % self.msg)
|
||||
|
||||
|
||||
def register_window_factory(msg):
|
||||
gdb.register_window_type("test_window", TestWindowFactory(msg))
|
||||
|
||||
|
||||
print("Python script imported")
|
||||
@@ -427,6 +427,14 @@ tui_register_window (const char *name, window_factory &&factory)
|
||||
if (!ISALPHA (name_copy[0]))
|
||||
error (_("window name must start with a letter, not '%c'"), name_copy[0]);
|
||||
|
||||
/* We already check above for all the builtin window names. If we get
|
||||
this far then NAME must be a user defined window. Remove any existing
|
||||
factory and replace it with this new version. */
|
||||
|
||||
auto iter = known_window_types->find (name);
|
||||
if (iter != known_window_types->end ())
|
||||
known_window_types->erase (iter);
|
||||
|
||||
known_window_types->emplace (std::move (name_copy),
|
||||
std::move (factory));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user