sender 1.3 → 1.4

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc 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 = -g
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?( :non_existing_function )
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?( :non_existing_function, Test2, self )
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
- Sender was: #<Test:0x0000010101ef18>
77
- Caller was: and_another_test_in_another_object
78
- [{:object=>#<Test:0x0000010101ef18>, :method=>:another_test},
79
- {:object=>#<Test:0x0000010101ef18>, :method=>:test},
80
- {:object=>main}]
81
- [{:object=>#<Test:0x0000010101ef18>, :method=>:another_test},
82
- {:object=>#<Test:0x0000010101ef18>, :method=>:test}]
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="1.3"
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
- * framePriorToCurrent *
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* c_control_frame );
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
+
@@ -7,5 +7,6 @@
7
7
 
8
8
  ID frame_func_id( rb_control_frame_t *cfp );
9
9
  ID rb_frame_caller(void);
10
+ int rb_vm_get_sourceline(const rb_control_frame_t *cfp);
10
11
 
11
12
  #endif
@@ -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
- // get the frame prior to current frame
39
- rb_control_frame_t* c_sending_frame = RPRuby_internal_framePriorTo( NULL );
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
- // make sure the current frame wasn't the first frame or return nil
42
- if ( c_sending_frame == NULL ) {
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 c_sending_frame->self;
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
- // get the frame prior to current frame
63
- rb_control_frame_t* c_sending_frame = RPRuby_internal_framePriorTo( NULL );
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
- // make sure the current frame wasn't the first frame or return nil
66
- if ( c_sending_frame == NULL ) {
67
- return Qnil;
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 ID2SYM( frame_func_id( c_sending_frame ) );
100
+ // assuming we have a previous frame, return its rb_self (our current receiver's sender)
101
+ return rb_caller;
71
102
  }
72
103
 
@@ -3,5 +3,6 @@
3
3
  #define RP_SENDER_CALLER_GLOBAL_INTERNAL
4
4
 
5
5
  rb_control_frame_t* RPRuby_internal_framePriorTo();
6
+ rb_control_instruction_t* RPRuby_internal_instructionPriorTo( rb_control_instruction_t* c_control_instruction );
6
7
 
7
8
  #endif
@@ -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
- * __backtrace__( number_of_frames = nil ) -> [ { :object => object, :method => method }, ... ]
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
- // create return array
41
- VALUE rb_return_array = rb_ary_new();
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
- // loop while we have a prior control frame
51
- while ( ( c_control_frame = RPRuby_internal_framePriorTo( c_control_frame ) ) != NULL
52
- // and as long as we either have no stack limit specified, or until we reach the stack limit
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
- // get hash of info for this frame
57
- VALUE rb_frame_hash = RPSender_internal_backtraceHashForControlFrame( c_control_frame );
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
- c_stack_level++;
68
- }
69
-
70
- // return return array
71
- return rb_return_array;
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
- * __backtrace_includes?( method_or_object, ... ) -> true or false
81
- * __backtrace_includes?( number_of_frames, method_or_object, ... ) -> true or false
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
- * Backtrace begins with the prior frame, so asking if the backtrace includes the current method
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
- // for each previous frame
102
- rb_control_frame_t* c_control_frame = NULL;
103
- while ( ( c_control_frame = RPRuby_internal_framePriorTo( c_control_frame ) ) != NULL ) {
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( c_control_frame->self ) ) {
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( c_control_frame ) ) {
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 == c_control_frame->self ) {
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
+ }
@@ -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 argc,
10
- VALUE* args,
11
- VALUE rb_self );
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 );
@@ -0,0 +1,10 @@
1
+
2
+ #ifndef RP_SENDER_KERNEL_INTERNAL
3
+ #define RP_SENDER_KERNEL_INTERNAL
4
+
5
+ #include "ruby.h"
6
+ #include "eval_intern.h"
7
+
8
+ VALUE rb_RPRuby_Sender_Kernel_internal_backtraceHashForControlFrame( rb_control_frame_t** c_current_frame );
9
+
10
+ #endif
Binary file
data/lib/sender.rb CHANGED
@@ -2,33 +2,6 @@ require 'sender/sender'
2
2
 
3
3
  module Sender
4
4
 
5
- # VERSION is set by hoe
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.3"
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-27}
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
@@ -11,4 +11,12 @@ class TestSender < Test::Unit::TestCase
11
11
  assert_equal :run, __caller__
12
12
  end
13
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
+
14
22
  end
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: 9
4
+ hash: 7
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
- - 3
9
- version: "1.3"
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-27 00:00:00 -04:00
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