Распечатка кода анонимной подпрограммы - PullRequest
15 голосов
/ 08 апреля 2011

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

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

Ответы [ 4 ]

25 голосов
/ 08 апреля 2011

Основной модуль 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 и приведут к сбою перекомпиляции. Это можно решить с помощью функций 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
15 голосов
/ 08 апреля 2011

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

Примечание: выводимый код не будет идентичным тому, который вы указали изначально, но у него будет та же семантика.

7 голосов
/ 08 апреля 2011

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

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

дает

{
  callback => sub {
      2;
  }
}
6 голосов
/ 08 апреля 2011

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

...