123456789_123456789_123456789_123456789_123456789_

Class: IRB::ExtendCommand::Debug

Do not use. This class is for internal use only.
Relationships & Source Files
Namespace Children
Modules:
Extension / Inclusion / Inheritance Descendants
Subclasses:
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Nop
Instance Chain:
self, Nop
Inherits: IRB::ExtendCommand::Nop
Defined in: lib/irb/cmd/debug.rb

Constant Summary

Class Method Summary

Nop - Inherited

.category, .description,
.execute

See additional method definition at line 39.

.new, .string_literal?

Instance Attribute Summary

Nop - Inherited

Instance Method Summary

Nop - Inherited

Constructor Details

This class inherits a constructor from IRB::ExtendCommand::Nop

Instance Attribute Details

#binding_irb?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/irb/cmd/debug.rb', line 50

def binding_irb?
  caller.any? do |frame|
    BINDING_IRB_FRAME_REGEXPS.any? do |regexp|
      frame.match?(regexp)
    end
  end
end

Instance Method Details

#execute(pre_cmds: nil, do_cmds: nil)

[ GitHub ]

  
# File 'lib/irb/cmd/debug.rb', line 17

def execute(pre_cmds: nil, do_cmds: nil)
  unless binding_irb?
    puts "`debug` command is only available when IRB is started with binding.irb"
    return
  end

  unless setup_debugger
    puts <<~MSG
      You need to install the debug gem before using this command.
      If you use `bundle exec`, please add `gem "debug"` into your Gemfile.
    MSG
    return
  end

  options = { oneshot: true, hook_call: false }
  if pre_cmds || do_cmds
    options[:command] = ['irb', pre_cmds, do_cmds]
  end
  if DEBUGGER__::LineBreakpoint.instance_method(:initialize).parameters.include?([:key, :skip_src])
    options[:skip_src] = true
  end

  # To make debugger commands like `next` or `continue` work without asking
  # the user to quit IRB after that, we need to exit IRB first and then hit
  # a TracePoint on #debug_break.
  file, lineno = IRB::Irb.instance_method(:debug_break).source_location
  DEBUGGER__::SESSION.add_line_breakpoint(file, lineno + 1, **options)
  # exit current Irb#run call
  throw :IRB_EXIT
end

#load_bundled_debug_gem

This is used when debug.gem is not written in Gemfile. Even if it’s not installed by bundle install, debug.gem is installed by default because it’s a bundled gem. This method tries to activate and load that.

[ GitHub ]

  
# File 'lib/irb/cmd/debug.rb', line 95

def load_bundled_debug_gem
  # Discover latest debug.gem under GEM_PATH
  debug_gem = Gem.paths.path.flat_map { |path| Dir.glob("#{path}/gems/debug-*") }.select do |path|
    File.basename(path).match?(/\Adebug-\d\.\d\.\d(\w)?\z/)
  end.sort_by do |path|
    Gem::Version.new(File.basename(path).delete_prefix('debug-'))
  end.last
  return false unless debug_gem

  # Discover debug/debug.so under extensions for Ruby 3.2+
  ext_name = "/debug/debug.#{RbConfig::CONFIG['DLEXT']}"
  ext_path = Gem.paths.path.flat_map do |path|
    Dir.glob("#{path}/extensions/**/#{File.basename(debug_gem)}#{ext_name}")
  end.first

  # Attempt to forcibly load the bundled gem
  if ext_path
    $LOAD_PATH << ext_path.delete_suffix(ext_name)
  end
  $LOAD_PATH << "#{debug_gem}/lib"
  begin
    require "debug/session"
    puts "Loaded #{File.basename(debug_gem)}"
    true
  rescue LoadError
    false
  end
end

#setup_debugger

[ GitHub ]

  
# File 'lib/irb/cmd/debug.rb', line 65

def setup_debugger
  unless defined?(DEBUGGER__::SESSION)
    begin
      require "debug/session"
    rescue LoadError # debug.gem is not written in Gemfile
      return false unless load_bundled_debug_gem
    end
    DEBUGGER__.start(nonstop: true)
  end

  unless DEBUGGER__.respond_to?(:capture_frames_without_irb)
    DEBUGGER__.singleton_class.send(:alias_method, :capture_frames_without_irb, :capture_frames)

    def DEBUGGER__.capture_frames(*args)
      frames = capture_frames_without_irb(*args)
      frames.reject! do |frame|
        frame.realpath&.start_with?(IRB_DIR) || frame.path == "<internal:prelude>"
      end
      frames
    end

    DEBUGGER__::ThreadClient.prepend(SkipPathHelperForIRB)
  end

  true
end