File: //proc/thread-self/root/usr/lib/ruby/gems/3.2.0/gems/debug-1.7.1/lib/debug/frame_info.rb
# frozen_string_literal: true
module DEBUGGER__
FrameInfo = Struct.new(:location, :self, :binding, :iseq, :class, :frame_depth,
:has_return_value, :return_value,
:has_raised_exception, :raised_exception,
:show_line,
:_local_variables, :_callee, # for recorder
:dupped_binding,
)
# extend FrameInfo with debug.so
begin
require_relative 'debug.so'
rescue LoadError
require 'debug/debug.so'
end
class FrameInfo
HOME = ENV['HOME'] ? (ENV['HOME'] + '/') : nil
def path
location.path
end
def realpath
location.absolute_path
end
def self.pretty_path path
return '#<none>' unless path
use_short_path = CONFIG[:use_short_path]
case
when use_short_path && path.start_with?(dir = RbConfig::CONFIG["rubylibdir"] + '/')
path.sub(dir, '$(rubylibdir)/')
when use_short_path && Gem.path.any? do |gp|
path.start_with?(dir = gp + '/gems/')
end
path.sub(dir, '$(Gem)/')
when HOME && path.start_with?(HOME)
path.sub(HOME, '~/')
else
path
end
end
def pretty_path
FrameInfo.pretty_path path
end
def name
# p frame_type: frame_type, self: self
case frame_type
when :block
level, block_loc = block_identifier
"block in #{block_loc}#{level}"
when :method
method_identifier
when :c
c_identifier
when :other
other_identifier
end
end
def file_lines
SESSION.source(self.iseq)
end
def frame_type
if self.local_variables && iseq
if iseq.type == :block
:block
elsif callee
:method
else
:other
end
else
:c
end
end
BLOCK_LABL_REGEXP = /\Ablock( \(\d+ levels\))* in (.+)\z/
def block_identifier
return unless frame_type == :block
_, level, block_loc = location.label.match(BLOCK_LABL_REGEXP).to_a
[level || "", block_loc]
end
def method_identifier
return unless frame_type == :method
"#{klass_sig}#{callee}"
end
def c_identifier
return unless frame_type == :c
"[C] #{klass_sig}#{location.base_label}"
end
def other_identifier
return unless frame_type == :other
location.label
end
def callee
self._callee ||= begin
self.binding&.eval('__callee__')
rescue NameError # BasicObject
nil
end
end
def return_str
if self.binding && iseq && has_return_value
DEBUGGER__.safe_inspect(return_value, short: true)
end
end
def matchable_location
# realpath can sometimes be nil so we can't use it here
"#{path}:#{location.lineno}"
end
def location_str
"#{pretty_path}:#{location.lineno}"
end
def eval_binding
if b = self.dupped_binding
b
else
b = self.binding || TOPLEVEL_BINDING
self.dupped_binding = b.dup
end
end
def local_variables
if lvars = self._local_variables
lvars
elsif b = self.binding
b.local_variables.map{|var|
[var, b.local_variable_get(var)]
}.to_h
end
end
def parameters_info
vars = iseq.parameters_symbols
vars.map{|var|
begin
{ name: var, value: DEBUGGER__.safe_inspect(local_variable_get(var), short: true) }
rescue NameError, TypeError
nil
end
}.compact
end
private def get_singleton_class obj
obj.singleton_class # TODO: don't use it
rescue TypeError
nil
end
private def local_variable_get var
local_variables[var]
end
private def klass_sig
if self.class == get_singleton_class(self.self)
"#{self.self}."
else
"#{self.class}#"
end
end
end
end