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
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.org>
|
2
|
+
All rights reserved.
|
3
|
+
*
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions
|
6
|
+
are met:
|
7
|
+
1. Redistributions of source code must retain the above copyright
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer in the
|
11
|
+
documentation and/or other materials provided with the distribution.
|
12
|
+
*
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
14
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
15
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
16
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
17
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
18
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
19
|
+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
20
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
21
|
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
22
|
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
23
|
+
SUCH DAMAGE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
# -*- Ruby -*-
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
require 'rake/rdoctask'
|
7
|
+
require 'rake/testtask'
|
8
|
+
|
9
|
+
rake_dir = File.dirname(__FILE__)
|
10
|
+
|
11
|
+
require 'rbconfig'
|
12
|
+
RUBY_PATH = File.join(RbConfig::CONFIG['bindir'],
|
13
|
+
RbConfig::CONFIG['RUBY_INSTALL_NAME'])
|
14
|
+
|
15
|
+
SO_NAME = 'thread_frame.so'
|
16
|
+
|
17
|
+
PACKAGE_VERSION = open("ext/thread_frame.c") do |f|
|
18
|
+
f.grep(/^#define THREADFRAME_VERSION/).first[/"(.+)"/,1]
|
19
|
+
end
|
20
|
+
|
21
|
+
EXT_FILES = FileList[%w(ext/*.c ext/*.h)]
|
22
|
+
INCLUDE_FILES = FileList['include/*.h']
|
23
|
+
LIB_FILES = FileList['lib/*.rb']
|
24
|
+
TEST_FILES = FileList['test/**/*.rb']
|
25
|
+
COMMON_FILES = FileList[%w(README.md Rakefile LICENSE NEWS)]
|
26
|
+
ALL_FILES = COMMON_FILES + INCLUDE_FILES + LIB_FILES + EXT_FILES +
|
27
|
+
TEST_FILES
|
28
|
+
|
29
|
+
desc 'Create the core thread-frame shared library extension'
|
30
|
+
task :ext do
|
31
|
+
Dir.chdir('ext') do
|
32
|
+
system("#{Gem.ruby} extconf.rb && make")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'Remove built files'
|
37
|
+
task :clean do
|
38
|
+
cd 'ext' do
|
39
|
+
if File.exist?('Makefile')
|
40
|
+
sh 'make clean'
|
41
|
+
rm 'Makefile'
|
42
|
+
end
|
43
|
+
derived_files = Dir.glob('.o') + Dir.glob('*.so')
|
44
|
+
rm derived_files unless derived_files.empty?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def run_standalone_ruby_file(directory)
|
49
|
+
puts ('*' * 10) + ' ' + directory + ' ' + ('*' * 10)
|
50
|
+
Dir.chdir(directory) do
|
51
|
+
Dir.glob('*.rb').each do |ruby_file|
|
52
|
+
puts ('-' * 20) + ' ' + ruby_file + ' ' + ('-' * 20)
|
53
|
+
system(RUBY_PATH, ruby_file)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
desc "Create a GNU-style ChangeLog via git2cl"
|
59
|
+
task :ChangeLog do
|
60
|
+
system("git log --pretty --numstat --summary | git2cl > ChangeLog")
|
61
|
+
end
|
62
|
+
|
63
|
+
task :default => [:test]
|
64
|
+
|
65
|
+
desc 'Test units - the smaller tests'
|
66
|
+
task :'test:unit' => [:ext] do |t|
|
67
|
+
Rake::TestTask.new(:'test:unit') do |t|
|
68
|
+
t.libs << './ext'
|
69
|
+
t.test_files = FileList['test/unit/**/*.rb']
|
70
|
+
# t.pattern = 'test/**/*test-*.rb' # instead of above
|
71
|
+
t.verbose = true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
desc 'Test everything - unit tests for now.'
|
76
|
+
task :test do
|
77
|
+
exceptions = ['test:unit'].collect do |task|
|
78
|
+
begin
|
79
|
+
Rake::Task[task].invoke
|
80
|
+
nil
|
81
|
+
rescue => e
|
82
|
+
e
|
83
|
+
end
|
84
|
+
end.compact
|
85
|
+
|
86
|
+
exceptions.each {|e| puts e;puts e.backtrace }
|
87
|
+
raise "Test failures" unless exceptions.empty?
|
88
|
+
end
|
89
|
+
|
90
|
+
desc "Test everything - same as test."
|
91
|
+
|
92
|
+
task :'check' do
|
93
|
+
run_standalone_ruby_file(File.join(%W(#{rake_dir} test unit)))
|
94
|
+
end
|
95
|
+
|
96
|
+
# --------- RDoc Documentation ------
|
97
|
+
desc 'Generate rdoc documentation'
|
98
|
+
Rake::RDocTask.new('rdoc') do |rdoc|
|
99
|
+
rdoc.rdoc_dir = 'doc/rdoc'
|
100
|
+
rdoc.title = 'rb-threadframe'
|
101
|
+
# Show source inline with line numbers
|
102
|
+
rdoc.options << '--inline-source' << '--line-numbers'
|
103
|
+
# Make the readme file the start page for the generated html
|
104
|
+
rdoc.options << '--main' << 'README.md'
|
105
|
+
rdoc.rdoc_files.include('ext/**/*.c',
|
106
|
+
'README.md')
|
107
|
+
end
|
108
|
+
|
109
|
+
# Base GEM Specification
|
110
|
+
spec = Gem::Specification.new do |spec|
|
111
|
+
spec.name = "rb-threadframe"
|
112
|
+
|
113
|
+
spec.homepage = "http://github.com/rocky/rb-threadframe/tree/master"
|
114
|
+
spec.summary = "Frame introspection"
|
115
|
+
spec.description = <<-EOF
|
116
|
+
|
117
|
+
rb-threadframe gives introspection access for frames of a thread.
|
118
|
+
EOF
|
119
|
+
|
120
|
+
spec.version = PACKAGE_VERSION
|
121
|
+
spec.require_path = 'lib'
|
122
|
+
spec.extensions = ["ext/extconf.rb"]
|
123
|
+
|
124
|
+
spec.author = "R. Bernstein"
|
125
|
+
spec.email = "rocky@gnu.org"
|
126
|
+
spec.platform = Gem::Platform::RUBY
|
127
|
+
spec.files = ALL_FILES.to_a
|
128
|
+
|
129
|
+
spec.required_ruby_version = '>= 1.9.2'
|
130
|
+
spec.date = Time.now
|
131
|
+
# spec.rubyforge_project = 'rocky-hacks'
|
132
|
+
|
133
|
+
# rdoc
|
134
|
+
spec.has_rdoc = true
|
135
|
+
spec.extra_rdoc_files = ['README.md', 'threadframe.rd'] +
|
136
|
+
FileList['ext/*.c']
|
137
|
+
end
|
138
|
+
|
139
|
+
# Rake task to build the default package
|
140
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
141
|
+
pkg.need_tar = true
|
142
|
+
end
|
143
|
+
|
144
|
+
def install(spec, *opts)
|
145
|
+
args = ['gem', 'install', "pkg/#{spec.name}-#{spec.version}.gem"] + opts
|
146
|
+
system(*args)
|
147
|
+
end
|
148
|
+
|
149
|
+
desc 'Install locally'
|
150
|
+
task :install => :package do
|
151
|
+
Dir.chdir(File::dirname(__FILE__)) do
|
152
|
+
# ri and rdoc take lots of time
|
153
|
+
install(spec, '--no-ri', '--no-rdoc')
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
task :install_full => :package do
|
158
|
+
Dir.chdir(File::dirname(__FILE__)) do
|
159
|
+
install(spec)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
data/ext/extconf.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "mkmf"
|
2
|
+
|
3
|
+
fail "You need to install a threadframe-patched Ruby.
|
4
|
+
See http://github.com/rocky/rb-threadframe/wiki/How-to-Install" unless
|
5
|
+
RbConfig::CONFIG.member?('rb-threadframe')
|
6
|
+
|
7
|
+
config_file = File.join(File.dirname(__FILE__), 'config_options')
|
8
|
+
load config_file if File.exist?(config_file)
|
9
|
+
|
10
|
+
# Temporary: to turn off optimization
|
11
|
+
# $CFLAGS='-fno-strict-aliasing -g -fPIC'
|
12
|
+
create_makefile("thread_frame")
|
data/ext/iseq_extra.c
ADDED
@@ -0,0 +1,404 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (C) 2010 Rocky Bernstein
|
3
|
+
*/
|
4
|
+
#if 0 /* The following is to fake out rdoc, until I find a better fix. */
|
5
|
+
/*
|
6
|
+
* Additions to the RubyVM::InstructionSequence class
|
7
|
+
*/
|
8
|
+
VALUE rb_cIseq = rb_define_class_under(rb_cRubyVM, "InstructionSequence", ...)
|
9
|
+
#endif
|
10
|
+
|
11
|
+
#include "../include/vm_core_mini.h" /* Pulls in ruby.h and node.h */
|
12
|
+
#ifdef HAVE_COMPILE_OPTIONS
|
13
|
+
#endif
|
14
|
+
#include "iseq_mini.h" /* Pulls in ruby.h */
|
15
|
+
#include "../include/ruby19_externs.h"
|
16
|
+
#include <string.h> /* For strlen() */
|
17
|
+
|
18
|
+
struct iseq_insn_info_entry {
|
19
|
+
unsigned short position;
|
20
|
+
unsigned short line_no;
|
21
|
+
unsigned short sp;
|
22
|
+
};
|
23
|
+
|
24
|
+
#ifdef HAVE_COMPILE_OPTIONS
|
25
|
+
/*
|
26
|
+
* Document-method: RubyVM::InstructionSequence::compile_options
|
27
|
+
*
|
28
|
+
* call-seq:
|
29
|
+
* RubyVM::InstructionSequence#compile_options -> Hash
|
30
|
+
*
|
31
|
+
* Returns a hash of the compiler options used to create the
|
32
|
+
* instruction sequence.
|
33
|
+
*/
|
34
|
+
VALUE
|
35
|
+
iseq_compile_options(VALUE iseqval)
|
36
|
+
{
|
37
|
+
rb_iseq_t *iseq;
|
38
|
+
if (Qnil == iseqval) return Qnil;
|
39
|
+
else {
|
40
|
+
VALUE hash_opts = rb_hash_new();
|
41
|
+
rb_compile_option_t *compile_opts;
|
42
|
+
GetISeqPtr(iseqval, iseq);
|
43
|
+
if (!iseq->compile_data) return Qnil;
|
44
|
+
compile_opts = iseq->compile_data->option;
|
45
|
+
rb_hash_aset(hash_opts, rb_str_new2("inline_const_cache"),
|
46
|
+
(compile_opts->inline_const_cache) ? Qtrue : Qfalse);
|
47
|
+
return hash_opts;
|
48
|
+
}
|
49
|
+
|
50
|
+
}
|
51
|
+
#endif
|
52
|
+
|
53
|
+
/*
|
54
|
+
* Document-method: RubyVM::InstructionSequence::encoded
|
55
|
+
*
|
56
|
+
* call-seq:
|
57
|
+
* RubyVM::InstructionSequence#iseq_encoded -> String
|
58
|
+
*
|
59
|
+
* Returns a string of the encoded bytes of the instruction
|
60
|
+
* sequence. Note that this is probably not usable as is, may be useful in
|
61
|
+
* decoding instructions (using other info) or for getting a sha1
|
62
|
+
* checksum.
|
63
|
+
*/
|
64
|
+
VALUE
|
65
|
+
iseq_iseq_encoded(VALUE iseqval)
|
66
|
+
{
|
67
|
+
rb_iseq_t *iseq;
|
68
|
+
GetISeqPtr(iseqval, iseq);
|
69
|
+
return rb_str_new((char *) iseq->iseq_encoded, iseq->iseq_size);
|
70
|
+
}
|
71
|
+
|
72
|
+
|
73
|
+
/*
|
74
|
+
* Document-method: RubyVM::InstructionSequence::equal?
|
75
|
+
*
|
76
|
+
* call-seq:
|
77
|
+
* RubyVM::InstructionSequence#equal?(iseq2) -> bool
|
78
|
+
*
|
79
|
+
* Returns true if the instruction sequences are equal.
|
80
|
+
*/
|
81
|
+
VALUE
|
82
|
+
iseq_equal(VALUE iseqval1, VALUE iseqval2)
|
83
|
+
{
|
84
|
+
rb_iseq_t *iseq1, *iseq2;
|
85
|
+
|
86
|
+
if (Qnil == iseqval2) return Qfalse;
|
87
|
+
if (!rb_obj_is_kind_of(iseqval2, rb_cISeq)) {
|
88
|
+
rb_raise(rb_eTypeError,
|
89
|
+
"comparison argument must be an instance of %s or nil (is %s)",
|
90
|
+
rb_obj_classname(iseqval1), rb_obj_classname(iseqval2));
|
91
|
+
}
|
92
|
+
|
93
|
+
if (iseqval1 == iseqval2) return Qtrue;
|
94
|
+
GetISeqPtr(iseqval1, iseq1);
|
95
|
+
GetISeqPtr(iseqval2, iseq2);
|
96
|
+
|
97
|
+
/* FIXME: the count 28 below is bogus. I think this should be the fields
|
98
|
+
from "type" to "mark_ary". Should also include iseq->encoded.
|
99
|
+
*/
|
100
|
+
if (0 == memcmp(iseq1, iseq2, 28))
|
101
|
+
return Qtrue;
|
102
|
+
else
|
103
|
+
return Qfalse;
|
104
|
+
}
|
105
|
+
|
106
|
+
|
107
|
+
/*
|
108
|
+
* call-seq:
|
109
|
+
* RubyVM::InstructionSequence#local_name(i) - String
|
110
|
+
*
|
111
|
+
* Returns the string name of local variable in i'th position
|
112
|
+
* of the instruction sequence local table, or nil if i is
|
113
|
+
* out of range.
|
114
|
+
*/
|
115
|
+
VALUE
|
116
|
+
iseq_local_name(VALUE iseqval, VALUE val)
|
117
|
+
{
|
118
|
+
rb_iseq_t *iseq;
|
119
|
+
if (FIXNUM_P(val)) {
|
120
|
+
long int i = FIX2INT(val);
|
121
|
+
long int size;
|
122
|
+
|
123
|
+
GetISeqPtr(iseqval, iseq);
|
124
|
+
|
125
|
+
size = iseq->local_table_size;
|
126
|
+
|
127
|
+
if (i < 0) i = size + i;
|
128
|
+
|
129
|
+
if (i >= size)
|
130
|
+
rb_raise(rb_eIndexError,
|
131
|
+
"local table index %ld should be in the range -%ld .. %ld",
|
132
|
+
i, size, size-1);
|
133
|
+
|
134
|
+
return rb_str_new2(rb_id2name(iseq->local_table[i]));
|
135
|
+
} else {
|
136
|
+
rb_raise(rb_eTypeError, "type mismatch: %s given, Fixnum expected",
|
137
|
+
rb_class2name(CLASS_OF(val)));
|
138
|
+
}
|
139
|
+
/* not reached. */
|
140
|
+
return Qnil;
|
141
|
+
}
|
142
|
+
|
143
|
+
/*
|
144
|
+
* call-seq:
|
145
|
+
* RubyVM::InstructionSequence#name -> String
|
146
|
+
*
|
147
|
+
* Returns the name if the instruction sequence.
|
148
|
+
*/
|
149
|
+
VALUE
|
150
|
+
iseq_name(VALUE iseqval)
|
151
|
+
{
|
152
|
+
rb_iseq_t *iseq;
|
153
|
+
GetISeqPtr(iseqval, iseq);
|
154
|
+
return(iseq->name);
|
155
|
+
}
|
156
|
+
|
157
|
+
/*
|
158
|
+
* call-seq:
|
159
|
+
* RubyVM::InstructionSequence#offsetlines -> Hash[Fixnum] -> [Fixnum]
|
160
|
+
*
|
161
|
+
* Returns an hash. The keys in the hash form the VM offsets of the
|
162
|
+
* instructions. The value of the hash for a given offset is a list
|
163
|
+
* of line numbers associated with that offset.
|
164
|
+
*/
|
165
|
+
VALUE iseq_offsetlines(VALUE iseqval)
|
166
|
+
{
|
167
|
+
rb_iseq_t *iseq;
|
168
|
+
VALUE offsetlines = rb_hash_new();
|
169
|
+
unsigned long i, size;
|
170
|
+
struct iseq_insn_info_entry *table;
|
171
|
+
|
172
|
+
GetISeqPtr(iseqval, iseq);
|
173
|
+
|
174
|
+
size = iseq->insn_info_size;
|
175
|
+
table = iseq->insn_info_table;
|
176
|
+
|
177
|
+
for (i = 0; i < size; i++) {
|
178
|
+
VALUE ary = rb_ary_new2(1);
|
179
|
+
rb_ary_push(ary, INT2FIX(table[i].line_no));
|
180
|
+
rb_hash_aset(offsetlines, INT2FIX(table[i].position), ary);
|
181
|
+
}
|
182
|
+
return offsetlines;
|
183
|
+
}
|
184
|
+
|
185
|
+
/*
|
186
|
+
* call-seq:
|
187
|
+
* RubyVM::InstructionSequence#offset2lines(offset) -> [Fixnum]
|
188
|
+
*
|
189
|
+
* Returns an Array or nil. If offset is found then return the list of
|
190
|
+
* lines associated with that offset. If the offset isn't found return nil.
|
191
|
+
*/
|
192
|
+
VALUE iseq_offset2lines(VALUE iseqval, VALUE offsetval)
|
193
|
+
{
|
194
|
+
rb_iseq_t *iseq;
|
195
|
+
|
196
|
+
GetISeqPtr(iseqval, iseq);
|
197
|
+
|
198
|
+
if (FIXNUM_P(offsetval)) {
|
199
|
+
unsigned long i, size;
|
200
|
+
int offset = FIX2INT(offsetval);
|
201
|
+
struct iseq_insn_info_entry *table;
|
202
|
+
|
203
|
+
size = iseq->insn_info_size;
|
204
|
+
table = iseq->insn_info_table;
|
205
|
+
|
206
|
+
for (i = 0; i < size; i++) {
|
207
|
+
if (table[i].position == offset) {
|
208
|
+
VALUE ary = rb_ary_new2(1);
|
209
|
+
rb_ary_push(ary, INT2FIX(table[i].line_no));
|
210
|
+
return ary;
|
211
|
+
}
|
212
|
+
}
|
213
|
+
}
|
214
|
+
return Qnil;
|
215
|
+
}
|
216
|
+
|
217
|
+
/* FIXME: should return array of destroyed entries */
|
218
|
+
VALUE iseq_killcache(VALUE iseqval)
|
219
|
+
{
|
220
|
+
rb_iseq_t *iseqdat;
|
221
|
+
VALUE *iseq ;
|
222
|
+
unsigned long i, size, count = 0;
|
223
|
+
struct iseq_insn_info_entry *table;
|
224
|
+
|
225
|
+
GetISeqPtr(iseqval, iseqdat);
|
226
|
+
iseq = iseqdat->iseq;
|
227
|
+
size = iseqdat->insn_info_size;
|
228
|
+
table = iseqdat->insn_info_table;
|
229
|
+
for (i = 0; i < size; i++) {
|
230
|
+
const unsigned long pos = table[i].position;
|
231
|
+
const VALUE insn = iseq[pos];
|
232
|
+
if (0 == strncmp(insn_name(insn), "getinlinecache",
|
233
|
+
sizeof("getinlinecache")))
|
234
|
+
{
|
235
|
+
/* printf("pos: %lu\n", pos); */
|
236
|
+
count ++;
|
237
|
+
iseq[pos] = 0;
|
238
|
+
iseq[pos+1] = 0;
|
239
|
+
iseq[pos+2] = 0;
|
240
|
+
}
|
241
|
+
}
|
242
|
+
return INT2FIX(count);
|
243
|
+
}
|
244
|
+
|
245
|
+
const char *
|
246
|
+
source_container_type(VALUE fileval)
|
247
|
+
{
|
248
|
+
const char *filename = RSTRING_PTR(fileval);
|
249
|
+
size_t len = strlen(filename);
|
250
|
+
|
251
|
+
/* FIXME: Looking for (...) is a hack that I would love to know how
|
252
|
+
to remove. Probably Ruby has to be changed to record this kind
|
253
|
+
of information.
|
254
|
+
*/
|
255
|
+
if (len > 0 &&
|
256
|
+
((filename[0] == '(' && filename[len-1] == ')')
|
257
|
+
|| 0 == strncmp(filename, "<compiled>",
|
258
|
+
sizeof("<compiled>"))))
|
259
|
+
return "string";
|
260
|
+
else
|
261
|
+
return "file";
|
262
|
+
}
|
263
|
+
|
264
|
+
|
265
|
+
VALUE
|
266
|
+
iseq_source_container_internal(rb_iseq_t *iseq)
|
267
|
+
{
|
268
|
+
VALUE fileval = iseq->filename;
|
269
|
+
const char *contain_type = source_container_type(fileval);
|
270
|
+
|
271
|
+
return rb_ary_new3(2, rb_str_new2(contain_type), fileval);
|
272
|
+
}
|
273
|
+
|
274
|
+
/*
|
275
|
+
* call-seq:
|
276
|
+
* RubyVM::InstructionSequence#source_container() -> [Type, String]
|
277
|
+
*
|
278
|
+
* Returns a tuple representing kind of container, e.g. file
|
279
|
+
* eval'd string object, and the name of the container. If file,
|
280
|
+
* it would be a file name. If an eval'd string it might be the string.
|
281
|
+
*/
|
282
|
+
static VALUE
|
283
|
+
iseq_source_container(VALUE iseqval)
|
284
|
+
{
|
285
|
+
rb_iseq_t *iseq;
|
286
|
+
|
287
|
+
if (Qnil == iseqval) return Qnil;
|
288
|
+
GetISeqPtr(iseqval, iseq);
|
289
|
+
return iseq_source_container_internal(iseq);
|
290
|
+
}
|
291
|
+
|
292
|
+
|
293
|
+
#if 0
|
294
|
+
/*
|
295
|
+
* call-seq:
|
296
|
+
* RubyVM::InstructionSequence#type() -> Fixnum
|
297
|
+
*
|
298
|
+
* Returns instruction-sequence type.
|
299
|
+
*/
|
300
|
+
static VALUE
|
301
|
+
iseq_type(VALUE iseqval)
|
302
|
+
#endif
|
303
|
+
|
304
|
+
#define ISEQ_FIELD_METHOD(FIELD) \
|
305
|
+
static VALUE \
|
306
|
+
iseq_##FIELD(VALUE iseqval) \
|
307
|
+
{ \
|
308
|
+
rb_iseq_t *iseq; \
|
309
|
+
if (Qnil == iseqval) return Qnil; \
|
310
|
+
GetISeqPtr(iseqval, iseq); \
|
311
|
+
return iseq->FIELD; \
|
312
|
+
}
|
313
|
+
|
314
|
+
ISEQ_FIELD_METHOD(orig) ;
|
315
|
+
ISEQ_FIELD_METHOD(self) ;
|
316
|
+
ISEQ_FIELD_METHOD(type) ;
|
317
|
+
|
318
|
+
#define ISEQ_INT_FIELD_METHOD(FIELD) \
|
319
|
+
extern VALUE \
|
320
|
+
iseq_##FIELD(VALUE iseqval) \
|
321
|
+
{ \
|
322
|
+
rb_iseq_t *iseq; \
|
323
|
+
GetISeqPtr(iseqval, iseq); \
|
324
|
+
return INT2FIX(iseq->FIELD); \
|
325
|
+
}
|
326
|
+
|
327
|
+
ISEQ_INT_FIELD_METHOD(arg_block) ;
|
328
|
+
ISEQ_INT_FIELD_METHOD(arg_opts) ;
|
329
|
+
ISEQ_INT_FIELD_METHOD(arg_post_len) ;
|
330
|
+
ISEQ_INT_FIELD_METHOD(arg_rest) ;
|
331
|
+
ISEQ_INT_FIELD_METHOD(arg_simple) ;
|
332
|
+
ISEQ_INT_FIELD_METHOD(argc) ;
|
333
|
+
ISEQ_INT_FIELD_METHOD(iseq_size) ;
|
334
|
+
ISEQ_INT_FIELD_METHOD(klass) ;
|
335
|
+
ISEQ_INT_FIELD_METHOD(line_no) ;
|
336
|
+
ISEQ_INT_FIELD_METHOD(local_size) ;
|
337
|
+
ISEQ_INT_FIELD_METHOD(local_table_size) ;
|
338
|
+
|
339
|
+
/*
|
340
|
+
* call-seq:
|
341
|
+
* RubyVM::InstructionSequence#line_range() -> Range
|
342
|
+
*
|
343
|
+
* Returns a range containing the starting line number and the
|
344
|
+
* ending line of the source code for the instruction-sequence.
|
345
|
+
*/
|
346
|
+
static VALUE
|
347
|
+
iseq_line_range(VALUE iseqval)
|
348
|
+
{
|
349
|
+
rb_iseq_t *iseq;
|
350
|
+
|
351
|
+
GetISeqPtr(iseqval, iseq);
|
352
|
+
if (Qnil == iseqval) return Qnil;
|
353
|
+
else {
|
354
|
+
unsigned long i, size = iseq->insn_info_size;
|
355
|
+
struct iseq_insn_info_entry *table = iseq->insn_info_table;
|
356
|
+
unsigned short min_line = table[0].line_no;
|
357
|
+
unsigned short max_line = table[0].line_no;
|
358
|
+
|
359
|
+
for (i = 0; i < size; i++) {
|
360
|
+
if (table[i].line_no < min_line)
|
361
|
+
min_line = table[i].line_no;
|
362
|
+
else if (table[i].line_no > max_line)
|
363
|
+
max_line = table[i].line_no;
|
364
|
+
}
|
365
|
+
return rb_range_new(INT2FIX(min_line), INT2FIX(max_line), 0);
|
366
|
+
}
|
367
|
+
}
|
368
|
+
|
369
|
+
|
370
|
+
/* RDoc can't find methods when we use a definition like this: */
|
371
|
+
#define RB_DEFINE_ISEQ_METHOD(FIELD, ARGC) \
|
372
|
+
rb_define_method(rb_cISeq, #FIELD, iseq_##FIELD, ARGC);
|
373
|
+
|
374
|
+
void
|
375
|
+
Init_iseq_extra(void)
|
376
|
+
{
|
377
|
+
rb_define_method(rb_cISeq, "arg_block", iseq_arg_block, 0) ;
|
378
|
+
rb_define_method(rb_cISeq, "arg_opts", iseq_arg_opts, 0) ;
|
379
|
+
rb_define_method(rb_cISeq, "arg_post_len", iseq_arg_post_len, 0) ;
|
380
|
+
rb_define_method(rb_cISeq, "arg_rest", iseq_arg_rest, 0) ;
|
381
|
+
rb_define_method(rb_cISeq, "arg_simple", iseq_arg_simple, 0) ;
|
382
|
+
rb_define_method(rb_cISeq, "argc", iseq_argc, 0) ;
|
383
|
+
#ifdef HAVE_COMPILE_OPTIONS
|
384
|
+
rb_define_method(rb_cISeq, "compile_options", iseq_compile_options, 0) ;
|
385
|
+
#endif
|
386
|
+
rb_define_method(rb_cISeq, "equal?", iseq_equal, 1) ;
|
387
|
+
rb_define_method(rb_cISeq, "encoded", iseq_iseq_encoded, 0) ;
|
388
|
+
rb_define_method(rb_cISeq, "iseq_size", iseq_iseq_size, 0) ;
|
389
|
+
rb_define_method(rb_cISeq, "killcache", iseq_killcache, 0) ;
|
390
|
+
rb_define_method(rb_cISeq, "klass", iseq_klass, 0) ;
|
391
|
+
rb_define_method(rb_cISeq, "lineno", iseq_line_no, 0) ;
|
392
|
+
rb_define_method(rb_cISeq, "line_range", iseq_line_range, 0) ;
|
393
|
+
rb_define_method(rb_cISeq, "local_name", iseq_local_name, 1) ;
|
394
|
+
rb_define_method(rb_cISeq, "local_size", iseq_local_size, 0) ;
|
395
|
+
rb_define_method(rb_cISeq, "local_table_size", iseq_local_table_size, 0) ;
|
396
|
+
rb_define_method(rb_cISeq, "offset2lines", iseq_offset2lines, 1) ;
|
397
|
+
rb_define_method(rb_cISeq, "offsetlines", iseq_offsetlines, 0) ;
|
398
|
+
rb_define_method(rb_cISeq, "orig", iseq_orig, 0) ;
|
399
|
+
rb_define_method(rb_cISeq, "name", iseq_name, 0) ;
|
400
|
+
rb_define_method(rb_cISeq, "self", iseq_self, 0) ;
|
401
|
+
rb_define_method(rb_cISeq, "source_container", iseq_source_container, 0) ;
|
402
|
+
rb_define_method(rb_cISeq, "type", iseq_type, 0) ;
|
403
|
+
|
404
|
+
}
|