rb-threadframe 0.39 → 0.40
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/NEWS +6 -0
- data/README.md +6 -3
- data/Rakefile +4 -4
- data/ext/1.9.3/thread_frame.c +22 -0
- data/ext/extconf.rb +2 -2
- data/ext/version.h +1 -0
- data/lib/iseq_extra.rb +7 -6
- data/lib/thread_frame.rb +9 -1
- data/test/unit/cfunc-use.rb +1 -1
- data/test/unit/test-argc.rb +3 -2
- data/test/unit/test-binding.rb +5 -5
- data/test/unit/{test-thread.rb → test-frame.rb} +36 -33
- data/test/unit/test-invalid.rb +6 -5
- data/test/unit/test-iseq-brkpt.rb +4 -4
- data/test/unit/test-iseq-save.rb +1 -1
- data/test/unit/test-iseq.rb +10 -9
- data/test/unit/test-lib-iseq-extra.rb +14 -5
- data/test/unit/test-lib-iseq.rb +9 -8
- data/test/unit/test-prev.rb +12 -12
- data/test/unit/test-proc.rb +4 -1
- data/test/unit/test-return-stop.rb +3 -4
- data/test/unit/test-settracefunc.rb +67 -43
- data/test/unit/test-source.rb +7 -8
- data/test/unit/test-sp-size.rb +4 -5
- data/test/unit/test-thread-trace-masks.rb +2 -1
- data/test/unit/test-trace.rb +4 -4
- data/threadframe.rd +31 -31
- metadata +31 -57
- data/ext/iseq_extra.c +0 -441
- data/ext/iseq_extra.h +0 -8
- data/ext/iseq_mini.h +0 -41
- data/ext/node.h +0 -483
- data/ext/proc_extra.c +0 -186
- data/ext/proc_extra.h +0 -3
- data/ext/thread_extra.c +0 -84
- data/ext/thread_extra.h +0 -5
- data/ext/thread_frame.c +0 -1165
- data/ext/thread_frame.h +0 -4
- data/ext/thread_pthread.h +0 -24
- data/test/ruby/test_brkpt.rb +0 -60
- data/test/ruby/test_disasm.rb +0 -17
- data/test/ruby/test_iseq.rb +0 -50
- data/test/ruby/test_tracefunc_raise.rb +0 -26
data/NEWS
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
Nov 25, 2012 (0.40)
|
2
|
+
- Redone for Ruby 1.9.3 p327
|
3
|
+
- RubyVM::ThreadFrame is now RubyVM::Frame. RubyVM::ThreadFrame is still
|
4
|
+
there for compatibility
|
5
|
+
- Some small administrative and bug fixes
|
6
|
+
|
1
7
|
Oct 27, 2011 (0.39)
|
2
8
|
- Revise patch for 1.9.2 revision 33354
|
3
9
|
- save return value in one more C_RETURN hook
|
data/README.md
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
# rb-threadframe
|
2
2
|
|
3
|
-
rb-threadframe is a set of patches to Ruby 1.9.2 and
|
4
|
-
|
3
|
+
rb-threadframe is a set of patches to Ruby MRI 1.9.2 and 1.9.3 that
|
4
|
+
adds introspection of call frames and adds other run-time support for debuggers and run-time introspection.
|
5
|
+
|
6
|
+
For MRI 1.9.2, there are additional routines are in a C extension. For MRI 1.9.3, everthing is in a patched Ruby. Necessary patches and some simple patch code are found in this repository though.
|
5
7
|
|
6
8
|
## Requirements
|
7
9
|
|
8
|
-
|
10
|
+
Source for Ruby MRI version 1.9.2 or 1.9.3 p327. Patches are provided
|
11
|
+
for these two versions only.
|
9
12
|
|
10
13
|
See the [wiki](http://github.com/rocky/rb-threadframe/wiki) for more information.
|
data/Rakefile
CHANGED
@@ -14,7 +14,7 @@ def gemspec
|
|
14
14
|
@gemspec ||= eval(File.read(Gemspec_filename), binding, Gemspec_filename)
|
15
15
|
end
|
16
16
|
|
17
|
-
require '
|
17
|
+
require 'rubygems/package_task'
|
18
18
|
desc "Build the gem"
|
19
19
|
task :package=>:gem
|
20
20
|
task :gem=>:gemspec do
|
@@ -32,11 +32,11 @@ task :install => :gem do
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
desc 'Create the
|
35
|
+
desc 'Create the thread-frame shared library extension'
|
36
36
|
task :ext do
|
37
37
|
Dir.chdir('ext') do
|
38
38
|
system("#{Gem.ruby} extconf.rb && make")
|
39
|
-
end
|
39
|
+
end if '1.9.2' == RUBY_VERSION
|
40
40
|
end
|
41
41
|
|
42
42
|
desc 'Remove built files'
|
@@ -110,7 +110,7 @@ task :gemspec do
|
|
110
110
|
end
|
111
111
|
|
112
112
|
# --------- RDoc Documentation ------
|
113
|
-
require '
|
113
|
+
require 'rdoc/task'
|
114
114
|
desc "Generate rdoc documentation"
|
115
115
|
Rake::RDocTask.new("rdoc") do |rdoc|
|
116
116
|
rdoc.rdoc_dir = 'doc'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
/* Ruby 1.9.3 doesn't need a C extension while 1.9.2 does. I can't figure
|
2
|
+
out a way build a gem so that it loads the C extension conditionally.
|
3
|
+
So instead we have a simple stupid stub extension.
|
4
|
+
|
5
|
+
This as it is not elegant.
|
6
|
+
*/
|
7
|
+
|
8
|
+
#include <ruby.h>
|
9
|
+
#include <ruby/version.h>
|
10
|
+
#include <string.h>
|
11
|
+
void
|
12
|
+
Init_thread_frame(void)
|
13
|
+
{
|
14
|
+
if (0 == strncmp("1.9.2", ruby_version, sizeof("1.9.2")))
|
15
|
+
{
|
16
|
+
rb_raise(rb_eLoadError,
|
17
|
+
"Gem installed under Ruby 1.9.3 but this Ruby 1.9.2. Please reinstall 'rb-threadframe' gem under 1.9.2.");
|
18
|
+
} else if (0 == strncmp("1.9.3", ruby_version, sizeof("1.9.3"))) {
|
19
|
+
rb_raise(rb_eLoadError,
|
20
|
+
"Under Ruby 1.9.3 there is no reason to load this thread_frame C extension.");
|
21
|
+
}
|
22
|
+
}
|
data/ext/extconf.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'mkmf'
|
2
2
|
|
3
3
|
fail "You need to install a threadframe-patched Ruby.
|
4
4
|
See http://github.com/rocky/rb-threadframe/wiki/How-to-Install" unless
|
@@ -11,4 +11,4 @@ See http://github.com/rocky/rb-threadframe/wiki/How-to-Install" unless
|
|
11
11
|
config_file = File.join(File.dirname(__FILE__), 'config_options.rb')
|
12
12
|
load config_file if File.exist?(config_file)
|
13
13
|
|
14
|
-
create_makefile("thread_frame")
|
14
|
+
create_makefile("thread_frame", RUBY_VERSION)
|
data/ext/version.h
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
#define THREADFRAME_VERSION "0.40"
|
data/lib/iseq_extra.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'digest/sha1'
|
2
|
-
require_relative '../ext/thread_frame'
|
2
|
+
require_relative '../ext/thread_frame' if '1.9.2' == RUBY_VERSION
|
3
3
|
# Some additions to RubyVM::InstructionSequence
|
4
4
|
class RubyVM::InstructionSequence
|
5
5
|
|
@@ -141,8 +141,9 @@ end
|
|
141
141
|
|
142
142
|
if __FILE__ == $0
|
143
143
|
# Demo it.
|
144
|
-
iseq = RubyVM::
|
145
|
-
|
144
|
+
iseq = RubyVM::Frame.current.iseq
|
145
|
+
## FIXME
|
146
|
+
## puts iseq.format_args
|
146
147
|
puts iseq.disassemble
|
147
148
|
puts iseq.lineoffsets
|
148
149
|
puts iseq.sha1
|
@@ -150,7 +151,7 @@ if __FILE__ == $0
|
|
150
151
|
p iseq.line2offsets(__LINE__+100)
|
151
152
|
|
152
153
|
def show_type # :nodoc:
|
153
|
-
tf = RubyVM::
|
154
|
+
tf = RubyVM::Frame.current
|
154
155
|
while tf do
|
155
156
|
is = tf.iseq
|
156
157
|
if is
|
@@ -166,12 +167,12 @@ if __FILE__ == $0
|
|
166
167
|
|
167
168
|
line = __LINE__
|
168
169
|
def find_line(line) # :nodoc
|
169
|
-
tf = RubyVM::
|
170
|
+
tf = RubyVM::Frame.current
|
170
171
|
puts "find_line has lines: #{tf.iseq.lines}"
|
171
172
|
p tf.iseq.find_iseq_with_line(line)
|
172
173
|
end
|
173
174
|
|
174
|
-
tf = RubyVM::
|
175
|
+
tf = RubyVM::Frame.current
|
175
176
|
puts tf.iseq.disassemble
|
176
177
|
puts("offset %d above should be at line %d" %
|
177
178
|
[tf.iseq.locate_line(line), line])
|
data/lib/thread_frame.rb
CHANGED
@@ -1,3 +1,11 @@
|
|
1
1
|
# Boilerplate to pull shared object and helper routines.
|
2
|
-
|
2
|
+
if RUBY_VERSION == '1.9.2'
|
3
|
+
require_relative '../ext/thread_frame'
|
4
|
+
end
|
3
5
|
require_relative 'iseq_extra'
|
6
|
+
|
7
|
+
RubyVM::ThreadFrame = RubyVM::Frame
|
8
|
+
class Thread
|
9
|
+
# For compatibility with old stuff
|
10
|
+
alias tracing tracing?
|
11
|
+
end
|
data/test/unit/cfunc-use.rb
CHANGED
data/test/unit/test-argc.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
|
2
|
+
|
3
|
+
require_relative '../../ext/thread_frame' if '1.9.2' == RUBY_VERSION
|
3
4
|
|
4
5
|
class TestARGC < Test::Unit::TestCase
|
5
6
|
|
@@ -23,7 +24,7 @@ class TestARGC < Test::Unit::TestCase
|
|
23
24
|
all_events = []
|
24
25
|
eval <<-EOF.gsub(/^.*?: /, "")
|
25
26
|
1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
|
26
|
-
2: tf = RubyVM::
|
27
|
+
2: tf = RubyVM::Frame.prev
|
27
28
|
3: all_events << [tf.argc, tf.arity, tf.type, mid]
|
28
29
|
4: if :basename == mid
|
29
30
|
5: events << [tf.argc, tf.arity, tf.type, mid]
|
data/test/unit/test-binding.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
require_relative '../../ext/thread_frame'
|
2
|
+
require_relative '../../ext/thread_frame' if '1.9.2' == RUBY_VERSION
|
3
3
|
|
4
4
|
def outside(a)
|
5
|
-
return eval('a', RubyVM::
|
5
|
+
return eval('a', RubyVM::Frame.new(Thread::current).binding)
|
6
6
|
end
|
7
7
|
|
8
8
|
$a = 10
|
@@ -12,13 +12,13 @@ class TestBinding < Test::Unit::TestCase
|
|
12
12
|
a = 1
|
13
13
|
c = 0
|
14
14
|
assert_equal(5, outside(5))
|
15
|
-
tf = RubyVM::
|
15
|
+
tf = RubyVM::Frame.new(Thread::current)
|
16
16
|
b = tf.binding
|
17
17
|
assert_equal(1, eval('a', b))
|
18
18
|
assert_equal(10, eval('$a', b))
|
19
19
|
assert_equal(self, tf.self)
|
20
20
|
1.times do |i;a|
|
21
|
-
tf2 = Thread::current.
|
21
|
+
tf2 = Thread::current.frame()
|
22
22
|
b2 = tf2.binding
|
23
23
|
a = 2
|
24
24
|
assert_equal(2, eval('a', b2))
|
@@ -29,7 +29,7 @@ class TestBinding < Test::Unit::TestCase
|
|
29
29
|
assert_equal(1, eval('a', tf2.prev(2).binding))
|
30
30
|
end
|
31
31
|
def inner(a)
|
32
|
-
tf3 = Thread::current.
|
32
|
+
tf3 = Thread::current.frame()
|
33
33
|
b3 = tf3.binding
|
34
34
|
if a == 4
|
35
35
|
assert_equal(4, eval('a', b3))
|
@@ -1,17 +1,17 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
require_relative '../../ext/thread_frame'
|
2
|
+
require_relative '../../ext/thread_frame' if '1.9.2' == RUBY_VERSION
|
3
3
|
|
4
4
|
class TestThread < Test::Unit::TestCase
|
5
5
|
def test_basic
|
6
|
-
assert_equal(RubyVM::
|
7
|
-
RubyVM::
|
8
|
-
assert_equal(RubyVM::
|
6
|
+
assert_equal(RubyVM::Frame.new(Thread::current).thread,
|
7
|
+
RubyVM::Frame::current.thread)
|
8
|
+
assert_equal(RubyVM::Frame.new(Thread::current).thread,
|
9
9
|
Thread::current)
|
10
|
-
assert_equal(Thread::current.
|
10
|
+
assert_equal(Thread::current.frame.thread, Thread::current)
|
11
11
|
end
|
12
12
|
|
13
13
|
def test_pc_offset
|
14
|
-
tf = RubyVM::
|
14
|
+
tf = RubyVM::Frame::current
|
15
15
|
offset_1 = tf.pc_offset
|
16
16
|
assert_equal(true, offset_1 > 0,
|
17
17
|
"Expecting a positive integer pc offset, got %s" % offset_1)
|
@@ -24,11 +24,10 @@ class TestThread < Test::Unit::TestCase
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def test_sp
|
27
|
-
tf = RubyVM::
|
27
|
+
tf = RubyVM::Frame.prev
|
28
28
|
assert tf.sp(1)
|
29
29
|
tf.sp_set(1, 5)
|
30
|
-
assert_equal(5, tf.sp(1),
|
31
|
-
'chcking value of recently-set sp(1)')
|
30
|
+
assert_equal(5, tf.sp(1), 'checking value of recently-set sp(1)')
|
32
31
|
end
|
33
32
|
|
34
33
|
def test_source_container
|
@@ -38,7 +37,7 @@ class TestThread < Test::Unit::TestCase
|
|
38
37
|
assert_equal(['CFUNC', cfunc_filebase], [type, File.basename(loc, '.rb')],
|
39
38
|
'CFUNCs should get their file location from frame.prev*')
|
40
39
|
cont = 'file'
|
41
|
-
eval '1.times{cont = RubyVM::
|
40
|
+
eval '1.times{cont = RubyVM::Frame.current.source_container[0]}'
|
42
41
|
assert_equal('string', cont,
|
43
42
|
'source container[0] of an eval(...) should be "string"')
|
44
43
|
end
|
@@ -51,8 +50,9 @@ class TestThread < Test::Unit::TestCase
|
|
51
50
|
# because the location is reported on a traceback
|
52
51
|
# and that probably can't happen at PC 0.
|
53
52
|
def bug_when_zero_pc
|
53
|
+
skip "pc_offset= not implemented on 1.9.3" if '1.9.3' == RUBY_VERSION
|
54
54
|
@not_first = true
|
55
|
-
tf = RubyVM::
|
55
|
+
tf = RubyVM::Frame::current.prev
|
56
56
|
pc_save = tf.pc_offset
|
57
57
|
tf.pc_offset = 0
|
58
58
|
loc = tf.source_location
|
@@ -65,23 +65,23 @@ class TestThread < Test::Unit::TestCase
|
|
65
65
|
|
66
66
|
|
67
67
|
def test_thread_tracing
|
68
|
-
assert_equal(false, Thread.current.tracing)
|
68
|
+
assert_equal(false, Thread.current.tracing?)
|
69
69
|
Thread.current.tracing = true
|
70
|
-
assert_equal(true, Thread.current.tracing)
|
70
|
+
assert_equal(true, Thread.current.tracing?)
|
71
71
|
Thread.current.tracing = false
|
72
|
-
assert_equal(false, Thread.current.tracing)
|
72
|
+
assert_equal(false, Thread.current.tracing?)
|
73
73
|
end
|
74
74
|
|
75
75
|
def test_thread_exec_event_tracing
|
76
|
-
assert_equal(false, Thread.current.exec_event_tracing)
|
76
|
+
assert_equal(false, Thread.current.exec_event_tracing?)
|
77
77
|
Thread.current.exec_event_tracing = true
|
78
|
-
assert_equal(true, Thread.current.exec_event_tracing)
|
78
|
+
assert_equal(true, Thread.current.exec_event_tracing?)
|
79
79
|
Thread.current.exec_event_tracing = false
|
80
|
-
assert_equal(false, Thread.current.exec_event_tracing)
|
80
|
+
assert_equal(false, Thread.current.exec_event_tracing?)
|
81
81
|
end
|
82
82
|
|
83
83
|
def test_fields(notused=nil)
|
84
|
-
tf = RubyVM::
|
84
|
+
tf = RubyVM::Frame::current
|
85
85
|
pc1 = tf.pc_offset
|
86
86
|
assert(pc1 > 0, 'Should be able to get a valid PC offset')
|
87
87
|
# pc_offset is dynamic - it changes constantly
|
@@ -91,12 +91,13 @@ class TestThread < Test::Unit::TestCase
|
|
91
91
|
assert_equal(self, tf.self)
|
92
92
|
assert_equal(0, tf.arity)
|
93
93
|
assert_equal(0, tf.argc)
|
94
|
-
|
95
|
-
assert tf.
|
94
|
+
## FIXME: Should we allow this?
|
95
|
+
## assert tf.dfp(0)
|
96
|
+
## assert tf.lfp(0)
|
96
97
|
|
97
|
-
assert_raises IndexError do
|
98
|
-
|
99
|
-
end
|
98
|
+
# assert_raises IndexError do
|
99
|
+
# x = tf.lfp(tf.iseq.local_size+1)
|
100
|
+
# end
|
100
101
|
|
101
102
|
|
102
103
|
tf_prev = tf.prev
|
@@ -107,10 +108,10 @@ class TestThread < Test::Unit::TestCase
|
|
107
108
|
|
108
109
|
# 1.times creates a C frame.
|
109
110
|
1.times do
|
110
|
-
tf = RubyVM::
|
111
|
+
tf = RubyVM::Frame::current
|
111
112
|
tup = tf.source_container
|
112
113
|
tup[1] = File.basename(tup[1])
|
113
|
-
assert_equal(['file', 'test-
|
114
|
+
assert_equal(['file', 'test-frame.rb'], tup)
|
114
115
|
assert_equal('block in test_fields', tf.method)
|
115
116
|
assert_equal('CFUNC', tf.prev.type)
|
116
117
|
assert_equal('times', tf.prev.method)
|
@@ -124,24 +125,26 @@ class TestThread < Test::Unit::TestCase
|
|
124
125
|
|
125
126
|
# 1.upto also creates a C frame.
|
126
127
|
1.upto(1) do
|
127
|
-
|
128
|
+
# We could use ".prev" below instead of '.current.prev", but we
|
129
|
+
# may as well test current.prev.
|
130
|
+
tf = RubyVM::Frame::current.prev
|
128
131
|
assert_equal('CFUNC', tf.type)
|
129
132
|
assert_equal(1, tf.arity, 'C arity should work nowadays' )
|
130
133
|
assert_equal(1, tf.argc)
|
131
134
|
end
|
132
135
|
|
133
136
|
x = lambda do |x,y|
|
134
|
-
frame = RubyVM::
|
137
|
+
frame = RubyVM::Frame::current
|
135
138
|
assert_equal('block in test_fields', frame.method)
|
136
139
|
assert_equal('LAMBDA', frame.type)
|
137
140
|
assert_equal(x, tf.self)
|
138
|
-
|
139
|
-
|
141
|
+
assert_equal(2, frame.arity)
|
142
|
+
assert_equal(2, frame.argc)
|
140
143
|
end
|
141
144
|
x.call(x,2)
|
142
145
|
|
143
146
|
x = Proc.new do |x, y|
|
144
|
-
frame = RubyVM::
|
147
|
+
frame = RubyVM::Frame::current
|
145
148
|
assert_equal('block in test_fields', frame.method)
|
146
149
|
assert_equal(x, tf.self)
|
147
150
|
assert_equal('BLOCK', frame.type)
|
@@ -150,9 +153,9 @@ class TestThread < Test::Unit::TestCase
|
|
150
153
|
|
151
154
|
end
|
152
155
|
|
153
|
-
def
|
154
|
-
tf = RubyVM::
|
155
|
-
tf2 = RubyVM::
|
156
|
+
def test_frame_equal
|
157
|
+
tf = RubyVM::Frame.current
|
158
|
+
tf2 = RubyVM::Frame.current
|
156
159
|
assert_equal(true, tf.equal?(tf))
|
157
160
|
assert_equal(true, tf.equal?(tf2))
|
158
161
|
tf2 = tf2.prev
|
data/test/unit/test-invalid.rb
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
require_relative '../../ext/thread_frame'
|
3
2
|
|
4
|
-
|
3
|
+
require_relative '../../ext/thread_frame' if '1.9.2' == RUBY_VERSION
|
4
|
+
|
5
|
+
class TestInvalid < Test::Unit::TestCase
|
5
6
|
def test_basic
|
6
|
-
@tf = RubyVM::
|
7
|
+
@tf = RubyVM::Frame::current
|
7
8
|
assert_equal(false, @tf.invalid?,
|
8
|
-
'Frame should be valid right after
|
9
|
+
'Frame should be valid right after RubyVM::Frame::current')
|
9
10
|
def notgood(test_tf=nil)
|
10
11
|
# FIXME
|
11
12
|
# if test_tf
|
12
13
|
# assert_equal(test_tf != @tf, test_tf.invalid?)
|
13
14
|
# end
|
14
|
-
return RubyVM::
|
15
|
+
return RubyVM::Frame::current
|
15
16
|
end
|
16
17
|
|
17
18
|
def inner_fn(tf)
|
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
require_relative '../../ext/thread_frame'
|
2
|
+
require_relative '../../ext/thread_frame' if '1.9.2' == RUBY_VERSION
|
3
3
|
|
4
4
|
class TestISeqBrkpt < Test::Unit::TestCase
|
5
5
|
|
6
6
|
def test_iseq_brkpt
|
7
|
-
iseq = RubyVM::
|
7
|
+
iseq = RubyVM::Frame.current.iseq
|
8
8
|
assert iseq
|
9
9
|
assert_equal(nil, iseq.brkpts)
|
10
10
|
assert_equal(true, iseq.brkpt_alloc)
|
@@ -41,14 +41,14 @@ class TestISeqBrkpt < Test::Unit::TestCase
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def test_iseq_brkpt_set
|
44
|
-
|
44
|
+
set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
|
45
45
|
if 'brkpt' == event
|
46
46
|
$saw_brkpt = true
|
47
47
|
end
|
48
48
|
})
|
49
49
|
|
50
50
|
$saw_brkpt = false
|
51
|
-
tf = RubyVM::
|
51
|
+
tf = RubyVM::Frame.current
|
52
52
|
tf.iseq.offsetlines.keys.each do |offset|
|
53
53
|
tf.iseq.brkpt_set(offset)
|
54
54
|
end
|
data/test/unit/test-iseq-save.rb
CHANGED