2011-04-08 44 views
15

我目前在一个非常复杂的Perl体系结构中工作,并且我想创建一些调试工具。由于许多行为涉及匿名子例程,因此我想分析一些行为,我必须处理的是对子例程的引用。打印出一个匿名子程序的代码

简而言之,是否有一种方法可以打印代码(因为解释为Perl可能仍然可用?)的子程序引用?

回答

23

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

use B::Deparse(); 

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

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

print 'sub ', $deparse->coderef2text($code), "\n"; 

它打印:

sub { 
    print 'hello, world!'; 
} 

当使用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 

最后,如果你想找出子程序真实的源代码,你可以使用核心B模块:

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

print "$closure at ".$meta->FILE.' line '.$meta->GV->LINE."\n"; 

它打印一样的东西:

 
CODE(0x28dcffc) at filename.pl line 21 
+0

非常深入的解答,非常感谢。 :) – 2011-04-09 03:30:42

+0

不幸的是,我意识到我的主要问题实际上是在我的梅森运行的安全隔间中 - 后端类被阻止执行,所以我相信我运气不好。 – 2011-04-09 03:31:33

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; 

有一个面向对象的接口,以及(在对的perldoc描述Data::Dumper),如果你喜欢。

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

7

另外,Devel::Dwarn设置Data::Dumper,因此它默认情况下会解除分析。它赶快致电吧我最喜欢的自卸车:

perl -MDevel::Dwarn -e "Dwarn { callback => sub { 1+1 } }" 

{ 
    callback => sub { 
     2; 
    } 
}