Распечатка кода анонимной подпрограммы
в настоящее время я работаю в очень сложной архитектуре Perl, и я хочу создать некоторые инструменты отладки. Поскольку большая часть поведения включает в себя анонимные подпрограммы, я хотел бы проанализировать некоторые из них, и все, с чем я должен работать, это ссылка на подпрограмму.
короче говоря, есть ли способ распечатать код (поскольку Perl интерпретируется, он все еще может быть доступен?) ссылки на подпрограмму?
4 ответов
базовый блок 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
важно помнить, что он возвращает декомпилированную версию скомпилированного дерева op-кодов, а не исходный текст. Это означает, что константы, арифметические выражения, и другие конструкции могут быть сложены и переписать оптимизатором.
другая часть головоломки имеет дело с закрытым над лексическими переменными. Если подпрограммы, с которыми вы работаете, имеют доступ к любым внешним лексикам, они не будут присутствовать в выходных данных deparse и приведут к сбою перекомпиляции. Вы можете решить эту проблему с помощью closed_over
и set_closed_over
функции PadWalker модуль.
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
наконец, если вы хотите узнать, где находится реальный исходный код подпрограммы, вы можете использовать core 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
), если хотите.
Примечание: код, который выводится, не будет идентичен тому, что вы первоначально указали, но он будет иметь ту же семантику.
и Devel:: Dwarn наборы Data::Dumper
так это deparses по умолчанию. Это быстро сделало его моим любимым самосвалом:
perl -MDevel::Dwarn -e "Dwarn { callback => sub { 1+1 } }"
дает
{
callback => sub {
2;
}
}
для такого рода вещей я всегда ссылаюсь на отслеживать именем/номер строки анонимного coderef на PerlMonks. У Рэндала появилась идея пометить анонимные подпрограммы, чтобы вы могли видеть, где вы их определяете, и я немного расширил ее. Он использует некоторые из то же самое, что Эрик опубликовал, но с немного больше.