我目前在一个非常复杂的Perl体系结构中工作,并且我想创建一些调试工具。由于许多行为涉及匿名子例程,因此我想分析一些行为,我必须处理的是对子例程的引用。打印出一个匿名子程序的代码
简而言之,是否有一种方法可以打印代码(因为解释为Perl可能仍然可用?)的子程序引用?
我目前在一个非常复杂的Perl体系结构中工作,并且我想创建一些调试工具。由于许多行为涉及匿名子例程,因此我想分析一些行为,我必须处理的是对子例程的引用。打印出一个匿名子程序的代码
简而言之,是否有一种方法可以打印代码(因为解释为Perl可能仍然可用?)的子程序引用?
核心模块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_over
和set_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
呀,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
),如果你喜欢。
注意:输出的代码不会与您最初指定的代码相同,但它具有相同的语义。
对于这类事情,我总是在PerlMonks上提到Track the filename/line number of an anonymous coderef。兰德尔有一个想法来标记匿名子程序,所以你可以看到你定义他们的位置,我扩展了一下。它使用same stuff that Eric posted的一些,但稍微多一点。
另外,Devel::Dwarn设置Data::Dumper
,因此它默认情况下会解除分析。它赶快致电吧我最喜欢的自卸车:
perl -MDevel::Dwarn -e "Dwarn { callback => sub { 1+1 } }"
给
{
callback => sub {
2;
}
}
非常深入的解答,非常感谢。 :) – 2011-04-09 03:30:42
不幸的是,我意识到我的主要问题实际上是在我的梅森运行的安全隔间中 - 后端类被阻止执行,所以我相信我运气不好。 – 2011-04-09 03:31:33