This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
put signature ops in their own subtree.
[perl5.git] / lib / B / Deparse.pm
index 8fedf81..bf5f29e 100644 (file)
@@ -52,7 +52,7 @@ use B qw(class main_root main_start main_cv svref_2object opnumber perlstring
         MDEREF_SHIFT
     );
 
-$VERSION = '1.47';
+$VERSION = '1.50';
 use strict;
 our $AUTOLOAD;
 use warnings ();
@@ -68,7 +68,7 @@ BEGIN {
                OPpPAD_STATE PMf_SKIPWHITE RXf_SKIPWHITE
                PMf_CHARSET PMf_KEEPCOPY PMf_NOCAPTURE CVf_ANONCONST
                CVf_LOCKED OPpREVERSE_INPLACE OPpSUBSTR_REPL_FIRST
-               PMf_NONDESTRUCT OPpCONST_ARYBASE OPpEVAL_BYTES
+               PMf_NONDESTRUCT OPpEVAL_BYTES
                OPpLVREF_TYPE OPpLVREF_SV OPpLVREF_AV OPpLVREF_HV
                OPpLVREF_CV OPpLVREF_ELEM SVpad_STATE)) {
        eval { B->import($_) };
@@ -271,7 +271,7 @@ BEGIN {
 
 
 BEGIN { for (qw[ const stringify rv2sv list glob pushmark null aelem
-                kvaslice kvhslice padsv
+                kvaslice kvhslice padsv argcheck
                  nextstate dbstate rv2av rv2hv helem custom ]) {
     eval "sub OP_\U$_ () { " . opnumber($_) . "}"
 }}
@@ -791,7 +791,6 @@ sub new {
     $self->{'use_dumper'} = 0;
     $self->{'use_tabs'} = 0;
 
-    $self->{'ambient_arybase'} = 0;
     $self->{'ambient_warnings'} = undef; # Assume no lexical warnings
     $self->{'ambient_hints'} = 0;
     $self->{'ambient_hinthash'} = undef;
@@ -837,7 +836,6 @@ sub new {
 sub init {
     my $self = shift;
 
-    $self->{'arybase'}  = $self->{'ambient_arybase'};
     $self->{'warnings'} = defined ($self->{'ambient_warnings'})
                                ? $self->{'ambient_warnings'} & WARN_MASK
                                : undef;
@@ -953,7 +951,7 @@ my %strict_bits = do {
 
 sub ambient_pragmas {
     my $self = shift;
-    my ($arybase, $hint_bits, $warning_bits, $hinthash) = (0, 0);
+    my ($hint_bits, $warning_bits, $hinthash) = (0);
 
     while (@_ > 1) {
        my $name = shift();
@@ -980,14 +978,6 @@ sub ambient_pragmas {
            $hint_bits |= $strict_bits{$_} for @names;
        }
 
-       elsif ($name eq '$[') {
-           if (OPpCONST_ARYBASE) {
-               $arybase = $val;
-           } else {
-               croak "\$[ can't be non-zero on this perl" unless $val == 0;
-           }
-       }
-
        elsif ($name eq 'integer'
            || $name eq 'bytes'
            || $name eq 'utf8') {
@@ -1058,7 +1048,6 @@ sub ambient_pragmas {
        croak "The ambient_pragmas method expects an even number of args";
     }
 
-    $self->{'ambient_arybase'} = $arybase;
     $self->{'ambient_warnings'} = $warning_bits;
     $self->{'ambient_hints'} = $hint_bits;
     $self->{'ambient_hinthash'} = $hinthash;
@@ -1187,42 +1176,68 @@ sub pad_subs {
 # or altered. In this case we return "()" and fall back to general
 # deparsing of the individual sigelems as 'my $x = $_[N]' etc.
 #
-# We're only called if the first two ops are nextstate and argcheck.
+# We're only called if the top is an ex-argcheck, which is a placeholder
+# indicating a signature subtree.
+#
+# Return a signature string, or an empty list if no deparseable as a
+# signature
 
 sub deparse_argops {
-    my ($self, $firstop, $cv) = @_;
+    my ($self, $topop, $cv) = @_;
 
     my @sig;
-    my $o = $firstop;
-    return if $o->label; #first nextstate;
+
+
+    $topop = $topop->first;
+    return unless $$topop and $topop->name eq 'lineseq';
+
+
+    # last op should be nextstate
+    my $last = $topop->last;
+    return unless $$last
+                    and (   _op_is_or_was($last, OP_NEXTSTATE)
+                         or _op_is_or_was($last, OP_DBSTATE));
+
+    # first OP_NEXTSTATE
+
+    my $o = $topop->first;
+    return unless $$o;
+    return if $o->label;
 
     # OP_ARGCHECK
 
     $o = $o->sibling;
+    return unless $$o and $o->name eq 'argcheck';
+
     my ($params, $opt_params, $slurpy) = $o->aux_list($cv);
     my $mandatory = $params - $opt_params;
     my $seen_slurpy = 0;
     my $last_ix = -1;
 
-    # keep looking for valid nextstate + argelem pairs
+    # keep looking for valid nextstate + argelem pairs, terminated
+    # by a final nextstate
 
     while (1) {
-        # OP_NEXTSTATE
         $o = $o->sibling;
-        last unless $$o;
-        last unless $o->name =~ /^(next|db)state$/;
-        last if $o->label;
+        return unless $$o;
+
+        # skip trailing nextstate
+        last if $$o == $$last;
+
+        # OP_NEXTSTATE
+        return unless $o->name =~ /^(next|db)state$/;
+        return if $o->label;
 
         # OP_ARGELEM
-        my $o2 = $o->sibling;
-        last unless $$o2;
+        $o = $o->sibling;
+        last unless $$o;
 
-        if ($o2->name eq 'argelem') {
-            my $ix  = $o2->string($cv);
+        if ($o->name eq 'argelem') {
+            my $ix  = $o->string($cv);
             while (++$last_ix < $ix) {
                 push @sig, $last_ix <  $mandatory ? '$' : '$=';
             }
-            my $var = $self->padname($o2->targ);
+            my $var = $self->padname($o->targ);
             if ($var =~ /^[@%]/) {
                 return if $seen_slurpy;
                 $seen_slurpy = 1;
@@ -1232,8 +1247,8 @@ sub deparse_argops {
             else {
                 return if $ix >= $params;
             }
-            if ($o2->flags & OPf_KIDS) {
-                my $kid = $o2->first;
+            if ($o->flags & OPf_KIDS) {
+                my $kid = $o->first;
                 return unless $$kid and $kid->name eq 'argdefelem';
                 my $def = $self->deparse($kid->first, 7);
                 $def = "($def)" if $kid->first->flags & OPf_PARENS;
@@ -1241,13 +1256,13 @@ sub deparse_argops {
             }
             push @sig, $var;
         }
-        elsif ($o2->name eq 'null'
-               and ($o2->flags & OPf_KIDS)
-               and $o2->first->name eq 'argdefelem')
+        elsif ($o->name eq 'null'
+               and ($o->flags & OPf_KIDS)
+               and $o->first->name eq 'argdefelem')
         {
             # special case - a void context default expression: $ = expr
 
-            my $defop = $o2->first;
+            my $defop = $o->first;
             my $ix = $defop->targ;
             while (++$last_ix < $ix) {
                 push @sig, $last_ix <  $mandatory ? '$' : '$=';
@@ -1259,10 +1274,9 @@ sub deparse_argops {
             push @sig, '$ = ' . $def;
         }
         else {
-            last;
+            return;
         }
 
-        $o = $o2;
     }
 
     while (++$last_ix < $params) {
@@ -1270,9 +1284,10 @@ sub deparse_argops {
     }
     push @sig, $slurpy if $slurpy and !$seen_slurpy;
 
-    return ($o, join(', ', @sig));
+    return (join(', ', @sig));
 }
 
+
 # Deparse a sub. Returns everything except the 'sub foo',
 # e.g.  ($$) : method { ...; }
 # or    : prototype($$) lvalue ($a, $b) { ...; };
@@ -1315,27 +1330,26 @@ Carp::confess("SPECIAL in deparse_sub") if $cv->isa("B::SPECIAL");
        $self->pad_subs($cv);
        $self->pessimise($root, $cv->START);
        my $lineseq = $root->first;
-       if ($lineseq->name eq "lineseq") {
-           my $firstop = $lineseq->first;
-
-            if ($has_sig) {
-                my $o2;
-                # try to deparse first few ops as a signature if possible
-                if (     $$firstop
-                     and $firstop->name =~  /^(next|db)state$/
-                     and (($o2 = $firstop->sibling))
-                     and $$o2)
-                {
-                    if ($o2->name eq 'argcheck') {
-                        my ($nexto, $mysig) = $self->deparse_argops($firstop, $cv);
-                        if (defined $nexto) {
-                            $firstop = $nexto;
-                            $sig = $mysig;
-                        }
-                    }
-                }
+
+        # stub sub may have single op rather than list of ops
+        my $is_list = ($lineseq->name eq "lineseq");
+        my $firstop = $is_list ? $lineseq->first : $lineseq;
+
+        # Try to deparse first subtree as a signature if possible.
+        # Top of signature subtree has an ex-argcheck as a placeholder
+        if (    $has_sig
+            and $$firstop
+            and $firstop->name eq 'null'
+            and $firstop->targ == OP_ARGCHECK
+        ) {
+            my ($mysig) = $self->deparse_argops($firstop, $cv);
+            if (defined $mysig) {
+                $sig = $mysig;
+                $firstop = $is_list ? $firstop->sibling : undef;
             }
+        }
 
+        if ($is_list && $firstop) {
             my @ops;
            for (my $o = $firstop; $$o; $o=$o->sibling) {
                push @ops, $o;
@@ -1352,9 +1366,12 @@ Carp::confess("SPECIAL in deparse_sub") if $cv->isa("B::SPECIAL");
                $body .= ";\n$subs" if length($subs);
            }
        }
-       else {
+       elsif ($firstop) {
            $body = $self->deparse($root->first, 0);
        }
+        else {
+            $body = ';'; # stub sub
+        }
 
         my $l = '';
         if ($self->{'linenums'}) {
@@ -1575,7 +1592,7 @@ sub maybe_local {
            if $self->{'avoid_local'}{$$op};
        if ($need_parens) {
            return "$our_local($text)";
-       } elsif (want_scalar($op)) {
+       } elsif (want_scalar($op) || $our_local eq 'our') {
            return "$our_local $text";
        } else {
            return $self->maybe_parens_func("$our_local", $text, $cx, 16);
@@ -2052,11 +2069,6 @@ sub pragmata {
        $self->{'curstash'} = $stash;
     }
 
-    if (OPpCONST_ARYBASE && $self->{'arybase'} != $op->arybase) {
-       push @text, '$[ = '. $op->arybase .";\n";
-       $self->{'arybase'} = $op->arybase;
-    }
-
     my $warnings = $op->warnings;
     my $warning_bits;
     if ($warnings->isa("B::SPECIAL") && $$warnings == 4) {
@@ -4129,7 +4141,7 @@ sub pp_aelemfast_lex {
     $name =~ s/^@/\$/;
     my $i = $op->private;
     $i -= 256 if $i > 127;
-    return $name . "[" .  ($i + $self->{'arybase'}) . "]";
+    return $name . "[$i]";
 }
 
 sub pp_aelemfast {
@@ -4143,7 +4155,7 @@ sub pp_aelemfast {
     $name = $quoted ? "$name->" : '$' . $name;
     my $i = $op->private;
     $i -= 256 if $i > 127;
-    return $name . "[" .  ($i + $self->{'arybase'}) . "]";
+    return $name . "[$i]";
 }
 
 sub rv2x {
@@ -4194,11 +4206,28 @@ sub pp_rv2hv {
 sub pp_av2arylen {
     my $self = shift;
     my($op, $cx) = @_;
-    if ($op->first->name eq "padav") {
-       return $self->maybe_local($op, $cx, '$#' . $self->padany($op->first));
+    my $kid = $op->first;
+    if ($kid->name eq "padav") {
+       return $self->maybe_local($op, $cx, '$#' . $self->padany($kid));
     } else {
-       return $self->maybe_local($op, $cx,
-                                 $self->rv2x($op->first, $cx, '$#'));
+        my $kkid;
+        if (   $kid->name eq "rv2av"
+           && ($kkid = $kid->first)
+           && $kkid->name !~ /^(scope|leave|gv)$/)
+        {
+            # handle (expr)->$#* postfix form
+            my $expr;
+            $expr = $self->deparse($kkid, 24); # 24 is '->'
+            $expr = "$expr->\$#*";
+            # XXX maybe_local is probably wrong here: local($#-expression)
+            # doesn't "do" local (the is no INTRO flag set)
+            return $self->maybe_local($op, $cx, $expr);
+        }
+        else {
+            # handle $#{expr} form
+            # XXX see maybe_local comment above
+            return $self->maybe_local($op, $cx, $self->rv2x($kid, $cx, '$#'));
+        }
     }
 }
 
@@ -5439,9 +5468,6 @@ sub meth_rclass_sv {
 sub pp_const {
     my $self = shift;
     my($op, $cx) = @_;
-    if ($op->private & OPpCONST_ARYBASE) {
-        return '$[';
-    }
 #    if ($op->private & OPpCONST_BARE) { # trouble with '=>' autoquoting
 #      return $self->const_sv($op)->PV;
 #    }
@@ -5473,7 +5499,6 @@ sub dq {
     my $op = shift;
     my $type = $op->name;
     if ($type eq "const") {
-       return '$[' if $op->private & OPpCONST_ARYBASE;
        return uninterp(escape_str(unback($self->const_sv($op)->as_string)));
     } elsif ($type eq "concat") {
         return dq_disambiguate($self->dq($op->first), $self->dq($op->last));
@@ -5834,7 +5859,6 @@ sub re_dq {
 
     my $type = $op->name;
     if ($type eq "const") {
-       return '$[' if $op->private & OPpCONST_ARYBASE;
        my $unbacked = re_unback($self->const_sv($op)->as_string);
        return re_uninterp(escape_re($unbacked));
     } elsif ($type eq "concat") {