ActionScript 3.0 compiles to ABC, ActionScript Byte Code: a file of constants, method signatures, classes, scripts and method bodies, run by the AVM2. A SWF 9 or later movie carries ABC files in DoABC tags, and runs them when its FileAttributes tag has ActionScript3 set. Most ActionScript 3 movies are CWS files, so their bytecode is inside the zlib stream; offsets on this page are offsets in the decompressed data. Adobe describes the format in the AVM2 Overview (May 2007).
DoABC (82)
| Type | Name | Description |
|---|---|---|
| UI32 | Flags | Bit 0, kDoAbcLazyInitializeFlag: parse the ABC but run it only when needed. 0 or 1. |
| STRING | Name | A name for the block, often empty |
| bytes | ABCData | The ABC file, to the end of the tag |
SWF 9. Flex 3 applications seen for this site write flag 1 and the names
frame1, frame2, one ABC a frame; a Flex debug build writes
one DoABC a class, named by the class path (mx/core/IChildList); other
compilers write flags 0 and empty names. A
SymbolClass tag links
the classes to characters, character 0 being the main timeline.
Tag 72
The ABC file alone, with no flags or name. The specification documents only tag 82, as DoABC; JPEXS calls 72 DoABC and 82 DoABC2. That it came from Flash Player 9’s pre-release tools, the Flex 2 betas, is an inference from its absence in the specification. No movie examined for this site has one. The explorer parses it like DoABC’s ABC data.
Numbers and strings
| Type | Bytes | Meaning |
|---|---|---|
| u8, u16 | 1, 2 | Unsigned, little-endian |
| u30, u32 | 1 to 5 | Seven bits a byte, lowest group first; the top bit (0x80) is set while another byte follows. The fifth byte adds its low four bits as bits 28 to 31. A u30 must be below 230. |
| s32 | 1 to 5 | The same bytes, taken as a 32-bit two’s-complement number |
| s24 | 3 | Little-endian, two’s complement: branch offsets |
| d64 | 8 | IEEE 754 double, little-endian |
| string | … | A u30 byte length, then that many bytes of UTF-8, with no terminating zero |
The AVM2 Overview says an s32 is sign-extended. Tamarin, Adobe’s AVM2 source
(AbcParser::readS32), and RABCDAsm do not sign-extend shorter encodings: a
one-byte 7F is +127, and negative numbers are written in all five bytes.
For example, FF 81 FC 07 is 0x7F + 0x01 × 27 + 0x7C ×
214 + 0x07 × 221 = 16,711,935 (0xFF00FF).
The ABC file
| Field | Type | Meaning |
|---|---|---|
minor_version | u16 | 16 in files from the Flash and Flex compilers |
major_version | u16 | 46. Every ABC examined for this site is 46.16. A version 47 with minor 12 to 14 is reported for a float extension that never shipped in released players (not confirmed). |
constant_pool | cpool_info | The constant pool |
method_count | u30 | |
method | method_info[method_count] | Every method, function, initialiser and closure |
metadata_count | u30 | |
metadata | metadata_info[metadata_count] | Metadata |
class_count | u30 | |
instance | instance_info[class_count] | All the instance halves first … |
class | class_info[class_count] | … then all the class halves; there is no second count |
script_count | u30 | |
script | script_info[script_count] | Scripts; the last is the entry point |
method_body_count | u30 | |
method_body | method_body_info[method_body_count] | The code |
The constant pool
Seven pools, in this order, each a u30 count then its entries. For every pool the count is the number of entries plus one: entry 0 is implicit and not stored, and a count of 0 or 1 means the pool is empty. Everything else in the file refers to these entries by index.
| Pool | Entry | Entry 0 means |
|---|---|---|
int_count, integer | s32 | 0 |
uint_count, uinteger | u32 | 0 |
double_count, double | d64 | NaN |
string_count, string | string_info: u30 size, then UTF-8 bytes | The empty name, or no string |
namespace_count, namespace | namespace_info: u8 kind, u30 name (a string index) | Any namespace (*) |
ns_set_count, ns_set | ns_set_info: u30 count, then u30 namespace indexes, never 0 | — |
multiname_count, multiname | multiname_info: u8 kind, then data by kind | Any name (*) |
Namespaces
| Kind | Name | In ActionScript 3 |
|---|---|---|
| 0x08 | Namespace | A user namespace (namespace foo = "uri"), and interface method namespaces |
| 0x16 | PackageNamespace | Public in a package; the name is the package, empty for the top level |
| 0x17 | PackageInternalNs | internal |
| 0x18 | ProtectedNamespace | protected |
| 0x19 | ExplicitNamespace | An explicit namespace |
| 0x1A | StaticProtectedNs | static protected |
| 0x05 | PrivateNs | private; each private namespace is distinct, even with the same name |
Namespace sets
A u30 count, then that many namespace indexes. A multiname with a namespace set matches a name in any of its namespaces, which is how an unqualified name in source code is looked up through the open namespaces and imports.
Multinames
A name, possibly with its namespace. The kinds ending in A name XML attributes
(@x).
| Kind | Name | Data |
|---|---|---|
| 0x07 | QName | u30 ns, u30 name |
| 0x0D | QNameA | u30 ns, u30 name |
| 0x0F | RTQName | u30 name; the namespace comes from the stack at run time |
| 0x10 | RTQNameA | u30 name |
| 0x11 | RTQNameL | None: namespace and name from the stack |
| 0x12 | RTQNameLA | None |
| 0x09 | Multiname | u30 name, u30 ns_set |
| 0x0E | MultinameA | u30 name, u30 ns_set |
| 0x1B | MultinameL | u30 ns_set; the name from the stack |
| 0x1C | MultinameLA | u30 ns_set |
| 0x1D | TypeName | u30 qname (a multiname index), u30 param_count, u30 params[param_count]: Vector.<T>, Flash Player 10. Not in the 2007 overview; the layout is from Tamarin and RABCDAsm. |
method_info
| Field | Type | Meaning |
|---|---|---|
param_count | u30 | The formal parameters |
return_type | u30 | A multiname index; 0 is any (*) |
param_type | u30[param_count] | Multiname indexes; 0 is any |
name | u30 | A string index, for debugging only (Flex writes names such as mx.core:UIComponent/get width); not the name the method is called by |
flags | u8 | Below |
options | option_info | If HAS_OPTIONAL: u30 option_count, then option_count × (u30 val, u8 kind), the defaults of the last option_count parameters; kind as a constant kind |
param_names | param_info | If HAS_PARAM_NAMES: u30 param_name[param_count], string indexes |
| Flag | Name | Meaning |
|---|---|---|
| 0x01 | NEED_ARGUMENTS | Make an arguments object |
| 0x02 | NEED_ACTIVATION | Make an activation object (the method has closures or with) |
| 0x04 | NEED_REST | ...rest: extra arguments as an Array |
| 0x08 | HAS_OPTIONAL | Options follow |
| 0x10 | IGNORE_REST | Rarely used; its meaning in practice is not confirmed |
| 0x20 | NATIVE | Implemented by the player; no body |
| 0x40 | SET_DXNS | The method sets the default XML namespace |
| 0x80 | HAS_PARAM_NAMES | param_names follow |
metadata_info
| Field | Type | Meaning |
|---|---|---|
name | u30 | A string index: the tag name, such as Embed, SWF, Event or Bindable |
item_count | u30 | |
keys | u30[item_count] | String indexes; 0 for a value with no key |
values | u30[item_count] | String indexes |
The overview draws the items as key and value pairs. The compilers, Tamarin and
RABCDAsm put all the keys first, then all the values, and the explorer reads it that
way; the byte count is the same either way. Only the tags the compiler is told to keep
reach the file: Flex keeps Bindable, Event and a few
others.
instance_info
| Field | Type | Meaning |
|---|---|---|
name | u30 | A multiname index, a QName |
super_name | u30 | The base class, a multiname index; 0 for none (Object itself, interfaces) |
flags | u8 | ClassSealed 0x01 (not dynamic), ClassFinal 0x02, ClassInterface 0x04, ClassProtectedNs 0x08 |
protectedNs | u30 | If ClassProtectedNs: a namespace index |
intrf_count | u30 | |
interface | u30[intrf_count] | Multiname indexes |
iinit | u30 | A method index: the constructor |
trait_count | u30 | |
trait | traits_info[trait_count] | The instance members |
class_info
| Field | Type | Meaning |
|---|---|---|
cinit | u30 | A method index: the static initialiser, run by newclass |
trait_count | u30 | |
traits | traits_info[trait_count] | The static members |
class_info n and instance_info n are the two halves of one class.
script_info
| Field | Type | Meaning |
|---|---|---|
init | u30 | A method index: the script initialiser |
trait_count | u30 | |
traits | traits_info[trait_count] | What the script defines in the global scope: classes, functions, variables |
A script runs when one of the names it defines is first needed; the last script in the file runs at once, as the entry point.
method_body_info
| Field | Type | Meaning |
|---|---|---|
method | u30 | The method_info this is the body of |
max_stack | u30 | The deepest the operand stack goes |
local_count | u30 | Registers: 0 is this, then the parameters, then the locals |
init_scope_depth | u30 | The scope stack depth on entry |
max_scope_depth | u30 | The deepest scope stack |
code_length | u30 | |
code | u8[code_length] | The instructions |
exception_count | u30 | |
exception | exception_info[exception_count] | Below |
trait_count | u30 | |
trait | traits_info[trait_count] | The activation object’s slots: locals captured by closures |
Native methods and interface methods have no body.
exception_info
| Field | Type | Meaning |
|---|---|---|
from | u30 | The start of the protected code, an offset in code |
to | u30 | Its end |
target | u30 | Where the handler starts |
exc_type | u30 | A multiname index: the type caught; 0 for any |
var_name | u30 | A multiname index: the catch variable; 0 for a finally block |
traits_info
| Field | Type | Meaning |
|---|---|---|
name | u30 | A multiname index, a QName |
kind | u8 | The low four bits are the kind, the high four the attributes |
data | by kind | Below |
metadata_count, metadata | u30, u30[] | Only with the Metadata attribute: indexes into the metadata array |
| Kind | Value | Data |
|---|---|---|
| Slot | 0 | u30 slot_id, u30 type_name (a multiname), u30 vindex, and u8 vkind if vindex is not 0 |
| Method | 1 | u30 disp_id, u30 method |
| Getter | 2 | u30 disp_id, u30 method |
| Setter | 3 | u30 disp_id, u30 method |
| Class | 4 | u30 slot_id, u30 classi (an index into the instance and class arrays) |
| Function | 5 | u30 slot_id, u30 function (a method index) |
| Const | 6 | As Slot |
Attributes: Final 0x1, Override 0x2, Metadata 0x4. A slot_id or disp_id of 0 lets the AVM2 choose one.
Constant kinds, for a slot’s vkind and an option’s kind:
| Value | Kind | Index into |
|---|---|---|
| 0x03 | Int | The integer pool |
| 0x04 | UInt | The unsigned integer pool |
| 0x06 | Double | The double pool |
| 0x01 | Utf8 | The string pool |
| 0x0B | True | (index ignored) |
| 0x0A | False | |
| 0x0C | Null | |
| 0x00 | Undefined | |
| 0x08, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x05 | The namespace kinds | The namespace pool |
An ABC file, byte by byte
The 645-byte SWF 9 test movie on the overview
is CWS. In its decompressed data the DoABC tag is at offset 49: BF 14 is
code 82 with the long form, then 49 03 00 00, 841 bytes. The body starts
00 00 00 00 (flags 0, not lazy) and 00 (an empty name), so
the ABC file starts at offset 60 and is 836 bytes long:
0000003C 10 00 2E 00 03 FF 81 FC 07 91 84 9E 03 00 00 1E
0000004C 04 6D 61 69 6E 00 01 62 09 6D 61 69 6E 2E 61 73
| Bytes | Field | Value |
|---|---|---|
10 00 | minor_version | 16 |
2E 00 | major_version | 46 |
03 | int_count | 3: two integers follow |
FF 81 FC 07 | integer[1] | 16,711,935 (0xFF00FF), four bytes |
91 84 9E 03 | integer[2] | 6,783,505 |
00 | uint_count | 0: none |
00 | double_count | 0: none |
1E | string_count | 30: 29 strings follow |
04 6D 61 69 6E | string[1] | "main" |
00 | string[2] | "" (length 0) |
01 62 | string[3] | "b" |
09 6D 61 69 6E 2E 61 73 … | string[4] | "main.as$2", 9 bytes, running on past these 32 |
String 5 is http://adobe.com/AS3/2006/builtin, the ActionScript 3
built-in namespace. Namespace 1 is PrivateNs "main"; namespace 3 is PackageNamespace
"", the top level. The file has one class, main extends
flash.display.Sprite (sealed, with a protected namespace), four methods and four
bodies, with 191 bytes of code. The SymbolClass tag after it names
main as the main timeline’s class.
What an ActionScript 3 class becomes
For this source:
package com.example {
import flash.display.Sprite;
public class Main extends Sprite {
public static var count:int = 0;
private var b:int;
public function Main() { super(); }
public function go(x:int):void { b = x; }
}
}
the compiler (Flex mxmlc and Flash CS3 and later both work this way)
writes:
- Multinames
QName(package com.example, "Main")andQName(package flash.display, "Sprite"), and QNames forcount(PackageNamespace ""),b(PrivateNs) andgo. - An instance_info: name Main, super_name Sprite, flags ClassSealed, plus ClassProtectedNs with a ProtectedNamespace
com.example:Main; iinit the constructor’s method; traitsSlot b:intandMethod go. - A class_info at the same index: cinit the static initialiser; traits
Slot count:int, with vindex and vkind for the default, or the default set in cinit code. - method_infos for the constructor, cinit,
go(param_count 1, param_type int, return_type void) and the script initialiser, each with a method_body_info. A constructor body typically startsgetlocal_0,pushscope,getlocal_0,constructsuper 0. - A script_info whose traits hold
Class Main, and whose init method builds the class: it pushes the scope of each base class in turn (getscopeobject 0,getlex Object,pushscope,getlex flash.events.EventDispatcher,pushscope…), thengetlex Sprite,newclass,popscopes andinitproperty Main. The test movie above does exactly this.
A movie compiled by Flash CS3 or later with timeline code also gets a class for each
timeline, such as MainTimeline, whose frame scripts are methods
frame1, frame2 … registered through
addFrameScript in its constructor. See
ActionScript 3.0 compiled bytecode.
In the files examined
45 movies examined for this site carry ABC, in CWS and uncompressed files of SWF 9 to 43: 3,837 ABC blocks in all. Flex debug builds hold over a thousand DoABC tags each, one a class. Every ABC is version 46.16, and the explorer parses every block, finding no undecodable code. The Flex 3.4 framework library alone has 689 classes and 715,372 bytes of code.