File Coverage

blib/lib/Devel/Chitin.pm
Criterion Covered Total %
statement 125 427 29.2
branch 14 140 10.0
condition 2 76 2.6
subroutine 34 92 36.9
pod 36 51 70.5
total 211 786 26.8


line stmt bran cond sub pod time code
1 34     34   181919 use 5.008009;
  34         273  
2 34     34   141 use warnings;
  34         51  
  34         746  
3 34     34   141 use strict;
  34         52  
  34         1427  
4              
5             package Devel::Chitin;
6              
7             our $VERSION = '0.16';
8              
9 34     34   174 use Scalar::Util;
  34         50  
  34         2219  
10 34     34   13376 use IO::File;
  34         241348  
  34         3464  
11 34     34   238 use B;
  34         58  
  34         1142  
12              
13 34     34   12491 use Devel::Chitin::Actionable; # Breakpoints and Actions
  34         87  
  34         779  
14 34     34   10600 use Devel::Chitin::Eval;
  34         72  
  34         962  
15 34     34   10775 use Devel::Chitin::Stack;
  34         75  
  34         931  
16 34     34   10366 use Devel::Chitin::Location;
  34         1262  
  34         6716  
17 34     34   15286 use Devel::Chitin::SubroutineLocation;
  34         69  
  34         828  
18 34     34   9932 use Devel::Chitin::Exception;
  34         66  
  34         735  
19 34     34   12139 use Devel::Chitin::OpTree;
  34         105  
  34         1205  
20              
21 34     34   164 use base 'Exporter';
  34         58  
  34         17716  
22             our @EXPORT_OK = qw( $VERSION );
23              
24             # lexicals shared between the interface package and the DB package
25             my(%attached_clients,
26             @attached_clients,
27             %trace_clients,
28             $is_initialized,
29             @pending_eval,
30             $current_location,
31             $previous_location,
32             @new_watch_exprs,
33             @watch_exprs,
34             );
35             sub attach {
36 4     4 1 491 my $self = shift;
37              
38 4 100       12 unless ($attached_clients{$self}) {
39 3         7 $attached_clients{$self} = $self;
40 3         5 push @attached_clients, $self;
41              
42 3 50       7 if ($is_initialized) {
43 0         0 $self->init();
44             }
45             }
46 4         13 return $self;
47             }
48              
49             sub _turn_off_trace_if_not_needed {
50 6   33 6   30 $DB::trace = %trace_clients || @watch_exprs;
51             }
52              
53             sub detach {
54 6     6 1 13 my $self = shift;
55 6         12 my $deleted = delete $attached_clients{$self};
56 6         9 delete $trace_clients{$self};
57 6         14 _turn_off_trace_if_not_needed();
58 6 100       11 if ($deleted) {
59 3         9 for (my $i = 0; $i < @attached_clients; $i++) {
60 5 100       19 my $same = ref($self)
61             ? Scalar::Util::refaddr($self) == Scalar::Util::refaddr($attached_clients[$i])
62             : $self eq $attached_clients[$i];
63 5 100       9 if ($same) {
64 3         9 splice(@attached_clients, $i, 1);
65             }
66             }
67             }
68 6         21 return $deleted;
69             }
70              
71              
72             sub _clients {
73 14     14   61 return @attached_clients;
74             }
75              
76             ## Methods callable from client code
77              
78             sub step {
79 0     0 1 0 $DB::single=1;
80             }
81              
82             sub stepover {
83 0     0 1 0 local $DB::in_debugger = 1;
84 0         0 $DB::single=1;
85 0         0 $DB::step_over_depth = $DB::stack_depth;
86 0         0 return 1;
87             }
88              
89             sub stepout {
90 0     0 1 0 $DB::single=0;
91 0         0 $DB::step_over_depth = $DB::stack_depth - 1;
92 0         0 return 1;
93             }
94              
95             # Should support running to a subname, or file+line
96             sub continue {
97 0     0 1 0 $DB::single=0;
98 0         0 return 1;
99             }
100              
101             sub trace {
102 0     0 1 0 local $DB::in_debugger = 1;
103 0         0 my $class = shift;
104 0         0 my $rv;
105 0 0       0 if (@_) {
106 0         0 my $new_val = shift;
107 0 0       0 if ($new_val) {
108             # turning trace on
109 0         0 $trace_clients{$class} = $class;
110 0         0 $DB::trace = 1;
111 0         0 $rv = 1;
112             } else {
113             # turning it off
114 0         0 delete $trace_clients{$class};
115 0         0 _turn_off_trace_if_not_needed();
116 0         0 $rv = 0;
117             }
118              
119             } else {
120             # Checking value
121 0         0 $rv = exists $trace_clients{$class};
122             }
123 0         0 return $rv;
124             }
125              
126              
127              
128             sub eval {
129 0     0 1 0 my($class, $eval_string, $wantarray, $cb) = @_;
130 0         0 push @pending_eval, [ $eval_string, $wantarray, $cb ];
131             }
132              
133              
134             sub eval_at {
135 0     0 1 0 my($class, $eval_string, $level) = @_;
136              
137 34     34   208 { no warnings 'numeric';
  34         64  
  34         12051  
  0         0  
138 0 0       0 $level = 0 if ($level < 1);
139             }
140              
141             }
142              
143             sub stack {
144 0     0 1 0 return Devel::Chitin::Stack->new();
145             }
146              
147             sub current_location {
148 0     0 1 0 return $current_location;
149             }
150              
151             sub disable_debugger {
152             # Setting $^P disables single stepping and subrouting entry
153             # but if the program sets $DB::single explicitly, it'll still enter DB()
154 0     0 1 0 $^P = 0; # Stops single-stepping
155 0         0 $DB::debugger_disabled = 1;
156             }
157              
158             sub is_loaded {
159 0     0 1 0 my($self, $filename) = @_;
160             #no strict 'refs';
161 0         0 return $main::{'_<' . $filename};
162             }
163              
164             sub loaded_files {
165 0     0 1 0 my @files = grep /^_
166 0         0 return map { substr($_,2) } @files; # remove the <_
  0         0  
167             }
168              
169             sub add_watchexpr {
170 0     0 0 0 my($class, $expr) = @_;
171 0         0 $DB::trace = 1;
172 0         0 push @new_watch_exprs, { expr => $expr, client => $class, value => undef };
173             }
174              
175             sub remove_watchexpr {
176 0     0 0 0 my($class, $expr) = @_;
177 0         0 my $deleted;
178              
179             SEARCH:
180 0         0 foreach my $store ( \@watch_exprs, \@new_watch_exprs) {
181 0         0 for (my $i = 0; $i < @$store; $i++) {
182 0 0 0     0 if ($store->[$i]->{client} eq $class
183             and
184             $store->[$i]->{expr} eq $expr
185             ) {
186 0         0 $deleted = splice(@$store, $i, 1);
187 0         0 last SEARCH;
188             }
189             }
190             }
191              
192 0         0 _turn_off_trace_if_not_needed();
193              
194 0         0 return $deleted;
195             }
196              
197             sub is_breakable {
198 0     0 1 0 my($class, $filename, $line) = @_;
199              
200 34     34   225 use vars qw(@dbline);
  34         70  
  34         30108  
201 0         0 local(*dbline) = $main::{'_<' . $filename};
202 0         0 return $dbline[$line] + 0; # FIXME change to == 0
203             }
204              
205             sub add_break {
206 0     0 0 0 my $self = shift;
207 0         0 Devel::Chitin::Breakpoint->new(@_);
208             }
209              
210             sub get_breaks {
211 0     0 0 0 my $self = shift;
212 0         0 my %params = @_;
213 0 0       0 if (defined $params{file}) {
214 0         0 return Devel::Chitin::Breakpoint->get(@_);
215             } else {
216 0         0 return map { Devel::Chitin::Breakpoint->get(@_, file => $_) }
  0         0  
217             $self->loaded_files;
218             }
219             }
220              
221             sub remove_break {
222 0     0 0 0 my $self = shift;
223 0 0       0 if (ref $_[0]) {
224             # given a breakpoint object
225 0         0 shift->delete();
226             } else {
227             # given breakpoint params
228 0         0 Devel::Chitin::Breakpoint->delete(@_);
229             }
230             }
231              
232             sub add_action {
233 0     0 0 0 my $self = shift;
234 0         0 Devel::Chitin::Action->new(@_);
235             }
236              
237             sub remove_action {
238 0     0 0 0 my $self = shift;
239 0 0       0 if (ref $_[0]) {
240             # given an action object
241 0         0 shift->delete();
242             } else {
243             # given breakpoint params
244 0         0 Devel::Chitin::Action->delete(@_);
245             }
246             }
247              
248             sub get_actions {
249 0     0 0 0 my $self = shift;
250 0         0 my %params = @_;
251 0 0       0 if (defined $params{file}) {
252 0         0 Devel::Chitin::Action->get(@_);
253             } else {
254 0         0 return map { Devel::Chitin::Action->get(@_, file => $_) }
  0         0  
255             $self->loaded_files;
256             }
257             }
258              
259             sub get_var_at_level {
260 0     0 1 0 my($class, $varname, $level) = @_;
261              
262 0         0 require Devel::Chitin::GetVarAtLevel;
263 0         0 return Devel::Chitin::GetVarAtLevel::get_var_at_level($varname, $level);
264             }
265              
266              
267             sub subroutine_location {
268 0     0 1 0 my $class = shift;
269 0         0 my $subname = shift;
270 0         0 return Devel::Chitin::SubroutineLocation->new_from_db_sub($subname);
271             }
272              
273             # NOTE: This postpones until a named file is loaded.
274             # Have another interface for postponing until a module is loaded
275             sub postpone {
276 0     0 1 0 my($class, $filename, $sub) = @_;
277              
278 0 0       0 if ($class->is_loaded($filename)) {
279             # already loaded, run immediately
280 0         0 $sub->($filename);
281             } else {
282 0   0     0 $DB::postpone_until_loaded{$filename} ||= [];
283 0         0 push @{ $DB::postpone_until_loaded{$filename} }, $sub;
  0         0  
284             }
285             }
286              
287             sub user_requested_exit {
288 0     0 1 0 $DB::user_requested_exit = 1;
289             }
290              
291             sub file_source {
292 0     0 1 0 my($class, $file) = @_;
293              
294 0         0 my $glob = $main::{'_<' . $file};
295 0 0       0 return unless $glob;
296 0         0 return *{$glob}{ARRAY};
  0         0  
297             }
298              
299             my %optrees;
300             our $current_sub;
301             sub _get_optree_for_current_sub {
302 0     0   0 my $loc = current_location;
303              
304 0 0       0 my $optree_cache_key = ref($current_sub) ? "$current_sub" : $loc->subroutine;
305 0 0 0     0 my $optree = $optrees{$optree_cache_key} ||= Devel::Chitin::OpTree->build_from_location(ref($current_sub) ? $current_sub : $loc);
306             }
307              
308             # Some OPs don't deparse to anything useful on their own
309             my %fragment_transforms = (
310             enterloop => sub { shift->sibling->children->[0]->children->[0] }, # deparse the conditional
311             leaveloop => sub { shift->children->[0]->sibling->children->[0]->children->[0] }, # deparse the conditional
312             pushmark => sub {
313             # deparse either the list or entersub
314             my $parent = shift->parent;
315             my $grandparent = $parent->parent;
316             $grandparent->op->name eq 'entersub'
317             ? $grandparent
318             : $parent;
319             },
320             padrange => sub {
321             # deparse either the list or entersub
322             my $parent = shift->parent;
323             my $grandparent = $parent->parent;
324             $grandparent->op->name eq 'entersub'
325             ? $grandparent
326             : $parent;
327             },
328             );
329              
330             sub next_statement {
331 0     0 1 0 my $class = shift;
332              
333 0         0 my $optree = _get_optree_for_current_sub();
334 0         0 my $loc = $class->current_location();
335 0         0 $loc = $class->_fixup_location_inside_eval($loc);
336              
337 0         0 my $callsite = $loc->callsite;
338 0         0 my($last_cop, $current_op);
339             BREAKOUT:
340 0         0 for(1) {
341             $optree->walk_inorder(sub {
342 0     0   0 my $op = shift;
343 0 0       0 $last_cop = $op if ($op->isa('Devel::Chitin::OpTree::COP'));
344 0 0       0 if (${$op->op} == $callsite) {
  0         0  
345 0         0 $current_op = $op;
346 34     34   222 no warnings 'exiting';
  34         69  
  34         34302  
347 0         0 last BREAKOUT;
348             }
349 0         0 });
350             }
351              
352 0 0       0 my $op_to_deparse = $last_cop ? $last_cop->sibling : $current_op;
353              
354 0 0 0     0 if (my $xform = $fragment_transforms{$op_to_deparse->op->name}) {
    0 0        
    0 0        
      0        
      0        
355 0         0 local $@;
356 0   0     0 $op_to_deparse = eval { $xform->($op_to_deparse) } || $op_to_deparse;
357              
358             } elsif ($op_to_deparse->is_null
359             and $op_to_deparse->children
360             and $op_to_deparse->children->[0]->is_if_statement
361             ) {
362 0         0 $op_to_deparse = $op_to_deparse->children->[0]->children->[0]; # deparse the if-condition, not the whole block
363              
364             # !!! special deparsing for landing on a block-map/grep...
365             # return just the list we're mapping/grepping over
366             } elsif ($op_to_deparse->op->name eq 'mapwhile' or $op_to_deparse->op->name eq 'grepwhile'
367             and ( $op_to_deparse->first->children->[1]->first->is_scopelike
368             or
369             ( $op_to_deparse->first->children->[1]->first->is_null
370             and
371             $op_to_deparse->first->children->[1]->first->first->is_scopelike
372             )
373             )
374             ) {
375             # This list contains a pushmark, the block, then all the args
376 0         0 my $map_args = $op_to_deparse->first->children;
377 0         0 my @maplist = @$map_args[2 .. $#$map_args];
378 0         0 return join(', ', map { $_->deparse } @maplist);
  0         0  
379             }
380              
381 0 0       0 if ($op_to_deparse) {
382 0         0 local $@;
383 0         0 my $deparsed = eval { $op_to_deparse->deparse };
  0         0  
384 0 0       0 if ($@) {
385 0         0 warn "failed to deparse: $@";
386 0         0 $optree->print_as_tree($callsite);
387             }
388 0         0 return $deparsed;
389             } else {
390 0         0 Carp::carp("Cannot find current opcode at $callsite in ".$loc->subroutine);
391 0         0 return '';
392             }
393             }
394              
395             sub next_fragment {
396 0     0 1 0 my($class, $parents) = @_;
397              
398 0         0 my $optree = _get_optree_for_current_sub();
399 0         0 my $loc = $class->current_location();
400 0         0 $loc = $class->_fixup_location_inside_eval($loc);
401              
402 0         0 my $callsite = $loc->callsite;
403 0         0 my $current_op = Devel::Chitin::OpTree->_obj_for_op(\$callsite);
404              
405 0 0       0 if (defined $parents) {
    0          
    0          
406 0   0     0 while($current_op && $parents--) {
407 0         0 my $parent = $current_op->parent;
408 0 0       0 $current_op = $parent if $parent;
409             }
410             } elsif (! $current_op) {
411 0         0 Carp::carp("Cannot find current opcode at $callsite in ".$loc->subroutine);
412 0         0 return '';
413             } elsif (my $xform = $fragment_transforms{$current_op->op->name}) {
414 0         0 local $@;
415 0         0 $current_op = eval { $xform->($current_op) };
  0         0  
416             }
417              
418 0 0       0 if ($current_op) {
419 0         0 local $@;
420 0         0 my $deparsed = eval { $current_op->deparse };
  0         0  
421 0 0       0 if ($@) {
422 0         0 warn "failed to deparse: $@\ncurrent op name ",$current_op->op->name,"\n";
423 0         0 $optree->print_as_tree($callsite);
424             }
425 0         0 return $deparsed;
426             } else {
427 0         0 Carp::carp("Cannot find current opcode at $callsite in ".$loc->subroutine);
428 0         0 return '';
429             }
430             }
431              
432             sub _fixup_location_inside_eval {
433 0     0   0 my($class, $loc) = @_;
434              
435 0 0       0 if ($loc->subroutine eq '(eval)') {
436 0         0 my $stack = $class->stack->iterator;
437 0         0 my $frame;
438 0         0 for($frame = $stack->(); $frame; $frame = $stack->()) {
439 0 0       0 last if $frame->subroutine ne '(eval)';
440             }
441 0 0       0 if ($frame) {
442             return Devel::Chitin::Location->new(
443 0         0 (map { $_ => $frame->$_ } qw(package filename line subroutine)),
  0         0  
444             callsite => $loc->callsite
445             );
446             }
447             }
448 0         0 return $loc;
449             }
450              
451             ## Methods called by the DB core - override in clients
452              
453       0 1   sub init {}
454       0 1   sub poll {}
455 0     0 1 0 sub idle { 1;}
456       0 0   sub cleanup {}
457       0 1   sub notify_stopped {}
458       0 1   sub notify_resumed {}
459       0 1   sub notify_trace {}
460       0 1   sub notify_trace_resumed {}
461       0 1   sub notify_fork_parent {}
462       0 1   sub notify_fork_child {}
463       0 1   sub notify_program_terminated {}
464       0 1   sub notify_program_exit {}
465       0 1   sub notify_uncaught_exception {}
466       0 1   sub notify_watch_expr {}
467              
468             sub _do_each_client {
469 66     66   603 my($method, @args) = @_;
470              
471 66         716 $_->$method(@args) foreach @attached_clients;
472             }
473              
474             package DB;
475              
476 34     34   234 use vars qw( %dbline @dbline );
  34         60  
  34         5135  
477              
478             our($stack_depth,
479             $single,
480             $signal,
481             $trace,
482             $debugger_disabled,
483             $no_stopping,
484             $step_over_depth,
485             $ready,
486             @saved,
487             $usercontext,
488             $in_debugger,
489             $finished,
490             $user_requested_exit,
491             @AUTOLOAD_names,
492             $sub,
493             $uncaught_exception,
494             %postpone_until_loaded,
495             );
496              
497             BEGIN {
498 34     34   94 $stack_depth = 0;
499 34         73 $single = 0;
500 34         70 $trace = 0;
501 34         53 $debugger_disabled = 0;
502 34         42 $no_stopping = 0;
503 34         59 $step_over_depth = undef;
504 34         63 $ready = 0;
505 34         61 @saved = ();
506 34         54 $usercontext = '';
507 34         55 $in_debugger = 0;
508              
509             # Controlling program end of life
510 34         49 $finished = 0;
511 34         59 $user_requested_exit = 0;
512              
513             # Remember AUTOLOAD sub names
514 34         5726 @AUTOLOAD_names = ();
515             }
516              
517             sub save {
518             # Save eval failure, command failure, extended OS error, output field
519             # separator, input record separator, output record separator and
520             # the warning setting.
521 0     0 0 0 @saved = ( $@, $!, $^E, $,, $/, $\, $^W );
522              
523 0         0 $, = ""; # output field separator is null string
524 0         0 $/ = "\n"; # input record separator is newline
525 0         0 $\ = ""; # output record separator is null string
526 0         0 $^W = 0; # warnings are off
527             }
528              
529             sub restore {
530 0     0 0 0 ( $@, $!, $^E, $,, $/, $\, $^W ) = @saved;
531             }
532              
533             sub _evaluate_watch_exprs {
534             EXPR:
535 0     0   0 foreach my $details ( @watch_exprs ) {
536 0         0 my($current_value) = _eval_in_program_context($details->{expr}, 1);
537 0         0 my $old_value = $details->{value};
538              
539 0 0       0 if (@$current_value != @$old_value) {
540 0         0 $details->{client}->notify_watch_expr($previous_location, $details->{expr}, $old_value, $current_value);
541 0         0 $details->{value} = $current_value;
542 0         0 next EXPR;
543             }
544              
545 0         0 for (my $i = 0; $i < @$current_value; $i++) {
546 34     34   185 no warnings 'uninitialized';
  34         49  
  34         14867  
547 0 0 0     0 if ((defined($current_value->[$i]) xor defined($old_value->[$i]))
      0        
548             or
549             $current_value->[$i] ne $old_value->[$i]
550             ) {
551 0         0 $details->{client}->notify_watch_expr($previous_location, $details->{expr}, $old_value, $current_value);
552 0         0 $details->{value} = $current_value;
553 0         0 next EXPR;
554             }
555             }
556             }
557             }
558              
559             sub is_breakpoint {
560 0     0 0 0 my($package, $filename, $line) = @_;
561              
562 0 0 0     0 if ($single and defined($step_over_depth) and $step_over_depth < $stack_depth) {
      0        
563             # This is from a step-over
564 0         0 $single = 0;
565 0         0 return 0;
566             }
567              
568 0 0 0     0 if ($single || $signal) {
569 0         0 $single = $signal = 0;
570 0         0 return 1;
571             }
572              
573 0         0 local(*dbline)= $main::{'_<' . $filename};
574              
575 0         0 my $should_break = 0;
576 0         0 my $breakpoint_key = Devel::Chitin::Breakpoint->type;
577 0 0 0     0 if ($dbline{$line} && $dbline{$line}->{$breakpoint_key}) {
578 0         0 my @delete;
579 0         0 foreach my $condition ( @{ $dbline{$line}->{$breakpoint_key} }) {
  0         0  
580 0 0       0 next if $condition->inactive;
581 0         0 my $code = $condition->code;
582 0 0       0 if ($code eq '1') {
583 0         0 $should_break = 1;
584             } else {
585 0         0 ($should_break) = _eval_in_program_context($condition->code, 0);
586             }
587 0 0       0 push @delete, $condition if $condition->once;
588             }
589 0         0 $_->delete for @delete;
590             }
591              
592 0 0       0 if ($should_break) {
593 0         0 $single = $signal = 0;
594             }
595 0         0 return $should_break;
596             }
597              
598              
599             sub _parent_stack_location {
600 32     32   2514 my($package, $filename, $line) = caller(1);
601 32         1115 my(undef, undef, undef, $subname) = caller(2);
602 32         874 my $callsite = Devel::Chitin::Location::get_callsite(2);
603 32   50     673 $subname ||= 'MAIN';
604 32         511 return ($package, $filename, $line, $subname, $callsite);
605             }
606              
607             BEGIN {
608             # Code to get control when the debugged process forks
609             *CORE::GLOBAL::fork = sub {
610 32     32   27921 my $pid = CORE::fork();
611 32 50       2548 return $pid unless $ready;
612              
613 32         1711 my($package, $filename, $line, $subname, $callsite) = _parent_stack_location();
614 32         1900 my $location = Devel::Chitin::Location->new(
615             'package' => $package,
616             line => $line,
617             filename => $filename,
618             subroutine => $subname,
619             callsite => $callsite,
620             );
621              
622 32 50       737 my $notify = $pid ? 'notify_fork_parent' : 'notify_fork_child';
623 32         570 Devel::Chitin::_do_each_client($notify, $location, $pid);
624 32         1249 return $pid;
625 34     34   21138 };
626             };
627              
628             # Reporting uncaught exceptions back to the debugger clients
629             # inside the handler, note the value for $^S:
630             # undef - died while parsing something
631             # 1 - died while executing an eval
632             # 0 - Died not inside an eval
633             # We could re-throw the die if $^S is 1
634             $SIG{__DIE__} = sub {
635             if (defined($^S) && $^S == 0) {
636             $in_debugger = 1;
637             my $exception = $_[0];
638             # It's interesting to note that if we pass an arg to caller() to
639             # find out the offending subroutine name, then the line reported
640             # changes. Instead of reporting the line the exception occurred
641             # (which it correctly does with no args), it returns the line which
642             # called the function which threw the exception.
643             # We'll work around it by calling it twice
644             my($package, $filename, $line, $subname, $callsite) = _parent_stack_location();
645              
646             $uncaught_exception = Devel::Chitin::Exception->new(
647             'package' => $package,
648             line => $line,
649             filename => $filename,
650             exception => $exception,
651             subroutine => $subname,
652             callsite => $callsite,
653             );
654             # After we fall off the end, the interpreter will try and exit,
655             # triggering the END block that calls DB::fake::at_exit()
656             }
657             };
658              
659              
660             sub _execute_actions {
661 0     0   0 my($filename, $line) = @_;
662 0         0 local(*dbline) = $main::{'_<' . $filename};
663 0 0 0     0 if ($dbline{$line} && $dbline{$line}->{action}) {
664 0         0 my @delete;
665 0         0 foreach my $action ( @{ $dbline{$line}->{action}} ) {
  0         0  
666 0 0       0 next if $action->inactive;
667 0         0 _eval_in_program_context($action->code, undef);
668 0 0       0 push @delete, $action if $action->once;
669             }
670 0         0 $_->delete for @delete;
671             }
672             }
673              
674             sub fill_in_values_for_new_watch_exprs {
675 0     0 0 0 foreach my $detail ( @new_watch_exprs ) {
676 0         0 my($value) = _eval_in_program_context($detail->{expr}, 1);
677 0         0 $detail->{value} = $value;
678 0         0 push @watch_exprs, $detail;
679             }
680 0         0 @new_watch_exprs = ();
681             }
682              
683             sub DB {
684 0 0 0 0 0 0 return if (!$ready or $debugger_disabled or $in_debugger);
      0        
685              
686 0         0 local($in_debugger) = 1;
687              
688 0         0 my($package, $filename, $line) = caller;
689 0         0 my(undef, undef, undef, $subroutine) = caller(1);
690 0 0       0 if ($package eq 'DB::fake') {
691 0         0 $package = 'main';
692             }
693 0   0     0 $subroutine ||= 'MAIN';
694              
695 0 0       0 unless ($is_initialized) {
696 0         0 $is_initialized = 1;
697 0         0 Devel::Chitin::_do_each_client('init');
698             }
699              
700             # set up the context for DB::eval, so it can properly execute
701             # code on behalf of the user. We add the package in so that the
702             # code is eval'ed in the proper package (not in the debugger!).
703 0         0 save();
704 0         0 local $usercontext =
705             'no strict; no warnings; ($@, $!, $^E, $,, $/, $\, $^W) = @DB::saved;' . "package $package;";
706              
707 0         0 $current_location = Devel::Chitin::Location->new(
708             'package' => $package,
709             filename => $filename,
710             line => $line,
711             subroutine => $subroutine,
712             callsite => scalar Devel::Chitin::Location::get_callsite(),
713             );
714              
715 0         0 $_->notify_trace($current_location) foreach values(%trace_clients);
716              
717 0         0 _execute_actions($filename, $line);
718              
719 0 0       0 goto RETURN_TO_DEBUGGED_PROGRAM if $no_stopping;
720              
721 0         0 _evaluate_watch_exprs();
722              
723 0 0       0 if (! is_breakpoint($package, $filename, $line)) {
724 0         0 goto RETURN_TO_DEBUGGED_PROGRAM;
725             }
726 0         0 $step_over_depth = undef;
727              
728 0         0 Devel::Chitin::_do_each_client('notify_stopped', $current_location);
729              
730             STOPPED_LOOP:
731 0         0 foreach (1) {
732              
733 0         0 while (my $e = shift @pending_eval) {
734 0         0 _eval_in_program_context(@$e);
735             }
736              
737 0         0 my $should_continue = 0;
738 0         0 until ($should_continue) {
739 0         0 my @ready_clients = grep { $_->poll($current_location) } @attached_clients;
  0         0  
740 0 0       0 last STOPPED_LOOP unless (@ready_clients);
741 0         0 do { $should_continue |= $_->idle($current_location) } foreach @ready_clients;
  0         0  
742             }
743              
744 0 0 0     0 redo if ($finished || @pending_eval);
745             }
746              
747 0         0 fill_in_values_for_new_watch_exprs();
748              
749 0         0 Devel::Chitin::_do_each_client('notify_resumed', $current_location);
750              
751             RETURN_TO_DEBUGGED_PROGRAM:
752              
753 0         0 $_->notify_trace_resumed($current_location) foreach values(%trace_clients);
754              
755 0         0 $previous_location = $current_location;
756 0         0 undef $current_location;
757 0         0 Devel::Chitin::Stack::invalidate();
758 0         0 restore();
759             }
760              
761 0         0 BEGIN {
762 34     34   106 my $sub_serial = 1;
763 34         111 @Devel::Chitin::stack_serial = ( [ 'main::MAIN', $sub_serial++ ] );
764 34         1329 %Devel::Chitin::eval_serial = ();
765              
766             sub _allocate_sub_serial {
767 0     0   0 $sub_serial++;
768             }
769             }
770              
771              
772             # When using Class::Autouse, the B::* objects created below to determine if an
773             # anon sub has a name (such as via Sub::Name) trigger calls to its UNIVERSAL
774             # DESTROY as the B::* objects go out of scope as you step in to a call to
775             # that named sub. This hack gives those classes a DESTROY method to avoid that
776             foreach my $class ( qw(B::HV B::GV B::CV) ) {
777             next if $class->can('DESTROY');
778             my $destroy = $class . '::DESTROY';
779 34     34   199 no strict 'refs';
  34         52  
  34         1868  
780       0     *$destroy = sub {};
781             }
782              
783             sub sub {
784 34     34   187 no strict 'refs';
  34         53  
  34         18032  
785 0 0 0 0 1 0 goto &$sub if (! $ready or index($sub, 'Devel::Chitin::StackTracker') == 0 or $debugger_disabled);
      0        
786             #goto &$sub if (! $ready or $in_debugger or index($sub, 'Devel::Chitin::StackTracker') == 0 or $debugger_disabled);
787              
788 0 0       0 local $Devel::Chitin::current_sub = $sub unless $in_debugger;
789              
790 0         0 local @AUTOLOAD_names = @AUTOLOAD_names;
791 0 0       0 if (index($sub, '::AUTOLOAD', -10) >= 0) {
792 0         0 my $caller_pkg = substr($sub, 0, length($sub)-8);
793 0         0 my $caller_AUTOLOAD = ${ $caller_pkg . 'AUTOLOAD'};
  0         0  
794 0         0 unshift @AUTOLOAD_names, $caller_AUTOLOAD;
795             }
796 0         0 my $stack_tracker;
797 0         0 local @Devel::Chitin::stack_serial = @Devel::Chitin::stack_serial;
798 0 0       0 unless ($in_debugger) {
799 0         0 $stack_depth++;
800 0         0 $stack_tracker = _new_stack_tracker(_allocate_sub_serial());
801              
802 0         0 my $subname = $sub;
803 0 0       0 if (ref $sub) {
804 0         0 my $cv = B::svref_2object($sub);
805 0         0 my $gv = $cv->GV;
806 0 0       0 if (my $name = $gv->NAME) {
807 0         0 my $package = $gv->STASH->NAME;
808 0         0 $subname = join('::', $package, $name);
809             }
810             }
811              
812 0         0 push(@Devel::Chitin::stack_serial, [ $subname, $$stack_tracker]);
813             }
814              
815 0         0 my @rv;
816 0 0       0 if (wantarray) {
    0          
817 0         0 @rv = &$sub;
818             } elsif (defined wantarray) {
819 0         0 $rv[0] = &$sub;
820             } else {
821 0         0 &$sub;
822             }
823              
824 0 0       0 delete $Devel::Chitin::eval_serial{$$stack_tracker} if $stack_tracker;
825              
826 0 0       0 return wantarray ? @rv : $rv[0];
827             }
828              
829             sub _new_stack_tracker {
830 0     0   0 my $token = shift;
831 0         0 my $self = bless \$token, 'Devel::Chitin::StackTracker';
832             }
833              
834             sub Devel::Chitin::StackTracker::DESTROY {
835 0     0   0 $stack_depth--;
836 0 0 0     0 $single = 1 if (defined($step_over_depth) and $step_over_depth >= $stack_depth);
837             }
838              
839              
840              
841             # This gets called after a require'd file is compiled, but before it's executed
842             # it's called as DB::postponed(*{"_<$filename"})
843             # We can use this to break on module load, for example.
844             # If $DB::postponed{$subname} exists, then this is called as
845             # DB::postponed($subname)
846             sub postponed {
847 0     0 0 0 my($filename) = ($_[0] =~ m/_\<(.*)$/);
848              
849 0 0       0 if (my $actions = delete $postpone_until_loaded{$filename}) {
850 0         0 $_->($filename) foreach @$actions;
851             }
852             }
853              
854             END {
855 34     34   5132830 $trace = 0;
856              
857 34 50       397 return if $debugger_disabled;
858              
859 34         304 $single=0;
860 34         281 $in_debugger = 1;
861              
862 34         154 eval {
863 34 50       296 Devel::Chitin::_do_each_client('notify_uncaught_exception', $uncaught_exception) if $uncaught_exception;
864              
865 34 50       380 if ($user_requested_exit) {
866 0         0 Devel::Chitin::_do_each_client('notify_program_exit');
867             } else {
868 34         307 Devel::Chitin::_do_each_client('notify_program_terminated', $?);
869 34         150 $finished = 1;
870             # These two will trigger DB::DB and the event loop
871 34         146 $in_debugger = 0;
872 34         139 $single=1;
873 34         298 Devel::Chitin::exiting::at_exit();
874             }
875             }
876             }
877              
878             package Devel::Chitin::exiting;
879             sub at_exit {
880 34     34   45 1;
881             }
882              
883             package DB;
884 34     34   1222 BEGIN { $DB::ready = 1; }
885              
886             1;
887              
888             __END__