binding_of_caller 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,6 +6,27 @@
6
6
 
7
7
  typedef enum { false, true } bool;
8
8
 
9
+ static VALUE
10
+ string2sym(const char * string)
11
+ {
12
+ return ID2SYM(rb_intern(string));
13
+ }
14
+
15
+ static inline const rb_data_type_t *
16
+ threadptr_data_type(void)
17
+ {
18
+ static const rb_data_type_t *thread_data_type;
19
+ if (!thread_data_type) {
20
+ VALUE current_thread = rb_thread_current();
21
+ thread_data_type = RTYPEDDATA_TYPE(current_thread);
22
+ }
23
+ return thread_data_type;
24
+ }
25
+
26
+ #define ruby_thread_data_type *threadptr_data_type()
27
+
28
+ #define ruby_current_thread ((rb_thread_t *)RTYPEDDATA_DATA(rb_thread_current()))
29
+
9
30
  static size_t
10
31
  binding_memsize(const void *ptr)
11
32
  {
@@ -54,27 +75,29 @@ binding_alloc(VALUE klass)
54
75
  }
55
76
 
56
77
  static bool valid_frame_p(rb_control_frame_t * cfp, rb_control_frame_t * limit_cfp) {
57
- if (cfp >= limit_cfp)
58
- rb_raise(rb_eRuntimeError, "Invalid frame, gone beyond end of stack!");
59
-
60
78
  return cfp->iseq && !NIL_P(cfp->self);
61
79
  }
62
80
 
63
81
  static rb_control_frame_t * find_valid_frame(rb_control_frame_t * cfp, rb_control_frame_t * limit_cfp) {
64
- while (true) {
82
+ while (cfp < limit_cfp) {
65
83
  cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
66
84
 
85
+ if (cfp >= limit_cfp)
86
+ return NULL;
87
+
67
88
  if (valid_frame_p(cfp, limit_cfp))
68
89
  return cfp;
69
90
  }
70
91
 
71
- // never reached
72
- return 0;
92
+ // beyond end of stack
93
+ return NULL;
73
94
  }
74
95
 
75
96
  static VALUE binding_of_caller(VALUE self, VALUE rb_level)
76
97
  {
77
- rb_thread_t *th = GET_THREAD();
98
+ rb_thread_t *th;
99
+ GetThreadPtr(rb_thread_current(), th);
100
+
78
101
  rb_control_frame_t *cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp);
79
102
  rb_control_frame_t *limit_cfp = (void *)(th->stack + th->stack_size);
80
103
  int level = FIX2INT(rb_level);
@@ -83,6 +106,9 @@ static VALUE binding_of_caller(VALUE self, VALUE rb_level)
83
106
  for (int i = 0; i < level; i++) {
84
107
  cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
85
108
 
109
+ if (cfp >= limit_cfp)
110
+ rb_raise(rb_eRuntimeError, "Invalid frame, gone beyond end of stack!");
111
+
86
112
  // skip invalid frames
87
113
  if (!valid_frame_p(cfp, limit_cfp))
88
114
  cfp = find_valid_frame(cfp, limit_cfp);
@@ -98,15 +124,82 @@ static VALUE binding_of_caller(VALUE self, VALUE rb_level)
98
124
  bind->env = rb_vm_make_env_object(th, cfp);
99
125
  bind->filename = cfp->iseq->filename;
100
126
  bind->line_no = rb_vm_get_sourceline(cfp);
127
+
128
+ rb_iv_set(bindval, "@frame_type", cfp->flag);
101
129
  return bindval;
102
130
  }
103
131
 
132
+
133
+ static VALUE
134
+ frametype_name(VALUE flag)
135
+ {
136
+ switch (flag & VM_FRAME_MAGIC_MASK) {
137
+ case VM_FRAME_MAGIC_METHOD: return string2sym("method");
138
+ case VM_FRAME_MAGIC_BLOCK: return string2sym("block");
139
+ case VM_FRAME_MAGIC_CLASS: return string2sym("class");
140
+ case VM_FRAME_MAGIC_TOP: return string2sym("top");
141
+ case VM_FRAME_MAGIC_FINISH: return string2sym("finish");
142
+ case VM_FRAME_MAGIC_CFUNC: return string2sym("cfunc");
143
+ case VM_FRAME_MAGIC_PROC: return string2sym("proc");
144
+ case VM_FRAME_MAGIC_IFUNC: return string2sym("ifunc");
145
+ case VM_FRAME_MAGIC_EVAL: return string2sym("eval");
146
+ case VM_FRAME_MAGIC_LAMBDA: return string2sym("lambda");
147
+ default:
148
+ rb_raise(rb_eRuntimeError, "frame_type can only be returned for bindings created with Binding#of_caller().");
149
+ }
150
+ }
151
+
152
+ static VALUE
153
+ frame_type(VALUE self)
154
+ {
155
+ return frametype_name(rb_iv_get(self, "@frame_type"));
156
+ }
157
+
158
+ static VALUE frame_count(VALUE self)
159
+ {
160
+ rb_thread_t *th;
161
+ GetThreadPtr(rb_thread_current(), th);
162
+
163
+ rb_control_frame_t *cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp);
164
+ rb_control_frame_t *limit_cfp = (void *)(th->stack + th->stack_size);
165
+
166
+ int i = 1;
167
+ while (cfp < limit_cfp) {
168
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
169
+
170
+ if (cfp >= limit_cfp)
171
+ return INT2FIX(i);
172
+
173
+ // skip invalid frames
174
+ if (!valid_frame_p(cfp, limit_cfp))
175
+ cfp = find_valid_frame(cfp, limit_cfp);
176
+
177
+ i++;
178
+ }
179
+
180
+ return INT2FIX(i);
181
+ }
182
+
183
+ static VALUE
184
+ callers(VALUE self)
185
+ {
186
+ VALUE ary = rb_ary_new();
187
+
188
+ for (int i = 0; i < FIX2INT(frame_count(self)); i++)
189
+ rb_ary_push(ary, binding_of_caller(self, INT2FIX(i)));
190
+
191
+ return ary;
192
+ }
193
+
104
194
  void
105
195
  Init_binding_of_caller()
106
196
  {
107
197
  VALUE mBindingOfCaller = rb_define_module("BindingOfCaller");
108
198
 
109
199
  rb_define_method(mBindingOfCaller, "of_caller", binding_of_caller, 1);
200
+ rb_define_method(mBindingOfCaller, "frame_count", frame_count, 0);
201
+ rb_define_method(mBindingOfCaller, "frame_type", frame_type, 0);
202
+ rb_define_method(mBindingOfCaller, "callers", callers, 0);
110
203
  rb_include_module(rb_cBinding, mBindingOfCaller);
111
204
  }
112
205
 
@@ -1,3 +1,3 @@
1
1
  module BindingOfCaller
2
- VERSION = "0.4.1"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -8,52 +8,87 @@ puts "Testing binding_of_caller version #{BindingOfCaller::VERSION}..."
8
8
  puts "Ruby version: #{RUBY_VERSION}"
9
9
 
10
10
  describe BindingOfCaller do
11
- it "should fetch immediate caller's binding when 0 is passed" do
12
- o = Object.new
13
- def o.a
14
- var = 1
15
- binding.of_caller(0).eval('var')
11
+ describe "of_caller" do
12
+ it "should fetch immediate caller's binding when 0 is passed" do
13
+ o = Object.new
14
+ def o.a
15
+ var = 1
16
+ binding.of_caller(0).eval('var')
17
+ end
18
+
19
+ o. a.should == 1
16
20
  end
17
21
 
18
- o. a.should == 1
19
- end
22
+ it "should fetch parent of caller's binding when 1 is passed" do
23
+ o = Object.new
24
+ def o.a
25
+ var = 1
26
+ b
27
+ end
20
28
 
21
- it "should fetch parent of caller's binding when 1 is passed" do
22
- o = Object.new
23
- def o.a
24
- var = 1
25
- b
26
- end
29
+ def o.b
30
+ binding.of_caller(1).eval('var')
31
+ end
27
32
 
28
- def o.b
29
- binding.of_caller(1).eval('var')
33
+ o.a.should == 1
30
34
  end
31
35
 
32
- o.a.should == 1
33
- end
36
+ it "should modify locals in parent of caller's binding" do
37
+ o = Object.new
38
+ def o.a
39
+ var = 1
40
+ b
41
+ var
42
+ end
43
+
44
+ def o.b
45
+ binding.of_caller(1).eval('var = 20')
46
+ end
34
47
 
35
- it "should modify locals in parent of caller's binding" do
36
- o = Object.new
37
- def o.a
38
- var = 1
39
- b
40
- var
48
+ o.a.should == 20
41
49
  end
42
50
 
43
- def o.b
44
- binding.of_caller(1).eval('var = 20')
51
+ it "should raise an exception when retrieving an out of band binding" do
52
+ o = Object.new
53
+ def o.a
54
+ binding.of_caller(100)
55
+ end
56
+
57
+ lambda { o.a }.should.raise RuntimeError
45
58
  end
59
+ end
46
60
 
47
- o.a.should == 20
61
+ describe "frame_count" do
62
+ it 'frame_count should equal callers.count' do
63
+ binding.frame_count.should == binding.callers.count
64
+ end
48
65
  end
49
66
 
50
- it "should raise an exception when retrieving an out of band binding" do
51
- o = Object.new
52
- def o.a
53
- binding.of_caller(100)
67
+ describe "frame_type" do
68
+ it 'should return the correct frame types' do
69
+ o = Object.new
70
+
71
+ # method frame
72
+ def o.a
73
+ b
74
+ end
75
+
76
+ # method frame
77
+ def o.b
78
+ # block frame
79
+ proc do
80
+ binding.callers
81
+ end.call
82
+ end
83
+ caller_bindings = o.a
84
+ caller_bindings[0].frame_type.should == :block
85
+ caller_bindings[1].frame_type.should == :method
86
+ caller_bindings[2].frame_type.should == :method
54
87
  end
55
88
 
56
- lambda { o.a }.should.raise RuntimeError
89
+ it 'should raise when invoked on an ordinary binding (i.e one not generated through Binding#of_caller)' do
90
+ lambda { binding.frame_type }.should.raise RuntimeError
91
+ end
57
92
  end
58
93
  end
59
94
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: binding_of_caller
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-22 00:00:00.000000000Z
12
+ date: 2011-11-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bacon
16
- requirement: &70343943988340 !ruby/object:Gem::Requirement
16
+ requirement: &70308421840640 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: 1.1.0
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70343943988340
24
+ version_requirements: *70308421840640
25
25
  description: Retrieve the binding of a method's caller. Can also retrieve bindings
26
26
  even further up the stack. Currently only works for MRI 1.9.2.
27
27
  email: jrmair@gmail.com
@@ -110,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
110
110
  version: '0'
111
111
  requirements: []
112
112
  rubyforge_project:
113
- rubygems_version: 1.8.6
113
+ rubygems_version: 1.8.11
114
114
  signing_key:
115
115
  specification_version: 3
116
116
  summary: Retrieve the binding of a method's caller. Can also retrieve bindings even