class Byebug::DAP::CommandProcessor
Processes thread-specific commands and handles Byebug/TracePoint events.
Attributes
The thread context. @return [gem:byebug:Byebug::Context]
The last exception that occured. @return [std:Exception]
Indicates that the client requested a pause. @return [Boolean] @note This should only be set by {Command::Pause} @api private
Public Class Methods
Create a new command processor. @param context [gem:byebug:Byebug::Context] the thread context @param session [Session] the debugging session @note This should only be used by Byebug
internals @api private
# File lib/byebug/dap/command_processor.rb, line 38 def initialize(context, session) @context = context @session = session @requests = Channel.new @exec_mu = Mutex.new @exec_ch = Channel.new end
Public Instance Methods
Send a message to the thread context. @param message the message to send @note Raises a {TimeoutError timeout error} after 1 second if the thread is not paused or not responding.
# File lib/byebug/dap/command_processor.rb, line 54 def <<(message) @requests.push(message, timeout: 1) { raise TimeoutError.new(context) } end
Breakpoint handler. @note This should only be called by Byebug
internals @api private
# File lib/byebug/dap/command_processor.rb, line 109 def at_breakpoint(breakpoint) @last_breakpoint = breakpoint end
Catchpoint handler. @note This should only be called by Byebug
internals @api private
# File lib/byebug/dap/command_processor.rb, line 116 def at_catchpoint(exception) @last_exception = exception end
End of class/module handler. @note This should only be called by Byebug
internals @api private
# File lib/byebug/dap/command_processor.rb, line 87 def at_end stopped! end
Line handler. @note This should only be called by Byebug
internals @api private
# File lib/byebug/dap/command_processor.rb, line 80 def at_line stopped! end
Return handler. @note This should only be called by Byebug
internals @api private
# File lib/byebug/dap/command_processor.rb, line 94 def at_return(return_value) @at_return = return_value stopped! end
Tracing handler. @note This should only be called by Byebug
internals @api private
# File lib/byebug/dap/command_processor.rb, line 102 def at_tracing # @session.puts "Tracing: #{context.full_location}" end
Execute a code block in the thread. @yield the code block to execute @note This calls {#<<} and thus may raise a {TimeoutError timeout error}.
# File lib/byebug/dap/command_processor.rb, line 61 def execute(&block) raise "Block required" unless block_given? r, err = nil, nil @exec_mu.synchronize { self << block r, err = @exec_ch.pop } if err raise err else r end end
(see Session#log
)
# File lib/byebug/dap/command_processor.rb, line 47 def log(*args) @session.log(*args) end
Private Instance Methods
# File lib/byebug/dap/command_processor.rb, line 187 def logpoint! return false unless @last_breakpoint breakpoint, @last_breakpoint = @last_breakpoint, nil expr = @session.get_log_point(breakpoint) return false unless expr binding = @context.frame._binding msg = expr.gsub(/\{([^\}]+)\}/) do |x| safe(binding, :eval, x[1...-1]) { return true } # ignore bad log points end body = { category: 'console', output: msg + "\n", } if breakpoint.pos.is_a?(Integer) body[:line] = breakpoint.pos body[:source] = { name: File.basename(breakpoint.source), path: breakpoint.source, } end @session.event! 'output', **body return true end
# File lib/byebug/dap/command_processor.rb, line 122 def process_requests loop do request = @requests.pop break unless request if request.is_a?(Proc) err = nil r = safe(request, :call) { |e| err = e; nil } @exec_ch.push [r, err] next end break if ContextualCommand.execute(@session, request, self) == :stop end @last_exception = nil @session.invalidate_handles! rescue StandardError => e log "\n! #{e.message} (#{e.class})", *e.backtrace end
# File lib/byebug/dap/command_processor.rb, line 144 def stopped! return if logpoint! case context.stop_reason when :breakpoint args = { reason: 'breakpoint', description: 'Hit breakpoint', text: "Hit breakpoint at #{context.location}", } when :catchpoint args = { reason: 'exception', description: 'Hit catchpoint', text: "Hit catchpoint at #{context.location}", } when :step if @pause_requested @pause_requested = false args = { reason: 'pause', description: 'Paused', text: "Paused at #{context.location}" } else args = { reason: 'step', description: 'Stepped', text: "Stepped at #{context.location}" } end else log "Stopped for unknown reason: #{context.stop_reason}" end @session.event! 'stopped', threadId: context.thnum, **args if args process_requests end