-
Notifications
You must be signed in to change notification settings - Fork 130
/
Copy pathframe_info.rb
179 lines (153 loc) · 4 KB
/
frame_info.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# 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