Подтвердить что ты не робот

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

В настоящее время я работаю в очень сложной архитектуре Perl, и я хочу создать некоторые инструменты для отладки. Поскольку во многом поведение связано с анонимными подпрограммами, я бы хотел проанализировать некоторые из этих поведений, и все, с чем я должен работать, - это ссылка на подпрограмму.

Короче говоря, есть способ распечатать код (поскольку Perl интерпретируется, он все еще может быть доступен?) ссылки подпрограммы?

4b9b3361

Ответ 1

Основной модуль 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

Наконец, если вы хотите узнать, где находится исходный код подпрограммы, вы можете использовать модуль 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

Ответ 2

Да, 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), если вы предпочитаете.

Примечание. Вывод кода не будет идентичен тому, что вы изначально указали, но будет иметь одинаковую семантику.

Ответ 3

Кроме того, Devel::Dwarn устанавливает Data::Dumper, поэтому он депарафинирует по умолчанию. Он быстро сделал его моим любимым самосвалом:

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

дает

{
  callback => sub {
      2;
  }
}

Ответ 4

Для такого рода вещей я всегда ссылаюсь на Отслеживать имя файла/номер анонимного coderef на PerlMonks. У Рэндала была идея пометить анонимные подпрограммы, чтобы вы могли видеть, где вы их определили, и я немного расширил его. Он использует некоторые из того же материала, который опубликовал Эрик, но немного больше.