E4X の変数展開 深追い (2)

続き。変数展開するときに、どういうバイトコードになってるか気になったので調べてみた。

ActionScript のコードはこんなの。

var a = <hoge>{3}</hoge>;

asc でコンパイルして、abcdump でダンプしたらこうなった。

  0         getlocal0
  1         pushscope
  2         findpropstrict      XML
  4         getproperty         XML
  6         pushstring          "<hoge>"
  8         pushbyte            3
  10        esc_xelem
  11        add
  12        pushstring          "</hoge>"
  14        add
  15        construct           (1)
  17        getglobalscope
  18        swap
  19        setslot             1
  21        returnvoid

new String() と比較

new String() するコードと比べてみる。

var a = new String("hoge");

ダンプしたらこうなった。

  0         getlocal0
  1         pushscope
  2         findpropstrict      String
  4         pushstring          "hoge"
  6         constructprop       String (1)
  9         getglobalscope
  10        swap
  11        setslot             1
  13        returnvoid

ふむ、似たような形になってるね。

スタックをつむところだけ抜き出してみる

まずは String のほう。

  4         pushstring          "hoge"
  6         constructprop       String (1)

String は "hoge" をスタックにつんで、コンストラクタを呼び出してる。そのままだ。

対して、XML。

  6         pushstring          "<hoge>"
  8         pushbyte            3
  10        esc_xelem                    
  11        add
  12        pushstring          "</hoge>"
  14        add
  15        construct           (1)

ちょっと複雑なので順番に。

まず、"<hoge>" という文字列をスタックにつむ。

   +----------------+
   |    "<hoge>"    |
---+----------------+---

続けて、3 もスタックにつむ。

   +----------------+
   |       3        |
   +----------------+
   |    "<hoge>"    |
---+----------------+---

次の、esc_xelem はなんだろう。

avm2overview.pdf によると、XML ノードの値を決定するためのオペレーションで、スタックから値を取り出して文字列に変換するらしい。変換には E4X で定義されている ToXmlString() を使うようだ。

ってことで、esc_xelem 実行後はこうなる。

   +----------------+
   |      "3"       |
   +----------------+
   |    "<hoge>"    |
---+----------------+---

続けて、add。スタックから2つを取り出して足し算する。

   +----------------+
   |   "<hoge>3"    |
---+----------------+---

次に、"</hoge>" を push して、add するので最終的には次のようになる。

   +----------------+
   |"<hoge>3</hoge>"|
---+----------------+---

で、ここで construct を呼ぶので、new XML("<hoge>3</hoge>") した結果がスタックに入るわけだ。

まとめ

イメージ的には

var a = <hoge>{3}</hoge>;

var a = new XML("<hoge>"
              + ToXmlString(3)
              + "</hoge>");

というコードに変換される感じ。

{} の中身は ToXmlString() の引数として扱われる、というイメージで捉えておくと、前回の {} の中に無名関数突っ込んだやつが動いたのも納得できる。

実際には、ToXmlString は内部的な関数なので呼び出せないんだけど、あくまでイメージということで。

ところで、なんで急に ABC をやりだしたかというと、みずぴー日記 さんの 関数関連のABC変数関係の命令 という記事を読んで興味が沸いたから。

みずぴーさんは Scheme-abc という ABC を吐く Scheme コンパイラを OCaml で実装していたりする…。すごい!

あ、あと AVM2 の資料については、公式のものより AVM2 Instructions のほうが分かりやすかった。