if defined?(RUBY_ENGINE) && 'rbx' == RUBY_ENGINE require 'debase/rbx' elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'truffleruby' require "debase/version" Debugger = Module.new return else require "debase_internals" end require "debase/version" require "debase/context" module Debase class << self attr_accessor :handler alias start_ setup_tracepoints alias stop remove_tracepoints # possibly deprecated options attr_accessor :keep_frame_binding, :tracing def start(options={}, &block) Debugger.const_set('ARGV', ARGV.clone) unless defined? Debugger::ARGV Debugger.const_set('PROG_SCRIPT', $0) unless defined? Debugger::PROG_SCRIPT Debugger.const_set('INITIAL_DIR', Dir.pwd) unless defined? Debugger::INITIAL_DIR monkey_patch_prepend Debugger.started? ? block && block.call(self) : Debugger.start_(&block) end def monkey_patch_prepend class << RubyVM::InstructionSequence def self.prepend(mod, *smth) super if mod.to_s.include?('Bootsnap') && RUBY_VERSION >= '2.5' && RUBY_VERSION < '2.6' prepend InstructionSequenceMixin end end end end # @param [String] file # @param [Fixnum] line # @param [String] expr def add_breakpoint(file, line, expr=nil) breakpoint = Breakpoint.new(file, line, expr) breakpoints << breakpoint enable_trace_points breakpoint end def remove_breakpoint(id) Breakpoint.remove breakpoints, id end def source_reload; {} end def post_mortem? false end def debug false end def add_catchpoint(exception) catchpoints[exception] = 0 enable_trace_points end def remove_catchpoint(exception) catchpoints.delete(exception) end def clear_catchpoints catchpoints.clear end #call-seq: # Debase.skip { block } -> obj or nil # #The code inside of the block is escaped from the debugger. def skip #it looks like no-op is ok for this method for now #no-op end #call-seq: # Debugger.last_interrupted -> context # #Returns last debugged context. def last_context # not sure why we need this so let's return nil for now ;) nil end def file_filter @file_filter ||= FileFilter.new end module InstructionSequenceMixin def load_iseq(path) iseq = super(path) do_set_flags(iseq) iseq end def do_set_flags(iseq) Debugger.set_trace_flag_to_iseq(iseq) iseq.each_child { |child_iseq| do_set_flags(child_iseq) } if iseq.respond_to? :each_child end end end class FileFilter def initialize @enabled = false end def include(file_path) included << file_path unless excluded.delete(file_path) end def exclude(file_path) excluded << file_path unless included.delete(file_path) end def enable @enabled = true Debase.enable_file_filtering(@enabled); end def disable @enabled = false Debase.enable_file_filtering(@enabled); end def accept?(file_path) return true unless @enabled return false if file_path.nil? included.any? { |path| file_path.start_with?(path) } && excluded.all? { |path| !file_path.start_with?(path)} end private def included @included ||= [] end def excluded @excluded ||= [] end end class DebugThread < Thread def self.inherited raise RuntimeError.new("Can't inherit Debugger::DebugThread class") end end end Debugger = Debase