2011-04-08 11 views
15

Actualmente estoy trabajando en una arquitectura Perl muy compleja, y quiero crear algunas herramientas de depuración. Dado que gran parte del comportamiento implica subrutinas anónimas, me gustaría analizar parte del comportamiento, y todo lo que tengo que trabajar es una referencia a la subrutina.Imprimir el código de una subrutina anónima

En resumen, ¿hay alguna manera de imprimir el código (ya que se interpreta que Perl aún puede estar disponible?) De una referencia de subrutina?

Respuesta

23

El módulo de núcleo B::Deparse proporciona esta funcionalidad.

use B::Deparse(); 

my $deparse = B::Deparse->new; 

my $code = sub {print "hello, world!"}; 

print 'sub ', $deparse->coderef2text($code), "\n"; 

que imprime:

sub { 
    print 'hello, world!'; 
} 

Al utilizar B::Deparse es importante recordar que lo que devuelve es una versión descompilada del árbol compilado de códigos de operación, no el texto fuente original. Esto significa que las constantes, las expresiones aritméticas y otras construcciones pueden ser dobladas y reescritas por el optimizador.

La otra parte del rompecabezas se trata de cerrar las variables léxicas. Si las subrutinas con las que está trabajando acceden a léxicos externos, no estarán presentes en la salida de deparse y provocarán la falla de la recopilación. Puede resolverlo con las funciones closed_over y set_closed_over del módulo 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 

Por último, si quieres averiguar dónde código fuente real de la subrutina es, puede utilizar el módulo principal B:

use B(); 
my $meta = B::svref_2object($closure); 

print "$closure at ".$meta->FILE.' line '.$meta->GV->LINE."\n"; 

que imprime algo como:

 
CODE(0x28dcffc) at filename.pl line 21 
+0

respuesta muy en profundidad, gracias. :) –

+0

Desafortunadamente, me di cuenta de que mi problema principal era en realidad con el compartimento seguro en el que se encontraba mi Mason: las clases de back-end están bloqueadas de ejecución, así que creo que no tengo suerte. –

15

Sí, Data::Dumper puede ser contada para traer B::Deparse, a través de algo como:

#!/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; 

Existe una interfaz orientada a objetos, así (que se describe en el perldoc para Data::Dumper), si lo prefiere.

Nota: El código que se emite no será idéntico al que se especificó originalmente, pero tendrá la misma semántica.

7

También, Devel::Dwarn establece Data::Dumper por lo que se depara por defecto. Rápidamente se hizo mi dumper favorita:

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

da

{ 
    callback => sub { 
     2; 
    } 
} 
Cuestiones relacionadas