Catalyst->setupでマッチ変数が使用されてしまう

Text::Balancedが修正されていました

1.99.0  Thu Nov 16 07:32:06 2006

    - Removed reliance on expensive $& variable (thanks John)

しまった。パッチ送っておけば良かった。コアモジュールに名前載ったかもしれないのにぃ。



マッチ変数「$` $' $&」がプログラム中に一つでも存在すると、実行する全ての正規表現のパフォーマンスに影響してしまいます。
参考:

ということなので、普段からDevel::SawAmpersand - Perl extension querying PL_sawampersand variable - metacpan.orgを使って以下のようなsawampersandテストを実施しています。

ok( !sawampersand(), q{$`, $&, and $' should not appear} ); ## no critic

ところで今回、Catalystを使ったアプリケーションにこのテストを追加したところ、テストに失敗してしまいました。しかも

package CatalystApp
use Catalyst;
__PACKAGE__->setup;

というsetupするだけのコードで。。。

どこでマッチ変数が使われているか調べたく、B::FindAmpersand - A compiler backend to find variables that set sawampersand - metacpan.orgを使おうとしたのですが

$ perl -MO=FindAmpersand CatalystApp.pm
B::FindAmpersand not supported for threaded perl at ...

というエラーが出て使えませんでした。スレッド対応perlでは実行できないみたいです。

仕方なくデバッガで追っかけてみました。

$ perl -d CatalystApp.pm
DB<1> > print Devel::SawAmpersand::sawampersand() ? q{$`, $&, and $' should not appear} : '';

として、プロンプトが出るたびにsawampersandチェックが走るように設定してステップ実行。

ごにょごにょデバッグしていると、Catalyst::Dispatcher::_load_dispatch_typesの実行後におかしくなっていることは分かったけども、このサブルーチン自体には特に怪しい部分は無し。eval "require $class";をしているので、ここで読み込んでいるモジュールがダメなんだろうと推測。
$classを表示しながら実行するとCatalyst::DispatchType::Regexの読み込み後におかしくなることが分かったので、再確認。

$ perl -MDevel::SawAmpersand=sawampersand -MCatalyst::DispatchType::Regex -e 'print sawampersand()'
1

( ゚д゚)ビンゴー

しかしCatalyst::DispatchType::Regex自体にはマッチ変数の記述がなかったため、さらに読み込まれているモジュールをチェック。

$ perl -MDevel::SawAmpersand=sawampersand -MCatalyst::DispatchType::Regex -e 'print sawampersand()'
1
$ perl -MDevel::SawAmpersand=sawampersand -MText::SimpleTable -e 'print sawampersand()'
$ perl -MDevel::SawAmpersand=sawampersand -MText::Balanced -e 'print sawampersand()'
1

$ grep -nF '$&' `perldoc -ml Text::Balanced`
331:              $rdelspec = $&;
927:                                      { @bits = $field = defined($1) ? $1 : $& }
2036:complete match (i.e. $&).

というわけで、犯人はText::Balancedでした。

とりあえず原因は分かったけど、これ、どうしよう。Text::Balancedをいじろうにもコアモジュールだから気が引けるし。。。Catalyst::DispatchType::Regexの読み込み自体をキャンセルしてもいいかもしれない。どうせDispatcherはPrivateかLocalぐらいしか使わないし。キャンセル方法は後で調べよう。