Bug 591908: Remove Math.floor from microbenchmark iterations/sec calculation (r=fklockii)
[tamarin-stm.git] / utils / abcdump.as
blob5b225aeed5b5fcfd92869e3b6e5ec71ea872ab42
1 /* -*- indent-tabs-mode: nil; tab-width: 4 -*- */
2 /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is [Open Source Virtual Machine.].
18 * The Initial Developer of the Original Code is
19 * Adobe System Incorporated.
20 * Portions created by the Initial Developer are Copyright (C) 2004-2006
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Adobe AS3 Team
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 package abcdump
42 import flash.utils.ByteArray
43 import avmplus.System
44 import avmplus.File
46 include "abc-constants.as"
48 var usage = [
49 "Displays the contents of abc or swf files",
50 "",
51 "Usage:",
52 "",
53 " abcdump [options] file ...",
54 "",
55 "Each file can be an ABC or SWF/SWC format file",
56 "",
57 "Options:",
58 "",
59 " -a Extract the ABC blocks from the SWF/SWC, but do not",
60 " otherwise display their contents. The file names are",
61 " of the form file<n>.abc where \"file\" is the name",
62 " of the input file minus the .swf/.swc extension;",
63 " and <n> is omitted if it is 0.",
64 "",
65 " -i Print information about the ABC, but do not dump the byte code.",
66 "",
67 " -abs Print the bytecode, but no information about the ABC",
68 " -api Print the public API exposed by this abc/swf",
69 " -mdversions Use in conjunction with -api when the abc/swf uses old-style versioning",
70 " -pools Print out the contents of the constant pools"
71 ].join("\n");
73 const TAB = " "
75 var totalSize:int
76 var opSizes:Array = new Array(256)
78 function dumpPrint(s) {
79 if (doExtractAbs)
80 print(s);
83 function infoPrint(s) {
84 if (doExtractInfo)
85 print((doExtractAbs ? "// " : "") + s)
88 function toStringNull(x) {
89 return (x == null) ? "<null>" : x.toString()
92 // keep track of old-style versioning metadata in a global stack
93 var currentVersionMetadata:Array = new Array()
94 var apiVersionNames:Array = ["9", "air1", "10", "air1.5", "air1.5.1",
95 "10.0.32", "air1.5.2", "10.1", "air2",
96 "fpsys", "airsys"]
98 function getVersionMetadata():Array {
99 if (currentVersionMetadata.length > 0)
100 return currentVersionMetadata[currentVersionMetadata.length - 1]
102 return []
105 function pushVersionMetadata(md:Array):Boolean {
106 if(md == null)
107 return false
109 var vers:Array = []
110 for each(var m in md) {
111 if(m.name == "Version")
112 for each(var t:Tuple in m.tuples)
113 vers.push(t.value)
114 if(m.name == "API")
115 for each(var t:Tuple in m.tuples)
116 vers.push(currentVersionMetadata[int(t.value) - 660])
119 if(vers.length > 0) {
120 currentVersionMetadata.push(vers)
121 return true
123 return false
126 function popVersionMetadata(cond:Boolean):void {
127 if(cond)
128 currentVersionMetadata.pop()
131 class ABCNamespace
133 public var kind:int
134 public var uri:String
135 public var apiVersions:Array = new Array()
137 public function ABCNamespace(ns:String, k:int = 0x08 /* CONSTANT_Namespace */) {
138 kind = k
139 uri = ns == null ? null : stripVersioningChars(ns)
142 public function clone():ABCNamespace {
143 var ns:ABCNamespace = new ABCNamespace(uri, kind)
144 ns.apiVersions = apiVersions.concat()
145 return ns
148 function stripVersioningChars(s:String) {
149 var c:int = s.charCodeAt(s.length-1)
150 if(c > 0xE000) {
151 if(c > 0xE294)
152 apiVersions.push(apiVersionNames[c - 0xE294])
153 return stripVersioningChars(s.slice(0,s.length-1))
155 return s
158 public function toString(useMD:Boolean = false):String {
159 var vers:Array = useMD ? getVersionMetadata() : apiVersions
160 return (uri == null ? "" : uri) + (vers.length > 0 && doDumpAPI ? ("[api: " + vers.join(',') + "]") : "") // + ("(" + constantKinds[kind] + ")")
163 public function isHidden():Boolean {
164 return (kind == CONSTANT_PrivateNs || kind == CONSTANT_PackageInternalNs)
168 class QualifiedName
170 public var ns:ABCNamespace
171 public var localname:String
173 public function QualifiedName(n:ABCNamespace, ln:String) {
174 ns = n
175 localname = ln
178 public function toString(useMD:Boolean = false):String {
179 var nsstr = (ns == null ? "" : ns.toString(useMD))
180 return (nsstr == "" ? "" : nsstr + "::") + localname
183 public function isHidden():Boolean {
184 return ns.isHidden()
188 class Multiname
190 var nsset:Array
191 var name:String
192 function Multiname(nsset:Array, name:String)
194 this.nsset = nsset
195 this.name = name
198 public function toString(useMD:Boolean = false)
200 if (nsset.length == 1)
201 return (new QualifiedName(nsset[0], name).toString(useMD))
202 else
203 return '{' + joinNsset(nsset, useMD) + '}::' + toStringNull(name)
206 function joinNsset(nsset:Array, useMD:Boolean):String {
207 var s:String = ""
208 for each (var ns:ABCNamespace in nsset) {
209 s += (ns.toString(useMD) + ", ")
211 return s
214 public function isHidden():Boolean {
215 for each(var ns in nsset)
216 if (ns.isHidden())
217 return true
218 return false
221 public static function createMultiname(nsset:Array, name:String, flatten:Boolean) {
222 if(flatten) {
223 var cur:ABCNamespace = nsset[0].clone()
224 for(var i:int=1; i<nsset.length; i++) {
225 var ns:ABCNamespace = nsset[i]
226 if (cur.uri == ns.uri) {
227 for each(var v:String in ns.apiVersions)
228 if(cur.apiVersions.indexOf(v) == -1)
229 cur.apiVersions.push(v)
230 } else {
231 cur = null
232 break
236 if(cur)
237 return new QualifiedName(cur, name)
239 return new Multiname(nsset, name)
243 class TypeName
245 var name;
246 var types:Array;
247 function TypeName(name, types)
249 this.name = name;
250 this.types = types;
253 public function toString()
255 var s : String = name.toString();
256 s += ".<"
257 for( var i = 0; i < types.length; ++i )
258 s += types[i] != null ? types[i].toString() : "*" + " ";
259 s += ">"
260 return s;
264 class Tuple
266 public var key:String
267 public var value:String
269 function Tuple(k:String, v:String) {
270 key = k
271 value = v
274 public function toString() {
275 return key + "=" + value
279 class MetaData
281 public var name:String
282 public var tuples:Vector.<Tuple> = new Vector.<Tuple>()
284 public function toString():String
286 var last:String
287 var s:String = last = '['+name+'('
288 for each (var t:Tuple in tuples)
289 s = (last = s + t.key + "=" + '"' + t.value + '"') + ','
290 return last + ')]'
293 public function addPair(k:String, v:String) {
294 tuples.push(new Tuple(k, v))
298 class MemberInfo
300 var id:int
301 var kind:int
302 var name
303 var metadata:Array
306 dynamic class LabelInfo
308 var count:int
309 function labelFor (target:int):String
311 if (target in this)
312 return this[target]
313 return this[target] = "L" + (++count)
317 class ExceptionInfo
319 var from:int
320 var to:int
321 var target:int
322 var type
323 var name
326 class MethodInfo extends MemberInfo
328 var method_id:int
329 var dumped:Boolean
330 var flags:int
331 var debugName
332 var paramTypes
333 var optionalValues
334 var returnType
335 var local_count:int
336 var max_scope:int
337 var max_stack:int
338 var code_offset:uint
339 var code_length:uint
340 var code:ByteArray
341 var exceptions // ExceptionInfo[]
342 var activation:Traits
344 public function toString():String
346 return format()
349 public function format():String
351 var name = this.name ? (this.name is String ? this.name : this.name.toString(useMetadataVersions)) : "function"
353 return name + "(" + paramTypes + "):" + returnType + (doDumpAPI ? "" : "\t/* disp_id=" + id + " method_id=" + method_id + " */")
356 function dump(abc:Abc, indent:String, attr:String="")
358 if(doDumpAPI && name && (name is String || name.isHidden()))
359 return;
361 dumped = true
362 dumpPrint("")
364 if (metadata && !doDumpAPI) {
365 for each (var md in metadata)
366 dumpPrint(indent+md)
369 var mdpushed:Boolean = pushVersionMetadata(metadata)
371 var s:String = ""
372 if (flags & NATIVE && !doDumpAPI)
373 s = "native "
374 s += traitKinds[kind] + " "
376 dumpPrint(indent+attr+s+format())
377 if (code && !doDumpAPI)
379 dumpPrint(indent+"{")
380 var oldindent = indent
381 indent += TAB
382 if (flags & NEED_ACTIVATION) {
383 dumpPrint(indent+"activation {")
384 activation.dump(abc, indent+TAB, "")
385 dumpPrint(indent+"}")
387 dumpPrint(indent+"// local_count="+local_count+
388 " max_scope=" + max_scope +
389 " max_stack=" + max_stack +
390 " framesize=" + (local_count + max_scope + max_stack) +
391 " code_len=" + code.length +
392 " code_offset=" + code_offset)
393 code.position = 0
394 var labels:LabelInfo = new LabelInfo()
395 while (code.bytesAvailable > 0)
397 var start:int = code.position
398 var s = indent + start
399 while (s.length < 12) s += ' ';
400 var opcode = code.readUnsignedByte()
402 if (opcode == OP_label || ((code.position-1) in labels)) {
403 dumpPrint(indent)
404 dumpPrint(indent + labels.labelFor(code.position-1) + ": ")
407 s += opNames[opcode]
408 s += opNames[opcode].length < 8 ? "\t\t" : "\t"
410 switch(opcode)
412 case OP_debugfile:
413 case OP_pushstring:
414 s += '"' + abc.strings[readU32()].replace(/\n/g,"\\n").replace(/\t/g,"\\t") + '"'
415 break
416 case OP_pushnamespace:
417 s += abc.namespaces[readU32()]
418 break
419 case OP_pushint:
420 var i:int = abc.ints[readU32()]
421 s += i + "\t// 0x" + i.toString(16)
422 break
423 case OP_pushuint:
424 var u:uint = abc.uints[readU32()]
425 s += u + "\t// 0x" + u.toString(16)
426 break;
427 case OP_pushdouble:
428 s += abc.doubles[readU32()]
429 break;
430 case OP_getsuper:
431 case OP_setsuper:
432 case OP_getproperty:
433 case OP_initproperty:
434 case OP_setproperty:
435 case OP_getlex:
436 case OP_findpropstrict:
437 case OP_findproperty:
438 case OP_finddef:
439 case OP_deleteproperty:
440 case OP_istype:
441 case OP_coerce:
442 case OP_astype:
443 case OP_getdescendants:
444 s += abc.names[readU32()]
445 break;
446 case OP_constructprop:
447 case OP_callproperty:
448 case OP_callproplex:
449 case OP_callsuper:
450 case OP_callsupervoid:
451 case OP_callpropvoid:
452 s += abc.names[readU32()]
453 s += " (" + readU32() + ")"
454 break;
455 case OP_newfunction: {
456 var method_id = readU32()
457 s += abc.methods[method_id]
458 break;
460 case OP_callstatic:
461 s += abc.methods[readU32()]
462 s += " (" + readU32() + ")"
463 break;
464 case OP_newclass:
465 s += abc.instances[readU32()]
466 break;
467 case OP_lookupswitch:
468 var pos = code.position-1;
469 var target = pos + readS24()
470 var maxindex = readU32()
471 s += "default:" + labels.labelFor(target) // target + "("+(target-pos)+")"
472 s += " maxcase:" + maxindex
473 for (var i:int=0; i <= maxindex; i++) {
474 target = pos + readS24();
475 s += " " + labels.labelFor(target) // target + "("+(target-pos)+")"
477 break;
478 case OP_jump:
479 case OP_iftrue: case OP_iffalse:
480 case OP_ifeq: case OP_ifne:
481 case OP_ifge: case OP_ifnge:
482 case OP_ifgt: case OP_ifngt:
483 case OP_ifle: case OP_ifnle:
484 case OP_iflt: case OP_ifnlt:
485 case OP_ifstricteq: case OP_ifstrictne:
486 var offset = readS24()
487 var target = code.position+offset
488 //s += target + " ("+offset+")"
489 s += labels.labelFor(target)
490 if (!((code.position) in labels))
491 s += "\n"
492 break;
493 case OP_inclocal:
494 case OP_declocal:
495 case OP_inclocal_i:
496 case OP_declocal_i:
497 case OP_getlocal:
498 case OP_kill:
499 case OP_setlocal:
500 case OP_bkptline:
501 case OP_debugline:
502 case OP_getglobalslot:
503 case OP_getslot:
504 case OP_setglobalslot:
505 case OP_setslot:
506 case OP_pushshort:
507 case OP_newcatch:
508 case OP_getouterscope:
509 s += readU32()
510 break
511 case OP_debug:
512 s += code.readUnsignedByte()
513 s += " " + readU32()
514 s += " " + code.readUnsignedByte()
515 s += " " + readU32()
516 break;
517 case OP_newobject:
518 s += "{" + readU32() + "}"
519 break;
520 case OP_newarray:
521 s += "[" + readU32() + "]"
522 break;
523 case OP_call:
524 case OP_construct:
525 case OP_constructsuper:
526 case OP_applytype:
527 s += "(" + readU32() + ")"
528 break;
529 case OP_pushbyte:
530 case OP_getscopeobject:
531 s += code.readByte()
532 break;
533 case OP_hasnext2:
534 s += readU32() + " " + readU32()
535 default:
536 /*if (opNames[opcode] == ("0x"+opcode.toString(16).toUpperCase()))
537 s += " UNKNOWN OPCODE"*/
538 break
540 var size:int = code.position - start
541 totalSize += size
542 opSizes[opcode] = int(opSizes[opcode]) + size
543 dumpPrint(s)
545 if (exceptions) {
546 for each (var ex in exceptions)
547 dumpPrint(indent + "// handler [" + ex.from + ", " + ex.to + "] -> " + ex.target +
548 (ex.name ? (" " + ex.name + ":" + ex.type) : (" :" + ex.type)));
550 dumpPrint(oldindent+"}\n")
553 popVersionMetadata(mdpushed)
556 function readU32():int
558 var result:int = code.readUnsignedByte();
559 if (!(result & 0x00000080))
560 return result;
561 result = result & 0x0000007f | code.readUnsignedByte()<<7;
562 if (!(result & 0x00004000))
563 return result;
564 result = result & 0x00003fff | code.readUnsignedByte()<<14;
565 if (!(result & 0x00200000))
566 return result;
567 result = result & 0x001fffff | code.readUnsignedByte()<<21;
568 if (!(result & 0x10000000))
569 return result;
570 return result & 0x0fffffff | code.readUnsignedByte()<<28;
573 function readS24():int
575 var b:int = code.readUnsignedByte()
576 b |= code.readUnsignedByte()<<8
577 b |= code.readByte()<<16
578 return b
582 class SlotInfo extends MemberInfo
584 var type
585 var value
586 public function format():String
588 return traitKinds[kind] + " " + name.toString(useMetadataVersions) + ":" + type +
589 (value !== undefined ? (" = " + (value is String ? ('"'+value+'"') : value)) : "") +
590 (doDumpAPI ? "" : "\t/* slot_id " + id + " */")
592 function dump(abc:Abc, indent:String, attr:String="")
594 if(doDumpAPI && name.isHidden())
595 return
597 var mdpushed:Boolean = pushVersionMetadata(metadata)
599 if (kind == TRAIT_Const || kind == TRAIT_Slot)
601 if (metadata && !doDumpAPI) {
602 for each (var md in metadata)
603 dumpPrint(indent+md)
605 dumpPrint(indent+attr+format())
606 popVersionMetadata(mdpushed)
607 return
610 // else, class
611 var ct:Traits = value
612 var it:Traits = ct.itraits
613 dumpPrint('')
614 if (metadata && !doDumpAPI) {
615 for each (var md in metadata)
616 dumpPrint(indent+md)
618 var def:String;
619 if (it.flags & CLASS_FLAG_interface)
620 def = "interface"
621 else {
622 def = "class";
623 if (!(it.flags & CLASS_FLAG_sealed))
624 def = "dynamic " + def;
625 if (it.flags & CLASS_FLAG_final)
626 def = "final " + def;
629 dumpPrint(indent+attr+def+" "+name.toString(useMetadataVersions)+" extends "+it.base)
630 var oldindent = indent
631 indent += TAB
632 if (it.interfaces.length > 0)
633 dumpPrint(indent+"implements "+it.interfaces)
635 if(doDumpAPI) {
636 var prefix:String = indent+attr+def+" "+name.toString(useMetadataVersions)+" "
637 it.init.dump(abc,prefix)
638 it.dump(abc,indent,prefix)
639 ct.dump(abc,indent,prefix + "static ")
640 ct.init.dump(abc,indent,prefix + "static ")
641 } else {
642 dumpPrint(oldindent+"{")
643 it.init.dump(abc,indent)
644 it.dump(abc,indent)
645 ct.dump(abc,indent,"static ")
646 ct.init.dump(abc,indent,"static ")
647 dumpPrint(oldindent+"}\n")
650 popVersionMetadata(mdpushed)
654 class Traits
656 var name
657 var init:MethodInfo
658 var itraits:Traits
659 var base
660 var flags:int
661 var protectedNs:ABCNamespace
662 const interfaces:Array = []
663 const names:Object = {}
664 const slots:Array = []
665 const methods:Array = []
666 const members:Array = []
668 public function toString():String
670 return String(name)
673 public function dump(abc:Abc, indent:String, attr:String="")
675 for each (var m in members)
676 m.dump(abc, indent, attr)
680 class Abc
682 private var data:ByteArray
684 var major:int
685 var minor:int
687 var ints:Array
688 var uints:Array
689 var doubles:Array
690 var strings:Array
691 var namespaces:Array
692 var nssets:Array
693 var names:Array
695 var defaults:Array = new Array(constantKinds.length)
697 var methods:Array
698 var instances:Array
699 var classes:Array
700 var scripts:Array
701 var metadata:Array
703 var publicNs = new ABCNamespace("")
704 var anyNs = new ABCNamespace("*")
706 var magic:int
708 function Abc(data:ByteArray)
710 data.position = 0
711 this.data = data
712 magic = data.readInt()
714 infoPrint("magic " + magic.toString(16))
716 if (magic != (46<<16|14) && magic != (46<<16|15) && magic != (46<<16|16))
717 throw new Error("not an abc file. magic=" + magic.toString(16))
719 parseCpool()
721 defaults[CONSTANT_Utf8] = strings
722 defaults[CONSTANT_Int] = ints
723 defaults[CONSTANT_UInt] = uints
724 defaults[CONSTANT_Double] = doubles
725 defaults[CONSTANT_Int] = ints
726 defaults[CONSTANT_False] = { 10:false }
727 defaults[CONSTANT_True] = { 11:true }
728 defaults[CONSTANT_Namespace] = namespaces
729 defaults[CONSTANT_PrivateNs] = namespaces
730 defaults[CONSTANT_PackageNs] = namespaces
731 defaults[CONSTANT_PackageInternalNs] = namespaces
732 defaults[CONSTANT_ProtectedNs] = namespaces
733 defaults[CONSTANT_StaticProtectedNs] = namespaces
734 defaults[CONSTANT_StaticProtectedNs2] = namespaces
735 defaults[CONSTANT_Null] = { 12: null }
737 parseMethodInfos()
738 parseMetadataInfos()
739 parseInstanceInfos()
740 parseClassInfos()
741 parseScriptInfos()
742 parseMethodBodies()
744 if (doExtractAbc==true)
745 File.writeByteArray(nextAbcFname(), data);
748 function readU32():int
750 var result:int = data.readUnsignedByte();
751 if (!(result & 0x00000080))
752 return result;
753 result = result & 0x0000007f | data.readUnsignedByte()<<7;
754 if (!(result & 0x00004000))
755 return result;
756 result = result & 0x00003fff | data.readUnsignedByte()<<14;
757 if (!(result & 0x00200000))
758 return result;
759 result = result & 0x001fffff | data.readUnsignedByte()<<21;
760 if (!(result & 0x10000000))
761 return result;
762 return result & 0x0fffffff | data.readUnsignedByte()<<28;
765 function dumpPool(name:String, pool:Array)
767 if(!doDumpPools)
768 return
770 for(var i:int = 0; i<pool.length; i++)
771 infoPrint(name + "[" + i + "] = " + pool[i])
774 function padString(s:String, l:uint)
776 if(s.length < l)
777 return padString(s + " ", l)
778 return s
781 function parseCpool()
783 var i:int, j:int
784 var n:int
785 var kind:int
787 var start:int = data.position
789 // ints
790 n = readU32()
791 ints = [0]
792 for (i=1; i < n; i++)
793 ints[i] = readU32()
794 dumpPool("int", ints)
796 // uints
797 n = readU32()
798 uints = [0]
799 for (i=1; i < n; i++)
800 uints[i] = uint(readU32())
801 dumpPool("uint", uints)
803 // doubles
804 n = readU32()
805 doubles = [NaN]
806 for (i=1; i < n; i++)
807 doubles[i] = data.readDouble()
808 dumpPool("double", doubles)
810 infoPrint("Cpool numbers size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
811 start = data.position
813 // strings
814 n = readU32()
815 strings = [""]
816 for (i=1; i < n; i++)
817 strings[i] = data.readUTFBytes(readU32())
818 dumpPool("string", strings)
820 infoPrint("Cpool strings count "+ n +" size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
821 start = data.position
823 // namespaces
824 n = readU32()
825 namespaces = [publicNs]
826 var nskind = 0;
827 for (i=1; i < n; i++) {
828 switch (nskind = data.readByte())
830 case CONSTANT_Namespace:
831 case CONSTANT_PackageNs:
832 case CONSTANT_PackageInternalNs:
833 case CONSTANT_ProtectedNs:
834 case CONSTANT_StaticProtectedNs:
835 case CONSTANT_StaticProtectedNs2:
837 namespaces[i] = new ABCNamespace(strings[readU32()], nskind)
838 break;
840 case CONSTANT_PrivateNs:
841 readU32();
842 namespaces[i] = new ABCNamespace("private", nskind)
843 break;
845 if(doDumpPools)
846 infoPrint("namespace[" + i + "] = " + padString(constantKinds[nskind], 20) + ": " + namespaces[i].uri)
849 infoPrint("Cpool namespaces count "+ n +" size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
850 start = data.position
852 // namespace sets
853 n = readU32()
854 nssets = [null]
855 for (i=1; i < n; i++)
857 var count:int = readU32()
858 var nsset = nssets[i] = []
859 var nsids = []
860 for (j=0; j < count; j++) {
861 var nsid = readU32()
862 nsids.push(nsid)
863 nsset[j] = namespaces[nsid]
865 if(doDumpPools)
866 infoPrint("nsset[" + i + "] = {" + nsids.join(", ") + "}")
869 infoPrint("Cpool nssets count "+ n +" size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
870 start = data.position
872 // multinames
873 n = readU32()
874 names = [null]
875 namespaces[0] = anyNs
876 strings[0] = "*" // any name
877 for (i=1; i < n; i++) {
878 var nametype = data.readByte()
879 switch (nametype)
881 case CONSTANT_Qname:
882 case CONSTANT_QnameA:
883 names[i] = new QualifiedName(namespaces[readU32()], strings[readU32()])
884 break;
886 case CONSTANT_RTQname:
887 case CONSTANT_RTQnameA:
888 names[i] = new QualifiedName(null, strings[readU32()])
889 break;
891 case CONSTANT_RTQnameL:
892 case CONSTANT_RTQnameLA:
893 names[i] = null
894 break;
896 case CONSTANT_NameL:
897 case CONSTANT_NameLA:
898 names[i] = new QualifiedName(publicNs, null)
899 break;
901 case CONSTANT_Multiname:
902 case CONSTANT_MultinameA:
903 var name = strings[readU32()]
904 names[i] = Multiname.createMultiname(nssets[readU32()], name, doDumpAPI)
905 break;
907 case CONSTANT_MultinameL:
908 case CONSTANT_MultinameLA:
909 names[i] = Multiname.createMultiname(nssets[readU32()], null, doDumpAPI)
910 break;
912 case CONSTANT_TypeName:
913 var name = names[readU32()];
914 var count = readU32();
915 var types = [];
916 for( var t=0; t < count; ++t )
917 types.push(names[readU32()]);
918 names[i] = new TypeName(name, types);
919 break;
921 default:
922 throw new Error("invalid kind " + data[data.position-1])
924 if(doDumpPools)
925 infoPrint("name[" + i + "] = " + padString(constantKinds[nametype], 12) + ": " + names[i])
928 infoPrint("Cpool names count "+ n +" size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
929 start = data.position
931 namespaces[0] = publicNs
932 strings[0] = "*"
935 function parseMethodInfos()
937 var start:int = data.position
938 names[0] = new QualifiedName(publicNs, "*")
939 var method_count:int = readU32()
940 methods = []
941 for (var i:int=0; i < method_count; i++)
943 var m = methods[i] = new MethodInfo()
944 m.method_id = i
945 var param_count:int = readU32()
946 m.returnType = names[readU32()]
947 m.paramTypes = []
948 for (var j:int=0; j < param_count; j++)
949 m.paramTypes[j] = names[readU32()]
950 m.debugName = strings[readU32()]
951 m.flags = data.readByte()
952 if (m.flags & HAS_OPTIONAL)
954 // has_optional
955 var optional_count:int = readU32();
956 m.optionalValues = []
957 for( var k:int = param_count-optional_count; k < param_count; ++k)
959 var index = readU32() // optional value index
960 var kind:int = data.readByte() // kind byte for each default value
961 if (index == 0)
963 // kind is ignored, default value is based on type
964 m.optionalValues[k] = undefined
966 else
968 if (!defaults[kind])
969 print("ERROR kind="+kind+" method_id " + i)
970 else
971 m.optionalValues[k] = defaults[kind][index]
975 if (m.flags & HAS_ParamNames)
977 // has_paramnames
978 for( var k:int = 0; k < param_count; ++k)
980 readU32();
984 infoPrint("MethodInfo count " +method_count+ " size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
987 function parseMetadataInfos()
989 var start:int = data.position
990 var count:int = readU32()
991 metadata = []
992 for (var i:int=0; i < count; i++)
994 // MetadataInfo
995 var m = metadata[i] = new MetaData()
996 m.name = strings[readU32()];
997 var values_count:int = readU32();
998 var names:Array = []
999 var keys:Array = []
1000 var values:Array = []
1002 for(var q:int = 0; q < values_count; ++q)
1003 keys[q] = strings[readU32()]
1004 for(var q:int = 0; q < values_count; ++q)
1005 values[q] = strings[readU32()]
1007 for(var q:int = 0; q < values_count; ++q)
1008 m.addPair(keys[q], values[q])
1010 if(doDumpPools)
1011 infoPrint("metadata[" + i + "] = {" + m.tuples.join(", ") + "}")
1013 infoPrint("MetadataInfo count " +values_count+ " size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
1016 function parseInstanceInfos()
1018 var start:int = data.position
1019 var count:int = readU32()
1020 instances = []
1021 for (var i:int=0; i < count; i++)
1023 var t = instances[i] = new Traits()
1024 t.name = names[readU32()]
1025 t.base = names[readU32()]
1026 t.flags = data.readByte()
1027 if (t.flags & 8)
1028 t.protectedNs = namespaces[readU32()]
1029 var interface_count = readU32()
1030 for (var j:int=0; j < interface_count; j++)
1031 t.interfaces[j] = names[readU32()]
1032 var m = t.init = methods[readU32()]
1033 m.name = t.name
1034 m.kind = TRAIT_Method
1035 m.id = -1
1036 parseTraits(t)
1038 infoPrint("InstanceInfo count " + count + " size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
1041 function parseTraits(t:Traits)
1043 var namecount = readU32()
1044 for (var i:int=0; i < namecount; i++)
1046 var name = names[readU32()]
1047 var tag = data.readByte()
1048 var kind = tag & 0xf
1049 var member
1050 switch(kind) {
1051 case TRAIT_Slot:
1052 case TRAIT_Const:
1053 case TRAIT_Class:
1054 var slot = member = new SlotInfo()
1055 slot.id = readU32()
1056 t.slots[slot.id] = slot
1057 if (kind==TRAIT_Slot || kind==TRAIT_Const)
1059 slot.type = names[readU32()]
1060 var index=readU32()
1061 if (index)
1062 slot.value = defaults[data.readByte()][index]
1064 else // (kind == TRAIT_Class)
1066 slot.value = classes[readU32()]
1068 break;
1069 case TRAIT_Method:
1070 case TRAIT_Getter:
1071 case TRAIT_Setter:
1072 var disp_id = readU32()
1073 var method = member = methods[readU32()]
1074 t.methods[disp_id] = method
1075 method.id = disp_id
1076 //print("\t",traitKinds[kind],name,disp_id,method,"// disp_id", disp_id)
1077 break;
1079 if (!member)
1080 print("error trait kind "+kind)
1081 member.kind = kind
1082 member.name = name
1083 t.names[String(name)] = t.members[i] = member
1085 if ( (tag >> 4) & ATTR_metadata ) {
1086 member.metadata = []
1087 for(var j:int=0, mdCount:int=readU32(); j < mdCount; ++j)
1088 member.metadata[j] = metadata[readU32()]
1093 function parseClassInfos()
1095 var start:int = data.position
1096 var count:int = instances.length
1097 classes = []
1098 for (var i:int=0; i < count; i++)
1100 var t:Traits = classes[i] = new Traits()
1101 t.init = methods[readU32()]
1102 t.base = "Class"
1103 t.itraits = instances[i]
1104 t.name = t.itraits.name + "$"
1105 t.init.name = t.itraits.name + "$cinit"
1106 t.init.kind = TRAIT_Method
1107 parseTraits(t)
1109 infoPrint("ClassInfo count " + count + " size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+"%")
1112 function parseScriptInfos()
1114 var start:int = data.position
1115 var count:int = readU32()
1116 scripts = []
1117 for (var i:int=0; i < count; i++)
1119 var t = new Traits()
1120 scripts[i] = t
1121 t.name = "script" + i
1122 t.base = names[0] // Object
1123 t.init = methods[readU32()]
1124 t.init.name = t.name + "$init"
1125 t.init.kind = TRAIT_Method
1126 parseTraits(t)
1128 infoPrint("ScriptInfo size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
1131 function parseMethodBodies()
1133 var start:int = data.position
1134 var count:int = readU32()
1135 for (var i:int=0; i < count; i++)
1137 var m = methods[readU32()]
1138 m.max_stack = readU32()
1139 m.local_count = readU32()
1140 var initScopeDepth = readU32()
1141 var maxScopeDepth = readU32()
1142 m.max_scope = maxScopeDepth - initScopeDepth
1143 var code_length = readU32()
1144 m.code = new ByteArray()
1145 m.code.endian = "littleEndian"
1146 if (code_length > 0) {
1147 m.code_offset = data.position;
1148 data.readBytes(m.code, 0, code_length)
1150 var ex_count = readU32()
1151 if (ex_count > 0) {
1152 m.exceptions = []
1153 for (var j:int = 0; j < ex_count; j++)
1155 var ex = new ExceptionInfo()
1156 m.exceptions.push(ex)
1157 ex.from = readU32()
1158 ex.to = readU32()
1159 ex.target = readU32()
1160 ex.type = names[readU32()]
1161 //print("magic " + magic.toString(16))
1162 //if (magic >= (46<<16|16))
1163 ex.name = names[readU32()];
1164 //infoPrint("exception method_id=" + i + " [" + ex.from + ", " + ex.to + "] " + ex.type + " -> " + ex.target)
1167 parseTraits(m.activation = new Traits)
1169 infoPrint("MethodBodies count " + count + " size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
1172 function dump(indent:String="")
1174 for each (var t in scripts)
1176 infoPrint(indent+t.name)
1177 t.dump(this,indent)
1178 t.init.dump(this,indent)
1181 for each (var m in methods)
1183 if (!m.dumped)
1184 m.dump(this,indent)
1187 infoPrint("OPCODE\tSIZE\t% OF "+totalSize)
1188 var done = []
1189 for (;;)
1191 var max:int = -1;
1192 var maxsize:int = 0;
1193 for (var i:int=0; i < 256; i++)
1195 if (opSizes[i] > maxsize && !done[i])
1197 max = i;
1198 maxsize = opSizes[i];
1201 if (max == -1)
1202 break;
1203 done[max] = 1;
1204 infoPrint(opNames[max]+"\t"+int(opSizes[max])+"\t"+int(100*opSizes[max]/totalSize)+"%")
1209 class Rect
1211 var nBits:int
1212 var xMin:int, xMax:int
1213 var yMin:int, yMax:int
1214 public function toString()
1216 return "[Rect "+xMin+" "+yMin+" "+xMax+" "+yMax+"]"
1220 const stagDoABC :int = 72; // embedded .abc (AVM+) bytecode
1221 const stagSymbolClass :int = 76;
1222 const stagMetadata :int = 77;
1223 const stagDoABC2 :int = 82; // revised ABC version with a name
1225 var tagNames:Array = [
1226 "End", // 00
1227 "ShowFrame", // 01
1228 "DefineShape", // 02
1229 "FreeCharacter", // 03
1230 "PlaceObject", // 04
1231 "RemoveObject", // 05
1232 "DefineBits", // 06
1233 "DefineButton", // 07
1234 "JPEGTables", // 08
1235 "SetBackgroundColor", // 09
1237 "DefineFont", // 10
1238 "DefineText", // 11
1239 "DoAction", // 12
1240 "DefineFontInfo", // 13
1242 "DefineSound", // 14
1243 "StartSound", // 15
1244 "StopSound", // 16
1246 "DefineButtonSound", // 17
1248 "SoundStreamHead", // 18
1249 "SoundStreamBlock", // 19
1251 "DefineBitsLossless", // 20
1252 "DefineBitsJPEG2", // 21
1254 "DefineShape2", // 22
1255 "DefineButtonCxform", // 23
1257 "Protect", // 24
1259 "PathsArePostScript", // 25
1261 "PlaceObject2", // 26
1262 "27 (invalid)", // 27
1263 "RemoveObject2", // 28
1265 "SyncFrame", // 29
1266 "30 (invalid)", // 30
1267 "FreeAll", // 31
1269 "DefineShape3", // 32
1270 "DefineText2", // 33
1271 "DefineButton2", // 34
1272 "DefineBitsJPEG3", // 35
1273 "DefineBitsLossless2", // 36
1274 "DefineEditText", // 37
1276 "DefineVideo", // 38
1278 "DefineSprite", // 39
1279 "NameCharacter", // 40
1280 "ProductInfo", // 41
1281 "DefineTextFormat", // 42
1282 "FrameLabel", // 43
1283 "DefineBehavior", // 44
1284 "SoundStreamHead2", // 45
1285 "DefineMorphShape", // 46
1286 "FrameTag", // 47
1287 "DefineFont2", // 48
1288 "GenCommand", // 49
1289 "DefineCommandObj", // 50
1290 "CharacterSet", // 51
1291 "FontRef", // 52
1293 "DefineFunction", // 53
1294 "PlaceFunction", // 54
1296 "GenTagObject", // 55
1298 "ExportAssets", // 56
1299 "ImportAssets", // 57
1301 "EnableDebugger", // 58
1303 "DoInitAction", // 59
1304 "DefineVideoStream", // 60
1305 "VideoFrame", // 61
1307 "DefineFontInfo2", // 62
1308 "DebugID", // 63
1309 "EnableDebugger2", // 64
1310 "ScriptLimits", // 65
1312 "SetTabIndex", // 66
1314 "DefineShape4", // 67
1315 "DefineMorphShape2", // 68
1317 "FileAttributes", // 69
1319 "PlaceObject3", // 70
1320 "ImportAssets2", // 71
1322 "DoABC", // 72
1323 "DefineFontAlignZones", // 73
1324 "CSMSettings", // 74
1325 "DefineFont3", // 75
1326 "SymbolClass", // 76
1327 "Metadata", // 77
1328 "DefineScalingGrid", // 78
1329 "DefineDeviceVideo", // 79
1330 "80 (invalid)", // 80
1331 "81 (invalid)", // 81
1332 "DoABC2", // 82
1333 "DefineShape4", // 83
1334 "DefineMorphShape2", // 84
1335 "PlaceImagePrivate", // 85
1336 "DefineSceneAndFrameLabelData", // 86
1337 "DefineBinaryData", // 87
1338 "DefineFontName", // 88
1339 "StartSound", // 89
1340 "DefineBitsJPEG64", // 90
1341 "DefineFont4", // 91
1345 class Swf
1347 private var bitPos:int
1348 private var bitBuf:int
1350 private var data:ByteArray
1352 function Swf(data:ByteArray)
1354 this.data = data
1355 infoPrint("size "+decodeRect())
1356 infoPrint("frame rate "+(data.readUnsignedByte()<<8|data.readUnsignedByte()))
1357 infoPrint("frame count "+data.readUnsignedShort())
1358 decodeTags()
1361 private function decodeTags()
1363 var type:int, h:int, length:int
1364 var offset:int
1366 while (data.position < data.length)
1368 type = (h = data.readUnsignedShort()) >> 6;
1370 if (((length = h & 0x3F) == 0x3F))
1371 length = data.readInt();
1373 var tagN = tagNames[type]
1374 if (type >= tagNames.length)
1375 tagN = type+" (unknown)"
1377 infoPrint(tagN+" "+length+"b "+int(100*length/data.length)+"%")
1378 switch (type)
1380 case 0: return
1381 case stagDoABC2:
1382 var pos1:int = data.position
1383 data.readInt()
1384 infoPrint("\nabc name "+readString())
1385 length -= (data.position-pos1)
1386 // fall through
1387 case stagDoABC:
1388 var data2 = new ByteArray
1389 data2.endian = "littleEndian"
1390 data.readBytes(data2,0,length)
1391 new Abc(data2).dump(" ")
1392 infoPrint("")
1393 break
1394 case stagMetadata:
1395 infoPrint(new XML(readString()));
1396 break;
1397 default:
1398 data.position += length
1403 private function readString():String
1405 var s:String = ""
1406 var c:int
1408 while (c=data.readUnsignedByte())
1409 s += String.fromCharCode(c)
1411 return s
1414 private function syncBits()
1416 bitPos = 0
1419 private function decodeRect():Rect
1421 syncBits();
1423 var rect:Rect = new Rect();
1425 var nBits:int = readUBits(5)
1426 rect.xMin = readSBits(nBits);
1427 rect.xMax = readSBits(nBits);
1428 rect.yMin = readSBits(nBits);
1429 rect.yMax = readSBits(nBits);
1431 return rect;
1434 function readSBits(numBits:int):int
1436 if (numBits > 32)
1437 throw new Error("Number of bits > 32");
1439 var num:int = readUBits(numBits);
1440 var shift:int = 32-numBits;
1441 // sign extension
1442 num = (num << shift) >> shift;
1443 return num;
1446 function readUBits(numBits:int):uint
1448 if (numBits == 0)
1449 return 0
1451 var bitsLeft:int = numBits;
1452 var result:int = 0;
1454 if (bitPos == 0) //no value in the buffer - read a byte
1456 bitBuf = data.readUnsignedByte()
1457 bitPos = 8;
1460 while (true)
1462 var shift:int = bitsLeft - bitPos;
1463 if (shift > 0)
1465 // Consume the entire buffer
1466 result |= bitBuf << shift;
1467 bitsLeft -= bitPos;
1469 // Get the next byte from the input stream
1470 bitBuf = data.readUnsignedByte();
1471 bitPos = 8;
1473 else
1475 // Consume a portion of the buffer
1476 result |= bitBuf >> -shift;
1477 bitPos -= bitsLeft;
1478 bitBuf &= 0xff >> (8 - bitPos); // mask off the consumed bits
1480 // if (print) System.out.println(" read"+numBits+" " + result);
1481 return result;
1485 // unreachable, but fixes a spurious compiler warning
1486 return result;
1490 function help()
1492 print(usage);
1493 System.exit(1)
1496 function processArg(arg:String)
1498 if (arg == '-a') {
1499 doExtractAbc = true;
1500 doExtractInfo = false
1501 doExtractAbs = false
1502 } else if (arg == '-i') {
1503 // suppress abs output
1504 doExtractAbs = false;
1505 } else if (arg == '-abs') {
1506 // suppress info output
1507 doExtractInfo = false
1508 } else if (arg == '-api') {
1509 doDumpAPI = true;
1510 doExtractInfo = false
1511 } else if (arg == '-mdversions') {
1512 useMetadataVersions = true
1513 } else if (arg == '-pools') {
1514 doDumpPools = true
1515 } else {
1516 print('Unknown option '+arg)
1517 help()
1521 function nextAbcFname():String
1523 var s = currentFname
1524 if (currentFcount>0)
1525 s = s.concat(currentFcount);
1526 currentFcount++
1527 return s+'.abc'
1530 // main
1531 var doExtractAbc = false
1532 var doExtractInfo = true
1533 var doExtractAbs = true
1534 var doDumpAPI = false
1535 var doDumpPools = false
1536 var useMetadataVersions = false
1537 var currentFname = ''
1538 var currentFcount = 0
1539 for each (var file in System.argv)
1541 if (file.indexOf('-')==0)
1543 processArg(file)
1544 continue
1547 var x;
1548 if ((x = file.lastIndexOf(".swf")) != -1 || (x = file.lastIndexOf(".swc")) != -1)
1549 currentFname = file.substring(0,x);
1550 else
1551 currentFname = file;
1552 var data:ByteArray = File.readByteArray(file)
1553 data.endian = "littleEndian"
1554 var version:uint = data.readUnsignedInt()
1555 switch (version) {
1556 case 46<<16|14:
1557 case 46<<16|15:
1558 case 46<<16|16:
1559 var abc:Abc = new Abc(data)
1560 abc.dump()
1561 break
1562 case 67|87<<8|83<<16|10<<24: // SWC10
1563 case 67|87<<8|83<<16|9<<24: // SWC9
1564 case 67|87<<8|83<<16|8<<24: // SWC8
1565 case 67|87<<8|83<<16|7<<24: // SWC7
1566 case 67|87<<8|83<<16|6<<24: // SWC6
1567 var udata:ByteArray = new ByteArray
1568 udata.endian = "littleEndian"
1569 data.position = 8
1570 data.readBytes(udata,0,data.length-data.position)
1571 var csize:int = udata.length
1572 udata.uncompress()
1573 infoPrint("decompressed swf "+csize+" -> "+udata.length)
1574 udata.position = 0
1575 /*var swf:Swf =*/ new Swf(udata)
1576 break
1577 case 70|87<<8|83<<16|10<<24: // SWF10
1578 case 70|87<<8|83<<16|9<<24: // SWF9
1579 case 70|87<<8|83<<16|8<<24: // SWF8
1580 case 70|87<<8|83<<16|7<<24: // SWF7
1581 case 70|87<<8|83<<16|6<<24: // SWF6
1582 case 70|87<<8|83<<16|5<<24: // SWF5
1583 case 70|87<<8|83<<16|4<<24: // SWF4
1584 data.position = 8 // skip header and length
1585 /*var swf:Swf =*/ new Swf(data)
1586 break
1587 default:
1588 print('unknown format '+version)
1589 break
1593 if (System.argv.length < 1)
1594 help();