2007年06月22日

ことの発端はmiyagawaさんの

というわけで上記のような問題になやまされずにリテラルをソースに書くことが出来そう。5.9 からバックポートされたのかなぁ、と思ってソースをみると思いっきり use 5.9.5 が。5.10 のリリースがまたひとつ待ち遠しくなったなぁ。

から

なんで5.9から?

encoding:sourceのソースを見てみると5.10の新演算子//が使われていた。のでまずはdefined使いまくりでソースを書き換えたけど上手く動かない。よく見ると

my $hinthash = (caller($level))[10];

なるコードがある、callerの戻り値のリストは10個しか無い筈なのに11個目を取ってる。
調べてみると、5.10からの仕様でperlpragmaなるドキュメントがある。
callerで指定した呼び出し元のhinthashを取得出来る物らしい。

$^Hと%^H

$^Hはおなじみのuse strictとかでも使用されている、インタプリタに特殊なフラグを渡す為に使われる変数です。
%^Hなんですが、あまり使われてない(overloadとかで使ってる)ソースのコンパイル時に役立ちそうな情報を保存しておく場所みたいな使われ方です。
どれもBEGINのフェーズで活用されます。

なんでBEGINかって言うと、コンパイルフェーズ中にhintを見てコンパイラが色々挙動を買えてくれるので、BEGINフェーズが終わると値が変になる。

BEGIN {
    $^H{hoge} = 'aaa';
}
print $^{hoge};# BBBが表示される
BEGIN {
    $^H{hoge} = 'BBB';
}

こんな感じで普通に考えたら違和感の在る事になる(そういうもんだけどね)

でperlvarを見ると%^Hはレキシカルスコープを抜けたら元に戻る的な事が書いてあったんだけど、これをやるには$^Hで特定のビットを立てなきゃいけないoverload.pmとか見るとわかると思う。

(caller(x))[10]

で、さっきの11個目の値というのは

BEGIN {
    $^H{hoge} = 'aaa';
}
sub c {
    my $hinthash = (caller(0))[10];
    print $hinthash->{hoge};# aaaが表示される
}
c();
BEGIN {
    $^H{hoge} = 'BBB';
}

って事をやってくれる。
5.9.4だと上の上のコードのprint $^H{hoge}で、値が取って来れなくなってた。
perl 5.8系だとこれが使えないから意図した動作にならないわけです。

とりあえずパッチ作った

意図した通りにはならないけど、caller使わず%^Hだけを駆使してパッチを書いてみた
http://tech.yappo.jp/download/encoding-source-0.02_v58.patch
とりあえず一通りのケースで動くけどpackとかで上手く動かない。なんで買って言うとリテラルのencodingはBEGINフェーズ中に行われているので%^Hが上手く活用されるんだが、変数に入ったデータとかをdecodeしようとする時(pack)はBEGINが終わって通常のフェーズ中で処理されるおで%^Hが取れなくなるわけ。

hioさんも5.8で動くのを作っていて、そっちはソースフィルタを使ってcallerの件を上手く回避してます。さすがです><

今後

とりあえず呼び出し元のレキシカルスコープを細く出来るモジュールをXSで書いて、それを使って上手い具合に5.8で動かせる様にしたいす。

Scope::Stash (Lexical::Stash ?)

これは、呼び出し元のx階層上のスコープに特定の値をメモします。例として

use Scope::Stash;

sub hoge {
    print Scope::Stash->get(0, 'hoge');# 出力しない
    Scope::Stash->set(0, hoge => 'boofy');
    print Scope::Stash->get(0, 'hoge');# boofy
    {
        print Scope::Stash->get(0, 'hoge');# boofy
        Scope::Stash->set(0, hoge => 'mixi');
        print Scope::Stash->get(0, 'hoge');# mixi
    }
    print Scope::Stash->get(0, 'hoge');# boofy
}
print Scope::Stash->get(0, 'hoge');# 出力しない

こんな動作をするモジュールをどうにかして書こうかなぁと

PadWalkerのXSコードを見るとpads_into_hashの中のCOP_SEQ_RANGE_(LOW|HIGH)辺りを参考にすれば、補足出来そうな気がする。
呼び出し元のcos_seqを探して来てHIGHの値までに紐づく様にデータ保持しておいて、上書きが合ったら上書き前のcos_seqを、上書き呼び出し時のcos_seqに書き換えてみたいな構造で。
これを%^Hの変わりに使う策略。

%^Hと同期も取れる様にScope::Stash::Hinthashってのも作っておくと良いのかな?
プラグマパッケージのimport/unimport中ならScope::Stash::Hinthashを普通に使うだけで上手く動く気がする。

ついでにhinthashって名前のpragmaも作っておくと、普通のプログラム上で操作出来る様になるから面白い事できる?

副産物

$^ENCODINGの使い方わかったお!



このへんの事は体で理解している弾さんに添削されたい><

Posted by Yappo at 2007年06月22日 17:57 | TrackBack | Perl
Comments
Post a comment









Remember personal info?






コメントを投稿する前に↓の場所にnospamと入力してください。