先のエントリでお伝えした通り、Player10用のFlexSDKが出ていたので、密かに注目度の高い、FlashPlayer10の新機能「Vector」について、分かってることをまとめてみた。
Vectorとは何か
VectorはECMAScript4から先行導入された機能で、一見Arrayと同じですが、全ての要素が同じ型でなければならないという制約が付きます。一般的な言葉で言えば、ジェネリクスです。
Vectorは、次のような新文法で宣言します。
var vec:Vector.<String> = new Vector.<String>();
.<String>という部分で、このVectorに付ける型制約を指定します。この場合、作られたVector変数vecは、Stringのみの配列になります。
いまのところ、Vectorの実体は、__AS3__.vec.Vectorにあります。
Vectorのサンプル
Sparkにコミットしてあります。折角なのでソースコード表示ブログパーツを使って見てみましょう。
ここでは、カスタムクラス(自分で定義したクラス)「A」のVectorを作っています。ご覧の通り、「A」と、Aの派生クラスである「B」はpushできますが、特に関係のない「C」をpushしようとすると、「TypeError」がスローされます。
Vectorの要素数
もうひとつArrayと違う点として、Vectorは連続した配列でなければならない点が挙げられます。例えば、要素数が2の状態で、vec[2]を飛ばしてvec[3]に代入しようとするとエラーが出ます。
また、要素数を固定化させるプロパティfixedが追加されており(コンストラクタの第二引数でも指定可能)、これをtrueにすると(デフォルトはfalse)、要素数を変更出来なくなり、pushやpop等、要素数が変わるアクションをするとエラーが出ます。
Vectorの裏側: Tamarin編
当然のことながら、Vectorのサポートにあたって、Tamarin(AVM2)とFlexSDKの両方に修正が加わっています。Tamarinの方は、チェンジログをVectorで検索(thx: rch850)すると、変更を追うことが出来ます。Vectorクラスの定義はここ(.as)、Vectorクラスの実体はここ(.h)とここ(.cpp)にあります。また、テストはここにあるので、大体の仕様が分かるでしょう。
ABC(ActionScriptByteCode)にも変更が加わっており、まず、A.<B>という型を表現する為の仕様が追加されています。もともと、クラスの完全修飾名を定義する為に、Multinameというものが定数プールに定義されているのですが、そこに新しく、TypeName(kind=29(0x1D))というものが追加されました。こいつは、「QNameの番号, 長さ, [QNameの番号]*長さ」というデータを取ります。「A.<B>」の場合は、「型Aの番号, 1, 型Bの番号」となります。これに伴い、MultinameにもTypeParameterというプロパティが追加されました。現在、長さは1のみがサポートされていますが、将来的にジェネリクスが正式サポートされた時には、「型Aの番号, 2, 型Bの番号, 型Cの番号」で「A.<B, C>」を表現するものと思われます。
次に、new A.<B> を可能にする為に、applytype(code=83(0x53))というインストラクションが追加されました。使い方は単純で、
GetLex A GetLex B ApplyType Construct
というように、クラスAを取得し、Constructを呼び出す前に、クラスBを取得し、ApplyTypeをすることでジェネリクスを有効化させます。実装はInterpreterを見れば分かります。
Vectorの裏側: FlexSDK編
FlexSDKの方はあまり追ってないのですが、 A.<B> のコンパイルが通るように、コンパイラに修正が加わっています。FlexSDKのリポジトリを見れば、大体分かるでしょう。Tamarinの変更と同様、TypeNameのサポート、ApplyTypeのサポート、A.<B>構文の追加等が行われています。
これでいいのかVector: 暗黙的な型変換
どうやら、Vectorは、プリミティブ型を指定した場合は、型が違えどエラーは出さずに、暗黙的な型変換を行うようです。例えば、Vector.<uint>に対してBooleanであるtrueをpushすると、結果として型変換により「1」がpushされます。該当するテストもあるので、仕様として間違いないようです。感覚的には、エラーが出て欲しいのですがどうなのでしょう?
これでいいのかVector: push時にnull
上の方で出したサンプルを確認して頂けると分かるのですが、互換性の無い型をpushしようとしてTypeErrorが出る際、そこまではいいのですが、最終的なVectorを見ると、代わりにnullが入っています。VectorBaseObject::pushで、型チェックをする前に内部の配列をgrowさせているのが原因のようです。感覚的には、エラーが出た時には配列を変更して欲しくない(growより先に型チェックすべき)のですがどうでしょう?
サイト内関連記事
この記事へのトラックバック
トラックバックはありません。
TrackBack URL:
http://www.be-interactive.org/trackback.php?id=369

Vectorの勉強になりましたー。
に関して、まったく同感です。
エラーが発生した際には、エラーが起きる処理を呼び出さなかった状態にして欲しいかなと思いますね。
私も、これは仕様としてどうかと思います。
Vector.<Number>に対して、uintをpushする場合にアップキャストされるという話なら良いと思いますが、逆にVector.<uint>に対してNumberをpushしてダウンキャストされるのは問題ある気がします。
trueが「1」に変換なんて、あり得ないですね。