Распечатка кода анонимной подпрограммы

в настоящее время я работаю в очень сложной архитектуре 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. У Рэндала появилась идея пометить анонимные подпрограммы, чтобы вы могли видеть, где вы их определяете, и я немного расширил ее. Он использует некоторые из то же самое, что Эрик опубликовал, но с немного больше.