abcdumpがネストした関数をダンプしない。

環境

この記事の内容は、Tamarin changeset 583:6582fd7afc59で確認しました。

問題

以下のように、関数の中に関数があった場合、内側にある関数(以下の例ならbar)を、abcdumpがダンプしません。

function foo(): void {
    function bar(): void {
    }
}

対策

以下のパッチを適用します。

--- abcdump.as.orig 2008-06-21 17:32:45.000000000 +0900
+++ abcdump.as  2008-06-21 22:03:21.000000000 +0900
@@ -605,7 +605,6 @@
        var code_length:uint
        var code:ByteArray
        var activation:Traits
-       var anon:Boolean

        public function toString():String
        {
@@ -710,7 +709,6 @@
                        case OP_newfunction: {
                            var method_id = readU32()
                            s += abc.methods[method_id]
-                           abc.methods[method_id].anon = true
                            break;
                        }
                        case OP_callstatic:
@@ -1364,9 +1362,20 @@

            for each (var m in methods)
            {
-               if (m.anon) {
-                   m.dump(this,indent)
+               var method_is_script = false
+               for each (var t in scripts) 
+               {
+                   if (t.init == m) 
+                   {
+                       method_is_script = true
+                       break
+                   }
+               }
+               if (method_is_script) 
+               {
+                   continue
                }
+               m.dump(this, indent)
            }

            print("OPCODE\tSIZE\t% OF "+totalSize)

詳細

abcファイルでは、以下のように、内側にある関数やメソッドほど前に記述されているようです。また、abcdumpも、この順序で関数を配列に保存して管理しています。

 +------------+
0|    bar     |
 +------------+
1|    foo     |
 +------------+
2|トップレベル|
 +------------+

abcdumpは、関数の配列以外の所から参照できるトップレベルのコードを先頭に出力し、その後それ以外の関数(上の例ならfooとbar)を出力します。さて、関数はfooもbarもトップレベルも同じ配列に入っているので、fooとbarを出力するために配列を走査するとき、関数が既に出力されているトップレベルでないかどうかを判断する必要があります。修正前のabcdumpでは、newfunction命令の対象となっている関数にフラグを設定し、その関数がトップレベルでないと判断します。

                        case OP_newfunction: {
                            (略)
                            abc.methods[method_id].anon = true
                            break;
                        }

しかし、この命令の走査は、関数のダンプと同時に実行されます。また関数は、配列の先頭にあるものからダンプしようとします。このため、barがトップレベルの関数でないことを示すフラグは、fooをダンプするときに設定されますが、このときには既にbarをダンプしようとし(たが、フラグが設定されていなかったのでダンプしなかっ)た後であり、結局barはダンプされません。

上のパッチでは、関数がトップレベルでないかどうかの判断を、フラグでなく、データ構造から行うようにします。

付記

id:mzp:20080610:abcdumpの問題も、これで解決できないでしょうか。