Catalyst->setupでマッチ変数が使用されてしまう
Text::Balancedが修正されていました
1.99.0 Thu Nov 16 07:32:06 2006 - Removed reliance on expensive $& variable (thanks John)
しまった。パッチ送っておけば良かった。コアモジュールに名前載ったかもしれないのにぃ。
マッチ変数「$` $' $&」がプログラム中に一つでも存在すると、実行する全ての正規表現のパフォーマンスに影響してしまいます。
参考:
- English - use nice English (or awk) names for ugly punctuation variables - metacpan.orgのPERFORMANCEセクション
- perlvar - Perl predefined variables - metacpan.orgのBUGSセクション
ということなので、普段から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ぐらいしか使わないし。キャンセル方法は後で調べよう。