sender 1.5.7 → 1.5.8
Sign up to get free protection for your applications and to get access to all the features.
- data/ext/sender/RPSender_internal.c +37 -0
- data/ext/sender/RPSender_internal.h +15 -0
- data/ext/sender/RubySourceSupport.c +47 -0
- data/ext/sender/RubySourceSupport.h +77 -0
- data/ext/sender/rb_Global.c +151 -0
- data/ext/sender/rb_Global.h +11 -0
- data/ext/sender/rb_Global_internal.h +8 -0
- data/ext/sender/rb_Kernel.c +683 -0
- data/ext/sender/rb_Kernel.h +31 -0
- data/ext/sender/rb_Kernel_internal.h +10 -0
- data/ext/sender/sender.c +16 -0
- data/test/test_sender.rb +22 -0
- metadata +16 -6
- data/lib/VERSION.rdoc +0 -1
- data/lib/sender.rb +0 -5
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
#include "RPSender_internal.h"
|
3
|
+
#include "RubySourceSupport.h"
|
4
|
+
|
5
|
+
/******************
|
6
|
+
* framePriorTo *
|
7
|
+
*****************/
|
8
|
+
|
9
|
+
rb_control_frame_t* RPRuby_internal_framePriorTo( rb_control_frame_t* c_control_frame ) {
|
10
|
+
|
11
|
+
rb_thread_t* c_thread = GET_THREAD();
|
12
|
+
rb_control_frame_t* c_prior_control_frame = NULL;
|
13
|
+
// get the current frame pointer
|
14
|
+
if ( c_control_frame == NULL ) {
|
15
|
+
c_control_frame = c_thread->cfp;
|
16
|
+
}
|
17
|
+
|
18
|
+
if ( ( c_prior_control_frame = rb_vm_get_ruby_level_next_cfp( c_thread, c_control_frame ) ) != 0) {
|
19
|
+
|
20
|
+
// not sure why we have to call this a second time after it was called at the end of rb_vm_get_ruby_level_next_cfp,
|
21
|
+
// but for some reason it seems to be necessary
|
22
|
+
c_prior_control_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( c_prior_control_frame );
|
23
|
+
|
24
|
+
}
|
25
|
+
else {
|
26
|
+
c_prior_control_frame = NULL;
|
27
|
+
}
|
28
|
+
|
29
|
+
// if we have a nil object we've passed main, we're done
|
30
|
+
if ( c_prior_control_frame->self == Qnil ) {
|
31
|
+
return NULL;
|
32
|
+
}
|
33
|
+
|
34
|
+
return c_prior_control_frame;
|
35
|
+
|
36
|
+
}
|
37
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#ifndef RP_SENDER_INTERNAL
|
2
|
+
#define RP_SENDER_INTERNAL
|
3
|
+
|
4
|
+
#include "ruby.h"
|
5
|
+
#include "eval_intern.h"
|
6
|
+
|
7
|
+
typedef enum BOOL_e {
|
8
|
+
FALSE,
|
9
|
+
TRUE
|
10
|
+
} BOOL;
|
11
|
+
|
12
|
+
rb_control_frame_t* RPRuby_internal_framePriorTo( rb_control_frame_t* control_frame );
|
13
|
+
VALUE RPSender_internal_backtraceHashForControlFrame( const rb_control_frame_t* c_top_of_control_frame );
|
14
|
+
|
15
|
+
#endif
|
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
#include "RubySourceSupport.h"
|
3
|
+
|
4
|
+
#include "rb_Kernel.h"
|
5
|
+
#include "RPSender_internal.h"
|
6
|
+
|
7
|
+
#include "iseq.h"
|
8
|
+
|
9
|
+
// Taken from eval.c in Ruby source
|
10
|
+
// No header, so easiest way to integrate was to copy the code and make my own header.
|
11
|
+
// Previously declared static; otherwise unchanged
|
12
|
+
|
13
|
+
int rb_vm_get_sourceline(const rb_control_frame_t *cfp)
|
14
|
+
{
|
15
|
+
int line_no = 0;
|
16
|
+
const rb_iseq_t *iseq = cfp->iseq;
|
17
|
+
|
18
|
+
if (RUBY_VM_NORMAL_ISEQ_P(iseq)) {
|
19
|
+
rb_num_t i;
|
20
|
+
size_t pos = cfp->pc - cfp->iseq->iseq_encoded;
|
21
|
+
|
22
|
+
for (i = 0; i < iseq->insn_info_size; i++) {
|
23
|
+
if (iseq->insn_info_table[i].position == pos) {
|
24
|
+
if (i == 0) goto found;
|
25
|
+
line_no = iseq->insn_info_table[i - 1].line_no;
|
26
|
+
goto found;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
line_no = iseq->insn_info_table[i - 1].line_no;
|
30
|
+
}
|
31
|
+
found:
|
32
|
+
return line_no;
|
33
|
+
}
|
34
|
+
|
35
|
+
struct enumerator* enumerator_ptr(VALUE obj)
|
36
|
+
{
|
37
|
+
struct enumerator *ptr;
|
38
|
+
|
39
|
+
Data_Get_Struct(obj, struct enumerator, ptr);
|
40
|
+
|
41
|
+
// modified because a call to a local version of enumerator_mark won't verify
|
42
|
+
|
43
|
+
if (!ptr || ptr->obj == Qundef) {
|
44
|
+
rb_raise(rb_eArgError, "uninitialized enumerator");
|
45
|
+
}
|
46
|
+
return ptr;
|
47
|
+
}
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#ifndef RP_SENDER_RUBY_SOURCE_SUPPORT
|
2
|
+
#define RP_SENDER_RUBY_SOURCE_SUPPORT
|
3
|
+
|
4
|
+
#include "ruby.h"
|
5
|
+
#include "eval_intern.h"
|
6
|
+
#include "version.h"
|
7
|
+
#if RUBY_PATCHLEVEL == -1
|
8
|
+
#include "vm_core.h"
|
9
|
+
#endif
|
10
|
+
#include "method.h"
|
11
|
+
|
12
|
+
#define MAX_POSBUF 128
|
13
|
+
|
14
|
+
enum context_type {
|
15
|
+
CONTINUATION_CONTEXT = 0,
|
16
|
+
FIBER_CONTEXT = 1,
|
17
|
+
ROOT_FIBER_CONTEXT = 2
|
18
|
+
};
|
19
|
+
|
20
|
+
typedef struct rb_context_struct {
|
21
|
+
enum context_type type;
|
22
|
+
VALUE self;
|
23
|
+
int argc;
|
24
|
+
VALUE value;
|
25
|
+
VALUE *vm_stack;
|
26
|
+
#ifdef CAPTURE_JUST_VALID_VM_STACK
|
27
|
+
int vm_stack_slen; /* length of stack (head of th->stack) */
|
28
|
+
int vm_stack_clen; /* length of control frames (tail of th->stack) */
|
29
|
+
#endif
|
30
|
+
VALUE *machine_stack;
|
31
|
+
VALUE *machine_stack_src;
|
32
|
+
#ifdef __ia64
|
33
|
+
VALUE *machine_register_stack;
|
34
|
+
VALUE *machine_register_stack_src;
|
35
|
+
int machine_register_stack_size;
|
36
|
+
#endif
|
37
|
+
rb_thread_t saved_thread;
|
38
|
+
rb_jmpbuf_t jmpbuf;
|
39
|
+
int machine_stack_size;
|
40
|
+
} rb_context_t;
|
41
|
+
|
42
|
+
enum fiber_status {
|
43
|
+
CREATED,
|
44
|
+
RUNNING,
|
45
|
+
TERMINATED
|
46
|
+
};
|
47
|
+
|
48
|
+
typedef struct rb_fiber_struct {
|
49
|
+
rb_context_t cont;
|
50
|
+
VALUE prev;
|
51
|
+
enum fiber_status status;
|
52
|
+
struct rb_fiber_struct *prev_fiber;
|
53
|
+
struct rb_fiber_struct *next_fiber;
|
54
|
+
} rb_fiber_t;
|
55
|
+
|
56
|
+
struct enumerator {
|
57
|
+
VALUE obj;
|
58
|
+
ID meth;
|
59
|
+
VALUE args;
|
60
|
+
VALUE fib;
|
61
|
+
VALUE dst;
|
62
|
+
VALUE no_next;
|
63
|
+
};
|
64
|
+
|
65
|
+
#define GetFiberPtr(obj, ptr) do {\
|
66
|
+
ptr = (rb_fiber_t*)DATA_PTR(obj);\
|
67
|
+
if (!ptr) rb_raise(rb_eFiberError, "uninitialized fiber");\
|
68
|
+
} while(0)
|
69
|
+
|
70
|
+
|
71
|
+
ID frame_func_id( rb_control_frame_t *cfp );
|
72
|
+
ID rb_frame_caller(void);
|
73
|
+
int rb_vm_get_sourceline(const rb_control_frame_t *cfp);
|
74
|
+
void control_frame_dump(rb_thread_t *th, rb_control_frame_t *cfp);
|
75
|
+
struct enumerator* enumerator_ptr(VALUE obj);
|
76
|
+
|
77
|
+
#endif
|
@@ -0,0 +1,151 @@
|
|
1
|
+
|
2
|
+
#include "rb_Global.h"
|
3
|
+
#include "rb_Kernel.h"
|
4
|
+
|
5
|
+
// Internals from ruby that aren't included in the ruby lib
|
6
|
+
#include "RubySourceSupport.h"
|
7
|
+
|
8
|
+
#include "eval_intern.h"
|
9
|
+
|
10
|
+
/***********
|
11
|
+
* Global *
|
12
|
+
***********/
|
13
|
+
|
14
|
+
void Init_senderGlobal() {
|
15
|
+
|
16
|
+
rb_define_global_function( "__sender__", rb_RPRuby_Sender___sender__, 0 );
|
17
|
+
rb_define_global_function( "__caller__", rb_RPRuby_Sender___caller__, 0 );
|
18
|
+
|
19
|
+
}
|
20
|
+
|
21
|
+
/***************************************************************************************************************************************************************
|
22
|
+
****************************************************************************************************************************************************************
|
23
|
+
Ruby Global Methods
|
24
|
+
****************************************************************************************************************************************************************
|
25
|
+
***************************************************************************************************************************************************************/
|
26
|
+
|
27
|
+
/***************
|
28
|
+
* __sender__ *
|
29
|
+
***************/
|
30
|
+
|
31
|
+
/*
|
32
|
+
* call-seq:
|
33
|
+
* __sender__ -> object
|
34
|
+
*
|
35
|
+
* Return object sending message to receiver.
|
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 );
|
48
|
+
|
49
|
+
int c_backtrace_index = 1;
|
50
|
+
|
51
|
+
VALUE rb_backtrace_frame_hash = rb_ary_entry( rb_backtrace_array, c_backtrace_index );
|
52
|
+
|
53
|
+
VALUE rb_caller = rb_hash_aref( rb_backtrace_frame_hash,
|
54
|
+
ID2SYM( rb_intern( "method" ) ) );
|
55
|
+
|
56
|
+
// if we get :initialize as our caller and our __method__ is :initialize, we need to go up the chain
|
57
|
+
// until our caller is no longer :initialize or :new
|
58
|
+
while ( rb_caller == ID2SYM( rb_intern( "initialize" ) ) ) {
|
59
|
+
c_backtrace_index++;
|
60
|
+
rb_backtrace_frame_hash = rb_ary_entry( rb_backtrace_array, c_backtrace_index );
|
61
|
+
rb_caller = rb_hash_aref( rb_backtrace_frame_hash,
|
62
|
+
ID2SYM( rb_intern( "method" ) ) );
|
63
|
+
|
64
|
+
// we have one parent past our current method; if that is also :initialize, get the whole backtrace
|
65
|
+
if ( c_backtrace_index == 2 ) {
|
66
|
+
rb_backtrace_array = rb_RPRuby_Sender_Kernel_backtrace( 0, NULL, rb_mKernel );
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
// if we get "new" as our caller we need to get the next level, as we are in :initialize
|
71
|
+
// and want to know what called :new
|
72
|
+
//
|
73
|
+
// since we might have had to go up the chain from :initialize through parents before getting here we
|
74
|
+
// want to use a separate if statement
|
75
|
+
if ( rb_caller == ID2SYM( rb_intern( "new" ) ) ) {
|
76
|
+
c_backtrace_index++;
|
77
|
+
rb_backtrace_frame_hash = rb_ary_entry( rb_backtrace_array, c_backtrace_index );
|
78
|
+
}
|
79
|
+
|
80
|
+
VALUE rb_sender = rb_hash_aref( rb_backtrace_frame_hash,
|
81
|
+
ID2SYM( rb_intern( "object" ) ) );
|
82
|
+
|
83
|
+
// assuming we have a previous frame, return its rb_self (our current receiver's sender)
|
84
|
+
return rb_sender;
|
85
|
+
}
|
86
|
+
|
87
|
+
/***************
|
88
|
+
* __caller__ *
|
89
|
+
***************/
|
90
|
+
|
91
|
+
/*
|
92
|
+
* call-seq:
|
93
|
+
* __caller__ -> object
|
94
|
+
*
|
95
|
+
* Return method sending message to receiver.
|
96
|
+
*/
|
97
|
+
VALUE rb_RPRuby_Sender___caller__() {
|
98
|
+
|
99
|
+
|
100
|
+
// we want 3 levels of backtrace:
|
101
|
+
// 1: current call to __method__ (__method__ in context)
|
102
|
+
// 2: the frame we want, unless it is :new (call to context: __sender__)
|
103
|
+
// 3: the frame we want in the case #2 is :new
|
104
|
+
VALUE rb_backtrace_limit = INT2FIX( 3 );
|
105
|
+
|
106
|
+
VALUE rb_backtrace_array = rb_RPRuby_Sender_Kernel_backtrace( 1,
|
107
|
+
& rb_backtrace_limit,
|
108
|
+
rb_mKernel );
|
109
|
+
|
110
|
+
int c_backtrace_index = 1;
|
111
|
+
|
112
|
+
VALUE rb_backtrace_frame_hash = rb_ary_entry( rb_backtrace_array, c_backtrace_index );
|
113
|
+
|
114
|
+
VALUE rb_caller = rb_hash_aref( rb_backtrace_frame_hash,
|
115
|
+
ID2SYM( rb_intern( "method" ) ) );
|
116
|
+
|
117
|
+
// we want to compare our caller as we go up the chain to our first caller to deal with super methods
|
118
|
+
// we have a symbol so no cloning is necessary (or appropriate)
|
119
|
+
VALUE rb_first_caller = rb_hash_aref( rb_ary_entry( rb_backtrace_array, 0 ),
|
120
|
+
ID2SYM( rb_intern( "method" ) ) );
|
121
|
+
|
122
|
+
// if we get :initialize as our caller and our __method__ is :initialize, we need to go up the chain
|
123
|
+
// until our caller is no longer :initialize or :new
|
124
|
+
while ( rb_caller == rb_first_caller ) {
|
125
|
+
c_backtrace_index++;
|
126
|
+
rb_backtrace_frame_hash = rb_ary_entry( rb_backtrace_array, c_backtrace_index );
|
127
|
+
rb_caller = rb_hash_aref( rb_backtrace_frame_hash,
|
128
|
+
ID2SYM( rb_intern( "method" ) ) );
|
129
|
+
|
130
|
+
// we have one parent past our current method; if that is also :initialize, get the whole backtrace
|
131
|
+
if ( c_backtrace_index == 2 ) {
|
132
|
+
rb_backtrace_array = rb_RPRuby_Sender_Kernel_backtrace( 0, NULL, rb_mKernel );
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
// if we get "new" as our caller we need to get the next level, as we are in :initialize
|
137
|
+
// and want to know what called :new
|
138
|
+
//
|
139
|
+
// since we might have had to go up the chain from :initialize through parents before getting here we
|
140
|
+
// want to use a separate if statement
|
141
|
+
if ( rb_caller == ID2SYM( rb_intern( "new" ) ) ) {
|
142
|
+
c_backtrace_index++;
|
143
|
+
rb_backtrace_frame_hash = rb_ary_entry( rb_backtrace_array, c_backtrace_index );
|
144
|
+
rb_caller = rb_hash_aref( rb_backtrace_frame_hash,
|
145
|
+
ID2SYM( rb_intern( "method" ) ) );
|
146
|
+
}
|
147
|
+
|
148
|
+
// assuming we have a previous frame, return its rb_self (our current receiver's sender)
|
149
|
+
return rb_caller;
|
150
|
+
}
|
151
|
+
|
@@ -0,0 +1,8 @@
|
|
1
|
+
|
2
|
+
#ifndef RP_SENDER_CALLER_GLOBAL_INTERNAL
|
3
|
+
#define RP_SENDER_CALLER_GLOBAL_INTERNAL
|
4
|
+
|
5
|
+
rb_control_frame_t* RPRuby_internal_framePriorTo();
|
6
|
+
rb_control_instruction_t* RPRuby_internal_instructionPriorTo( rb_control_instruction_t* c_control_instruction );
|
7
|
+
|
8
|
+
#endif
|
@@ -0,0 +1,683 @@
|
|
1
|
+
|
2
|
+
#include "rb_Kernel.h"
|
3
|
+
#include "rb_Kernel_internal.h"
|
4
|
+
|
5
|
+
#include "RPSender_internal.h"
|
6
|
+
|
7
|
+
#include "RubySourceSupport.h"
|
8
|
+
|
9
|
+
/***********
|
10
|
+
* Kernel *
|
11
|
+
***********/
|
12
|
+
|
13
|
+
void Init_senderKernel() {
|
14
|
+
|
15
|
+
rb_define_singleton_method( rb_mKernel, "backtrace", rb_RPRuby_Sender_Kernel_backtrace, -1 );
|
16
|
+
rb_define_singleton_method( rb_mKernel, "each_backtrace_frame", rb_RPRuby_Sender_Kernel_each_backtrace_frame, -1 );
|
17
|
+
rb_define_singleton_method( rb_mKernel, "backtrace_includes?", rb_RPRuby_Sender_Kernel_backtrace_includes, -1 );
|
18
|
+
rb_define_singleton_method( rb_mKernel, "backtrace_includes_one_of?", rb_RPRuby_Sender_Kernel_backtrace_includes_one_of, -1 );
|
19
|
+
rb_define_singleton_method( rb_mKernel, "backtrace_frame_with", rb_RPRuby_Sender_Kernel_backtrace_frame_with, -1 );
|
20
|
+
rb_define_singleton_method( rb_mKernel, "backtrace_frames_with", rb_RPRuby_Sender_Kernel_backtrace_frames_with, -1 );
|
21
|
+
|
22
|
+
|
23
|
+
}
|
24
|
+
|
25
|
+
static VALUE rb_eFiberError;
|
26
|
+
|
27
|
+
#define RPRUBY_SENDER_ERROR_NO_ENUMERATORS "Enumerators not currently enabled due to difficulty with Fiber support."
|
28
|
+
|
29
|
+
/***************************************************************************************************************************************************************
|
30
|
+
****************************************************************************************************************************************************************
|
31
|
+
Ruby Kernel Methods
|
32
|
+
****************************************************************************************************************************************************************
|
33
|
+
***************************************************************************************************************************************************************/
|
34
|
+
|
35
|
+
/**********************
|
36
|
+
* Kernel.backtrace *
|
37
|
+
*********************/
|
38
|
+
|
39
|
+
/*
|
40
|
+
* call-seq:
|
41
|
+
* Kernel.backtrace( number_of_frames = nil ) -> [ { :object => object, :method => method }, ... ]
|
42
|
+
*
|
43
|
+
* Return array of hashes with object and method frame information for backtrace.
|
44
|
+
* Specifying number_of_frames will cause only the last number_of_frames to be returned.
|
45
|
+
* Kernel.backtrace returns all frames including the current context (__method__/__callee__).
|
46
|
+
*/
|
47
|
+
VALUE rb_RPRuby_Sender_Kernel_backtrace( int argc,
|
48
|
+
VALUE* args,
|
49
|
+
VALUE rb_self ) {
|
50
|
+
|
51
|
+
// Get max stack level from args if it is there
|
52
|
+
int c_max_stack_level = 0;
|
53
|
+
if ( argc ) {
|
54
|
+
c_max_stack_level = FIX2INT( args[ 0 ] );
|
55
|
+
|
56
|
+
// if max_stack_level is 0 return empty array
|
57
|
+
if ( c_max_stack_level == 0 ) {
|
58
|
+
return rb_ary_new();
|
59
|
+
}
|
60
|
+
// if max_stack_level < 0, throw error
|
61
|
+
else if ( c_max_stack_level < 0 ) {
|
62
|
+
rb_raise( rb_eArgError, RPRUBY_SENDER_ERROR_STACK_LEVEL_LESS_THAN_ZERO );
|
63
|
+
}
|
64
|
+
|
65
|
+
}
|
66
|
+
|
67
|
+
rb_thread_t* c_thread = GET_THREAD();
|
68
|
+
// Get the current frame - we're doing a backtrace, so our current working frame to start is the first previous thread
|
69
|
+
rb_control_frame_t* c_current_context_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( c_thread->cfp );
|
70
|
+
|
71
|
+
// c_top_of_control_frame describes the top edge of the stack trace
|
72
|
+
// set c_top_of_control_frame to the first frame in <main>
|
73
|
+
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 ) ) );
|
74
|
+
|
75
|
+
VALUE rb_return_array = rb_ary_new();
|
76
|
+
|
77
|
+
int c_stack_level = 0;
|
78
|
+
// for each control frame:
|
79
|
+
while ( c_current_context_frame < c_top_of_control_frame
|
80
|
+
&& ( argc == 0
|
81
|
+
|| c_stack_level < c_max_stack_level ) ) {
|
82
|
+
|
83
|
+
VALUE rb_frame_hash = rb_RPRuby_Sender_Kernel_internal_backtraceHashForControlFrame( & c_current_context_frame );
|
84
|
+
|
85
|
+
if ( rb_frame_hash == Qnil ) {
|
86
|
+
break;
|
87
|
+
}
|
88
|
+
|
89
|
+
// push hash to array
|
90
|
+
rb_ary_push( rb_return_array,
|
91
|
+
rb_frame_hash );
|
92
|
+
|
93
|
+
c_current_context_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( c_current_context_frame );
|
94
|
+
c_stack_level++;
|
95
|
+
}
|
96
|
+
|
97
|
+
return rb_return_array;
|
98
|
+
}
|
99
|
+
|
100
|
+
/*********************************
|
101
|
+
* Kernel.each_backtrace_frame *
|
102
|
+
********************************/
|
103
|
+
|
104
|
+
/*
|
105
|
+
* call-seq:
|
106
|
+
* Kernel.each_backtrace_frame( & block )
|
107
|
+
*
|
108
|
+
* Return array of hashes with object and method frame information for backtrace.
|
109
|
+
* Specifying number_of_frames will cause only the last number_of_frames to be returned.
|
110
|
+
* Kernel.backtrace returns all frames including the current context (__method__/__callee__).
|
111
|
+
*/
|
112
|
+
VALUE rb_RPRuby_Sender_Kernel_each_backtrace_frame( int argc,
|
113
|
+
VALUE* args,
|
114
|
+
VALUE rb_self ) {
|
115
|
+
|
116
|
+
rb_thread_t* c_thread = GET_THREAD();
|
117
|
+
// Get the current frame - we're doing a backtrace, so our current working frame to start is the first previous thread
|
118
|
+
rb_control_frame_t* c_current_context_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( RUBY_VM_PREVIOUS_CONTROL_FRAME( c_thread->cfp ) );
|
119
|
+
|
120
|
+
// c_top_of_control_frame describes the top edge of the stack trace
|
121
|
+
// set c_top_of_control_frame to the first frame in <main>
|
122
|
+
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 ) ) );
|
123
|
+
|
124
|
+
VALUE rb_stored_backtrace_array = Qnil;
|
125
|
+
|
126
|
+
// if we were passed a stored backtrace array, use it
|
127
|
+
if ( argc == 1
|
128
|
+
&& TYPE( args[ 0 ] ) == T_ARRAY ) {
|
129
|
+
rb_stored_backtrace_array = args[ 0 ];
|
130
|
+
}
|
131
|
+
|
132
|
+
// for each control frame:
|
133
|
+
while ( c_current_context_frame < c_top_of_control_frame ) {
|
134
|
+
|
135
|
+
VALUE rb_frame_hash;
|
136
|
+
// if we are using a stored backtrace we don't need to ask for a new hash
|
137
|
+
if ( rb_stored_backtrace_array == Qnil ) {
|
138
|
+
rb_frame_hash = rb_RPRuby_Sender_Kernel_internal_backtraceHashForControlFrame( & c_current_context_frame );
|
139
|
+
}
|
140
|
+
else {
|
141
|
+
rb_frame_hash = rb_ary_shift( rb_stored_backtrace_array );
|
142
|
+
}
|
143
|
+
|
144
|
+
if ( rb_frame_hash == Qnil ) {
|
145
|
+
break;
|
146
|
+
}
|
147
|
+
|
148
|
+
// if we try to iterate using an Enumerator we will lose our context
|
149
|
+
if ( ! rb_block_given_p() ) {
|
150
|
+
|
151
|
+
// we solve this by assuming that the desired context is the moment when each_backtrace_frame is called
|
152
|
+
// this allows us to store the backtrace and iterate it as we want
|
153
|
+
// the only downside is that we have to get the entire backtrace first in order to store it
|
154
|
+
rb_stored_backtrace_array = rb_RPRuby_Sender_Kernel_backtrace( 0,
|
155
|
+
NULL,
|
156
|
+
rb_self );
|
157
|
+
|
158
|
+
RETURN_ENUMERATOR( rb_self, 1, & rb_stored_backtrace_array );
|
159
|
+
}
|
160
|
+
|
161
|
+
// otherwise, yield the block
|
162
|
+
rb_yield( rb_frame_hash );
|
163
|
+
|
164
|
+
// only move the frame if we are not using a stored backtrace
|
165
|
+
if ( rb_stored_backtrace_array == Qnil ) {
|
166
|
+
c_current_context_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( c_current_context_frame );
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
return Qnil;
|
171
|
+
}
|
172
|
+
|
173
|
+
/********************************
|
174
|
+
* Kernel.backtrace_includes? *
|
175
|
+
*******************************/
|
176
|
+
|
177
|
+
/*
|
178
|
+
* call-seq:
|
179
|
+
* Kernel.backtrace_includes?( method_or_object, ... ) -> true or false
|
180
|
+
* Kernel.backtrace_includes?( number_of_frames, method_or_object, ... ) -> true or false
|
181
|
+
*
|
182
|
+
* Returns whether specified methods or objects or classes are in the current backtrace context.
|
183
|
+
* Kernel.backtrace_includes? begins with the prior frame, so asking if the backtrace includes the current method
|
184
|
+
* will only report true if the current method is part of the earlier call chain.
|
185
|
+
*/
|
186
|
+
VALUE rb_RPRuby_Sender_Kernel_backtrace_includes( int argc,
|
187
|
+
VALUE* args,
|
188
|
+
VALUE rb_self ) {
|
189
|
+
|
190
|
+
// this function is also used for
|
191
|
+
// * backtrace_includes_one_of?
|
192
|
+
// * backtrace_includes_frame?
|
193
|
+
// * backtrace_includes_one_of_frames?
|
194
|
+
|
195
|
+
// create tracking array
|
196
|
+
VALUE rb_tracking_array = rb_ary_new();
|
197
|
+
|
198
|
+
// populate tracking array with methods/objects
|
199
|
+
// optional - if first arg is Qtrue, we are looking for one of the args instead of all of the args
|
200
|
+
int c_which_arg = 0;
|
201
|
+
BOOL c_requires_all_items = TRUE;
|
202
|
+
if ( args[ 0 ] == Qnil
|
203
|
+
|| ( argc > 1
|
204
|
+
&& args[ 1 ] == Qnil ) ) {
|
205
|
+
c_which_arg++;
|
206
|
+
c_requires_all_items = FALSE;
|
207
|
+
}
|
208
|
+
BOOL c_return_frame = FALSE;
|
209
|
+
if ( args[ 0 ] == Qfalse
|
210
|
+
|| ( argc > 1
|
211
|
+
&& args[ 1 ] == Qfalse ) ) {
|
212
|
+
c_which_arg++;
|
213
|
+
c_return_frame = TRUE;
|
214
|
+
}
|
215
|
+
BOOL c_return_all_frames = FALSE;
|
216
|
+
if ( args[ 0 ] == Qtrue
|
217
|
+
|| ( argc > 1
|
218
|
+
&& args[ 1 ] == Qtrue ) ) {
|
219
|
+
c_which_arg++;
|
220
|
+
c_return_all_frames = TRUE;
|
221
|
+
}
|
222
|
+
int c_args_offset = c_which_arg;
|
223
|
+
for ( ; c_which_arg < argc ; c_which_arg++ ) {
|
224
|
+
rb_ary_push( rb_tracking_array,
|
225
|
+
args[ c_which_arg ] );
|
226
|
+
}
|
227
|
+
|
228
|
+
rb_thread_t* c_thread = GET_THREAD();
|
229
|
+
// Get the current frame - we're doing a backtrace, so our current working frame to start is the first previous thread
|
230
|
+
rb_control_frame_t* c_current_context_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( RUBY_VM_PREVIOUS_CONTROL_FRAME( c_thread->cfp ) );
|
231
|
+
|
232
|
+
// c_top_of_control_frame describes the top edge of the stack trace
|
233
|
+
// set c_top_of_control_frame to the first frame in <main>
|
234
|
+
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 ) ) );
|
235
|
+
|
236
|
+
VALUE rb_test_index_array = rb_ary_new();
|
237
|
+
// :object
|
238
|
+
// instance or class
|
239
|
+
rb_ary_push( rb_test_index_array,
|
240
|
+
ID2SYM( rb_intern( "object" ) ) );
|
241
|
+
// :method
|
242
|
+
rb_ary_push( rb_test_index_array,
|
243
|
+
ID2SYM( rb_intern( "method" ) ) );
|
244
|
+
// :file
|
245
|
+
rb_ary_push( rb_test_index_array,
|
246
|
+
ID2SYM( rb_intern( "file" ) ) );
|
247
|
+
// :line
|
248
|
+
rb_ary_push( rb_test_index_array,
|
249
|
+
ID2SYM( rb_intern( "line" ) ) );
|
250
|
+
|
251
|
+
// only used if c_return_all_frames == TRUE
|
252
|
+
VALUE rb_frame_hashes_array = Qnil;
|
253
|
+
if ( c_return_all_frames == TRUE ) {
|
254
|
+
rb_frame_hashes_array = rb_ary_new();
|
255
|
+
}
|
256
|
+
|
257
|
+
VALUE rb_frame_hash = Qnil;
|
258
|
+
|
259
|
+
// for each control frame:
|
260
|
+
while ( c_current_context_frame < c_top_of_control_frame ) {
|
261
|
+
|
262
|
+
// iterate each array member
|
263
|
+
int c_which_member;
|
264
|
+
for ( c_which_member = 0 ; c_which_member < RARRAY_LEN( rb_tracking_array ) ; c_which_member++ ) {
|
265
|
+
|
266
|
+
VALUE rb_this_arg = args[ c_which_member + c_args_offset ];
|
267
|
+
|
268
|
+
BOOL matched = FALSE;
|
269
|
+
|
270
|
+
rb_frame_hash = rb_RPRuby_Sender_Kernel_internal_backtraceHashForControlFrame( & c_current_context_frame );
|
271
|
+
|
272
|
+
// if we have a hash we are testing multiple items in a frame
|
273
|
+
if ( TYPE( rb_this_arg ) == T_HASH ) {
|
274
|
+
|
275
|
+
VALUE rb_frame_test_array = rb_obj_clone( rb_test_index_array );
|
276
|
+
|
277
|
+
// for each element that we could test for
|
278
|
+
int c_which_index;
|
279
|
+
int c_skipped_index_count = 0;
|
280
|
+
for ( c_which_index = 0 ; c_which_index < RARRAY_LEN( rb_frame_test_array ) ; c_which_index++ ) {
|
281
|
+
|
282
|
+
VALUE rb_this_index = RARRAY_PTR( rb_frame_test_array )[ c_which_index ];
|
283
|
+
|
284
|
+
// see if our requested test hash includes the potential test element
|
285
|
+
if ( rb_hash_lookup( rb_this_arg,
|
286
|
+
rb_this_index ) != Qnil ) {
|
287
|
+
|
288
|
+
VALUE rb_required_element = rb_hash_aref( rb_this_arg,
|
289
|
+
rb_this_index );
|
290
|
+
VALUE rb_frame_element = rb_hash_aref( rb_frame_hash,
|
291
|
+
rb_this_index );
|
292
|
+
|
293
|
+
// if it does, we need to see if the current frame's element matches this element
|
294
|
+
VALUE rb_required_element_klass;
|
295
|
+
if ( rb_required_element == rb_frame_element
|
296
|
+
// if we have a string, which is a filename
|
297
|
+
|| ( TYPE( rb_required_element ) == T_STRING
|
298
|
+
&& rb_funcall( rb_frame_element, rb_intern( "==" ), 1, rb_required_element ) == Qtrue )
|
299
|
+
// if we have a class, which is a special case for :object
|
300
|
+
|| ( rb_this_index == ID2SYM( rb_intern( "class" ) )
|
301
|
+
&& ( rb_required_element_klass = ( ( TYPE( rb_required_element ) == T_CLASS ) ? rb_required_element : rb_funcall( rb_required_element, rb_intern( "class" ), 0 ) ) )
|
302
|
+
&& rb_required_element_klass == rb_required_element ) ) {
|
303
|
+
|
304
|
+
rb_ary_delete_at( rb_frame_test_array,
|
305
|
+
c_which_index );
|
306
|
+
c_which_index--;
|
307
|
+
}
|
308
|
+
}
|
309
|
+
else {
|
310
|
+
c_skipped_index_count++;
|
311
|
+
}
|
312
|
+
|
313
|
+
if ( RARRAY_LEN( rb_frame_test_array ) == c_skipped_index_count ) {
|
314
|
+
if ( c_return_frame == TRUE ) {
|
315
|
+
return rb_frame_hash;
|
316
|
+
}
|
317
|
+
else if ( c_return_all_frames == TRUE ) {
|
318
|
+
rb_ary_push( rb_frame_hashes_array,
|
319
|
+
rb_frame_hash );
|
320
|
+
}
|
321
|
+
else {
|
322
|
+
return Qtrue;
|
323
|
+
}
|
324
|
+
}
|
325
|
+
}
|
326
|
+
}
|
327
|
+
else {
|
328
|
+
|
329
|
+
// :object => <class:instance>
|
330
|
+
if ( TYPE( rb_this_arg ) == T_OBJECT ) {
|
331
|
+
|
332
|
+
if ( rb_hash_aref( rb_frame_hash,
|
333
|
+
ID2SYM( rb_intern( "object" ) ) ) == rb_this_arg ) {
|
334
|
+
matched = TRUE;
|
335
|
+
}
|
336
|
+
}
|
337
|
+
// :object => <class>
|
338
|
+
else if ( TYPE( rb_this_arg ) == T_CLASS ) {
|
339
|
+
|
340
|
+
VALUE rb_frame_object = rb_hash_aref( rb_frame_hash,
|
341
|
+
ID2SYM( rb_intern( "object" ) ) );
|
342
|
+
VALUE rb_frame_object_klass = TYPE( rb_frame_object ) == T_CLASS ? rb_frame_object : rb_funcall( rb_frame_object, rb_intern( "class" ), 0 );
|
343
|
+
if ( rb_frame_object_klass == rb_this_arg ) {
|
344
|
+
matched = TRUE;
|
345
|
+
}
|
346
|
+
}
|
347
|
+
// :method => :method
|
348
|
+
else if ( TYPE( rb_this_arg ) == T_SYMBOL ) {
|
349
|
+
|
350
|
+
if ( rb_hash_aref( rb_frame_hash,
|
351
|
+
ID2SYM( rb_intern( "method" ) ) ) == rb_this_arg ) {
|
352
|
+
matched = TRUE;
|
353
|
+
}
|
354
|
+
}
|
355
|
+
// :file => "filename"
|
356
|
+
else if ( TYPE( rb_this_arg ) == T_STRING ) {
|
357
|
+
VALUE rb_filename = rb_hash_aref( rb_frame_hash,
|
358
|
+
ID2SYM( rb_intern( "file" ) ) );
|
359
|
+
VALUE rb_comparison = rb_funcall( rb_filename, rb_intern( "==" ), 1, rb_this_arg );
|
360
|
+
if ( rb_comparison == Qtrue ) {
|
361
|
+
matched = TRUE;
|
362
|
+
}
|
363
|
+
}
|
364
|
+
// :line => number
|
365
|
+
else if ( TYPE( rb_this_arg ) == T_FIXNUM ) {
|
366
|
+
if ( rb_hash_aref( rb_frame_hash,
|
367
|
+
ID2SYM( rb_intern( "line" ) ) ) == rb_this_arg ) {
|
368
|
+
matched = TRUE;
|
369
|
+
}
|
370
|
+
}
|
371
|
+
|
372
|
+
// if array member exists in frame, remove from array
|
373
|
+
if ( matched ) {
|
374
|
+
if ( c_requires_all_items == FALSE ) {
|
375
|
+
if ( c_return_frame == TRUE ) {
|
376
|
+
return rb_frame_hash;
|
377
|
+
}
|
378
|
+
else {
|
379
|
+
return Qtrue;
|
380
|
+
}
|
381
|
+
|
382
|
+
}
|
383
|
+
else {
|
384
|
+
|
385
|
+
// delete this index
|
386
|
+
rb_ary_delete_at( rb_tracking_array,
|
387
|
+
c_which_member );
|
388
|
+
|
389
|
+
// decrement the loop iterator so that the increase is offset
|
390
|
+
// this is necessary since we just removed an index and are iterating vs. the length of the array
|
391
|
+
c_which_member--;
|
392
|
+
}
|
393
|
+
}
|
394
|
+
}
|
395
|
+
}
|
396
|
+
|
397
|
+
// if array is empty, return true
|
398
|
+
// we check here as well as at the end so we can stop iterating the backtrace if we find all our items
|
399
|
+
if ( RARRAY_LEN( rb_tracking_array ) == 0 ) {
|
400
|
+
if ( c_return_frame == TRUE ) {
|
401
|
+
return rb_frame_hash;
|
402
|
+
}
|
403
|
+
else if ( c_return_all_frames == TRUE ) {
|
404
|
+
rb_ary_push( rb_frame_hashes_array,
|
405
|
+
rb_frame_hash );
|
406
|
+
return rb_frame_hashes_array;
|
407
|
+
}
|
408
|
+
else {
|
409
|
+
return Qtrue;
|
410
|
+
}
|
411
|
+
}
|
412
|
+
c_current_context_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( c_current_context_frame );
|
413
|
+
}
|
414
|
+
|
415
|
+
if ( c_return_all_frames == TRUE
|
416
|
+
&& RARRAY_LEN( rb_frame_hashes_array ) > 0 ) {
|
417
|
+
return rb_frame_hashes_array;
|
418
|
+
}
|
419
|
+
// if we finish iterating frames and still have items in the array, return false
|
420
|
+
else if ( RARRAY_LEN( rb_tracking_array ) > 0 ) {
|
421
|
+
if ( c_return_frame == TRUE ) {
|
422
|
+
return Qnil;
|
423
|
+
}
|
424
|
+
else {
|
425
|
+
return Qfalse;
|
426
|
+
}
|
427
|
+
}
|
428
|
+
// otherwise, return true
|
429
|
+
else if ( c_return_frame == TRUE ) {
|
430
|
+
return rb_frame_hash;
|
431
|
+
}
|
432
|
+
else {
|
433
|
+
return Qtrue;
|
434
|
+
}
|
435
|
+
// we don't get here
|
436
|
+
return Qnil;
|
437
|
+
}
|
438
|
+
|
439
|
+
/***************************************
|
440
|
+
* Kernel.backtrace_includes_one_of? *
|
441
|
+
**************************************/
|
442
|
+
|
443
|
+
/*
|
444
|
+
* call-seq:
|
445
|
+
* Kernel.backtrace_includes_one_of?( method_or_object, ... ) -> true or false
|
446
|
+
* Kernel.backtrace_includes_one_of?( number_of_frames, method_or_object, ... ) -> true or false
|
447
|
+
*
|
448
|
+
* Returns whether one of the specified methods or objects or classes are in the current backtrace context.
|
449
|
+
* Kernel.backtrace_includes_one_of? begins with the prior frame, so asking if the backtrace includes the current method
|
450
|
+
* will only report true if the current method is part of the earlier call chain.
|
451
|
+
*/
|
452
|
+
VALUE rb_RPRuby_Sender_Kernel_backtrace_includes_one_of( int argc,
|
453
|
+
VALUE* args,
|
454
|
+
VALUE rb_self ) {
|
455
|
+
|
456
|
+
// method implemented as special case of backtrace_includes?
|
457
|
+
|
458
|
+
// create a new array starting with Qtrue and then followed by args
|
459
|
+
VALUE c_methods_array[ argc + 1 ];
|
460
|
+
c_methods_array[ 0 ] = Qnil;
|
461
|
+
int c_which_arg;
|
462
|
+
for ( c_which_arg = 0 ; c_which_arg < argc ; c_which_arg++ ) {
|
463
|
+
c_methods_array[ c_which_arg + 1 ] = args[ c_which_arg ];
|
464
|
+
}
|
465
|
+
return rb_RPRuby_Sender_Kernel_backtrace_includes( argc + 1,
|
466
|
+
c_methods_array,
|
467
|
+
rb_self );
|
468
|
+
}
|
469
|
+
|
470
|
+
/*********************************
|
471
|
+
* Kernel.backtrace_frame_with *
|
472
|
+
********************************/
|
473
|
+
|
474
|
+
/*
|
475
|
+
* call-seq:
|
476
|
+
* Kernel.backtrace_frame_with( method_or_object, ... ) -> true or false
|
477
|
+
* Kernel.backtrace_frame_with( number_of_frames, method_or_object, ... ) -> true or false
|
478
|
+
*
|
479
|
+
* Returns first frame matching specifications, which work like Kernel.backtrace_includes?.
|
480
|
+
* Kernel.backtrace_includes_one_of? begins with the prior frame, so asking if the backtrace includes the current method
|
481
|
+
* will only report true if the current method is part of the earlier call chain.
|
482
|
+
*/
|
483
|
+
VALUE rb_RPRuby_Sender_Kernel_backtrace_frame_with( int argc,
|
484
|
+
VALUE* args,
|
485
|
+
VALUE rb_self ) {
|
486
|
+
|
487
|
+
// create a new array starting with Qtrue and then followed by args
|
488
|
+
VALUE c_methods_array[ argc + 1 ];
|
489
|
+
// Qfalse tells backtrace_includes to return frame hash instead of true/false
|
490
|
+
c_methods_array[ 0 ] = Qfalse;
|
491
|
+
int c_which_arg;
|
492
|
+
for ( c_which_arg = 0 ; c_which_arg < argc ; c_which_arg++ ) {
|
493
|
+
c_methods_array[ c_which_arg + 1 ] = args[ c_which_arg ];
|
494
|
+
}
|
495
|
+
return rb_RPRuby_Sender_Kernel_backtrace_includes_one_of( argc + 1,
|
496
|
+
c_methods_array,
|
497
|
+
rb_self );
|
498
|
+
}
|
499
|
+
|
500
|
+
/**********************************
|
501
|
+
* Kernel.backtrace_frames_with *
|
502
|
+
*********************************/
|
503
|
+
|
504
|
+
/*
|
505
|
+
* call-seq:
|
506
|
+
* Kernel.backtrace_frames_with( method_or_object, ... ) -> true or false
|
507
|
+
* Kernel.backtrace_frames_with( number_of_frames, method_or_object, ... ) -> true or false
|
508
|
+
*
|
509
|
+
* Returns all frames matching specifications, which work like Kernel.backtrace_includes?.
|
510
|
+
* Kernel.backtrace_includes_one_of? begins with the prior frame, so asking if the backtrace includes the current method
|
511
|
+
* will only report true if the current method is part of the earlier call chain.
|
512
|
+
*/
|
513
|
+
VALUE rb_RPRuby_Sender_Kernel_backtrace_frames_with( int argc,
|
514
|
+
VALUE* args,
|
515
|
+
VALUE rb_self ) {
|
516
|
+
|
517
|
+
|
518
|
+
// create a new array starting with Qtrue and then followed by args
|
519
|
+
VALUE c_methods_array[ argc + 1 ];
|
520
|
+
// Qtrue tells backtrace_includes to return all frame hashes specified instead of the first
|
521
|
+
c_methods_array[ 0 ] = Qtrue;
|
522
|
+
int c_which_arg;
|
523
|
+
for ( c_which_arg = 0 ; c_which_arg < argc ; c_which_arg++ ) {
|
524
|
+
c_methods_array[ c_which_arg + 1 ] = args[ c_which_arg ];
|
525
|
+
}
|
526
|
+
return rb_RPRuby_Sender_Kernel_backtrace_includes( argc + 1,
|
527
|
+
c_methods_array,
|
528
|
+
rb_self );
|
529
|
+
}
|
530
|
+
|
531
|
+
/***************************************************************************************************************************************************************
|
532
|
+
****************************************************************************************************************************************************************
|
533
|
+
Internal Methods
|
534
|
+
****************************************************************************************************************************************************************
|
535
|
+
***************************************************************************************************************************************************************/
|
536
|
+
|
537
|
+
/**********************************
|
538
|
+
* backtraceHashForControlFrame *
|
539
|
+
*********************************/
|
540
|
+
|
541
|
+
VALUE rb_RPRuby_Sender_Kernel_internal_backtraceHashForControlFrame( rb_control_frame_t** c_current_frame ) {
|
542
|
+
|
543
|
+
const char* c_method_name = NULL;
|
544
|
+
int c_sourcefile_line = 0;
|
545
|
+
|
546
|
+
// create new hash for this frame
|
547
|
+
VALUE rb_frame_hash = rb_hash_new();
|
548
|
+
|
549
|
+
VALUE rb_sourcefile_name = Qnil;
|
550
|
+
VALUE rb_sourcefile_line = Qnil;
|
551
|
+
VALUE rb_method_name = Qnil;
|
552
|
+
VALUE rb_object_for_frame = Qnil;
|
553
|
+
|
554
|
+
if ( ( *c_current_frame )->iseq != 0 ) {
|
555
|
+
|
556
|
+
if ( ( *c_current_frame )->pc != 0 ) {
|
557
|
+
|
558
|
+
rb_iseq_t *iseq = ( *c_current_frame )->iseq;
|
559
|
+
|
560
|
+
// get sourcefile name and set in hash
|
561
|
+
rb_sourcefile_name = iseq->filename;
|
562
|
+
|
563
|
+
// get sourcefile line and set in hash
|
564
|
+
c_sourcefile_line = rb_vm_get_sourceline( *c_current_frame );
|
565
|
+
rb_sourcefile_line = INT2FIX( c_sourcefile_line );
|
566
|
+
|
567
|
+
// get name of instruction sequence
|
568
|
+
rb_method_name = ID2SYM( rb_intern( StringValuePtr( iseq->name ) ) );
|
569
|
+
rb_object_for_frame = ( *c_current_frame )->self;
|
570
|
+
}
|
571
|
+
}
|
572
|
+
else if ( RUBYVM_CFUNC_FRAME_P( *c_current_frame ) ) {
|
573
|
+
|
574
|
+
// get name of method
|
575
|
+
|
576
|
+
#if RUBY_PATCHLEVEL >= -1
|
577
|
+
// For 1.9.2:
|
578
|
+
const rb_method_entry_t* c_method_for_frame = ( *c_current_frame )->me;
|
579
|
+
c_method_name = rb_id2name( c_method_for_frame->called_id );
|
580
|
+
#else
|
581
|
+
// For 1.9.1:
|
582
|
+
c_method_name = rb_id2name( ( *c_current_frame )->method_id );
|
583
|
+
#endif
|
584
|
+
|
585
|
+
rb_method_name = ( c_method_name == NULL ? Qnil : ID2SYM( rb_intern( c_method_name ) ) );
|
586
|
+
rb_object_for_frame = ( *c_current_frame )->self;
|
587
|
+
}
|
588
|
+
// we have to test this case - it works for blocks but there may be other cases too
|
589
|
+
else if ( ( *c_current_frame )->block_iseq != 0
|
590
|
+
&& ( *c_current_frame )->pc == 0) {
|
591
|
+
|
592
|
+
// If we got here we have a fiber
|
593
|
+
// There doesn't seem to be much that we can tell about a fiber's context
|
594
|
+
|
595
|
+
VALUE rb_current_fiber = rb_fiber_current();
|
596
|
+
rb_fiber_t* c_current_fiber = NULL;
|
597
|
+
|
598
|
+
GetFiberPtr( rb_current_fiber,
|
599
|
+
c_current_fiber);
|
600
|
+
|
601
|
+
rb_context_t* c_context = & c_current_fiber->cont;
|
602
|
+
|
603
|
+
// rb_block_t* c_blockptr = RUBY_VM_GET_BLOCK_PTR_IN_CFP( *c_current_frame );
|
604
|
+
|
605
|
+
rb_object_for_frame = ( *c_current_frame )->self;
|
606
|
+
|
607
|
+
// get sourcefile name and set in hash
|
608
|
+
rb_sourcefile_name = Qnil;
|
609
|
+
|
610
|
+
// get sourcefile line and set in hash
|
611
|
+
rb_sourcefile_line = Qnil;
|
612
|
+
|
613
|
+
// get name of instruction sequence
|
614
|
+
rb_method_name = rb_str_new2( "<Fiber>" );
|
615
|
+
|
616
|
+
// if we have a fiber we also include its ruby reference since we have so little other context
|
617
|
+
rb_hash_aset( rb_frame_hash,
|
618
|
+
ID2SYM( rb_intern( "fiber" ) ),
|
619
|
+
c_context->self );
|
620
|
+
|
621
|
+
// The one time that we know a fiber is in use in the Ruby base is with Enumerators
|
622
|
+
// For now we will handle that with a special case
|
623
|
+
|
624
|
+
VALUE rb_enumerator_class = rb_const_get( rb_cObject,
|
625
|
+
rb_intern( "Enumerator" ) );
|
626
|
+
|
627
|
+
VALUE rb_object_for_frame_klass = ( ( TYPE( rb_object_for_frame ) == T_CLASS ) ? rb_object_for_frame : rb_funcall( rb_object_for_frame, rb_intern( "class" ), 0 ) );
|
628
|
+
|
629
|
+
VALUE rb_ancestors = rb_funcall( rb_object_for_frame_klass,
|
630
|
+
rb_intern( "ancestors" ),
|
631
|
+
0 );
|
632
|
+
|
633
|
+
if ( rb_ary_includes( rb_ancestors,
|
634
|
+
rb_enumerator_class ) ) {
|
635
|
+
|
636
|
+
struct enumerator* c_enumerator = enumerator_ptr( rb_object_for_frame );
|
637
|
+
|
638
|
+
rb_object_for_frame = c_enumerator->obj;
|
639
|
+
rb_method_name = ID2SYM( c_enumerator->meth );
|
640
|
+
}
|
641
|
+
|
642
|
+
}
|
643
|
+
else if ( ( *c_current_frame )->block_iseq == 0
|
644
|
+
&& ( *c_current_frame )->pc == 0) {
|
645
|
+
// this happens after we had a fiber and we try to go up - which doesn't make sense with a fiber
|
646
|
+
// not sure what we want to do here, if anything
|
647
|
+
return Qnil;
|
648
|
+
}
|
649
|
+
else {
|
650
|
+
// The third possibility is that we have an iseq frame with nil params for what we want
|
651
|
+
// In that case we can simply return the next frame
|
652
|
+
*c_current_frame = RUBY_VM_PREVIOUS_CONTROL_FRAME( *c_current_frame );
|
653
|
+
|
654
|
+
// in theory this could crash because we are going forward a frame when we don't know what's there
|
655
|
+
// in practice I think we are ok, since we are only jumping forward from nil frames which should never be at the end
|
656
|
+
// at least - I don't think they should... we shall see.
|
657
|
+
//
|
658
|
+
// a fix would be to check the next frame, but that requires access to the thread or the limit cfp,
|
659
|
+
// which requires passing more context; so for now, I'm leaving it there
|
660
|
+
|
661
|
+
return rb_RPRuby_Sender_Kernel_internal_backtraceHashForControlFrame( c_current_frame );
|
662
|
+
}
|
663
|
+
|
664
|
+
// Push values to return hash
|
665
|
+
|
666
|
+
rb_hash_aset( rb_frame_hash,
|
667
|
+
ID2SYM( rb_intern( "object" ) ),
|
668
|
+
rb_object_for_frame );
|
669
|
+
|
670
|
+
rb_hash_aset( rb_frame_hash,
|
671
|
+
ID2SYM( rb_intern( "file" ) ),
|
672
|
+
rb_sourcefile_name );
|
673
|
+
|
674
|
+
rb_hash_aset( rb_frame_hash,
|
675
|
+
ID2SYM( rb_intern( "line" ) ),
|
676
|
+
rb_sourcefile_line );
|
677
|
+
|
678
|
+
rb_hash_aset( rb_frame_hash,
|
679
|
+
ID2SYM( rb_intern( "method" ) ),
|
680
|
+
rb_method_name );
|
681
|
+
|
682
|
+
return rb_frame_hash;
|
683
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
#ifndef RP_SENDER_KERNEL
|
3
|
+
#define RP_SENDER_KERNEL
|
4
|
+
|
5
|
+
#include "ruby.h"
|
6
|
+
|
7
|
+
#define RPRUBY_SENDER_ERROR_STACK_LEVEL_LESS_THAN_ZERO "Maximum level for stack trace specified was less than zero."
|
8
|
+
|
9
|
+
void Init_senderKernel();
|
10
|
+
|
11
|
+
VALUE rb_RPRuby_Sender_Kernel_backtrace( int argc,
|
12
|
+
VALUE* args,
|
13
|
+
VALUE rb_self );
|
14
|
+
VALUE rb_RPRuby_Sender_Kernel_each_backtrace_frame( int argc,
|
15
|
+
VALUE* args,
|
16
|
+
VALUE rb_self );
|
17
|
+
VALUE rb_RPRuby_Sender_Kernel_backtrace_includes( int argc,
|
18
|
+
VALUE* args,
|
19
|
+
VALUE rb_self );
|
20
|
+
VALUE rb_RPRuby_Sender_Kernel_backtrace_includes_one_of( int argc,
|
21
|
+
VALUE* args,
|
22
|
+
VALUE rb_self );
|
23
|
+
VALUE rb_RPRuby_Sender_Kernel_backtrace_frame_with( int argc,
|
24
|
+
VALUE* args,
|
25
|
+
VALUE rb_self );
|
26
|
+
VALUE rb_RPRuby_Sender_Kernel_backtrace_frames_with( int argc,
|
27
|
+
VALUE* args,
|
28
|
+
VALUE rb_self );
|
29
|
+
|
30
|
+
|
31
|
+
#endif
|
data/ext/sender/sender.c
ADDED
data/test/test_sender.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "sender"
|
3
|
+
|
4
|
+
class TestSender < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test___sender__
|
7
|
+
assert_equal self, __sender__
|
8
|
+
end
|
9
|
+
|
10
|
+
def test___caller__
|
11
|
+
assert_equal :run, __caller__
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_Kernel_backtrace
|
15
|
+
# not sure how to test this
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_Kernel_backtrace_includes
|
19
|
+
# not sure how to test this
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 1
|
7
7
|
- 5
|
8
|
-
-
|
9
|
-
version: 1.5.
|
8
|
+
- 8
|
9
|
+
version: 1.5.8
|
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:
|
17
|
+
date: 2011-02-12 00:00:00 -05:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -65,10 +65,20 @@ extensions:
|
|
65
65
|
extra_rdoc_files: []
|
66
66
|
|
67
67
|
files:
|
68
|
-
- lib/sender.rb
|
69
|
-
- lib/VERSION.rdoc
|
70
|
-
- README.rdoc
|
71
68
|
- ext/sender/extconf.rb
|
69
|
+
- ext/sender/rb_Global.c
|
70
|
+
- ext/sender/rb_Global.h
|
71
|
+
- ext/sender/rb_Global_internal.h
|
72
|
+
- ext/sender/rb_Kernel.c
|
73
|
+
- ext/sender/rb_Kernel.h
|
74
|
+
- ext/sender/rb_Kernel_internal.h
|
75
|
+
- ext/sender/RPSender_internal.c
|
76
|
+
- ext/sender/RPSender_internal.h
|
77
|
+
- ext/sender/RubySourceSupport.c
|
78
|
+
- ext/sender/RubySourceSupport.h
|
79
|
+
- ext/sender/sender.c
|
80
|
+
- test/test_sender.rb
|
81
|
+
- README.rdoc
|
72
82
|
has_rdoc: true
|
73
83
|
homepage: http://rubygems.org/gems/sender
|
74
84
|
licenses: []
|
data/lib/VERSION.rdoc
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
1.5.4
|