📄 deparse.pm
字号:
if ($module eq 'strict' || $module eq 'integer' || $module eq 'bytes' || $module eq 'warnings' || $module eq 'feature') { return ""; } if (defined $version && length $args) { return "$use $module $version ($args);\n"; } elsif (defined $version) { return "$use $module $version;\n"; } elsif (length $args) { return "$use $module ($args);\n"; } else { return "$use $module;\n"; }}sub stash_subs { my ($self, $pack) = @_; my (@ret, $stash); if (!defined $pack) { $pack = ''; $stash = \%::; } else { $pack =~ s/(::)?$/::/; no strict 'refs'; $stash = \%$pack; } my %stash = svref_2object($stash)->ARRAY; while (my ($key, $val) = each %stash) { my $class = class($val); if ($class eq "PV") { # Just a prototype. As an ugly but fairly effective way # to find out if it belongs here is to see if the AUTOLOAD # (if any) for the stash was defined in one of our files. my $A = $stash{"AUTOLOAD"}; if (defined ($A) && class($A) eq "GV" && defined($A->CV) && class($A->CV) eq "CV") { my $AF = $A->FILE; next unless $AF eq $0 || exists $self->{'files'}{$AF}; } push @{$self->{'protos_todo'}}, [$pack . $key, $val->PV]; } elsif ($class eq "IV") { # Just a name. As above. my $A = $stash{"AUTOLOAD"}; if (defined ($A) && class($A) eq "GV" && defined($A->CV) && class($A->CV) eq "CV") { my $AF = $A->FILE; next unless $AF eq $0 || exists $self->{'files'}{$AF}; } push @{$self->{'protos_todo'}}, [$pack . $key, undef]; } elsif ($class eq "GV") { if (class(my $cv = $val->CV) ne "SPECIAL") { next if $self->{'subs_done'}{$$val}++; next if $$val != ${$cv->GV}; # Ignore imposters $self->todo($cv, 0); } if (class(my $cv = $val->FORM) ne "SPECIAL") { next if $self->{'forms_done'}{$$val}++; next if $$val != ${$cv->GV}; # Ignore imposters $self->todo($cv, 1); } if (class($val->HV) ne "SPECIAL" && $key =~ /::$/) { $self->stash_subs($pack . $key) unless $pack eq '' && $key eq 'main::'; # avoid infinite recursion } } }}sub print_protos { my $self = shift; my $ar; my @ret; foreach $ar (@{$self->{'protos_todo'}}) { my $proto = (defined $ar->[1] ? " (". $ar->[1] . ")" : ""); push @ret, "sub " . $ar->[0] . "$proto;\n"; } delete $self->{'protos_todo'}; return @ret;}sub style_opts { my $self = shift; my $opts = shift; my $opt; while (length($opt = substr($opts, 0, 1))) { if ($opt eq "C") { $self->{'cuddle'} = " "; $opts = substr($opts, 1); } elsif ($opt eq "i") { $opts =~ s/^i(\d+)//; $self->{'indent_size'} = $1; } elsif ($opt eq "T") { $self->{'use_tabs'} = 1; $opts = substr($opts, 1); } elsif ($opt eq "v") { $opts =~ s/^v([^.]*)(.|$)//; $self->{'ex_const'} = $1; } }}sub new { my $class = shift; my $self = bless {}, $class; $self->{'cuddle'} = "\n"; $self->{'curcop'} = undef; $self->{'curstash'} = "main"; $self->{'ex_const'} = "'???'"; $self->{'expand'} = 0; $self->{'files'} = {}; $self->{'indent_size'} = 4; $self->{'linenums'} = 0; $self->{'parens'} = 0; $self->{'subs_todo'} = []; $self->{'unquote'} = 0; $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; $self->init(); while (my $arg = shift @_) { if ($arg eq "-d") { $self->{'use_dumper'} = 1; require Data::Dumper; } elsif ($arg =~ /^-f(.*)/) { $self->{'files'}{$1} = 1; } elsif ($arg eq "-l") { $self->{'linenums'} = 1; } elsif ($arg eq "-p") { $self->{'parens'} = 1; } elsif ($arg eq "-P") { $self->{'noproto'} = 1; } elsif ($arg eq "-q") { $self->{'unquote'} = 1; } elsif (substr($arg, 0, 2) eq "-s") { $self->style_opts(substr $arg, 2); } elsif ($arg =~ /^-x(\d)$/) { $self->{'expand'} = $1; } } return $self;}{ # Mask out the bits that L<warnings::register> uses my $WARN_MASK; BEGIN { $WARN_MASK = $warnings::Bits{all} | $warnings::DeadBits{all}; } sub WARN_MASK () { return $WARN_MASK; }}# Initialise the contextual information, either from# defaults provided with the ambient_pragmas method,# or from perl's own defaults otherwise.sub init { my $self = shift; $self->{'arybase'} = $self->{'ambient_arybase'}; $self->{'warnings'} = defined ($self->{'ambient_warnings'}) ? $self->{'ambient_warnings'} & WARN_MASK : undef; $self->{'hints'} = $self->{'ambient_hints'}; $self->{'hints'} &= 0xFF if $] < 5.009; $self->{'hinthash'} = $self->{'ambient_hinthash'}; # also a convenient place to clear out subs_declared delete $self->{'subs_declared'};}sub compile { my(@args) = @_; return sub { my $self = B::Deparse->new(@args); # First deparse command-line args if (defined $^I) { # deparse -i print q(BEGIN { $^I = ).perlstring($^I).qq(; }\n); } if ($^W) { # deparse -w print qq(BEGIN { \$^W = $^W; }\n); } if ($/ ne "\n" or defined $O::savebackslash) { # deparse -l and -0 my $fs = perlstring($/) || 'undef'; my $bs = perlstring($O::savebackslash) || 'undef'; print qq(BEGIN { \$/ = $fs; \$\\ = $bs; }\n); } my @BEGINs = B::begin_av->isa("B::AV") ? B::begin_av->ARRAY : (); my @UNITCHECKs = B::unitcheck_av->isa("B::AV") ? B::unitcheck_av->ARRAY : (); my @CHECKs = B::check_av->isa("B::AV") ? B::check_av->ARRAY : (); my @INITs = B::init_av->isa("B::AV") ? B::init_av->ARRAY : (); my @ENDs = B::end_av->isa("B::AV") ? B::end_av->ARRAY : (); for my $block (@BEGINs, @UNITCHECKs, @CHECKs, @INITs, @ENDs) { $self->todo($block, 0); } $self->stash_subs(); local($SIG{"__DIE__"}) = sub { if ($self->{'curcop'}) { my $cop = $self->{'curcop'}; my($line, $file) = ($cop->line, $cop->file); print STDERR "While deparsing $file near line $line,\n"; } }; $self->{'curcv'} = main_cv; $self->{'curcvlex'} = undef; print $self->print_protos; @{$self->{'subs_todo'}} = sort {$a->[0] <=> $b->[0]} @{$self->{'subs_todo'}}; print $self->indent($self->deparse_root(main_root)), "\n" unless null main_root; my @text; while (scalar(@{$self->{'subs_todo'}})) { push @text, $self->next_todo; } print $self->indent(join("", @text)), "\n" if @text; # Print __DATA__ section, if necessary no strict 'refs'; my $laststash = defined $self->{'curcop'} ? $self->{'curcop'}->stash->NAME : $self->{'curstash'}; if (defined *{$laststash."::DATA"}{IO}) { print "package $laststash;\n" unless $laststash eq $self->{'curstash'}; print "__DATA__\n"; print readline(*{$laststash."::DATA"}); } }}sub coderef2text { my $self = shift; my $sub = shift; croak "Usage: ->coderef2text(CODEREF)" unless UNIVERSAL::isa($sub, "CODE"); $self->init(); return $self->indent($self->deparse_sub(svref_2object($sub)));}sub ambient_pragmas { my $self = shift; my ($arybase, $hint_bits, $warning_bits, $hinthash) = (0, 0); while (@_ > 1) { my $name = shift(); my $val = shift(); if ($name eq 'strict') { require strict; if ($val eq 'none') { $hint_bits &= ~strict::bits(qw/refs subs vars/); next(); } my @names; if ($val eq "all") { @names = qw/refs subs vars/; } elsif (ref $val) { @names = @$val; } else { @names = split' ', $val; } $hint_bits |= strict::bits(@names); } elsif ($name eq '$[') { $arybase = $val; } elsif ($name eq 'integer' || $name eq 'bytes' || $name eq 'utf8') { require "$name.pm"; if ($val) { $hint_bits |= ${$::{"${name}::"}{"hint_bits"}}; } else { $hint_bits &= ~${$::{"${name}::"}{"hint_bits"}}; } } elsif ($name eq 're') { require re; if ($val eq 'none') { $hint_bits &= ~re::bits(qw/taint eval/); next(); } my @names; if ($val eq 'all') { @names = qw/taint eval/; } elsif (ref $val) { @names = @$val; } else { @names = split' ',$val; } $hint_bits |= re::bits(@names); } elsif ($name eq 'warnings') { if ($val eq 'none') { $warning_bits = $warnings::NONE; next(); } my @names; if (ref $val) { @names = @$val; } else { @names = split/\s+/, $val; } $warning_bits = $warnings::NONE if !defined ($warning_bits); $warning_bits |= warnings::bits(@names); } elsif ($name eq 'warning_bits') { $warning_bits = $val; } elsif ($name eq 'hint_bits') { $hint_bits = $val; } elsif ($name eq '%^H') { $hinthash = $val; } else { croak "Unknown pragma type: $name"; } } if (@_) { 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;}# This method is the inner loop, so try to keep it simplesub deparse { my $self = shift; my($op, $cx) = @_; Carp::confess("Null op in deparse") if !defined($op) || class($op) eq "NULL"; my $meth = "pp_" . $op->name; return $self->$meth($op, $cx);}sub indent { my $self = shift; my $txt = shift; my @lines = split(/\n/, $txt); my $leader = ""; my $level = 0; my $line; for $line (@lines) { my $cmd = substr($line, 0, 1); if ($cmd eq "\t" or $cmd eq "\b") { $level += ($cmd eq "\t" ? 1 : -1) * $self->{'indent_size'}; if ($self->{'use_tabs'}) { $leader = "\t" x ($level / 8) . " " x ($level % 8); } else { $leader = " " x $level; } $line = substr($line, 1); } if (substr($line, 0, 1) eq "\f") { $line = substr($line, 1); # no indent } else { $line = $leader . $line; } $line =~ s/\cK;?//g; } return join("\n", @lines);}sub deparse_sub { my $self = shift; my $cv = shift; my $proto = "";Carp::confess("NULL in deparse_sub") if !defined($cv) || $cv->isa("B::NULL");Carp::confess("SPECIAL in deparse_sub") if $cv->isa("B::SPECIAL"); local $self->{'curcop'} = $self->{'curcop'}; if ($cv->FLAGS & SVf_POK) { $proto = "(". $cv->PV . ") "; } if ($cv->CvFLAGS & (CVf_METHOD|CVf_LOCKED|CVf_LVALUE)) { $proto .= ": "; $proto .= "lvalue " if $cv->CvFLAGS & CVf_LVALUE; $proto .= "locked " if $cv->CvFLAGS & CVf_LOCKED; $proto .= "method " if $cv->CvFLAGS & CVf_METHOD; } local($self->{'curcv'}) = $cv; local($self->{'curcvlex'}); local(@$self{qw'curstash warnings hints hinthash'}) = @$self{qw'curstash warnings hints hinthash'}; my $body; if (not null $cv->ROOT) { my $lineseq = $cv->ROOT->first; if ($lineseq->name eq "lineseq") { my @ops; for(my$o=$lineseq->first; $$o; $o=$o->sibling) { push @ops, $o; } $body = $self->lineseq(undef, @ops).";"; my $scope_en = $self->find_scope_en($lineseq); if (defined $scope_en) { my $subs = join"", $self->seq_subs($scope_en); $body .= ";\n$subs" if length($subs); } } else { $body = $self->deparse($cv->ROOT->first, 0); } } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -