我目前正在使用一个非常复杂的Perl架构,我想创建一些调试工具。由于很多行为涉及匿名子程序,我想分析一些行为,而我必须使用的只是对子程序的引用。
简而言之,有没有办法打印代码(因为Perl被解释为可能仍然可用?)的子程序引用?
答案 0 :(得分:24)
核心模块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
答案 1 :(得分: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;
如果您愿意,还有一个面向对象的界面(在Data::Dumper
的perldoc中描述)。
注意:输出的代码与您最初指定的代码不同,但它具有相同的语义。
答案 2 :(得分:7)
此外,Devel::Dwarn设置Data::Dumper
,因此默认情况下会取消。它很快成为我最喜欢的翻斗车:
perl -MDevel::Dwarn -e "Dwarn { callback => sub { 1+1 } }"
给出
{
callback => sub {
2;
}
}
答案 3 :(得分:6)
对于这类事情,我总是在PerlMonks上引用Track the filename/line number of an anonymous coderef。 Randal有一个标记匿名子程序的想法,所以你可以看到你定义它们的位置,并且我对它进行了一些扩展。它使用了一些same stuff that Eric posted,但还有一些。