📄 deparse.pm
字号:
# B::Deparse.pm# Copyright (c) 1998-2000, 2002, 2003, 2004, 2005, 2006 Stephen McCamant.# All rights reserved.# This module is free software; you can redistribute and/or modify# it under the same terms as Perl itself.# This is based on the module of the same name by Malcolm Beattie,# but essentially none of his code remains.package B::Deparse;use Carp;use B qw(class main_root main_start main_cv svref_2object opnumber perlstring OPf_WANT OPf_WANT_VOID OPf_WANT_SCALAR OPf_WANT_LIST OPf_KIDS OPf_REF OPf_STACKED OPf_SPECIAL OPf_MOD OPpPAD_STATE OPpLVAL_INTRO OPpOUR_INTRO OPpENTERSUB_AMPER OPpSLICE OPpCONST_BARE OPpTRANS_SQUASH OPpTRANS_DELETE OPpTRANS_COMPLEMENT OPpTARGET_MY OPpCONST_ARYBASE OPpEXISTS_SUB OPpSORT_NUMERIC OPpSORT_INTEGER OPpSORT_REVERSE OPpSORT_INPLACE OPpSORT_DESCEND OPpITER_REVERSED SVf_IOK SVf_NOK SVf_ROK SVf_POK SVpad_OUR SVf_FAKE SVs_RMG SVs_SMG CVf_METHOD CVf_LOCKED CVf_LVALUE PMf_KEEP PMf_GLOBAL PMf_CONTINUE PMf_EVAL PMf_ONCE PMf_MULTILINE PMf_SINGLELINE PMf_FOLD PMf_EXTENDED), ($] < 5.009 ? 'PMf_SKIPWHITE' : 'RXf_SKIPWHITE');$VERSION = 0.83;use strict;use vars qw/$AUTOLOAD/;use warnings ();# Changes between 0.50 and 0.51:# - fixed nulled leave with live enter in sort { }# - fixed reference constants (\"str")# - handle empty programs gracefully# - handle infinte loops (for (;;) {}, while (1) {})# - differentiate between `for my $x ...' and `my $x; for $x ...'# - various minor cleanups# - moved globals into an object# - added `-u', like B::C# - package declarations using cop_stash# - subs, formats and code sorted by cop_seq# Changes between 0.51 and 0.52:# - added pp_threadsv (special variables under USE_5005THREADS)# - added documentation# Changes between 0.52 and 0.53:# - many changes adding precedence contexts and associativity# - added `-p' and `-s' output style options# - various other minor fixes# Changes between 0.53 and 0.54:# - added support for new `for (1..100)' optimization,# thanks to Gisle Aas# Changes between 0.54 and 0.55:# - added support for new qr// construct# - added support for new pp_regcreset OP# Changes between 0.55 and 0.56:# - tested on base/*.t, cmd/*.t, comp/*.t, io/*.t# - fixed $# on non-lexicals broken in last big rewrite# - added temporary fix for change in opcode of OP_STRINGIFY# - fixed problem in 0.54's for() patch in `for (@ary)'# - fixed precedence in conditional of ?:# - tweaked list paren elimination in `my($x) = @_'# - made continue-block detection trickier wrt. null ops# - fixed various prototype problems in pp_entersub# - added support for sub prototypes that never get GVs# - added unquoting for special filehandle first arg in truncate# - print doubled rv2gv (a bug) as `*{*GV}' instead of illegal `**GV'# - added semicolons at the ends of blocks# - added -l `#line' declaration option -- fixes cmd/subval.t 27,28# Changes between 0.56 and 0.561:# - fixed multiply-declared my var in pp_truncate (thanks to Sarathy)# - used new B.pm symbolic constants (done by Nick Ing-Simmons)# Changes between 0.561 and 0.57:# - stylistic changes to symbolic constant stuff# - handled scope in s///e replacement code# - added unquote option for expanding "" into concats, etc.# - split method and proto parts of pp_entersub into separate functions# - various minor cleanups# Changes after 0.57:# - added parens in \&foo (patch by Albert Dvornik)# Changes between 0.57 and 0.58:# - fixed `0' statements that weren't being printed# - added methods for use from other programs# (based on patches from James Duncan and Hugo van der Sanden)# - added -si and -sT to control indenting (also based on a patch from Hugo)# - added -sv to print something else instead of '???'# - preliminary version of utf8 tr/// handling# Changes after 0.58:# - uses of $op->ppaddr changed to new $op->name (done by Sarathy)# - added support for Hugo's new OP_SETSTATE (like nextstate)# Changes between 0.58 and 0.59# - added support for Chip's OP_METHOD_NAMED# - added support for Ilya's OPpTARGET_MY optimization# - elided arrows before `()' subscripts when possible# Changes between 0.59 and 0.60# - support for method attribues was added# - some warnings fixed# - separate recognition of constant subs# - rewrote continue block handling, now recoginizing for loops# - added more control of expanding control structures# Changes between 0.60 and 0.61 (mostly by Robin Houston)# - many bug-fixes# - support for pragmas and 'use'# - support for the little-used $[ variable# - support for __DATA__ sections# - UTF8 support# - BEGIN, CHECK, INIT and END blocks# - scoping of subroutine declarations fixed# - compile-time output from the input program can be suppressed, so that the# output is just the deparsed code. (a change to O.pm in fact)# - our() declarations# - *all* the known bugs are now listed in the BUGS section# - comprehensive test mechanism (TEST -deparse)# Changes between 0.62 and 0.63 (mostly by Rafael Garcia-Suarez)# - bug-fixes# - new switch -P# - support for command-line switches (-l, -0, etc.)# Changes between 0.63 and 0.64# - support for //, CHECK blocks, and assertions# - improved handling of foreach loops and lexicals# - option to use Data::Dumper for constants# - more bug fixes# - discovered lots more bugs not yet fixed## ...## Changes between 0.72 and 0.73# - support new switch constructs# Todo:# (See also BUGS section at the end of this file)## - finish tr/// changes# - add option for even more parens (generalize \&foo change)# - left/right context# - copy comments (look at real text with $^P?)# - avoid semis in one-statement blocks# - associativity of &&=, ||=, ?:# - ',' => '=>' (auto-unquote?)# - break long lines ("\r" as discretionary break?)# - configurable syntax highlighting: ANSI color, HTML, TeX, etc.# - more style options: brace style, hex vs. octal, quotes, ...# - print big ints as hex/octal instead of decimal (heuristic?)# - handle `my $x if 0'?# - version using op_next instead of op_first/sibling?# - avoid string copies (pass arrays, one big join?)# - here-docs?# Current test.deparse failures# comp/hints 6 - location of BEGIN blocks wrt. block openings# run/switchI 1 - missing -I switches entirely# perl -Ifoo -e 'print @INC'# op/caller 2 - warning mask propagates backwards before warnings::register# 'use warnings; BEGIN {${^WARNING_BITS} eq "U"x12;} use warnings::register'# op/getpid 2 - can't assign to shared my() declaration (threads only)# 'my $x : shared = 5'# op/override 7 - parens on overriden require change v-string interpretation# 'BEGIN{*CORE::GLOBAL::require=sub {}} require v5.6'# c.f. 'BEGIN { *f = sub {0} }; f 2'# op/pat 774 - losing Unicode-ness of Latin1-only strings# 'use charnames ":short"; $x="\N{latin:a with acute}"'# op/recurse 12 - missing parens on recursive call makes it look like method# 'sub f { f($x) }'# op/subst 90 - inconsistent handling of utf8 under "use utf8"# op/taint 29 - "use re 'taint'" deparsed in the wrong place wrt. block open# op/tiehandle compile - "use strict" deparsed in the wrong place# uni/tr_ several# ext/B/t/xref 11 - line numbers when we add newlines to one-line subs# ext/Data/Dumper/t/dumper compile# ext/DB_file/several# ext/Encode/several# ext/Ernno/Errno warnings# ext/IO/lib/IO/t/io_sel 23# ext/PerlIO/t/encoding compile# ext/POSIX/t/posix 6# ext/Socket/Socket 8# ext/Storable/t/croak compile# lib/Attribute/Handlers/t/multi compile# lib/bignum/ several# lib/charnames 35# lib/constant 32# lib/English 40# lib/ExtUtils/t/bytes 4# lib/File/DosGlob compile# lib/Filter/Simple/t/data 1# lib/Math/BigInt/t/constant 1# lib/Net/t/config Deparse-warning# lib/overload compile# lib/Switch/ several# lib/Symbol 4# lib/Test/Simple several# lib/Term/Complete# lib/Tie/File/t/29_downcopy 5# lib/vars 22# Object fields (were globals):## avoid_local:# (local($a), local($b)) and local($a, $b) have the same internal# representation but the short form looks better. We notice we can# use a large-scale local when checking the list, but need to prevent# individual locals too. This hash holds the addresses of OPs that# have already had their local-ness accounted for. The same thing# is done with my().## curcv:# CV for current sub (or main program) being deparsed## curcvlex:# Cached hash of lexical variables for curcv: keys are names,# each value is an array of pairs, indicating the cop_seq of scopes# in which a var of that name is valid.## curcop:# COP for statement being deparsed## curstash:# name of the current package for deparsed code## subs_todo:# array of [cop_seq, CV, is_format?] for subs and formats we still# want to deparse## protos_todo:# as above, but [name, prototype] for subs that never got a GV## subs_done, forms_done:# keys are addresses of GVs for subs and formats we've already# deparsed (or at least put into subs_todo)## subs_declared# keys are names of subs for which we've printed declarations.# That means we can omit parentheses from the arguments.## subs_deparsed# Keeps track of fully qualified names of all deparsed subs.## parens: -p# linenums: -l# unquote: -q# cuddle: ` ' or `\n', depending on -sC# indent_size: -si# use_tabs: -sT# ex_const: -sv# A little explanation of how precedence contexts and associativity# work:## deparse() calls each per-op subroutine with an argument $cx (short# for context, but not the same as the cx* in the perl core), which is# a number describing the op's parents in terms of precedence, whether# they're inside an expression or at statement level, etc. (see# chart below). When ops with children call deparse on them, they pass# along their precedence. Fractional values are used to implement# associativity (`($x + $y) + $z' => `$x + $y + $y') and related# parentheses hacks. The major disadvantage of this scheme is that# it doesn't know about right sides and left sides, so say if you# assign a listop to a variable, it can't tell it's allowed to leave# the parens off the listop.# Precedences:# 26 [TODO] inside interpolation context ("")# 25 left terms and list operators (leftward)# 24 left -># 23 nonassoc ++ --# 22 right **# 21 right ! ~ \ and unary + and -# 20 left =~ !~# 19 left * / % x# 18 left + - .# 17 left << >># 16 nonassoc named unary operators# 15 nonassoc < > <= >= lt gt le ge# 14 nonassoc == != <=> eq ne cmp# 13 left &# 12 left | ^# 11 left &&# 10 left ||# 9 nonassoc .. ...# 8 right ?:# 7 right = += -= *= etc.# 6 left , =># 5 nonassoc list operators (rightward)# 4 right not# 3 left and# 2 left or xor# 1 statement modifiers# 0.5 statements, but still print scopes as do { ... }# 0 statement level# Nonprinting characters with special meaning:# \cS - steal parens (see maybe_parens_unop)# \n - newline and indent# \t - increase indent# \b - decrease indent (`outdent')# \f - flush left (no indent)# \cK - kill following semicolon, if anysub null { my $op = shift; return class($op) eq "NULL";}sub todo { my $self = shift; my($cv, $is_form) = @_; return unless ($cv->FILE eq $0 || exists $self->{files}{$cv->FILE}); my $seq; if ($cv->OUTSIDE_SEQ) { $seq = $cv->OUTSIDE_SEQ; } elsif (!null($cv->START) and is_state($cv->START)) { $seq = $cv->START->cop_seq; } else { $seq = 0; } push @{$self->{'subs_todo'}}, [$seq, $cv, $is_form]; unless ($is_form || class($cv->STASH) eq 'SPECIAL') { $self->{'subs_deparsed'}{$cv->STASH->NAME."::".$cv->GV->NAME} = 1; }}sub next_todo { my $self = shift; my $ent = shift @{$self->{'subs_todo'}}; my $cv = $ent->[1]; my $gv = $cv->GV; my $name = $self->gv_name($gv); if ($ent->[2]) { return "format $name =\n" . $self->deparse_format($ent->[1]). "\n"; } else { $self->{'subs_declared'}{$name} = 1; if ($name eq "BEGIN") { my $use_dec = $self->begin_is_use($cv); if (defined ($use_dec) and $self->{'expand'} < 5) { return () if 0 == length($use_dec); return $use_dec; } } my $l = ''; if ($self->{'linenums'}) { my $line = $gv->LINE; my $file = $gv->FILE; $l = "\n\f#line $line \"$file\"\n"; } my $p = ''; if (class($cv->STASH) ne "SPECIAL") { my $stash = $cv->STASH->NAME; if ($stash ne $self->{'curstash'}) { $p = "package $stash;\n"; $name = "$self->{'curstash'}::$name" unless $name =~ /::/; $self->{'curstash'} = $stash; } $name =~ s/^\Q$stash\E::(?!\z|.*::)//; } return "${p}${l}sub $name " . $self->deparse_sub($cv); }}# Return a "use" declaration for this BEGIN block, if appropriatesub begin_is_use { my ($self, $cv) = @_; my $root = $cv->ROOT; local @$self{qw'curcv curcvlex'} = ($cv);#require B::Debug;#B::walkoptree($cv->ROOT, "debug"); my $lineseq = $root->first; return if $lineseq->name ne "lineseq"; my $req_op = $lineseq->first->sibling; return if $req_op->name ne "require"; my $module; if ($req_op->first->private & OPpCONST_BARE) { # Actually it should always be a bareword $module = $self->const_sv($req_op->first)->PV; $module =~ s[/][::]g; $module =~ s/.pm$//; } else { $module = $self->const($self->const_sv($req_op->first), 6); } my $version; my $version_op = $req_op->sibling; return if class($version_op) eq "NULL"; if ($version_op->name eq "lineseq") { # We have a version parameter; skip nextstate & pushmark my $constop = $version_op->first->next->next; return unless $self->const_sv($constop)->PV eq $module; $constop = $constop->sibling; $version = $self->const_sv($constop); if (class($version) eq "IV") { $version = $version->int_value; } elsif (class($version) eq "NV") { $version = $version->NV; } elsif (class($version) ne "PVMG") { # Includes PVIV and PVNV $version = $version->PV; } else { # version specified as a v-string $version = 'v'.join '.', map ord, split //, $version->PV; } $constop = $constop->sibling; return if $constop->name ne "method_named"; return if $self->const_sv($constop)->PV ne "VERSION"; } $lineseq = $version_op->sibling; return if $lineseq->name ne "lineseq"; my $entersub = $lineseq->first->sibling; if ($entersub->name eq "stub") { return "use $module $version ();\n" if defined $version; return "use $module ();\n"; } return if $entersub->name ne "entersub"; # See if there are import arguments my $args = ''; my $svop = $entersub->first->sibling; # Skip over pushmark return unless $self->const_sv($svop)->PV eq $module; # Pull out the arguments for ($svop=$svop->sibling; $svop->name ne "method_named"; $svop = $svop->sibling) { $args .= ", " if length($args); $args .= $self->deparse($svop, 6); } my $use = 'use'; my $method_named = $svop; return if $method_named->name ne "method_named"; my $method_name = $self->const_sv($method_named)->PV; if ($method_name eq "unimport") { $use = 'no'; } # Certain pragmas are dealt with using hint bits, # so we ignore them here
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -