私は現在、非常に複雑なPerlアーキテクチャで作業しており、いくつかのデバッグツールを作成したいと考えています。多くの動作には匿名のサブルーチンが含まれるため、動作の一部を分析したいと思います。操作する必要があるのは、サブルーチンへの参照だけです。
要するに、サブルーチン参照のコードを印刷する方法はありますか(Perlは解釈されるので、まだ利用できる可能性がありますか?)?
私は現在、非常に複雑なPerlアーキテクチャで作業しており、いくつかのデバッグツールを作成したいと考えています。多くの動作には匿名のサブルーチンが含まれるため、動作の一部を分析したいと思います。操作する必要があるのは、サブルーチンへの参照だけです。
要するに、サブルーチン参照のコードを印刷する方法はありますか(Perlは解釈されるので、まだ利用できる可能性がありますか?)?
コアモジュール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の出力に存在せず、再コンパイルが失敗します。これは、 PadWalkerモジュールのclosed_over
およびset_closed_over
関数を使用して解決できます。
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";
これは次のようなものを印刷します:
filename.plの21行目のCODE(0x28dcffc)
ええ、次のようなものを介して、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
されるため、デフォルトで逆解析されます。それはすぐに私のお気に入りのダンパーになりました:
perl -MDevel::Dwarn -e "Dwarn { callback => sub { 1+1 } }"
与える
{
callback => sub {
2;
}
}
この種のことについては、私は常にPerlMonksの匿名coderefのファイル名/行番号を追跡するを参照します。Randalは、匿名サブルーチンにタグを付けて、それらを定義した場所を確認できるようにするというアイデアを思いつきました。私はそれを少し拡張しました。エリックが投稿したものと同じもののいくつかを使用していますが、もう少しあります。