rb-threadframe 0.32

Sign up to get free protection for your applications and to get access to all the features.
data/ext/proc_extra.c ADDED
@@ -0,0 +1,108 @@
1
+ /*
2
+ * Copyright (C) 2010 Rocky Bernstein
3
+ */
4
+ #if 0 /* The following is to fake out rdoc, until I find a better fix. */
5
+ /*
6
+ * Additions to the RubyVM::Proc classes
7
+ */
8
+ VALUE rb_cProc = rb_define_class("Proc", ...)
9
+ #endif
10
+
11
+ #include "../include/vm_core_mini.h" /* Pulls in ruby.h and node.h */
12
+ #include "../include/ruby19_externs.h"
13
+
14
+ struct METHOD {
15
+ VALUE recv;
16
+ VALUE rclass;
17
+ ID id;
18
+ rb_method_entry_t me;
19
+ };
20
+
21
+ /*
22
+ * call-seq:
23
+ * Proc#iseq -> RubyVM::InstructionSequence
24
+ *
25
+ * Returns the instruction sequence for a Proc object.
26
+ */
27
+ VALUE
28
+ proc_iseq(VALUE self)
29
+ {
30
+ rb_proc_t *proc;
31
+ rb_iseq_t *iseq;
32
+ VALUE rb_iseq;
33
+ GetProcPtr(self, proc);
34
+ iseq = proc->block.iseq;
35
+ if (!iseq
36
+ #if 0
37
+ /* Our iseq struct isn't complete enough to contain self. */
38
+ || !RUBY_VM_NORMAL_ISEQ_P(iseq->self)
39
+ #endif
40
+ )
41
+ return Qnil;
42
+ rb_iseq = iseq_alloc_shared(rb_cISeq);
43
+ RDATA(rb_iseq)->data = iseq;
44
+ return rb_iseq;
45
+ }
46
+
47
+ /* Defined in Ruby 1.9 proc.c */
48
+ extern rb_iseq_t *rb_method_get_iseq(VALUE method);
49
+
50
+ #if 0 /* The following is to fake out rdoc, until I find a better fix. */
51
+ /*
52
+ * Additions to the RubyVM::Method class
53
+ */
54
+ VALUE rb_cIseq = rb_define_class("Method", ...)
55
+ #endif
56
+ /*
57
+ * call-seq:
58
+ * Method#iseq -> RubyVM::InstructionSequence
59
+ *
60
+ * Returns the instruction sequence of a Method object.
61
+ */
62
+ VALUE
63
+ method_iseq(VALUE self)
64
+ {
65
+ VALUE rb_iseq;
66
+ rb_iseq_t *iseq = rb_method_get_iseq(self);
67
+ if (!iseq) return Qnil;
68
+ rb_iseq = iseq_alloc_shared(rb_cISeq);
69
+ RDATA(rb_iseq)->data = iseq;
70
+ return rb_iseq;
71
+ }
72
+
73
+ /*
74
+ * call-seq:
75
+ * Method#alias_count -> Fixnum
76
+ *
77
+ * number of aliases a method has
78
+ */
79
+ VALUE
80
+ method_alias_count(VALUE self)
81
+ {
82
+ struct METHOD *m1 = (struct METHOD *)DATA_PTR(self);
83
+ return INT2FIX(m1->me.def->alias_count);
84
+ }
85
+
86
+ /*
87
+ * call-seq:
88
+ * Method#original_id - Original name of method
89
+ */
90
+ VALUE
91
+ method_original_id(VALUE self)
92
+ {
93
+ struct METHOD *m1 = (struct METHOD *)DATA_PTR(self);
94
+ return ID2SYM(m1->me.def->original_id);
95
+ }
96
+
97
+
98
+ void
99
+ Init_proc_extra(void)
100
+ {
101
+ /* Additions to Proc */
102
+ rb_define_method(rb_cProc, "iseq", proc_iseq, 0);
103
+
104
+ /* Additions to Method */
105
+ rb_define_method(rb_cMethod, "iseq", method_iseq, 0);
106
+ rb_define_method(rb_cMethod, "alias_count", method_alias_count, 0);
107
+ rb_define_method(rb_cMethod, "original_id", method_original_id, 0);
108
+ }
data/ext/proc_extra.h ADDED
@@ -0,0 +1,3 @@
1
+ /* in proc_extra.c */
2
+ extern VALUE proc_iseq(VALUE self);
3
+ extern void Init_proc_extra(void);
@@ -0,0 +1,84 @@
1
+ /*
2
+ * Copyright (C) 2010 Rocky Bernstein
3
+ */
4
+ #if 0 /* The following is to fake out rdoc, until I find a better fix. */
5
+ /*
6
+ * Additions to the RubyVM::Method class
7
+ */
8
+ VALUE rb_cThread = rb_define_class("Method", ...)
9
+ #endif
10
+
11
+ #include "../include/vm_core_mini.h" /* Pulls in ruby.h and node.h */
12
+ #include "thread_extra.h"
13
+
14
+ /*
15
+ * call-seq:
16
+ * Thread#exec_event_tracing -> bool
17
+ *
18
+ * Returns the value of the thread event-hook tracing.
19
+ */
20
+ VALUE
21
+ thread_exec_event_tracing(VALUE self)
22
+ {
23
+ rb_thread_t *th;
24
+ GetThreadPtr(self, th);
25
+ return th->exec_event_tracing ? Qtrue : Qfalse;
26
+ }
27
+
28
+ /*
29
+ * call-seq:
30
+ * Thread#exec_event_tracing=(new_value)
31
+ *
32
+ * Sets the value of thread event-hook tracing.
33
+ */
34
+ VALUE
35
+ thread_exec_event_tracing_set(VALUE self, VALUE new_value)
36
+ {
37
+ rb_thread_t *th;
38
+ GetThreadPtr(self, th);
39
+
40
+ th->exec_event_tracing = RTEST(new_value) ? Qtrue : Qfalse;
41
+ return th->exec_event_tracing;
42
+ }
43
+
44
+ /*
45
+ * call-seq:
46
+ * Thread#tracing -> bool
47
+ *
48
+ * Returns the value of the thread event-hook tracing.
49
+ */
50
+ VALUE
51
+ thread_tracing(VALUE self)
52
+ {
53
+ rb_thread_t *th;
54
+ GetThreadPtr(self, th);
55
+ return th->tracing ? Qtrue : Qfalse;
56
+ }
57
+
58
+ /*
59
+ * call-seq:
60
+ * Thread#tracing= bool
61
+ *
62
+ * Sets the value of thread event-hook tracing.
63
+ */
64
+ VALUE
65
+ thread_tracing_set(VALUE self, VALUE new_value)
66
+ {
67
+ rb_thread_t *th;
68
+ GetThreadPtr(self, th);
69
+
70
+ th->tracing = RTEST(new_value) ? Qtrue : Qfalse;
71
+ return th->tracing;
72
+ }
73
+
74
+ void
75
+ Init_thread_extra(void)
76
+ {
77
+ /* Additions to Thread class */
78
+ rb_define_method(rb_cThread, "exec_event_tracing=",
79
+ thread_exec_event_tracing_set, 1);
80
+ rb_define_method(rb_cThread, "exec_event_tracing",
81
+ thread_exec_event_tracing, 0);
82
+ rb_define_method(rb_cThread, "tracing=", thread_tracing_set, 1);
83
+ rb_define_method(rb_cThread, "tracing", thread_tracing, 0);
84
+ }
@@ -0,0 +1,5 @@
1
+ /* in thread_extra.c */
2
+ extern VALUE thread_tracing(VALUE self);
3
+ extern VALUE thread_set_tracing(VALUE self, VALUE new_value);
4
+ extern void Init_thread_extra(void);
5
+
@@ -0,0 +1,1022 @@
1
+ /*
2
+ * Copyright (C) 2010 Rocky Bernstein
3
+ *
4
+ * Access to Ruby's rb_control_frame_t and methods for working with that.
5
+ * Things like getting a binding for a control frame.
6
+ */
7
+
8
+ /* What release we got? */
9
+ #define THREADFRAME_VERSION "0.32"
10
+
11
+ #include <string.h>
12
+ #include "../include/vm_core_mini.h" /* Pulls in ruby.h and node.h */
13
+ #include "proc_extra.h"
14
+ #include "iseq_extra.h"
15
+ #include "thread_extra.h"
16
+
17
+ /* Frames can't be detached from the control frame they live in.
18
+ So we create a structure to contain the pair.
19
+
20
+ The signature fields are used to weakly verify the validity of cfp.
21
+ it stores to contents of fields in cfp on allocation. This, the
22
+ validity of "th" pointing to a valid thread, and cfp pointing to valid
23
+ location inside the frame area we use to check that this structure
24
+ is valid. */
25
+
26
+ typedef struct
27
+ {
28
+ rb_thread_t *th;
29
+ rb_control_frame_t *cfp;
30
+ VALUE *signature1[3]; /* iseq, flag, self */
31
+ VALUE *signature2[1]; /* proc */
32
+ } thread_frame_t;
33
+
34
+ #include "../include/ruby19_externs.h"
35
+
36
+ VALUE rb_cThreadFrame; /* ThreadFrame class */
37
+ VALUE rb_eThreadFrameError; /* Error raised on invalid frames. */
38
+
39
+ /* Static forward declarations */
40
+ static VALUE thread_frame_iseq(VALUE klass);
41
+ static VALUE thread_frame_prev_internal(rb_control_frame_t *prev_cfp,
42
+ rb_thread_t *th, int n);
43
+ static int thread_frame_stack_size_internal(rb_control_frame_t *cfp,
44
+ rb_thread_t *th);
45
+ static VALUE thread_frame_type(VALUE klass);
46
+
47
+
48
+ /*
49
+ Allocate a RubyVM::ThreadFrame used by new. Less common than
50
+ thread_frame_t_alloc().
51
+ */
52
+ static VALUE
53
+ thread_frame_alloc(VALUE klass)
54
+ {
55
+ return Data_Wrap_Struct(klass, NULL, xfree, 0);
56
+ }
57
+
58
+ /*
59
+ Allocate a RubyVM::ThreadFrame and set its threadframe structure.
60
+ This is the more common allocate routine since one normally doesn't
61
+ create a threadframe without <i>first</i> having seomthing to put in it.
62
+ */
63
+ static thread_frame_t *
64
+ thread_frame_t_alloc(VALUE tfval)
65
+ {
66
+ thread_frame_t *tf = ALLOC(thread_frame_t);
67
+ memset(tf, 0, sizeof(thread_frame_t));
68
+ DATA_PTR(tfval) = tf;
69
+ return tf;
70
+ }
71
+
72
+ static VALUE
73
+ thread_frame_invalid_internal(thread_frame_t *tf)
74
+ {
75
+ int cmp;
76
+
77
+ /* All valid frame types have 0x1 set so we will use this.
78
+ Warning: this is an undocumented assumption which may someday
79
+ be wrong. */
80
+ if ((tf->cfp->flag & 0x1) == 0) return Qtrue;
81
+
82
+ if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(tf->th, tf->cfp))
83
+ return Qtrue;
84
+ if (RUBY_VM_NORMAL_ISEQ_P(tf->cfp->iseq)) {
85
+ cmp = (0 == memcmp(tf->signature1, &(tf->cfp->iseq),
86
+ sizeof(tf->signature1)) &&
87
+ 0 == memcmp(tf->signature2, &(tf->cfp->proc),
88
+ sizeof(tf->signature2)));
89
+ return cmp ? Qfalse : Qtrue;
90
+ } else {
91
+ /* FIXME: figure out what to do here. Probably more work is
92
+ * needed in thread_frame_prev_internal.
93
+ */
94
+ return Qnil;
95
+ }
96
+ }
97
+
98
+ /*
99
+ COPY_SIGNATURE saves some invariant data from the frame for
100
+ comparison later when the frame is used again.
101
+
102
+ Even though fields like iseq may not be valid for things C function
103
+ frames, nevertheless all we care about is whether they could change
104
+ or not over the course of evaluation. Hving more data to compare
105
+ against to verify whether a frame is valid is helpful. If the data
106
+ is random unitialized data, that's even better. Again just so long as
107
+ that random data doesn't change in the course of normal use.
108
+
109
+ FIXME: There are probably more fields which could be saved. */
110
+ #define COPY_SIGNATURE(tf, cfp) \
111
+ memcpy(tf->signature1, &(cfp->iseq), sizeof(tf->signature1)); \
112
+ memcpy(tf->signature2, &(cfp->proc), sizeof(tf->signature2))
113
+
114
+ #define SAVE_FRAME(TF, TH) \
115
+ tf->th = TH; \
116
+ tf->cfp = thread_control_frame(tf->th); \
117
+ COPY_SIGNATURE(tf, tf->cfp); \
118
+
119
+ #define GET_THREAD_PTR \
120
+ rb_thread_t *th; \
121
+ GetThreadPtr(thval, th)
122
+
123
+
124
+ /*
125
+ * call-seq:
126
+ * RubyVM::ThreadFrame#threadframe -> thread_frame_object
127
+ *
128
+ * Returns a RubyVM::ThreadFrame object for the given thread.
129
+ */
130
+ static VALUE
131
+ thread_frame_threadframe(VALUE thval)
132
+ {
133
+ thread_frame_t *tf = ALLOC(thread_frame_t);
134
+ GET_THREAD_PTR;
135
+ memset(tf, 0, sizeof(thread_frame_t));
136
+ SAVE_FRAME(tf, th) ;
137
+ return Data_Wrap_Struct(rb_cThreadFrame, NULL, xfree, tf);
138
+ }
139
+
140
+ #define THREAD_FRAME_SETUP \
141
+ thread_frame_t *tf; \
142
+ Data_Get_Struct(klass, thread_frame_t, tf)
143
+
144
+ #define THREAD_FRAME_SETUP_WITH_ERROR \
145
+ THREAD_FRAME_SETUP; \
146
+ if (Qtrue == thread_frame_invalid_internal(tf)) \
147
+ rb_raise(rb_eThreadFrameError, "invalid frame")
148
+
149
+ #define THREAD_FRAME_FIELD_METHOD(FIELD) \
150
+ static VALUE \
151
+ thread_frame_##FIELD(VALUE klass) \
152
+ { \
153
+ THREAD_FRAME_SETUP ; \
154
+ return tf->cfp->FIELD; \
155
+ }
156
+
157
+ #define THREAD_FRAME_FP_METHOD(REG) \
158
+ VALUE \
159
+ thread_frame_##REG(VALUE klass, VALUE index) \
160
+ { \
161
+ if (!FIXNUM_P(index)) { \
162
+ rb_raise(rb_eTypeError, "integer argument expected"); \
163
+ } else { \
164
+ long int i = FIX2INT(index); \
165
+ THREAD_FRAME_SETUP_WITH_ERROR ; \
166
+ /* FIXME: check index is within range. */ \
167
+ return tf->cfp->REG[-i]; /* stack grows "down" */ \
168
+ } \
169
+ }
170
+
171
+ #if 0
172
+ /*
173
+ * call-seq:
174
+ * RubyVM::ThreadFrame#dfp(n) -> object
175
+ *
176
+ * Returns a RubyVM object stored at dfp position <i>i</i>. The top object
177
+ * is position 0.
178
+ */
179
+ static VALUE
180
+ thread_frame_dfp(VALUE klass, VALUE index)
181
+ {
182
+ /* handled by THREAD_FRAME_FP_METHOD macro; */
183
+ }
184
+ #endif
185
+ /* The above declaration is to make RDOC happy. */
186
+ THREAD_FRAME_FP_METHOD(dfp)
187
+
188
+ /*
189
+ * call-seq:
190
+ * RubyVM::ThreadFrame#lfp(i) -> object
191
+ *
192
+ * Returns a RubyVM object stored at lfp position <i>i</i>. The top object
193
+ * is position 0. Negative values of <i>i</i> count from the end.
194
+ */
195
+ static VALUE
196
+ thread_frame_lfp(VALUE klass, VALUE index)
197
+ {
198
+ if (!FIXNUM_P(index)) {
199
+ rb_raise(rb_eTypeError, "integer argument expected");
200
+ } else {
201
+ long int i = FIX2INT(index);
202
+ long int size;
203
+
204
+ THREAD_FRAME_SETUP_WITH_ERROR ;
205
+
206
+ size = tf->cfp->iseq->local_size;
207
+ if (i < 0) i = size - i;
208
+
209
+ if (i > size)
210
+ rb_raise(rb_eIndexError,
211
+ "local frame index %ld should be in the range %ld .. %ld",
212
+ i, -size, size-1);
213
+
214
+ return tf->cfp->lfp[-i]; /* stack grows "down" */
215
+ }
216
+ }
217
+
218
+ #if 0
219
+ /*
220
+ * call-seq:
221
+ * RubyVM::ThreadFrame#sp(n) -> object
222
+ *
223
+ * Returns a RubyVM object stored at stack position <i>i</i>. The top object
224
+ * is position 0. 1 is the next object.
225
+ */
226
+ VALUE
227
+ thread_frame_sp(VALUE klass, VALUE index)
228
+ {
229
+ /* handled by THREAD_FRAME_FP_METHOD macro; */
230
+ }
231
+ #endif
232
+ /* The above declaration is to make RDOC happy.
233
+ FIXME: Figure out a way to check if "index" is valid!
234
+ */
235
+ THREAD_FRAME_FP_METHOD(sp)
236
+
237
+ static long int
238
+ thread_frame_sp_size_internal(thread_frame_t *tf)
239
+ {
240
+ rb_control_frame_t *prev_cfp;
241
+ long int ret_val;
242
+ prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(tf->cfp);
243
+ if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(tf->th, prev_cfp))
244
+ return Qnil;
245
+ ret_val = tf->cfp->sp - prev_cfp->sp - 1;
246
+ /* FIXME: Why For C Functions we tack on 3? Possibly we're doing
247
+ the previous frame and possibly that's where the action is that
248
+ we should be looking at.? */
249
+ if (RUBYVM_CFUNC_FRAME_P(tf->cfp)) ret_val += 3;
250
+ return ret_val;
251
+ }
252
+
253
+ /*
254
+ * call-seq:
255
+ * RubyVM::ThreadFrame#sp_size -> FixNum
256
+ *
257
+ * Returns the number of stack or sp entries in the current
258
+ * frame. That is the, number values that have been pushed onto the
259
+ * stack since the current call. This is different than
260
+ * RubyVM::ThreadFrame#stack_size which counts the number of frames in
261
+ * the call stack. +nil+ is returned if there is an error.
262
+ */
263
+ VALUE
264
+ thread_frame_sp_size(VALUE klass)
265
+ {
266
+ THREAD_FRAME_SETUP_WITH_ERROR ;
267
+ return INT2FIX(thread_frame_sp_size_internal(tf));
268
+ }
269
+
270
+ /*
271
+ * call-seq:
272
+ * RubyVM::ThreadFrame#sp_set(n, newvalue) -> object
273
+ *
274
+ * Sets VM stack position <i>n</i> to <i>newvalue</i>. The top object
275
+ * is position 0. 1 is the next object.
276
+ */
277
+ static VALUE
278
+ thread_frame_sp_set(VALUE klass, VALUE index, VALUE newvalue)
279
+ {
280
+ if (!FIXNUM_P(index)) {
281
+ rb_raise(rb_eTypeError, "integer argument expected");
282
+ } else {
283
+ long int i = FIX2INT(index);
284
+ THREAD_FRAME_SETUP_WITH_ERROR ;
285
+ if (i <= thread_frame_sp_size_internal(tf)) {
286
+ /* stack grows "down" */
287
+ tf->cfp->sp[-i] = newvalue;
288
+ } else {
289
+ rb_raise(rb_eArgError, "argument too big");
290
+ }
291
+ return newvalue;
292
+ }
293
+ }
294
+
295
+ #ifndef NO_reg_pc
296
+ /*
297
+ * call-seq:
298
+ * RubyVM::ThreadFrame#pc_offset=
299
+ *
300
+ * Sets pc to the offset given.
301
+ * WARNING, this is pretty dangerous. You need to set this to a valid
302
+ * instruction offset since little checking is done.
303
+ */
304
+ static VALUE
305
+ thread_frame_set_pc_offset(VALUE klass, VALUE offset_val)
306
+ {
307
+ int offset;
308
+ THREAD_FRAME_SETUP_WITH_ERROR ;
309
+
310
+ if (!FIXNUM_P(offset_val)) {
311
+ rb_raise(rb_eTypeError, "integer argument expected");
312
+ } else {
313
+ offset = FIX2INT(offset_val);
314
+ if (RUBY_VM_NORMAL_ISEQ_P(tf->cfp->iseq) &&
315
+ (tf->cfp->pc != 0 && tf->cfp->iseq != 0)) {
316
+ tf->cfp->pc = tf->cfp->iseq->iseq_encoded + offset;
317
+ }
318
+ }
319
+ return Qtrue;
320
+ }
321
+ #endif
322
+
323
+ #if 0
324
+ /*
325
+ * call-seq:
326
+ * RubyVM::ThreadFrame#flag -> Fixnum
327
+ *
328
+ * Returns the frame flags, a FIXNUM which should be interpreted as a
329
+ * bitmask.
330
+ *
331
+ */
332
+ static VALUE thread_frame_flag(VALUE klass)
333
+ {
334
+ /* handled by THREAD_FRAME_FIELD_METHOD macro; */
335
+ }
336
+ /* The above declaration is to make RDOC happy. */
337
+ #endif
338
+ THREAD_FRAME_FIELD_METHOD(flag) ;
339
+
340
+ /*
341
+ * call-seq:
342
+ * RubyVM::ThreadFrame#argc -> Fixnum
343
+ *
344
+ * Returns the number of arguments that were actually passed
345
+ * in the call to this frame. This differs from arity when
346
+ * arity can take optional or "splat"ted parameters.
347
+ *
348
+ */
349
+ static VALUE
350
+ thread_frame_argc(VALUE klass)
351
+ {
352
+ THREAD_FRAME_SETUP_WITH_ERROR;
353
+
354
+ if (RUBYVM_CFUNC_FRAME_P(tf->cfp)) {
355
+ return INT2FIX(tf->cfp->me->def->body.cfunc.actual_argc);
356
+ } else if (RUBY_VM_NORMAL_ISEQ_P(tf->cfp->iseq)) {
357
+ return iseq_argc(thread_frame_iseq(klass));
358
+ } else
359
+ return Qnil;
360
+ }
361
+
362
+ /*
363
+ * call-seq:
364
+ * RubyVM::ThreadFrame#arity -> Fixnum
365
+ *
366
+ * Returns the number of arguments that would not be ignored.
367
+ * See Ruby 1.9 proc_arity of proc.c
368
+ *
369
+ */
370
+ static VALUE
371
+ thread_frame_arity(VALUE klass)
372
+ {
373
+ THREAD_FRAME_SETUP_WITH_ERROR ;
374
+
375
+ if (RUBY_VM_NORMAL_ISEQ_P(tf->cfp->iseq)) {
376
+ return rb_iseq_arity(thread_frame_iseq(klass));
377
+ } else if (RUBYVM_CFUNC_FRAME_P(tf->cfp)) {
378
+ return INT2FIX(tf->cfp->me->def->body.cfunc.argc);
379
+ } else
380
+ return Qnil;
381
+ }
382
+
383
+ /*
384
+ * call-seq:
385
+ * RubyVM::ThreadFrame#binding -> binding
386
+ *
387
+ * Returns a binding for a given thread frame.
388
+ */
389
+ static VALUE
390
+ thread_frame_binding(VALUE klass)
391
+ {
392
+ THREAD_FRAME_SETUP_WITH_ERROR ;
393
+
394
+ {
395
+ rb_binding_t *bind = 0;
396
+ VALUE bindval = rb_binding_frame_new(tf->th, tf->cfp);
397
+ GetBindingPtr(bindval, bind);
398
+ bind->line_no = rb_vm_get_sourceline(tf->cfp);
399
+ if (tf->cfp->iseq) {
400
+ bind->filename = tf->cfp->iseq->filename;
401
+ } else {
402
+ bind->filename = tf->th->vm->progname;
403
+ }
404
+ return bindval;
405
+ }
406
+ /* NOTREACHED */
407
+ return Qnil;
408
+ }
409
+
410
+ /*
411
+ * call-seq:
412
+ * RubyVM::ThreadFrame#equal(tf) -> bool
413
+ *
414
+ * Returns true if two thread frames are equal.
415
+ */
416
+ static VALUE
417
+ thread_frame_equal(VALUE klass, VALUE tfval2)
418
+ {
419
+ THREAD_FRAME_SETUP_WITH_ERROR ;
420
+
421
+ {
422
+ thread_frame_t *tf2;
423
+ if (!rb_obj_is_kind_of(tfval2, rb_cThreadFrame)) {
424
+ rb_raise(rb_eTypeError,
425
+ "comparison argument must be an instance of %s (is %s)",
426
+ rb_obj_classname(klass), rb_obj_classname(tfval2));
427
+ }
428
+ Data_Get_Struct(tfval2, thread_frame_t, tf2);
429
+ if (Qtrue == thread_frame_invalid_internal(tf2))
430
+ rb_raise(rb_eThreadFrameError, "invalid frame");
431
+
432
+ /* And just when you thought I'd never get around to the
433
+ actual comparison...
434
+
435
+ Comparing cfp's should be enough, but we'll throw in the thread
436
+ for good measure.
437
+ */
438
+ return (tf->th == tf2->th && tf->cfp == tf2->cfp)
439
+ ? Qtrue : Qfalse;
440
+ }
441
+ /* NOTREACHED */
442
+ return Qnil;
443
+ }
444
+
445
+ /*
446
+ * call-seq:
447
+ * RubyVM::ThreadFrame.new(thread) -> thread_frame_object
448
+ *
449
+ * Returns an RubyVM::ThreadFrame object which can contains dynamic frame
450
+ * information. Don't use this directly. Instead use one of the
451
+ * class methods.
452
+ *
453
+ * RubyVM::ThreadFrame::VERSION => 0.1
454
+ * RubyVM::ThreadFrame::current.flag => 72
455
+ * RubyVM::ThreadFrame::current.proc => false
456
+ * RubyVM::ThreadFrame::current.self => 'main'
457
+ */
458
+ static VALUE
459
+ thread_frame_initialize(VALUE tfval, VALUE thval)
460
+ {
461
+ thread_frame_t *tf = thread_frame_t_alloc(tfval);
462
+ GET_THREAD_PTR ;
463
+ memset(tf, 0, sizeof(thread_frame_t));
464
+ DATA_PTR(tfval) = tf;
465
+ SAVE_FRAME(tf, th) ;
466
+ return tfval;
467
+ }
468
+
469
+ /*
470
+ * call-seq:
471
+ * RubyVM::ThreadFrame#invalid? -> Boolean
472
+ *
473
+ * Returns true if the frame is no longer valid. On the other hand,
474
+ * since the test we use is weak, returning false might not mean the
475
+ * frame is valid, just that we can't disprove that it is not invalid.
476
+ *
477
+ * It is suggested that frames are used in a way that ensures they will
478
+ * be valid. In particular frames should have local scope and frames to
479
+ * threads other than the running one should be stopped while the frame
480
+ * variable is active.
481
+ */
482
+ static VALUE
483
+ thread_frame_invalid(VALUE klass)
484
+ {
485
+ THREAD_FRAME_SETUP ;
486
+ return thread_frame_invalid_internal(tf);
487
+ }
488
+
489
+ static VALUE
490
+ thread_frame_is_return_stop(VALUE klass)
491
+ {
492
+ THREAD_FRAME_SETUP ;
493
+ return (tf->cfp->tracing & VM_FRAME_TRACE_RETURN) ? Qtrue : Qfalse;
494
+ }
495
+
496
+ static VALUE
497
+ thread_frame_is_trace_off(VALUE klass)
498
+ {
499
+ THREAD_FRAME_SETUP ;
500
+ return (tf->cfp->tracing & VM_FRAME_TRACE_OFF) ? Qtrue : Qfalse;
501
+ }
502
+
503
+ /*
504
+ * call-seq:
505
+ * ThreadFrame#method -> String or nil
506
+ *
507
+ * Returns the method associated with the frame or nil of none.
508
+ * ThreadFrameError can be raised if the threadframe
509
+ * object is no longer valid.
510
+ */
511
+ static VALUE
512
+ thread_frame_method(VALUE klass)
513
+ {
514
+ THREAD_FRAME_SETUP_WITH_ERROR ; \
515
+
516
+ switch (VM_FRAME_TYPE(tf->cfp)) {
517
+ case VM_FRAME_MAGIC_BLOCK:
518
+ case VM_FRAME_MAGIC_EVAL:
519
+ case VM_FRAME_MAGIC_LAMBDA:
520
+ case VM_FRAME_MAGIC_METHOD:
521
+ case VM_FRAME_MAGIC_TOP:
522
+ if (RUBY_VM_NORMAL_ISEQ_P(tf->cfp->iseq))
523
+ return tf->cfp->iseq->name;
524
+ else
525
+ return Qnil; /* unknown */
526
+ case VM_FRAME_MAGIC_CFUNC: {
527
+ const char *method_name = rb_id2name(tf->cfp->me->def->original_id);
528
+ return rb_str_new2((NULL == method_name) ? "" : method_name );
529
+ }
530
+ default:
531
+ /* FIXME */
532
+ return thread_frame_type(klass);
533
+ }
534
+ /* NOTREACHED */
535
+ return Qnil;
536
+ }
537
+
538
+ /*
539
+ * call-seq:
540
+ * ThreadFrame#pc_offset -> Fixnum
541
+ *
542
+ * Returns the offset inside the iseq or "program-counter offset" or -1
543
+ * If invalid/unstarted. ThreadFrameError can be raised if the threadframe
544
+ * object is no longer valid.
545
+ */
546
+ static VALUE
547
+ thread_frame_pc_offset(VALUE klass)
548
+ {
549
+ unsigned long pc;
550
+ THREAD_FRAME_SETUP_WITH_ERROR ;
551
+
552
+ if (RUBY_VM_NORMAL_ISEQ_P(tf->cfp->iseq) &&
553
+ (tf->cfp->pc != 0 && tf->cfp->iseq != 0)) {
554
+ pc = tf->cfp->pc - tf->cfp->iseq->iseq_encoded;
555
+ return INT2FIX(pc);
556
+ } else {
557
+ return INT2FIX(-1);
558
+ }
559
+ }
560
+
561
+
562
+ /*
563
+ * call-seq:
564
+ * ThreadFrame#iseq -> ISeq
565
+ *
566
+ * Returns an instruction sequence object from the instruction sequence
567
+ * found inside the ThreadFrame object or nil if there is none.
568
+ *
569
+ */
570
+ static VALUE
571
+ thread_frame_iseq(VALUE klass)
572
+ {
573
+ rb_iseq_t *iseq;
574
+ VALUE rb_iseq;
575
+ THREAD_FRAME_SETUP_WITH_ERROR ;
576
+ iseq = tf->cfp->iseq;
577
+ if (!iseq) return Qnil;
578
+ rb_iseq = iseq_alloc_shared(rb_cISeq);
579
+ RDATA(rb_iseq)->data = iseq;
580
+ return rb_iseq;
581
+ }
582
+
583
+ /*
584
+ See the above thread_frame_prev comment for what's going on here.
585
+ */
586
+ static VALUE
587
+ thread_frame_next(VALUE klass)
588
+ {
589
+ rb_control_frame_t *cfp = NULL;
590
+ THREAD_FRAME_SETUP_WITH_ERROR ;
591
+ cfp = RUBY_VM_NEXT_CONTROL_FRAME(tf->cfp);
592
+
593
+ if ((void *)(cfp) <= (void *)(tf->th->stack))
594
+ return Qnil;
595
+ else {
596
+ thread_frame_t *next_tf;
597
+ VALUE next = thread_frame_alloc(rb_cThreadFrame);
598
+ thread_frame_t_alloc(next);
599
+ Data_Get_Struct(next, thread_frame_t, next_tf);
600
+ next_tf->th = tf->th;
601
+ next_tf->cfp = cfp;
602
+ COPY_SIGNATURE(tf, cfp);
603
+ return next;
604
+ }
605
+ }
606
+
607
+ /*
608
+ * call-seq:
609
+ * RubyVM::ThreadFrame#prev(n=1) -> thread_frame_object
610
+ *
611
+ * Returns a RubyVM::ThreadFrame object for the frame prior to the
612
+ * ThreadFrame object or +nil+ if there is none. Setting n=0 just
613
+ * returns the object passed. A negative starts from the end. So
614
+ * prev(-1) is the top frame. Counts outside of the range
615
+ * -stack_size .. stack_size-1 exceed the the range of the stack and
616
+ * return nil.
617
+ *
618
+ */
619
+ VALUE
620
+ thread_frame_prev(int argc, VALUE *argv, VALUE klass)
621
+ {
622
+ VALUE nv;
623
+ int n;
624
+
625
+ THREAD_FRAME_SETUP_WITH_ERROR ;
626
+
627
+ rb_scan_args(argc, argv, "01", &nv);
628
+
629
+ if (Qnil == nv)
630
+ n = 1;
631
+ else if (!FIXNUM_P(nv)) {
632
+ rb_raise(rb_eTypeError, "integer argument expected");
633
+ } else
634
+ n = FIX2INT(nv);
635
+
636
+ if (n < 0) {
637
+ int stack_size = thread_frame_stack_size_internal(tf->cfp, tf->th);
638
+ if (-n > stack_size) return Qnil;
639
+ n = stack_size + n;
640
+ }
641
+ if (n == 0) return klass;
642
+ return thread_frame_prev_internal(tf->cfp, tf->th, n);
643
+ }
644
+
645
+ /*
646
+ See the above thread_frame_prev comment for what's going on here.
647
+ */
648
+ static VALUE
649
+ thread_frame_prev_internal(rb_control_frame_t *prev_cfp, rb_thread_t *th,
650
+ int n)
651
+ {
652
+ thread_frame_t *tf;
653
+ VALUE prev;
654
+ rb_control_frame_t *cfp = NULL;
655
+
656
+ for (; n > 0; n--) {
657
+ cfp = prev_cfp;
658
+ prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
659
+ if (VM_FRAME_TYPE(prev_cfp) == VM_FRAME_MAGIC_FINISH) {
660
+ prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(prev_cfp);
661
+ }
662
+ if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, prev_cfp))
663
+ return Qnil;
664
+ }
665
+ if (!cfp) return Qnil;
666
+
667
+ prev = thread_frame_alloc(rb_cThreadFrame);
668
+ thread_frame_t_alloc(prev);
669
+ Data_Get_Struct(prev, thread_frame_t, tf);
670
+ tf->th = th;
671
+ tf->cfp = prev_cfp;
672
+ COPY_SIGNATURE(tf, tf->cfp);
673
+ return prev;
674
+ }
675
+
676
+ THREAD_FRAME_FIELD_METHOD(proc) ;
677
+ THREAD_FRAME_FIELD_METHOD(self) ;
678
+
679
+ static VALUE
680
+ thread_frame_return_stop_set(VALUE klass, VALUE boolval)
681
+ {
682
+ short int boolmask = !(NIL_P(boolval) || Qfalse == boolval);
683
+ THREAD_FRAME_SETUP ;
684
+
685
+ if (boolmask)
686
+ tf->cfp->tracing |= VM_FRAME_TRACE_RETURN;
687
+ else
688
+ tf->cfp->tracing &= ~VM_FRAME_TRACE_RETURN;
689
+ return boolval;
690
+ }
691
+
692
+ static VALUE
693
+ thread_frame_trace_off_set(VALUE klass, VALUE boolval)
694
+ {
695
+ short int boolmask = !(NIL_P(boolval) || Qfalse == boolval);
696
+ THREAD_FRAME_SETUP ;
697
+
698
+ if (boolmask)
699
+ tf->cfp->tracing |= VM_FRAME_TRACE_OFF;
700
+ else
701
+ tf->cfp->tracing &= ~VM_FRAME_TRACE_OFF;
702
+ return boolval;
703
+ }
704
+
705
+ /*
706
+ * call-seq:
707
+ * RubyVM::ThreadFrame::current -> thread_frame_object
708
+ *
709
+ * Returns a ThreadFrame object for the currently executing thread.
710
+ * Same as: RubyVM::ThreadFrame.new(Thread::current)
711
+ */
712
+ static VALUE
713
+ thread_frame_s_current(VALUE klass)
714
+ {
715
+ thread_frame_t *tf = thread_frame_t_alloc(klass);
716
+ SAVE_FRAME(tf, ruby_current_thread) ;
717
+ return Data_Wrap_Struct(klass, NULL, xfree, tf);
718
+ }
719
+
720
+ /*
721
+ * call-seq:
722
+ * RubyVM::ThreadFrame::prev(thread) -> threadframe_object
723
+ * RubyVM::ThreadFrame::prev(thread, n) -> threadframe_object
724
+ *
725
+ * Returns a RubyVM::ThreadFrame for the frame prior to the
726
+ * Thread object passed or nil if there is none. The default value for n
727
+ * is 1. 0 just returns the object passed.
728
+ * Negative counts or counts exceeding the stack will return nil.
729
+ */
730
+ static VALUE
731
+ thread_frame_s_prev(int argc, VALUE *argv, VALUE klass)
732
+ {
733
+ VALUE thval;
734
+ VALUE nv;
735
+ int n;
736
+
737
+ rb_scan_args(argc, argv, "11", &thval, &nv);
738
+
739
+ if (Qfalse == rb_obj_is_kind_of(thval, rb_cThread))
740
+ rb_raise(rb_eTypeError,
741
+ "ThreadFrame object needed for first argument");
742
+
743
+ GET_THREAD_PTR ;
744
+
745
+ if (Qnil == nv)
746
+ n = 1;
747
+ else if (!FIXNUM_P(nv)) {
748
+ rb_raise(rb_eTypeError, "Fixnum needed for second argument");
749
+ } else
750
+ n = FIX2INT(nv);
751
+
752
+ if (n == 0) {
753
+ return thread_frame_s_current(klass);
754
+ } else if (n < 0) {
755
+ int stack_size = thread_frame_stack_size_internal(th->cfp, th);
756
+ if (-n > stack_size) return Qnil;
757
+ n = stack_size + n;
758
+ }
759
+
760
+ return thread_frame_prev_internal(th->cfp, th, n);
761
+ }
762
+
763
+ /*
764
+ * call-seq:
765
+ * RubyVM::ThreadFrame#source_container() -> [Type, String]
766
+ *
767
+ * Returns a tuple representing kind of container, e.g. file
768
+ * eval'd string object, and the name of the container. If file,
769
+ * it would be a file name. If an eval'd string it might be the string.
770
+ */
771
+ static VALUE
772
+ thread_frame_source_container(VALUE klass)
773
+ {
774
+ VALUE filename = Qnil;
775
+ const char *contain_type;
776
+ rb_control_frame_t *cfp;
777
+ int is_eval = 0;
778
+
779
+ THREAD_FRAME_SETUP ;
780
+
781
+ for ( cfp = tf->cfp; cfp && !cfp->iseq && RUBYVM_CFUNC_FRAME_P(cfp);
782
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp) ) ;
783
+
784
+
785
+ if (cfp->iseq)
786
+ filename = cfp->iseq->filename;
787
+ else {
788
+ if (tf->th->vm->progname)
789
+ filename = tf->th->vm->progname;
790
+ else
791
+ return Qnil;
792
+ }
793
+
794
+ contain_type = source_container_type(filename);
795
+
796
+ is_eval = ( 0 == strcmp("string", contain_type)
797
+ && VM_FRAME_MAGIC_EVAL == VM_FRAME_TYPE(tf->cfp) );
798
+
799
+ if ( is_eval ) {
800
+ /* Try to pick up string from stack. */
801
+ VALUE prev = thread_frame_prev_internal(tf->cfp, tf->th, 1);
802
+ thread_frame_t *prev_tf;
803
+ Data_Get_Struct(prev, thread_frame_t, prev_tf);
804
+
805
+ if (RUBYVM_CFUNC_FRAME_P(prev_tf->cfp) &&
806
+ thread_frame_stack_size_internal(prev_tf->cfp, prev_tf->th) >= 3)
807
+ filename = thread_frame_sp(prev, INT2FIX(3));
808
+ }
809
+
810
+ return rb_ary_new3(2, rb_str_new2(contain_type), filename);
811
+ }
812
+
813
+ /*
814
+ * call-seq:
815
+ * RubyVM::ThreadFrame#source_location() -> Array
816
+ *
817
+ * Returns an array of source location positions that match
818
+ * +tf.instruction_offset+. A source location position is left
819
+ * implementation dependent. It could be line number, a line number
820
+ * and start and end column, or a start line number, start column, end
821
+ * line number, end column.
822
+ */
823
+ static VALUE
824
+ thread_frame_source_location(VALUE klass)
825
+ {
826
+ rb_control_frame_t *cfp;
827
+ THREAD_FRAME_SETUP ;
828
+
829
+ /* For now, it appears like we have line numbers only when there
830
+ is an instruction sequence. The heuristic that is used by
831
+ vm_backtrace_each of vm.c seems to be to use the line number of
832
+ the closest control frame that has an instruction sequence.
833
+ FIXME: investigate whether this is always the most accurate location. If
834
+ not, improve.
835
+ */
836
+ for ( cfp = tf->cfp; cfp && !cfp->iseq && RUBYVM_CFUNC_FRAME_P(cfp);
837
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp) ) ;
838
+
839
+ return (cfp->iseq)
840
+ /* NOTE: for now sourceline returns a single int. In the
841
+ future it might return an array of ints.
842
+ */
843
+ ? rb_ary_new3(1, INT2FIX(rb_vm_get_sourceline(cfp)))
844
+ : Qnil;
845
+ }
846
+
847
+ /*
848
+ * call-seq:
849
+ * RubyVM::ThreadFrame#stack_size -> Fixnum;
850
+ *
851
+ * Returns a count of the number of frames including the current one.
852
+ * ThreadFrame#prev(ThreadFrame#stack_size) = nil
853
+ * ThreadFrame#prev(ThreadFrame#stack_size-1) = top frame
854
+ *
855
+ *
856
+ */
857
+ static VALUE
858
+ thread_frame_stack_size(VALUE klass)
859
+ {
860
+ THREAD_FRAME_SETUP ;
861
+ return INT2FIX(thread_frame_stack_size_internal(tf->cfp, tf->th));
862
+ }
863
+
864
+ /*
865
+ See the above thread_frame_stack_size comment for what's going on here.
866
+ */
867
+ static int
868
+ thread_frame_stack_size_internal(rb_control_frame_t *cfp, rb_thread_t *th)
869
+ {
870
+ int n;
871
+ for ( n = 0;
872
+ !RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, cfp);
873
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) {
874
+ n++;
875
+ if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_FINISH) {
876
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
877
+ if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, cfp))
878
+ break;
879
+ }
880
+ }
881
+ return n;
882
+ }
883
+
884
+ /*
885
+ * call-seq:
886
+ * RubyVM::ThreadFrame#thread -> thread
887
+ *
888
+ * Returns the thread object for the thread frame.
889
+ */
890
+ static VALUE
891
+ thread_frame_thread(VALUE klass)
892
+ {
893
+ THREAD_FRAME_SETUP ;
894
+ return tf->th->self;
895
+ }
896
+
897
+ /* Extracted from vm_dump.c. Would be nice to have this routine put there
898
+ and used in both places. */
899
+ static const char *
900
+ frame_magic2str(rb_control_frame_t *cfp)
901
+ {
902
+ switch (VM_FRAME_TYPE(cfp)) {
903
+ case VM_FRAME_MAGIC_TOP:
904
+ return "TOP";
905
+ case VM_FRAME_MAGIC_METHOD:
906
+ return "METHOD";
907
+ case VM_FRAME_MAGIC_CLASS:
908
+ return "CLASS";
909
+ case VM_FRAME_MAGIC_BLOCK:
910
+ return "BLOCK";
911
+ case VM_FRAME_MAGIC_FINISH:
912
+ return "FINISH";
913
+ case VM_FRAME_MAGIC_CFUNC:
914
+ return "CFUNC";
915
+ case VM_FRAME_MAGIC_PROC:
916
+ return "PROC";
917
+ case VM_FRAME_MAGIC_LAMBDA:
918
+ return "LAMBDA";
919
+ case VM_FRAME_MAGIC_IFUNC:
920
+ return "IFUNC";
921
+ case VM_FRAME_MAGIC_EVAL:
922
+ return "EVAL";
923
+ case 0:
924
+ return "------";
925
+ default:
926
+ return "(none)";
927
+ }
928
+ /* NOTREACHED */
929
+ return "?";
930
+ }
931
+
932
+ /*
933
+ * call-seq:
934
+ * ThreadFrame#type -> String
935
+ *
936
+ * Returns the kind of frame. Basically interprets VM_FRAME_MAGIC for
937
+ * tf->cfp->flag
938
+ */
939
+ static VALUE
940
+ thread_frame_type(VALUE klass)
941
+ {
942
+ THREAD_FRAME_SETUP ;
943
+ return rb_str_new2(frame_magic2str(tf->cfp));
944
+ }
945
+
946
+ void
947
+ Init_thread_frame(void)
948
+ {
949
+ /* Additions to RubyVM */
950
+ rb_cThreadFrame = rb_define_class_under(rb_cRubyVM, "ThreadFrame",
951
+ rb_cObject);
952
+ rb_define_method(rb_cThread, "threadframe", thread_frame_threadframe, 0);
953
+
954
+ /* Thread::Frame */
955
+ rb_define_const(rb_cThreadFrame, "VERSION",
956
+ rb_str_new2(THREADFRAME_VERSION));
957
+ rb_define_alloc_func(rb_cThreadFrame, thread_frame_alloc);
958
+
959
+ rb_define_method(rb_cThreadFrame, "invalid?", thread_frame_invalid, 0);
960
+
961
+ /* RubyVM::ThreadFrame */
962
+ rb_define_method(rb_cThreadFrame, "argc", thread_frame_argc, 0);
963
+ rb_define_method(rb_cThreadFrame, "arity", thread_frame_arity, 0);
964
+ rb_define_method(rb_cThreadFrame, "binding", thread_frame_binding, 0);
965
+ rb_define_method(rb_cThreadFrame, "dfp", thread_frame_dfp, 1);
966
+ rb_define_method(rb_cThreadFrame, "flag", thread_frame_flag, 0);
967
+ rb_define_method(rb_cThreadFrame, "initialize", thread_frame_initialize, 1);
968
+ rb_define_method(rb_cThreadFrame, "iseq", thread_frame_iseq, 0);
969
+ rb_define_method(rb_cThreadFrame, "lfp", thread_frame_lfp, 1);
970
+ rb_define_method(rb_cThreadFrame, "method", thread_frame_method, 0);
971
+ rb_define_method(rb_cThreadFrame, "next", thread_frame_next, 0);
972
+ rb_define_method(rb_cThreadFrame, "pc_offset", thread_frame_pc_offset, 0);
973
+ rb_define_method(rb_cThreadFrame, "prev", thread_frame_prev, -1);
974
+ rb_define_method(rb_cThreadFrame, "proc", thread_frame_proc, 0);
975
+ rb_define_method(rb_cThreadFrame, "return_stop=", thread_frame_return_stop_set, 1);
976
+ rb_define_method(rb_cThreadFrame, "return_stop?", thread_frame_is_return_stop, 0);
977
+ rb_define_method(rb_cThreadFrame, "self", thread_frame_self, 0);
978
+ rb_define_method(rb_cThreadFrame, "source_container",
979
+ thread_frame_source_container, 0);
980
+ rb_define_method(rb_cThreadFrame, "source_location",
981
+ thread_frame_source_location, 0);
982
+
983
+ /* sp[] and sp[]= would be neater, but that would require making sp an
984
+ object which I am not sure I want to do.
985
+ */
986
+ rb_define_method(rb_cThreadFrame, "sp", thread_frame_sp, 1);
987
+ rb_define_method(rb_cThreadFrame, "sp_set", thread_frame_sp_set, 2);
988
+ rb_define_method(rb_cThreadFrame, "sp_size", thread_frame_sp_size, 0);
989
+
990
+ /* I think I like the more explicit stack_size over size or length. */
991
+ rb_define_method(rb_cThreadFrame, "stack_size",
992
+ thread_frame_stack_size, 0);
993
+
994
+ rb_define_method(rb_cThreadFrame, "thread", thread_frame_thread, 0);
995
+ rb_define_method(rb_cThreadFrame, "trace_off?", thread_frame_is_trace_off, 0);
996
+ rb_define_method(rb_cThreadFrame, "trace_off=", thread_frame_trace_off_set, 1);
997
+ rb_define_method(rb_cThreadFrame, "type", thread_frame_type, 0);
998
+
999
+ rb_define_method(rb_cThreadFrame, "equal?",
1000
+ thread_frame_equal, 1);
1001
+
1002
+ #ifndef NO_reg_pc
1003
+ rb_define_method(rb_cThreadFrame, "pc_offset=",
1004
+ thread_frame_set_pc_offset, 1);
1005
+ #endif
1006
+
1007
+
1008
+ rb_eThreadFrameError = rb_define_class("ThreadFrameError",
1009
+ rb_eStandardError);
1010
+
1011
+ rb_define_singleton_method(rb_cThreadFrame, "prev",
1012
+ thread_frame_s_prev, -1);
1013
+ rb_define_singleton_method(rb_cThreadFrame, "current",
1014
+ thread_frame_s_current, 0);
1015
+
1016
+
1017
+ /* Perform the other C extension initializations. */
1018
+ Init_iseq_extra();
1019
+ Init_proc_extra();
1020
+ Init_thread_extra();
1021
+ Init_thread_extra();
1022
+ }