rb-threadframe 0.32

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/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
+ }