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
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
|
+
}
|