« 2007年06月 | メイン | 2008年03月 »

2007年07月12日

Flash: コールスタック

FlashのVMはスタックマシンなので、たとえば演算をする場合は引数をスタックに積んでから演算命令を実行します。この用途で使われるスタック(ここでは値スタックと呼ぶことにしましょう)のほかにも、FlashのVM内で使われているスタックがあります。それは、Function CallやFrame Callをしたときに戻ってくる場所を保持しておく、コールスタックです。
おなじ「スタック」ですが、VM内ではまったく別の場所で独立に管理されているようです。 ですから、コールする側が値をスタックに積んでおいて、コールされた側でその値を読み出したり、 逆に、コールされた側で値をスタックに積んで、Returnした後にその値を読み出す、といったことができます。
以下を実行すると、
function foo () {
  __bytecode__("2696040000420003");
  // trace
  // push "B", undefined
  // pop
}

__bytecode__("96040000410003");
// push "A", undefined
// pop
foo()
__bytecode__("2696010003");
// trace
// push undefined
// pop
画面に、
A
B
が表示されます。foo()内のtraceで、呼び出す前に積んでおいたAが、 foo()から帰ってきた後のtraceでfoo()の中で積んでおいたBが使われます。 関数呼び出しではなく、フレーム呼び出しの場合にも、同様のことができます。
上でちょっとわかりにくいのは、__bytecode__命令で毎回最後にundefined(0x03)をpush しているところかもしれません。 __bytecode__は、x = __bytecode__("...")のようにひとつの値を返す関数のように扱われるので、 値を使わない場合は自動的にpopが挿入されてしまいます。 これによってスタックに積んだ値が捨てられてしまうことがないように、undefinedを pushしているのです。
関数呼び出しの引数や返り値の受け渡しには実際にこれと似たようなことが行われています。 ただし、引数についてはローカル変数へのバインドを行うところが違います。
上に述べたことを応用すると、 フレーム呼び出しでも返り値の受け渡しをスタック経由で行える、ということになります。 ActionScriptの文法の枠からは外れてしまいますが。