打印出匿名子程序的代码

And*_*kov 15 reflection perl subroutine

我目前正在使用一个非常复杂的Perl架构,我想创建一些调试工具.由于很多行为都涉及匿名子程序,我想分析一些行为,而我所要处理的只是对子程序的引用.

简而言之,有没有办法打印代码(因为Perl被解释为可能仍然可用?)的子程序引用?

Eri*_*rom 27

核心模块B :: Deparse提供此功能.

use B::Deparse ();

my $deparse = B::Deparse->new;

my $code = sub {print "hello, world!"};

print 'sub ', $deparse->coderef2text($code), "\n";
Run Code Online (Sandbox Code Playgroud)

打印:

sub {
    print 'hello, world!';
}
Run Code Online (Sandbox Code Playgroud)

在使用B::Deparse它时,重要的是要记住它返回的是编译的操作码树的反编译版本,而不是原始源文本.这意味着常量,算术表达式和其他构造可以由优化器折叠和重写.

拼图的另一部分是处理封闭的词汇变量.如果您正在使用的子例程访问任何外部词法,它们将不会出现在deparse的输出中,并将导致重新编译失败.您可以使用PadWalker模块中的closed_overset_closed_over函数解决此问题.

use PadWalker qw/closed_over set_closed_over/;

my $closure = do {
    my $counter = 0;
    sub {$counter++}
};

print $closure->(), ' ' for 1..3; # 0 1 2
print "\n";

my $pad = closed_over $closure; # hash of lexicals

                 # create dummy lexicals for compilation
my $copy = eval 'my ('.join(','=> keys %$pad).');'. 
                'sub '.$deparse->coderef2text($closure);

set_closed_over $copy, $pad;  # replace dummy lexicals with real ones

print $copy->(), ' ' for 1..3; # 3 4 5
Run Code Online (Sandbox Code Playgroud)

最后,如果要查找子例程的真实源代码的位置,可以使用核心B模块:

use B ();
my $meta = B::svref_2object($closure);

print "$closure at ".$meta->FILE.' line '.$meta->GV->LINE."\n";
Run Code Online (Sandbox Code Playgroud)

它打印的东西像:

CODE(0x28dcffc) at filename.pl line 21


Sda*_*ons 15

是的,Data::Dumper可以通过以下方式告诉他们B::Deparse:

#!/usr/bin/perl

use Data::Dumper;
use strict;
use warnings;
$Data::Dumper::Deparse = 1;

my $code = sub { my $a = 42;  print $a ** 2; };

print Dumper $code;
Run Code Online (Sandbox Code Playgroud)

Data::Dumper如果您愿意,还有一个面向对象的接口(在perldoc中描述).

注意:输出的代码与您最初指定的代码不同,但它具有相同的语义.


bvr*_*bvr 7

此外,Devel :: Dwarn设置Data::Dumper默认为deparses.它很快成为我最喜欢的翻斗车:

perl -MDevel::Dwarn -e "Dwarn { callback => sub { 1+1 } }"
Run Code Online (Sandbox Code Playgroud)

{
  callback => sub {
      2;
  }
}
Run Code Online (Sandbox Code Playgroud)


bri*_*foy 6

对于这种事情,我总是提到跟踪PerlMonks匿名coderef的文件名/行号.Randal有一个标记匿名子程序的想法,所以你可以看到你定义它们的位置,并且我对它进行了一些扩展.它使用了Eric发布的一些相同的东西,但还有一些.