rb-threadframe 0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +23 -0
- data/NEWS +2 -0
- data/README.md +9 -0
- data/Rakefile +162 -0
- data/ext/extconf.rb +12 -0
- data/ext/iseq_extra.c +404 -0
- data/ext/iseq_extra.h +8 -0
- data/ext/iseq_mini.h +41 -0
- data/ext/node.h +483 -0
- data/ext/proc_extra.c +108 -0
- data/ext/proc_extra.h +3 -0
- data/ext/thread_extra.c +84 -0
- data/ext/thread_extra.h +5 -0
- data/ext/thread_frame.c +1022 -0
- data/ext/thread_frame.h +4 -0
- data/ext/thread_pthread.h +24 -0
- data/include/method_mini.h +90 -0
- data/include/node.h +483 -0
- data/include/ruby19_externs.h +36 -0
- data/include/thread_pthread.h +24 -0
- data/include/vm_core_mini.h +357 -0
- data/lib/iseq_extra.rb +89 -0
- data/lib/thread_frame.rb +3 -0
- data/test/unit/cfunc-use.rb +11 -0
- data/test/unit/test-argc.rb +45 -0
- data/test/unit/test-binding.rb +44 -0
- data/test/unit/test-invalid.rb +40 -0
- data/test/unit/test-iseq-brkpt.rb +61 -0
- data/test/unit/test-iseq.rb +121 -0
- data/test/unit/test-lib-iseq-extra.rb +57 -0
- data/test/unit/test-prev.rb +54 -0
- data/test/unit/test-proc.rb +23 -0
- data/test/unit/test-return-stop.rb +64 -0
- data/test/unit/test-settracefunc.rb +315 -0
- data/test/unit/test-source.rb +104 -0
- data/test/unit/test-sp-size.rb +45 -0
- data/test/unit/test-thread-trace-masks.rb +90 -0
- data/test/unit/test-thread.rb +168 -0
- data/test/unit/test-trace.rb +55 -0
- data/threadframe.rd +163 -0
- metadata +110 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
# require 'thread_frame' # To compare with previous version
|
4
|
+
require_relative '../../ext/thread_frame'
|
5
|
+
|
6
|
+
# Test source_location and source_container.
|
7
|
+
class TestSpSize < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def sizes
|
10
|
+
tf = RubyVM::ThreadFrame::current
|
11
|
+
ary = []
|
12
|
+
0.upto(2) do |i|
|
13
|
+
ary << tf.sp_size
|
14
|
+
tf = tf.prev
|
15
|
+
end
|
16
|
+
# Swap first two items. The item that generally
|
17
|
+
# will vary is the a[0].
|
18
|
+
ary[0], ary[1] = ary[1], ary[0]
|
19
|
+
# p ary
|
20
|
+
return ary
|
21
|
+
end
|
22
|
+
|
23
|
+
def f0; return sizes end
|
24
|
+
def f1; a=1; return sizes end
|
25
|
+
def f1a(a) return sizes end
|
26
|
+
def f2(a,b) return sizes end
|
27
|
+
|
28
|
+
def test_sp_size
|
29
|
+
f0_s = f0
|
30
|
+
f1_s = f1
|
31
|
+
f1a_s = f1a(1)
|
32
|
+
f2_s = f2(1,2)
|
33
|
+
assert_equal(f0_s[0]+1, f1_s[0])
|
34
|
+
assert_equal(f0_s[1..-1], f1_s[1..-1])
|
35
|
+
assert_equal(f1_s, f1a_s)
|
36
|
+
assert_equal(f1_s[0]+1, f2_s[0])
|
37
|
+
assert_equal(f1_s[1..-1], f2_s[1..-1])
|
38
|
+
|
39
|
+
assert_raises ArgumentError do
|
40
|
+
tf = RubyVM::ThreadFrame.current
|
41
|
+
tf.sp_set(tf.sp_size, "Should not be able to set this.")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# Test of additional tracing flag use to selectively turn on off tracing
|
2
|
+
require 'test/unit'
|
3
|
+
require_relative '../../ext/thread_frame'
|
4
|
+
|
5
|
+
class TestTracingMasks < Test::Unit::TestCase
|
6
|
+
@@EVENT2MASK = {
|
7
|
+
'line' => 0x01,
|
8
|
+
'call' => 0x08,
|
9
|
+
'return' => 0x10,
|
10
|
+
'c-call' => 0x20,
|
11
|
+
'c-return' => 0x40,
|
12
|
+
'raise' => 0x80,
|
13
|
+
}
|
14
|
+
|
15
|
+
def something_to_test(n)
|
16
|
+
def sqr(x)
|
17
|
+
x * x
|
18
|
+
end
|
19
|
+
begin
|
20
|
+
n += sqr(5)
|
21
|
+
raise TypeError, 'error'
|
22
|
+
rescue TypeError => $e
|
23
|
+
end
|
24
|
+
return [1,2,3].all? {|n| n}
|
25
|
+
end
|
26
|
+
|
27
|
+
def chunk(list, char='-')
|
28
|
+
sep = char * 30 + "\n"
|
29
|
+
sep + list.map{|e| e.join(' ')}.join("\n") + "\n"
|
30
|
+
end
|
31
|
+
|
32
|
+
def checkit(event_name)
|
33
|
+
chunk(@events) if $DEBUG
|
34
|
+
assert @events.all?{|e| e[1] == event_name}, chunk(@events)
|
35
|
+
assert @events.size > 0, "Expecting at least one #{event_name}"
|
36
|
+
@counts[event_name] = @events.size if @keep_count
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_thread_trace_mask
|
40
|
+
def trace_hook(event, file, line, id, binding, klass)
|
41
|
+
@events << [line, event, id, klass]
|
42
|
+
end
|
43
|
+
|
44
|
+
@keep_count = true
|
45
|
+
@counts = {}
|
46
|
+
@@EVENT2MASK.each do |event_name, mask|
|
47
|
+
@events = []
|
48
|
+
Thread.current.set_trace_func(method(:trace_hook).to_proc, mask)
|
49
|
+
something_to_test(5)
|
50
|
+
Thread.current.set_trace_func(nil)
|
51
|
+
checkit(event_name)
|
52
|
+
end
|
53
|
+
[%w(call return),
|
54
|
+
%w(c-call c-return)].each do |c, r|
|
55
|
+
assert_equal(@counts[c], @counts[r],
|
56
|
+
"Expecting # of #{c}'s to equal # of #{r}'s")
|
57
|
+
end
|
58
|
+
|
59
|
+
@keep_count = false
|
60
|
+
|
61
|
+
[%w(line call),
|
62
|
+
%w(raise c-return return),
|
63
|
+
].each do |event_names|
|
64
|
+
@events = []
|
65
|
+
mask = event_names.map{|name|
|
66
|
+
@@EVENT2MASK[name]
|
67
|
+
}.inject do
|
68
|
+
|mask, bit|
|
69
|
+
mask |= bit
|
70
|
+
end
|
71
|
+
Thread.current.set_trace_func(method(:trace_hook).to_proc, mask)
|
72
|
+
something_to_test(5)
|
73
|
+
Thread.current.set_trace_func(nil)
|
74
|
+
total = event_names.map{|name| @counts[name]}.inject do
|
75
|
+
|sum, n|
|
76
|
+
sum += n
|
77
|
+
end
|
78
|
+
assert_equal(total, @events.size, chunk(@events))
|
79
|
+
end
|
80
|
+
|
81
|
+
# Try without a mask and see that we get the sum of all events
|
82
|
+
@events = []
|
83
|
+
Thread.current.set_trace_func(method(:trace_hook).to_proc)
|
84
|
+
something_to_test(5)
|
85
|
+
Thread.current.set_trace_func(nil)
|
86
|
+
total = @counts.values.inject {|sum, n| sum += n}
|
87
|
+
assert_equal(total, @events.size, chunk(@events))
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require_relative '../../ext/thread_frame'
|
3
|
+
|
4
|
+
class TestThread < Test::Unit::TestCase
|
5
|
+
def test_basic
|
6
|
+
assert_equal(RubyVM::ThreadFrame.new(Thread::current).thread,
|
7
|
+
RubyVM::ThreadFrame::current.thread)
|
8
|
+
assert_equal(RubyVM::ThreadFrame.new(Thread::current).thread,
|
9
|
+
Thread::current)
|
10
|
+
assert_equal(Thread::current.threadframe.thread, Thread::current)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_pc_offset
|
14
|
+
tf = RubyVM::ThreadFrame::current
|
15
|
+
offset_1 = tf.pc_offset
|
16
|
+
assert_equal(true, offset_1 > 0,
|
17
|
+
"Expecting a positive integer pc offset, got %s" % offset_1)
|
18
|
+
offset_2 = tf.pc_offset
|
19
|
+
assert_equal(true, offset_2 > 0,
|
20
|
+
"Expecting a positive integer pc offset, got %s" % offset_2)
|
21
|
+
assert_equal(true, offset_2 > offset_1,
|
22
|
+
"Expecting second pc offset %s to be larger than the first %s" %
|
23
|
+
[offset_2, offset_1])
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_sp
|
27
|
+
tf = RubyVM::ThreadFrame::current.prev
|
28
|
+
assert tf.sp(1)
|
29
|
+
tf.sp_set(1, 5)
|
30
|
+
assert_equal(5, tf.sp(1),
|
31
|
+
'chcking value of recently-set sp(1)')
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_source_container
|
35
|
+
cfunc_filebase = 'cfunc-use'
|
36
|
+
load File.join(File.dirname(__FILE__), cfunc_filebase + '.rb')
|
37
|
+
type, loc = cfunc_loc
|
38
|
+
assert_equal(['CFUNC', cfunc_filebase], [type, File.basename(loc, '.rb')],
|
39
|
+
'CFUNCs should get their file location from frame.prev*')
|
40
|
+
cont = 'file'
|
41
|
+
eval '1.times{cont = RubyVM::ThreadFrame.current.source_container[0]}'
|
42
|
+
assert_equal('string', cont,
|
43
|
+
'source container[0] of an eval(...) should be "string"')
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def test_source_location
|
48
|
+
line = __LINE__
|
49
|
+
# There is a bug in Ruby 1.9.2 and before in reporting the source
|
50
|
+
# location when the PC is 0. Probably never reported before
|
51
|
+
# because the location is reported on a traceback
|
52
|
+
# and that probably can't happen at PC 0.
|
53
|
+
def bug_when_zero_pc
|
54
|
+
@not_first = true
|
55
|
+
tf = RubyVM::ThreadFrame::current.prev
|
56
|
+
pc_save = tf.pc_offset
|
57
|
+
tf.pc_offset = 0
|
58
|
+
loc = tf.source_location
|
59
|
+
tf.pc_offset = pc_save
|
60
|
+
loc
|
61
|
+
end
|
62
|
+
loc = bug_when_zero_pc unless @not_first
|
63
|
+
assert_equal([line - 1], loc)
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def test_thread_tracing
|
68
|
+
assert_equal(false, Thread.current.tracing)
|
69
|
+
Thread.current.tracing = true
|
70
|
+
assert_equal(true, Thread.current.tracing)
|
71
|
+
Thread.current.tracing = false
|
72
|
+
assert_equal(false, Thread.current.tracing)
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_thread_exec_event_tracing
|
76
|
+
assert_equal(false, Thread.current.exec_event_tracing)
|
77
|
+
Thread.current.exec_event_tracing = true
|
78
|
+
assert_equal(true, Thread.current.exec_event_tracing)
|
79
|
+
Thread.current.exec_event_tracing = false
|
80
|
+
assert_equal(false, Thread.current.exec_event_tracing)
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_fields(notused=nil)
|
84
|
+
tf = RubyVM::ThreadFrame::current
|
85
|
+
pc1 = tf.pc_offset
|
86
|
+
assert(pc1 > 0, 'Should be able to get a valid PC offset')
|
87
|
+
# pc_offset is dynamic - it changes constantly
|
88
|
+
pc2 = tf.pc_offset
|
89
|
+
assert(pc2 > pc1, 'PC offset should have changed (for the greater)')
|
90
|
+
assert_equal('test_fields', tf.method)
|
91
|
+
assert_equal(self, tf.self)
|
92
|
+
assert_equal(0, tf.arity)
|
93
|
+
assert_equal(0, tf.argc)
|
94
|
+
assert tf.dfp(0)
|
95
|
+
assert tf.lfp(0)
|
96
|
+
|
97
|
+
assert_raises IndexError do
|
98
|
+
x = tf.lfp(tf.iseq.local_size+1)
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
tf_prev = tf.prev
|
103
|
+
assert(tf_prev.pc_offset > 0, "Should be valid PC offset for prev")
|
104
|
+
|
105
|
+
# Is this too specific to test/unit.rb implementation details?
|
106
|
+
assert_equal('run', tf_prev.method)
|
107
|
+
|
108
|
+
# 1.times creates a C frame.
|
109
|
+
1.times do
|
110
|
+
tf = RubyVM::ThreadFrame::current
|
111
|
+
tup = tf.source_container
|
112
|
+
tup[1] = File.basename(tup[1])
|
113
|
+
assert_equal(['file', 'test-thread.rb'], tup)
|
114
|
+
assert_equal('block in test_fields', tf.method)
|
115
|
+
assert_equal('CFUNC', tf.prev.type)
|
116
|
+
assert_equal('times', tf.prev.method)
|
117
|
+
assert_equal(self, tf.self)
|
118
|
+
assert_equal(0, tf.prev.arity, 'C arity should work nowadays' )
|
119
|
+
assert_equal(0, tf.prev.argc, 'C args is the same as arity')
|
120
|
+
assert_equal('test_fields', tf.prev.prev.method)
|
121
|
+
assert_equal(0, tf.arity)
|
122
|
+
assert_equal(0, tf.argc)
|
123
|
+
end
|
124
|
+
|
125
|
+
# 1.upto also creates a C frame.
|
126
|
+
1.upto(1) do
|
127
|
+
tf = RubyVM::ThreadFrame::current.prev
|
128
|
+
assert_equal('CFUNC', tf.type)
|
129
|
+
assert_equal(1, tf.arity, 'C arity should work nowadays' )
|
130
|
+
assert_equal(1, tf.argc)
|
131
|
+
end
|
132
|
+
|
133
|
+
x = lambda do |x,y|
|
134
|
+
frame = RubyVM::ThreadFrame::current
|
135
|
+
assert_equal('block in test_fields', frame.method)
|
136
|
+
assert_equal('LAMBDA', frame.type)
|
137
|
+
assert_equal(x, tf.self)
|
138
|
+
assert_equal(2, frame.arity)
|
139
|
+
assert_equal(2, frame.argc)
|
140
|
+
end
|
141
|
+
x.call(x,2)
|
142
|
+
|
143
|
+
x = Proc.new do |x, y|
|
144
|
+
frame = RubyVM::ThreadFrame::current
|
145
|
+
assert_equal('block in test_fields', frame.method)
|
146
|
+
assert_equal(x, tf.self)
|
147
|
+
assert_equal('BLOCK', frame.type)
|
148
|
+
end
|
149
|
+
x.call(x,2)
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_threadframe_equal
|
154
|
+
tf = RubyVM::ThreadFrame.current
|
155
|
+
tf2 = RubyVM::ThreadFrame.current
|
156
|
+
assert_equal(true, tf.equal?(tf))
|
157
|
+
assert_equal(true, tf.equal?(tf2))
|
158
|
+
tf2 = tf2.prev
|
159
|
+
assert_equal(false, tf.equal?(tf2))
|
160
|
+
assert_raises TypeError do
|
161
|
+
tf.equal?(tf.iseq)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# We want to double-check we didn't mess up any pointers somewhere along
|
167
|
+
# the line.
|
168
|
+
at_exit { GC.start }
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Test of additional tracing flag use to selectively turn on off tracing
|
2
|
+
require 'test/unit'
|
3
|
+
require_relative '../../ext/thread_frame'
|
4
|
+
|
5
|
+
class TestTracing < Test::Unit::TestCase
|
6
|
+
def test_basic_query_set_unset
|
7
|
+
tf = RubyVM::ThreadFrame::current
|
8
|
+
# Test default values
|
9
|
+
assert_equal(false, tf.trace_off?)
|
10
|
+
assert_equal(false, tf.return_stop?)
|
11
|
+
|
12
|
+
# Test set true
|
13
|
+
tf.trace_off = true
|
14
|
+
assert_equal(true, tf.trace_off?)
|
15
|
+
tf.return_stop = true
|
16
|
+
assert_equal(true, tf.return_stop?)
|
17
|
+
|
18
|
+
# Test setting off when on
|
19
|
+
tf.trace_off = nil
|
20
|
+
assert_equal(false, tf.trace_off?)
|
21
|
+
tf.return_stop = false
|
22
|
+
assert_equal(false, tf.return_stop?)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_trace_off
|
26
|
+
@levels = []
|
27
|
+
def trace_hook(event, file, line, id, binding, klass)
|
28
|
+
@levels << RubyVM::ThreadFrame::current.stack_size
|
29
|
+
end
|
30
|
+
|
31
|
+
def baz
|
32
|
+
6
|
33
|
+
end
|
34
|
+
def bar(set_off)
|
35
|
+
RubyVM::ThreadFrame::current.trace_off = true if set_off
|
36
|
+
baz
|
37
|
+
5
|
38
|
+
end
|
39
|
+
def foo(set_off)
|
40
|
+
bar(set_off)
|
41
|
+
end
|
42
|
+
# 0x10 is the mask for tracing return events
|
43
|
+
Thread.current.set_trace_func(method(:trace_hook).to_proc, 0x10)
|
44
|
+
foo(false)
|
45
|
+
assert_equal(3, @levels.size)
|
46
|
+
|
47
|
+
@levels = []
|
48
|
+
Thread.current.set_trace_func(method(:trace_hook).to_proc, 0x10)
|
49
|
+
foo(true)
|
50
|
+
Thread.current.set_trace_func(nil)
|
51
|
+
assert_equal(1, @levels.size)
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
data/threadframe.rd
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
# = NAME
|
2
|
+
# +RubyVM+::+ThreadFrame+
|
3
|
+
#
|
4
|
+
# = SYNOPSES
|
5
|
+
#
|
6
|
+
# The +RubyVM+::+ThreadFrame+ class gives call-stack frame information
|
7
|
+
# (controlled access to +rb_control_frame_t+)
|
8
|
+
#
|
9
|
+
#
|
10
|
+
# It is possible that a The +Thread+::+Frame+ may be proposed for
|
11
|
+
# all Ruby 1.9 implementations. +RubyVM+::+ThreadFrame+
|
12
|
+
# contains routines in the YARV 1.9 implementation.
|
13
|
+
#
|
14
|
+
# Should there be a +Thread+::+Frame+ +RubyVM+::+ThreadFrame+ would be
|
15
|
+
# a subclass of +Thread+::+Frame+. In code:
|
16
|
+
#
|
17
|
+
# class Thread
|
18
|
+
# class Frame # In all 1.9 implementations
|
19
|
+
# # ...
|
20
|
+
# end
|
21
|
+
# def threadframe
|
22
|
+
# RubyVM::ThreadFrame.new(self) # or possibly Thread::Frame.new(self)
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# class RubyVM::ThreadFrame < Thread::Frame # In YARV
|
27
|
+
# def initialize(thread_object)
|
28
|
+
# # implementation-specific code
|
29
|
+
# end
|
30
|
+
# def iseq
|
31
|
+
# # implementation of iseq
|
32
|
+
# end
|
33
|
+
# # ...
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# A +RubyVM+::Thread+Frame+ contains information from a frame running +Thread+
|
37
|
+
# object, and the information may change or disappear in the course of
|
38
|
+
# running that thread. Therefore, it is advisable ensure that the
|
39
|
+
# threads of the ThreadFrame objects are blocked.
|
40
|
+
#
|
41
|
+
#
|
42
|
+
# === RubyVM::ThreadFrame::new(thread_object)
|
43
|
+
# Creates a thread-frame object for the given thread object.
|
44
|
+
#
|
45
|
+
# === RubyVM::ThreadFrame::current
|
46
|
+
# Shorthand for RubyVM::ThreadFrame.new(Thread::current)
|
47
|
+
#
|
48
|
+
# === Thread#threadframe
|
49
|
+
# tf = Thread::current.threadframe()
|
50
|
+
#
|
51
|
+
# Creates a thread-frame object for the given thread object.
|
52
|
+
# Note:
|
53
|
+
# Thread::current.threadframe() == RubyVM::ThreadFrame.new(Thread::current)
|
54
|
+
#
|
55
|
+
# == RubyVM::ThreadFrame Instance Methods
|
56
|
+
#
|
57
|
+
# === RubyVM::ThreadFrame#prev
|
58
|
+
# tf.prev(n) -> tf or nil
|
59
|
+
# tf.prev() -> tf or nil # same as tf.prev(1)
|
60
|
+
#
|
61
|
+
# Returns the previous control frame. If tail recursion has removed
|
62
|
+
# frames as seen in the source, deal with it. ;-) +nil+ can be
|
63
|
+
# returned if there is no previous frame or if tf is no longer exists.
|
64
|
+
# If a number is passed go back that many frames. The value 0 gives back
|
65
|
+
# tf. A negative number of a number greater than the number of frames
|
66
|
+
# returns nil.
|
67
|
+
#
|
68
|
+
# === RubyVM::ThreadFrame#invalid?
|
69
|
+
# tf.invalid?() -> boolean
|
70
|
+
#
|
71
|
+
# Returns true if the frame is no longer valid. On the other hand,
|
72
|
+
# since the test we use is weak, returning false might not mean the
|
73
|
+
# frame is valid, just that we can't disprove that it is not invalid.
|
74
|
+
#
|
75
|
+
# It is suggested that frames are used in a way that ensures they will
|
76
|
+
# be valid. In particular frames should have local scope and frames to
|
77
|
+
# threads other than the running one should be stopped while the frame
|
78
|
+
# variable is active.
|
79
|
+
#
|
80
|
+
#
|
81
|
+
# === RubyVM::ThreadFrame#thread
|
82
|
+
# tf.thread() -> Thread
|
83
|
+
# RubyVM::ThreadFrame.current().thread == Thread.current
|
84
|
+
#
|
85
|
+
# === RubyVM::ThreadFrame#type
|
86
|
+
# tf.type() -> 'C' or 'Ruby'
|
87
|
+
#
|
88
|
+
# Indicates whether the frame is implemented in C or Ruby.
|
89
|
+
#
|
90
|
+
# === RubyVM::ThreadFrame#source_container
|
91
|
+
# RubyVM::Threadframe#source_container() -> [Type, String]
|
92
|
+
#
|
93
|
+
# Returns a tuple representing kind of container, e.g. file
|
94
|
+
# eval'd string object, and the name of the container. If file,
|
95
|
+
# it would be a file name. If an eval'd string it might be the string.
|
96
|
+
#
|
97
|
+
# === RubyVM::ThreadFrame#source_location
|
98
|
+
# RubyVM::ThreadFrame#.source_location() -> Array
|
99
|
+
#
|
100
|
+
# Returns an array of source location positions that match
|
101
|
+
# +tf.instruction_offset+. A source location position is left
|
102
|
+
# implementation dependent. It could be line number, a line number
|
103
|
+
# and start and end column, or a start line number, start column, end
|
104
|
+
# line number, end column.
|
105
|
+
#
|
106
|
+
# === RubyVM::ThreadFrame#stack_size
|
107
|
+
# RubyVM::ThreadFrame#.stack_size -> Fixnum
|
108
|
+
#
|
109
|
+
# Returns the number of entries
|
110
|
+
#
|
111
|
+
# === RubyVM::ThreadFrame#binding
|
112
|
+
# tf.binding() -> binding
|
113
|
+
#
|
114
|
+
#
|
115
|
+
# If the frame is still valid, a binding that is in effect for the
|
116
|
+
# scope of the frame is created. As with all bindings, over the
|
117
|
+
# execution of the program, variables may spring into existence and
|
118
|
+
# values may change.
|
119
|
+
|
120
|
+
# == RubyVM::ThreadFrame
|
121
|
+
#
|
122
|
+
# === RubyVM::new(thread_object)
|
123
|
+
|
124
|
+
# Like RubyVM::ThreadFrame.new(thread_object), but has additional information
|
125
|
+
# available.
|
126
|
+
#
|
127
|
+
# === RubyVM::ThreadFrame#iseq
|
128
|
+
# tf.iseq() -> ISeq
|
129
|
+
#
|
130
|
+
# Returns an instruction sequence object from the instruction sequence
|
131
|
+
# found inside the +ThreadFrame+ object or +nil+ if there is none.
|
132
|
+
# But if iseq is +nil+ and tf.type is not C, +binding+, and
|
133
|
+
# +instruction_offset+, and +source_location+ are probably meaningless
|
134
|
+
# and will be +nil+ as well.
|
135
|
+
#
|
136
|
+
#
|
137
|
+
# === RubyVM::ThreadFrame#instruction_offset
|
138
|
+
# tf.instruction_offset -> Fixnum
|
139
|
+
# Offset inside ISeq of instruction that the frame is currently on.
|
140
|
+
#
|
141
|
+
# === RubyVM::ThreadFrame#instruction_offset=
|
142
|
+
#
|
143
|
+
# tf.instruction_offset=(Fixnum)
|
144
|
+
# Sets the threadframe to a new offset. Some restrictions may apply, e.g.
|
145
|
+
# the offset will have to refer to a valid offset location and the scope
|
146
|
+
# and block level has to be the same.
|
147
|
+
#
|
148
|
+
# <em>Don't need to implement initially.</em>
|
149
|
+
#
|
150
|
+
# === RubyVM::ThreadFrame#return_changed?
|
151
|
+
# tf.return_changed?() -> boolean
|
152
|
+
#
|
153
|
+
# Returns true if tf _may_ be part of tail recursion removal so when
|
154
|
+
# the code in frame returns it may not be to the immediate caller as
|
155
|
+
# seen in the source code. Koichi says this is too difficult to do in
|
156
|
+
# YARV.
|
157
|
+
#
|
158
|
+
# <em>Don't need to implement initially.</em>
|
159
|
+
#
|
160
|
+
# = THANKS
|
161
|
+
#
|
162
|
+
# Martin Davis for suggesting
|
163
|
+
# RubyVM::ThreadFrame#new == Thread::current.threadframe
|