File Coverage

blib/lib/Devel/PDB.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             # vi: set autoindent shiftwidth=4 tabstop=8 softtabstop=4 expandtab:
2             package DB;
3              
4 1     1   57501 use 5.006001;
  1         4  
  1         39  
5 1     1   5 use strict;
  1         3  
  1         32  
6 1     1   5 use warnings;
  1         6  
  1         28  
7              
8 1     1   12 use Carp;
  1         2  
  1         103  
9 1     1   12 use B qw(svref_2object comppadlist class);
  1         1  
  1         70  
10 1     1   865 use B::Showlex;
  1         16980  
  1         29  
11 1     1   517 use Curses;
  0            
  0            
12             use Curses::UI;
13             use Data::Dumper;
14              
15             use Devel::PDB::Source;
16              
17             use vars qw(*dbline);
18              
19             our $VERSION = '0.06';
20              
21             our $single;
22             our $sub;
23             our $trace;
24             our $signal;
25             our $stack_depth;
26             our @stack;
27             our $current_sub;
28              
29             my @complied;
30             my $inited = 0;
31             my $cui;
32             my $sv_win;
33             my $sv;
34             my $exit = 0;
35             my $yield;
36             my %sources;
37             my $new_single;
38             my $current_source;
39             my $evalarg;
40             my $package;
41             my $filename;
42             my $line;
43             my @watch_exprs;
44             my $update_watch_list;
45              
46             my $lower_win;
47             my $auto_win;
48             my $watch_win;
49             my $padvar_list;
50             my $watch_list;
51              
52             my $padlist_scope;
53             my %padlist;
54             my @padlist_disp;
55              
56             my $stdout;
57             my $output;
58              
59             $trace = $signal = $single = 0;
60             $stack_depth = 0;
61             @stack = (0);
62              
63             END {
64             open STDOUT, ">&", $stdout if $stdout;
65             $single = 0;
66             }
67              
68             our %def_style = (
69             -bg => 'white',
70             -fg => 'blue',
71             -bbg => 'blue',
72             -bfg => 'white',
73             -tbg => 'white',
74             -tfg => 'blue',
75             );
76              
77             sub db_quit {
78             return if not $cui->dialog(
79             -title => 'Quit Debugger',
80             -buttons => ['yes', 'no'],
81             -message => 'Do you really want to quit?',
82             %def_style,
83             );
84             $single = 0;
85             for (my $i = 0; $i <= $stack_depth; ++$i) {
86             $stack[$i] = 0;
87             }
88             #print(STDERR $_, "\n") foreach (@complied);
89             exit(0);
90             }
91              
92             sub db_cont {
93             $new_single = 0;
94             for (my $i = 0; $i <= $stack_depth; ++$i) {
95             $stack[$i] &= ~1;
96             }
97             $yield = 1;
98             }
99              
100             sub db_step_in {
101             $new_single = 1;
102             $yield = 1;
103             }
104              
105             sub db_step_over {
106             $new_single = 2;
107             $yield = 1;
108             }
109              
110             sub db_step_out {
111             $new_single = 0;
112             $stack[-1] &= ~1;
113             $yield = 1;
114             }
115              
116             sub db_toggle_break {
117             local (*dbline) = $main::{'_<' . $current_source->filename};
118             $current_source->toggle_break;
119             }
120              
121             sub db_add_watch_expr {
122             my $expr = $cui->question(-question => 'Please enter an expression to watch for', %def_style);
123             return if !$expr;
124             push @watch_exprs, { name => $expr };
125             $update_watch_list = 1;
126             }
127              
128             sub ui_open_file {
129             my ($title, $files) = @_;
130              
131             my $filename = $cui->tempdialog('Devel::PDB::Dialog::FileBrowser',
132             -title => $title,
133             -files => $files,
134             %def_style,
135             );
136             if ($filename) {
137             my $source = $current_source = get_source($filename);
138             $sv->source($source) if $source;
139             $sv->intellidraw;
140             }
141             }
142              
143             sub ui_adjust_vert_parts {
144             my $delta = shift;
145             return if $delta > 0 && $sv_win->{-padbottom} >= $cui->{-height} - $sv_win->{-padtop} - 5 or
146             $delta < 0 && $lower_win->{-height} <= 5;
147             $sv_win->{-padbottom} += $delta;
148             $lower_win->{-height} += $delta;
149             $cui->layout_contained_objects;
150             }
151              
152             sub ui_adjust_hori_parts {
153             my $delta = shift;
154             return if $delta > 0 && $auto_win->{-width} >= $cui->{-width} - 15 or
155             $delta < 0 && $auto_win->{-width} <= 15;
156             $auto_win->{-width} += $delta;
157             $watch_win->{-padleft} += $delta;
158             $cui->layout_contained_objects;
159             }
160              
161             sub init {
162             # can anybody tell me why $win->notimeout(1) doesn't work?
163             $ENV{ESCDELAY} = '0';
164              
165             $cui = new Curses::UI(
166             -clear_on_exit => 1,
167             -color_support => 1
168             );
169              
170             if ($Curses::UI::color_support) {
171             my $old_draw = \&Curses::UI::Widget::draw;
172             no warnings;
173             *Curses::UI::Widget::draw = sub (;$) {
174             my ($this) = @_;
175             if (defined $this->{-fg} && defined $this->{-bg}) {
176             my $canvas = defined $this->{-borderscr} ? $this->{-borderscr} : $this->{-canvasscr};
177             $canvas->bkgdset(COLOR_PAIR($Curses::UI::color_object->get_color_pair($this->{-fg}, $this->{-bg})));
178             }
179             &$old_draw(@_);
180             };
181             }
182              
183             my $lower_height = int($cui->{-height} * 0.25);
184             my $half_width = int($cui->{-width} * 0.5);
185              
186             $sv_win = $cui->add(
187             'sv_win', 'Window',
188             -padtop => 1,
189             -padbottom => $lower_height,
190             -border => 0,
191             -ipad => 0,
192             -title => 'Source',
193             );
194             $sv = $sv_win->add(
195             'sv', 'Devel::PDB::SourceView',
196             -border => 1,
197             #-padbottom => 3,
198             %def_style,
199             );
200            
201             $lower_win = $cui->add(
202             'lower_win', 'Window',
203             -border => 0,
204             -y => -1,
205             -height => $lower_height,
206             %def_style,
207             );
208              
209             $auto_win = $lower_win->add(
210             'auto_win', 'Window',
211             -border => 1,
212             -y => -1,
213             -width => $half_width,
214             -title => 'Auto',
215             %def_style,
216             );
217             $padvar_list = $auto_win->add(
218             'padvar_list', 'Devel::PDB::NamedListbox',
219             -readonly => 1,
220             -named_list => \@padlist_disp,
221             );
222              
223             $watch_win = $lower_win->add(
224             'watch_win', 'Window',
225             -border => 1,
226             -x => -1,
227             -y => -1,
228             -padleft => $half_width,
229             -title => 'Watch',
230             %def_style,
231             );
232             $watch_list = $watch_win->add(
233             'watch_list', 'Devel::PDB::NamedListbox',
234             -named_list => \@watch_exprs,
235             );
236              
237             $cui->add(
238             'menu', 'Menubar',
239             -menu => [
240             { -label => 'File', -submenu => [
241             { -label => 'Exit', -value => \&db_quit },
242             ] },
243             { -label => 'Help', submenu => [
244             { -label => 'About', -value => sub { } },
245             ] },
246             ],
247             %def_style,
248             );
249              
250             $cui->set_binding(\&db_quit, "\cQ", "\cC");
251             $cui->set_binding(\&db_cont, KEY_F(5));
252             $cui->set_binding(\&db_step_out, KEY_F(6));
253             $cui->set_binding(\&db_step_in, KEY_F(7));
254             $cui->set_binding(\&db_step_over, KEY_F(8));
255             $cui->set_binding(\&db_toggle_break, KEY_F(9));
256             $cui->set_binding(sub { ui_open_file('Compiled Files', \@complied); }, KEY_F(11));
257             $cui->set_binding(sub { ui_open_file('Opened Files', [keys(%sources)]); }, KEY_F(12));
258             $cui->set_binding(sub { shift->getobj('menu')->focus }, KEY_F(10));
259              
260             $cui->set_binding(\&db_add_watch_expr, "\cW");
261              
262             $cui->set_binding(sub { $sv_win->focus }, KEY_F(1));
263             $cui->set_binding(sub { $auto_win->focus }, KEY_F(2));
264             $cui->set_binding(sub { $watch_win->focus }, KEY_F(3));
265            
266             $cui->set_binding(sub { ui_adjust_vert_parts(1) }, '{');
267             $cui->set_binding(sub { ui_adjust_vert_parts(-1) }, '}');
268             $cui->set_binding(sub { ui_adjust_hori_parts(-1) }, '[');
269             $cui->set_binding(sub { ui_adjust_hori_parts(1) }, ']');
270              
271             #open my $fd0, '>stdout';
272             #open my $fd1, '>stderr';
273             #open STDOUT, ">&$fd0";
274             #open STDERR, ">&$fd1";
275             #open STDOUT, ">stdout";
276              
277             open STDERR, ">stderr";
278             open $output, ">stdout";
279             open $stdout, ">&STDOUT";
280              
281             $inited = 1;
282             }
283              
284             sub get_source {
285             my $filename = shift;
286             my $source = $sources{$filename};
287              
288             if (!defined $source) {
289             local (*dbline) = $main::{"_<$filename"};
290             $sources{$filename} = $source = new Devel::PDB::Source(
291             filename => $filename,
292             lines => \@dbline,
293             breaks => \%dbline,
294             );
295             }
296              
297             return $source;
298             }
299              
300             my @saved;
301              
302             sub save {
303             @saved = ($@, $!, $,, $/, $\, $^W);
304             $, = '';
305             $/ = "\n";
306             $\ = '';
307             $^W = 0;
308             }
309              
310             sub eval {
311             ($@, $!, $,, $/, $\, $^W) = @saved;
312             my $res = eval "package $package; $evalarg";
313             save;
314             $res;
315             }
316              
317             sub ui_update_watch_list {
318             local $Data::Dumper::Terse = 1;
319             local $Data::Dumper::Maxdepth;
320             local $Data::Dumper::Indent;
321              
322             foreach my $expr (@watch_exprs) {
323             $evalarg = $expr->{name};
324             my $res = &DB::eval;
325             $Data::Dumper::Indent = 0;
326             $Data::Dumper::Maxdepth = 2;
327             $expr->{value} = Dumper $res;
328             $Data::Dumper::Indent = 1;
329             $Data::Dumper::Maxdepth = 0;
330             $expr->{long_value} = Dumper $res;
331             }
332              
333             $watch_list->update;
334             }
335              
336             sub DB {
337             return if $exit;
338             save;
339             init if !$inited;
340              
341             open STDOUT, ">&", $stdout;
342              
343             ($package, $filename, $line) = caller;
344              
345             my $scope = $current_sub ? $current_sub : $package;
346             my $renew = !defined $padlist_scope || $scope ne $padlist_scope;
347             if ($renew) {
348             %padlist = ();
349             @padlist_disp = ();
350             $padlist_scope = $scope;
351             }
352              
353             my ($names, $vals) = $scope eq 'main' ? comppadlist->ARRAY : svref_2object(\&$scope)->PADLIST->ARRAY;
354             my @names = $names->ARRAY;
355             my @vals = $vals->ARRAY;
356             my $count = @names;
357              
358             local $Data::Dumper::Terse = 1;
359             local $Data::Dumper::Maxdepth;
360             local $Data::Dumper::Indent;
361             for (my ($i, $j) = (0, 0); $i < $count; $i++) {
362             my $sv = $names[$i];
363             next if class($sv) eq 'SPECIAL';
364             my $name = $sv->PVX;
365             $Data::Dumper::Indent = 0;
366             $Data::Dumper::Maxdepth = 2;
367             my $val = Dumper $vals[$i]->object_2svref;
368             $val =~ s/^\\// if class($sv) ne 'RV';
369             $Data::Dumper::Indent = 1;
370             $Data::Dumper::Maxdepth = 0;
371             my $long_val = Dumper $vals[$i]->object_2svref;
372             $long_val =~ s/^\\// if class($sv) ne 'RV';
373             if ($renew || $val ne $padlist{$name}) {
374             $padlist_disp[$j] = { name => $name, value => $val, long_value => $long_val };
375             $padlist{$name} = $val;
376             }
377             ++$j;
378             }
379             $padvar_list->update($renew);
380              
381             #local (*dbline) = $main::{"_<$filename"};
382             $sv->source($current_source = get_source($filename));
383             $current_source->current_line($line);
384              
385             ui_update_watch_list;
386              
387             $yield = 0;
388             $new_single = $single;
389             $cui->focus(undef, 1);
390             $cui->draw;
391             $update_watch_list = 0;
392             while (!$yield) {
393             $cui->do_one_event;
394             if ($update_watch_list) {
395             ui_update_watch_list;
396             $cui->draw;
397             }
398             }
399             $single = $new_single;
400              
401             open STDOUT, ">&", $output;
402             ($@, $!, $,, $/, $\, $^W) = @saved;
403             }
404              
405             sub sub {
406             my ($ret, @ret);
407              
408             local $current_sub = $sub;
409             local $stack_depth = $stack_depth + 1;
410             $#stack = $stack_depth;
411             $stack[-1] = $single;
412             $single &= 1;
413              
414             if (wantarray) {
415             no strict;
416             @ret = &$sub;
417             use strict;
418             $single |= $stack[$stack_depth--];
419             @ret;
420             } else {
421             if (defined wantarray) {
422             no strict;
423             $ret = &$sub;
424             use strict;
425             } else {
426             no strict;
427             &$sub;
428             use strict;
429             undef $ret;
430             }
431              
432             $single |= $stack[$stack_depth--];
433             $ret;
434             }
435             }
436              
437             sub postponed {
438             my $file = shift;
439             push @complied, $$file;
440             }
441              
442             package Devel::PDB;
443              
444             1;
445              
446             __END__