[kansaipm] Re: [kansaipm] 暗黙のuse utf8
Dan Kogai
dankogai at dan.co.jp
Sun Oct 5 07:52:44 CDT 2003
On Sunday, Oct 5, 2003, at 18:48 Asia/Tokyo, Yasushi Nakajima wrote:
> ちょうど弾さんが顔を出して下さったので、弾さん向けの(^^)質問を一つ。
>
> 以下、Perl5.8(テストはWindows上の5.8.0でやってます)での話。
>
> いま、PDFJのXMLフロントエンドであるXPDFJが、ようやくリリースに近づいて
> きたのですが、その中で「明示的にuse utf8;していないコードをeval(正確に
> はSafe::reval)するときに、暗黙にuse utf8;されることがある」という事態に
> 突き当たって悩んでいます。どうもutf8::SWASHNEWなんかを呼んでいるようです。
うーん、なかなか高度な奴ですね。encodingもそうですが、pragmaというのは、moduleとして提供されはしていますが、あくまで
pragmatic な役割をすることもあり、この場合はその影響は可能な限りlexical
scopeとして処理されるようになっています。そうでない場合には perldoc に警告文が書いてあります(i.e. encoding).
それで、utf8 pragmaなのですが、これは lexical scope です。有名な strict
もそうです。ですから暗黙に、例えば別moduleがすでに一旦useしている場合が多く、通常はそれを気にする必要は全くないはずなのですが。
> まだ、どんな時に起きるのか、短い再現コードにまで追いつめられていないの
> ですが、実行するコードを少し変えただけで起きたり起きなかったりです。
実際のcodeを見ていないのでなんとも言えないのですが、いづれの場合も utf8.pm はすでに use
されているように見えます。それ自体は多分中嶋さんも問題にはしていないと思いますが、utf8::SWASHNEWというのは、実は utf8.pm
内部ではなく、utf8_heavy.pl という外部scriptに存在しています。
# utf8.pm の全て
package utf8;
$utf8::hint_bits = 0x00800000;
our $VERSION = '1.00';
sub import {
$^H |= $utf8::hint_bits;
$enc{caller()} = $_[1] if $_[1];
}
sub unimport {
$^H &= ~$utf8::hint_bits;
}
sub AUTOLOAD {
require "utf8_heavy.pl";
goto &$AUTOLOAD if defined &$AUTOLOAD;
Carp::croak("Undefined subroutine $AUTOLOAD called");
}
1;
# 本当にこれだけ!
見ての通り、未定義の utf8::function() が呼ばれた時に、 utf8_heavy.pl
が読まれるような仕組みになっていて、具体的には、regex中のUnicodeを扱う時に呼ばれます。普通は
> 安全のためにSafe環境はpermit_only(':default')にして、その中で実行した
> いのですが、そうするとrequireがダメなので、上記の現象が起こると当然エラー
> になります。実行したいコード自体は、変数を使った数式の評価ができればよく、
> 外部とやりとりする機能は殺しておきたいので、requireを許可したくはありま
> せん。
しかし、やはりpragmaは許可をしておかないとこれ以外でも結構大変ですよね。というわけで perldoc Safe
を見てみると、こういう方法で可能にしておくというのは考えられます。
$cpt = new Safe;
sub wrapper {
use utf8 ();
require "utf8_heavy.pl";
}
$cpt->share('&wrapper');
$cpt->reval("wrapper()");
> 質問は、いったいどんな条件の時に上記の現象が起こるのか、抑止する方法が
> あるのかどうか、です。
>
> 書き忘れましたが、実行するコードはXMLデータ中に埋め込まれていて、
> XML::Parserで読み込んでいるので、utf8になった状態でrevalに渡されています。
ああ、多分これだ!状況によってutf8_heavy.plが読み込まれるかどうかというのは、読み込んだXMLデータ中に、<本当の>、すなわち
\x80 よりもordが大きな文字が含まれるかどうかによって決まるわけです。すなわち
$cpt->reval(qq/print "Hello, World\n"/); # OK
$cpt->reval(qq/print "世界にようこそ\n"/); # NG
ですから、これはもう素直に上記のような方法で、先に utf8_heavy.pl をあらかじめ require しておいた方がいいでしょう。
Dan the Camel Vet
More information about the Kansai-pm
mailing list