sender 1.3 → 1.4
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/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
|