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 Aug 9, 2023
1 parent a11b1dd commit f3f334c
Show file tree
Hide file tree
Showing 3 changed files with 71 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
36 changes: 34 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 @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
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 @@ -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

0 comments on commit f3f334c

Please sign in to comment.