From 8e9f4ee17b783caa8151f217f69b72fe9c950dd4 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Tue, 18 Jul 2023 17:35:12 +0100 Subject: [PATCH] Prevent using multi-irb and activating debugger at the same time Multi-irb makes a few assumptions: - IRB will manage all threads that host sub-irb sessions - All IRB sessions will be run on the threads created by IRB itself However, when using the debugger these assumptions are broken: - `debug` will freeze ALL threads when it suspends the session (e.g. when hitting a breakpoint, or performing step-debugging). - Since the irb-debug integration runs IRB as the debugger's interface, it will be run on the debugger's thread, which is not managed by IRB. So we should prevent the 2 features from being used at the same time. To do that, we check if the other feature is already activated when executing the commands that would activate the other feature. --- lib/irb/cmd/debug.rb | 5 +++++ lib/irb/cmd/subirb.rb | 36 ++++++++++++++++++++++++++++++++++-- test/irb/test_debug_cmd.rb | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/lib/irb/cmd/debug.rb b/lib/irb/cmd/debug.rb index 01bbf49d5..9eca96421 100644 --- a/lib/irb/cmd/debug.rb +++ b/lib/irb/cmd/debug.rb @@ -35,6 +35,11 @@ def execute(pre_cmds: nil, do_cmds: nil) return end + if IRB.respond_to?(:JobManager) + warn "Can't start the debugger when IRB is running in a multi-IRB session." + return + end + unless IRB::Debug.setup(irb_context.irb) puts <<~MSG You need to install the debug gem before using this command. diff --git a/lib/irb/cmd/subirb.rb b/lib/irb/cmd/subirb.rb index 3018ab277..5ffd64641 100644 --- a/lib/irb/cmd/subirb.rb +++ b/lib/irb/cmd/subirb.rb @@ -11,8 +11,7 @@ module IRB module ExtendCommand class MultiIRBCommand < Nop - def initialize(conf) - super + def execute(*args) extend_irb_context end @@ -29,6 +28,10 @@ def extend_irb_context # this extension patches IRB context like IRB.CurrentContext require_relative "../ext/multi-irb" end + + def print_debugger_warning + warn "Multi-IRB commands are not available when the debugger is enabled." + end end class IrbCommand < MultiIRBCommand @@ -37,6 +40,13 @@ class IrbCommand < MultiIRBCommand def execute(*obj) print_deprecated_warning + + if irb_context.with_debugger + print_debugger_warning + return + end + + super IRB.irb(nil, *obj) end end @@ -47,6 +57,13 @@ class Jobs < MultiIRBCommand def execute print_deprecated_warning + + if irb_context.with_debugger + print_debugger_warning + return + end + + super IRB.JobManager end end @@ -57,6 +74,14 @@ class Foreground < MultiIRBCommand def execute(key = nil) print_deprecated_warning + + if irb_context.with_debugger + print_debugger_warning + return + end + + super + raise CommandArgumentError.new("Please specify the id of target IRB job (listed in the `jobs` command).") unless key IRB.JobManager.switch(key) end @@ -68,6 +93,13 @@ class Kill < MultiIRBCommand def execute(*keys) print_deprecated_warning + + if irb_context.with_debugger + print_debugger_warning + return + end + + super IRB.JobManager.kill(*keys) end end diff --git a/test/irb/test_debug_cmd.rb b/test/irb/test_debug_cmd.rb index 90b19e52d..01546ba6c 100644 --- a/test/irb/test_debug_cmd.rb +++ b/test/irb/test_debug_cmd.rb @@ -356,5 +356,37 @@ def bar assert_match(/irb\(main\):001:0> next/, output) assert_include(output, "InputMethod: RelineInputMethod") end + + def test_debugger_cant_be_activated_while_multi_irb_is_active + write_ruby <<~'ruby' + binding.irb + a = 1 + ruby + + output = run_ruby_file do + type "jobs" + type "next" + type "exit" + end + + assert_match(/irb\(main\):001:0> jobs/, output) + assert_include(output, "Can't start the debugger when IRB is running in a multi-IRB session.") + end + + def test_multi_irb_commands_are_not_available_after_activating_the_debugger + write_ruby <<~'ruby' + binding.irb + a = 1 + ruby + + output = run_ruby_file do + type "next" + type "jobs" + type "continue" + end + + assert_match(/irb\(main\):001:0> next/, output) + assert_include(output, "Multi-IRB commands are not available when the debugger is enabled.") + end end end