File Coverage

blib/lib/Devel/Chitin/OpTree.pm
Criterion Covered Total %
statement 372 437 85.1
branch 93 138 67.3
condition 76 135 56.3
subroutine 119 132 90.1
pod 11 71 15.4
total 671 913 73.4


line stmt bran cond sub pod time code
1             package Devel::Chitin::OpTree;
2              
3 35     35   673 use strict;
  35         61  
  35         774  
4 35     35   137 use warnings;
  35         47  
  35         1052  
5              
6             our $VERSION = '0.16';
7              
8 35     35   139 use Carp;
  35         46  
  35         1732  
9 35     35   156 use Scalar::Util qw(blessed reftype weaken refaddr);
  35         46  
  35         2655  
10 35     35   173 use B qw(ppname);
  35         46  
  35         1459  
11              
12 35     35   11975 use Devel::Chitin::OpTree::UNOP;
  35         81  
  35         1025  
13 35     35   11572 use Devel::Chitin::OpTree::SVOP;
  35         69  
  35         788  
14 35     35   10610 use Devel::Chitin::OpTree::PADOP;
  35         66  
  35         828  
15 35     35   9970 use Devel::Chitin::OpTree::COP;
  35         86  
  35         921  
16 35     35   11133 use Devel::Chitin::OpTree::PVOP;
  35         78  
  35         1014  
17 35     35   10981 use Devel::Chitin::OpTree::METHOP;
  35         78  
  35         1029  
18 35     35   11771 use Devel::Chitin::OpTree::BINOP;
  35         103  
  35         1227  
19 35     35   11899 use Devel::Chitin::OpTree::LOGOP;
  35         77  
  35         1145  
20 35     35   10501 use Devel::Chitin::OpTree::LOGOP_AUX;
  35         85  
  35         1018  
21 35     35   12913 use Devel::Chitin::OpTree::LISTOP;
  35         120  
  35         2086  
22 35     35   13266 use Devel::Chitin::OpTree::LOOP;
  35         67  
  35         1113  
23 35     35   10589 use Devel::Chitin::OpTree::PMOP;
  35         65  
  35         1667  
24             BEGIN {
25 35 50   35   342 if ($^V ge v5.22.0) {
26 35         10480 require Devel::Chitin::OpTree::UNOP_AUX;
27             }
28             }
29              
30             my %objs_for_op;
31             sub _obj_for_op {
32 2819     2819   5552 my($self, $op) = @_;
33 2819         7379 $objs_for_op{$$op};
34             }
35             sub build_from_location {
36 332     332 0 837 my($class, $start) = @_;
37              
38 332         849 my($start_op, $cv) = _determine_start_of($start);
39              
40             # adapted from B::walkoptree_slow
41 332         681 my @parents;
42             my $build_walker;
43             $build_walker = sub {
44 6689     6689   9049 my $op = shift;
45              
46 6689         12582 my $self = $class->new(op => $op, cv => $cv);
47 6689         23135 $objs_for_op{$$op} = $self;
48 6689         20199 weaken $objs_for_op{$$op};
49              
50 6689         8438 my @children;
51 6689 100 66     32512 if ($$op && ($op->flags & B::OPf_KIDS)) {
52 2751         5125 unshift(@parents, $self);
53 2751         10239 for (my $kid_op = $op->first; $$kid_op; $kid_op = $kid_op->sibling) {
54 6356         15623 push @children, $build_walker->($kid_op);
55             }
56 2751         5107 shift(@parents);
57             }
58              
59 6689 100 100     36251 if (B::class($op) eq 'PMOP'
      100        
      66        
60             and ref($op->pmreplroot)
61 20         153 and ${$op->pmreplroot}
62             and $op->pmreplroot->isa('B::OP')
63             ) {
64 1         4 unshift @parents, $self;
65 1         7 push @children, $build_walker->($op->pmreplroot);
66 1         2 shift @parents;
67             }
68              
69 6689         17868 @$self{'parent','children'} = ($parents[0], \@children);
70 6689         30948 $self;
71 332         1830 };
72              
73 332         811 $build_walker->($start_op);
74             }
75              
76             sub _determine_start_of {
77 332     332   563 my $start = shift;
78              
79 332 100       1589 if (reftype($start) eq 'CODE') {
80 3         13 my $cv = B::svref_2object($start);
81 3         19 return ($cv->ROOT, $cv);
82             }
83              
84 329 50 33     3197 unless (blessed($start) and $start->isa('Devel::Chitin::Location')) {
85 0         0 Carp::croak('build_from_location() requires a coderef or Devel::Chitin::Location as an argument');
86             }
87              
88 329 50 33     1171 if ($start->package eq 'main' and $start->subroutine eq 'MAIN') {
    50          
89 0         0 return (B::main_root(), B::main_cv);
90              
91             } elsif ($start->subroutine =~ m/::__ANON__\[\S+:\d+\]/) {
92 0         0 Carp::croak(q(Don't know how to handle anonymous subs yet));
93              
94             } else {
95 329         839 my $subname = join('::', $start->package, $start->subroutine);
96 35     35   215 my $subref = do { no strict 'refs'; \&$subname };
  35         53  
  35         74831  
  329         778  
  329         1776  
97 329         1453 my $cv = B::svref_2object($subref);
98 329         1665 return ($cv->ROOT, $cv);
99             }
100             }
101              
102             sub new {
103 6689     6689 0 17989 my($class, %params) = @_;
104 6689 50       13722 unless (exists $params{op}) {
105 0         0 Carp::croak(q{'op' is a required parameter of new()});
106             }
107              
108 6689         11246 my $final_class = _class_for_op($params{op});
109              
110 6689         13532 my $self = bless \%params, $final_class;
111 6689         15688 $self->_build();
112 6689         11891 return $self;
113             }
114              
115             sub _class_for_op {
116 6689     6689   8909 my $op = shift;
117 6689         35371 my $b_class = B::class($op);
118 6689 100 100     25741 if ($b_class eq 'OP') {
    100 66        
119 1905         3920 return __PACKAGE__,
120             } elsif ($b_class eq 'UNOP'
121             and $op->name eq 'null'
122             and $op->flags & B::OPf_KIDS
123             ) {
124 558         992 my $num_children = 0;
125 558         2299 for (my $kid_op = $op->first; $$kid_op; $kid_op = $kid_op->sibling) {
126 995         3443 $num_children++ ;
127             }
128 558 100       1407 if ($num_children > 2) {
    100          
129 84         255 return join('::', __PACKAGE__, 'LISTOP');
130             } elsif ($num_children > 1) {
131 216         623 return join('::', __PACKAGE__, 'BINOP');
132              
133             } else {
134 258         841 return join('::', __PACKAGE__, 'UNOP');
135             }
136             } else {
137 4226         21440 join('::', __PACKAGE__, B::class($op));
138             }
139             }
140              
141       6689     sub _build { }
142              
143 37193     37193 1 161535 sub op { shift->{op} }
144 15202     15202 1 31442 sub parent { shift->{parent} }
145 4786     4786 1 9726 sub children { shift->{children} }
146 1242     1242 1 10410 sub cv { shift->{cv} }
147             sub root_op {
148 2101     2101 1 2897 my $obj = shift;
149 2101         3373 $obj = $obj->parent while ($obj->parent);
150 2101         4756 $obj;
151             }
152              
153             sub next {
154 664     664 1 1157 my $self = shift;
155 664         1259 $self->_obj_for_op($self->op->next);
156             }
157              
158             sub sibling {
159 2150     2150 1 2827 my $self = shift;
160 2150         3521 $self->_obj_for_op($self->op->sibling);
161             }
162              
163             sub walk_preorder {
164 0     0 1 0 my($self, $cb) = @_;
165 0         0 $_->walk_preorder($cb) foreach (@{ $self->children });
  0         0  
166 0         0 $cb->($self);
167             }
168              
169             sub walk_inorder {
170 6     6 1 405 my($self, $cb) = @_;
171 6         23 $cb->($self);
172 6         21 $_->walk_inorder($cb) foreach (@{ $self->children } );
  6         21  
173             }
174              
175             sub deparse {
176 5939     5939 1 10564 my $self = shift;
177 5939         10203 my $bounce = 'pp_' . $self->op->name;
178 5939         20997 $self->$bounce(@_);
179             }
180              
181             sub _deparsed_children {
182 0     0   0 my $self = shift;
183 0         0 return grep { $_ }
184 0         0 map { $_->deparse }
185 0         0 @{ $self->children };
  0         0  
186             }
187              
188             sub is_null {
189 8493     8493 0 13623 return shift->op->name eq 'null';
190             }
191              
192             sub pp_null {
193 484     484 0 780 my $self = shift;
194 484         936 my $bounce = $self->_ex_name;
195              
196 484 100       1077 if ($bounce eq 'pp_null') {
197 35         81 my $children = $self->children;
198 35 100 66     180 if (@$children == 2
    50 66        
199             and $self->first->is_scalar_container
200             and $self->last->op->name eq 'readline'
201             ) {
202             # not sure why this gets special-cased...
203 4         13 $self->Devel::Chitin::OpTree::BINOP::pp_sassign(is_swapped => 1);
204              
205             } elsif (@$children == 1) {
206 31         94 $children->[0]->deparse(@_);
207              
208             } else {
209 0         0 ";\n" # maybe a COP that got optimized away?
210             }
211              
212             } else {
213 449         1480 $self->$bounce(@_);
214             }
215             }
216              
217             # These are nextstate/dbstate that got optimized away to null
218             *pp_nextstate = \&Devel::Chitin::OpTree::COP::pp_nextstate;
219             *pp_dbstate = \&Devel::Chitin::OpTree::COP::pp_dbstate;
220              
221             sub pp_padsv {
222 1134     1134 0 1654 my $self = shift;
223             # These are 'my' variables. We're omitting the 'my' because
224             # that happens at compile time
225 1134         2464 $self->_padname_sv->PV;
226             }
227             *pp_padav = \&pp_padsv;
228             *pp_padhv = \&pp_padsv;
229              
230             sub pp_aelemfast_lex {
231 3     3 0 5 my $self = shift;
232 3         8 my $list_name = substr($self->pp_padav, 1); # remove the sigil
233 3         11 "\$${list_name}[" . $self->op->private . ']';
234             }
235             *pp_aelemfast = \&pp_aelemfast_lex;
236              
237             sub pp_padrange {
238 0     0 0 0 my $self = shift;
239             # These are 'my' variables. We're omitting the 'my' because
240             # that happens at compile time
241 0         0 $self->_padname_sv->PV;
242             }
243              
244             sub pp_pushmark {
245 0     0 0 0 my $self = shift;
246              
247 0         0 die "didn't expect to deparse a pushmark";
248             }
249              
250             sub _padname_sv {
251 1226     1226   1630 my $self = shift;
252 1226   66     3128 my $targ = shift || $self->op->targ;
253             # print "in padname_sv\n";
254             # print "PADLIST: ",$self->cv->PADLIST,"\n";
255             # print "ARRAYelt(0): ",$self->cv->PADLIST->ARRAYelt(0),"\n";
256 1226         2649 return $self->cv->PADLIST->ARRAYelt(0)->ARRAYelt( $targ );
257             }
258              
259             sub _padval_sv {
260 3     3   12 my($self, $idx) = @_;
261 3         10 return $self->cv->PADLIST->ARRAYelt(1)->ARRAYelt( $idx );
262             }
263              
264             sub _gv_name {
265 244     244   610 my($self, $gv) = @_;
266 244         608 my $last_cop = $self->nearest_cop();
267 244         580 my $curr_package = $last_cop->op->stashpv;
268 244         1178 my $gv_package = $gv->STASH->NAME;
269              
270 244 100       1735 $curr_package eq $gv_package
271             ? $gv->NAME
272             : join('::', $gv_package, $gv->NAME);
273             }
274              
275             sub _ex_name {
276 970     970   1524 my $self = shift;
277 970 100       1518 if ($self->op->name eq 'null') {
278 965         1796 ppname($self->op->targ);
279             }
280             }
281              
282             sub _sibling_helper {
283 10     10   20 my($self, $cb) = @_;
284 10         15 my $parent = $self->parent;
285 10 50       21 return unless $parent;
286 10         16 my $children = $parent->children;
287 10 50 33     36 return unless ($children and @$children);
288              
289 10         30 for (my $i = 0; $i < @$children; $i++) {
290 22 100       60 if ($children->[$i] eq $self) {
291 10         18 return $cb->($i, $children);
292             }
293             }
294             }
295             sub pre_siblings {
296 10     10 0 15 my $self = shift;
297             $self->_sibling_helper(sub {
298 10     10   18 my($i, $children) = @_;
299 10         73 @$children[0 .. ($i-1)];
300 10         48 });
301             }
302              
303             sub _parse_bit_flags {
304 0     0   0 my($bits, %flags) = @_;
305             map {
306 0 0       0 $bits & $flags{$_}
  0         0  
307             ? $_
308             : ()
309             } sort keys %flags;
310             }
311              
312             my %flag_values = (
313             WANT_VOID => B::OPf_WANT_VOID,
314             WANT_SCALAR => B::OPf_WANT_SCALAR,
315             WANT_LIST => B::OPf_WANT_LIST,
316             KIDS => B::OPf_KIDS,
317             PARENS => B::OPf_PARENS,
318             REF => B::OPf_REF,
319             MOD => B::OPf_MOD,
320             STACKED => B::OPf_STACKED,
321             SPECIAL => B::OPf_SPECIAL,
322             );
323             my %private_values = (
324             BARE => B::OPpCONST_BARE,
325             TARGMY => B::OPpTARGET_MY,
326             SLICE => B::OPpSLICE,
327             ($^V ge v5.28.0 ? ( KVSLICE => &B::OPpKVSLICE ) : ()),
328             );
329             sub print_as_tree {
330 0     0 0 0 my $self = shift;
331 0         0 my $current_callsite = shift;
332              
333             $self->walk_inorder(sub {
334 0     0   0 my $op = shift;
335 0         0 my($level, $parent) = (0, $op);
336 0         0 $level++ while($parent = $parent->parent);
337 0         0 my $name = $op->op->name;
338 0 0       0 if ($name eq 'null') {
339 0         0 $name .= ' (ex-' . $op->_ex_name . ')';
340             }
341              
342 0         0 my @flags = _parse_bit_flags($op->op->flags, %flag_values);
343 0         0 my @private = _parse_bit_flags($op->op->private, %private_values);
344              
345 0         0 my $mini_deparsed = '';
346 0 0 0     0 if ($op->class eq 'COP') {
    0 0        
    0          
347 0         0 $mini_deparsed = join(':', $op->op->file, $op->op->line);
348             } elsif ($op->is_scalar_container
349             or $op->is_array_container
350             or $op->op->name eq 'const'
351             ) {
352 0         0 $mini_deparsed = $op->deparse;
353 0 0       0 $mini_deparsed = '' unless defined $mini_deparsed; # multiconcat can optimze away the target of an assignment
354              
355             } elsif ($op->op->name eq 'multiconcat') {
356 0         0 my($nargs, $const_str, @substr_lengths) = $op->op->aux_list($op->cv);
357 0         0 my $substr_lengths = join(',', @substr_lengths);
358              
359 0         0 my $target= '';
360 0 0       0 if ($op->op->private & B::OPpTARGET_MY) {
361 0         0 $target = $op->_padname_sv($op->op->targ)->PV . ' = ';
362             }
363              
364 0         0 push @private, _parse_bit_flags($op->op->private,
365             ( APPEND => &B::OPpMULTICONCAT_APPEND,
366             STRINGIFY => &B::OPpMULTICONCAT_STRINGIFY,
367             SPRINTF => &B::OPpMULTICONCAT_FAKE,
368             ));
369 0         0 $mini_deparsed = qq(${target}"$const_str"[$substr_lengths]);
370             }
371              
372 0 0 0     0 my $indent = ($current_callsite and ${$op->op} == $current_callsite)
373             ? '=>' . (' ' x($level-1))
374             : ' 'x$level;
375             printf("%s%s %s (%s) %s %s 0x%x\n", $indent, $op->class, $name,
376             join(', ', @flags),
377             $mini_deparsed,
378             join(', ', @private),
379 0 0       0 $current_callsite ? ${$op->op} : refaddr($op));
  0         0  
380 0         0 });
381             }
382              
383             sub class {
384 534     534 0 787 my $self = shift;
385 534         2338 return substr(ref($self), rindex(ref($self), ':')+1);
386             }
387              
388             sub nearest_cop {
389 891     891 0 1254 my $self = shift;
390              
391 891         1642 my $parent = $self->parent;
392 891 50       1695 return unless $parent;
393 891         1442 my $siblings = $parent->children;
394 891 50 33     2613 return unless $siblings and @$siblings;
395              
396 891         1866 for (my $i = 0; $i < @$siblings; $i++) {
397 1114         1599 my $sib = $siblings->[$i];
398 1114 100       3045 if ($sib eq $self) {
    100          
399             # Didn't find it on one of the siblings already executed, try the parent
400 647         1461 return $parent->nearest_cop();
401              
402             } elsif ($sib->class eq 'COP') {
403 244         933 return $sib;
404             }
405             }
406 0         0 return;
407             }
408              
409             sub check_feature {
410 0 0   0 0 0 return unless $^V ge v5.10.1; # hints hash didn't exist before this
411 0         0 my($self, $feature_name) = @_;
412 0         0 my $cop = $self->nearest_cop;
413 0         0 my $hints = $cop->op->hints_hash->HASH;
414 0         0 return $hints->{"feature_${feature_name}"};
415             }
416              
417             # The current COP op is stored on scope-like OPs, and on the root op
418             sub _enter_scope {
419 51     51   137 shift->{cur_cop} = undef;
420             }
421             sub _leave_scope {
422 51     51   137 shift->{cur_cop} = undef;
423             }
424             sub _get_cur_cop {
425 1052     1052   1944 shift->root_op->{cur_cop};
426             }
427             sub _get_cur_cop_in_scope {
428 0     0   0 shift->_encompassing_scope_op->{cur_cop};
429             }
430             sub _set_cur_cop {
431 1048     1048   1606 my $self = shift;
432 1048         2072 $self->_encompassing_scope_op->{cur_cop} = $self;
433 1048         2138 $self->root_op->{cur_cop} = $self;
434             };
435             sub _encompassing_scope_op {
436 1048     1048   1434 my $self = my $op = shift;
437 1048   66     2973 for(; $op && !$op->is_scopelike; $op = $op->parent) { }
438 1048 50       3032 $op || $self->root_op;
439             }
440              
441             # Usually, rand/srand/pop/shift is an UNOP, but with no args, it's a base-OP
442             foreach my $d ( [ pp_rand => 'rand' ],
443             [ pp_srand => 'srand' ],
444             [ pp_getppid => 'getppid' ],
445             [ pp_wait => 'wait' ],
446             [ pp_time => 'time' ],
447             ) {
448             my($pp_name, $perl_name) = @$d;
449             my $sub = sub {
450 8     8   36 my $target = shift->_maybe_targmy;
451 8         39 "${target}${perl_name}()";
452             };
453 35     35   254 no strict 'refs';
  35         66  
  35         4579  
454             *$pp_name = $sub;
455             }
456              
457             # Chdir and sleep can be either a UNOP or base-OP
458             foreach my $d ( [ pp_chdir => 'chdir' ],
459             [ pp_sleep => 'sleep' ],
460             [ pp_localtime => 'localtime' ],
461             [ pp_gmtime => 'gmtime' ],
462             ) {
463             my($pp_name, $perl_name) = @$d;
464             my $sub = sub {
465 9     9   21 my $self = shift;
466 9         26 my $children = $self->children;
467 9         31 my $target = $self->_maybe_targmy;
468 9 100       26 if (@$children) {
469 5         23 "${target}${perl_name}(" . $children->[0]->deparse . ')';
470             } else {
471 4         28 "${target}${perl_name}()";
472             }
473             };
474 35     35   197 no strict 'refs';
  35         53  
  35         24621  
475             *$pp_name = $sub;
476             }
477              
478 0     0 0 0 sub pp_enter { '' }
479 1     1 0 5 sub pp_stub { ';' }
480 0     0 0 0 sub pp_unstack { '' }
481 6     6 0 20 sub pp_undef { 'undef' }
482 1     1 0 7 sub pp_wantarray { 'wantarray' }
483 1     1 0 6 sub pp_dump { 'dump' }
484 1     1 0 5 sub pp_next { 'next' }
485 0     0 0 0 sub pp_last { 'last' }
486 1     1 0 4 sub pp_redo { 'redo' }
487 3     3 0 11 sub pp_const { q('constant optimized away') }
488              
489 1     1 0 3 sub pp_close { 'close()' }
490 1     1 0 3 sub pp_getc { 'getc()' }
491 1     1 0 4 sub pp_tell { 'tell()' }
492 1     1 0 5 sub pp_enterwrite { 'write()' }
493 2     2 0 9 sub pp_fork { 'fork()' }
494 2     2 0 8 sub pp_tms { 'times()' }
495 2     2 0 10 sub pp_ggrent { 'getgrent()' }
496 0     0 0 0 sub pp_eggrent { 'endgrent()' }
497 1     1 0 6 sub pp_ehostent { 'endhostent()' }
498 1     1 0 5 sub pp_enetent { 'endnetent()' }
499 1     1 0 6 sub pp_eservent { 'endservent()' }
500 1     1 0 5 sub pp_egrent { 'endgrent()' }
501 1     1 0 5 sub pp_epwent { 'endpwent()' }
502 1     1 0 4 sub pp_spwent { 'setpwent()' }
503 1     1 0 5 sub pp_sgrent { 'setgrent()' }
504 3     3 0 12 sub pp_gpwent { 'getpwent()' }
505 1     1 0 4 sub pp_getlogin { 'getlogin()' }
506 2     2 0 5 sub pp_ghostent { 'gethostent()' }
507 2     2 0 6 sub pp_gnetent { 'getnetent()' }
508 2     2 0 6 sub pp_gprotoent { 'getprotoent()' }
509 2     2 0 6 sub pp_gservent { 'getservent()' }
510 2     2 0 10 sub pp_caller { 'caller()' }
511 1     1 0 5 sub pp_exit { 'exit()' }
512 1     1 0 3 sub pp_umask { 'umask()' }
513              
514             sub pp_eof {
515 2 100   2 0 8 shift->op->flags & B::OPf_SPECIAL
516             ? 'eof()'
517             : 'eof';
518             }
519              
520             sub pp_break {
521 1     1 0 4 my $self = shift;
522 1 50       5 ($self->op->flags & B::OPf_SPECIAL)
523             ? '' # an implicit break at the end of each when { }
524             : 'break'; # and explicit break
525             }
526              
527             sub is_implicit_break_at_end_of_when_block {
528 1047     1047 0 1600 my $self = shift;
529              
530 1047 50       1837 $self->op->name eq 'break'
531             and $self->op->flags & B::OPf_SPECIAL
532             }
533              
534             sub pp_continue {
535 1     1 0 5 'continue';
536             }
537              
538             # Starting with Perl 5.14, these are base-ops with the special flag set when used without args
539             foreach my $a ( [ pp_shift => 'shift' ],
540             [ pp_pop => 'pop' ],
541             ) {
542             my($pp_name, $perl_name) = @$a;
543             my $sub = sub {
544 6     6   12 my $self = shift;
545 6 100       17 if ($self->op->flags & B::OPf_SPECIAL) {
546 2         9 "$perl_name";
547             } else {
548 4         16 my $arg = $self->first->deparse;
549 4 50       14 if ($arg eq '@_') {
550 0         0 "$perl_name";
551             } else {
552 4         27 "$perl_name($arg)";
553             }
554             }
555             };
556 35     35   213 no strict 'refs';
  35         57  
  35         4017  
557             *$pp_name = $sub;
558             }
559              
560             # reference aliasing OPs
561             foreach my $a ( [ pp_lvavref => '@' ],
562             [ pp_lvref => '$' ],
563             ) {
564             my($pp_name, $sigil) = @$a;
565             my $sub = sub {
566 4     4   15 my $self = shift;
567 4 100       9 my $var = $self->op->flags & B::OPf_STACKED
568             ? $sigil . $self->children->[0]->deparse # an our var
569             : $self->_padname_sv->PV; # a my var
570 4         19 "\($var)";
571             };
572 35     35   221 no strict 'refs';
  35         99  
  35         7345  
573             *$pp_name = $sub;
574             }
575              
576             # file test operators
577             # These actually show up as UNOPs (usually) and SVOPs (-X _) but it's
578             # convienent to put them here in the base class
579             foreach my $a ( [ pp_fteread => '-r' ],
580             [ pp_ftewrite => '-w' ],
581             [ pp_fteexec => '-x' ],
582             [ pp_fteowned => '-o' ],
583             [ pp_ftrread => '-R' ],
584             [ pp_ftrwrite => '-W' ],
585             [ pp_ftrexec => '-X' ],
586             [ pp_ftrowned => '-O' ],
587             [ pp_ftis => '-e' ],
588             [ pp_ftzero => '-z' ],
589             [ pp_ftsize => '-s' ],
590             [ pp_ftfile => '-f' ],
591             [ pp_ftdir => '-d' ],
592             [ pp_ftlink => '-l' ],
593             [ pp_ftpipe => '-p' ],
594             [ pp_ftblk => '-b' ],
595             [ pp_ftsock => '-S' ],
596             [ pp_ftchr => '-c' ],
597             [ pp_fttty => '-t' ],
598             [ pp_ftsuid => '-u' ],
599             [ pp_ftsgid => '-g' ],
600             [ pp_ftsvtx => '-k' ],
601             [ pp_fttext => '-T' ],
602             [ pp_ftbinary => '-B' ],
603             [ pp_ftmtime => '-M' ],
604             [ pp_ftatime => '-A' ],
605             [ pp_ftctime => '-C' ],
606             [ pp_stat => 'stat' ],
607             [ pp_lstat => 'lstat' ],
608             ) {
609             my($pp_name, $perl_name) = @$a;
610             my $sub = sub {
611 44     44   67 my $self = shift;
612              
613 44         56 my $fh;
614 44 100       90 if ($self->class eq 'UNOP') {
615 35         65 $fh = $self->children->[0]->deparse;
616 35 100       82 $fh = '' if $fh eq '$_';
617             } else {
618             # It's a test on _: -w _
619 9 50       19 $fh = $self->class eq 'SVOP'
620             ? $self->Devel::Chitin::OpTree::SVOP::pp_gv()
621             : $self->Devel::Chitin::OpTree::PADOP::pp_gv();
622             }
623              
624 44 100       112 if (substr($perl_name, 0, 1) eq '-') {
625             # -X type test
626 30 100       66 if ($fh) {
627 25         87 "$perl_name $fh";
628             } else {
629 5         16 $perl_name;
630             }
631             } else {
632 14         48 "${perl_name}($fh)";
633             }
634             };
635 35     35   199 no strict 'refs';
  35         59  
  35         49591  
636             *$pp_name = $sub;
637             }
638              
639             # The return values for some OPs is encoded specially, and not through a
640             # normal sassign
641             sub _maybe_targmy {
642 194     194   334 my $self = shift;
643              
644 194 100       381 if ($self->op->private & B::OPpTARGET_MY) {
645 70         212 $self->_padname_sv->PV . ' = ';
646             } else {
647 124         394 '';
648             }
649             }
650              
651             sub op_name {
652 4704     4704 1 5821 my $self = shift;
653 4704 100       7439 return $self->is_null
654             ? substr($self->_ex_name, 3) # remove the preceding pp_
655             : $self->op->name;
656             }
657              
658             # return true for scalar things we can assign to
659             my %scalar_container_ops = (
660             rv2sv => 1,
661             padsv => 1,
662             );
663             sub is_scalar_container {
664 174     174 0 279 my $self = shift;
665 174         362 $scalar_container_ops{$self->op_name};
666             }
667              
668             my %array_container_ops = (
669             rv2av => 1,
670             padav => 1,
671             );
672             sub is_array_container {
673 15     15 0 25 my $self = shift;
674 15         30 $array_container_ops{$self->op_name};
675             }
676              
677             my %scopelike_ops = (
678             scope => 1,
679             leave => 1,
680             leavetry => 1,
681             leavesub => 1,
682             leaveloop => 1,
683             entergiven => 1,
684             enterwhile => 1,
685             #entergiven => 1, # Part of the reverted given/whereso/whereis from 5.27.7
686             #pp_entergiven => 1,
687             #enterwhereso => 1,
688             #pp_enterwhereso => 1,
689             );
690             sub is_scopelike {
691 4498     4498 0 6404 my $self = shift;
692 4498         7775 $scopelike_ops{$self->op_name};
693             }
694              
695             sub is_for_loop {
696 2118     2118 0 2964 my $self = shift;
697             # $self, here, is the initialization part of the for-loop, usually an sassign.
698             # The sibling is either:
699             # 1) a lineseq whose first child is a nextstate and second child is a leaveloop
700             # 2) an unstack whose sibling is a leaveloop
701 2118         3913 my $sib = $self->sibling;
702 2118 100 100     13620 return '' if !$sib or $self->isa('Devel::Chitin::OpTree::COP') or $self->is_null;
      100        
703              
704 658         1517 my $name = $sib->op->name;
705 658 50 100     2432 if ($name eq 'lineseq') {
    100          
706 0         0 my($first ,$second) = @{$sib->children};
  0         0  
707 0 0 0     0 if ($first && ! $first->is_null && $first->isa('Devel::Chitin::OpTree::COP')
      0        
      0        
      0        
      0        
708             && $second && ! $second->is_null && $second->op->name eq 'leaveloop'
709             ) {
710 0         0 return 1;
711             }
712              
713             } elsif ($name eq 'unstack' && ($sib->op->flags & B::OPf_SPECIAL)) {
714 1         11 my $sibsib = $sib->sibling;
715 1   33     6 return $sibsib && ! $sibsib->is_null && $sibsib->op->name eq 'leaveloop'
716             }
717 657         1642 return ''
718             }
719              
720             # Return true for
721             # if (conditional) { ... }
722             # and
723             # unless (conditional) { ... }
724             sub is_if_statement {
725 12     12 0 19 my $self = shift;
726 12         22 my $name = $self->op->name;
727              
728 12 50 66     64 ( $name eq 'and' or $name eq 'or' or $name eq 'cond_expr')
      33        
729             and $self->other->is_scopelike;
730             }
731              
732             sub is_postfix_if {
733 8     8 0 14 my $self = shift;
734 8         16 my $name = $self->op->name;
735              
736 8 100 66     46 ( $name eq 'and' or $name eq 'or' )
      33        
      66        
737             and $self->parent->is_null
738             and $self->parent->pre_siblings
739             and ($self->parent->pre_siblings)[-1]->class eq 'COP'
740             }
741              
742             sub _num_ops_in_for_loop {
743 1     1   4 my $self = shift;
744 1 50       3 $self->sibling->op->name eq 'unstack' ? 2 : 1;
745             }
746              
747             sub _deparse_for_loop {
748 1     1   2 my $self = shift;
749             # A for-loop is structured like this:
750             # nextstate
751             # sassign ( initialization)
752             # ...
753             # unstack
754             # leaveloop
755             # enterloop
756             # null
757             # and
758             # loop-test
759             # ...
760             # lineseq
761             # leave
762             # ... (loop body)
763             # loop-continue
764 1         4 my $init = $self->deparse;
765 1         3 my $sib = $self->sibling;
766 1 50       4 my $leaveloop = $sib->op->name eq 'unstack' ? $sib->sibling : $sib->children->[1];
767 1         4 my $and_op = $leaveloop->children->[1]->children->[0];
768 1         3 my $test_op = $and_op->children->[0];
769 1         3 my $test = $test_op->deparse;
770 1         4 my $body_op = $and_op->children->[1]->first;
771 1         3 my $cont_op = $body_op->sibling;
772 1         3 my $cont = $cont_op->deparse;
773              
774 1         4 "for ($init; $test; $cont) " . $body_op->deparse;
775             }
776              
777             # Return true if this op is the inner list on the right of
778             # \(@a) = \(@b)
779             # The optree for this construct looks like:
780             # aassign
781             # ex-list
782             # pushmark
783             # refgen
784             # ex-list <-- Return true here
785             # pushmark
786             # padav/gv
787             # ex-list
788             # pushmark
789             # ex-refgen
790             # ex-list <-- return true here
791             # pushmark
792             # lvavref
793             sub is_list_reference_alias {
794 168     168 0 251 my $self = shift;
795              
796 168   66     321 return $self->is_null
797             && $self->_ex_name eq 'pp_list'
798             && $self->parent->op->name eq 'refgen'
799             && $self->last->is_array_container;
800             }
801              
802             # Based on B::Deparse::is_miniwhile()
803             sub _deparse_postfix_while {
804 1413     1413   2285 my $self = shift;
805              
806 1413         2665 my $top = $self->children->[1];
807 1413         2007 my $condition_op;
808 1413 50 66     2308 if ($self->op->name eq 'leave'
      100        
      66        
      66        
      66        
      33        
      33        
      33        
809             and $top
810             and $top->is_null
811             and $top->class eq 'UNOP'
812             and ($condition_op = $top->first)
813             and ($condition_op->op->name eq 'and' or $condition_op->op->name eq 'or')
814             and (
815             $top->first->children->[1]->op->name eq 'lineseq'
816             or
817             ( $top->first->op->name eq 'lineseq'
818             and ! $top->first->children->[1]->is_null
819             and $top->first->children->[1]->op->name eq 'unstack'
820             )
821             )
822             ) {
823 12         14 my $type;
824 12         32 my $condition = $condition_op->first->deparse;
825 12 100       28 if ($condition_op->op->name eq 'and') {
826 6         36 $type = 'while';
827             } else {
828 6         14 $type = 'until';
829 6         24 $condition =~ s/^!//;
830             }
831 12         30 return $condition_op->children->[1]->deparse . " $type ($condition)";
832             }
833 1401         5224 return '';
834             }
835              
836             sub is_postfix_foreach {
837 1349     1349 0 1911 my $self = shift;
838 1349   100     2147 return $self->op->name eq 'leaveloop'
839             && $self->first->op->name eq 'enteriter'
840             && ! $self->first->sibling->first->first->sibling->first->isa('Devel::Chitin::OpTree::COP');
841             }
842              
843             sub is_postfix_loop {
844 1349     1349 0 1918 my $self = shift;
845 1349   100     2607 return $self->is_postfix_foreach
846             || $self->_deparse_postfix_while;
847             }
848              
849             sub _quote_sv {
850 366     366   774 my($self, $sv, %params) = @_;
851 366         983 my $string = $sv->PV;
852 366         864 $self->_quote_string($string, %params);
853             }
854              
855             sub _quote_string {
856 366     366   710 my($self, $string, %params) = @_;
857              
858             # Seems that multiconcat can have the BARE flag set erroneously? on 5.27.6
859 366 100 100     1095 my $quote = ($params{skip_quotes} or ($self->op->private & B::OPpCONST_BARE and $self->op->name ne 'multiconcat'))
860             ? ''
861             : q(');
862 366 100 100     1310 if ($string =~ m/[\000-\037]/ and !$params{regex_x_flag}) {
863 16 100       51 $quote = '"' unless $params{skip_quotes};
864 16         59 $string = $self->_escape_for_double_quotes($string, %params);
865             }
866              
867 366         2206 "${quote}${string}${quote}";
868             }
869              
870             my %control_chars = ((map { chr($_) => '\c'.chr($_ + 64) } (1 .. 26)), # \cA .. \cZ
871             "\c@" => '\c@', "\c[" => '\c[');
872             my $control_char_rx = join('|', sort keys %control_chars);
873             sub _escape_for_double_quotes {
874 16     16   46 my($self, $str, %params) = @_;
875              
876 16         40 $str =~ s/\\/\\\\/g;
877 16         33 $str =~ s/\a/\\a/g; # alarm
878 16 50       49 $str =~ s/\cH/\\b/g unless $params{in_regex}; # backspace
879 16         38 $str =~ s/\e/\\e/g; # escape
880 16         29 $str =~ s/\f/\\f/g; # form feed
881 16         61 $str =~ s/\n/\\n/g; # newline
882 16         43 $str =~ s/\r/\\r/g; # CR
883 16         36 $str =~ s/\t/\\t/g; # tab
884 16         31 $str =~ s/"/\\"/g;
885 16         184 $str =~ s/($control_char_rx)/$control_chars{$1}/ge;
  1         8  
886 16         43 $str =~ s/([[:^print:]])/sprintf('\x{%x}', ord($1))/ge;
  1         6  
887              
888 16         54 $str;
889             }
890              
891             sub _as_octal {
892 6     6   14 my($self, $val) = @_;
893 35     35   222 no warnings 'numeric';
  35         58  
  35         3442  
894 6 100       45 $val + 0 eq $val
895             ? sprintf('0%3o', $val)
896             : $val;
897             }
898              
899             # given an integer and a list of bitwise flag name/value pairs, return
900             # a string representing the flags or-ed together
901             sub _deparse_flags {
902 12     12   33 my($self, $val, $flags_listref) = @_;
903              
904 12         18 do {
905 35     35   230 no warnings 'numeric';
  35         87  
  35         8629  
906 12 100       45 unless ($val + 0 eq $val) {
907 1         3 return $val; # wasn't a number
908             }
909             };
910              
911 11         17 my @flags;
912 11         34 for (my $i = 0; $i < @$flags_listref; $i += 2) {
913 60         94 my($flag_name, $flag_value) = @$flags_listref[$i, $i+1];
914 60 100       113 if ($val & $flag_value) {
915 18         31 push @flags, $flag_name;
916 18         39 $val ^= $flag_value;
917             }
918             }
919 11 50       30 if ($val) {
920             # there were unexpected bits set
921 0         0 push @flags, $val;
922             }
923 11         47 join(' | ', @flags);
924             }
925              
926             sub _indent_block_text {
927 58     58   175 my($self, $text, %params) = @_;
928              
929 58         242 my $newlines = $text =~ s/\n/\n\t/g;
930 58 100 100     259 if ($newlines or $params{force_multiline}) {
    50          
931 35         129 "\n\t" . $text . "\n";
932             } elsif ($params{noindent}) {
933 0         0 $text;
934             } else {
935 23         109 " $text ";
936             }
937             }
938              
939             1;
940              
941             __END__