This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
B::Concise was failing to traverse some children of PMOPs
[perl5.git] / ext / B / B / Concise.pm
index 668b378..6d06956 100644 (file)
@@ -14,7 +14,7 @@ use warnings; # uses #3 and #4, since warnings uses Carp
 
 use Exporter (); # use #5
 
-our $VERSION   = "0.64";
+our $VERSION   = "0.81";
 our @ISA       = qw(Exporter);
 our @EXPORT_OK = qw( set_style set_style_standard add_callback
                     concise_subref concise_cv concise_main
@@ -28,7 +28,7 @@ our %EXPORT_TAGS =
 # use #6
 use B qw(class ppname main_start main_root main_cv cstring svref_2object
         SVf_IOK SVf_NOK SVf_POK SVf_IVisUV SVf_FAKE OPf_KIDS OPf_SPECIAL
-        CVf_ANON);
+        CVf_ANON PAD_FAKELEX_ANON PAD_FAKELEX_MULTI SVf_ROK);
 
 my %style =
   ("terse" =>
@@ -37,8 +37,8 @@ my %style =
     "(*(    )*)goto #class (#addr)\n",
     "#class pp_#name"],
    "concise" =>
-   ["#hyphseq2 (*(   (x( ;)x))*)<#classsym> "
-    . "#exname#arg(?([#targarglife])?)~#flags(?(/#private)?)(x(;~->#next)x)\n"
+   ["#hyphseq2 (*(   (x( ;)x))*)<#classsym> #exname#arg(?([#targarglife])?)"
+    . "~#flags(?(/#private)?)(?(:#hints)?)(x(;~->#next)x)\n"
     , "  (*(    )*)     goto #seq\n",
     "(?(<#seq>)?)#exname#arg(?([#targarglife])?)"],
    "linenoise" =>
@@ -49,7 +49,7 @@ my %style =
    ["#class (#addr)\n\top_next\t\t#nextaddr\n\top_sibling\t#sibaddr\n\t"
     . "op_ppaddr\tPL_ppaddr[OP_#NAME]\n\top_type\t\t#typenum\n" .
     ($] > 5.009 ? '' : "\top_seq\t\t#seqnum\n")
-    . "\top_flags\t#flagval\n\top_private\t#privval\n"
+    . "\top_flags\t#flagval\n\top_private\t#privval\t#hintsval\n"
     . "(?(\top_first\t#firstaddr\n)?)(?(\top_last\t\t#lastaddr\n)?)"
     . "(?(\top_sv\t\t#svaddr\n)?)",
     "    GOTO #addr\n",
@@ -74,6 +74,7 @@ my $big_endian = 1;   # more <sequence#> display
 my $tree_style = 0;    # tree-order details
 my $banner = 1;                # print banner before optree is traversed
 my $do_main = 0;       # force printing of main routine
+my $show_src;          # show source code
 
 # another factor: can affect all styles!
 our @callbacks;                # allow external management
@@ -132,41 +133,73 @@ sub walk_output { # updates $walkHandle
 }
 
 sub concise_subref {
-    my($order, $coderef) = @_;
+    my($order, $coderef, $name) = @_;
     my $codeobj = svref_2object($coderef);
 
-    return concise_stashref(@_)        
+    return concise_stashref(@_)
        unless ref $codeobj eq 'B::CV';
-    concise_cv_obj($order, $codeobj);
+    concise_cv_obj($order, $codeobj, $name);
 }
 
 sub concise_stashref {
     my($order, $h) = @_;
+    local *s;
     foreach my $k (sort keys %$h) {
-       local *s = $h->{$k};
+       next unless defined $h->{$k};
+       *s = $h->{$k};
        my $coderef = *s{CODE} or next;
        reset_sequence();
        print "FUNC: ", *s, "\n";
        my $codeobj = svref_2object($coderef);
        next unless ref $codeobj eq 'B::CV';
-       eval { concise_cv_obj($order, $codeobj) }
-       or warn "err $@ on $codeobj";
+       eval { concise_cv_obj($order, $codeobj, $k) };
+       warn "err $@ on $codeobj" if $@;
     }
 }
 
 # This should have been called concise_subref, but it was exported
 # under this name in versions before 0.56
-sub concise_cv { concise_subref(@_); }
+*concise_cv = \&concise_subref;
 
 sub concise_cv_obj {
-    my ($order, $cv) = @_;
+    my ($order, $cv, $name) = @_;
+    # name is either a string, or a CODE ref (copy of $cv arg??)
+
     $curcv = $cv;
-    die "err: coderef has no START\n" if class($cv->START) eq "NULL";
+
+    if (ref($cv->XSUBANY) =~ /B::(\w+)/) {
+       print $walkHandle "$name is a constant sub, optimized to a $1\n";
+       return;
+    }
+    if ($cv->XSUB) {
+       print $walkHandle "$name is XS code\n";
+       return;
+    }
+    if (class($cv->START) eq "NULL") {
+       no strict 'refs';
+       if (ref $name eq 'CODE') {
+           print $walkHandle "coderef $name has no START\n";
+       }
+       elsif (exists &$name) {
+           print $walkHandle "$name exists in stash, but has no START\n";
+       }
+       else {
+           print $walkHandle "$name not in symbol table\n";
+       }
+       return;
+    }
     sequence($cv->START);
     if ($order eq "exec") {
        walk_exec($cv->START);
-    } elsif ($order eq "basic") {
-       walk_topdown($cv->ROOT, sub { $_[0]->concise($_[1]) }, 0);
+    }
+    elsif ($order eq "basic") {
+       # walk_topdown($cv->ROOT, sub { $_[0]->concise($_[1]) }, 0);
+       my $root = $cv->ROOT;
+       unless (ref $root eq 'B::NULL') {
+           walk_topdown($root, sub { $_[0]->concise($_[1]) }, 0);
+       } else {
+           print $walkHandle "B::NULL encountered doing ROOT on $cv. avoiding disaster\n";
+       }
     } else {
        print $walkHandle tree($cv->ROOT, 0);
     }
@@ -193,14 +226,14 @@ sub concise_specials {
     my($name, $order, @cv_s) = @_;
     my $i = 1;
     if ($name eq "BEGIN") {
-       splice(@cv_s, 0, 7); # skip 7 BEGIN blocks in this file
+       splice(@cv_s, 0, 8); # skip 7 BEGIN blocks in this file. NOW 8 ??
     } elsif ($name eq "CHECK") {
        pop @cv_s; # skip the CHECK block that calls us
     }
     for my $cv (@cv_s) {
        print $walkHandle "$name $i:\n";
        $i++;
-       concise_cv_obj($order, $cv);
+       concise_cv_obj($order, $cv, $name);
     }
 }
 
@@ -214,11 +247,15 @@ my @tree_decorations =
    [" ", map("$start_sym$_$end_sym", "q", "w", "t", "x", "m"), "", 0],
   );
 
+my @render_packs; # collect -stash=<packages>
 
 sub compileOpts {
     # set rendering state from options and args
-    my @options = grep(/^-/, @_);
-    my @args = grep(!/^-/, @_);
+    my (@options,@args);
+    if (@_) {
+       @options = grep(/^-/, @_);
+       @args = grep(!/^-/, @_);
+    }
     for my $o (@options) {
        # mode/order
        if ($o eq "-basic") {
@@ -246,6 +283,7 @@ sub compileOpts {
        } elsif ($o eq "-littleendian") {
            $big_endian = 0;
        }
+       # miscellaneous, presentation
        elsif ($o eq "-nobanner") {
            $banner = 0;
        } elsif ($o eq "-banner") {
@@ -255,6 +293,25 @@ sub compileOpts {
            $do_main = 1;
        } elsif ($o eq "-nomain") {
            $do_main = 0;
+       } elsif ($o eq "-src") {
+           $show_src = 1;
+       }
+       elsif ($o =~ /^-stash=(.*)/) {
+           my $pkg = $1;
+           no strict 'refs';
+           if (! %{$pkg.'::'}) {
+               eval "require $pkg";
+           } else {
+               require Config;
+               if (!$Config::Config{usedl}
+                   && keys %{$pkg.'::'} == 1
+                   && $pkg->can('bootstrap')) {
+                   # It is something that we're staticly linked to, but hasn't
+                   # yet been used.
+                   eval "require $pkg";
+               }
+           }
+           push @render_packs, $pkg;
        }
        # line-style options
        elsif (exists $style{substr($o, 1)}) {
@@ -278,20 +335,24 @@ sub compile {
 
            if ($objname eq "BEGIN") {
                concise_specials("BEGIN", $order,
-                              B::begin_av->isa("B::AV") ?
-                              B::begin_av->ARRAY : ());
+                                B::begin_av->isa("B::AV") ?
+                                B::begin_av->ARRAY : ());
            } elsif ($objname eq "INIT") {
                concise_specials("INIT", $order,
-                              B::init_av->isa("B::AV") ?
-                              B::init_av->ARRAY : ());
+                                B::init_av->isa("B::AV") ?
+                                B::init_av->ARRAY : ());
            } elsif ($objname eq "CHECK") {
                concise_specials("CHECK", $order,
-                              B::check_av->isa("B::AV") ?
-                              B::check_av->ARRAY : ());
+                                B::check_av->isa("B::AV") ?
+                                B::check_av->ARRAY : ());
+           } elsif ($objname eq "UNITCHECK") {
+               concise_specials("UNITCHECK", $order,
+                                B::unitcheck_av->isa("B::AV") ?
+                                B::unitcheck_av->ARRAY : ());
            } elsif ($objname eq "END") {
                concise_specials("END", $order,
-                                     B::end_av->isa("B::AV") ?
-                              B::end_av->ARRAY : ());
+                                B::end_av->isa("B::AV") ?
+                                B::end_av->ARRAY : ());
            }
            else {
                # convert function names to subrefs
@@ -304,14 +365,21 @@ sub compile {
                    $objname = "main::" . $objname unless $objname =~ /::/;
                    print $walkHandle "$objname:\n";
                    no strict 'refs';
-                   die "err: unknown function ($objname)\n"
-                       unless *{$objname}{CODE};
+                   unless (exists &$objname) {
+                       print $walkHandle "err: unknown function ($objname)\n";
+                       return;
+                   }
                    $objref = \&$objname;
                }
-               concise_subref($order, $objref);
+               concise_subref($order, $objref, $objname);
            }
        }
-       if (!@args or $do_main) {
+       for my $pkg (@render_packs) {
+           no strict 'refs';
+           concise_stashref($order, \%{$pkg.'::'});
+       }
+
+       if (!@args or $do_main or @render_packs) {
            print $walkHandle "main program:\n" if $do_main;
            concise_main($order);
        }
@@ -346,7 +414,7 @@ my @linenoise =
 
 my $chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
-sub op_flags {
+sub op_flags { # common flags (see BASOP.op_flags in op.h)
     my($x) = @_;
     my(@v);
     push @v, "v" if ($x & 3) == 1;
@@ -514,27 +582,35 @@ sub fmt_line {    # generate text-line for op.
 
     $text =~ s/\#([a-zA-Z]+)/$hr->{$1}/eg;     # populate #var's
     $text =~ s/[ \t]*~+[ \t]*/ /g;             # squeeze tildes
+
+    $text = "# $hr->{src}\n$text" if $show_src and $hr->{src};
+
     chomp $text;
     return "$text\n" if $text ne "";
     return $text; # suppress empty lines
 }
 
-my %priv;
+our %priv; # used to display each opcode's BASEOP.op_private values
+
 $priv{$_}{128} = "LVINTRO"
   for ("pos", "substr", "vec", "threadsv", "gvsv", "rv2sv", "rv2hv", "rv2gv",
        "rv2av", "rv2arylen", "aelem", "helem", "aslice", "hslice", "padsv",
        "padav", "padhv", "enteriter");
 $priv{$_}{64} = "REFC" for ("leave", "leavesub", "leavesublv", "leavewrite");
 $priv{"aassign"}{64} = "COMMON";
-$priv{"aassign"}{32} = "PHASH" if $] < 5.009;
+$priv{"aassign"}{32} = $] < 5.009 ? "PHASH" : "STATE";
+$priv{"sassign"}{32} = "STATE";
 $priv{"sassign"}{64} = "BKWARD";
-$priv{$_}{64} = "RTIME" for ("match", "subst", "substcont");
+$priv{$_}{64} = "RTIME" for ("match", "subst", "substcont", "qr");
 @{$priv{"trans"}}{1,2,4,8,16,64} = ("<UTF", ">UTF", "IDENT", "SQUASH", "DEL",
                                    "COMPL", "GROWS");
+$priv{transr} = $priv{trans};
 $priv{"repeat"}{64} = "DOLIST";
 $priv{"leaveloop"}{64} = "CONT";
+$priv{$_}{4} = "DREFed" for (qw(rv2sv rv2av rv2hv));
 @{$priv{$_}}{32,64,96} = ("DREFAV", "DREFHV", "DREFSV")
   for (qw(rv2gv rv2sv padsv aelem helem));
+$priv{$_}{16} = "STATE" for ("padav", "padhv", "padsv");
 @{$priv{"entersub"}}{16,32,64} = ("DBG","TARG","NOMOD");
 @{$priv{$_}}{4,8,128} = ("INARGS","AMPER","NO()") for ("entersub", "rv2cv");
 $priv{"gv"}{32} = "EARLYCV";
@@ -559,16 +635,15 @@ $priv{"flip"}{64} = $priv{"flop"}{64} = "LINENUM";
 $priv{"list"}{64} = "GUESSED";
 $priv{"delete"}{64} = "SLICE";
 $priv{"exists"}{64} = "SUB";
-$priv{$_}{64} = "LOCALE"
-  for ("sort", "prtf", "sprintf", "slt", "sle", "seq", "sne", "sgt", "sge",
-       "scmp", "lc", "uc", "lcfirst", "ucfirst");
-@{$priv{"sort"}}{1,2,4,8,16} = ("NUM", "INT", "REV", "INPLACE","DESC");
+@{$priv{"sort"}}{1,2,4,8,16,32,64} = ("NUM", "INT", "REV", "INPLACE","DESC","QSORT","STABLE");
+$priv{"reverse"}{8} = "INPLACE";
 $priv{"threadsv"}{64} = "SVREFd";
 @{$priv{$_}}{16,32,64,128} = ("INBIN","INCR","OUTBIN","OUTCR")
   for ("open", "backtick");
 $priv{"exit"}{128} = "VMS";
 $priv{$_}{2} = "FTACCESS"
   for ("ftrread", "ftrwrite", "ftrexec", "fteread", "ftewrite", "fteexec");
+$priv{"entereval"}{2} = "HAS_HH";
 if ($] >= 5.009) {
   # Stacked filetests are post 5.8.x
   $priv{$_}{4} = "FTSTACKED"
@@ -582,29 +657,54 @@ if ($] >= 5.009) {
     for ("mapwhile", "mapstart", "grepwhile", "grepstart");
 }
 
-sub private_flags {
-    my($name, $x) = @_;
+our %hints; # used to display each COP's op_hints values
+
+# strict refs, subs, vars
+@hints{2,512,1024} = ('$', '&', '*');
+# integers, locale, bytes, arybase
+@hints{1,4,8,16,32} = ('i', 'l', 'b', '[');
+# block scope, localise %^H, $^OPEN (in), $^OPEN (out)
+@hints{256,131072,262144,524288} = ('{','%','<','>');
+# overload new integer, float, binary, string, re
+@hints{4096,8192,16384,32768,65536} = ('I', 'F', 'B', 'S', 'R');
+# taint and eval
+@hints{1048576,2097152} = ('T', 'E');
+# filetest access, UTF-8
+@hints{4194304,8388608} = ('X', 'U');
+
+sub _flags {
+    my($hash, $x) = @_;
     my @s;
-    for my $flag (128, 96, 64, 32, 16, 8, 4, 2, 1) {
-       if ($priv{$name}{$flag} and $x & $flag and $x >= $flag) {
+    for my $flag (sort {$b <=> $a} keys %$hash) {
+       if ($hash->{$flag} and $x & $flag and $x >= $flag) {
            $x -= $flag;
-           push @s, $priv{$name}{$flag};
+           push @s, $hash->{$flag};
        }
     }
     push @s, $x if $x;
     return join(",", @s);
 }
 
+sub private_flags {
+    my($name, $x) = @_;
+    _flags($priv{$name}, $x);
+}
+
+sub hints_flags {
+    my($x) = @_;
+    _flags(\%hints, $x);
+}
+
 sub concise_sv {
-    my($sv, $hr) = @_;
+    my($sv, $hr, $preferpv) = @_;
     $hr->{svclass} = class($sv);
     $hr->{svclass} = "UV"
       if $hr->{svclass} eq "IV" and $sv->FLAGS & SVf_IVisUV;
+    Carp::cluck("bad concise_sv: $sv") unless $sv and $$sv;
     $hr->{svaddr} = sprintf("%#x", $$sv);
-    if ($hr->{svclass} eq "GV") {
+    if ($hr->{svclass} eq "GV" && $sv->isGV_with_GP()) {
        my $gv = $sv;
-       my $stash = $gv->STASH->NAME;
-       if ($stash eq "main") {
+       my $stash = $gv->STASH->NAME; if ($stash eq "main") {
            $stash = "";
        } else {
            $stash = $stash . "::";
@@ -612,12 +712,21 @@ sub concise_sv {
        $hr->{svval} = "*$stash" . $gv->SAFENAME;
        return "*$stash" . $gv->SAFENAME;
     } else {
-       while (class($sv) eq "RV") {
-           $hr->{svval} .= "\\";
-           $sv = $sv->RV;
+       if ($] >= 5.011) {
+           while (class($sv) eq "IV" && $sv->FLAGS & SVf_ROK) {
+               $hr->{svval} .= "\\";
+               $sv = $sv->RV;
+           }
+       } else {
+           while (class($sv) eq "RV") {
+               $hr->{svval} .= "\\";
+               $sv = $sv->RV;
+           }
        }
        if (class($sv) eq "SPECIAL") {
            $hr->{svval} .= ["Null", "sv_undef", "sv_yes", "sv_no"]->[$$sv];
+       } elsif ($preferpv && $sv->FLAGS & SVf_POK) {
+           $hr->{svval} .= cstring($sv->PV);
        } elsif ($sv->FLAGS & SVf_NOK) {
            $hr->{svval} .= $sv->NV;
        } elsif ($sv->FLAGS & SVf_IOK) {
@@ -634,6 +743,23 @@ sub concise_sv {
     }
 }
 
+my %srclines;
+
+sub fill_srclines {
+    my $fullnm = shift;
+    if ($fullnm eq '-e') {
+       $srclines{$fullnm} = [ $fullnm, "-src not supported for -e" ];
+       return;
+    }
+    open (my $fh, '<', $fullnm)
+       or warn "# $fullnm: $!, (chdirs not supported by this feature yet)\n"
+       and return;
+    my @l = <$fh>;
+    chomp @l;
+    unshift @l, $fullnm; # like @{_<$fullnm} in debug, array starts at 1
+    $srclines{$fullnm} = \@l;
+}
+
 sub concise_op {
     my ($op, $level, $format) = @_;
     my %h;
@@ -663,15 +789,18 @@ sub concise_op {
                    # These changes relate to the jumbo closure fix.
                    # See changes 19939 and 20005
                    my $fake = '';
-                   $fake .= 'a' if $padname->IVX & 1; # PAD_FAKELEX_ANON
-                   $fake .= 'm' if $padname->IVX & 2; # PAD_FAKELEX_MULTI
-                   $fake .= ':' . $padname->NVX if $curcv->CvFLAGS & CVf_ANON;
+                   $fake .= 'a'
+                       if $padname->PARENT_FAKELEX_FLAGS & PAD_FAKELEX_ANON;
+                   $fake .= 'm'
+                       if $padname->PARENT_FAKELEX_FLAGS & PAD_FAKELEX_MULTI;
+                   $fake .= ':' . $padname->PARENT_PAD_INDEX
+                       if $curcv->CvFLAGS & CVf_ANON;
                    $h{targarglife} = "$h{targarg}:FAKE:$fake";
                }
            }
            else {
-               my $intro = $padname->NVX - $cop_seq_base;
-               my $finish = int($padname->IVX) - $cop_seq_base;
+               my $intro = $padname->COP_SEQ_RANGE_LOW - $cop_seq_base;
+               my $finish = int($padname->COP_SEQ_RANGE_HIGH) - $cop_seq_base;
                $finish = "end" if $finish == 999999999 - $cop_seq_base;
                $h{targarglife} = "$h{targarg}:$intro,$finish";
            }
@@ -708,7 +837,7 @@ sub concise_op {
        } else {
            $h{arg} = "($precomp)";
        }
-    } elsif ($h{class} eq "PVOP" and $h{name} ne "trans") {
+    } elsif ($h{class} eq "PVOP" and $h{name} !~ '^transr?\z') {
        $h{arg} = '("' . $op->pv . '")';
        $h{svval} = '"' . $op->pv . '"';
     } elsif ($h{class} eq "COP") {
@@ -716,37 +845,46 @@ sub concise_op {
        $h{coplabel} = $label;
        $label = $label ? "$label: " : "";
        my $loc = $op->file;
+       my $pathnm = $loc;
        $loc =~ s[.*/][];
-       $loc .= ":" . $op->line;
+       my $ln = $op->line;
+       $loc .= ":$ln";
        my($stash, $cseq) = ($op->stash->NAME, $op->cop_seq - $cop_seq_base);
        my $arybase = $op->arybase;
        $arybase = $arybase ? ' $[=' . $arybase : "";
        $h{arg} = "($label$stash $cseq $loc$arybase)";
+       if ($show_src) {
+           fill_srclines($pathnm) unless exists $srclines{$pathnm};
+           # Would love to retain Jim's use of // but this code needs to be
+           # portable to 5.8.x
+           my $line = $srclines{$pathnm}[$ln];
+           $line = "-src unavailable under -e" unless defined $line;
+           $h{src} = "$ln: $line";
+       }
     } elsif ($h{class} eq "LOOP") {
        $h{arg} = "(next->" . seq($op->nextop) . " last->" . seq($op->lastop)
          . " redo->" . seq($op->redoop) . ")";
     } elsif ($h{class} eq "LOGOP") {
        undef $lastnext;
        $h{arg} = "(other->" . seq($op->other) . ")";
-    } elsif ($h{class} eq "SVOP") {
+    }
+    elsif ($h{class} eq "SVOP" or $h{class} eq "PADOP") {
        unless ($h{name} eq 'aelemfast' and $op->flags & OPf_SPECIAL) {
-           if (! ${$op->sv}) {
-               my $sv = (($curcv->PADLIST->ARRAY)[1]->ARRAY)[$op->targ];
-               $h{arg} = "[" . concise_sv($sv, \%h) . "]";
+           my $idx = ($h{class} eq "SVOP") ? $op->targ : $op->padix;
+           my $preferpv = $h{name} eq "method_named";
+           if ($h{class} eq "PADOP" or !${$op->sv}) {
+               my $sv = (($curcv->PADLIST->ARRAY)[1]->ARRAY)[$idx];
+               $h{arg} = "[" . concise_sv($sv, \%h, $preferpv) . "]";
                $h{targarglife} = $h{targarg} = "";
            } else {
-               $h{arg} = "(" . concise_sv($op->sv, \%h) . ")";
+               $h{arg} = "(" . concise_sv($op->sv, \%h, $preferpv) . ")";
            }
        }
-    } elsif ($h{class} eq "PADOP") {
-       my $sv = (($curcv->PADLIST->ARRAY)[1]->ARRAY)[$op->padix];
-       $h{arg} = "[" . concise_sv($sv, \%h) . "]";
     }
     $h{seq} = $h{hyphseq} = seq($op);
     $h{seq} = "" if $h{seq} eq "-";
     if ($] > 5.009) {
        $h{opt} = $op->opt;
-       $h{static} = $op->static;
        $h{label} = $labels{$$op};
     } else {
        $h{seqnum} = $op->seq;
@@ -764,6 +902,12 @@ sub concise_op {
     $h{flags} = op_flags($op->flags);
     $h{privval} = $op->private;
     $h{private} = private_flags($h{name}, $op->private);
+    if ($op->can("hints")) {
+      $h{hintsval} = $op->hints;
+      $h{hints} = hints_flags($h{hintsval});
+    } else {
+      $h{hintsval} = $h{hints} = '';
+    }
     $h{addr} = sprintf("%#x", $$op);
     $h{typenum} = $op->type;
     $h{noise} = $linenoise[$op->type];
@@ -907,56 +1051,82 @@ tree in one of several space-efficient text formats suitable for debugging
 the inner workings of perl or other compiler backends. It can print OPs in
 the order they appear in the OP tree, in the order they will execute, or
 in a text approximation to their tree structure, and the format of the
-information displyed is customizable. Its function is similar to that of
+information displayed is customizable. Its function is similar to that of
 perl's B<-Dx> debugging flag or the B<B::Terse> module, but it is more
 sophisticated and flexible.
 
 =head1 EXAMPLE
 
-Here's is a short example of output (aka 'rendering'), using the
-default formatting conventions :
+Here's two outputs (or 'renderings'), using the -exec and -basic
+(i.e. default) formatting conventions on the same code snippet.
+
+    % perl -MO=Concise,-exec -e '$a = $b + 42'
+    1  <0> enter
+    2  <;> nextstate(main 1 -e:1) v
+    3  <#> gvsv[*b] s
+    4  <$> const[IV 42] s
+ *  5  <2> add[t3] sK/2
+    6  <#> gvsv[*a] s
+    7  <2> sassign vKS/2
+    8  <@> leave[1 ref] vKP/REFC
+
+In this -exec rendering, each opcode is executed in the order shown.
+The add opcode, marked with '*', is discussed in more detail.
+
+The 1st column is the op's sequence number, starting at 1, and is
+displayed in base 36 by default.  Here they're purely linear; the
+sequences are very helpful when looking at code with loops and
+branches.
+
+The symbol between angle brackets indicates the op's type, for
+example; <2> is a BINOP, <@> a LISTOP, and <#> is a PADOP, which is
+used in threaded perls. (see L</"OP class abbreviations">).
+
+The opname, as in B<'add[t1]'>, may be followed by op-specific
+information in parentheses or brackets (ex B<'[t1]'>).
+
+The op-flags (ex B<'sK/2'>) are described in (L</"OP flags
+abbreviations">).
 
     % perl -MO=Concise -e '$a = $b + 42'
     8  <@> leave[1 ref] vKP/REFC ->(end)
     1     <0> enter ->2
     2     <;> nextstate(main 1 -e:1) v ->3
     7     <2> sassign vKS/2 ->8
   5        <2> add[t1] sK/2 ->6
*  5        <2> add[t1] sK/2 ->6
     -           <1> ex-rv2sv sK/1 ->4
     3              <$> gvsv(*b) s ->4
     4           <$> const(IV 42) s ->5
     -        <1> ex-rv2sv sKRM*/1 ->7
     6           <$> gvsv(*a) s ->7
 
-Each line corresponds to an opcode. Null ops appear as C<ex-opname>,
-where I<opname> is the op that has been optimized away by perl.
-
-The number on the first row indicates the op's sequence number. It's
-given in base 36 by default.
+The default rendering is top-down, so they're not in execution order.
+This form reflects the way the stack is used to parse and evaluate
+expressions; the add operates on the two terms below it in the tree.
 
-The symbol between angle brackets indicates the op's type : for example,
-<2> is a BINOP, <@> a LISTOP, etc. (see L</"OP class abbreviations">).
+Nullops appear as C<ex-opname>, where I<opname> is an op that has been
+optimized away by perl.  They're displayed with a sequence-number of
+'-', because they are not executed (they don't appear in previous
+example), they're printed here because they reflect the parse.
 
-The opname may be followed by op-specific information in parentheses
-(e.g. C<gvsv(*b)>), and by targ information in brackets (e.g.
-C<leave[t1]>).
+The arrow points to the sequence number of the next op; they're not
+displayed in -exec mode, for obvious reasons.
 
-Next come the op flags. The common flags are listed below
-(L</"OP flags abbreviations">). The private flags follow, separated
-by a slash. For example, C<vKP/REFC> means that the leave op has
-public flags OPf_WANT_VOID, OPf_KIDS, and OPf_PARENS, and the private
-flag OPpREFCOUNTED.
+Note that because this rendering was done on a non-threaded perl, the
+PADOPs in the previous examples are now SVOPs, and some (but not all)
+of the square brackets have been replaced by round ones.  This is a
+subtle feature to provide some visual distinction between renderings
+on threaded and un-threaded perls.
 
-Finally an arrow points to the sequence number of the next op.
 
 =head1 OPTIONS
 
 Arguments that don't start with a hyphen are taken to be the names of
-subroutines to print the OPs of; if no such functions are specified,
-the main body of the program (outside any subroutines, and not
-including use'd or require'd files) is printed. Passing C<BEGIN>,
+subroutines to render; if no such functions are specified, the main
+body of the program (outside any subroutines, and not including use'd
+or require'd files) is rendered.  Passing C<BEGIN>, C<UNITCHECK>,
 C<CHECK>, C<INIT>, or C<END> will cause all of the corresponding
-special blocks to be printed.
+special blocks to be printed.  Arguments must follow options.
 
 Options affect how things are rendered (ie printed).  They're presented
 here by their visual effect, 1st being strongest.  They're grouped
@@ -974,8 +1144,9 @@ These options control the 'vertical display' of opcodes.  The display
 
 Print OPs in the order they appear in the OP tree (a preorder
 traversal, starting at the root). The indentation of each OP shows its
-level in the tree.  This mode is the default, so the flag is included
-simply for completeness.
+level in the tree, and the '->' at the end of the line indicates the
+next opcode in execution order.  This mode is the default, so the flag
+is included simply for completeness.
 
 =item B<-exec>
 
@@ -1091,7 +1262,42 @@ obviously mutually exclusive with bigendian.
 
 =head2 Other options
 
-These are pairwise exclusive.
+=over 4
+
+=item B<-src>
+
+With this option, the rendering of each statement (starting with the
+nextstate OP) will be preceded by the 1st line of source code that
+generates it.  For example:
+
+    1  <0> enter
+    # 1: my $i;
+    2  <;> nextstate(main 1 junk.pl:1) v:{
+    3  <0> padsv[$i:1,10] vM/LVINTRO
+    # 3: for $i (0..9) {
+    4  <;> nextstate(main 3 junk.pl:3) v:{
+    5  <0> pushmark s
+    6  <$> const[IV 0] s
+    7  <$> const[IV 9] s
+    8  <{> enteriter(next->j last->m redo->9)[$i:1,10] lKS
+    k  <0> iter s
+    l  <|> and(other->9) vK/1
+    # 4:     print "line ";
+    9      <;> nextstate(main 2 junk.pl:4) v
+    a      <0> pushmark s
+    b      <$> const[PV "line "] s
+    c      <@> print vK
+    # 5:     print "$i\n";
+    ...
+
+=item B<-stash="somepackage">
+
+With this, "somepackage" will be required, then the stash is
+inspected, and each function is rendered.
+
+=back
+
+The following options are pairwise exclusive.
 
 =over 4
 
@@ -1162,12 +1368,14 @@ B:: namespace that represents the ops in your Perl code.
 
 =head2 OP flags abbreviations
 
-These symbols represent various flags which alter behavior of the
-opcode, sometimes in opcode-specific ways.
+OP flags are either public or private.  The public flags alter the
+behavior of each opcode in consistent ways, and are represented by 0
+or more single characters.
 
     v      OPf_WANT_VOID    Want nothing (void context)
     s      OPf_WANT_SCALAR  Want single value (scalar context)
     l      OPf_WANT_LIST    Want list of any length (list context)
+                            Want is unknown
     K      OPf_KIDS         There is a firstborn child.
     P      OPf_PARENS       This operator was parenthesized.
                              (Or block needs explicit scope entry.)
@@ -1177,6 +1385,18 @@ opcode, sometimes in opcode-specific ways.
     S      OPf_STACKED      Some arg is arriving on the stack.
     *      OPf_SPECIAL      Do something weird for this op (see op.h)
 
+Private flags, if any are set for an opcode, are displayed after a '/'
+
+    8  <@> leave[1 ref] vKP/REFC ->(end)
+    7     <2> sassign vKS/2 ->8
+
+They're opcode specific, and occur less often than the public ones, so
+they're represented by short mnemonics instead of single-chars; see
+F<op.h> for gory details, or try this quick 2-liner:
+
+  $> perl -MB::Concise -de 1
+  DB<1> |x \%B::Concise::priv
+
 =head1 FORMATTING SPECIFICATIONS
 
 For each line-style ('concise', 'terse', 'linenoise', etc.) there are
@@ -1268,7 +1488,7 @@ rendering of each opcode.
 Only some of these are used by the standard styles, the others are
 provided for you to delve into optree mechanics, should you wish to
 add a new style (see L</add_style> below) that uses them.  You can
-also add new ones using L<add_callback>.
+also add new ones using L</add_callback>.
 
 =over 4
 
@@ -1303,7 +1523,7 @@ The target of the OP, or nothing for a nulled OP.
 
 =item B<#firstaddr>
 
-The address of the OP's first child, in hexidecimal.
+The address of the OP's first child, in hexadecimal.
 
 =item B<#flags>
 
@@ -1313,6 +1533,37 @@ The OP's flags, abbreviated as a series of symbols.
 
 The numeric value of the OP's flags.
 
+=item B<#hints>
+
+The COP's hint flags, rendered with abbreviated names if possible. An empty
+string if this is not a COP. Here are the symbols used:
+
+    $ strict refs
+    & strict subs
+    * strict vars
+    i integers
+    l locale
+    b bytes
+    [ arybase
+    { block scope
+    % localise %^H
+    < open in
+    > open out
+    I overload int
+    F overload float
+    B overload binary
+    S overload string
+    R overload re
+    T taint
+    E eval
+    X filetest access
+    U utf-8
+
+=item B<#hintsval>
+
+The numeric value of the COP's hint flags, or an empty string if this is not
+a COP.
+
 =item B<#hyphseq>
 
 The sequence number of the OP, or a hyphen if it doesn't have one.
@@ -1324,7 +1575,7 @@ mode, or empty otherwise.
 
 =item B<#lastaddr>
 
-The address of the OP's last child, in hexidecimal.
+The address of the OP's last child, in hexadecimal.
 
 =item B<#name>
 
@@ -1340,7 +1591,7 @@ The sequence number of the OP's next OP.
 
 =item B<#nextaddr>
 
-The address of the OP's next OP, in hexidecimal.
+The address of the OP's next OP, in hexadecimal.
 
 =item B<#noise>
 
@@ -1374,20 +1625,13 @@ Whether or not the op has been optimised by the peephole optimiser.
 
 Only available in 5.9 and later.
 
-=item B<#static>
-
-Whether or not the op is statically defined.  This flag is used by the
-B::C compiler backend and indicates that the op should not be freed.
-
-Only available in 5.9 and later.
-
 =item B<#sibaddr>
 
-The address of the OP's next youngest sibling, in hexidecimal.
+The address of the OP's next youngest sibling, in hexadecimal.
 
 =item B<#svaddr>
 
-The address of the OP's SV, if it has an SV, in hexidecimal.
+The address of the OP's SV, if it has an SV, in hexadecimal.
 
 =item B<#svclass>
 
@@ -1418,6 +1662,47 @@ The numeric value of the OP's type, in decimal.
 
 =back
 
+=head1 One-Liner Command tips
+
+=over 4
+
+=item perl -MO=Concise,bar foo.pl
+
+Renders only bar() from foo.pl.  To see main, drop the ',bar'.  To see
+both, add ',-main'
+
+=item perl -MDigest::MD5=md5 -MO=Concise,md5 -e1
+
+Identifies md5 as an XS function.  The export is needed so that BC can
+find it in main.
+
+=item perl -MPOSIX -MO=Concise,_POSIX_ARG_MAX -e1
+
+Identifies _POSIX_ARG_MAX as a constant sub, optimized to an IV.
+Although POSIX isn't entirely consistent across platforms, this is
+likely to be present in virtually all of them.
+
+=item perl -MPOSIX -MO=Concise,a -e 'print _POSIX_SAVED_IDS'
+
+This renders a print statement, which includes a call to the function.
+It's identical to rendering a file with a use call and that single
+statement, except for the filename which appears in the nextstate ops.
+
+=item perl -MPOSIX -MO=Concise,a -e 'sub a{_POSIX_SAVED_IDS}'
+
+This is B<very> similar to previous, only the first two ops differ.  This
+subroutine rendering is more representative, insofar as a single main
+program will have many subs.
+
+=item perl -MB::Concise -e 'B::Concise::compile("-exec","-src", \%B::Concise::)->()'
+
+This renders all functions in the B::Concise package with the source
+lines.  It eschews the O framework so that the stashref can be passed
+directly to B::Concise::compile().  See -stash option for a more
+convenient way to render a package.
+
+=back
+
 =head1 Using B::Concise outside of the O framework
 
 The common (and original) usage of B::Concise was for command-line
@@ -1459,18 +1744,19 @@ This restores one of the standard line-styles: C<terse>, C<concise>,
 C<linenoise>, C<debug>, C<env>, into effect.  It also accepts style
 names previously defined with add_style().
 
-=head2 add_style()
+=head2 add_style ()
 
 This subroutine accepts a new style name and three style arguments as
 above, and creates, registers, and selects the newly named style.  It is
 an error to re-add a style; call set_style_standard() to switch between
 several styles.
 
-=head2 add_callback()
+=head2 add_callback ()
 
-If your newly minted styles refer to any #variables, you'll need to
-define a callback subroutine that will populate (or modify) those
-variables.  They are then available for use in the style you've chosen.
+If your newly minted styles refer to any new #variables, you'll need
+to define a callback subroutine that will populate (or modify) those
+variables.  They are then available for use in the style you've
+chosen.
 
 The callbacks are called for each opcode visited by Concise, in the
 same order as they are added.  Each subroutine is passed five
@@ -1499,8 +1785,8 @@ STDOUT.  You can reuse this, and can change the rendering style used
 each time; thereafter the coderef renders in the new style.
 
 B<walk_output> lets you change the print destination from STDOUT to
-another open filehandle, or (unless you've built with -Uuseperlio)
-into a string passed as a ref.
+another open filehandle, or into a string passed as a ref (unless
+you've built perl with -Uuseperlio).
 
     my $walker = B::Concise::compile('-terse','aFuncName', \&aSubRef);  # 1
     walk_output(\my $buf);
@@ -1537,12 +1823,13 @@ the output.
 
 =head2 Errors
 
-All detected errors, (invalid arguments, internal errors, etc.) are
-resolved with a die($message). Use an eval if you wish to catch these
-errors and continue processing.
+Errors in rendering (non-existent function-name, non-existent coderef)
+are written to the STDOUT, or wherever you've set it via
+walk_output().
 
-In particular, B<compile> will die if you've asked for a non-existent
-function-name, a non-existent coderef, or a non-CODE reference.
+Errors using the various *style* calls, and bad args to walk_output(),
+result in die().  Use an eval if you wish to catch these errors and
+continue processing.
 
 =head1 AUTHOR