Skip to content

Commit

Permalink
Prevent using multi-irb and activating debugger at the same time
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
st0012 committed Jul 21, 2023
1 parent 4df99ae commit 0727006
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 2 deletions.
5 changes: 5 additions & 0 deletions lib/irb/cmd/debug.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
31 changes: 29 additions & 2 deletions lib/irb/cmd/subirb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ module IRB

module ExtendCommand
class MultiIRBCommand < Nop
def initialize(conf)
super
def execute(*args)
extend_irb_context
end

Expand All @@ -22,13 +21,23 @@ 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
category "IRB"
description "Start a child IRB."

def execute(*obj)
if irb_context.with_debugger
print_debugger_warning
return
end

super
IRB.irb(nil, *obj)
end
end
Expand All @@ -38,6 +47,12 @@ class Jobs < MultiIRBCommand
description "List of current sessions."

def execute
if irb_context.with_debugger
print_debugger_warning
return
end

super
IRB.JobManager
end
end
Expand All @@ -47,6 +62,12 @@ class Foreground < MultiIRBCommand
description "Switches to the session of the given number."

def execute(key = nil)
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
Expand All @@ -57,6 +78,12 @@ class Kill < MultiIRBCommand
description "Kills the session with the given number."

def execute(*keys)
if irb_context.with_debugger
print_debugger_warning
return
end

super
IRB.JobManager.kill(*keys)
end
end
Expand Down
32 changes: 32 additions & 0 deletions test/irb/test_debug_cmd.rb
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,38 @@ def bar
assert_include(output, "show_source Show the source code of a given method or constant.")
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

private

TIMEOUT_SEC = 3
Expand Down

0 comments on commit 0727006

Please sign in to comment.