📄 deparse.pm
字号:
my $sv = $cv->const_sv; if ($$sv) { # uh-oh. inlinable sub... format it differently return $proto . "{ " . $self->const($sv, 0) . " }\n"; } else { # XSUB? (or just a declaration) return "$proto;\n"; } } return $proto ."{\n\t$body\n\b}" ."\n";}sub deparse_format { my $self = shift; my $form = shift; my @text; local($self->{'curcv'}) = $form; local($self->{'curcvlex'}); local($self->{'in_format'}) = 1; local(@$self{qw'curstash warnings hints hinthash'}) = @$self{qw'curstash warnings hints hinthash'}; my $op = $form->ROOT; my $kid; return "\f." if $op->first->name eq 'stub' || $op->first->name eq 'nextstate'; $op = $op->first->first; # skip leavewrite, lineseq while (not null $op) { $op = $op->sibling; # skip nextstate my @exprs; $kid = $op->first->sibling; # skip pushmark push @text, "\f".$self->const_sv($kid)->PV; $kid = $kid->sibling; for (; not null $kid; $kid = $kid->sibling) { push @exprs, $self->deparse($kid, 0); } push @text, "\f".join(", ", @exprs)."\n" if @exprs; $op = $op->sibling; } return join("", @text) . "\f.";}sub is_scope { my $op = shift; return $op->name eq "leave" || $op->name eq "scope" || $op->name eq "lineseq" || ($op->name eq "null" && class($op) eq "UNOP" && (is_scope($op->first) || $op->first->name eq "enter"));}sub is_state { my $name = $_[0]->name; return $name eq "nextstate" || $name eq "dbstate" || $name eq "setstate";}sub is_miniwhile { # check for one-line loop (`foo() while $y--') my $op = shift; return (!null($op) and null($op->sibling) and $op->name eq "null" and class($op) eq "UNOP" and (($op->first->name =~ /^(and|or)$/ and $op->first->first->sibling->name eq "lineseq") or ($op->first->name eq "lineseq" and not null $op->first->first->sibling and $op->first->first->sibling->name eq "unstack") ));}# Check if the op and its sibling are the initialization and the rest of a# for (..;..;..) { ... } loopsub is_for_loop { my $op = shift; # This OP might be almost anything, though it won't be a # nextstate. (It's the initialization, so in the canonical case it # will be an sassign.) The sibling is a lineseq whose first child # is a nextstate and whose second is a leaveloop. my $lseq = $op->sibling; if (!is_state $op and !null($lseq) and $lseq->name eq "lineseq") { if ($lseq->first && !null($lseq->first) && is_state($lseq->first) && (my $sib = $lseq->first->sibling)) { return (!null($sib) && $sib->name eq "leaveloop"); } } return 0;}sub is_scalar { my $op = shift; return ($op->name eq "rv2sv" or $op->name eq "padsv" or $op->name eq "gv" or # only in array/hash constructs $op->flags & OPf_KIDS && !null($op->first) && $op->first->name eq "gvsv");}sub maybe_parens { my $self = shift; my($text, $cx, $prec) = @_; if ($prec < $cx # unary ops nest just fine or $prec == $cx and $cx != 4 and $cx != 16 and $cx != 21 or $self->{'parens'}) { $text = "($text)"; # In a unop, let parent reuse our parens; see maybe_parens_unop $text = "\cS" . $text if $cx == 16; return $text; } else { return $text; }}# same as above, but get around the `if it looks like a function' rulesub maybe_parens_unop { my $self = shift; my($name, $kid, $cx) = @_; if ($cx > 16 or $self->{'parens'}) { $kid = $self->deparse($kid, 1); if ($name eq "umask" && $kid =~ /^\d+$/) { $kid = sprintf("%#o", $kid); } return "$name($kid)"; } else { $kid = $self->deparse($kid, 16); if ($name eq "umask" && $kid =~ /^\d+$/) { $kid = sprintf("%#o", $kid); } if (substr($kid, 0, 1) eq "\cS") { # use kid's parens return $name . substr($kid, 1); } elsif (substr($kid, 0, 1) eq "(") { # avoid looks-like-a-function trap with extra parens # (`+' can lead to ambiguities) return "$name(" . $kid . ")"; } else { return "$name $kid"; } }}sub maybe_parens_func { my $self = shift; my($func, $text, $cx, $prec) = @_; if ($prec <= $cx or substr($text, 0, 1) eq "(" or $self->{'parens'}) { return "$func($text)"; } else { return "$func $text"; }}sub maybe_local { my $self = shift; my($op, $cx, $text) = @_; my $our_intro = ($op->name =~ /^(gv|rv2)[ash]v$/) ? OPpOUR_INTRO : 0; if ($op->private & (OPpLVAL_INTRO|$our_intro) and not $self->{'avoid_local'}{$$op}) { my $our_local = ($op->private & OPpLVAL_INTRO) ? "local" : "our"; if( $our_local eq 'our' ) { # XXX This assertion fails code with non-ASCII identifiers, # like ./ext/Encode/t/jperl.t die "Unexpected our($text)\n" unless $text =~ /^\W(\w+::)*\w+\z/; $text =~ s/(\w+::)+//; } if (want_scalar($op)) { return "$our_local $text"; } else { return $self->maybe_parens_func("$our_local", $text, $cx, 16); } } else { return $text; }}sub maybe_targmy { my $self = shift; my($op, $cx, $func, @args) = @_; if ($op->private & OPpTARGET_MY) { my $var = $self->padname($op->targ); my $val = $func->($self, $op, 7, @args); return $self->maybe_parens("$var = $val", $cx, 7); } else { return $func->($self, $op, $cx, @args); }}sub padname_sv { my $self = shift; my $targ = shift; return $self->{'curcv'}->PADLIST->ARRAYelt(0)->ARRAYelt($targ);}sub maybe_my { my $self = shift; my($op, $cx, $text) = @_; if ($op->private & OPpLVAL_INTRO and not $self->{'avoid_local'}{$$op}) { my $my = $op->private & OPpPAD_STATE ? "state" : "my"; if (want_scalar($op)) { return "$my $text"; } else { return $self->maybe_parens_func($my, $text, $cx, 16); } } else { return $text; }}# The following OPs don't have functions:# pp_padany -- does not exist after parsingsub AUTOLOAD { if ($AUTOLOAD =~ s/^.*::pp_//) { warn "unexpected OP_".uc $AUTOLOAD; return "XXX"; } else { die "Undefined subroutine $AUTOLOAD called"; }}sub DESTROY {} # Do not AUTOLOAD# $root should be the op which represents the root of whatever# we're sequencing here. If it's undefined, then we don't append# any subroutine declarations to the deparsed ops, otherwise we# append appropriate declarations.sub lineseq { my($self, $root, @ops) = @_; my($expr, @exprs); my $out_cop = $self->{'curcop'}; my $out_seq = defined($out_cop) ? $out_cop->cop_seq : undef; my $limit_seq; if (defined $root) { $limit_seq = $out_seq; my $nseq; $nseq = $self->find_scope_st($root->sibling) if ${$root->sibling}; $limit_seq = $nseq if !defined($limit_seq) or defined($nseq) && $nseq < $limit_seq; } $limit_seq = $self->{'limit_seq'} if defined($self->{'limit_seq'}) && (!defined($limit_seq) || $self->{'limit_seq'} < $limit_seq); local $self->{'limit_seq'} = $limit_seq; $self->walk_lineseq($root, \@ops, sub { push @exprs, $_[0]} ); my $body = join(";\n", grep {length} @exprs); my $subs = ""; if (defined $root && defined $limit_seq && !$self->{'in_format'}) { $subs = join "\n", $self->seq_subs($limit_seq); } return join(";\n", grep {length} $body, $subs);}sub scopeop { my($real_block, $self, $op, $cx) = @_; my $kid; my @kids; local(@$self{qw'curstash warnings hints hinthash'}) = @$self{qw'curstash warnings hints hinthash'} if $real_block; if ($real_block) { $kid = $op->first->sibling; # skip enter if (is_miniwhile($kid)) { my $top = $kid->first; my $name = $top->name; if ($name eq "and") { $name = "while"; } elsif ($name eq "or") { $name = "until"; } else { # no conditional -> while 1 or until 0 return $self->deparse($top->first, 1) . " while 1"; } my $cond = $top->first; my $body = $cond->sibling->first; # skip lineseq $cond = $self->deparse($cond, 1); $body = $self->deparse($body, 1); return "$body $name $cond"; } } else { $kid = $op->first; } for (; !null($kid); $kid = $kid->sibling) { push @kids, $kid; } if ($cx > 0) { # inside an expression, (a do {} while for lineseq) return "do {\n\t" . $self->lineseq($op, @kids) . "\n\b}"; } else { my $lineseq = $self->lineseq($op, @kids); return (length ($lineseq) ? "$lineseq;" : ""); }}sub pp_scope { scopeop(0, @_); }sub pp_lineseq { scopeop(0, @_); }sub pp_leave { scopeop(1, @_); }# This is a special case of scopeop and lineseq, for the case of the# main_root. The difference is that we print the output statements as# soon as we get them, for the sake of impatient users.sub deparse_root { my $self = shift; my($op) = @_; local(@$self{qw'curstash warnings hints hinthash'}) = @$self{qw'curstash warnings hints hinthash'}; my @kids; return if null $op->first; # Can happen, e.g., for Bytecode without -k for (my $kid = $op->first->sibling; !null($kid); $kid = $kid->sibling) { push @kids, $kid; } $self->walk_lineseq($op, \@kids, sub { print $self->indent($_[0].';'); print "\n" unless $_[1] == $#kids; });}sub walk_lineseq { my ($self, $op, $kids, $callback) = @_; my @kids = @$kids; for (my $i = 0; $i < @kids; $i++) { my $expr = ""; if (is_state $kids[$i]) { $expr = $self->deparse($kids[$i++], 0); if ($i > $#kids) { $callback->($expr, $i); last; } } if (is_for_loop($kids[$i])) { $callback->($expr . $self->for_loop($kids[$i], 0), $i++); next; } $expr .= $self->deparse($kids[$i], (@kids != 1)/2); $expr =~ s/;\n?\z//; $callback->($expr, $i); }}# The BEGIN {} is used here because otherwise this code isn't executed# when you run B::Deparse on itself.my %globalnames;BEGIN { map($globalnames{$_}++, "SIG", "STDIN", "STDOUT", "STDERR", "INC", "ENV", "ARGV", "ARGVOUT", "_"); }sub gv_name { my $self = shift; my $gv = shift;Carp::confess() unless ref($gv) eq "B::GV"; my $stash = $gv->STASH->NAME; my $name = $gv->SAFENAME; if ($stash eq 'main' && $name =~ /^::/) { $stash = '::'; } elsif (($stash eq 'main' && $globalnames{$name}) or ($stash eq $self->{'curstash'} && !$globalnames{$name} && ($stash eq 'main' || $name !~ /::/)) or $name =~ /^[^A-Za-z_:]/) { $stash = ""; } else { $stash = $stash . "::"; } if ($name =~ /^(\^..|{)/) { $name = "{$name}"; # ${^WARNING_BITS}, etc and ${ } return $stash . $name;}# Return the name to use for a stash variable.# If a lexical with the same name is in scope, it may need to be# fully-qualified.sub stash_variable { my ($self, $prefix, $name) = @_; return "$prefix$name" if $name =~ /::/; unless ($prefix eq '$' || $prefix eq '@' || #' $prefix eq '%' || $prefix eq '$#') { return "$prefix$name"; } my $v = ($prefix eq '$#' ? '@' : $prefix) . $name; return $prefix .$self->{'curstash'}.'::'. $name if $self->lex_in_scope($v); return "$prefix$name";}sub lex_in_scope { my ($self, $name) = @_; $self->populate_curcvlex() if !defined $self->{'curcvlex'}; return 0 if !defined($self->{'curcop'}); my $seq = $self->{'curcop'}->cop_seq; return 0 if !exists $self->{'curcvlex'}{$name}; for my $a (@{$self->{'curcvlex'}{$name}}) { my ($st, $en) = @$a; return 1 if $seq > $st && $seq <= $en; } return 0;}sub populate_curcvlex { my $self = shift; for (my $cv = $self->{'curcv'}; class($cv) eq "CV"; $cv = $cv->OUTSIDE) { my $padlist = $cv->PADLIST; # an undef CV still in lexical chain next if class($padlist) eq "SPECIAL"; my @padlist = $padlist->ARRAY; my @ns = $padlist[0]->ARRAY; for (my $i=0; $i<@ns; ++$i) { next if class($ns[$i]) eq "SPECIAL"; next if $ns[$i]->FLAGS & SVpad_OUR; # Skip "our" vars if (class($ns[$i]) eq "PV") { # Probably that pesky lexical @_ next; } my $name = $ns[$i]->PVX; my ($seq_st, $seq_en) = ($ns[$i]->FLAGS & SVf_FAKE) ? (0, 999999) : ($ns[$i]->COP_SEQ_RANGE_LOW, $ns[$i]->COP_SEQ_RANGE_HIGH); push @{$self->{'curcvlex'}{$name}}, [$seq_st, $seq_en]; } }}sub find_scope_st { ((find_scope(@_))[0]); }sub find_scope_en { ((find_scope(@_))[1]); }# Recurses down the tree, looking for pad variable introductions and COPssub find_scope { my ($self, $op, $scope_st, $scope_en) = @_; carp("Undefined op in find_scope") if !defined $op; return ($scope_st, $scope_en) unless $op->flags & OPf_KIDS; my @queue = ($op); while(my $op = shift @queue ) { for (my $o=$op->first; $$o; $o=$o->sibling) { if ($o->name =~ /^pad.v$/ && $o->private & OPpLVAL_INTRO) { my $s = int($self->padname_sv($o->targ)->COP_SEQ_RANGE_LOW); my $e = $self->padname_sv($o->targ)->COP_SEQ_RANGE_HIGH;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -