sender 1.5.7 → 1.5.8
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/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
|