File Coverage

blib/lib/Devel/Chitin/OpTree.pm
Criterion Covered Total %
statement 373 437 85.3
branch 96 140 68.5
condition 76 135 56.3
subroutine 118 131 90.0
pod 10 70 14.2
total 673 913 73.7


line stmt bran cond sub pod time code
1             package Devel::Chitin::OpTree;
2              
3 35     35   540 use strict;
  35         61  
  35         881  
4 35     35   155 use warnings;
  35         89  
  35         1272  
5              
6             our $VERSION = '0.12'; # TRIAL
7              
8 35     35   173 use Carp;
  35         50  
  35         2007  
9 35     35   174 use Scalar::Util qw(blessed reftype weaken refaddr);
  35         51  
  35         2393  
10 35     35   171 use B qw(ppname);
  35         67  
  35         1365  
11              
12 35     35   9490 use Devel::Chitin::OpTree::UNOP;
  35         97  
  35         1083  
13 35     35   8508 use Devel::Chitin::OpTree::SVOP;
  35         64  
  35         813  
14 35     35   7682 use Devel::Chitin::OpTree::PADOP;
  35         73  
  35         829  
15 35     35   7537 use Devel::Chitin::OpTree::COP;
  35         72  
  35         928  
16 35     35   8239 use Devel::Chitin::OpTree::PVOP;
  35         79  
  35         1071  
17 35     35   8469 use Devel::Chitin::OpTree::METHOP;
  35         75  
  35         954  
18 35     35   8809 use Devel::Chitin::OpTree::BINOP;
  35         88  
  35         1311  
19 35     35   8865 use Devel::Chitin::OpTree::LOGOP;
  35         70  
  35         1120  
20 35     35   8006 use Devel::Chitin::OpTree::LOGOP_AUX;
  35         72  
  35         1009  
21 35     35   9888 use Devel::Chitin::OpTree::LISTOP;
  35         111  
  35         1943  
22 35     35   9985 use Devel::Chitin::OpTree::LOOP;
  35         73  
  35         1150  
23 35     35   8392 use Devel::Chitin::OpTree::PMOP;
  35         75  
  35         1687  
24             BEGIN {
25 35 50   35   329 if ($^V ge v5.22.0) {
26 35         8661 require Devel::Chitin::OpTree::UNOP_AUX;
27             }
28             }
29              
30             my %objs_for_op;
31             sub _obj_for_op {
32 2819     2819   5007 my($self, $op) = @_;
33 2819         6473 $objs_for_op{$$op};
34             }
35             sub build_from_location {
36 332     332 0 667 my($class, $start) = @_;
37              
38 332         624 my($start_op, $cv) = _determine_start_of($start);
39              
40             # adapted from B::walkoptree_slow
41 332         530 my @parents;
42             my $build_walker;
43             $build_walker = sub {
44 6689     6689   8005 my $op = shift;
45              
46 6689         10658 my $self = $class->new(op => $op, cv => $cv);
47 6689         16875 $objs_for_op{$$op} = $self;
48 6689         16665 weaken $objs_for_op{$$op};
49              
50 6689         8324 my @children;
51 6689 100 66     24488 if ($$op && ($op->flags & B::OPf_KIDS)) {
52 2751         4430 unshift(@parents, $self);
53 2751         8067 for (my $kid_op = $op->first; $$kid_op; $kid_op = $kid_op->sibling) {
54 6356         12926 push @children, $build_walker->($kid_op);
55             }
56 2751         4129 shift(@parents);
57             }
58              
59 6689 100 100     28596 if (B::class($op) eq 'PMOP'
      100        
      66        
60             and ref($op->pmreplroot)
61 20         142 and ${$op->pmreplroot}
62             and $op->pmreplroot->isa('B::OP')
63             ) {
64 1         5 unshift @parents, $self;
65 1         8 push @children, $build_walker->($op->pmreplroot);
66 1         3 shift @parents;
67             }
68              
69 6689         14987 @$self{'parent','children'} = ($parents[0], \@children);
70 6689         25303 $self;
71 332         1556 };
72              
73 332         645 $build_walker->($start_op);
74             }
75              
76             sub _determine_start_of {
77 332     332   597 my $start = shift;
78              
79 332 100       1211 if (reftype($start) eq 'CODE') {
80 3         11 my $cv = B::svref_2object($start);
81 3         12 return ($cv->ROOT, $cv);
82             }
83              
84 329 50 33     2140 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     985 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         656 my $subname = join('::', $start->package, $start->subroutine);
96 35     35   234 my $subref = do { no strict 'refs'; \&$subname };
  35         57  
  35         66971  
  329         475  
  329         1550  
97 329         1109 my $cv = B::svref_2object($subref);
98 329         1609 return ($cv->ROOT, $cv);
99             }
100             }
101              
102             sub new {
103 6689     6689 0 15600 my($class, %params) = @_;
104 6689 50       11843 unless (exists $params{op}) {
105 0         0 Carp::croak(q{'op' is a required parameter of new()});
106             }
107              
108 6689         9241 my $final_class = _class_for_op($params{op});
109              
110 6689         11505 my $self = bless \%params, $final_class;
111 6689         12997 $self->_build();
112 6689         9517 return $self;
113             }
114              
115             sub _class_for_op {
116 6689     6689   7570 my $op = shift;
117 6689         27944 my $b_class = B::class($op);
118 6689 100 100     21087 if ($b_class eq 'OP') {
    100 66        
119 1905         2999 return __PACKAGE__,
120             } elsif ($b_class eq 'UNOP'
121             and $op->name eq 'null'
122             and $op->flags & B::OPf_KIDS
123             ) {
124 558         852 my $num_children = 0;
125 558         1658 for (my $kid_op = $op->first; $$kid_op; $kid_op = $kid_op->sibling) {
126 995         2821 $num_children++ ;
127             }
128 558 100       1180 if ($num_children > 2) {
    100          
129 84         200 return join('::', __PACKAGE__, 'LISTOP');
130             } elsif ($num_children > 1) {
131 216         563 return join('::', __PACKAGE__, 'BINOP');
132              
133             } else {
134 258         622 return join('::', __PACKAGE__, 'UNOP');
135             }
136             } else {
137 4226         17282 join('::', __PACKAGE__, B::class($op));
138             }
139             }
140              
141       6689     sub _build { }
142              
143 37187     37187 1 120772 sub op { shift->{op} }
144 15202     15202 1 25527 sub parent { shift->{parent} }
145 4786     4786 1 8275 sub children { shift->{children} }
146 1242     1242 1 8130 sub cv { shift->{cv} }
147             sub root_op {
148 2101     2101 1 2505 my $obj = shift;
149 2101         2999 $obj = $obj->parent while ($obj->parent);
150 2101         3940 $obj;
151             }
152              
153             sub next {
154 664     664 1 932 my $self = shift;
155 664         1013 $self->_obj_for_op($self->op->next);
156             }
157              
158             sub sibling {
159 2150     2150 1 2450 my $self = shift;
160 2150         3258 $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 436 my($self, $cb) = @_;
171 6         12 $cb->($self);
172 6         17 $_->walk_inorder($cb) foreach (@{ $self->children } );
  6         15  
173             }
174              
175             sub deparse {
176 5939     5939 1 9054 my $self = shift;
177 5939         8227 my $bounce = 'pp_' . $self->op->name;
178 5939         16974 $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 11836 return shift->op->name eq 'null';
190             }
191              
192             sub pp_null {
193 484     484 0 616 my $self = shift;
194 484         853 my $bounce = $self->_ex_name;
195              
196 484 100       958 if ($bounce eq 'pp_null') {
197 35         62 my $children = $self->children;
198 35 100 66     152 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         82 $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 1356 my $self = shift;
223             # These are 'my' variables. We're omitting the 'my' because
224             # that happens at compile time
225 1134         1859 $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         13 "\$${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   1387 my $self = shift;
252 1226   66     2393 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         2120 return $self->cv->PADLIST->ARRAYelt(0)->ARRAYelt( $targ );
257             }
258              
259             sub _padval_sv {
260 3     3   7 my($self, $idx) = @_;
261 3         8 return $self->cv->PADLIST->ARRAYelt(1)->ARRAYelt( $idx );
262             }
263              
264             sub _gv_name {
265 244     244   430 my($self, $gv) = @_;
266 244         482 my $last_cop = $self->nearest_cop();
267 244         431 my $curr_package = $last_cop->op->stashpv;
268 244         928 my $gv_package = $gv->STASH->NAME;
269              
270 244 100       1500 $curr_package eq $gv_package
271             ? $gv->NAME
272             : join('::', $gv_package, $gv->NAME);
273             }
274              
275             sub _ex_name {
276 963     963   1203 my $self = shift;
277 963 100       1357 if ($self->op->name eq 'null') {
278 958         1468 ppname($self->op->targ);
279             }
280             }
281              
282             sub _sibling_helper {
283 10     10   18 my($self, $cb) = @_;
284 10         17 my $parent = $self->parent;
285 10 50       19 return unless $parent;
286 10         19 my $children = $parent->children;
287 10 50 33     32 return unless ($children and @$children);
288              
289 10         41 for (my $i = 0; $i < @$children; $i++) {
290 22 100       56 if ($children->[$i] eq $self) {
291 10         20 return $cb->($i, $children);
292             }
293             }
294             }
295             sub pre_siblings {
296 10     10 0 17 my $self = shift;
297             $self->_sibling_helper(sub {
298 10     10   17 my($i, $children) = @_;
299 10         73 @$children[0 .. ($i-1)];
300 10         54 });
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 635 my $self = shift;
383 534         2041 return substr(ref($self), rindex(ref($self), ':')+1);
384             }
385              
386             sub nearest_cop {
387 891     891 0 1107 my $self = shift;
388              
389 891         1235 my $parent = $self->parent;
390 891 50       1550 return unless $parent;
391 891         1220 my $siblings = $parent->children;
392 891 50 33     2400 return unless $siblings and @$siblings;
393              
394 891         1670 for (my $i = 0; $i < @$siblings; $i++) {
395 1114         1396 my $sib = $siblings->[$i];
396 1114 100       2441 if ($sib eq $self) {
    100          
397             # Didn't find it on one of the siblings already executed, try the parent
398 647         1201 return $parent->nearest_cop();
399              
400             } elsif ($sib->class eq 'COP') {
401 244         754 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   107 shift->{cur_cop} = undef;
418             }
419             sub _leave_scope {
420 51     51   98 shift->{cur_cop} = undef;
421             }
422             sub _get_cur_cop {
423 1052     1052   2007 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   1261 my $self = shift;
430 1048         1679 $self->_encompassing_scope_op->{cur_cop} = $self;
431 1048         1943 $self->root_op->{cur_cop} = $self;
432             };
433             sub _encompassing_scope_op {
434 1048     1048   1264 my $self = my $op = shift;
435 1048   66     2375 for(; $op && !$op->is_scopelike; $op = $op->parent) { }
436 1048 50       2480 $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   20 my $target = shift->_maybe_targmy;
449 8         28 "${target}${perl_name}()";
450             };
451 35     35   285 no strict 'refs';
  35         75  
  35         4576  
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   16 my $self = shift;
464 9         17 my $children = $self->children;
465 9         27 my $target = $self->_maybe_targmy;
466 9 100       21 if (@$children) {
467 5         17 "${target}${perl_name}(" . $children->[0]->deparse . ')';
468             } else {
469 4         15 "${target}${perl_name}()";
470             }
471             };
472 35     35   210 no strict 'refs';
  35         67  
  35         21588  
473             *$pp_name = $sub;
474             }
475              
476 0     0 0 0 sub pp_enter { '' }
477 1     1 0 3 sub pp_stub { ';' }
478 0     0 0 0 sub pp_unstack { '' }
479 6     6 0 17 sub pp_undef { 'undef' }
480 1     1 0 3 sub pp_wantarray { 'wantarray' }
481 1     1 0 2 sub pp_dump { 'dump' }
482 1     1 0 3 sub pp_next { 'next' }
483 0     0 0 0 sub pp_last { 'last' }
484 1     1 0 3 sub pp_redo { 'redo' }
485 3     3 0 15 sub pp_const { q('constant optimized away') }
486              
487 1     1 0 5 sub pp_close { 'close()' }
488 1     1 0 4 sub pp_getc { 'getc()' }
489 1     1 0 3 sub pp_tell { 'tell()' }
490 1     1 0 6 sub pp_enterwrite { 'write()' }
491 2     2 0 5 sub pp_fork { 'fork()' }
492 2     2 0 7 sub pp_tms { 'times()' }
493 2     2 0 8 sub pp_ggrent { 'getgrent()' }
494 0     0 0 0 sub pp_eggrent { 'endgrent()' }
495 1     1 0 3 sub pp_ehostent { 'endhostent()' }
496 1     1 0 4 sub pp_enetent { 'endnetent()' }
497 1     1 0 4 sub pp_eservent { 'endservent()' }
498 1     1 0 5 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 9 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 5 sub pp_gservent { 'getservent()' }
508 2     2 0 6 sub pp_caller { 'caller()' }
509 1     1 0 4 sub pp_exit { 'exit()' }
510 1     1 0 4 sub pp_umask { 'umask()' }
511              
512             sub pp_eof {
513 2 100   2 0 6 shift->op->flags & B::OPf_SPECIAL
514             ? 'eof()'
515             : 'eof';
516             }
517              
518             sub pp_break {
519 1     1 0 2 my $self = shift;
520 1 50       5 ($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 1426 my $self = shift;
527              
528 1047 50       1633 $self->op->name eq 'break'
529             and $self->op->flags & B::OPf_SPECIAL
530             }
531              
532             sub pp_continue {
533 1     1 0 3 '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   9 my $self = shift;
543 6 100       10 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         15 "$perl_name($arg)";
551             }
552             }
553             };
554 35     35   234 no strict 'refs';
  35         66  
  35         3600  
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   8 my $self = shift;
565 4 100       9 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         16 "\($var)";
569             };
570 35     35   209 no strict 'refs';
  35         63  
  35         6883  
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   77 my $self = shift;
610              
611 44         54 my $fh;
612 44 100       93 if ($self->class eq 'UNOP') {
613 35         77 $fh = $self->children->[0]->deparse;
614 35 100       91 $fh = '' if $fh eq '$_';
615             } else {
616             # It's a test on _: -w _
617 9 50       19 $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       117 if (substr($perl_name, 0, 1) eq '-') {
623             # -X type test
624 30 100       52 if ($fh) {
625 25         133 "$perl_name $fh";
626             } else {
627 5         19 $perl_name;
628             }
629             } else {
630 14         44 "${perl_name}($fh)";
631             }
632             };
633 35     35   197 no strict 'refs';
  35         56  
  35         45712  
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   248 my $self = shift;
641              
642 194 100       284 if ($self->op->private & B::OPpTARGET_MY) {
643 70         202 $self->_padname_sv->PV . ' = ';
644             } else {
645 124         285 '';
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 323 my $self = shift;
658 174 100       301 my $op_name = $self->is_null
659             ? $self->_ex_name
660             : $self->op->name;
661 174         713 $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 21 my $self = shift;
672 15 100       28 my $op_name = $self->is_null
673             ? $self->_ex_name
674             : $self->op->name;
675 15         76 $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             );
694             sub is_scopelike {
695 4498     4498 0 5662 my $self = shift;
696 4498 100       6417 my $op_name = $self->is_null
697             ? $self->_ex_name
698             : $self->op->name;
699 4498         12252 $scopelike_ops{$op_name};
700             }
701              
702             sub is_for_loop {
703 2118     2118 0 2649 my $self = shift;
704             # $self, here, is the initialization part of the for-loop, usually an sassign.
705             # The sibling is either:
706             # 1) a lineseq whose first child is a nextstate and second child is a leaveloop
707             # 2) an unstack whose sibling is a leaveloop
708 2118         3657 my $sib = $self->sibling;
709 2118 100 100     11426 return '' if !$sib or $self->isa('Devel::Chitin::OpTree::COP') or $self->is_null;
      100        
710              
711 658         1187 my $name = $sib->op->name;
712 658 50 100     2229 if ($name eq 'lineseq') {
    100          
713 0         0 my($first ,$second) = @{$sib->children};
  0         0  
714 0 0 0     0 if ($first && ! $first->is_null && $first->isa('Devel::Chitin::OpTree::COP')
      0        
      0        
      0        
      0        
715             && $second && ! $second->is_null && $second->op->name eq 'leaveloop'
716             ) {
717 0         0 return 1;
718             }
719              
720             } elsif ($name eq 'unstack' && ($sib->op->flags & B::OPf_SPECIAL)) {
721 1         3 my $sibsib = $sib->sibling;
722 1   33     8 return $sibsib && ! $sibsib->is_null && $sibsib->op->name eq 'leaveloop'
723             }
724 657         1381 return ''
725             }
726              
727             # Return true for
728             # if (conditional) { ... }
729             # and
730             # unless (conditional) { ... }
731             sub is_if_statement {
732 12     12 0 20 my $self = shift;
733 12         19 my $name = $self->op->name;
734              
735 12 50 66     68 ( $name eq 'and' or $name eq 'or' or $name eq 'cond_expr')
      33        
736             and $self->other->is_scopelike;
737             }
738              
739             sub is_posfix_if {
740 8     8 0 11 my $self = shift;
741 8         17 my $name = $self->op->name;
742              
743 8 100 66     33 ( $name eq 'and' or $name eq 'or' )
      33        
      66        
744             and $self->parent->is_null
745             and $self->parent->pre_siblings
746             and ($self->parent->pre_siblings)[-1]->class eq 'COP'
747             }
748              
749             sub _num_ops_in_for_loop {
750 1     1   2 my $self = shift;
751 1 50       4 $self->sibling->op->name eq 'unstack' ? 2 : 1;
752             }
753              
754             sub _deparse_for_loop {
755 1     1   3 my $self = shift;
756             # A for-loop is structured like this:
757             # nextstate
758             # sassign ( initialization)
759             # ...
760             # unstack
761             # leaveloop
762             # enterloop
763             # null
764             # and
765             # loop-test
766             # ...
767             # lineseq
768             # leave
769             # ... (loop body)
770             # loop-continue
771 1         3 my $init = $self->deparse;
772 1         6 my $sib = $self->sibling;
773 1 50       5 my $leaveloop = $sib->op->name eq 'unstack' ? $sib->sibling : $sib->children->[1];
774 1         4 my $and_op = $leaveloop->children->[1]->children->[0];
775 1         3 my $test_op = $and_op->children->[0];
776 1         3 my $test = $test_op->deparse;
777 1         3 my $body_op = $and_op->children->[1]->first;
778 1         4 my $cont_op = $body_op->sibling;
779 1         2 my $cont = $cont_op->deparse;
780              
781 1         5 "for ($init; $test; $cont) " . $body_op->deparse;
782             }
783              
784             # Return true if this op is the inner list on the right of
785             # \(@a) = \(@b)
786             # The optree for this construct looks like:
787             # aassign
788             # ex-list
789             # pushmark
790             # refgen
791             # ex-list <-- Return true here
792             # pushmark
793             # padav/gv
794             # ex-list
795             # pushmark
796             # ex-refgen
797             # ex-list <-- return true here
798             # pushmark
799             # lvavref
800             sub is_list_reference_alias {
801 168     168 0 253 my $self = shift;
802              
803 168   66     274 return $self->is_null
804             && $self->_ex_name eq 'pp_list'
805             && $self->parent->op->name eq 'refgen'
806             && $self->last->is_array_container;
807             }
808              
809             # Based on B::Deparse::is_miniwhile()
810             sub _deparse_postfix_while {
811 1413     1413   1868 my $self = shift;
812              
813 1413         2133 my $top = $self->children->[1];
814 1413         1760 my $condition_op;
815 1413 50 66     1945 if ($self->op->name eq 'leave'
      100        
      66        
      66        
      66        
      33        
      33        
      33        
816             and $top
817             and $top->is_null
818             and $top->class eq 'UNOP'
819             and ($condition_op = $top->first)
820             and ($condition_op->op->name eq 'and' or $condition_op->op->name eq 'or')
821             and (
822             $top->first->children->[1]->op->name eq 'lineseq'
823             or
824             ( $top->first->op->name eq 'lineseq'
825             and ! $top->first->children->[1]->is_null
826             and $top->first->children->[1]->op->name eq 'unstack'
827             )
828             )
829             ) {
830 12         17 my $type;
831 12         19 my $condition = $condition_op->first->deparse;
832 12 100       24 if ($condition_op->op->name eq 'and') {
833 6         8 $type = 'while';
834             } else {
835 6         8 $type = 'until';
836 6         28 $condition =~ s/^!//;
837             }
838 12         25 return $condition_op->children->[1]->deparse . " $type ($condition)";
839             }
840 1401         4094 return '';
841             }
842              
843             sub is_postfix_foreach {
844 1349     1349 0 1610 my $self = shift;
845 1349   100     1830 return $self->op->name eq 'leaveloop'
846             && $self->first->op->name eq 'enteriter'
847             && ! $self->first->sibling->first->first->sibling->first->isa('Devel::Chitin::OpTree::COP');
848             }
849              
850             sub is_postfix_loop {
851 1349     1349 0 1594 my $self = shift;
852 1349   100     2075 return $self->is_postfix_foreach
853             || $self->_deparse_postfix_while;
854             }
855              
856             sub _quote_sv {
857 366     366   755 my($self, $sv, %params) = @_;
858 366         835 my $string = $sv->PV;
859 366         716 $self->_quote_string($string, %params);
860             }
861              
862             sub _quote_string {
863 366     366   642 my($self, $string, %params) = @_;
864              
865             # Seems that multiconcat can have the BARE flag set erroneously? on 5.27.6
866 366 100 100     1007 my $quote = ($params{skip_quotes} or ($self->op->private & B::OPpCONST_BARE and $self->op->name ne 'multiconcat'))
867             ? ''
868             : q(');
869 366 100 100     1226 if ($string =~ m/[\000-\037]/ and !$params{regex_x_flag}) {
870 16 100       38 $quote = '"' unless $params{skip_quotes};
871 16         44 $string = $self->_escape_for_double_quotes($string, %params);
872             }
873              
874 366         1947 "${quote}${string}${quote}";
875             }
876              
877             my %control_chars = ((map { chr($_) => '\c'.chr($_ + 64) } (1 .. 26)), # \cA .. \cZ
878             "\c@" => '\c@', "\c[" => '\c[');
879             my $control_char_rx = join('|', sort keys %control_chars);
880             sub _escape_for_double_quotes {
881 16     16   36 my($self, $str, %params) = @_;
882              
883 16         33 $str =~ s/\\/\\\\/g;
884 16         21 $str =~ s/\a/\\a/g; # alarm
885 16 50       34 $str =~ s/\cH/\\b/g unless $params{in_regex}; # backspace
886 16         23 $str =~ s/\e/\\e/g; # escape
887 16         71 $str =~ s/\f/\\f/g; # form feed
888 16         41 $str =~ s/\n/\\n/g; # newline
889 16         30 $str =~ s/\r/\\r/g; # CR
890 16         23 $str =~ s/\t/\\t/g; # tab
891 16         22 $str =~ s/"/\\"/g;
892 16         140 $str =~ s/($control_char_rx)/$control_chars{$1}/ge;
  1         7  
893 16         30 $str =~ s/([[:^print:]])/sprintf('\x{%x}', ord($1))/ge;
  1         5  
894              
895 16         37 $str;
896             }
897              
898             sub _as_octal {
899 6     6   15 my($self, $val) = @_;
900 35     35   257 no warnings 'numeric';
  35         51  
  35         2972  
901 6 100       39 $val + 0 eq $val
902             ? sprintf('0%3o', $val)
903             : $val;
904             }
905              
906             # given an integer and a list of bitwise flag name/value pairs, return
907             # a string representing the flags or-ed together
908             sub _deparse_flags {
909 12     12   24 my($self, $val, $flags_listref) = @_;
910              
911 12         16 do {
912 35     35   209 no warnings 'numeric';
  35         68  
  35         7481  
913 12 100       33 unless ($val + 0 eq $val) {
914 1         4 return $val; # wasn't a number
915             }
916             };
917              
918 11         17 my @flags;
919 11         27 for (my $i = 0; $i < @$flags_listref; $i += 2) {
920 60         107 my($flag_name, $flag_value) = @$flags_listref[$i, $i+1];
921 60 100       97 if ($val & $flag_value) {
922 18         26 push @flags, $flag_name;
923 18         30 $val ^= $flag_value;
924             }
925             }
926 11 50       21 if ($val) {
927             # there were unexpected bits set
928 0         0 push @flags, $val;
929             }
930 11         32 join(' | ', @flags);
931             }
932              
933             sub _indent_block_text {
934 58     58   121 my($self, $text, %params) = @_;
935              
936 58         258 my $newlines = $text =~ s/\n/\n\t/g;
937 58 100 100     203 if ($newlines or $params{force_multiline}) {
938 25         81 "\n\t" . $text . "\n";
939             } else {
940 33         99 " $text ";
941             }
942             }
943              
944             1;
945              
946             __END__