Merge remote-tracking branch 'redux/master' into sh4-pool
[tamarin-stm.git] / utils / abcdump.as
blob5a98b9ea2caf734aebe82cd12be9ad74275513db
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 " --decompress-only Write out a decompressed version of the swc and exit"
72 ].join("\n");
74 const TAB = " "
76 var totalSize:int
77 var opSizes:Array = new Array(256)
78 var opCounts:Array = new Array(256)
80 function dumpPrint(s) {
81 if (doExtractAbs)
82 print(s);
85 function infoPrint(s) {
86 if (doExtractInfo)
87 print((doExtractAbs ? "// " : "") + s)
90 function toStringNull(x) {
91 return (x == null) ? "<null>" : x.toString()
94 // keep track of old-style versioning metadata in a global stack
95 var currentVersionMetadata:Array = new Array()
96 var apiVersionNames:Array = ["9", "air1", "10", "air1.5", "air1.5.1",
97 "10.0.32", "air1.5.2", "10.1", "air2",
98 "fpsys", "airsys"]
100 function getVersionMetadata():Array {
101 if (currentVersionMetadata.length > 0)
102 return currentVersionMetadata[currentVersionMetadata.length - 1]
104 return []
107 function pushVersionMetadata(md:Array):Boolean {
108 if(md == null)
109 return false
111 var vers:Array = []
112 for each(var m in md) {
113 if(m.name == "Version")
114 for each(var t:Tuple in m.tuples)
115 vers.push(t.value)
116 if(m.name == "API")
117 for each(var t:Tuple in m.tuples)
118 vers.push(currentVersionMetadata[int(t.value) - 660])
121 if(vers.length > 0) {
122 currentVersionMetadata.push(vers)
123 return true
125 return false
128 function popVersionMetadata(cond:Boolean):void {
129 if(cond)
130 currentVersionMetadata.pop()
133 class ABCNamespace
135 public var kind:int
136 public var uri:String
137 public var apiVersions:Array = new Array()
139 public function ABCNamespace(ns:String, k:int = 0x08 /* CONSTANT_Namespace */) {
140 kind = k
141 uri = ns == null ? null : stripVersioningChars(ns)
144 public function clone():ABCNamespace {
145 var ns:ABCNamespace = new ABCNamespace(uri, kind)
146 ns.apiVersions = apiVersions.concat()
147 return ns
150 function stripVersioningChars(s:String) {
151 var c:int = s.charCodeAt(s.length-1)
152 if(c > 0xE000) {
153 if(c > 0xE294)
154 apiVersions.push(apiVersionNames[c - 0xE294])
155 return stripVersioningChars(s.slice(0,s.length-1))
157 return s
160 public function toString(useMD:Boolean = false):String {
161 var vers:Array = useMD ? getVersionMetadata() : apiVersions
162 return (uri == null ? "" : uri) + (vers.length > 0 && doDumpAPI ? ("[api: " + vers.join(',') + "]") : "") // + ("(" + constantKinds[kind] + ")")
165 public function isHidden():Boolean {
166 return (kind == CONSTANT_PrivateNs || kind == CONSTANT_PackageInternalNs)
170 class QualifiedName
172 public var ns:ABCNamespace
173 public var localname:String
175 public function QualifiedName(n:ABCNamespace, ln:String) {
176 ns = n
177 localname = ln
180 public function toString(useMD:Boolean = false):String {
181 var nsstr = (ns == null ? "" : ns.toString(useMD))
182 return (nsstr == "" ? "" : nsstr + "::") + localname
185 public function isHidden():Boolean {
186 return ns.isHidden()
190 class Multiname
192 var nsset:Array
193 var name:String
194 function Multiname(nsset:Array, name:String)
196 this.nsset = nsset
197 this.name = name
200 public function toString(useMD:Boolean = false)
202 if (nsset.length == 1)
203 return (new QualifiedName(nsset[0], name).toString(useMD))
204 else
205 return '{' + joinNsset(nsset, useMD) + '}::' + toStringNull(name)
208 function joinNsset(nsset:Array, useMD:Boolean):String {
209 var s:String = ""
210 for each (var ns:ABCNamespace in nsset) {
211 s += (ns.toString(useMD) + ", ")
213 return s
216 public function isHidden():Boolean {
217 for each(var ns in nsset)
218 if (ns.isHidden())
219 return true
220 return false
223 public static function createMultiname(nsset:Array, name:String, flatten:Boolean) {
224 if(flatten) {
225 var cur:ABCNamespace = nsset[0].clone()
226 for(var i:int=1; i<nsset.length; i++) {
227 var ns:ABCNamespace = nsset[i]
228 if (cur.uri == ns.uri) {
229 for each(var v:String in ns.apiVersions)
230 if(cur.apiVersions.indexOf(v) == -1)
231 cur.apiVersions.push(v)
232 } else {
233 cur = null
234 break
238 if(cur)
239 return new QualifiedName(cur, name)
241 return new Multiname(nsset, name)
245 class TypeName
247 var name;
248 var types:Array;
249 function TypeName(name, types)
251 this.name = name;
252 this.types = types;
255 public function toString()
257 var s : String = name.toString();
258 s += ".<"
259 for( var i = 0; i < types.length; ++i )
260 s += types[i] != null ? types[i].toString() : "*" + " ";
261 s += ">"
262 return s;
266 class Tuple
268 public var key:String
269 public var value:String
271 function Tuple(k:String, v:String) {
272 key = k
273 value = v
276 public function toString() {
277 return key + "=" + value
281 class MetaData
283 public var name:String
284 public var tuples:Vector.<Tuple> = new Vector.<Tuple>()
286 public function toString():String
288 var last:String
289 var s:String = last = '['+name+'('
290 for each (var t:Tuple in tuples)
291 s = (last = s + t.key + "=" + '"' + t.value + '"') + ','
292 return last + ')]'
295 public function addPair(k:String, v:String) {
296 tuples.push(new Tuple(k, v))
300 class MemberInfo
302 var id:int
303 var kind:int
304 var name
305 var metadata:Array
308 dynamic class LabelInfo
310 var count:int
311 function labelFor (target:int):String
313 if (target in this)
314 return this[target]
315 return this[target] = "L" + (++count)
319 class ExceptionInfo
321 var from:int
322 var to:int
323 var target:int
324 var type
325 var name
328 class MethodInfo extends MemberInfo
330 var method_id:int
331 var dumped:Boolean
332 var flags:int
333 var debugName
334 var paramTypes
335 var optionalValues
336 var returnType
337 var local_count:int
338 var max_scope:int
339 var max_stack:int
340 var code_offset:uint
341 var code_length:uint
342 var code:ByteArray
343 var exceptions // ExceptionInfo[]
344 var activation:Traits
346 public function toString():String
348 return format()
351 public function format():String
353 var name = this.name ? (this.name is String ? this.name : this.name.toString(useMetadataVersions)) : "function"
355 return name + "(" + paramTypes + "):" + returnType + (doDumpAPI ? "" : "\t/* disp_id=" + id + " method_id=" + method_id + " */")
358 function dump(abc:Abc, indent:String, attr:String="")
360 if(doDumpAPI && name && (name is String || name.isHidden()))
361 return;
363 dumped = true
364 dumpPrint("")
366 if (metadata && !doDumpAPI) {
367 for each (var md in metadata)
368 dumpPrint(indent+md)
371 var mdpushed:Boolean = pushVersionMetadata(metadata)
373 var s:String = ""
374 if (flags & NATIVE && !doDumpAPI)
375 s = "native "
376 s += traitKinds[kind] + " "
378 dumpPrint(indent+attr+s+format())
379 if (code && !doDumpAPI)
381 dumpPrint(indent+"{")
382 var oldindent = indent
383 indent += TAB
384 if (flags & NEED_ACTIVATION) {
385 dumpPrint(indent+"activation {")
386 activation.dump(abc, indent+TAB, "")
387 dumpPrint(indent+"}")
389 dumpPrint(indent+"// local_count="+local_count+
390 " max_scope=" + max_scope +
391 " max_stack=" + max_stack +
392 " framesize=" + (local_count + max_scope + max_stack) +
393 " code_len=" + code.length +
394 " code_offset=" + code_offset)
395 code.position = 0
396 var labels:LabelInfo = new LabelInfo()
397 while (code.bytesAvailable > 0)
399 var start:int = code.position
400 var s = indent + start
401 while (s.length < 12) s += ' ';
402 var opcode = code.readUnsignedByte()
404 if (opcode == OP_label || ((code.position-1) in labels)) {
405 dumpPrint(indent)
406 dumpPrint(indent + labels.labelFor(code.position-1) + ": ")
409 s += opNames[opcode]
410 s += opNames[opcode].length < 8 ? "\t\t" : "\t"
412 switch(opcode)
414 case OP_debugfile:
415 case OP_pushstring:
416 s += '"' + abc.strings[readU32()].replace(/\n/g,"\\n").replace(/\t/g,"\\t") + '"'
417 break
418 case OP_pushnamespace:
419 s += abc.namespaces[readU32()]
420 break
421 case OP_pushint:
422 var i:int = abc.ints[readU32()]
423 s += i + "\t// 0x" + i.toString(16)
424 break
425 case OP_pushuint:
426 var u:uint = abc.uints[readU32()]
427 s += u + "\t// 0x" + u.toString(16)
428 break;
429 case OP_pushdouble:
430 s += abc.doubles[readU32()]
431 break;
432 case OP_getsuper:
433 case OP_setsuper:
434 case OP_getproperty:
435 case OP_initproperty:
436 case OP_setproperty:
437 case OP_getlex:
438 case OP_findpropstrict:
439 case OP_findproperty:
440 case OP_finddef:
441 case OP_deleteproperty:
442 case OP_istype:
443 case OP_coerce:
444 case OP_astype:
445 case OP_getdescendants:
446 s += abc.names[readU32()]
447 break;
448 case OP_constructprop:
449 case OP_callproperty:
450 case OP_callproplex:
451 case OP_callsuper:
452 case OP_callsupervoid:
453 case OP_callpropvoid:
454 s += abc.names[readU32()]
455 s += " (" + readU32() + ")"
456 break;
457 case OP_newfunction: {
458 var method_id = readU32()
459 s += abc.methods[method_id]
460 break;
462 case OP_callstatic:
463 s += abc.methods[readU32()]
464 s += " (" + readU32() + ")"
465 break;
466 case OP_newclass:
467 s += abc.instances[readU32()]
468 break;
469 case OP_lookupswitch:
470 var pos = code.position-1;
471 var target = pos + readS24()
472 var maxindex = readU32()
473 s += "default:" + labels.labelFor(target) // target + "("+(target-pos)+")"
474 s += " maxcase:" + maxindex
475 for (var i:int=0; i <= maxindex; i++) {
476 target = pos + readS24();
477 s += " " + labels.labelFor(target) // target + "("+(target-pos)+")"
479 break;
480 case OP_jump:
481 case OP_iftrue: case OP_iffalse:
482 case OP_ifeq: case OP_ifne:
483 case OP_ifge: case OP_ifnge:
484 case OP_ifgt: case OP_ifngt:
485 case OP_ifle: case OP_ifnle:
486 case OP_iflt: case OP_ifnlt:
487 case OP_ifstricteq: case OP_ifstrictne:
488 var offset = readS24()
489 var target = code.position+offset
490 //s += target + " ("+offset+")"
491 s += labels.labelFor(target)
492 if (!((code.position) in labels))
493 s += "\n"
494 break;
495 case OP_inclocal:
496 case OP_declocal:
497 case OP_inclocal_i:
498 case OP_declocal_i:
499 case OP_getlocal:
500 case OP_kill:
501 case OP_setlocal:
502 case OP_bkptline:
503 case OP_debugline:
504 case OP_getglobalslot:
505 case OP_getslot:
506 case OP_setglobalslot:
507 case OP_setslot:
508 case OP_pushshort:
509 case OP_newcatch:
510 case OP_getouterscope:
511 s += readU32()
512 break
513 case OP_debug:
514 s += code.readUnsignedByte()
515 s += " " + readU32()
516 s += " " + code.readUnsignedByte()
517 s += " " + readU32()
518 break;
519 case OP_newobject:
520 s += "{" + readU32() + "}"
521 break;
522 case OP_newarray:
523 s += "[" + readU32() + "]"
524 break;
525 case OP_call:
526 case OP_construct:
527 case OP_constructsuper:
528 case OP_applytype:
529 s += "(" + readU32() + ")"
530 break;
531 case OP_pushbyte:
532 case OP_getscopeobject:
533 s += code.readByte()
534 break;
535 case OP_hasnext2:
536 s += readU32() + " " + readU32()
537 default:
538 /*if (opNames[opcode] == ("0x"+opcode.toString(16).toUpperCase()))
539 s += " UNKNOWN OPCODE"*/
540 break
542 var size:int = code.position - start
543 totalSize += size
544 opSizes[opcode] = int(opSizes[opcode]) + size
545 opCounts[opcode] = int(opCounts[opcode]) + 1
546 dumpPrint(s)
548 if (exceptions) {
549 for each (var ex in exceptions)
550 dumpPrint(indent + "// handler [" + ex.from + ", " + ex.to + "] -> " + ex.target +
551 (ex.name ? (" " + ex.name + ":" + ex.type) : (" :" + ex.type)));
553 dumpPrint(oldindent+"}\n")
556 popVersionMetadata(mdpushed)
559 function readU32():int
561 var result:int = code.readUnsignedByte();
562 if (!(result & 0x00000080))
563 return result;
564 result = result & 0x0000007f | code.readUnsignedByte()<<7;
565 if (!(result & 0x00004000))
566 return result;
567 result = result & 0x00003fff | code.readUnsignedByte()<<14;
568 if (!(result & 0x00200000))
569 return result;
570 result = result & 0x001fffff | code.readUnsignedByte()<<21;
571 if (!(result & 0x10000000))
572 return result;
573 return result & 0x0fffffff | code.readUnsignedByte()<<28;
576 function readS24():int
578 var b:int = code.readUnsignedByte()
579 b |= code.readUnsignedByte()<<8
580 b |= code.readByte()<<16
581 return b
585 class SlotInfo extends MemberInfo
587 var type
588 var value
589 public function format():String
591 return traitKinds[kind] + " " + name.toString(useMetadataVersions) + ":" + type +
592 (value !== undefined ? (" = " + (value is String ? ('"'+value+'"') : value)) : "") +
593 (doDumpAPI ? "" : "\t/* slot_id " + id + " */")
595 function dump(abc:Abc, indent:String, attr:String="")
597 if(doDumpAPI && name.isHidden())
598 return
600 var mdpushed:Boolean = pushVersionMetadata(metadata)
602 if (kind == TRAIT_Const || kind == TRAIT_Slot)
604 if (metadata && !doDumpAPI) {
605 for each (var md in metadata)
606 dumpPrint(indent+md)
608 dumpPrint(indent+attr+format())
609 popVersionMetadata(mdpushed)
610 return
613 // else, class
614 var ct:Traits = value
615 var it:Traits = ct.itraits
616 dumpPrint('')
617 if (metadata && !doDumpAPI) {
618 for each (var md in metadata)
619 dumpPrint(indent+md)
621 var def:String;
622 if (it.flags & CLASS_FLAG_interface)
623 def = "interface"
624 else {
625 def = "class";
626 if (!(it.flags & CLASS_FLAG_sealed))
627 def = "dynamic " + def;
628 if (it.flags & CLASS_FLAG_final)
629 def = "final " + def;
632 dumpPrint(indent+attr+def+" "+name.toString(useMetadataVersions)+" extends "+it.base)
633 var oldindent = indent
634 indent += TAB
635 if (it.interfaces.length > 0)
636 dumpPrint(indent+"implements "+it.interfaces)
638 if(doDumpAPI) {
639 var prefix:String = indent+attr+def+" "+name.toString(useMetadataVersions)+" "
640 it.init.dump(abc,prefix)
641 it.dump(abc,indent,prefix)
642 ct.dump(abc,indent,prefix + "static ")
643 ct.init.dump(abc,indent,prefix + "static ")
644 } else {
645 dumpPrint(oldindent+"{")
646 it.init.dump(abc,indent)
647 it.dump(abc,indent)
648 ct.dump(abc,indent,"static ")
649 ct.init.dump(abc,indent,"static ")
650 dumpPrint(oldindent+"}\n")
653 popVersionMetadata(mdpushed)
657 class Traits
659 var name
660 var init:MethodInfo
661 var itraits:Traits
662 var base
663 var flags:int
664 var protectedNs:ABCNamespace
665 const interfaces:Array = []
666 const names:Object = {}
667 const slots:Array = []
668 const methods:Array = []
669 const members:Array = []
671 public function toString():String
673 return String(name)
676 public function dump(abc:Abc, indent:String, attr:String="")
678 for each (var m in members)
679 m.dump(abc, indent, attr)
683 class Abc
685 private var data:ByteArray
687 var major:int
688 var minor:int
690 var ints:Array
691 var uints:Array
692 var doubles:Array
693 var strings:Array
694 var namespaces:Array
695 var nssets:Array
696 var names:Array
698 var defaults:Array = new Array(constantKinds.length)
700 var methods:Array
701 var instances:Array
702 var classes:Array
703 var scripts:Array
704 var metadata:Array
706 var publicNs = new ABCNamespace("")
707 var anyNs = new ABCNamespace("*")
709 var magic:int
711 function Abc(data:ByteArray)
713 data.position = 0
714 this.data = data
715 magic = data.readInt()
717 infoPrint("magic " + magic.toString(16))
719 if (magic != (46<<16|14) && magic != (46<<16|15) && magic != (46<<16|16))
720 throw new Error("not an abc file. magic=" + magic.toString(16))
722 parseCpool()
724 defaults[CONSTANT_Utf8] = strings
725 defaults[CONSTANT_Int] = ints
726 defaults[CONSTANT_UInt] = uints
727 defaults[CONSTANT_Double] = doubles
728 defaults[CONSTANT_Int] = ints
729 defaults[CONSTANT_False] = { 10:false }
730 defaults[CONSTANT_True] = { 11:true }
731 defaults[CONSTANT_Namespace] = namespaces
732 defaults[CONSTANT_PrivateNs] = namespaces
733 defaults[CONSTANT_PackageNs] = namespaces
734 defaults[CONSTANT_PackageInternalNs] = namespaces
735 defaults[CONSTANT_ProtectedNs] = namespaces
736 defaults[CONSTANT_StaticProtectedNs] = namespaces
737 defaults[CONSTANT_StaticProtectedNs2] = namespaces
738 defaults[CONSTANT_Null] = { 12: null }
740 parseMethodInfos()
741 parseMetadataInfos()
742 parseInstanceInfos()
743 parseClassInfos()
744 parseScriptInfos()
745 parseMethodBodies()
747 if (doExtractAbc==true)
748 File.writeByteArray(nextAbcFname(), data);
751 function readU32():int
753 var result:int = data.readUnsignedByte();
754 if (!(result & 0x00000080))
755 return result;
756 result = result & 0x0000007f | data.readUnsignedByte()<<7;
757 if (!(result & 0x00004000))
758 return result;
759 result = result & 0x00003fff | data.readUnsignedByte()<<14;
760 if (!(result & 0x00200000))
761 return result;
762 result = result & 0x001fffff | data.readUnsignedByte()<<21;
763 if (!(result & 0x10000000))
764 return result;
765 return result & 0x0fffffff | data.readUnsignedByte()<<28;
768 function dumpPool(name:String, pool:Array)
770 if(!doDumpPools)
771 return
773 for(var i:int = 0; i<pool.length; i++)
774 infoPrint(name + "[" + i + "] = " + pool[i])
777 function padString(s:String, l:uint)
779 if(s.length < l)
780 return padString(s + " ", l)
781 return s
784 function parseCpool()
786 var i:int, j:int
787 var n:int
788 var kind:int
790 var start:int = data.position
792 // ints
793 n = readU32()
794 ints = [0]
795 for (i=1; i < n; i++)
796 ints[i] = readU32()
797 dumpPool("int", ints)
799 // uints
800 n = readU32()
801 uints = [0]
802 for (i=1; i < n; i++)
803 uints[i] = uint(readU32())
804 dumpPool("uint", uints)
806 // doubles
807 n = readU32()
808 doubles = [NaN]
809 for (i=1; i < n; i++)
810 doubles[i] = data.readDouble()
811 dumpPool("double", doubles)
813 infoPrint("Cpool numbers size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
814 start = data.position
816 // strings
817 n = readU32()
818 strings = [""]
819 for (i=1; i < n; i++)
820 strings[i] = data.readUTFBytes(readU32())
821 dumpPool("string", strings)
823 infoPrint("Cpool strings count "+ n +" size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
824 start = data.position
826 // namespaces
827 n = readU32()
828 namespaces = [publicNs]
829 var nskind = 0;
830 for (i=1; i < n; i++) {
831 switch (nskind = data.readByte())
833 case CONSTANT_Namespace:
834 case CONSTANT_PackageNs:
835 case CONSTANT_PackageInternalNs:
836 case CONSTANT_ProtectedNs:
837 case CONSTANT_StaticProtectedNs:
838 case CONSTANT_StaticProtectedNs2:
840 namespaces[i] = new ABCNamespace(strings[readU32()], nskind)
841 break;
843 case CONSTANT_PrivateNs:
844 readU32();
845 namespaces[i] = new ABCNamespace("private", nskind)
846 break;
848 if(doDumpPools)
849 infoPrint("namespace[" + i + "] = " + padString(constantKinds[nskind], 20) + ": " + namespaces[i].uri)
852 infoPrint("Cpool namespaces count "+ n +" size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
853 start = data.position
855 // namespace sets
856 n = readU32()
857 nssets = [null]
858 for (i=1; i < n; i++)
860 var count:int = readU32()
861 var nsset = nssets[i] = []
862 var nsids = []
863 for (j=0; j < count; j++) {
864 var nsid = readU32()
865 nsids.push(nsid)
866 nsset[j] = namespaces[nsid]
868 if(doDumpPools)
869 infoPrint("nsset[" + i + "] = {" + nsids.join(", ") + "}")
872 infoPrint("Cpool nssets count "+ n +" size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
873 start = data.position
875 // multinames
876 n = readU32()
877 names = [null]
878 namespaces[0] = anyNs
879 strings[0] = "*" // any name
880 for (i=1; i < n; i++) {
881 var nametype = data.readByte()
882 switch (nametype)
884 case CONSTANT_Qname:
885 case CONSTANT_QnameA:
886 names[i] = new QualifiedName(namespaces[readU32()], strings[readU32()])
887 break;
889 case CONSTANT_RTQname:
890 case CONSTANT_RTQnameA:
891 names[i] = new QualifiedName(null, strings[readU32()])
892 break;
894 case CONSTANT_RTQnameL:
895 case CONSTANT_RTQnameLA:
896 names[i] = null
897 break;
899 case CONSTANT_NameL:
900 case CONSTANT_NameLA:
901 names[i] = new QualifiedName(publicNs, null)
902 break;
904 case CONSTANT_Multiname:
905 case CONSTANT_MultinameA:
906 var name = strings[readU32()]
907 names[i] = Multiname.createMultiname(nssets[readU32()], name, doDumpAPI)
908 break;
910 case CONSTANT_MultinameL:
911 case CONSTANT_MultinameLA:
912 names[i] = Multiname.createMultiname(nssets[readU32()], null, doDumpAPI)
913 break;
915 case CONSTANT_TypeName:
916 var name = names[readU32()];
917 var count = readU32();
918 var types = [];
919 for( var t=0; t < count; ++t )
920 types.push(names[readU32()]);
921 names[i] = new TypeName(name, types);
922 break;
924 default:
925 throw new Error("invalid kind " + data[data.position-1])
927 if(doDumpPools)
928 infoPrint("name[" + i + "] = " + padString(constantKinds[nametype], 12) + ": " + names[i])
931 infoPrint("Cpool names count "+ n +" size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
932 start = data.position
934 namespaces[0] = publicNs
935 strings[0] = "*"
938 function parseMethodInfos()
940 var start:int = data.position
941 names[0] = new QualifiedName(publicNs, "*")
942 var method_count:int = readU32()
943 methods = []
944 for (var i:int=0; i < method_count; i++)
946 var m = methods[i] = new MethodInfo()
947 m.method_id = i
948 var param_count:int = readU32()
949 m.returnType = names[readU32()]
950 m.paramTypes = []
951 for (var j:int=0; j < param_count; j++)
952 m.paramTypes[j] = names[readU32()]
953 m.debugName = strings[readU32()]
954 m.flags = data.readByte()
955 if (m.flags & HAS_OPTIONAL)
957 // has_optional
958 var optional_count:int = readU32();
959 m.optionalValues = []
960 for( var k:int = param_count-optional_count; k < param_count; ++k)
962 var index = readU32() // optional value index
963 var kind:int = data.readByte() // kind byte for each default value
964 if (index == 0)
966 // kind is ignored, default value is based on type
967 m.optionalValues[k] = undefined
969 else
971 if (!defaults[kind])
972 print("ERROR kind="+kind+" method_id " + i)
973 else
974 m.optionalValues[k] = defaults[kind][index]
978 if (m.flags & HAS_ParamNames)
980 // has_paramnames
981 for( var k:int = 0; k < param_count; ++k)
983 readU32();
987 infoPrint("MethodInfo count " +method_count+ " size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
990 function parseMetadataInfos()
992 var start:int = data.position
993 var count:int = readU32()
994 metadata = []
995 for (var i:int=0; i < count; i++)
997 // MetadataInfo
998 var m = metadata[i] = new MetaData()
999 m.name = strings[readU32()];
1000 var values_count:int = readU32();
1001 var names:Array = []
1002 var keys:Array = []
1003 var values:Array = []
1005 for(var q:int = 0; q < values_count; ++q)
1006 keys[q] = strings[readU32()]
1007 for(var q:int = 0; q < values_count; ++q)
1008 values[q] = strings[readU32()]
1010 for(var q:int = 0; q < values_count; ++q)
1011 m.addPair(keys[q], values[q])
1013 if(doDumpPools)
1014 infoPrint("metadata[" + i + "] = {" + m.tuples.join(", ") + "}")
1016 infoPrint("MetadataInfo count " +values_count+ " size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
1019 function parseInstanceInfos()
1021 var start:int = data.position
1022 var count:int = readU32()
1023 instances = []
1024 for (var i:int=0; i < count; i++)
1026 var t = instances[i] = new Traits()
1027 t.name = names[readU32()]
1028 t.base = names[readU32()]
1029 t.flags = data.readByte()
1030 if (t.flags & 8)
1031 t.protectedNs = namespaces[readU32()]
1032 var interface_count = readU32()
1033 for (var j:int=0; j < interface_count; j++)
1034 t.interfaces[j] = names[readU32()]
1035 var m = t.init = methods[readU32()]
1036 m.name = t.name
1037 m.kind = TRAIT_Method
1038 m.id = -1
1039 parseTraits(t)
1041 infoPrint("InstanceInfo count " + count + " size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
1044 function parseTraits(t:Traits)
1046 var namecount = readU32()
1047 for (var i:int=0; i < namecount; i++)
1049 var name = names[readU32()]
1050 var tag = data.readByte()
1051 var kind = tag & 0xf
1052 var member
1053 switch(kind) {
1054 case TRAIT_Slot:
1055 case TRAIT_Const:
1056 case TRAIT_Class:
1057 var slot = member = new SlotInfo()
1058 slot.id = readU32()
1059 t.slots[slot.id] = slot
1060 if (kind==TRAIT_Slot || kind==TRAIT_Const)
1062 slot.type = names[readU32()]
1063 var index=readU32()
1064 if (index)
1065 slot.value = defaults[data.readByte()][index]
1067 else // (kind == TRAIT_Class)
1069 slot.value = classes[readU32()]
1071 break;
1072 case TRAIT_Method:
1073 case TRAIT_Getter:
1074 case TRAIT_Setter:
1075 var disp_id = readU32()
1076 var method = member = methods[readU32()]
1077 t.methods[disp_id] = method
1078 method.id = disp_id
1079 //print("\t",traitKinds[kind],name,disp_id,method,"// disp_id", disp_id)
1080 break;
1082 if (!member)
1083 print("error trait kind "+kind)
1084 member.kind = kind
1085 member.name = name
1086 t.names[String(name)] = t.members[i] = member
1088 if ( (tag >> 4) & ATTR_metadata ) {
1089 member.metadata = []
1090 for(var j:int=0, mdCount:int=readU32(); j < mdCount; ++j)
1091 member.metadata[j] = metadata[readU32()]
1096 function parseClassInfos()
1098 var start:int = data.position
1099 var count:int = instances.length
1100 classes = []
1101 for (var i:int=0; i < count; i++)
1103 var t:Traits = classes[i] = new Traits()
1104 t.init = methods[readU32()]
1105 t.base = "Class"
1106 t.itraits = instances[i]
1107 t.name = t.itraits.name + "$"
1108 t.init.name = t.itraits.name + "$cinit"
1109 t.init.kind = TRAIT_Method
1110 parseTraits(t)
1112 infoPrint("ClassInfo count " + count + " size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+"%")
1115 function parseScriptInfos()
1117 var start:int = data.position
1118 var count:int = readU32()
1119 scripts = []
1120 for (var i:int=0; i < count; i++)
1122 var t = new Traits()
1123 scripts[i] = t
1124 t.name = "script" + i
1125 t.base = names[0] // Object
1126 t.init = methods[readU32()]
1127 t.init.name = t.name + "$init"
1128 t.init.kind = TRAIT_Method
1129 parseTraits(t)
1131 infoPrint("ScriptInfo size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
1134 function parseMethodBodies()
1136 var start:int = data.position
1137 var count:int = readU32()
1138 for (var i:int=0; i < count; i++)
1140 var m = methods[readU32()]
1141 m.max_stack = readU32()
1142 m.local_count = readU32()
1143 var initScopeDepth = readU32()
1144 var maxScopeDepth = readU32()
1145 m.max_scope = maxScopeDepth - initScopeDepth
1146 var code_length = readU32()
1147 m.code = new ByteArray()
1148 m.code.endian = "littleEndian"
1149 if (code_length > 0) {
1150 m.code_offset = data.position;
1151 data.readBytes(m.code, 0, code_length)
1153 var ex_count = readU32()
1154 if (ex_count > 0) {
1155 m.exceptions = []
1156 for (var j:int = 0; j < ex_count; j++)
1158 var ex = new ExceptionInfo()
1159 m.exceptions.push(ex)
1160 ex.from = readU32()
1161 ex.to = readU32()
1162 ex.target = readU32()
1163 ex.type = names[readU32()]
1164 //print("magic " + magic.toString(16))
1165 //if (magic >= (46<<16|16))
1166 ex.name = names[readU32()];
1167 //infoPrint("exception method_id=" + i + " [" + ex.from + ", " + ex.to + "] " + ex.type + " -> " + ex.target)
1170 parseTraits(m.activation = new Traits)
1172 infoPrint("MethodBodies count " + count + " size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %")
1175 function dump(indent:String="")
1177 for each (var t in scripts)
1179 infoPrint(indent+t.name)
1180 t.dump(this,indent)
1181 t.init.dump(this,indent)
1184 for each (var m in methods)
1186 if (!m.dumped)
1187 m.dump(this,indent)
1190 infoPrint(align(14,"OPCODE",' ',"left")+"\tCOUNT\t SIZE\t% OF "+totalSize)
1191 var done = []
1192 for (;;)
1194 var max:int = -1;
1195 var maxsize:int = 0;
1196 for (var i:int=0; i < 256; i++)
1198 if (opSizes[i] > maxsize && !done[i])
1200 max = i;
1201 maxsize = opSizes[i];
1204 if (max == -1)
1205 break;
1206 done[max] = 1;
1207 infoPrint(opNames[max]+"\t"+align(6,int(opCounts[max]))+"\t"+align(6,int(opSizes[max]))+"\t"+align(2,int(100*opSizes[max]/totalSize))+"%")
1211 // right/left align 's' to 'len' characters using 'pad' as padding
1212 function align(len, s, pad=' ', ment='right')
1214 return (ment == 'right')
1215 ? new Array(Math.max(0,len-String(s).length)).join(pad)+String(s)
1216 : new String(s)+Array(Math.max(0,len-String(s).length)).join(pad)
1220 class Rect
1222 var nBits:int
1223 var xMin:int, xMax:int
1224 var yMin:int, yMax:int
1225 public function toString()
1227 return "[Rect "+xMin+" "+yMin+" "+xMax+" "+yMax+"]"
1231 const stagDoABC :int = 72; // embedded .abc (AVM+) bytecode
1232 const stagSymbolClass :int = 76;
1233 const stagMetadata :int = 77;
1234 const stagDoABC2 :int = 82; // revised ABC version with a name
1236 var tagNames:Array = [
1237 "End", // 00
1238 "ShowFrame", // 01
1239 "DefineShape", // 02
1240 "FreeCharacter", // 03
1241 "PlaceObject", // 04
1242 "RemoveObject", // 05
1243 "DefineBits", // 06
1244 "DefineButton", // 07
1245 "JPEGTables", // 08
1246 "SetBackgroundColor", // 09
1248 "DefineFont", // 10
1249 "DefineText", // 11
1250 "DoAction", // 12
1251 "DefineFontInfo", // 13
1253 "DefineSound", // 14
1254 "StartSound", // 15
1255 "StopSound", // 16
1257 "DefineButtonSound", // 17
1259 "SoundStreamHead", // 18
1260 "SoundStreamBlock", // 19
1262 "DefineBitsLossless", // 20
1263 "DefineBitsJPEG2", // 21
1265 "DefineShape2", // 22
1266 "DefineButtonCxform", // 23
1268 "Protect", // 24
1270 "PathsArePostScript", // 25
1272 "PlaceObject2", // 26
1273 "27 (invalid)", // 27
1274 "RemoveObject2", // 28
1276 "SyncFrame", // 29
1277 "30 (invalid)", // 30
1278 "FreeAll", // 31
1280 "DefineShape3", // 32
1281 "DefineText2", // 33
1282 "DefineButton2", // 34
1283 "DefineBitsJPEG3", // 35
1284 "DefineBitsLossless2", // 36
1285 "DefineEditText", // 37
1287 "DefineVideo", // 38
1289 "DefineSprite", // 39
1290 "NameCharacter", // 40
1291 "ProductInfo", // 41
1292 "DefineTextFormat", // 42
1293 "FrameLabel", // 43
1294 "DefineBehavior", // 44
1295 "SoundStreamHead2", // 45
1296 "DefineMorphShape", // 46
1297 "FrameTag", // 47
1298 "DefineFont2", // 48
1299 "GenCommand", // 49
1300 "DefineCommandObj", // 50
1301 "CharacterSet", // 51
1302 "FontRef", // 52
1304 "DefineFunction", // 53
1305 "PlaceFunction", // 54
1307 "GenTagObject", // 55
1309 "ExportAssets", // 56
1310 "ImportAssets", // 57
1312 "EnableDebugger", // 58
1314 "DoInitAction", // 59
1315 "DefineVideoStream", // 60
1316 "VideoFrame", // 61
1318 "DefineFontInfo2", // 62
1319 "DebugID", // 63
1320 "EnableDebugger2", // 64
1321 "ScriptLimits", // 65
1323 "SetTabIndex", // 66
1325 "DefineShape4", // 67
1326 "DefineMorphShape2", // 68
1328 "FileAttributes", // 69
1330 "PlaceObject3", // 70
1331 "ImportAssets2", // 71
1333 "DoABC", // 72
1334 "DefineFontAlignZones", // 73
1335 "CSMSettings", // 74
1336 "DefineFont3", // 75
1337 "SymbolClass", // 76
1338 "Metadata", // 77
1339 "DefineScalingGrid", // 78
1340 "DefineDeviceVideo", // 79
1341 "80 (invalid)", // 80
1342 "81 (invalid)", // 81
1343 "DoABC2", // 82
1344 "DefineShape4", // 83
1345 "DefineMorphShape2", // 84
1346 "PlaceImagePrivate", // 85
1347 "DefineSceneAndFrameLabelData", // 86
1348 "DefineBinaryData", // 87
1349 "DefineFontName", // 88
1350 "StartSound", // 89
1351 "DefineBitsJPEG64", // 90
1352 "DefineFont4", // 91
1356 class Swf
1358 private var bitPos:int
1359 private var bitBuf:int
1361 private var data:ByteArray
1363 function Swf(data:ByteArray)
1365 this.data = data
1366 infoPrint("size "+decodeRect())
1367 infoPrint("frame rate "+(data.readUnsignedByte()<<8|data.readUnsignedByte()))
1368 infoPrint("frame count "+data.readUnsignedShort())
1369 decodeTags()
1372 static function emitSwf(fn:String,data:ByteArray,version:uint)
1374 var ba:ByteArray = new ByteArray
1375 ba.endian = "littleEndian"
1376 ba.writeByte(0x46); ba.writeByte(0x57); ba.writeByte(0x53); // FWS
1377 ba.writeByte(version>>24)
1378 ba.writeUnsignedInt(data.length+8)
1379 ba.writeBytes(data,0,data.length)
1380 File.writeByteArray(fn+".swf",ba)
1381 infoPrint("wrote "+ba.length+" bytes to file "+fn+".swf")
1384 private function decodeTags()
1386 var type:int, h:int, length:int
1387 var offset:int
1389 while (data.position < data.length)
1391 type = (h = data.readUnsignedShort()) >> 6;
1393 if (((length = h & 0x3F) == 0x3F))
1394 length = data.readInt();
1396 var tagN = tagNames[type]
1397 if (type >= tagNames.length)
1398 tagN = type+" (unknown)"
1400 infoPrint(tagN+" "+length+"b "+int(100*length/data.length)+"%")
1401 switch (type)
1403 case 0: return
1404 case stagDoABC2:
1405 var pos1:int = data.position
1406 data.readInt()
1407 infoPrint("\nabc name "+readString())
1408 length -= (data.position-pos1)
1409 // fall through
1410 case stagDoABC:
1411 var data2 = new ByteArray
1412 data2.endian = "littleEndian"
1413 data.readBytes(data2,0,length)
1414 new Abc(data2).dump(" ")
1415 infoPrint("")
1416 break
1417 case stagMetadata:
1418 infoPrint(new XML(readString()));
1419 break;
1420 default:
1421 data.position += length
1426 private function readString():String
1428 var s:String = ""
1429 var c:int
1431 while (c=data.readUnsignedByte())
1432 s += String.fromCharCode(c)
1434 return s
1437 private function syncBits()
1439 bitPos = 0
1442 private function decodeRect():Rect
1444 syncBits();
1446 var rect:Rect = new Rect();
1448 var nBits:int = readUBits(5)
1449 rect.xMin = readSBits(nBits);
1450 rect.xMax = readSBits(nBits);
1451 rect.yMin = readSBits(nBits);
1452 rect.yMax = readSBits(nBits);
1454 return rect;
1457 function readSBits(numBits:int):int
1459 if (numBits > 32)
1460 throw new Error("Number of bits > 32");
1462 var num:int = readUBits(numBits);
1463 var shift:int = 32-numBits;
1464 // sign extension
1465 num = (num << shift) >> shift;
1466 return num;
1469 function readUBits(numBits:int):uint
1471 if (numBits == 0)
1472 return 0
1474 var bitsLeft:int = numBits;
1475 var result:int = 0;
1477 if (bitPos == 0) //no value in the buffer - read a byte
1479 bitBuf = data.readUnsignedByte()
1480 bitPos = 8;
1483 while (true)
1485 var shift:int = bitsLeft - bitPos;
1486 if (shift > 0)
1488 // Consume the entire buffer
1489 result |= bitBuf << shift;
1490 bitsLeft -= bitPos;
1492 // Get the next byte from the input stream
1493 bitBuf = data.readUnsignedByte();
1494 bitPos = 8;
1496 else
1498 // Consume a portion of the buffer
1499 result |= bitBuf >> -shift;
1500 bitPos -= bitsLeft;
1501 bitBuf &= 0xff >> (8 - bitPos); // mask off the consumed bits
1503 // if (print) System.out.println(" read"+numBits+" " + result);
1504 return result;
1508 // unreachable, but fixes a spurious compiler warning
1509 return result;
1513 function help()
1515 print(usage);
1516 System.exit(1)
1519 function processArg(arg:String)
1521 if (arg == '-a') {
1522 doExtractAbc = true;
1523 doExtractInfo = false
1524 doExtractAbs = false
1525 } else if (arg == '-i') {
1526 // suppress abs output
1527 doExtractAbs = false;
1528 } else if (arg == '-abs') {
1529 // suppress info output
1530 doExtractInfo = false
1531 } else if (arg == '-api') {
1532 doDumpAPI = true;
1533 doExtractInfo = false
1534 } else if (arg == '-mdversions') {
1535 useMetadataVersions = true
1536 } else if (arg == '-pools') {
1537 doDumpPools = true
1538 } else if (arg == '--decompress-only') {
1539 doDecompressOnly = true
1540 } else {
1541 print('Unknown option '+arg)
1542 help()
1546 function nextAbcFname():String
1548 var s = currentFname
1549 if (currentFcount>0)
1550 s = s.concat(currentFcount);
1551 currentFcount++
1552 return s+'.abc'
1555 // main
1556 var doExtractAbc = false
1557 var doExtractInfo = true
1558 var doExtractAbs = true
1559 var doDumpAPI = false
1560 var doDumpPools = false
1561 var doDecompressOnly = false
1562 var useMetadataVersions = false
1563 var currentFname = ''
1564 var currentFcount = 0
1565 for each (var file in System.argv)
1567 if (file.indexOf('-')==0)
1569 processArg(file)
1570 continue
1573 var x;
1574 if ((x = file.lastIndexOf(".swf")) != -1 || (x = file.lastIndexOf(".swc")) != -1)
1575 currentFname = file.substring(0,x);
1576 else
1577 currentFname = file;
1578 var data:ByteArray = File.readByteArray(file)
1579 data.endian = "littleEndian"
1580 var version:uint = data.readUnsignedInt()
1581 switch (version&0xffffff) {
1582 case 46<<16|14:
1583 case 46<<16|15:
1584 case 46<<16|16:
1585 var abc:Abc = new Abc(data)
1586 abc.dump()
1587 break
1588 case 67|87<<8|83<<16: // SWC
1589 var udata:ByteArray = new ByteArray
1590 udata.endian = "littleEndian"
1591 data.position = 8
1592 data.readBytes(udata,0,data.length-data.position)
1593 var csize:int = udata.length
1594 udata.uncompress()
1595 infoPrint("decompressed swf "+csize+" -> "+udata.length)
1596 if (doDecompressOnly) {
1597 Swf.emitSwf(file,udata,version)
1598 System.exit(0)
1600 udata.position = 0
1601 /*var swf:Swf =*/ new Swf(udata)
1602 break
1603 case 70|87<<8|83<<16: // SWF
1604 if (doDecompressOnly)
1605 System.exit(0)
1606 data.position = 8 // skip header and length
1607 /*var swf:Swf =*/ new Swf(data)
1608 break
1609 default:
1610 print('unknown format 0x'+version.toString(16))
1611 break
1615 if (System.argv.length < 1)
1616 help();