rb-threadframe 0.32
Sign up to get free protection for your applications and to get access to all the features.
- 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
|