Perl Internalsについて

実は、Perl 5.8以降では、Internals::SvREADONLY()という関数がuseなしで使えるようになっていて、Internals::SvREADONLY($scalar, 1)で$scalarをREADONLY flagをonに、Internals::SvREADONLY($scalar, 0)でoffにできます。

404 Blog Not Found:perl - Const released -- True Readonly

SUGEEEE、ってかそんなのあるなんて知らなかった。まぢで?まぢで?早速試してみた。

my $foo = 10;
Internals::SvREADONLY($foo, 1);

eval { $foo = 'WRYYYYYY' }; # エラーになるはず
print $@ ? "error! $@" : 'not error!';

Internals::SvREADONLY($foo, 0);

eval { $foo = 'WRYYYYYY' }; # エラーにならないはず
print $@ ? "error! $@" : 'not error!';

実行結果は・・・?

error! Modification of a read-only value attempted
not error!

おおおおおおおおおお!フラグ付きの場合はちゃんとエラーになってる。へー凄ぉおいね。

じゃあさじゃあさ、ハッシュのキーにもできるの?

my $foo = { bar => 10 };
Internals::SvREADONLY($foo->{bar}, 1);

eval { $foo->{bar} = 'WRYYYYYY' }; # エラーになるはず
print $@ ? "error! $@" : "not error!\n";

eval { $foo->{baz} = 'WRYYYYYY' }; # 別のキーには影響無し
print $@ ? "error! $@" : "not error!\n";

Internals::SvREADONLY($foo->{bar}, 0);

eval { $foo->{bar} = 'WRYYYYYY' }; # エラーにならないはず
print $@ ? "error! $@" : "not error!\n";

__END__

error! Modification of a read-only value attempted
not error!
not error!

おー、おー、ちゃんとエラーになるねぇ。

じゃあlocalとの組み合わせで一時的に参照不可とかもバッチシできるんじゃね?

my $foo = { bar => 10 };
{
    Internals::SvREADONLY( local $foo->{bar} , 1);

    eval { $foo->{bar} = 'WRYYYYYY' }; # エラーになる
    print $@ ? "error! $@" : 'not error!';
}

eval { $foo->{bar} = 'WRYYYYYY' }; # localスコープ外なのでエラーにならない
print $@ ? "error! $@" : 'not error!';

__END__

error! Modification of a read-only value attempted
not error!

できたできた。

いやー、面白いねぇ。Internals::SvREADONLY。実はみんな知ってたりするものなのかな?コレって。

どれ程のものかなとググってみたら僅か10件しかヒットせず><(追記:今見てみたらもう既に160件ほどに・・・)

かなりマイナーっぽいねコレ・・・。

SvREADONLY以外にも何かないのかなと思ってシンボルダンプしてみた

use Devel::Symdump;

print Devel::Symdump->new( 'Internals' )->as_string;

__END__

arrays

functions
        Internals::HvREHASH
        Internals::SvREADONLY
        Internals::SvREFCNT
        Internals::hash_seed
        Internals::hv_clear_placeholders
        Internals::rehash_seed
hashes

ios

packages

scalars
        Internals::HvREHASH
        Internals::SvREADONLY
        Internals::SvREFCNT
        Internals::hash_seed
        Internals::hv_clear_placeholders
        Internals::rehash_seed
unknowns

おー、あるある結構ある。

SvREFCNTってのがあるね。名前からして参照カウントの変更ができるってこと?

my $foo = 10;

say Internals::SvREFCNT($foo); # 1

my $bar = \$foo;

say Internals::SvREFCNT($foo); # 2

say Internals::SvREFCNT($foo,1); # 参照カウントを1に変更!!

__END__

1
2
1
Attempt to free unreferenced scalar: SV 0x19da5a8, Perl interpreter: 0x274754

おお、ちゃんと確認&変更ができる!

ただしなにやら怪しげなエラーが出るね。参照カウントが0になったのに参照カウントを減らそうとしてエラーがでてるのかな多分。まあ無理矢理いじってるわけだから当然か。

もしかしてScalar::Util::weakenの代わりに使えるかとも思ったけどさずがに無理っぽいね。くわばらくわばら。

HvREHASHってのはなんだろう?ハッシュテーブルの再構築?でも何の意味があるんだろう。ハッシュテーブルの拡張ならともかく。


・・・まーとりあえず関係ないけど早くShibuya.pm#9 の動画を公開しました - techtalk.jpを全部見たい。まだ一つしか見れてないorz。

もしかしてInternals::系の話もあったりしたのかな?うおおお兎にも角にもはやく見たいぞおおお!



追記

ただし、これではscalarしかflagをいじれません。というわけで、同様のことをXSでやるようにするモジュールを書いたというわけです。

これは間違いでARRAYもHASHも弄れます。

YappoLogs: Internals::SvREADONLY

とのこと。

にゃるほど、思考停止してたお。