はじめに
Flash Pro の名前が Animate に変わり、急にタイムラインが騒がしくなった12月ですが、その話題は池田さんがまじめなエントリを書かれていますのでそちらにお任せしたいと思います。
この記事はこれからも増えていくだろうまじめな記事の箸休め程度に見てください。
この記事を通して「技術カテゴリの Advent Calendar ってこの程度の記事でもいいんだ」と Advent Calendar の敷居が下がって、他の方も参加していただけたら幸いです。
最近忙しくなってしまい(主に某イカのゲームで)全くブログを書いていないので、リハビリも兼ねてゆるーく参加してみようと思います。
内容はずいぶん昔に作って公開はしたけど数人しか見てないと思われる BZPeeper というライブラリについて解説します。
BZPeeper とは?
読み方は「ビーズピーパー」と呼んでいます。(ゲートキーパーなどと同じ音程です)
タイトルにある通り「誰も得しない」と思われる自作ライブラリです。
カオスしか生まない、privateを呼び出す覗き見ライブラリ BZPeeper (デバッグプレイヤーのみ) 。むしゃくしゃして公開した。反省はしない。説明もしない。 http://t.co/GK121UT5
— bkzen (@bkzen) 2012, 11月 30
このツイートにもあるように、 BZPeeper を使うと private な関数を呼び出したり private な値を書き換えたり、とにかく絶対そんなことしないよ。という事ができます。
反省も説明もしない予定でしたが、3年の月日が経った今、ついに解説をしようと思います。
残念ながらソースはどっかに行ってしまいましたが特に問題ないでしょう。(おそらく旧 PC のハードディスクと共にデジタルの海に消えました。)
使い方
例としてこのようなクラスを用意しましょう。
class Test
{
private var moja: String = "hoge hoge";
function Test() { }
private function hoge(): void
{
trace(moja);
}
}
見たとおりで Test クラスはとてもシンプルなクラスです。
このようなクラスのインスタンスを外側から操作したいと思います。
見てわかる通り hoge()
を呼ぶことができれば hoge hoge と言う呪文を出力することができます。
しかし、hoge()
は private なメソッドで、外から呼ぶことはできません。
そんな時は BZPeeper!
BZPeeper を使用すればそんなメソッドも呼び出すことができます。
使い方は簡単。
var t: Test = new Test();
var hoge: Function = BZPeeper.peek(t, "hoge");
hoge(); // hoge hoge
このように簡単に別クラスからでも private なメソッドへの参照を取り出すことができます。
では今度は hoge hoge ではなく、moja moja と出力してみましょう。
しかし、 hoge()
でアクセスしている moja
は private なパラメータです。
private では外から書き換えることができません。
でもご安心ください!
BZPeeper を使えば簡単に書き換えることが可能です。
var t: Test = new Test();
var hoge: Function = BZPeeper.peek(t, "hoge");
hoge(); // hoge hoge
BZPeeper.write(t, "moja", "moja moja");
hoge(); // moja moja
使い方はこんな感じです。
他には全てのメンバ名を取得する BZPeeper.allPickupNames()
などもあります。
コレを使えばビルドインクラスに隠されたメソッドなども発見できたりします。
解説
さて、深夜の通販みたいなノリになってしまいましたが、このライブラリの中では一体何が行われているのでしょうか。
BZPeeper の中は実は単純で、以下のようなことが行われています。
var o: Object = flash.sampler.getMemberNames(target);
for each ( var item: QName in o)
{
if (item.localName == memberName)
{
return target[item];
}
}
BZPeeper の中身を思い出しながら書いてみました。
このコードの中に flash.sampler.getMemberNames()
という見覚えのない関数があります。
この flash.sampler.getMemberNames()
は指定されたオブジェクトを解析し、プライベートを含む全てのメンバ名を返してくれる、本来プロファイラやデバッガなどで使用される特殊な関数です。
ここで返されたメンバ名は String ではなく QName というクラスのインスタンスになります。
QName もあまり目にすることがないクラスではないでしょうか。
QName はローカル名と名前空間をセットで持つクラスで、主に XML で使用されます。
XML や名前空間などと聞いて身構える人もいると思いますが、ここでは大雑把に「名前と住所のセット」と思ってください。
とても大雑把なので正しい表現とは言えませんがここで説明すると長くなるので勘弁してください。
普段 public なメンバにアクセスする際に以下のように記述していますが
target.hoge();
target["hoge"]();
以下のように記述することも可能です。
target.public::hoge();
target.public::["hoge"]();
コレを先ほどの大雑把な例えに当てはめると target の 「public という住所の hoge という名前」のメンバにアクセスしていることになります。アクセスする際、public は全てを有効範囲とするので省略しても問題がなかったわけです。
同じように名前空間(ここでは othernamespace としています)で有効範囲を定義されたメソッドにアクセスする例を見てみましょう。
use namespace othernamespace;
target.moja();
target["moja"]();
先ほどの public とは違い、アクセスする側が othernamespace 内の有効範囲にいないので othernamespace を使用することを1行目で記述してからアクセスしています。
もちろん public の時と同様に以下のように記述することもできます。
target.othernamespace::moja();
target.othernamespace::["moja"]();
use namespace
を使うやり方も、 ::
(名前空間エイリアス修飾子)を使うやり方も、target の「 othernamespace という住所の moja という名前のメンバ」にアクセスしていました。
さらに前述した通り、QName は 「名前と住所(ローカル名と名前空間)をセットで持つオブジェクト」でした。
つまり、othernamespace や moja は、以下のように置き換えることができます。
var qname: QName = new QName(othernamespace, "moja");
target[qname]();
とてもすっきりした書き方になりました。
では private で定義されたメンバはどうでしょうか?
private はその名の通りで、私的な有効範囲を定義するアクセス修飾子です。
例えるなら住所も名前も非公開な状態です。
そこで flash.sampler.getMemberNames()
というタウンページを使用して全ての住所と名前を取得するわけです。
後は、取得した「住所と名前」(QName) でプライベートなメンバにアクセスをして、書き換えたり参照を取得したりしています。
解説してみればとても単純ですね。
最後に
どうでしたか?明日使えそうですか?使い道がありましたか?
ありましたらぜひ教えてください。多分使いません。
QName を使ったメンバへのアクセスは、この時(ライブラリ公開時)のちょっと前まで知りませんでした。
この flash.sampler.getMemberNames()
を見つけ、実験して遊んでいる時に発見しました。
一見無駄に見えるこのライブラリにも実は学べることはあったのです。(?)
flash.sampler
のパッケージにはまだまだ面白そうな関数が残されています。
リファレンスには未だに使ったことがないクラス等がいっぱいあります。
普段使わない所に目を向けてみてください。とても面白い発見が隠されているかもしれません。
そして、それを使っておかしなことをするのはもっと楽しいと思いませんか?
あまり元気のない Flash 界隈ですが Flash はまだまだ面白いです。
皆さんも変なことをしてみてください。