Introduction

The as3commons-bytecode library offers a set of classes and interfaces which can help a developer work with SWF files in general and Abc bytecode in particular. Its not neccessarily meant as an all purpose SWF library, it focuses mostly on the Abc side of things.

It does include SWF serialization and deserialization functionality, but this only supports a small selection of the SWF tags that can be present in a SWF file.

For an allround SWF publishing library check out Claus Wahlers' excellent as3swf library.

Abc deserialization and serialization

To deserialize a DoABC tag use the AbcDeserializer class:

var ds:AbcDeserializer = new AbcDeserializer(byteStream); //The bytestream contains the DoAbc file
    and its assumed to be at the right position.
var abcFile:AbcFile = ds.deserialize();

The AbcFile instance will contain a full code representation of the DoABCTag. Including: The constantpool, class info, method info, etc.

Simple SWF deserialization and serialization

Simple SWF files that only contain code (such as class libraries or resource modules), so no embedded images, sounds or whatever, can be directly deserialized/serialized with the SWFFileIO class:

var io:SWFFileIO = new SWFFileIO();
var swfFile:SWFFile = io.read(byteArray); //Where the bytearray is a loaded SWF file (.swf, *not* .swc)

By using the SWFFile's getTagsByType method you can retrieve the different sorts of SWF tags and examine them:

var abcTags:Array = swfFile.getTagsByType(DoABCTag);
for each(var tag:DoABCTag in abcTags){
  var abcFile:AbcFile = tag.abcFile;
  processAbcFile(abcFile);
}

The SWF tags that are supported by as3commons-bytecode are:

  • DoABC
  • EndTag
  • FileAttributes
  • FrameLabel
  • Metadata
  • ProductInfo
  • ScriptLimits
  • SetBackgroundColor
  • ShowFrameTag
  • SymbolClass

Other tags will be loaded by an UnsupportedTag instance.

Bytecode based reflection

The most important class for reflection is ByteCodeType which will parse the currently loaded bytecode and populate its cache with ByteCodeType instances for each class that was parsed.

There are two static methods on the ByteCodeType:
ByteCodeType.fromLoader(loader:LoaderInfo)
and ByteCodeType.fromByteArray(input:ByteArray, applicationDomain:ApplicationDomain = null)

Both methods have the same end result, they just start off with a different parameter.
When using fromLoader() simply pass a valid LoaderInfo instance to it and it will pass its .bytes and .applicationDomain to the fromByteArray() method.

For example like this:

ByteCodeType.fromLoader(Application.loaderInfo);
Or in Flex 4:
ByteCodeType.fromLoader(FlexGlobals.topLevelApplication.loaderInfo);
When RSL's have been loaded, use the systemManager.preloadedRSLs property to loop through the loaderInfo instances of each RSL:

for (var info:* in systemManager.preloadedRSLs) {
  ByteCodeType.fromLoader(info);
}

When a SWF has been loaded manually, pass it as a ByteArray to the fromByteArray() method along with an associated ApplicationDomain.

[Embed(source="someswf.swf", mimeType="application/octet-stream")]
private var swfInput:Class;
function parseSWF():void {
  var ba:ByteArray = new swfInput() as ByteArray;
  ByteCodeType.fromByteArray(ba, ApplicationDomain.currentDomain);
}

After parsing ByteCodeTypes can be retrieved by calling these static methods:

  • forInstance(instance:*, applicationDomain:ApplicationDomain = null)
  • forClass(clazz:Class, applicationDomain:ApplicationDomain = null)
  • forName(name:String, applicationDomain:ApplicationDomain = null)

To loop through all the generated ByteCodeTypes use the getKeys() method on the TypeCache instance:

var typeCache:TypeCache = ByteCodeType.getCache();
for each (var key:String in keys) {
  var type:ByteCodeType = typeCache.get(key) as ByteCodeType; /* ... do something... */
}

Metadata scan

Having the full bytecode reflection data is not always useful and the time it takes to parse and extract the data might not always be worth the loading time.

There is also a more lightweight way of extracting just some bits and pieces from the bytecode that normally isn't available through describeType().

Here is a way to generate a lookup for class-level metadata annotations and their corresponding class names:

var metadataLookup:Object = ByteCodeType.metadataLookupFromLoader(loader);
var definitionNames:Array = metadataLookup['mixin'];
for(var i:uint=0; i < definitionNames.length;i++){
  var type:Type = Type.forName(definitionNames[i]); /* ... do something... */
}

Generating this list using metadataLookupFromLoader(loader) is much, much faster than reading all of the reflection data.

The list can later be accessed using the ByteCodeTypeCache.metadataLookup property or using the ByteCodeTypeCache.getClassesWithMetadata(metadataName) method.

At the same time a full list of definition names contained within the specified bytecode will be populated.
To retrieve this list use this ByteCodeTypeCache property:

var typeCache:ByteCodeTypeCache = ByteCodeType.getCache();
var definitionNames:Array = typeCache.definitionNames;
for(var i:uint=0; i < definitionNames.length;i++){
  var type:Type = Type.forName(definitionNames[i]); /* ... do something... */
}

Or use this, slighty less verbose, method instead:

var definitionNames:Array = ByteCodeType.definitionNamesFromLoader(loader);

Interface implementation lookup

Similar to the metadata lookup another type of lookup is also populated, this is assigned to the ByteCodeCache.interfaceLookup property.

To retrieve a list of class names that all implement a specific interface the getImplementationNames() method can be invoked:

var definitionNames:Array = ByteCodeType.getImplementationNames(IMyTestInterface);
for(var i:uint=0; i < definitionNames.length;i++){
  var type:Type = Type.forName(definitionNames[i]); /* ... do something... */
}

Notice that to retrieve the type information the regular as3commons-reflect Type is used. Combining as3commons-bytecode and -reflect can yield the most efficient result in this way.

Note: the definitionNames list, metadata and interface lookup property are of course also populated when the full bytecode reflection data is generated.