From 74cc581864c7b8754a253c293630750285229e12 Mon Sep 17 00:00:00 2001 From: st0012 Date: Sat, 29 Oct 2022 11:51:53 +0100 Subject: [PATCH] Add debug command --- lib/irb.rb | 13 +++++++++++++ lib/irb/cmd/debug.rb | 33 +++++++++++++++++++++++++++++++++ lib/irb/extend-command.rb | 4 ++++ 3 files changed, 50 insertions(+) create mode 100644 lib/irb/cmd/debug.rb diff --git a/lib/irb.rb b/lib/irb.rb index ab5702c9f..5cae93c90 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -393,6 +393,8 @@ def IRB.irb_abort(irb, exception = Abort) end class Irb + DIR_NAME = __dir__ + ASSIGNMENT_NODE_TYPES = [ # Local, instance, global, class, constant, instance, and index assignment: # "foo = bar", @@ -434,6 +436,16 @@ def initialize(workspace = nil, input_method = nil) @scanner = RubyLex.new end + def debug_break + # it means the debug command is executed + if defined?(DEBUGGER__) && DEBUGGER__.respond_to?(:original_capture_frames) + # after leaving this initial breakpoint, revert the capture_frames patch + DEBUGGER__.singleton_class.send(:alias_method, :capture_frames, :original_capture_frames) + # and remove the redundant method + DEBUGGER__.singleton_class.send(:undef_method, :original_capture_frames) + end + end + def run(conf = IRB.conf) conf[:IRB_RC].call(context) if conf[:IRB_RC] conf[:MAIN_CONTEXT] = context @@ -931,5 +943,6 @@ def irb binding_irb = IRB::Irb.new(workspace) binding_irb.context.irb_path = File.expand_path(source_location[0]) binding_irb.run(IRB.conf) + binding_irb.debug_break end end diff --git a/lib/irb/cmd/debug.rb b/lib/irb/cmd/debug.rb new file mode 100644 index 000000000..8aab40cf8 --- /dev/null +++ b/lib/irb/cmd/debug.rb @@ -0,0 +1,33 @@ +require_relative "nop" + +module IRB + # :stopdoc: + + module ExtendCommand + class Debug < Nop + def execute(*args) + require "debug/session" + DEBUGGER__.start(nonstop: true) + DEBUGGER__.singleton_class.send(:alias_method, :original_capture_frames, :capture_frames) + + def DEBUGGER__.capture_frames(skip_path_prefix) + frames = original_capture_frames(skip_path_prefix) + frames.reject! do |frame| + frame.realpath&.start_with?(::IRB::Irb::DIR_NAME) || frame.path.match?(/internal:prelude/) + end + frames + end + + file, lineno = IRB::Irb.instance_method(:debug_break).source_location + DEBUGGER__::SESSION.add_line_breakpoint(file, lineno + 1, oneshot: true, hook_call: false) + # exit current Irb#run call + throw :IRB_EXIT + rescue LoadError => e + 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 + end + end + end +end diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb index acc23c992..7da75fe14 100644 --- a/lib/irb/extend-command.rb +++ b/lib/irb/extend-command.rb @@ -116,6 +116,10 @@ def irb_context [:kill, OVERRIDE_PRIVATE_ONLY], ], + [ + :irb_debug, :Debug, "cmd/debug", + [:debug, NO_OVERRIDE], + ], [ :irb_help, :Help, "cmd/help", [:help, NO_OVERRIDE],