sender 1.3 → 1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +6 -0
- data/Makefile +1 -1
- data/Manifest.txt +2 -0
- data/README.rdoc +61 -16
- data/Rakefile +1 -1
- data/VERSION.rdoc +1 -0
- data/ext/sender/RPSender_internal.c +3 -32
- data/ext/sender/RPSender_internal.h +1 -1
- data/ext/sender/RubySourceSupport.c +35 -0
- data/ext/sender/RubySourceSupport.h +1 -0
- data/ext/sender/rb_Global.c +43 -12
- data/ext/sender/rb_Global_internal.h +1 -0
- data/ext/sender/rb_Kernel.c +144 -46
- data/ext/sender/rb_Kernel.h +5 -3
- data/ext/sender/rb_Kernel_internal.h +10 -0
- data/lib/sender/sender.bundle +0 -0
- data/lib/sender.rb +1 -28
- data/sender.gemspec +4 -4
- data/test/test_sender.rb +8 -0
- metadata +7 -4
data/CHANGELOG.rdoc
CHANGED
@@ -26,3 +26,9 @@ in the long term, I am investing what needs to be done for the special case.
|
|
26
26
|
|
27
27
|
Added init_sender_callbacks( sender, caller ) to take care of :initialize issue. Include Sender module in your class to activate and
|
28
28
|
:init_sender_callbacks( __sender__, __caller__ ) will be called on self from self.class.new before self.initialize.
|
29
|
+
|
30
|
+
=== 1.4 2010-06-29
|
31
|
+
|
32
|
+
Removed init_sender_callbacks that were added in 1.3.
|
33
|
+
New implementation of backtrace- now works for :initialize.
|
34
|
+
__sender__ and __caller__ now work for :initialize and return the object and method that called :new.
|
data/Makefile
CHANGED
@@ -53,7 +53,7 @@ COUTFLAG = -o
|
|
53
53
|
RUBY_EXTCONF_H =
|
54
54
|
cflags = $(optflags) $(debugflags) $(warnflags)
|
55
55
|
optflags =
|
56
|
-
debugflags = -
|
56
|
+
debugflags = -gdwarf-2 -g3
|
57
57
|
warnflags = -Wall -Wno-parentheses
|
58
58
|
CFLAGS = -fno-common $(cflags) -fno-common -pipe -fno-common
|
59
59
|
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir) -I/usr/src/ruby
|
data/Manifest.txt
CHANGED
@@ -4,6 +4,7 @@ Makefile
|
|
4
4
|
Manifest.txt
|
5
5
|
README.rdoc
|
6
6
|
Rakefile
|
7
|
+
VERSION.rdoc
|
7
8
|
ext/sender/RPSender_internal.c
|
8
9
|
ext/sender/RPSender_internal.h
|
9
10
|
ext/sender/RubySourceSupport.c
|
@@ -24,6 +25,7 @@ ext/sender/rb_Global.h
|
|
24
25
|
ext/sender/rb_Global_internal.h
|
25
26
|
ext/sender/rb_Kernel.c
|
26
27
|
ext/sender/rb_Kernel.h
|
28
|
+
ext/sender/rb_Kernel_internal.h
|
27
29
|
ext/sender/regenc.h
|
28
30
|
ext/sender/regint.h
|
29
31
|
ext/sender/regparse.h
|
data/README.rdoc
CHANGED
@@ -28,28 +28,42 @@ which allows contents of the backtrace to be queried.
|
|
28
28
|
|
29
29
|
== EXAMPLE:
|
30
30
|
|
31
|
-
require 'sender'
|
32
|
-
|
33
31
|
require '../sender/lib/sender/sender'
|
34
32
|
|
35
33
|
require 'pp'
|
36
34
|
|
37
35
|
class Test
|
38
|
-
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
puts 'In method <Test>:initialize'
|
39
|
+
puts 'Sender was: ' + __sender__.pretty_inspect.to_s
|
40
|
+
puts 'Caller was: ' + __caller__.to_s
|
41
|
+
end
|
42
|
+
|
39
43
|
def test
|
44
|
+
puts 'In <Test>:test'
|
40
45
|
self.another_test
|
41
46
|
end
|
42
|
-
|
47
|
+
|
43
48
|
def another_test
|
49
|
+
puts 'In method <Test>:another_test'
|
44
50
|
test2 = Test2.new
|
45
51
|
test2.and_another_test_in_another_object
|
46
52
|
end
|
47
|
-
|
53
|
+
|
48
54
|
end
|
49
55
|
|
50
56
|
class Test2
|
51
|
-
|
57
|
+
|
58
|
+
def initialize
|
59
|
+
puts 'In method <Test2>:initialize'
|
60
|
+
puts 'Sender was: ' + __sender__.pretty_inspect.to_s
|
61
|
+
puts 'Caller was: ' + __caller__.to_s
|
62
|
+
end
|
63
|
+
|
52
64
|
def and_another_test_in_another_object
|
65
|
+
puts 'In method <Test2>:and_another_test_in_another_object'
|
66
|
+
pp self
|
53
67
|
puts 'Sender was: ' + __sender__.pretty_inspect.to_s
|
54
68
|
puts 'Caller was: ' + __caller__.to_s
|
55
69
|
pp Kernel.backtrace
|
@@ -60,26 +74,56 @@ which allows contents of the backtrace to be queried.
|
|
60
74
|
pp Kernel.backtrace_includes?( $test )
|
61
75
|
pp Kernel.backtrace_includes?( :another_test, Test, $test )
|
62
76
|
puts 'These should be false:'
|
63
|
-
pp Kernel.backtrace_includes?( :
|
77
|
+
pp Kernel.backtrace_includes?( :yet_another_test )
|
64
78
|
pp Kernel.backtrace_includes?( Test2 )
|
65
79
|
pp Kernel.backtrace_includes?( self )
|
66
|
-
pp Kernel.backtrace_includes?( :
|
80
|
+
pp Kernel.backtrace_includes?( :yet_another_test, Test2, self )
|
67
81
|
end
|
68
|
-
|
82
|
+
|
69
83
|
end
|
70
84
|
|
71
85
|
$test = Test.new
|
72
86
|
$test.test
|
87
|
+
|
88
|
+
puts 'Finished Test.'
|
89
|
+
exit
|
73
90
|
|
74
91
|
== EXAMPLE's OUTPUT:
|
75
92
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
93
|
+
In method <Test>:initialize
|
94
|
+
Sender was: main
|
95
|
+
Caller was: <main>
|
96
|
+
In <Test>:test
|
97
|
+
In method <Test>:another_test
|
98
|
+
In method <Test2>:initialize
|
99
|
+
Sender was: #<Test:0x0000010180ddf8>
|
100
|
+
Caller was: another_test
|
101
|
+
In method <Test2>:and_another_test_in_another_object
|
102
|
+
#<Test2:0x0000010180b158>
|
103
|
+
Sender was: #<Test:0x0000010180ddf8>
|
104
|
+
Caller was: another_test
|
105
|
+
[{:object=>#<Test2:0x0000010180b158>,
|
106
|
+
:file=>"sender_test.rb",
|
107
|
+
:line=>39,
|
108
|
+
:method=>"and_another_test_in_another_object"},
|
109
|
+
{:object=>#<Test:0x0000010180ddf8>,
|
110
|
+
:file=>"sender_test.rb",
|
111
|
+
:line=>21,
|
112
|
+
:method=>"another_test"},
|
113
|
+
{:object=>#<Test:0x0000010180ddf8>,
|
114
|
+
:file=>"sender_test.rb",
|
115
|
+
:line=>15,
|
116
|
+
:method=>"test"},
|
117
|
+
{:object=>main, :file=>"sender_test.rb", :line=>56, :method=>"<main>"},
|
118
|
+
{:object=>main, :file=>"<main>", :line=>0, :method=>"<main>"}]
|
119
|
+
[{:object=>#<Test2:0x0000010180b158>,
|
120
|
+
:file=>"sender_test.rb",
|
121
|
+
:line=>40,
|
122
|
+
:method=>"and_another_test_in_another_object"},
|
123
|
+
{:object=>#<Test:0x0000010180ddf8>,
|
124
|
+
:file=>"sender_test.rb",
|
125
|
+
:line=>21,
|
126
|
+
:method=>"another_test"}]
|
83
127
|
These should be true:
|
84
128
|
true
|
85
129
|
true
|
@@ -90,6 +134,7 @@ which allows contents of the backtrace to be queried.
|
|
90
134
|
false
|
91
135
|
false
|
92
136
|
false
|
137
|
+
Finished Test.
|
93
138
|
|
94
139
|
== LICENSE:
|
95
140
|
|
data/Rakefile
CHANGED
@@ -11,7 +11,7 @@ Hoe.spec 'sender' do
|
|
11
11
|
self.spec_extras = { :extensions => ["ext/sender/extconf.rb"] }
|
12
12
|
self.extra_dev_deps << ['rake-compiler', '>= 0']
|
13
13
|
|
14
|
-
self.version=
|
14
|
+
self.version=File.open( 'VERSION.rdoc' ).readline
|
15
15
|
|
16
16
|
Rake::ExtensionTask.new( 'sender', spec ) do |ext|
|
17
17
|
ext.lib_dir = File.join('lib', 'sender')
|
data/VERSION.rdoc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.4
|
@@ -2,9 +2,9 @@
|
|
2
2
|
#include "RPSender_internal.h"
|
3
3
|
#include "RubySourceSupport.h"
|
4
4
|
|
5
|
-
|
6
|
-
*
|
7
|
-
|
5
|
+
/******************
|
6
|
+
* framePriorTo *
|
7
|
+
*****************/
|
8
8
|
|
9
9
|
rb_control_frame_t* RPRuby_internal_framePriorTo( rb_control_frame_t* c_control_frame ) {
|
10
10
|
|
@@ -35,32 +35,3 @@ rb_control_frame_t* RPRuby_internal_framePriorTo( rb_control_frame_t* c_control_
|
|
35
35
|
|
36
36
|
}
|
37
37
|
|
38
|
-
/**********************************
|
39
|
-
* backtraceHashForControlFrame *
|
40
|
-
*********************************/
|
41
|
-
|
42
|
-
VALUE RPSender_internal_backtraceHashForControlFrame( rb_control_frame_t* c_control_frame ) {
|
43
|
-
|
44
|
-
// create new hash for this frame
|
45
|
-
VALUE rb_frame_hash = rb_hash_new();
|
46
|
-
|
47
|
-
// get object and set in hash
|
48
|
-
VALUE rb_object_for_frame = c_control_frame->self;
|
49
|
-
// if we get nil we're done with our backtrace
|
50
|
-
rb_hash_aset( rb_frame_hash,
|
51
|
-
ID2SYM( rb_intern( "object" ) ),
|
52
|
-
rb_object_for_frame );
|
53
|
-
|
54
|
-
// get method and set in hash
|
55
|
-
ID c_frame_function_id = frame_func_id( c_control_frame );
|
56
|
-
// main has no method name
|
57
|
-
if ( c_frame_function_id != 0) {
|
58
|
-
VALUE rb_method_for_frame = ID2SYM( c_frame_function_id );
|
59
|
-
rb_hash_aset( rb_frame_hash,
|
60
|
-
ID2SYM( rb_intern( "method" ) ),
|
61
|
-
rb_method_for_frame );
|
62
|
-
|
63
|
-
}
|
64
|
-
|
65
|
-
return rb_frame_hash;
|
66
|
-
}
|
@@ -5,6 +5,6 @@
|
|
5
5
|
#include "eval_intern.h"
|
6
6
|
|
7
7
|
rb_control_frame_t* RPRuby_internal_framePriorTo( rb_control_frame_t* control_frame );
|
8
|
-
VALUE RPSender_internal_backtraceHashForControlFrame( rb_control_frame_t*
|
8
|
+
VALUE RPSender_internal_backtraceHashForControlFrame( const rb_control_frame_t* c_top_of_control_frame );
|
9
9
|
|
10
10
|
#endif
|
@@ -1,6 +1,11 @@
|
|
1
1
|
|
2
2
|
#include "RubySourceSupport.h"
|
3
3
|
|
4
|
+
#include "rb_Kernel.h"
|
5
|
+
#include "RPSender_internal.h"
|
6
|
+
|
7
|
+
#include "iseq.h"
|
8
|
+
|
4
9
|
// Taken from eval.c in Ruby source
|
5
10
|
// No header, so easiest way to integrate was to copy the code and make my own header.
|
6
11
|
// Previously declared static; otherwise unchanged
|
@@ -38,3 +43,33 @@ ID rb_frame_caller(void)
|
|
38
43
|
return frame_func_id(prev_cfp);
|
39
44
|
}
|
40
45
|
|
46
|
+
int rb_vm_get_sourceline(const rb_control_frame_t *cfp)
|
47
|
+
{
|
48
|
+
int line_no = 0;
|
49
|
+
const rb_iseq_t *iseq = cfp->iseq;
|
50
|
+
|
51
|
+
if (RUBY_VM_NORMAL_ISEQ_P(iseq)) {
|
52
|
+
rb_num_t i;
|
53
|
+
size_t pos = cfp->pc - cfp->iseq->iseq_encoded;
|
54
|
+
|
55
|
+
for (i = 0; i < iseq->insn_info_size; i++) {
|
56
|
+
if (iseq->insn_info_table[i].position == pos) {
|
57
|
+
if (i == 0) goto found;
|
58
|
+
line_no = iseq->insn_info_table[i - 1].line_no;
|
59
|
+
goto found;
|
60
|
+
}
|
61
|
+
}
|
62
|
+
line_no = iseq->insn_info_table[i - 1].line_no;
|
63
|
+
}
|
64
|
+
found:
|
65
|
+
return line_no;
|
66
|
+
}
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|
data/ext/sender/rb_Global.c
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
|
2
2
|
#include "rb_Global.h"
|
3
|
+
#include "rb_Kernel.h"
|
3
4
|
|
4
5
|
// Internals from ruby that aren't included in the ruby lib
|
5
6
|
#include "RubySourceSupport.h"
|
@@ -34,17 +35,31 @@ void Init_senderGlobal() {
|
|
34
35
|
* Return object sending message to receiver.
|
35
36
|
*/
|
36
37
|
VALUE rb_RPRuby_Sender___sender__() {
|
38
|
+
|
39
|
+
// we want 3 levels of backtrace:
|
40
|
+
// 1: current call to __method__ (__method__ in context)
|
41
|
+
// 2: the frame we want, unless it is :new (call to context: __sender__)
|
42
|
+
// 3: the frame we want in the case #2 is :new
|
43
|
+
VALUE rb_backtrace_limit = INT2FIX( 3 );
|
44
|
+
|
45
|
+
VALUE rb_backtrace_array = rb_RPRuby_Sender_Kernel_backtrace( 1,
|
46
|
+
& rb_backtrace_limit,
|
47
|
+
rb_mKernel );
|
37
48
|
|
38
|
-
|
39
|
-
|
49
|
+
VALUE rb_backtrace_frame_hash = rb_ary_entry( rb_backtrace_array, 1 );
|
50
|
+
|
51
|
+
VALUE rb_caller = rb_hash_aref( rb_backtrace_frame_hash,
|
52
|
+
ID2SYM( rb_intern( "method" ) ) );
|
40
53
|
|
41
|
-
|
42
|
-
|
43
|
-
return Qnil;
|
54
|
+
if ( rb_caller == ID2SYM( rb_intern( "new" ) ) ) {
|
55
|
+
rb_backtrace_frame_hash = rb_ary_entry( rb_backtrace_array, 2 );
|
44
56
|
}
|
45
57
|
|
58
|
+
VALUE rb_sender = rb_hash_aref( rb_backtrace_frame_hash,
|
59
|
+
ID2SYM( rb_intern( "object" ) ) );
|
60
|
+
|
46
61
|
// assuming we have a previous frame, return its rb_self (our current receiver's sender)
|
47
|
-
return
|
62
|
+
return rb_sender;
|
48
63
|
}
|
49
64
|
|
50
65
|
/***************
|
@@ -59,14 +74,30 @@ VALUE rb_RPRuby_Sender___sender__() {
|
|
59
74
|
*/
|
60
75
|
VALUE rb_RPRuby_Sender___caller__() {
|
61
76
|
|
62
|
-
|
63
|
-
|
77
|
+
|
78
|
+
// we want 3 levels of backtrace:
|
79
|
+
// 1: current call to __method__ (__method__ in context)
|
80
|
+
// 2: the frame we want, unless it is :new (call to context: __sender__)
|
81
|
+
// 3: the frame we want in the case #2 is :new
|
82
|
+
VALUE rb_backtrace_limit = INT2FIX( 3 );
|
64
83
|
|
65
|
-
|
66
|
-
|
67
|
-
|
84
|
+
VALUE rb_backtrace_array = rb_RPRuby_Sender_Kernel_backtrace( 1,
|
85
|
+
& rb_backtrace_limit,
|
86
|
+
rb_mKernel );
|
87
|
+
|
88
|
+
VALUE rb_backtrace_frame_hash = rb_ary_entry( rb_backtrace_array, 1 );
|
89
|
+
|
90
|
+
VALUE rb_caller = rb_hash_aref( rb_backtrace_frame_hash,
|
91
|
+
ID2SYM( rb_intern( "method" ) ) );
|
92
|
+
|
93
|
+
if ( rb_caller == ID2SYM( rb_intern( "new" ) ) ) {
|
94
|
+
rb_backtrace_frame_hash = rb_ary_entry( rb_backtrace_array, 2 );
|
95
|
+
|
96
|
+
rb_caller = rb_hash_aref( rb_backtrace_frame_hash,
|
97
|
+
ID2SYM( rb_intern( "method" ) ) );
|
68
98
|
}
|
69
99
|
|
70
|
-
return
|
100
|
+
// assuming we have a previous frame, return its rb_self (our current receiver's sender)
|
101
|
+
return rb_caller;
|
71
102
|
}
|
72
103
|
|
data/ext/sender/rb_Kernel.c
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
|
2
2
|
#include "rb_Kernel.h"
|
3
|
+
#include "rb_Kernel_internal.h"
|
3
4
|
|
4
5
|
#include "RPSender_internal.h"
|
5
6
|
|
@@ -28,60 +29,73 @@ void Init_senderKernel() {
|
|
28
29
|
|
29
30
|
/*
|
30
31
|
* call-seq:
|
31
|
-
*
|
32
|
+
* Kernel.backtrace( number_of_frames = nil ) -> [ { :object => object, :method => method }, ... ]
|
32
33
|
*
|
33
34
|
* Return array of hashes with object and method frame information for backtrace.
|
34
35
|
* Specifying number_of_frames will cause only the last number_of_frames to be returned.
|
36
|
+
* Kernel.backtrace returns all frames including the current context (__method__/__callee__).
|
35
37
|
*/
|
36
38
|
VALUE rb_RPRuby_Sender_Kernel_backtrace( int argc,
|
37
39
|
VALUE* args,
|
38
40
|
VALUE rb_self ) {
|
39
|
-
|
40
|
-
//
|
41
|
-
|
42
|
-
|
43
|
-
// for each previous frame
|
44
|
-
rb_control_frame_t* c_control_frame = NULL;
|
45
|
-
int c_stack_level = 0;
|
46
|
-
int c_max_stack_level = 0;
|
41
|
+
|
42
|
+
// Get max stack level from args if it is there
|
43
|
+
int c_max_stack_level = 0;
|
47
44
|
if ( argc ) {
|
48
45
|
c_max_stack_level = FIX2INT( args[ 0 ] );
|
46
|
+
|
47
|
+
// if max_stack_level is 0 return empty array
|
48
|
+
if ( c_max_stack_level == 0 ) {
|
49
|
+
return rb_ary_new();
|
50
|
+
}
|
51
|
+
// if max_stack_level < 0, throw error
|
52
|
+
else if ( c_max_stack_level < 0 ) {
|
53
|
+
rb_raise( rb_eArgError, RPRUBY_SENDER_ERROR_STACK_LEVEL_LESS_THAN_ZERO );
|
54
|
+
}
|
55
|
+
|
49
56
|
}
|
50
|
-
|
51
|
-
|
52
|
-
|
57
|
+
|
58
|
+
rb_thread_t* c_thread = GET_THREAD();
|
59
|
+
// Get the current frame - we're doing a backtrace, so our current working frame to start is the first previous thread
|
60
|
+
rb_control_frame_t* c_current_context_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( c_thread->cfp );
|
61
|
+
|
62
|
+
// c_top_of_control_frame describes the top edge of the stack trace
|
63
|
+
// set c_top_of_control_frame to the first frame in <main>
|
64
|
+
rb_control_frame_t* c_top_of_control_frame = RUBY_VM_NEXT_CONTROL_FRAME( RUBY_VM_NEXT_CONTROL_FRAME( (void *)( c_thread->stack + c_thread->stack_size ) ) );
|
65
|
+
|
66
|
+
VALUE rb_return_array = rb_ary_new();
|
67
|
+
|
68
|
+
int c_stack_level = 0;
|
69
|
+
// for each control frame:
|
70
|
+
while ( c_current_context_frame < c_top_of_control_frame
|
53
71
|
&& ( argc == 0
|
54
|
-
|| c_stack_level < c_max_stack_level ) )
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
if ( rb_frame_hash == Qnil ) {
|
60
|
-
break;
|
61
|
-
}
|
62
|
-
|
72
|
+
|| c_stack_level < c_max_stack_level ) ) {
|
73
|
+
|
74
|
+
VALUE rb_frame_hash = rb_RPRuby_Sender_Kernel_internal_backtraceHashForControlFrame( & c_current_context_frame );
|
75
|
+
|
63
76
|
// push hash to array
|
64
77
|
rb_ary_push( rb_return_array,
|
65
78
|
rb_frame_hash );
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
79
|
+
|
80
|
+
c_current_context_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( c_current_context_frame );
|
81
|
+
c_stack_level++;
|
82
|
+
}
|
83
|
+
|
84
|
+
return rb_return_array;
|
72
85
|
}
|
73
86
|
|
87
|
+
|
74
88
|
/*****************************
|
75
89
|
* __backtrace_includes?__ *
|
76
90
|
****************************/
|
77
91
|
|
78
92
|
/*
|
79
93
|
* call-seq:
|
80
|
-
*
|
81
|
-
*
|
94
|
+
* Kernel.backtrace_includes?( method_or_object, ... ) -> true or false
|
95
|
+
* Kernel.backtrace_includes?( number_of_frames, method_or_object, ... ) -> true or false
|
82
96
|
*
|
83
97
|
* Returns whether specified methods or objects or classes are in the current backtrace context.
|
84
|
-
*
|
98
|
+
* Kernel.backtrace_includes? begins with the prior frame, so asking if the backtrace includes the current method
|
85
99
|
* will only report true if the current method is part of the earlier call chain.
|
86
100
|
*/
|
87
101
|
VALUE rb_RPRuby_Sender_Kernel_backtrace_includes( int argc,
|
@@ -91,66 +105,74 @@ VALUE rb_RPRuby_Sender_Kernel_backtrace_includes( int argc,
|
|
91
105
|
// create tracking array
|
92
106
|
VALUE rb_tracking_array = rb_ary_new();
|
93
107
|
|
94
|
-
// populate array with methods/objects
|
108
|
+
// populate tracking array with methods/objects
|
95
109
|
int c_which_arg = 0;
|
96
110
|
for ( c_which_arg = 0 ; c_which_arg < argc ; c_which_arg++ ) {
|
97
111
|
rb_ary_push( rb_tracking_array,
|
98
112
|
args[ c_which_arg ] );
|
99
113
|
}
|
114
|
+
|
115
|
+
rb_thread_t* c_thread = GET_THREAD();
|
116
|
+
// Get the current frame - we're doing a backtrace, so our current working frame to start is the first previous thread
|
117
|
+
rb_control_frame_t* c_current_context_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( RUBY_VM_PREVIOUS_CONTROL_FRAME( c_thread->cfp ) );
|
100
118
|
|
101
|
-
//
|
102
|
-
|
103
|
-
|
119
|
+
// c_top_of_control_frame describes the top edge of the stack trace
|
120
|
+
// set c_top_of_control_frame to the first frame in <main>
|
121
|
+
rb_control_frame_t* c_top_of_control_frame = RUBY_VM_NEXT_CONTROL_FRAME( RUBY_VM_NEXT_CONTROL_FRAME( (void *)( c_thread->stack + c_thread->stack_size ) ) );
|
104
122
|
|
123
|
+
// for each control frame:
|
124
|
+
while ( c_current_context_frame < c_top_of_control_frame ) {
|
125
|
+
|
105
126
|
// iterate each array member
|
106
127
|
int c_which_member;
|
107
128
|
for ( c_which_member = 0 ; c_which_member < RARRAY_LEN( rb_tracking_array ) ; c_which_member++ ) {
|
108
|
-
|
129
|
+
|
109
130
|
VALUE rb_array_member = args[ c_which_member ];
|
110
|
-
|
131
|
+
|
111
132
|
int matched = 0;
|
112
|
-
|
133
|
+
|
113
134
|
// if rb_array_member is a class
|
114
135
|
if ( TYPE( rb_array_member ) == T_CLASS ) {
|
115
136
|
// if rb_array_member is the current frame's object's class
|
116
|
-
if ( rb_array_member == rb_class_of(
|
137
|
+
if ( rb_array_member == rb_class_of( c_current_context_frame->self ) ) {
|
117
138
|
matched = 1;
|
118
139
|
}
|
119
140
|
}
|
120
141
|
// if rb_array_member is a method symbol and matches our current frame's method
|
121
142
|
else if ( TYPE( rb_array_member ) == T_SYMBOL ) {
|
122
|
-
|
123
|
-
if ( rb_to_id( rb_array_member ) == frame_func_id(
|
143
|
+
|
144
|
+
if ( rb_to_id( rb_array_member ) == frame_func_id( c_current_context_frame ) ) {
|
124
145
|
matched = 1;
|
125
146
|
}
|
126
147
|
}
|
127
148
|
// if rb_array_member is an object
|
128
149
|
else if ( TYPE( rb_array_member ) == T_OBJECT ) {
|
129
150
|
// if rb_array_member is the current frame's object (self)
|
130
|
-
if ( rb_array_member ==
|
151
|
+
if ( rb_array_member == c_current_context_frame->self ) {
|
131
152
|
matched = 1;
|
132
153
|
}
|
133
154
|
}
|
134
|
-
|
155
|
+
|
135
156
|
// if array member exists in frame, remove from array
|
136
157
|
if ( matched ) {
|
137
158
|
// delete this index
|
138
159
|
rb_ary_delete_at( rb_tracking_array,
|
139
160
|
c_which_member );
|
140
|
-
|
161
|
+
|
141
162
|
// decrement the loop iterator so that the increase is offset
|
142
163
|
// this is necessary since we just removed an index and are iterating vs. the length of the array
|
143
164
|
c_which_member--;
|
144
165
|
}
|
145
166
|
}
|
146
|
-
|
167
|
+
|
147
168
|
// if array is empty, return true
|
148
169
|
// we check here as well as at the end so we can stop iterating the backtrace if we find all our items
|
149
170
|
if ( RARRAY_LEN( rb_tracking_array ) == 0 ) {
|
150
171
|
return Qtrue;
|
151
|
-
}
|
172
|
+
}
|
173
|
+
c_current_context_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( c_current_context_frame );
|
152
174
|
}
|
153
|
-
|
175
|
+
|
154
176
|
// if we finish iterating frames and still have items in the array, return false
|
155
177
|
if ( RARRAY_LEN( rb_tracking_array ) > 0 ) {
|
156
178
|
return Qfalse;
|
@@ -160,3 +182,79 @@ VALUE rb_RPRuby_Sender_Kernel_backtrace_includes( int argc,
|
|
160
182
|
return Qtrue;
|
161
183
|
}
|
162
184
|
|
185
|
+
/**********************************
|
186
|
+
* backtraceHashForControlFrame *
|
187
|
+
*********************************/
|
188
|
+
|
189
|
+
VALUE rb_RPRuby_Sender_Kernel_internal_backtraceHashForControlFrame( rb_control_frame_t** c_current_frame ) {
|
190
|
+
|
191
|
+
const char* c_method_name = NULL;
|
192
|
+
int c_sourcefile_line = 0;
|
193
|
+
|
194
|
+
// create new hash for this frame
|
195
|
+
VALUE rb_frame_hash = rb_hash_new();
|
196
|
+
|
197
|
+
VALUE rb_sourcefile_name = Qnil;
|
198
|
+
VALUE rb_sourcefile_line = Qnil;
|
199
|
+
VALUE rb_method_name = Qnil;
|
200
|
+
VALUE rb_object_for_frame = Qnil;
|
201
|
+
|
202
|
+
if ( ( *c_current_frame )->iseq != 0 ) {
|
203
|
+
|
204
|
+
if ( ( *c_current_frame )->pc != 0 ) {
|
205
|
+
|
206
|
+
rb_iseq_t *iseq = ( *c_current_frame )->iseq;
|
207
|
+
|
208
|
+
// get sourcefile name and set in hash
|
209
|
+
rb_sourcefile_name = iseq->filename;
|
210
|
+
|
211
|
+
// get sourcefile line and set in hash
|
212
|
+
c_sourcefile_line = rb_vm_get_sourceline( *c_current_frame );
|
213
|
+
rb_sourcefile_line = INT2FIX( c_sourcefile_line );
|
214
|
+
|
215
|
+
// get name of instruction sequence
|
216
|
+
rb_method_name = ID2SYM( rb_intern( StringValuePtr( iseq->name ) ) );
|
217
|
+
}
|
218
|
+
}
|
219
|
+
else if ( RUBYVM_CFUNC_FRAME_P( *c_current_frame ) ) {
|
220
|
+
|
221
|
+
// get name of method
|
222
|
+
c_method_name = rb_id2name( ( *c_current_frame )->method_id );
|
223
|
+
rb_method_name = ( c_method_name == NULL ? Qnil : ID2SYM( rb_intern( c_method_name ) ) );
|
224
|
+
}
|
225
|
+
else {
|
226
|
+
// The third possibility is that we have an iseq frame with nil params for what we want
|
227
|
+
// In that case we can simply return the next frame
|
228
|
+
*c_current_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( *c_current_frame );
|
229
|
+
|
230
|
+
// in theory this could crash because we are going forward a frame when we don't know what's there
|
231
|
+
// in practice I think we are ok, since we are only jumping forward from nil frames which should never be at the end
|
232
|
+
// at least - I don't think they should... we shall see.
|
233
|
+
//
|
234
|
+
// a fix would be to check the next frame, but that requires access to the thread or the limit cfp,
|
235
|
+
// which requires passing more context; so for now, I'm leaving it there
|
236
|
+
|
237
|
+
return rb_RPRuby_Sender_Kernel_internal_backtraceHashForControlFrame( c_current_frame );
|
238
|
+
}
|
239
|
+
|
240
|
+
// Push values to return hash
|
241
|
+
|
242
|
+
rb_object_for_frame = ( *c_current_frame )->self;
|
243
|
+
rb_hash_aset( rb_frame_hash,
|
244
|
+
ID2SYM( rb_intern( "object" ) ),
|
245
|
+
rb_object_for_frame );
|
246
|
+
|
247
|
+
rb_hash_aset( rb_frame_hash,
|
248
|
+
ID2SYM( rb_intern( "file" ) ),
|
249
|
+
rb_sourcefile_name );
|
250
|
+
|
251
|
+
rb_hash_aset( rb_frame_hash,
|
252
|
+
ID2SYM( rb_intern( "line" ) ),
|
253
|
+
rb_sourcefile_line );
|
254
|
+
|
255
|
+
rb_hash_aset( rb_frame_hash,
|
256
|
+
ID2SYM( rb_intern( "method" ) ),
|
257
|
+
rb_method_name );
|
258
|
+
|
259
|
+
return rb_frame_hash;
|
260
|
+
}
|
data/ext/sender/rb_Kernel.h
CHANGED
@@ -4,11 +4,13 @@
|
|
4
4
|
|
5
5
|
#include "ruby.h"
|
6
6
|
|
7
|
+
#define RPRUBY_SENDER_ERROR_STACK_LEVEL_LESS_THAN_ZERO "Maximum level for stack trace specified was less than zero."
|
8
|
+
|
7
9
|
void Init_senderKernel();
|
8
10
|
|
9
|
-
VALUE rb_RPRuby_Sender_Kernel_backtrace( int
|
10
|
-
|
11
|
-
|
11
|
+
VALUE rb_RPRuby_Sender_Kernel_backtrace( int argc,
|
12
|
+
VALUE* args,
|
13
|
+
VALUE rb_self );
|
12
14
|
VALUE rb_RPRuby_Sender_Kernel_backtrace_includes( int argc,
|
13
15
|
VALUE* args,
|
14
16
|
VALUE rb_self );
|
data/lib/sender/sender.bundle
CHANGED
Binary file
|
data/lib/sender.rb
CHANGED
@@ -2,33 +2,6 @@ require 'sender/sender'
|
|
2
2
|
|
3
3
|
module Sender
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
##############
|
8
|
-
# included #
|
9
|
-
##############
|
10
|
-
|
11
|
-
def self.included( module_or_class_const )
|
12
|
-
|
13
|
-
# define a class method "new" on self that calls :init_sender_callbacks if it is defined
|
14
|
-
module_or_class_const.class_eval do
|
15
|
-
|
16
|
-
#########
|
17
|
-
# new #
|
18
|
-
#########
|
19
|
-
|
20
|
-
def new( *args )
|
21
|
-
new_self = super( *args )
|
22
|
-
if self.respond_to?( :init_sender_callbacks )
|
23
|
-
# :init_sender_callbacks allows callback parameters to be defined during initialization
|
24
|
-
# it is called automatically when the object is created, prior to calling Object.initialize
|
25
|
-
self.__send__( :init_sender_callbacks, __sender__, __caller__ )
|
26
|
-
end
|
27
|
-
return new_self
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
5
|
+
Version = File.open( 'VERSION.rdoc' ).readline
|
33
6
|
|
34
7
|
end
|
data/sender.gemspec
CHANGED
@@ -2,18 +2,18 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{sender}
|
5
|
-
s.version = "1.
|
5
|
+
s.version = "1.4"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Asher"]
|
9
|
-
s.date = %q{2010-06-
|
9
|
+
s.date = %q{2010-06-29}
|
10
10
|
s.description = %q{Adds :__sender__ and :__caller__ to the built-in :__callee__ and :__method__ methods in Ruby 1.9.1, as well
|
11
11
|
as providing object-oriented :backtrace supporting n-levels backward, and :backtrace_includes?,
|
12
12
|
which allows contents of the backtrace to be queried.}
|
13
13
|
s.email = ["asher@ridiculouspower.com"]
|
14
14
|
s.extensions = ["ext/sender/extconf.rb"]
|
15
|
-
s.extra_rdoc_files = ["Manifest.txt", "CHANGELOG.rdoc", "README.rdoc"]
|
16
|
-
s.files = [".autotest", "CHANGELOG.rdoc", "Makefile", "Manifest.txt", "README.rdoc", "Rakefile", "ext/sender/RPSender_internal.c", "ext/sender/RPSender_internal.h", "ext/sender/RubySourceSupport.c", "ext/sender/RubySourceSupport.h", "ext/sender/debug.h", "ext/sender/dln.h", "ext/sender/encdb.h", "ext/sender/eval_intern.h", "ext/sender/extconf.rb", "ext/sender/gc.h", "ext/sender/id.h", "ext/sender/iseq.h", "ext/sender/main.c", "ext/sender/node.h", "ext/sender/parse.h", "ext/sender/rb_Global.c", "ext/sender/rb_Global.h", "ext/sender/rb_Global_internal.h", "ext/sender/rb_Kernel.c", "ext/sender/rb_Kernel.h", "ext/sender/regenc.h", "ext/sender/regint.h", "ext/sender/regparse.h", "ext/sender/revision.h", "ext/sender/thread_pthread.h", "ext/sender/thread_win32.h", "ext/sender/transcode_data.h", "ext/sender/transdb.h", "ext/sender/version.h", "ext/sender/vm_core.h", "ext/sender/vm_exec.h", "ext/sender/vm_insnhelper.h", "ext/sender/vm_opts.h", "lib/sender.rb", "lib/sender/sender.bundle", "sender.gemspec", "test/test_sender.rb"]
|
15
|
+
s.extra_rdoc_files = ["Manifest.txt", "CHANGELOG.rdoc", "README.rdoc", "VERSION.rdoc"]
|
16
|
+
s.files = [".autotest", "CHANGELOG.rdoc", "Makefile", "Manifest.txt", "README.rdoc", "Rakefile", "VERSION.rdoc", "ext/sender/RPSender_internal.c", "ext/sender/RPSender_internal.h", "ext/sender/RubySourceSupport.c", "ext/sender/RubySourceSupport.h", "ext/sender/debug.h", "ext/sender/dln.h", "ext/sender/encdb.h", "ext/sender/eval_intern.h", "ext/sender/extconf.rb", "ext/sender/gc.h", "ext/sender/id.h", "ext/sender/iseq.h", "ext/sender/main.c", "ext/sender/node.h", "ext/sender/parse.h", "ext/sender/rb_Global.c", "ext/sender/rb_Global.h", "ext/sender/rb_Global_internal.h", "ext/sender/rb_Kernel.c", "ext/sender/rb_Kernel.h", "ext/sender/rb_Kernel_internal.h", "ext/sender/regenc.h", "ext/sender/regint.h", "ext/sender/regparse.h", "ext/sender/revision.h", "ext/sender/thread_pthread.h", "ext/sender/thread_win32.h", "ext/sender/transcode_data.h", "ext/sender/transdb.h", "ext/sender/version.h", "ext/sender/vm_core.h", "ext/sender/vm_exec.h", "ext/sender/vm_insnhelper.h", "ext/sender/vm_opts.h", "lib/sender.rb", "lib/sender/sender.bundle", "sender.gemspec", "test/test_sender.rb"]
|
17
17
|
s.homepage = %q{http://rubygems.org/gems/sender}
|
18
18
|
s.rdoc_options = ["--main", "README.rdoc"]
|
19
19
|
s.require_paths = ["lib"]
|
data/test/test_sender.rb
CHANGED
metadata
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sender
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 7
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: "1.
|
8
|
+
- 4
|
9
|
+
version: "1.4"
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Asher
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-06-
|
17
|
+
date: 2010-06-29 00:00:00 -04:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -77,6 +77,7 @@ extra_rdoc_files:
|
|
77
77
|
- Manifest.txt
|
78
78
|
- CHANGELOG.rdoc
|
79
79
|
- README.rdoc
|
80
|
+
- VERSION.rdoc
|
80
81
|
files:
|
81
82
|
- .autotest
|
82
83
|
- CHANGELOG.rdoc
|
@@ -84,6 +85,7 @@ files:
|
|
84
85
|
- Manifest.txt
|
85
86
|
- README.rdoc
|
86
87
|
- Rakefile
|
88
|
+
- VERSION.rdoc
|
87
89
|
- ext/sender/RPSender_internal.c
|
88
90
|
- ext/sender/RPSender_internal.h
|
89
91
|
- ext/sender/RubySourceSupport.c
|
@@ -104,6 +106,7 @@ files:
|
|
104
106
|
- ext/sender/rb_Global_internal.h
|
105
107
|
- ext/sender/rb_Kernel.c
|
106
108
|
- ext/sender/rb_Kernel.h
|
109
|
+
- ext/sender/rb_Kernel_internal.h
|
107
110
|
- ext/sender/regenc.h
|
108
111
|
- ext/sender/regint.h
|
109
112
|
- ext/sender/regparse.h
|