File Coverage

blib/lib/Devel/Chitin/OpTree.pm
Criterion Covered Total %
statement 373 438 85.1
branch 97 142 68.3
condition 76 135 56.3
subroutine 118 131 90.0
pod 10 70 14.2
total 674 916 73.5


line stmt bran cond sub pod time code
1             package Devel::Chitin::OpTree;
2              
3 35     35   732 use strict;
  35         76  
  35         1013  
4 35     35   182 use warnings;
  35         55  
  35         1389  
5              
6             our $VERSION = '0.15';
7              
8 35     35   182 use Carp;
  35         58  
  35         2372  
9 35     35   210 use Scalar::Util qw(blessed reftype weaken refaddr);
  35         58  
  35         3276  
10 35     35   222 use B qw(ppname);
  35         55  
  35         1825  
11              
12 35     35   15201 use Devel::Chitin::OpTree::UNOP;
  35         119  
  35         1307  
13 35     35   14667 use Devel::Chitin::OpTree::SVOP;
  35         89  
  35         1000  
14 35     35   13194 use Devel::Chitin::OpTree::PADOP;
  35         81  
  35         1045  
15 35     35   12524 use Devel::Chitin::OpTree::COP;
  35         83  
  35         1073  
16 35     35   13621 use Devel::Chitin::OpTree::PVOP;
  35         95  
  35         1318  
17 35     35   13811 use Devel::Chitin::OpTree::METHOP;
  35         96  
  35         1230  
18 35     35   14410 use Devel::Chitin::OpTree::BINOP;
  35         119  
  35         1596  
19 35     35   14603 use Devel::Chitin::OpTree::LOGOP;
  35         93  
  35         1368  
20 35     35   13901 use Devel::Chitin::OpTree::LOGOP_AUX;
  35         85  
  35         1206  
21 35     35   15037 use Devel::Chitin::OpTree::LISTOP;
  35         133  
  35         2315  
22 35     35   15579 use Devel::Chitin::OpTree::LOOP;
  35         79  
  35         1299  
23 35     35   12739 use Devel::Chitin::OpTree::PMOP;
  35         88  
  35         2106  
24             BEGIN {
25 35 50   35   386 if ($^V ge v5.22.0) {
26 35         14267 require Devel::Chitin::OpTree::UNOP_AUX;
27             }
28             }
29              
30             my %objs_for_op;
31             sub _obj_for_op {
32 2819     2819   5516 my($self, $op) = @_;
33 2819         7478 $objs_for_op{$$op};
34             }
35             sub build_from_location {
36 332     332 0 737 my($class, $start) = @_;
37              
38 332         799 my($start_op, $cv) = _determine_start_of($start);
39              
40             # adapted from B::walkoptree_slow
41 332         610 my @parents;
42             my $build_walker;
43             $build_walker = sub {
44 6689     6689   10190 my $op = shift;
45              
46 6689         12792 my $self = $class->new(op => $op, cv => $cv);
47 6689         22346 $objs_for_op{$$op} = $self;
48 6689         20285 weaken $objs_for_op{$$op};
49              
50 6689         8650 my @children;
51 6689 100 66     28289 if ($$op && ($op->flags & B::OPf_KIDS)) {
52 2751         5115 unshift(@parents, $self);
53 2751         9589 for (my $kid_op = $op->first; $$kid_op; $kid_op = $kid_op->sibling) {
54 6356         15037 push @children, $build_walker->($kid_op);
55             }
56 2751         4836 shift(@parents);
57             }
58              
59 6689 100 100     32710 if (B::class($op) eq 'PMOP'
      100        
      66        
60             and ref($op->pmreplroot)
61 20         115 and ${$op->pmreplroot}
62             and $op->pmreplroot->isa('B::OP')
63             ) {
64 1         3 unshift @parents, $self;
65 1         5 push @children, $build_walker->($op->pmreplroot);
66 1         1 shift @parents;
67             }
68              
69 6689         17129 @$self{'parent','children'} = ($parents[0], \@children);
70 6689         29224 $self;
71 332         1629 };
72              
73 332         845 $build_walker->($start_op);
74             }
75              
76             sub _determine_start_of {
77 332     332   549 my $start = shift;
78              
79 332 100       1298 if (reftype($start) eq 'CODE') {
80 3         9 my $cv = B::svref_2object($start);
81 3         12 return ($cv->ROOT, $cv);
82             }
83              
84 329 50 33     2167 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     1046 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         645 my $subname = join('::', $start->package, $start->subroutine);
96 35     35   285 my $subref = do { no strict 'refs'; \&$subname };
  35         65  
  35         90807  
  329         552  
  329         1676  
97 329         1168 my $cv = B::svref_2object($subref);
98 329         1604 return ($cv->ROOT, $cv);
99             }
100             }
101              
102             sub new {
103 6689     6689 0 16581 my($class, %params) = @_;
104 6689 50       13517 unless (exists $params{op}) {
105 0         0 Carp::croak(q{'op' is a required parameter of new()});
106             }
107              
108 6689         10806 my $final_class = _class_for_op($params{op});
109              
110 6689         13097 my $self = bless \%params, $final_class;
111 6689         14873 $self->_build();
112 6689         10960 return $self;
113             }
114              
115             sub _class_for_op {
116 6689     6689   8910 my $op = shift;
117 6689         30969 my $b_class = B::class($op);
118 6689 100 100     23562 if ($b_class eq 'OP') {
    100 66        
119 1905         3457 return __PACKAGE__,
120             } elsif ($b_class eq 'UNOP'
121             and $op->name eq 'null'
122             and $op->flags & B::OPf_KIDS
123             ) {
124 558         843 my $num_children = 0;
125 558         2042 for (my $kid_op = $op->first; $$kid_op; $kid_op = $kid_op->sibling) {
126 995         3317 $num_children++ ;
127             }
128 558 100       1287 if ($num_children > 2) {
    100          
129 84         227 return join('::', __PACKAGE__, 'LISTOP');
130             } elsif ($num_children > 1) {
131 216         593 return join('::', __PACKAGE__, 'BINOP');
132              
133             } else {
134 258         721 return join('::', __PACKAGE__, 'UNOP');
135             }
136             } else {
137 4226         20053 join('::', __PACKAGE__, B::class($op));
138             }
139             }
140              
141       6689     sub _build { }
142              
143 37184     37184 1 139908 sub op { shift->{op} }
144 15202     15202 1 31736 sub parent { shift->{parent} }
145 4786     4786 1 10435 sub children { shift->{children} }
146 1242     1242 1 9751 sub cv { shift->{cv} }
147             sub root_op {
148 2101     2101 1 2957 my $obj = shift;
149 2101         3520 $obj = $obj->parent while ($obj->parent);
150 2101         4793 $obj;
151             }
152              
153             sub next {
154 664     664 1 1178 my $self = shift;
155 664         1324 $self->_obj_for_op($self->op->next);
156             }
157              
158             sub sibling {
159 2150     2150 1 2941 my $self = shift;
160 2150         3513 $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 416 my($self, $cb) = @_;
171 6         20 $cb->($self);
172 6         22 $_->walk_inorder($cb) foreach (@{ $self->children } );
  6         28  
173             }
174              
175             sub deparse {
176 5939     5939 1 10664 my $self = shift;
177 5939         9691 my $bounce = 'pp_' . $self->op->name;
178 5939         20309 $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 8476     8476 0 13491 return shift->op->name eq 'null';
190             }
191              
192             sub pp_null {
193 484     484 0 685 my $self = shift;
194 484         924 my $bounce = $self->_ex_name;
195              
196 484 100       1119 if ($bounce eq 'pp_null') {
197 35         68 my $children = $self->children;
198 35 100 66     140 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         14 $self->Devel::Chitin::OpTree::BINOP::pp_sassign(is_swapped => 1);
204              
205             } elsif (@$children == 1) {
206 31         68 $children->[0]->deparse(@_);
207              
208             } else {
209 0         0 ";\n" # maybe a COP that got optimized away?
210             }
211              
212             } else {
213 449         1325 $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 1635 my $self = shift;
223             # These are 'my' variables. We're omitting the 'my' because
224             # that happens at compile time
225 1134         2265 $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 6 my $self = shift;
232 3         8 my $list_name = substr($self->pp_padav, 1); # remove the sigil
233 3         12 "\$${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   1644 my $self = shift;
252 1226   66     2699 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         2542 return $self->cv->PADLIST->ARRAYelt(0)->ARRAYelt( $targ );
257             }
258              
259             sub _padval_sv {
260 3     3   7 my($self, $idx) = @_;
261 3         10 return $self->cv->PADLIST->ARRAYelt(1)->ARRAYelt( $idx );
262             }
263              
264             sub _gv_name {
265 244     244   520 my($self, $gv) = @_;
266 244         647 my $last_cop = $self->nearest_cop();
267 244         630 my $curr_package = $last_cop->op->stashpv;
268 244         1356 my $gv_package = $gv->STASH->NAME;
269              
270 244 100       1763 $curr_package eq $gv_package
271             ? $gv->NAME
272             : join('::', $gv_package, $gv->NAME);
273             }
274              
275             sub _ex_name {
276 963     963   1566 my $self = shift;
277 963 100       1466 if ($self->op->name eq 'null') {
278 958         1652 ppname($self->op->targ);
279             }
280             }
281              
282             sub _sibling_helper {
283 10     10   20 my($self, $cb) = @_;
284 10         19 my $parent = $self->parent;
285 10 50       21 return unless $parent;
286 10         21 my $children = $parent->children;
287 10 50 33     35 return unless ($children and @$children);
288              
289 10         27 for (my $i = 0; $i < @$children; $i++) {
290 22 100       87 if ($children->[$i] eq $self) {
291 10         22 return $cb->($i, $children);
292             }
293             }
294             }
295             sub pre_siblings {
296 10     10 0 16 my $self = shift;
297             $self->_sibling_helper(sub {
298 10     10   16 my($i, $children) = @_;
299 10         82 @$children[0 .. ($i-1)];
300 10         53 });
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             );
327             sub print_as_tree {
328 0     0 0 0 my $self = shift;
329 0         0 my $current_callsite = shift;
330              
331             $self->walk_inorder(sub {
332 0     0   0 my $op = shift;
333 0         0 my($level, $parent) = (0, $op);
334 0         0 $level++ while($parent = $parent->parent);
335 0         0 my $name = $op->op->name;
336 0 0       0 if ($name eq 'null') {
337 0         0 $name .= ' (ex-' . $op->_ex_name . ')';
338             }
339              
340 0         0 my @flags = _parse_bit_flags($op->op->flags, %flag_values);
341 0         0 my @private = _parse_bit_flags($op->op->private, %private_values);
342              
343 0         0 my $mini_deparsed = '';
344 0 0 0     0 if ($op->class eq 'COP') {
    0 0        
    0          
345 0         0 $mini_deparsed = join(':', $op->op->file, $op->op->line);
346             } elsif ($op->is_scalar_container
347             or $op->is_array_container
348             or $op->op->name eq 'const'
349             ) {
350 0         0 $mini_deparsed = $op->deparse;
351 0 0       0 $mini_deparsed = '' unless defined $mini_deparsed; # multiconcat can optimze away the target of an assignment
352              
353             } elsif ($op->op->name eq 'multiconcat') {
354 0         0 my($nargs, $const_str, @substr_lengths) = $op->op->aux_list($op->cv);
355 0         0 my $substr_lengths = join(',', @substr_lengths);
356              
357 0         0 my $target= '';
358 0 0       0 if ($op->op->private & B::OPpTARGET_MY) {
359 0         0 $target = $op->_padname_sv($op->op->targ)->PV . ' = ';
360             }
361              
362 0         0 push @private, _parse_bit_flags($op->op->private,
363             ( APPEND => &B::OPpMULTICONCAT_APPEND,
364             STRINGIFY => &B::OPpMULTICONCAT_STRINGIFY,
365             SPRINTF => &B::OPpMULTICONCAT_FAKE,
366             ));
367 0         0 $mini_deparsed = qq(${target}"$const_str"[$substr_lengths]);
368             }
369              
370 0 0 0     0 my $indent = ($current_callsite and ${$op->op} == $current_callsite)
371             ? '=>' . (' ' x($level-1))
372             : ' 'x$level;
373             printf("%s%s %s (%s) %s %s 0x%x\n", $indent, $op->class, $name,
374             join(', ', @flags),
375             $mini_deparsed,
376             join(', ', @private),
377 0 0       0 $current_callsite ? ${$op->op} : refaddr($op));
  0         0  
378 0         0 });
379             }
380              
381             sub class {
382 534     534 0 848 my $self = shift;
383 534         2275 return substr(ref($self), rindex(ref($self), ':')+1);
384             }
385              
386             sub nearest_cop {
387 891     891 0 1293 my $self = shift;
388              
389 891         1492 my $parent = $self->parent;
390 891 50       1734 return unless $parent;
391 891         1549 my $siblings = $parent->children;
392 891 50 33     2733 return unless $siblings and @$siblings;
393              
394 891         1897 for (my $i = 0; $i < @$siblings; $i++) {
395 1114         1650 my $sib = $siblings->[$i];
396 1114 100       3158 if ($sib eq $self) {
    100          
397             # Didn't find it on one of the siblings already executed, try the parent
398 647         1432 return $parent->nearest_cop();
399              
400             } elsif ($sib->class eq 'COP') {
401 244         883 return $sib;
402             }
403             }
404 0         0 return;
405             }
406              
407             sub check_feature {
408 0 0   0 0 0 return unless $^V ge v5.10.1; # hints hash didn't exist before this
409 0         0 my($self, $feature_name) = @_;
410 0         0 my $cop = $self->nearest_cop;
411 0         0 my $hints = $cop->op->hints_hash->HASH;
412 0         0 return $hints->{"feature_${feature_name}"};
413             }
414              
415             # The current COP op is stored on scope-like OPs, and on the root op
416             sub _enter_scope {
417 51     51   109 shift->{cur_cop} = undef;
418             }
419             sub _leave_scope {
420 51     51   102 shift->{cur_cop} = undef;
421             }
422             sub _get_cur_cop {
423 1052     1052   2072 shift->root_op->{cur_cop};
424             }
425             sub _get_cur_cop_in_scope {
426 0     0   0 shift->_encompassing_scope_op->{cur_cop};
427             }
428             sub _set_cur_cop {
429 1048     1048   1729 my $self = shift;
430 1048         1927 $self->_encompassing_scope_op->{cur_cop} = $self;
431 1048         2331 $self->root_op->{cur_cop} = $self;
432             };
433             sub _encompassing_scope_op {
434 1048     1048   1467 my $self = my $op = shift;
435 1048   66     2645 for(; $op && !$op->is_scopelike; $op = $op->parent) { }
436 1048 50       2851 $op || $self->root_op;
437             }
438              
439             # Usually, rand/srand/pop/shift is an UNOP, but with no args, it's a base-OP
440             foreach my $d ( [ pp_rand => 'rand' ],
441             [ pp_srand => 'srand' ],
442             [ pp_getppid => 'getppid' ],
443             [ pp_wait => 'wait' ],
444             [ pp_time => 'time' ],
445             ) {
446             my($pp_name, $perl_name) = @$d;
447             my $sub = sub {
448 8     8   28 my $target = shift->_maybe_targmy;
449 8         29 "${target}${perl_name}()";
450             };
451 35     35   340 no strict 'refs';
  35         77  
  35         5594  
452             *$pp_name = $sub;
453             }
454              
455             # Chdir and sleep can be either a UNOP or base-OP
456             foreach my $d ( [ pp_chdir => 'chdir' ],
457             [ pp_sleep => 'sleep' ],
458             [ pp_localtime => 'localtime' ],
459             [ pp_gmtime => 'gmtime' ],
460             ) {
461             my($pp_name, $perl_name) = @$d;
462             my $sub = sub {
463 9     9   20 my $self = shift;
464 9         26 my $children = $self->children;
465 9         37 my $target = $self->_maybe_targmy;
466 9 100       34 if (@$children) {
467 5         37 "${target}${perl_name}(" . $children->[0]->deparse . ')';
468             } else {
469 4         20 "${target}${perl_name}()";
470             }
471             };
472 35     35   248 no strict 'refs';
  35         62  
  35         30290  
473             *$pp_name = $sub;
474             }
475              
476 0     0 0 0 sub pp_enter { '' }
477 1     1 0 4 sub pp_stub { ';' }
478 0     0 0 0 sub pp_unstack { '' }
479 6     6 0 18 sub pp_undef { 'undef' }
480 1     1 0 4 sub pp_wantarray { 'wantarray' }
481 1     1 0 5 sub pp_dump { 'dump' }
482 1     1 0 4 sub pp_next { 'next' }
483 0     0 0 0 sub pp_last { 'last' }
484 1     1 0 4 sub pp_redo { 'redo' }
485 3     3 0 11 sub pp_const { q('constant optimized away') }
486              
487 1     1 0 5 sub pp_close { 'close()' }
488 1     1 0 6 sub pp_getc { 'getc()' }
489 1     1 0 5 sub pp_tell { 'tell()' }
490 1     1 0 8 sub pp_enterwrite { 'write()' }
491 2     2 0 7 sub pp_fork { 'fork()' }
492 2     2 0 6 sub pp_tms { 'times()' }
493 2     2 0 6 sub pp_ggrent { 'getgrent()' }
494 0     0 0 0 sub pp_eggrent { 'endgrent()' }
495 1     1 0 4 sub pp_ehostent { 'endhostent()' }
496 1     1 0 3 sub pp_enetent { 'endnetent()' }
497 1     1 0 4 sub pp_eservent { 'endservent()' }
498 1     1 0 3 sub pp_egrent { 'endgrent()' }
499 1     1 0 5 sub pp_epwent { 'endpwent()' }
500 1     1 0 3 sub pp_spwent { 'setpwent()' }
501 1     1 0 4 sub pp_sgrent { 'setgrent()' }
502 3     3 0 8 sub pp_gpwent { 'getpwent()' }
503 1     1 0 4 sub pp_getlogin { 'getlogin()' }
504 2     2 0 6 sub pp_ghostent { 'gethostent()' }
505 2     2 0 6 sub pp_gnetent { 'getnetent()' }
506 2     2 0 5 sub pp_gprotoent { 'getprotoent()' }
507 2     2 0 6 sub pp_gservent { 'getservent()' }
508 2     2 0 7 sub pp_caller { 'caller()' }
509 1     1 0 3 sub pp_exit { 'exit()' }
510 1     1 0 3 sub pp_umask { 'umask()' }
511              
512             sub pp_eof {
513 2 100   2 0 9 shift->op->flags & B::OPf_SPECIAL
514             ? 'eof()'
515             : 'eof';
516             }
517              
518             sub pp_break {
519 1     1 0 3 my $self = shift;
520 1 50       3 ($self->op->flags & B::OPf_SPECIAL)
521             ? '' # an implicit break at the end of each when { }
522             : 'break'; # and explicit break
523             }
524              
525             sub is_implicit_break_at_end_of_when_block {
526 1047     1047 0 1616 my $self = shift;
527              
528 1047 50       1767 $self->op->name eq 'break'
529             and $self->op->flags & B::OPf_SPECIAL
530             }
531              
532             sub pp_continue {
533 1     1 0 4 'continue';
534             }
535              
536             # Starting with Perl 5.14, these are base-ops with the special flag set when used without args
537             foreach my $a ( [ pp_shift => 'shift' ],
538             [ pp_pop => 'pop' ],
539             ) {
540             my($pp_name, $perl_name) = @$a;
541             my $sub = sub {
542 6     6   8 my $self = shift;
543 6 100       14 if ($self->op->flags & B::OPf_SPECIAL) {
544 2         7 "$perl_name";
545             } else {
546 4         10 my $arg = $self->first->deparse;
547 4 50       10 if ($arg eq '@_') {
548 0         0 "$perl_name";
549             } else {
550 4         16 "$perl_name($arg)";
551             }
552             }
553             };
554 35     35   282 no strict 'refs';
  35         68  
  35         4891  
555             *$pp_name = $sub;
556             }
557              
558             # reference aliasing OPs
559             foreach my $a ( [ pp_lvavref => '@' ],
560             [ pp_lvref => '$' ],
561             ) {
562             my($pp_name, $sigil) = @$a;
563             my $sub = sub {
564 4     4   7 my $self = shift;
565 4 100       11 my $var = $self->op->flags & B::OPf_STACKED
566             ? $sigil . $self->children->[0]->deparse # an our var
567             : $self->_padname_sv->PV; # a my var
568 4         15 "\($var)";
569             };
570 35     35   300 no strict 'refs';
  35         77  
  35         10074  
571             *$pp_name = $sub;
572             }
573              
574             # file test operators
575             # These actually show up as UNOPs (usually) and SVOPs (-X _) but it's
576             # convienent to put them here in the base class
577             foreach my $a ( [ pp_fteread => '-r' ],
578             [ pp_ftewrite => '-w' ],
579             [ pp_fteexec => '-x' ],
580             [ pp_fteowned => '-o' ],
581             [ pp_ftrread => '-R' ],
582             [ pp_ftrwrite => '-W' ],
583             [ pp_ftrexec => '-X' ],
584             [ pp_ftrowned => '-O' ],
585             [ pp_ftis => '-e' ],
586             [ pp_ftzero => '-z' ],
587             [ pp_ftsize => '-s' ],
588             [ pp_ftfile => '-f' ],
589             [ pp_ftdir => '-d' ],
590             [ pp_ftlink => '-l' ],
591             [ pp_ftpipe => '-p' ],
592             [ pp_ftblk => '-b' ],
593             [ pp_ftsock => '-S' ],
594             [ pp_ftchr => '-c' ],
595             [ pp_fttty => '-t' ],
596             [ pp_ftsuid => '-u' ],
597             [ pp_ftsgid => '-g' ],
598             [ pp_ftsvtx => '-k' ],
599             [ pp_fttext => '-T' ],
600             [ pp_ftbinary => '-B' ],
601             [ pp_ftmtime => '-M' ],
602             [ pp_ftatime => '-A' ],
603             [ pp_ftctime => '-C' ],
604             [ pp_stat => 'stat' ],
605             [ pp_lstat => 'lstat' ],
606             ) {
607             my($pp_name, $perl_name) = @$a;
608             my $sub = sub {
609 44     44   111 my $self = shift;
610              
611 44         85 my $fh;
612 44 100       150 if ($self->class eq 'UNOP') {
613 35         113 $fh = $self->children->[0]->deparse;
614 35 100       158 $fh = '' if $fh eq '$_';
615             } else {
616             # It's a test on _: -w _
617 9 50       34 $fh = $self->class eq 'SVOP'
618             ? $self->Devel::Chitin::OpTree::SVOP::pp_gv()
619             : $self->Devel::Chitin::OpTree::PADOP::pp_gv();
620             }
621              
622 44 100       182 if (substr($perl_name, 0, 1) eq '-') {
623             # -X type test
624 30 100       79 if ($fh) {
625 25         154 "$perl_name $fh";
626             } else {
627 5         32 $perl_name;
628             }
629             } else {
630 14         70 "${perl_name}($fh)";
631             }
632             };
633 35     35   243 no strict 'refs';
  35         75  
  35         62403  
634             *$pp_name = $sub;
635             }
636              
637             # The return values for some OPs is encoded specially, and not through a
638             # normal sassign
639             sub _maybe_targmy {
640 194     194   371 my $self = shift;
641              
642 194 100       359 if ($self->op->private & B::OPpTARGET_MY) {
643 70         184 $self->_padname_sv->PV . ' = ';
644             } else {
645 124         361 '';
646             }
647             }
648              
649             # return true for scalar things we can assign to
650             my %scalar_container_ops = (
651             rv2sv => 1,
652             pp_rv2sv => 1,
653             padsv => 1,
654             pp_padsv => 1,
655             );
656             sub is_scalar_container {
657 174     174 0 270 my $self = shift;
658 174 100       344 my $op_name = $self->is_null
659             ? $self->_ex_name
660             : $self->op->name;
661 174         751 $scalar_container_ops{$op_name};
662             }
663              
664             my %array_container_ops = (
665             rv2av => 1,
666             pp_rv2av => 1,
667             padav => 1,
668             pp_padav => 1,
669             );
670             sub is_array_container {
671 15     15 0 24 my $self = shift;
672 15 100       26 my $op_name = $self->is_null
673             ? $self->_ex_name
674             : $self->op->name;
675 15         70 $array_container_ops{$op_name};
676             }
677              
678             my %scopelike_ops = (
679             scope => 1,
680             pp_scope => 1,
681             leave => 1,
682             pp_leave => 1,
683             leavetry => 1,
684             pp_leavetry => 1,
685             leavesub => 1,
686             pp_leavesub => 1,
687             leaveloop => 1,
688             pp_leaveloop => 1,
689             entergiven => 1,
690             pp_entergiven => 1,
691             enterwhile => 1,
692             pp_enterwhile => 1,
693             #entergiven => 1, # Part of the reverted given/whereso/whereis from 5.27.7
694             #pp_entergiven => 1,
695             #enterwhereso => 1,
696             #pp_enterwhereso => 1,
697             );
698             sub is_scopelike {
699 4498     4498 0 6124 my $self = shift;
700 4498 100       7283 my $op_name = $self->is_null
701             ? $self->_ex_name
702             : $self->op->name;
703 4498         14130 $scopelike_ops{$op_name};
704             }
705              
706             sub is_for_loop {
707 2118     2118 0 3025 my $self = shift;
708             # $self, here, is the initialization part of the for-loop, usually an sassign.
709             # The sibling is either:
710             # 1) a lineseq whose first child is a nextstate and second child is a leaveloop
711             # 2) an unstack whose sibling is a leaveloop
712 2118         3929 my $sib = $self->sibling;
713 2118 100 100     12806 return '' if !$sib or $self->isa('Devel::Chitin::OpTree::COP') or $self->is_null;
      100        
714              
715 658         1488 my $name = $sib->op->name;
716 658 50 100     2292 if ($name eq 'lineseq') {
    100          
717 0         0 my($first ,$second) = @{$sib->children};
  0         0  
718 0 0 0     0 if ($first && ! $first->is_null && $first->isa('Devel::Chitin::OpTree::COP')
      0        
      0        
      0        
      0        
719             && $second && ! $second->is_null && $second->op->name eq 'leaveloop'
720             ) {
721 0         0 return 1;
722             }
723              
724             } elsif ($name eq 'unstack' && ($sib->op->flags & B::OPf_SPECIAL)) {
725 1         16 my $sibsib = $sib->sibling;
726 1   33     13 return $sibsib && ! $sibsib->is_null && $sibsib->op->name eq 'leaveloop'
727             }
728 657         1711 return ''
729             }
730              
731             # Return true for
732             # if (conditional) { ... }
733             # and
734             # unless (conditional) { ... }
735             sub is_if_statement {
736 12     12 0 19 my $self = shift;
737 12         22 my $name = $self->op->name;
738              
739 12 50 66     67 ( $name eq 'and' or $name eq 'or' or $name eq 'cond_expr')
      33        
740             and $self->other->is_scopelike;
741             }
742              
743             sub is_postfix_if {
744 8     8 0 12 my $self = shift;
745 8         16 my $name = $self->op->name;
746              
747 8 100 66     42 ( $name eq 'and' or $name eq 'or' )
      33        
      66        
748             and $self->parent->is_null
749             and $self->parent->pre_siblings
750             and ($self->parent->pre_siblings)[-1]->class eq 'COP'
751             }
752              
753             sub _num_ops_in_for_loop {
754 1     1   2 my $self = shift;
755 1 50       3 $self->sibling->op->name eq 'unstack' ? 2 : 1;
756             }
757              
758             sub _deparse_for_loop {
759 1     1   2 my $self = shift;
760             # A for-loop is structured like this:
761             # nextstate
762             # sassign ( initialization)
763             # ...
764             # unstack
765             # leaveloop
766             # enterloop
767             # null
768             # and
769             # loop-test
770             # ...
771             # lineseq
772             # leave
773             # ... (loop body)
774             # loop-continue
775 1         3 my $init = $self->deparse;
776 1         4 my $sib = $self->sibling;
777 1 50       5 my $leaveloop = $sib->op->name eq 'unstack' ? $sib->sibling : $sib->children->[1];
778 1         4 my $and_op = $leaveloop->children->[1]->children->[0];
779 1         3 my $test_op = $and_op->children->[0];
780 1         3 my $test = $test_op->deparse;
781 1         3 my $body_op = $and_op->children->[1]->first;
782 1         2 my $cont_op = $body_op->sibling;
783 1         4 my $cont = $cont_op->deparse;
784              
785 1         16 "for ($init; $test; $cont) " . $body_op->deparse;
786             }
787              
788             # Return true if this op is the inner list on the right of
789             # \(@a) = \(@b)
790             # The optree for this construct looks like:
791             # aassign
792             # ex-list
793             # pushmark
794             # refgen
795             # ex-list <-- Return true here
796             # pushmark
797             # padav/gv
798             # ex-list
799             # pushmark
800             # ex-refgen
801             # ex-list <-- return true here
802             # pushmark
803             # lvavref
804             sub is_list_reference_alias {
805 168     168 0 270 my $self = shift;
806              
807 168   66     308 return $self->is_null
808             && $self->_ex_name eq 'pp_list'
809             && $self->parent->op->name eq 'refgen'
810             && $self->last->is_array_container;
811             }
812              
813             # Based on B::Deparse::is_miniwhile()
814             sub _deparse_postfix_while {
815 1413     1413   2187 my $self = shift;
816              
817 1413         2566 my $top = $self->children->[1];
818 1413         1932 my $condition_op;
819 1413 50 66     2282 if ($self->op->name eq 'leave'
      100        
      66        
      66        
      66        
      33        
      33        
      33        
820             and $top
821             and $top->is_null
822             and $top->class eq 'UNOP'
823             and ($condition_op = $top->first)
824             and ($condition_op->op->name eq 'and' or $condition_op->op->name eq 'or')
825             and (
826             $top->first->children->[1]->op->name eq 'lineseq'
827             or
828             ( $top->first->op->name eq 'lineseq'
829             and ! $top->first->children->[1]->is_null
830             and $top->first->children->[1]->op->name eq 'unstack'
831             )
832             )
833             ) {
834 12         15 my $type;
835 12         25 my $condition = $condition_op->first->deparse;
836 12 100       28 if ($condition_op->op->name eq 'and') {
837 6         10 $type = 'while';
838             } else {
839 6         9 $type = 'until';
840 6         30 $condition =~ s/^!//;
841             }
842 12         32 return $condition_op->children->[1]->deparse . " $type ($condition)";
843             }
844 1401         5102 return '';
845             }
846              
847             sub is_postfix_foreach {
848 1349     1349 0 1816 my $self = shift;
849 1349   100     2259 return $self->op->name eq 'leaveloop'
850             && $self->first->op->name eq 'enteriter'
851             && ! $self->first->sibling->first->first->sibling->first->isa('Devel::Chitin::OpTree::COP');
852             }
853              
854             sub is_postfix_loop {
855 1349     1349 0 1889 my $self = shift;
856 1349   100     2392 return $self->is_postfix_foreach
857             || $self->_deparse_postfix_while;
858             }
859              
860             sub _quote_sv {
861 366     366   715 my($self, $sv, %params) = @_;
862 366         1171 my $string = $sv->PV;
863 366         931 $self->_quote_string($string, %params);
864             }
865              
866             sub _quote_string {
867 366     366   749 my($self, $string, %params) = @_;
868              
869             # Seems that multiconcat can have the BARE flag set erroneously? on 5.27.6
870 366 100 100     1112 my $quote = ($params{skip_quotes} or ($self->op->private & B::OPpCONST_BARE and $self->op->name ne 'multiconcat'))
871             ? ''
872             : q(');
873 366 100 100     1329 if ($string =~ m/[\000-\037]/ and !$params{regex_x_flag}) {
874 16 100       42 $quote = '"' unless $params{skip_quotes};
875 16         59 $string = $self->_escape_for_double_quotes($string, %params);
876             }
877              
878 366         2188 "${quote}${string}${quote}";
879             }
880              
881             my %control_chars = ((map { chr($_) => '\c'.chr($_ + 64) } (1 .. 26)), # \cA .. \cZ
882             "\c@" => '\c@', "\c[" => '\c[');
883             my $control_char_rx = join('|', sort keys %control_chars);
884             sub _escape_for_double_quotes {
885 16     16   41 my($self, $str, %params) = @_;
886              
887 16         39 $str =~ s/\\/\\\\/g;
888 16         37 $str =~ s/\a/\\a/g; # alarm
889 16 50       42 $str =~ s/\cH/\\b/g unless $params{in_regex}; # backspace
890 16         30 $str =~ s/\e/\\e/g; # escape
891 16         28 $str =~ s/\f/\\f/g; # form feed
892 16         52 $str =~ s/\n/\\n/g; # newline
893 16         34 $str =~ s/\r/\\r/g; # CR
894 16         32 $str =~ s/\t/\\t/g; # tab
895 16         30 $str =~ s/"/\\"/g;
896 16         176 $str =~ s/($control_char_rx)/$control_chars{$1}/ge;
  1         8  
897 16         43 $str =~ s/([[:^print:]])/sprintf('\x{%x}', ord($1))/ge;
  1         5  
898              
899 16         52 $str;
900             }
901              
902             sub _as_octal {
903 6     6   39 my($self, $val) = @_;
904 35     35   347 no warnings 'numeric';
  35         67  
  35         3798  
905 6 100       65 $val + 0 eq $val
906             ? sprintf('0%3o', $val)
907             : $val;
908             }
909              
910             # given an integer and a list of bitwise flag name/value pairs, return
911             # a string representing the flags or-ed together
912             sub _deparse_flags {
913 12     12   36 my($self, $val, $flags_listref) = @_;
914              
915 12         21 do {
916 35     35   251 no warnings 'numeric';
  35         67  
  35         10434  
917 12 100       38 unless ($val + 0 eq $val) {
918 1         4 return $val; # wasn't a number
919             }
920             };
921              
922 11         20 my @flags;
923 11         36 for (my $i = 0; $i < @$flags_listref; $i += 2) {
924 60         110 my($flag_name, $flag_value) = @$flags_listref[$i, $i+1];
925 60 100       141 if ($val & $flag_value) {
926 18         39 push @flags, $flag_name;
927 18         55 $val ^= $flag_value;
928             }
929             }
930 11 50       28 if ($val) {
931             # there were unexpected bits set
932 0         0 push @flags, $val;
933             }
934 11         48 join(' | ', @flags);
935             }
936              
937             sub _indent_block_text {
938 58     58   134 my($self, $text, %params) = @_;
939              
940 58         189 my $newlines = $text =~ s/\n/\n\t/g;
941 58 100 100     219 if ($newlines or $params{force_multiline}) {
    50          
942 35         111 "\n\t" . $text . "\n";
943             } elsif ($params{noindent}) {
944 0         0 $text;
945             } else {
946 23         85 " $text ";
947             }
948             }
949              
950             1;
951              
952             __END__