linecache 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2
1
+ 0.3
data/ext/extconf.rb ADDED
@@ -0,0 +1,16 @@
1
+ require "mkmf"
2
+
3
+ if RUBY_VERSION >= "1.9"
4
+ STDERR.print("Can't handle 1.9.x yet\n")
5
+ exit(1)
6
+ elsif RUBY_VERSION >= "1.8"
7
+ if RUBY_RELEASE_DATE < "2005-03-22"
8
+ STDERR.print("Ruby version is too old\n")
9
+ exit(1)
10
+ end
11
+ else
12
+ STDERR.print("Ruby version is too old\n")
13
+ exit(1)
14
+ end
15
+
16
+ create_makefile("trace_nums")
data/ext/trace_nums.c ADDED
@@ -0,0 +1,716 @@
1
+ /*
2
+ This code creates module TraceLineNumbers with one method
3
+ lnums_for_str. lnums_for_str returns an array lines for which
4
+ RUBY_EVENT_LINE can be called on. In other words, the line numbers
5
+ that can be traced (such as via Tracer) or stopped at in a
6
+ debugger (such as ruby-debug).
7
+
8
+ This code has been tested on Ruby 1.8.6; it does not work on Ruby
9
+ 1.9.x. The code was created via culling from various sources.
10
+
11
+ Ruby 1.8's eval.c, and rb_eval() in particular, is the definitive
12
+ source of how the tree is evaluated. However we don't want to
13
+ actually evaluate the code, which simplifies things. In contrast,
14
+ we need lines for all branches, and not just the ones that get
15
+ executed on a given run. For example in an "if" node the "then"
16
+ part may or may not get executed, but we want to get the trace line
17
+ numbers for the "then" part regardless.
18
+
19
+ Code enclosed in the ***'s contains code from eval.c which is
20
+ included for comparison.
21
+
22
+ Also parse.y from Ruby 1.8 can shed light on how the nodes get
23
+ created.
24
+
25
+ Some legacy code in ParseTree is similar and necessarily more
26
+ complex. We would have used that gem from the outside and lived
27
+ with the additional bloat were it not broken for our purposes and
28
+ were it not for the author's lack of interest in extending it to
29
+ handle what's needed here.
30
+
31
+ Finally, node_help.txt from nodewrap contains descriptions of many
32
+ of the node types.
33
+ */
34
+ #include <ruby.h>
35
+ #include <version.h>
36
+ #include <node.h>
37
+ #include <env.h>
38
+ #include <rubysig.h>
39
+ #include "trace_nums.h"
40
+
41
+ VALUE mTraceLineNumbers;
42
+ extern NODE *ruby_eval_tree_begin;
43
+
44
+ #define nd_3rd u3.node
45
+
46
+ struct METHOD {
47
+ VALUE klass, rklass;
48
+ VALUE recv;
49
+ ID id, oid;
50
+ #if RUBY_VERSION_CODE > 182
51
+ int safe_level;
52
+ #endif
53
+ NODE *body;
54
+ };
55
+
56
+ struct BLOCK {
57
+ NODE *var;
58
+ NODE *body;
59
+ VALUE self;
60
+ struct FRAME frame;
61
+ struct SCOPE *scope;
62
+ VALUE klass;
63
+ NODE *cref;
64
+ int iter;
65
+ int vmode;
66
+ int flags;
67
+ int uniq;
68
+ struct RVarmap *dyna_vars;
69
+ VALUE orig_thread;
70
+ VALUE wrapper;
71
+ VALUE block_obj;
72
+ struct BLOCK *outer;
73
+ struct BLOCK *prev;
74
+ };
75
+
76
+ #define RETURN \
77
+ goto finish
78
+
79
+ #define EVENT_LINE(node) \
80
+ rb_ary_push(ary, INT2NUM(nd_line(node)))
81
+
82
+ #ifdef FINISHED
83
+ #define EVENT_CALL(node) \
84
+ rb_ary_push(ary, INT2NUM(nd_line(node)))
85
+ #else
86
+ #define EVENT_CALL(node)
87
+ #endif
88
+
89
+ /* Used just in debugging. */
90
+ static indent_level = 0;
91
+
92
+ static
93
+ void ln_eval(VALUE self, NODE * n, VALUE ary) {
94
+ NODE * volatile contnode = 0;
95
+ NODE * volatile node = n;
96
+
97
+ if (RTEST(ruby_debug)) {
98
+ char fmt[30] = { '\0', };
99
+ snprintf(fmt, sizeof(fmt), "%%%ds", indent_level+1);
100
+ fprintf(stderr, fmt, "[");
101
+ indent_level += 2;
102
+ }
103
+
104
+ again:
105
+ if (!node) RETURN;
106
+
107
+ if (RTEST(ruby_debug)) {
108
+ NODE *r = RNODE(node); /* For debugging */
109
+ fprintf(stderr, "%s ", NODE2NAME[nd_type(node)]);
110
+ }
111
+
112
+ switch (nd_type(node)) {
113
+ case NODE_BLOCK:
114
+ while (node) {
115
+ ln_eval(self, node->nd_head, ary);
116
+ node = node->nd_next;
117
+ }
118
+
119
+ case NODE_POSTEXE: /* END { ... } */
120
+ /* Nothing to do here... we are in an iter block */
121
+ /***
122
+ rb_f_END();
123
+ nd_set_type(node, NODE_NIL); /+ exec just once +/
124
+ result = Qnil;
125
+ ***/
126
+ break;
127
+
128
+ /* begin .. end without clauses */
129
+ case NODE_BEGIN:
130
+ /* node for speed-up(top-level loop for -n/-p) */
131
+ node = node->nd_body;
132
+ goto again;
133
+
134
+ /* nodes for speed-up(default match) */
135
+ case NODE_MATCH:
136
+ /* result = rb_reg_match2(node->nd_lit); */
137
+ break;
138
+
139
+ /* nodes for speed-up(literal match) */
140
+ case NODE_MATCH2:
141
+ /* l = */ ln_eval(self, node->nd_recv, ary);
142
+ /* r = */ ln_eval(self, node->nd_value, ary);
143
+ /*** result = rb_reg_match(l, r); ***/
144
+ break;
145
+
146
+ /* nodes for speed-up(literal match) */
147
+ case NODE_MATCH3: /* z =~ /"#{var}"/ for example */
148
+ /* r = */ ln_eval(self, node->nd_recv, ary);
149
+ /* l = */ ln_eval(self, node->nd_value, ary);
150
+ /***
151
+ if (TYPE(l) == T_STRING) {
152
+ result = rb_reg_match(r, l);
153
+ }
154
+ else {
155
+ // It is possible that value can be a function call which
156
+ // can trigger an call event. So to be conservative,
157
+ // we have to add a line number here.
158
+ result = rb_funcall(l, match, 1, r);
159
+ }
160
+ ****/
161
+ EVENT_CALL(node);
162
+ break;
163
+
164
+ /* node for speed-up(top-level loop for -n/-p) */
165
+ case NODE_OPT_N:
166
+ /* Lots of ugliness in eval.c. */
167
+ ln_eval(self, node->nd_body, ary);
168
+ break;
169
+
170
+ /* These nodes are empty. */
171
+ case NODE_SELF:
172
+ case NODE_NIL:
173
+ case NODE_TRUE:
174
+ case NODE_FALSE:
175
+ RETURN /* (something) */;
176
+
177
+ case NODE_IF:
178
+ EVENT_LINE(node);
179
+ ln_eval(self, node->nd_cond, ary);
180
+ if (node->nd_body) {
181
+ if (!node->nd_else) {
182
+ node = node->nd_body;
183
+ goto again;
184
+ }
185
+ ln_eval(self, node->nd_body, ary);
186
+ }
187
+ if (node->nd_else) {
188
+ node = node->nd_else;
189
+ goto again;
190
+ }
191
+ break;
192
+
193
+ case NODE_WHEN:
194
+ {
195
+ NODE *orig_node = node;
196
+ while (node) {
197
+ NODE *tag;
198
+
199
+ if (nd_type(node) != NODE_WHEN) goto again;
200
+ tag = node->nd_head;
201
+ while (tag) {
202
+ EVENT_LINE(tag);
203
+ if (tag->nd_head && nd_type(tag->nd_head) == NODE_WHEN) {
204
+ ln_eval(self, tag->nd_head->nd_head, ary);
205
+ }
206
+ tag = tag->nd_next;
207
+ }
208
+ node = node->nd_next;
209
+ }
210
+ if (orig_node->nd_body) {
211
+ ln_eval(self, orig_node->nd_body, ary); /* body */
212
+ }
213
+ RETURN /***(Qnil)***/ ;
214
+ }
215
+
216
+ case NODE_CASE:
217
+ ln_eval(self, node->nd_head, ary); /* expr */
218
+ node = node->nd_body;
219
+ while (node) {
220
+ NODE *tag;
221
+ if (nd_type(node) != NODE_WHEN) {
222
+ goto again;
223
+ }
224
+ tag = node->nd_head;
225
+ while (tag) {
226
+ EVENT_LINE(tag);
227
+ if (tag->nd_head && nd_type(tag->nd_head) == NODE_WHEN) {
228
+ ln_eval(self, tag->nd_head->nd_head, ary);
229
+ tag = tag->nd_next;
230
+ continue;
231
+ }
232
+ ln_eval(self, tag->nd_head, ary);
233
+ tag = tag->nd_next;
234
+ }
235
+ ln_eval(self, node->nd_body, ary);
236
+ node = node->nd_next;
237
+ }
238
+ RETURN /***(Qnil)***/;
239
+
240
+ case NODE_WHILE:
241
+ case NODE_UNTIL:
242
+ /* Doesn't follow eval.c */
243
+ ln_eval(self, node->nd_cond, ary);
244
+ if (node->nd_body) {
245
+ ln_eval(self, node->nd_body, ary);
246
+ }
247
+ break;
248
+
249
+ case NODE_BLOCK_PASS:
250
+ /*** result = block_pass(self, node); ***/
251
+ ln_eval(self, node->nd_body, ary);
252
+ ln_eval(self, node->nd_iter, ary);
253
+ break;
254
+
255
+ case NODE_ITER:
256
+ case NODE_FOR:
257
+ ln_eval(self, node->nd_iter, ary);
258
+ if (node->nd_var != (NODE *)1
259
+ && node->nd_var != (NODE *)2
260
+ && node->nd_var != NULL) {
261
+ ln_eval(self, node->nd_var, ary);
262
+ }
263
+ node = node->nd_body;
264
+ goto again;
265
+
266
+ case NODE_BREAK:
267
+ /* break_jump(rb_eval(self, node->nd_stts)); */
268
+ ln_eval(self, node->nd_stts, ary);
269
+ break;
270
+
271
+ case NODE_NEXT:
272
+ /*** CHECK_INTS;
273
+ next_jump(rb_eval(self, node->nd_stts)); ***/
274
+ ln_eval(self, node->nd_stts, ary);
275
+ break;
276
+
277
+ case NODE_REDO:
278
+ /*** CHECK_INTS;
279
+ JUMP_TAG(TAG_REDO); ***/
280
+ break;
281
+
282
+ case NODE_RETRY:
283
+ /*** CHECK_INTS;
284
+ JUMP_TAG(TAG_RETRY); ***/
285
+ break;
286
+
287
+ case NODE_SPLAT:
288
+ /*** result = splat_value(rb_eval(self, node->nd_head)); ***/
289
+ ln_eval(self, node->nd_head, ary);
290
+ break;
291
+
292
+ case NODE_TO_ARY:
293
+ /*** result = rb_ary_to_ary(rb_eval(self, node->nd_head)); ***/
294
+ ln_eval(self, node->nd_head, ary);
295
+ break;
296
+
297
+ case NODE_SVALUE: /* a = b, c */
298
+ /***
299
+ result = avalue_splat(rb_eval(self, node->nd_head));
300
+ if (result == Qundef) result = Qnil; ***/
301
+ ln_eval(self, node->nd_head, ary);
302
+ break;
303
+
304
+ case NODE_YIELD:
305
+ if (node->nd_head) {
306
+ /*** result = rb_eval(self, node->nd_head);
307
+ ruby_current_node = node; else ... ***/
308
+ ln_eval(self, node->nd_head, ary);
309
+ }
310
+ break;
311
+
312
+ case NODE_RESCUE:
313
+ /* Follow ruby_parse.rb and pray for the best. */
314
+ ln_eval(self, node->nd_1st, ary);
315
+ ln_eval(self, node->nd_2nd, ary);
316
+ ln_eval(self, node->nd_3rd, ary);
317
+ break;
318
+
319
+ case NODE_ENSURE:
320
+ ln_eval(self, node->nd_head, ary);
321
+ if (node->nd_ensr) {
322
+ ln_eval(self, node->nd_ensr, ary);
323
+ }
324
+ break;
325
+
326
+ case NODE_AND:
327
+ case NODE_OR:
328
+ ln_eval(self, node->nd_1st, ary);
329
+ ln_eval(self, node->nd_2nd, ary);
330
+ break;
331
+
332
+ case NODE_NOT:
333
+ /*** if (RTEST(rb_eval(self, node->nd_body))) result = Qfalse;
334
+ else result = Qtrue; ***/
335
+ ln_eval(self, node->nd_body, ary);
336
+ break;
337
+
338
+ case NODE_DOT2:
339
+ case NODE_DOT3:
340
+ case NODE_FLIP2:
341
+ case NODE_FLIP3:
342
+ ln_eval(self, node->nd_beg, ary);
343
+ ln_eval(self, node->nd_end, ary);
344
+ break;
345
+
346
+ case NODE_RETURN:
347
+ if (node->nd_stts)
348
+ ln_eval(self, node->nd_stts, ary);
349
+ break;
350
+
351
+ case NODE_ARGSCAT:
352
+ case NODE_ARGSPUSH:
353
+ ln_eval(self, node->nd_head, ary);
354
+ ln_eval(self, node->nd_body, ary);
355
+ break;
356
+
357
+ case NODE_ATTRASGN: /* literal.meth = y u1 u2 u3 */
358
+ /* node id node */
359
+ if (node->nd_recv == (NODE *)1) {
360
+ ln_eval(self, NEW_SELF(), ary);
361
+ } else {
362
+ ln_eval(self, node->nd_recv, ary);
363
+ }
364
+ ln_eval(self, node->nd_3rd, ary);
365
+ break;
366
+ case NODE_CALL:
367
+ case NODE_FCALL:
368
+ case NODE_VCALL:
369
+ if (nd_type(node) != NODE_FCALL)
370
+ ln_eval(self, node->nd_recv, ary);
371
+ if (node->nd_args || nd_type(node) != NODE_FCALL)
372
+ ln_eval(self, node->nd_args, ary);
373
+ break;
374
+
375
+ case NODE_SUPER:
376
+ ln_eval(self, node->nd_args, ary);
377
+ break;
378
+
379
+ case NODE_ZSUPER:
380
+ break;
381
+
382
+ case NODE_SCOPE:
383
+ ln_eval(self, node->nd_next, ary);
384
+ break;
385
+
386
+ case NODE_OP_ASGN1:
387
+ ln_eval(self, node->nd_recv, ary);
388
+ #if RUBY_VERSION_CODE < 185
389
+ ln_eval(self, node->nd_args->nd_next, ary);
390
+ #else
391
+ ln_eval(self, node->nd_args->nd_2nd, ary);
392
+ #endif
393
+ ln_eval(self, node->nd_args->nd_head, ary);
394
+ break;
395
+
396
+ case NODE_OP_ASGN2:
397
+ ln_eval(self, node->nd_recv, ary);
398
+ ln_eval(self, node->nd_value, ary);
399
+ break;
400
+
401
+ case NODE_OP_ASGN_AND:
402
+ case NODE_OP_ASGN_OR:
403
+ ln_eval(self, node->nd_head, ary);
404
+ ln_eval(self, node->nd_value, ary);
405
+ break;
406
+
407
+ case NODE_MASGN:
408
+ ln_eval(self, node->nd_head, ary);
409
+ if (node->nd_args) {
410
+ if (node->nd_args != (NODE *)-1) {
411
+ ln_eval(self, node->nd_args, ary);
412
+ }
413
+ }
414
+ ln_eval(self, node->nd_value, ary);
415
+ break;
416
+
417
+ case NODE_LASGN:
418
+ case NODE_DASGN:
419
+ case NODE_DASGN_CURR:
420
+ case NODE_GASGN:
421
+ case NODE_IASGN:
422
+ case NODE_CDECL:
423
+ case NODE_CVDECL:
424
+ case NODE_CVASGN:
425
+ ln_eval(self, node->nd_value, ary);
426
+ break;
427
+
428
+ case NODE_LVAR:
429
+ case NODE_DVAR:
430
+ case NODE_GVAR:
431
+ case NODE_IVAR:
432
+ case NODE_CONST:
433
+ case NODE_CVAR:
434
+ break;
435
+
436
+ case NODE_BLOCK_ARG: /* u1 u3 (def x(&b) */
437
+ break;
438
+
439
+ case NODE_COLON2:
440
+ ln_eval(self, node->nd_head, ary);
441
+ break;
442
+
443
+ case NODE_COLON3: /* u2 (::OUTER_CONST) */
444
+ break;
445
+
446
+ case NODE_NTH_REF: /* u2 u3 ($1) - u3 is local_cnt('~') ignorable? */
447
+ break;
448
+
449
+ case NODE_BACK_REF: /* u2 u3 ($& etc) */
450
+ break;
451
+
452
+ case NODE_HASH:
453
+ {
454
+ NODE *list;
455
+ list = node->nd_head;
456
+ while (list) {
457
+ ln_eval(self, list->nd_head, ary);
458
+ list = list->nd_next;
459
+ if (list == 0)
460
+ rb_bug("odd number list for Hash");
461
+ ln_eval(self, list->nd_head, ary);
462
+ list = list->nd_next;
463
+ }
464
+ }
465
+ break;
466
+
467
+ case NODE_ZARRAY:
468
+ break;
469
+
470
+ case NODE_ARRAY:
471
+ {
472
+ long int i = node->nd_alen;
473
+ for (i=0; node; node=node->nd_next) {
474
+ ln_eval(self, node->nd_head, ary);
475
+ }
476
+ }
477
+ break;
478
+
479
+ case NODE_STR: /* u1 */
480
+ break;
481
+
482
+ case NODE_EVSTR: /* eval of a string */
483
+ ln_eval(self, node->nd_2nd, ary);
484
+ break;
485
+
486
+ case NODE_DSTR:
487
+ case NODE_DXSTR:
488
+ case NODE_DREGX:
489
+ case NODE_DREGX_ONCE:
490
+ case NODE_DSYM:
491
+ {
492
+ NODE *list = node->nd_next;
493
+ while (list) {
494
+ if (list->nd_head) {
495
+ switch (nd_type(list->nd_head)) {
496
+ case NODE_STR:
497
+ ln_eval(self, list->nd_head, ary);
498
+ break;
499
+ case NODE_EVSTR:
500
+ ln_eval(self, list->nd_head, ary);
501
+ break;
502
+ default:
503
+ ln_eval(self, list->nd_head, ary);
504
+ break;
505
+ }
506
+ }
507
+ list = list->nd_next;
508
+ }
509
+ }
510
+ break;
511
+
512
+ case NODE_XSTR: /* u1 (%x{ls}) */
513
+ /* Issues rb_funcall(self, '`'...). So I think we have to
514
+ register a call event. */
515
+ EVENT_CALL(node);
516
+ break;
517
+
518
+ case NODE_LIT:
519
+ break;
520
+
521
+ case NODE_DEFN:
522
+ ln_eval(self, node->nd_defn, ary);
523
+ break;
524
+
525
+ case NODE_DEFS:
526
+ if (node->nd_defn) {
527
+ ln_eval(self, node->nd_recv, ary);
528
+ }
529
+ ln_eval(self, node->nd_defn, ary);
530
+ break;
531
+
532
+ case NODE_UNDEF: /* u2 (undef name, ...) */
533
+ #if RUBY_VERSION_CODE >= 185
534
+ /*** ...
535
+ rb_undef(ruby_class, rb_to_id(rb_eval(self, node->u2.node)));
536
+ ...
537
+ ***/
538
+ ln_eval(self, node->u2.node, ary);
539
+ #endif
540
+ break;
541
+
542
+ case NODE_ALIAS: /* u1 u2 (alias :blah :blah2) */
543
+ #if RUBY_VERSION_CODE >= 185
544
+ ln_eval(self, node->nd_1st, ary);
545
+ ln_eval(self, node->nd_2nd, ary);
546
+ #endif
547
+ break;
548
+ case NODE_VALIAS: /* u1 u2 (alias $global $global2) */
549
+ break;
550
+
551
+ case NODE_CLASS:
552
+ if (node->nd_super) {
553
+ ln_eval(self, node->nd_super, ary);
554
+ }
555
+ ln_eval(self, node->nd_body, ary);
556
+ break;
557
+
558
+ case NODE_MODULE:
559
+ ln_eval(self, node->nd_body, ary);
560
+ break;
561
+
562
+ case NODE_SCLASS:
563
+ ln_eval(self, node->nd_recv, ary);
564
+ ln_eval(self, node->nd_body, ary);
565
+ break;
566
+
567
+ case NODE_DEFINED:
568
+ ln_eval(self, node->nd_head, ary);
569
+ break;
570
+
571
+ case NODE_NEWLINE:
572
+ EVENT_LINE(node);
573
+ node = node->nd_next;
574
+ goto again;
575
+
576
+ case NODE_CFUNC:
577
+ case NODE_IFUNC:
578
+ break;
579
+
580
+ #if RUBY_VERSION_CODE >= 190
581
+ case NODE_ERRINFO:
582
+ case NODE_VALUES:
583
+ case NODE_PRELUDE:
584
+ case NODE_LAMBDA:
585
+ rb_warn("Ruby 1.9 is very different. You shouldn't have gotten here.");
586
+ break;
587
+ #endif
588
+
589
+ case NODE_BMETHOD: /* define_method (or rb_mod_define_method) with a block */
590
+ {
591
+ struct BLOCK *data;
592
+ Data_Get_Struct(node->nd_cval, struct BLOCK, data);
593
+ if (!(data->var == 0 || data->var == (NODE *)1 ||
594
+ data->var == (NODE *)2)) {
595
+ /* block doesn't have args. */
596
+ ln_eval(self, data->var, ary);
597
+ }
598
+ ln_eval(self, data->body, ary);
599
+ break;
600
+ }
601
+ break;
602
+
603
+ #if RUBY_VERSION_CODE < 190
604
+ case NODE_DMETHOD:
605
+ {
606
+ struct METHOD *data;
607
+ Data_Get_Struct(node->nd_cval, struct METHOD, data);
608
+ ln_eval(self, data->body, ary);
609
+ break;
610
+ }
611
+ #endif
612
+
613
+ case NODE_METHOD:
614
+ ln_eval(self, node->nd_3rd, ary);
615
+ break;
616
+
617
+
618
+ case NODE_ARGS: {
619
+ if (node->nd_opt) {
620
+ ln_eval(self, node->nd_opt, ary);
621
+ }
622
+ } break;
623
+
624
+ case NODE_ATTRSET:
625
+ break;
626
+
627
+ /*
628
+ // rescue body:
629
+ // begin stmt rescue exception => var; stmt; [rescue e2 => v2; s2;]* end
630
+ // stmt rescue stmt
631
+ // a = b rescue c
632
+ // NODE_RESBODY doesn't appear in 1.8.6's rb_eval
633
+ */
634
+ case NODE_RESBODY:
635
+ if (node->nd_3rd) {
636
+ ln_eval(self, node->nd_3rd, ary);
637
+ }
638
+ ln_eval(self, node->nd_2nd, ary);
639
+ ln_eval(self, node->nd_1st, ary);
640
+ break;
641
+
642
+ /* Nodes we found but have yet to decypher */
643
+ /* I think these are all runtime only... not positive but... */
644
+ case NODE_MEMO: /* enum.c zip */
645
+ case NODE_CREF:
646
+ /* #defines: */
647
+ /* case NODE_LMASK: */
648
+ /* case NODE_LSHIFT: */
649
+
650
+ default:
651
+ rb_warn("Unhandled node '%s'", NODE2NAME[nd_type(node)]);
652
+ if (RNODE(node)->u1.node != NULL) rb_warning("unhandled u1 value");
653
+ if (RNODE(node)->u2.node != NULL) rb_warning("unhandled u2 value");
654
+ if (RNODE(node)->u3.node != NULL) rb_warning("unhandled u3 value");
655
+ if (RTEST(ruby_debug))
656
+ fprintf(stderr, "u1 = %p u2 = %p u3 = %p\n",
657
+ (void*)node->nd_1st, (void*)node->nd_2nd, (void*)node->nd_3rd);
658
+ break;
659
+ }
660
+ finish:
661
+ if (contnode) {
662
+ node = contnode;
663
+ contnode = 0;
664
+ goto again;
665
+ }
666
+ if (RTEST(ruby_debug)) {
667
+ char fmt[30] = { '\0', };
668
+ indent_level -= 2;
669
+ snprintf(fmt, sizeof(fmt), "%%%ds", indent_level+1);
670
+ fprintf(stderr, fmt, "]\n");
671
+ }
672
+
673
+ } /* ln_eval */
674
+
675
+ /* Return a list of trace hook line numbers for the string in Ruby source src*/
676
+ static VALUE
677
+ lnums_for_str(VALUE self, VALUE src) {
678
+ VALUE result = rb_ary_new(); /* The returned array of line numbers. */
679
+ NODE *node = NULL;
680
+ int critical;
681
+
682
+ ruby_nerrs = 0;
683
+ StringValue(src); /* Check that src is a string. */
684
+
685
+ critical = rb_thread_critical;
686
+ rb_thread_critical = Qtrue;
687
+
688
+ /* Making ruby_in_eval nonzero signals rb_compile_string not to save
689
+ source in SCRIPT_LINES__. */
690
+ ruby_in_eval++;
691
+ node = rb_compile_string("(numbers_for_str)", src, 1);
692
+ ruby_in_eval--;
693
+
694
+ rb_thread_critical = critical;
695
+
696
+ if (ruby_nerrs > 0) {
697
+ ruby_nerrs = 0;
698
+ #if RUBY_VERSION_CODE < 190
699
+ ruby_eval_tree_begin = 0;
700
+ #endif
701
+ rb_exc_raise(ruby_errinfo);
702
+ }
703
+
704
+ if (RTEST(ruby_debug)) {
705
+ indent_level = 0;
706
+ }
707
+ ln_eval(self, node, result);
708
+ return result;
709
+ }
710
+
711
+ void Init_trace_nums(void)
712
+ {
713
+ mTraceLineNumbers = rb_define_module("TraceLineNumbers");
714
+ rb_define_module_function(mTraceLineNumbers, "lnums_for_str",
715
+ lnums_for_str, 1);
716
+ }