abcdumpを移植してみた
tamarinに付属のabcdumpを移植してみました。
package
{
import flash.display.Sprite;
import flash.text.TextField;
import flash.utils.ByteArray;
import flash.utils.Endian;
[SWF(width=465, height=465, backgroundColor=0xffffff, frameRate=60)]
public class FlashTest extends Sprite
{
public function FlashTest()
{
var tf:TextField = new TextField();
tf.width = 465;
tf.height = 465;
tf.multiline = true;
addChild(tf);
//このSWFのバイナリ取得
var bytes:ByteArray = loaderInfo.bytes;
bytes.endian = Endian.LITTLE_ENDIAN;
var abcdump:String = "";
//Flash8以降はFileAttributesタグが先頭に必ずあるので、その位置を検索
for (var i:int = 8; i < bytes.length; i++)
{
if (bytes[i] == 0x44 && bytes[i + 1] == 0x11)
{
bytes.position = i;
break;
}
}
while (true)
{
//タグとタグ長
var code:uint = bytes.readUnsignedShort();
var length:uint = code & 0x3f;
code >>= 6;
//END
if (code === 0)
{
break;
}
//ロングタグ判定
if (length === 0x3f)
{
length = bytes.readInt();
}
//DoABC2の場合だけabcdumpを行う
if (code === 82)
{
var pos:uint = bytes.position;
//フラグを読み飛ばす
bytes.readInt();
//名前を読み飛ばす
while (bytes.readByte() !== 0)
{
}
//abcの内容をコピー
var newBytes:ByteArray = new ByteArray();
newBytes.endian = Endian.LITTLE_ENDIAN;
newBytes.writeBytes(bytes, bytes.position, length - (bytes.position - pos));
//ダンプ
abcdump += main(newBytes) + "\n";
}
else
{
//スキップ
bytes.position += length;
}
}
tf.text = abcdump;
}
}
}
import flash.utils.ByteArray;
const ATTR_final:int = 0x01; // 1=final, 0=virtual
const ATTR_metadata:int = 0x04; // 1=has metadata, 0=no metadata
const ATTR_override:int = 0x02; // 1=override, 0=new
const ATTR_public:int = 0x08; // 1=add public namespace
const CLASS_FLAG_final:int = 0x02;
const CLASS_FLAG_interface:int = 0x04;
const CLASS_FLAG_sealed:int = 0x01;
const CONSTANT_Double:int = 0x06
const CONSTANT_False:int = 0x0A
const CONSTANT_Int:int = 0x03
const CONSTANT_Multiname:int = 0x09 // o.name, ct nsset, ct name
const CONSTANT_MultinameA:int = 0x0E // o.@name, ct attr-name
const CONSTANT_MultinameL:int = 0x1B
const CONSTANT_MultinameLA:int = 0x1C
const CONSTANT_NameL:int = 0x13 // o.[], ns=public implied, rt name
const CONSTANT_NameLA:int = 0x14 // o.@[], ns=public implied, rt attr-name
const CONSTANT_Namespace:int = 0x08
const CONSTANT_NamespaceSet:int = 0x15
const CONSTANT_Null:int = 0x0C
const CONSTANT_PackageInternalNs:int = 0x17
const CONSTANT_PackageNs:int = 0x16
const CONSTANT_PrivateNs:int = 0x05 // non-shared namespace
const CONSTANT_ProtectedNs:int = 0x18
const CONSTANT_Qname:int = 0x07 // o.ns::name, ct ns, ct name
const CONSTANT_QnameA:int = 0x0D // o.@ns::name, ct ns, ct attr-name
const CONSTANT_RTQname:int = 0x0F // o.ns::name, rt ns, ct name
const CONSTANT_RTQnameA:int = 0x10 // o.@ns::name, rt ns, ct attr-name
const CONSTANT_RTQnameL:int = 0x11 // o.ns::[name], rt ns, rt name
const CONSTANT_RTQnameLA:int = 0x12 // o.@ns::[name], rt ns, rt attr-name
const CONSTANT_StaticProtectedNs:int = 0x19
const CONSTANT_StaticProtectedNs2:int = 0x1a
const CONSTANT_True:int = 0x0B
const CONSTANT_TypeName:int = 0x1D
const CONSTANT_UInt:int = 0x04
const CONSTANT_Utf8:int = 0x01
const HAS_OPTIONAL:int = 0x08
const HAS_ParamNames:int = 0x80
const IGNORE_REST:int = 0x10
const NATIVE:int = 0x20
const NEED_ACTIVATION:int = 0x02
const NEED_ARGUMENTS:int = 0x01
const NEED_REST:int = 0x04
const OP_add:int = 0xA0
const OP_add_d:int = 0x9B
const OP_add_i:int = 0xC5
const OP_applytype:int = 0x53
const OP_astype:int = 0x86
const OP_astypelate:int = 0x87
const OP_bitand:int = 0xA8
const OP_bitnot:int = 0x97
const OP_bitor:int = 0xA9
const OP_bitxor:int = 0xAA
const OP_bkpt:int = 0x01
const OP_bkptline:int = 0xF2
const OP_call:int = 0x41
const OP_callinterface:int = 0x4D
const OP_callmethod:int = 0x43
const OP_callproperty:int = 0x46
const OP_callproplex:int = 0x4C
const OP_callpropvoid:int = 0x4F
const OP_callstatic:int = 0x44
const OP_callsuper:int = 0x45
const OP_callsuperid:int = 0x4B
const OP_callsupervoid:int = 0x4E
const OP_coerce:int = 0x80
const OP_coerce_a:int = 0x82
const OP_coerce_b:int = 0x81
const OP_coerce_d:int = 0x84
const OP_coerce_i:int = 0x83
const OP_coerce_o:int = 0x89
const OP_coerce_s:int = 0x85
const OP_coerce_u:int = 0x88
const OP_construct:int = 0x42
const OP_constructprop:int = 0x4A
const OP_constructsuper:int = 0x49
const OP_convert_b:int = 0x76
const OP_convert_d:int = 0x75
const OP_convert_i:int = 0x73
const OP_convert_o:int = 0x77
const OP_convert_s:int = 0x70
const OP_convert_u:int = 0x74
const OP_debug:int = 0xEF
const OP_debugfile:int = 0xF1
const OP_debugline:int = 0xF0
const OP_declocal:int = 0x94
const OP_declocal_i:int = 0xC3
const OP_decrement:int = 0x93
const OP_decrement_i:int = 0xC1
const OP_deleteproperty:int = 0x6A
const OP_deletepropertylate:int = 0x6B
const OP_divide:int = 0xA3
const OP_dup:int = 0x2A
const OP_dxns:int = 0x06
const OP_dxnslate:int = 0x07
const OP_equals:int = 0xAB
const OP_esc_xattr:int = 0x72
const OP_esc_xelem:int = 0x71
const OP_finddef:int = 0x5F
const OP_findproperty:int = 0x5E
const OP_findpropstrict:int = 0x5D
const OP_getdescendants:int = 0x59
const OP_getglobalscope:int = 0x64
const OP_getglobalslot:int = 0x6E
const OP_getlex:int = 0x60
const OP_getlocal:int = 0x62
const OP_getlocal0:int = 0xD0
const OP_getlocal1:int = 0xD1
const OP_getlocal2:int = 0xD2
const OP_getlocal3:int = 0xD3
const OP_getouterscope:int = 0x67
const OP_getproperty:int = 0x66
const OP_getscopeobject:int = 0x65
const OP_getslot:int = 0x6C
const OP_getsuper:int = 0x04
const OP_greaterequals:int = 0xB0
const OP_greaterthan:int = 0xAF
const OP_hasnext:int = 0x1F
const OP_hasnext2:int = 0x32
const OP_ifeq:int = 0x13
const OP_iffalse:int = 0x12
const OP_ifge:int = 0x18
const OP_ifgt:int = 0x17
const OP_ifle:int = 0x16
const OP_iflt:int = 0x15
const OP_ifne:int = 0x14
const OP_ifnge:int = 0x0F
const OP_ifngt:int = 0x0E
const OP_ifnle:int = 0x0D
const OP_ifnlt:int = 0x0C
const OP_ifstricteq:int = 0x19
const OP_ifstrictne:int = 0x1A
const OP_iftrue:int = 0x11
const OP_in:int = 0xB4
const OP_inclocal:int = 0x92
const OP_inclocal_i:int = 0xC2
const OP_increment:int = 0x91
const OP_increment_i:int = 0xC0
const OP_initproperty:int = 0x68
const OP_instanceof:int = 0xB1
const OP_istype:int = 0xB2
const OP_istypelate:int = 0xB3
const OP_jump:int = 0x10
const OP_kill:int = 0x08
const OP_label:int = 0x09
const OP_lessequals:int = 0xAE
const OP_lessthan:int = 0xAD
const OP_lf32:int = 0x38
const OP_lf64:int = 0x39
const OP_li16:int = 0x36
const OP_li32:int = 0x37
const OP_li8:int = 0x35
const OP_lookupswitch:int = 0x1B
const OP_lshift:int = 0xA5
const OP_modulo:int = 0xA4
const OP_multiply:int = 0xA2
const OP_multiply_i:int = 0xC7
const OP_negate:int = 0x90
const OP_negate_i:int = 0xC4
const OP_newactivation:int = 0x57
const OP_newarray:int = 0x56
const OP_newcatch:int = 0x5A
const OP_newclass:int = 0x58
const OP_newfunction:int = 0x40
const OP_newobject:int = 0x55
const OP_nextname:int = 0x1E
const OP_nextvalue:int = 0x23
const OP_nop:int = 0x02
const OP_not:int = 0x96
const OP_pop:int = 0x29
const OP_popscope:int = 0x1D
const OP_pushbyte:int = 0x24
const OP_pushconstant:int = 0x22
const OP_pushdouble:int = 0x2F
const OP_pushfalse:int = 0x27
const OP_pushint:int = 0x2D
const OP_pushnamespace:int = 0x31
const OP_pushnan:int = 0x28
const OP_pushnull:int = 0x20
const OP_pushscope:int = 0x30
const OP_pushshort:int = 0x25
const OP_pushstring:int = 0x2C
const OP_pushtrue:int = 0x26
const OP_pushuint:int = 0x2E
const OP_pushundefined:int = 0x21
const OP_pushwith:int = 0x1C
const OP_returnvalue:int = 0x48
const OP_returnvoid:int = 0x47
const OP_rshift:int = 0xA6
const OP_setglobalslot:int = 0x6F
const OP_setlocal:int = 0x63
const OP_setlocal0:int = 0xD4
const OP_setlocal1:int = 0xD5
const OP_setlocal2:int = 0xD6
const OP_setlocal3:int = 0xD7
const OP_setproperty:int = 0x61
const OP_setpropertylate:int = 0x69
const OP_setslot:int = 0x6D
const OP_setsuper:int = 0x05
const OP_sf32:int = 0x3D
const OP_sf64:int = 0x3E
const OP_si16:int = 0x3B
const OP_si32:int = 0x3C
const OP_si8:int = 0x3A
const OP_strictequals:int = 0xAC
const OP_subtract:int = 0xA1
const OP_subtract_i:int = 0xC6
const OP_swap:int = 0x2B
const OP_sxi1:int = 0x50
const OP_sxi16:int = 0x52
const OP_sxi8:int = 0x51
const OP_throw:int = 0x03
const OP_typeof:int = 0x95
const OP_urshift:int = 0xA7
const TAB:String = " "
const TRAIT_Class:int = 0x04
const TRAIT_Const:int = 0x06
const TRAIT_Function:int = 0x05
const TRAIT_Getter:int = 0x02
const TRAIT_Method:int = 0x01
const TRAIT_Setter:int = 0x03
const TRAIT_Slot:int = 0x00
var buf:Array = [];
const constantKinds:Array = ["0", "utf8", "2",
"int", "uint", "private", "double", "qname", "namespace",
"multiname", "false", "true", "null", "@qname", "@multiname", "rtqname",
"@rtqname", "[qname]", "@[qname]", "[name]", "@[name]", "nsset"]
const opNames:Array = ["OP_0x00 ", "bkpt ", "nop ", "throw ", "getsuper ", "setsuper ", "dxns ", "dxnslate ",
"kill ", "label ", "OP_0x0A ", "OP_0x0B ", "ifnlt ", "ifnle ", "ifngt ", "ifnge ",
"jump ", "iftrue ", "iffalse ", "ifeq ", "ifne ", "iflt ", "ifle ", "ifgt ",
"ifge ", "ifstricteq ", "ifstrictne ", "lookupswitch ", "pushwith ", "popscope ", "nextname ", "hasnext ",
"pushnull ", "pushundefined ", "pushconstant ", "nextvalue ", "pushbyte ", "pushshort ", "pushtrue ", "pushfalse ",
"pushnan ", "pop ", "dup ", "swap ", "pushstring ", "pushint ", "pushuint ", "pushdouble ",
"pushscope ", "pushnamespace ", "hasnext2 ", "OP_0x33 ", "OP_0x34 ", "li8 ", "li16 ", "li32 ",
"lf32 ", "lf64 ", "si8 ", "si16 ", "si32 ", "sf32 ", "sf64 ", "OP_0x3F ",
"newfunction ", "call ", "construct ", "callmethod ", "callstatic ", "callsuper ", "callproperty ", "returnvoid ",
"returnvalue ", "constructsuper", "constructprop ", "callsuperid ", "callproplex ", "callinterface ", "callsupervoid ", "callpropvoid ",
"sxi1 ", "sxi8 ", "sxi16 ", "applytype ", "OP_0x54 ", "newobject ", "newarray ", "newactivation ",
"newclass ", "getdescendants", "newcatch ", "OP_0x5B ", "OP_0x5C ", "findpropstrict", "findproperty ", "finddef ",
"getlex ", "setproperty ", "getlocal ", "setlocal ", "getglobalscope", "getscopeobject", "getproperty ", "getouterscope ",
"initproperty ", "OP_0x69 ", "deleteproperty", "OP_0x6B ", "getslot ", "setslot ", "getglobalslot ", "setglobalslot ",
"convert_s ", "esc_xelem ", "esc_xattr ", "convert_i ", "convert_u ", "convert_d ", "convert_b ", "convert_o ",
"checkfilter ", "OP_0x79 ", "OP_0x7A ", "OP_0x7B ", "OP_0x7C ", "OP_0x7D ", "OP_0x7E ", "OP_0x7F ",
"coerce ", "coerce_b ", "coerce_a ", "coerce_i ", "coerce_d ", "coerce_s ", "astype ", "astypelate ",
"coerce_u ", "coerce_o ", "OP_0x8A ", "OP_0x8B ", "OP_0x8C ", "OP_0x8D ", "OP_0x8E ", "OP_0x8F ",
"negate ", "increment ", "inclocal ", "decrement ", "declocal ", "typeof ", "not ", "bitnot ",
"OP_0x98 ", "OP_0x99 ", "OP_0x9A ", "add_d ", "OP_0x9C ", "OP_0x9D ", "OP_0x9E ", "OP_0x9F ",
"add ", "subtract ", "multiply ", "divide ", "modulo ", "lshift ", "rshift ", "urshift ",
"bitand ", "bitor ", "bitxor ", "equals ", "strictequals ", "lessthan ", "lessequals ", "greaterthan ",
"greaterequals ", "instanceof ", "istype ", "istypelate ", "in ", "OP_0xB5 ", "OP_0xB6 ", "OP_0xB7 ",
"OP_0xB8 ", "OP_0xB9 ", "OP_0xBA ", "OP_0xBB ", "OP_0xBC ", "OP_0xBD ", "OP_0xBE ", "OP_0xBF ",
"increment_i ", "decrement_i ", "inclocal_i ", "declocal_i ", "negate_i ", "add_i ", "subtract_i ", "multiply_i ",
"OP_0xC8 ", "OP_0xC9 ", "OP_0xCA ", "OP_0xCB ", "OP_0xCC ", "OP_0xCD ", "OP_0xCE ", "OP_0xCF ",
"getlocal0 ", "getlocal1 ", "getlocal2 ", "getlocal3 ", "setlocal0 ", "setlocal1 ", "setlocal2 ", "setlocal3 ",
"OP_0xD8 ", "OP_0xD9 ", "OP_0xDA ", "OP_0xDB ", "OP_0xDC ", "OP_0xDD ", "OP_0xDE ", "OP_0xDF ",
"OP_0xE0 ", "OP_0xE1 ", "OP_0xE2 ", "OP_0xE3 ", "OP_0xE4 ", "OP_0xE5 ", "OP_0xE6 ", "OP_0xE7 ",
"OP_0xE8 ", "OP_0xE9 ", "OP_0xEA ", "OP_0xEB ", "OP_0xEC ", "OP_0xED ", "OP_0xEE ", "debug ",
"debugline ", "debugfile ", "bkptline ", "timestamp ", "OP_0xF4 ", "OP_0xF5 ", "OP_0xF6 ", "OP_0xF7 ",
"OP_0xF8 ", "OP_0xF9 ", "OP_0xFA ", "OP_0xFB ", "OP_0xFC ", "OP_0xFD ", "OP_0xFE ", "OP_0xFF "];
var opSizes:Array = new Array(256);
var totalSize:int;
const traitKinds:Array = ["var", "function", "function get", "function set", "class", "function", "const"];
function dumpPrint(s:String):void
{
print(s);
}
function infoPrint(s:String):void
{
print("// " + s);
}
function main(bytes:ByteArray):String
{
buf = [];
totalSize = 0;
new Abc(bytes).dump();
return buf.join("\n");
}
function print(s:String):void
{
buf[buf.length] = s;
}
class Multiname
{
public var name:String
public var nsset:Array
public function Multiname(nsset:Array, name:String)
{
this.nsset = nsset;
this.name = name;
}
public function toString():String
{
return name;
}
}
class TypeName
{
public var name:*;
public var types:Array;
public function TypeName(name:*, types:Array)
{
this.name = name;
this.types = types;
}
public function toString():String
{
var s:String = name.toString();
s += ".<";
for (var i:int = 0; i < types.length; ++i)
{
s += types[i] != null ? types[i].toString() : "*" + " ";
}
s += ">";
return s;
}
}
dynamic class MetaData
{
public var name:String;
public function toString():String
{
var last:String;
var s:String = last = '[' + name + '(';
var n:String;
for (n in this)
{
s = (last = s + n + "=" + '"' + this[n] + '"') + ',';
}
return last + ')]'
}
}
class MemberInfo
{
public var id:int;
public var kind:int;
public var metadata:Array;
public var name:*;
}
dynamic class LabelInfo
{
public var count:int;
public function labelFor(target:int):String
{
if (target in this)
{
return this[target];
}
return this[target] = "L" + (++count);
}
}
class MethodInfo extends MemberInfo
{
public var activation:Traits;
public var code:ByteArray;
public var code_length:uint;
public var debugName:*;
public var dumped:Boolean;
public var flags:int;
public var local_count:int;
public var max_scope:int;
public var max_stack:int;
public var method_id:int;
public var optionalValues:Array;
public var paramTypes:Array;
public var returnType:*;
public function dump(abc:Abc, indent:String, attr:String = ""):void
{
dumped = true;
dumpPrint("");
if (metadata)
{
for each (var md:MetaData in metadata)
{
dumpPrint(indent + md);
}
}
var s:String = ""
if (flags & NATIVE)
{
s = "native "
}
s += traitKinds[kind] + " ";
dumpPrint(indent + attr + s + format());
if (code)
{
dumpPrint(indent + "{");
var oldindent:String = indent;
indent += TAB;
if (flags & NEED_ACTIVATION)
{
dumpPrint(indent + "activation {");
activation.dump(abc, indent + TAB, "");
dumpPrint(indent + "}");
}
dumpPrint(indent + "// local_count=" + local_count + " max_scope=" + max_scope + " max_stack=" + max_stack + " code_len=" + code.length);
if (code.length === 145)
{
trace(123);
}
code.position = 0;
var labels:LabelInfo = new LabelInfo();
while (code.bytesAvailable > 0)
{
var start:int = code.position;
s = indent + start;
while (s.length < 12)
{
s += ' ';
}
var opcode:uint = code.readUnsignedByte();
if (opcode == OP_label || ((code.position - 1) in labels))
{
dumpPrint(indent);
dumpPrint(indent + labels.labelFor(code.position - 1) + ": ");
}
s += opNames[opcode];
s += opNames[opcode].length < 8 ? "\t\t" : "\t";
switch (opcode)
{
case OP_debugfile:
case OP_pushstring:
s += '"' + abc.strings[readU32()].replace(/\n/g, "\\n").replace(/\t/g, "\\t") + '"';
break
case OP_pushnamespace:
s += abc.namespaces[readU32()];
break
case OP_pushint:
var i:int = abc.ints[readU32()];
s += i + "\t// 0x" + i.toString(16);
break
case OP_pushuint:
var u:uint = abc.uints[readU32()];
s += u + "\t// 0x" + u.toString(16);
break;
case OP_pushdouble:
s += abc.doubles[readU32()];
break;
case OP_getsuper:
case OP_setsuper:
case OP_getproperty:
case OP_initproperty:
case OP_setproperty:
case OP_getlex:
case OP_findpropstrict:
case OP_findproperty:
case OP_finddef:
case OP_deleteproperty:
case OP_istype:
case OP_coerce:
case OP_astype:
case OP_getdescendants:
s += abc.names[readU32()];
break;
case OP_constructprop:
case OP_callproperty:
case OP_callproplex:
case OP_callsuper:
case OP_callsupervoid:
case OP_callpropvoid:
s += abc.names[readU32()];
s += " (" + readU32() + ")";
break;
case OP_newfunction:
{
var method_id:uint = readU32();
s += abc.methods[method_id];
break;
}
case OP_callstatic:
s += abc.methods[readU32()];
s += " (" + readU32() + ")";
break;
case OP_newclass:
s += abc.instances[readU32()];
break;
case OP_lookupswitch:
var pos:int = code.position - 1;
var target:int = pos + readS24();
var maxindex:uint = readU32();
s += "default:" + labels.labelFor(target); // target + "("+(target-pos)+")"
s += " maxcase:" + maxindex;
for (i = 0; i <= maxindex; i++)
{
target = pos + readS24();
s += " " + labels.labelFor(target); // target + "("+(target-pos)+")"
}
break;
case OP_jump:
case OP_iftrue:
case OP_iffalse:
case OP_ifeq:
case OP_ifne:
case OP_ifge:
case OP_ifnge:
case OP_ifgt:
case OP_ifngt:
case OP_ifle:
case OP_ifnle:
case OP_iflt:
case OP_ifnlt:
case OP_ifstricteq:
case OP_ifstrictne:
var offset:int = readS24();
target = code.position + offset;
//s += target + " ("+offset+")"
s += labels.labelFor(target);
if (!((code.position) in labels))
{
s += "\n";
}
break;
case OP_inclocal:
case OP_declocal:
case OP_inclocal_i:
case OP_declocal_i:
case OP_getlocal:
case OP_kill:
case OP_setlocal:
case OP_debugline:
case OP_getglobalslot:
case OP_getslot:
case OP_setglobalslot:
case OP_setslot:
case OP_pushshort:
case OP_newcatch:
s += readU32();
break;
case OP_debug:
s += code.readUnsignedByte();
s += " " + readU32();
s += " " + code.readUnsignedByte();
s += " " + readU32();
break;
case OP_newobject:
s += "{" + readU32() + "}";
break;
case OP_newarray:
s += "[" + readU32() + "]";
break;
case OP_call:
case OP_construct:
case OP_constructsuper:
case OP_applytype:
s += "(" + readU32() + ")";
break;
case OP_pushbyte:
case OP_getscopeobject:
s += code.readByte();
break;
case OP_hasnext2:
s += readU32() + " " + readU32();
default:
/*if (opNames[opcode] == ("0x"+opcode.toString(16).toUpperCase()))
s += " UNKNOWN OPCODE"*/
break;
}
var size:int = code.position - start;
totalSize += size;
opSizes[opcode] = int(opSizes[opcode]) + size;
dumpPrint(s);
}
dumpPrint(oldindent + "}\n");
}
}
public function format():String
{
var name:String = this.name ? this.name : "function";
return name + "(" + paramTypes + "):" + returnType + "\t/* disp_id=" + id + " method_id=" + method_id + " */";
}
public function readS24():int
{
var b:int = code.readUnsignedByte();
b |= code.readUnsignedByte() << 8;
b |= code.readByte() << 16;
return b;
}
public function readU32():int
{
var result:int = code.readUnsignedByte();
if (!(result & 0x00000080))
{
return result;
}
result = result & 0x0000007f | code.readUnsignedByte() << 7;
if (!(result & 0x00004000))
{
return result;
}
result = result & 0x00003fff | code.readUnsignedByte() << 14;
if (!(result & 0x00200000))
{
return result;
}
result = result & 0x001fffff | code.readUnsignedByte() << 21;
if (!(result & 0x10000000))
{
return result;
}
return result & 0x0fffffff | code.readUnsignedByte() << 28;
}
public function toString():String
{
return format();
}
}
class SlotInfo extends MemberInfo
{
public var type:*;
public var value:*;
public function dump(abc:Abc, indent:String, attr:String = ""):void
{
var md:MetaData;
if (kind == TRAIT_Const || kind == TRAIT_Slot)
{
if (metadata)
{
for each (md in metadata)
{
dumpPrint(indent + md);
}
}
dumpPrint(indent + attr + format());
return
}
// else, class
var ct:Traits = value;
var it:Traits = ct.itraits;
dumpPrint('');
if (metadata)
{
for each (md in metadata)
{
dumpPrint(indent + md);
}
}
var def:String;
if (it.flags & CLASS_FLAG_interface)
{
def = "interface";
}
else
{
def = "class";
if (!(it.flags & CLASS_FLAG_sealed))
{
def = "dynamic " + def;
}
if (it.flags & CLASS_FLAG_final)
{
def = "final " + def;
}
}
dumpPrint(indent + attr + def + " " + name + " extends " + it.base);
var oldindent:* = indent;
indent += TAB;
if (it.interfaces.length > 0)
{
dumpPrint(indent + "implements " + it.interfaces);
}
dumpPrint(oldindent + "{");
it.init.dump(abc, indent);
it.dump(abc, indent);
ct.dump(abc, indent, "static ");
ct.init.dump(abc, indent, "static ");
dumpPrint(oldindent + "}\n");
}
public function format():String
{
return traitKinds[kind] + " " + name + ":" + type + (value !== undefined ? (" = " + (value is String ? ('"' + value + '"') : value)) : "") + "\t/* slot_id " + id + " */";
}
}
class Traits
{
public var base:*;
public var flags:int;
public var init:MethodInfo;
public const interfaces:Array = [];
public var itraits:Traits;
public const members:Array = [];
public const methods:Array = [];
public var name:*;
public const names:Object = {};
public var protectedNs:Namespace;
public const slots:Array = [];
public function dump(abc:Abc, indent:String, attr:String = ""):void
{
for each (var m:* in members)
{
m.dump(abc, indent, attr);
}
}
public function toString():String
{
return String(name);
}
}
class Abc
{
public var anyNs:Namespace = new Namespace("*");
public var classes:Array;
public var defaults:Array = new Array(constantKinds.length)
public var doubles:Array;
public var instances:Array;
public var ints:Array;
public var magic:int;
public var major:int;
public var metadata:Array;
public var methods:Array;
public var minor:int;
public var names:Array;
public var namespaces:Array;
public var nssets:Array;
public var publicNs:Namespace = new Namespace("");
public var scripts:Array;
public var strings:Array;
public var uints:Array;
private var data:ByteArray;
public function Abc(data:ByteArray)
{
data.position = 0;
this.data = data;
magic = data.readInt();
infoPrint("magic " + magic.toString(16));
if (magic != (46 << 16 | 14) && magic != (46 << 16 | 15) && magic != (46 << 16 | 16))
{
throw new Error("not an abc file. magic=" + magic.toString(16));
}
parseCpool();
defaults[CONSTANT_Utf8] = strings;
defaults[CONSTANT_Int] = ints;
defaults[CONSTANT_UInt] = uints;
defaults[CONSTANT_Double] = doubles;
defaults[CONSTANT_Int] = ints;
defaults[CONSTANT_False] = {10: false};
defaults[CONSTANT_True] = {11: true};
defaults[CONSTANT_Namespace] = namespaces;
defaults[CONSTANT_PrivateNs] = namespaces;
defaults[CONSTANT_PackageNs] = namespaces;
defaults[CONSTANT_PackageInternalNs] = namespaces;
defaults[CONSTANT_ProtectedNs] = namespaces;
defaults[CONSTANT_StaticProtectedNs] = namespaces;
defaults[CONSTANT_StaticProtectedNs2] = namespaces;
defaults[CONSTANT_Null] = {12: null};
parseMethodInfos();
parseMetadataInfos();
parseInstanceInfos();
parseClassInfos();
parseScriptInfos();
parseMethodBodies();
}
public function dump(indent:String = ""):void
{
for each (var t:* in scripts)
{
dumpPrint("// " + indent + t.name);
t.dump(this, indent);
t.init.dump(this, indent);
}
for each (var m:* in methods)
{
if (!m.dumped)
{
m.dump(this, indent);
}
}
infoPrint("OPCODE\tSIZE\t% OF " + totalSize)
var done:Array = [];
for (; ; )
{
var max:int = -1;
var maxsize:int = 0;
for (var i:int = 0; i < 256; i++)
{
if (opSizes[i] > maxsize && !done[i])
{
max = i;
maxsize = opSizes[i];
}
}
if (max == -1)
{
break;
}
done[max] = 1;
infoPrint(opNames[max] + "\t" + int(opSizes[max]) + "\t" + int(100 * opSizes[max] / totalSize) + "%");
}
}
public function parseClassInfos():void
{
var start:int = data.position;
var count:int = instances.length;
classes = [];
for (var i:int = 0; i < count; i++)
{
var t:Traits = classes[i] = new Traits();
t.init = methods[readU32()];
t.base = "Class";
t.itraits = instances[i];
t.name = t.itraits.name + "$";
t.init.name = t.itraits.name + "$cinit";
t.init.kind = TRAIT_Method;
parseTraits(t);
}
infoPrint("ClassInfo count " + count + " size " + (data.position - start) + " " + int(100 * (data.position - start) / data.length) + "%");
}
public function parseCpool():void
{
var i:int, j:int;
var n:int;
var kind:int;
var start:int = data.position;
// ints
n = readU32();
ints = [0];
for (i = 1; i < n; i++)
{
ints[i] = readU32();
}
// uints
n = readU32();
uints = [0];
for (i = 1; i < n; i++)
{
uints[i] = uint(readU32());
}
// doubles
n = readU32();
doubles = [NaN];
for (i = 1; i < n; i++)
{
doubles[i] = data.readDouble();
}
infoPrint("Cpool numbers size " + (data.position - start) + " " + int(100 * (data.position - start) / data.length) + " %");
start = data.position;
// strings
n = readU32();
strings = [""];
for (i = 1; i < n; i++)
{
strings[i] = data.readUTFBytes(readU32());
}
infoPrint("Cpool strings count " + n + " size " + (data.position - start) + " " + int(100 * (data.position - start) / data.length) + " %");
start = data.position;
// namespaces
n = readU32();
namespaces = [publicNs];
for (i = 1; i < n; i++)
{
switch (data.readByte())
{
case CONSTANT_Namespace:
case CONSTANT_PackageNs:
case CONSTANT_PackageInternalNs:
case CONSTANT_ProtectedNs:
case CONSTANT_StaticProtectedNs:
case CONSTANT_StaticProtectedNs2:
{
namespaces[i] = new Namespace(strings[readU32()]);
// todo mark kind of namespace.
break;
}
case CONSTANT_PrivateNs:
readU32();
namespaces[i] = new Namespace(null, "private");
break;
}
}
infoPrint("Cpool namespaces count " + n + " size " + (data.position - start) + " " + int(100 * (data.position - start) / data.length) + " %");
start = data.position;
// namespace sets
n = readU32();
nssets = [null];
for (i = 1; i < n; i++)
{
var count:int = readU32();
var nsset:Array = nssets[i] = [];
for (j = 0; j < count; j++)
{
nsset[j] = namespaces[readU32()];
}
}
infoPrint("Cpool nssets count " + n + " size " + (data.position - start) + " " + int(100 * (data.position - start) / data.length) + " %");
start = data.position;
// multinames
n = readU32();
names = [null];
namespaces[0] = anyNs;
strings[0] = "*"; // any name
for (i = 1; i < n; i++)
{
switch (data.readByte())
{
case CONSTANT_Qname:
case CONSTANT_QnameA:
names[i] = new QName(namespaces[readU32()], strings[readU32()]);
break;
case CONSTANT_RTQname:
case CONSTANT_RTQnameA:
names[i] = new QName(strings[readU32()]);
break;
case CONSTANT_RTQnameL:
case CONSTANT_RTQnameLA:
names[i] = null;
break;
case CONSTANT_NameL:
case CONSTANT_NameLA:
names[i] = new QName(new Namespace(""), null);
break;
case CONSTANT_Multiname:
case CONSTANT_MultinameA:
var name:* = strings[readU32()];
names[i] = new Multiname(nssets[readU32()], name);
break;
case CONSTANT_MultinameL:
case CONSTANT_MultinameLA:
names[i] = new Multiname(nssets[readU32()], null);
break;
case CONSTANT_TypeName:
name = names[readU32()];
count = readU32();
var types:* = [];
for (var t:int = 0; t < count; ++t)
{
types.push(names[readU32()]);
}
names[i] = new TypeName(name, types);
break;
default:
throw new Error("invalid kind " + data[data.position - 1]);
}
}
infoPrint("Cpool names count " + n + " size " + (data.position - start) + " " + int(100 * (data.position - start) / data.length) + " %");
start = data.position;
namespaces[0] = publicNs;
strings[0] = "*";
}
public function parseInstanceInfos():void
{
var start:int = data.position;
var count:int = readU32();
instances = [];
for (var i:int = 0; i < count; i++)
{
var t:* = instances[i] = new Traits();
t.name = names[readU32()];
t.base = names[readU32()];
t.flags = data.readByte();
if (t.flags & 8)
{
t.protectedNs = namespaces[readU32()]
}
var interface_count:* = readU32();
for (var j:int = 0; j < interface_count; j++)
{
t.interfaces[j] = names[readU32()];
}
var m:* = t.init = methods[readU32()];
m.name = t.name;
m.kind = TRAIT_Method;
m.id = -1;
parseTraits(t);
}
infoPrint("InstanceInfo count " + count + " size " + (data.position - start) + " " + int(100 * (data.position - start) / data.length) + " %");
}
public function parseMetadataInfos():void
{
var count:int = readU32();
metadata = [];
for (var i:int = 0; i < count; i++)
{
// MetadataInfo
var m:* = metadata[i] = new MetaData();
m.name = strings[readU32()];
var values_count:int = readU32();
var names:Array = [];
var q:int;
for (q = 0; q < values_count; ++q)
{
names[q] = strings[readU32()]; // name
}
for (q = 0; q < values_count; ++q)
{
m[names[q]] = strings[readU32()]; // value
}
}
}
public function parseMethodBodies():void
{
var start:int = data.position;
var count:int = readU32();
for (var i:int = 0; i < count; i++)
{
var m:* = methods[readU32()];
m.max_stack = readU32();
m.local_count = readU32();
var initScopeDepth:* = readU32();
var maxScopeDepth:* = readU32();
m.max_scope = maxScopeDepth - initScopeDepth;
var code_length:* = readU32();
m.code = new ByteArray();
m.code.endian = "littleEndian";
if (code_length > 0)
{
data.readBytes(m.code, 0, code_length);
}
var ex_count:* = readU32()
for (var j:int = 0; j < ex_count; j++)
{
var from:* = readU32();
var to:* = readU32();
var target:* = readU32();
var type:* = names[readU32()];
//print("magic " + magic.toString(16))
//if (magic >= (46<<16|16))
var name:* = names[readU32()];
}
parseTraits(m.activation = new Traits);
}
infoPrint("MethodBodies count " + count + " size " + (data.position - start) + " " + int(100 * (data.position - start) / data.length) + " %");
}
public function parseMethodInfos():void
{
var start:int = data.position;
names[0] = new QName(publicNs, "*");
var method_count:int = readU32();
methods = [];
for (var i:int = 0; i < method_count; i++)
{
var m:* = methods[i] = new MethodInfo();
m.method_id = i;
var param_count:int = readU32();
m.returnType = names[readU32()];
m.paramTypes = [];
var k:int;
for (var j:int = 0; j < param_count; j++)
{
m.paramTypes[j] = names[readU32()];
}
m.debugName = strings[readU32()];
m.flags = data.readByte();
if (m.flags & HAS_OPTIONAL)
{
// has_optional
var optional_count:int = readU32();
m.optionalValues = [];
for (k = param_count - optional_count; k < param_count; ++k)
{
var index:* = readU32(); // optional value index
var kind:int = data.readByte(); // kind byte for each default value
if (index == 0)
{
// kind is ignored, default value is based on type
m.optionalValues[k] = undefined;
}
else
{
if (!defaults[kind])
{
print("ERROR kind=" + kind + " method_id " + i);
}
else
{
m.optionalValues[k] = defaults[kind][index];
}
}
}
}
if (m.flags & HAS_ParamNames)
{
// has_paramnames
for (k = 0; k < param_count; ++k)
{
readU32()
}
}
}
infoPrint("MethodInfo count " + method_count + " size " + (data.position - start) + " " + int(100 * (data.position - start) / data.length) + " %")
}
public function parseScriptInfos():void
{
var start:int = data.position;
var count:int = readU32();
scripts = [];
for (var i:int = 0; i < count; i++)
{
var t:Traits = new Traits();
scripts[i] = t;
t.name = "script" + i;
t.base = names[0];
t.init = methods[readU32()];
t.init.name = t.name + "$init";
t.init.kind = TRAIT_Method;
parseTraits(t);
}
infoPrint("ScriptInfo size " + (data.position - start) + " " + int(100 * (data.position - start) / data.length) + " %");
}
public function parseTraits(t:Traits):void
{
var namecount:* = readU32();
for (var i:int = 0; i < namecount; i++)
{
var name:* = names[readU32()];
var tag:* = data.readByte();
var kind:* = tag & 0xf;
var member:*;
switch (kind)
{
case TRAIT_Slot:
case TRAIT_Const:
case TRAIT_Class:
var slot:* = member = new SlotInfo();
slot.id = readU32();
t.slots[slot.id] = slot;
if (kind == TRAIT_Slot || kind == TRAIT_Const)
{
slot.type = names[readU32()];
var index:* = readU32();
if (index)
{
slot.value = defaults[data.readByte()][index];
}
}
else // (kind == TRAIT_Class)
{
slot.value = classes[readU32()];
}
break;
case TRAIT_Method:
case TRAIT_Getter:
case TRAIT_Setter:
var disp_id:* = readU32();
var method:* = member = methods[readU32()];
t.methods[disp_id] = method;
method.id = disp_id;
//print("\t",traitKinds[kind],name,disp_id,method,"// disp_id", disp_id)
break;
}
if (!member)
{
print("error trait kind " + kind)
}
member.kind = kind;
member.name = name;
t.names[String(name)] = t.members[i] = member;
if ((tag >> 4) & ATTR_metadata)
{
member.metadata = [];
for (var j:int = 0, mdCount:int = readU32(); j < mdCount; ++j)
{
member.metadata[j] = metadata[readU32()]
}
}
}
}
public function readU32():int
{
var result:int = data.readUnsignedByte();
if (!(result & 0x00000080))
{
return result;
}
result = result & 0x0000007f | data.readUnsignedByte() << 7;
if (!(result & 0x00004000))
{
return result;
}
result = result & 0x00003fff | data.readUnsignedByte() << 14;
if (!(result & 0x00200000))
{
return result;
}
result = result & 0x001fffff | data.readUnsignedByte() << 21;
if (!(result & 0x10000000))
{
return result;
}
return result & 0x0fffffff | data.readUnsignedByte() << 28;
}
}