FLASM 1.41
Updated: August 19 2002

 

 
 
What is it

flasm is a command line assembler/disassembler of flash actionscript bytecode.
flasm 1.41 disassembles your entire SWF including all the timelines, onMovieClip events and button events. After that you are expected to do some optimizations on the disassembled code by hand. flasm replaces then all actions in the original swf with your optimized content. flasm fully supports flash MX.

It's also possible to embed flasm actions in your actionscript, making optimizing of large projects more comfortable.

Besides of optimization, flasm disassembly shows "insider view" of your code, thus improving your understanding of actionscript.

flasm is not a decompiler! What you get is the human readable representation of swf bytecodes, not actionscript source.

Looking for simple tutorial? Come on, you don't have to read the whole page. First, make yourself familiar with usage. Then read flash virtual machine topic and make sure you understand the concept of registers and stack. Disassemble some of your swfs, starting with straight ones, to see how flash compiler works. Now you already understand 80% of all you need to. And the last.. Read the whole page.
 
 

Windows binary: flasm14.zip
Mac OS X binary: flasmac14.tgz
Linux binary (x86): flasmlinux14.tgz
Documentation (this file) is included.

Want to compile from sources?

Source code, platform independent: flasm14src.zip
You will need gcc or cc compiler with flex, bison and zlib packages installed. It should compile well without any changes. Tested with djgpp on win98se, cygwin on win98se and win2k, mac os X and Linux.
For cygwin, please install mingw package too, and get mingw's version of zlib from MinGW packages repository. On windows, MS Visual C++ or other environments not compatible to UNIX will require plenty of changes to the source because flasm uses some POSIX functions. Consider cygwin.
 
 

· flasm now handles compressed sfws transparently - no need to decompress before disassembling or updating. New attribute compressed added to the movie declaration (first line of your disassembly). How to use it.

· There is a really strange bug in flash player (MX too): it always expects long length for gif/png images. SWF File Format spec allows storing small lengthes in a more compact manner than big ones. In a special case of very small images (smaller than 64 byte) flasm would use short length, and flash player would be unable to display these images. flasm 1.41 takes care of this bug now. Thanks to Marcelo Vomaro for pointing me to the issue.

· Liam Morley has found a problem with gotoAndStop/gotoAndPlay actions. I've tracked it down to the following: undocumented argument for SWF action gotoFrame2 exists, containing the number of frames to add to the expression on stack. The behavior is described here.

· Updated Testing embedded actions paragraph for flash MX, Resources, Actionscript protection, and Project state sections.

Changes in previous versions
 
 

Unzip flasm distribution into the folder you like. Because flasm is a command line tool, you'll have to open msdos window first (windows). On mac, open terminal window: Applications/Utilities/Terminal. Then go to the flasm directory with cd c:\flash\flasm (windows) or cd flash/flasm (mac), assuming you saved it here. To execute flasm, simply type in flasm (windows) or ./flasm (mac). Called without arguments, flasm will show you the list of possible commands described below.

flasm command filename

command
-d   Disassemble the given swf file to the console
-a   Assemble the given flasm project
-u   Update the swf, replace flasm macros
-z   Compress SWF with Zlib
-x   Decompress SWF

-d foo.swf
Disassemble foo.swf to the console.

-d foo.swf > foo.flm
Disassemble foo.swf, redirect the output to foo.flm. Another option is to simply call flasm without command: flasm foo.swf creates foo.flm in the same directory.

-a foo.flm
Assemble foo.flm and update the swf defined inside.
The backup of original swf is created with .$wf extension.

-u foo.swf
Disassemble foo.swf to the temporary file.
Execute flasm macros embedded in swf.
Make trivial optimizations automatically:
remove double nots, replace 0.0 with 0, rebuild constant pools.
Create .$wf backup, update the original swf.

It's a good idea to update the final version of swf with -u.
Don't expect the swf to be noticeably faster, it will just make it a bit smaller.

-x foo.swf
Flash MX only. Decompress foo.swf, create .$wf backup.

-z foo.swf
Compress foo.swf, create .$wf backup. Source swf doesn't have to be flash MX file, however, only flash MX player will be able to play the resulting compressed file.

You can add flasm to windows context menu for swf files (right click). Start windows explorer. Select View, Folder Options, click the File Types tab, and choose Flash player movie (or similar) type, which stands for swf file extension. Click Edit button, then click New button. In the Action field enter Disassemble. Click the Browse button, navigate to the flasm's folder, and double-click on flasm.exe. No parameters are needed. Click OK, Close, and Close again. Now right click on any swf and choose Disassemble. Voila - .flm disassembly file is created. Further automating is possible, adding flasm -u for updating swfs or flasm -a for assembling flm files.

If you don't want to do that, look at winflasm - simple windows GUI wrapper for flasm.
 
 

Since SWF is an open format, every content you put in can be extracted. The goal of flasm is to help you optimize your scripts. flasm does not help converting bytecodes back into cut'n'paste actionscripts unlike other tools on the market.

Sadly, no cheating like including cryptical statements in every frame, or even low-level automatic manipulation with some kind of obfuscator will make your actionscript really secure (not to mention protect and enableDebugger tags, which are pretty useless). It's only a matter of time until the next version of actionscript viewers will resolve all this. If you don't believe me, read this. Java has a similar file format and longer history than actionscript, so the battle among decompilers and obfuscators was here already.

Nevertheless, some tools exist which allow you to play around with obfuscation. The oldest one is obfu by Dave Hayden. It uses a simple control flow trick to confuse decompilers. Another one seems to follow the same route. Robin Debreuil's Viewer Screwer is a "real" obfuscator - it renames all your variables, making the code unreadable albeit accessible. I haven't tried any of the above. However, from what I have read, none of them is production stable yet, Robin's one being most promising for the task.

If you want to hide your complex 3D engine, on which hundreds of hours were spent, optimizing it with flasm might help. It will make the extraction of your scripts significantly more difficult. Those decompiler tools must recognize certain patterns in the bytecode corresponding to the high level actionscript statements. Doing optimizations, you're likely to destroy such patterns. Look here for a simple example.

Addition: the above paragraph seems to confuse some people. They just ask me how to do that :) I have to say, you're going to work weeks on your project, with the primary goal of optimization, and a nice side effect of making decompiler' life more difficult. There is no hidden switch for this, and no promise.

And again, it's about hiding algorithms, not passwords. There is no way to hide the password on user machine - only some kind of server based technique may give you additional security.

If you're C programmer, and want to implement decompiler protection yourself, read my thoughts and dive into flasm code. The whole flash community, especially new users, seems to be just desperate on this topic.
 
 

dave@opaque.net released flasm in cooperation with Damien Morton in april? 2001. The original version was able to disassemble the main timeline of the swf and assemble to the first frame or external lib. Although flasm was quite useful for optimizing functions, it was difficult to handle many real-life situations with onClipEvents involved etc. So I've expanded flasm's functionality and fixed some bugs. Dave started then the project on sourceforge.net so that everybody can participate on the development. Currently I am the only person developing it, however. Hope it will change some day. Although I've added much functionality and bug fixes, there are still many things well worth implementing.
 
 

Stack  ·  Constant pool  ·  Registers  ·  Scope

Every actionscript statement is compiled by flash into a couple of simple bytecode actions. For example, a=b*b; is transformed into

constants 'a', 'b'
push 'a', 'b'
getVariable
push 'b'
getVariable
multiply
setVariable

The code above is visual representation of the bytecodes, created by flasm. Flash player/plug-in runs virtual machine that interpretes the bytecodes.

I'll call actions inside of a frame or event action blocks. Flash executes action blocks one after another, so the execution flow inside of a block is never interrupted, neither by event nor by gotoAndPlay() or similar actions. Real parallel execution would be nicer? I'm sure it would dramatically affect player stability, which is great now, considering all things going on in a complex movie.

Stack

Flash virtual machine is stack based, you can not refer to the particular memory location. The stack is a place in memory where data can be stored so that the last entered (pushed) value will be extracted (poped) first from the stack. Every command reads (and pops) operands from stack and pushes the result (if any) on stack. The operand can be an integer or string or float or object reference (actually the name of it) etc.

Further stack explanation by Robert Penner:

If you're familiar with Array.push and Array.pop, those commands are similar to stack manipulations. The stack is like an array of values, except you can only access the value on top, push another value onto the top, or swap the top two values.
For instance, to add two numbers, you have to push both of them onto the stack, then call add. The add command will pop the top two values off the stack, add them together, and push the value onto the stack.

Note btw that the pop action leads to no errors if the stack is empty.

There are two actions that give you additional functionality for stack handling: dup and swap. dup duplicates the value on top of the stack, swap swaps the two topmost values. Currently flash doesn't use dup and swap very often as you'll see in disassembly, but they are of great importance for optimization.

Every actionscript statement, regardless of its complexity, leaves the stack empty after execution to avoid memory leaks. In flash you don't see the bytecodes and don't have to worry about it. Making changes to bytecodes with flasm, however, you should always count what's on stack. Improper stack manipulation often doesn't lead to any errors in flash player. You will not see the 10.000 dead stack entries your loop produced, but the execution will slow down and the swf probably runs out of memory at some point.

Constant pool

At the beginning of every action block where variables, methods or strings are used more than once, flash creates so called constant pool. In fact, if at least one variable is used twice, the pool is created for all strings in the block. Here is an example:

constants 'bottom', 'paused', 'aliensleft', 'fire'

Constant pool can hold up to 65535 strings (in theory). These can be addressed later in your actions with 1 byte (first 256 strings in the pool) or 2 byte (the rest of the pool) reference. Commonly no more than 256 strings are stored, so you rarely meet 2 byte references in swf. Practically the number of strings is limited by overall size of constants action, which can't exceed 65535 bytes like any other action.

The difference to pushing strings or methods directly is in code size only, not in execution speed. If you write push 'paused' after the above constants definition, the bytecode actually is push byte: the second constant from the list and not push string: 'paused'. Although flash itself never redefines constant pool in the middle of the action block, theoretically you're allowed to do this.

In update mode (flasm -u foo.swf) flasm rebuilds all constants, removing empty strings and those referenced only once.

Registers

Flash virtual machine has 4 registers that are addressed r:0, r:1, r:2, r:3. Accessing variables is much slower than accessing registers, so you should store your most used variables there. Only r:0 is currently used by flash, therefore you have enough room for optimization. To store something in a register, you should first put this something onto the stack and then execute setRegister command:

push 'paused'
getVariable
setRegister r:1

Now the value of variable paused is stored in r:1. Instead of asking for paused next time, use push r:1. Note: Unlike most other commands, setRegister does not pop the top value from stack! If you don't need the value stored in register to be on stack, you should manually pop it.

Scope

I've done short testing on what's the scope of registers and stack across the frames.

Registers are not global. The value of register, if defined in a particular frame on _root, is available only in this frame. If some function is defined or movie clip happens here, it can access the register too. It looks like after the showFrame tag occurs in swf, registers disappear.

The stack is global in flash 5 so far I've checked. If the value is pushed in frame 1, frame 5 can trace it successfully. It's accessible in movie clips too. It means flash will not empty the stack for you. In flash MX, situation has changed: it seems flash MX player flushes stack contents after every action block.

I for my part would always treat stack and registers as local for frame or event. Even if stack is more flexible, it totally depends on internal behavior of flash player, which can change easily with future versions.

What's about constants? When constant pool is defined at the frame start, it's valid for any functions in this frame too - no need to redefine. Flash compiles actionscript this way, never seen constants defined in functions in disassembly. As opposite, every event has its own constant pool.

Not very clear? Sorry - you're welcome to do further investigations.
 
 

Alphabetic action list  ·  Unknown actions  ·  protect/enableDebugger  ·  Nested includes
Data types and push  ·  Control flow  ·  Button events  ·  getProperty/setProperty
Playhead control  ·  setTarget/setTargetExpr  ·  ifFrameLoaded/ifFrameLoadedExpr
enumerate/enumerateValue

flasm 1.41 disassembles/assembles all actions supported by flash, regardless of the version. There were numerous syntax changes to the original flasm though, so be sure to decompile with flasm 1.41 before you try to compile.

Every flasm project must start with movie 'moviename.swf'. The moviename.swf is the name of your swf origin. Don't forget to include the file name in quotes. At assembling time flasm first looks here and then tries to overwrite the file. The backup of target swf is created with .$wf extension. If update fails for whatever reason, however, the original file will not be destroyed and no backup will be created.

If compressed attribute is found just after movie name (movie 'moviename.swf' compressed), swf will be compressed (flash MX-like) after assembling. Original swf can be compressed or not, compressed keyword decides about compression of updated swf.

flasm is case insensitive (excluding string values that may be case sensitive). If you must use a single quote in your strings, escape it like this: 'it\'s beautiful' Alternatively you can include string in double quotes: "it's beautiful".

Comments work exactly like in actionscript:
// calculating distance
or multi-line comment:
/* calculating
distance */

The core assembler concepts and some actions are described in this document; if you disassemble any of your swfs, you'll quickly get familiar with other actions.

The full alphabetic action list

addandbitwiseAndbitwiseOr
bitwiseXorbranchbranchIfTruecallFrame
callFunctioncallMethodchrconcat
constantsdecrementdeletedelete2
dividedupduplicateClipenumerate
enumerateValue     equalsfunctiongetMember
getPropertygetTimergetVariablegetUrl
getUrl2gotoAndPlaygotoAndStopgotoFrame
gotoLabelgreaterThanifFrameLoaded     ifFrameLoadedExpr     
#includeincrementinitArrayinitObject
instanceOfintlessThanmbChr
loadMovieloadMovieNum     loadVariablesloadVariablesNum
mbLengthmbOrdmbSubstringmodulo
multiplynextFramenewnewMethod
notoldAddoldEqualsoldLessThan
orordplaypop
prevFramepushrandomremoveClip
returnsetMembersetPropertysetRegister
setTargetsetTargetExprsetVariableshiftLeft
shiftRightshiftRight2startDragstop
stopDragstopSoundsstrictEqualsstringEq
stringLengthstringLessThansubstringsubtract
swapswfActiontargetPathtoggleQuality
toNumbertoStringtracetypeof
varvarequalswith 

I introduced some extra constructs in order to match the swf structure. These serve as containers for flash actions.

frame   defineButton   defineMovieClip   movie   on   onClipEvent   placeMovieClip

Also supported: protect, enableDebugger and enableDebugger2 tags.

Please don't alter the swf structure! It means don't delete, replace or add action block containers! Well, you can add or delete an extra event without causing any damage. But if you remove a frame or change the movie clip id, flasm will be no more able to find the pendant to it and any subsequent statements at assembling time.

Unknown actions support

flasm knows every flash 3/4/5/MX* action. Only subset of possible bytecodes, however, is currently used by flash. Part of bytecodes space is reserved for third-party applications. For example, apple's quicktime added tag 0xAA for quicktime actions. flasm is able to disassemble/assemble actions it doesn't know. The disassembly line looks like

swfAction 0x02 // Unknown action!

If the action has additional data, hexdata part is present:

swfAction 0xAA hexdata 0x43,0x12,0x18 // Unknown action!

The data is shown as comma separated list of hex bytes. If you define your own actions for some proprietary application, there is no need to include tag length in hexdata field - the length is calculated and added automatically if hexdata keyword is found. Don't forget, only bytecodes >0x80 may have additional data.

*All flash MX actions I've found are here. Possibly I've missed some. Got swfAction in your disassembly? Please report!

protect, enableDebugger and enableDebugger2 tags

protect was meant by Macromedia as a hint for authoring program, saying that the author of particular swf doesn't wish it to be opened in flash IDE. protect is not actually protecting anything, any program that deals with swf can simply ignore it. In flasm, protect will be shown, and can be added/deleted. You can place it anywhere in swf, albeit usual location is somewhere near to the beginning. Note protect is not an action, so it has to be outside of action blocks. Passwords are encoded by flash compiler into 28 characters long string. flasm will show the encoded string, but not the password. Actually first 3 characters seem always to be "$1$", probably identifying encryption scheme or like.

enableDebugger is another attempt to secure the content of swf. Always protected by password (flasm will show the encoded string), this tag gives you the ability to "remote debug" the swf. If you don't know the password, debugger will not let you in. If you delete the password, debugger will not let you in. But if you change enableDebugger parameter to '$1$.e$7cXTDev5MooPv3voVnOMX1', empty password will be accepted. Sad.

To say it clear one more time: above tags, including encrypted passwords, give you no protection and can be safely deleted or altered.

flash MX allows debugging on source code level, so there is a new tag enableDebugger2, which is used instead of enableDebugger. It makes no difference at all. However, flasm will not show another tag (63) or contents of external file used by debugger, don't know anything about their format.

Nested includes

I've got requests for flasm to support #include macro to help maintain large projects, where you quickly end dealing with 5.000 lines of flasm code. Now it's here and works as you would expect - if #include 'loop.flm' is found, the macro will be substituted with the contents of loop.flm. Nested and multiple includes are allowed too: foo.flm includes routine.flm, which includes loop.flm and calc.flm. Maximum nesting depth is 10.

Data types and push

Well, push is the core action in swf and we'll go a bit more into detail here. Since you can push all kinds of values onto the stack, the push action has an internal type attribute in swf. While you don't see and can't access the push type from within flasm, flasm decides what type to use based on how your data is formatted.

push type     Number of bytes     what it meansexample
0string length + 1stringpush 'Hello'
14floatpush Y_PROPERTY
20nullpush NULL
30undefinedpush UNDEF
41registerpush r:2
51booleanpush TRUE
68doublepush 3.1415926
74integerpush 25
81constant (0-255)push 'Hello'
92constant (256-65534)   push 'Hello'

Strings must be included in single or double quotes and may contain escape characters: \b, \f, \n, \r, \t and \\. No line break is allowed inside of a string. If flasm founds push 'Hello' statement, it first looks into the constant pool for the current action block. If the string is defined there, 1- or 2-byte reference is pushed (push type 8 or 9); if not, the string itself (type 0).

Integers are recognized in decimal and hexadecimal notation (0xF0F0). Doubles are decimal: -3.1415926. The notation 9.4e-10 is supported too. In addition, constants _NAN, POSITIVE_INFINITY and NEGATIVE_INFINITY are defined as double values.

0.0 is considered double; 0 is an integer. Flash compiler itself always stores 0 as double 0.0. In update mode flasm will automatically replace all 0.0 occurrences with 0, saving 4 bytes per each replace.

push type 1 is only used by flash to store property values. See getProperty/setProperty for the list of property constants. flash 4 stored all number values as strings (push type 0), flash 5 utilizes push type 7 for integers and push type 6 for floats.

However, flash is not the only program creating swfs. I know now of at least one third-party program (3D-Flash Animator), which uses type 1 for actually storing numbers. So while flasm will disassemble type 1 to property constant if possible, all values that couldn't be resolved to any constant will be shown as floats: -3.1415926f or 100.0f. You can use this notation in your flasm projects too, saving 4 bytes per number. Any floating point value which ends with f will be treated as single-precision float and stored with push type 1 (beware of limited precision). The constants _NANF, POSITIVE_INFINITYF and NEGATIVE_INFINITYF are defined too.

One push statement can handle multiple values of different types: push 'Hello', 3.141, XSCALE_PROPERTY. It's not just a shortcut in flasm for 3 single push actions, but a shorter and faster way.

Control flow

Jumps inside of the action block are implemented with branch and branchIfTrue actions. Every high level actionscript construct like if (..) then .. else .. or while (..) do .. is converted to some branch/branchIfTrue pattern. branch simply jumps to the specified label. For example, the translation of if .. then .. else construct always has a branch after its then part, which skips the else part and jumps forward to the end of if. Backward jumps are allowed too, loops always contain them. branchIfTrue takes the condition from stack. In flash 5, if condition == true (condition is converted to boolean if needed), jump happens. Flash 4 checks for number (not boolean) values - if condition == 0, no jump; if condition != 0, jump.

Internally relative numerical branch offsets are stored in swf after every branch instruction. During disassembling flasm creates unique label for every offset with the name label1 .. labelN, which hides the branch offset from your eyes and makes the disassembly more readable. The syntax is branch label4 or branchIfTrue label6. Somewhere in the same action block the label (identifier followed by colon) must be present. You are by no means forced to use identifiers like label5:. Choose meaningful names (LoopStart:, SearchComplete: etc.) instead.

Let's take an example: the really fast countdown loop, which can't be made with flash (and can't be decompiled to any valid actionscript).

push 0,1,2,3,4,5,6,7,8,9,10
loopstart:
dup
trace
branchIfTrue loopstart

First 10 values are pushed onto the stack. Note the last pushed value (10) will be on top of the stack. We have to duplicate the value in loop with dup, because we need it two times: trace pops the first value, branchIfTrue gets the second as loop condition. Since branchIfTrue converts condition to boolean, loop executes until 0 is found, which evaluates to false and stops the loop.

Button events

Every single button event on contains one or multiple of the following:

idleToOverUpoverUpToIdleoverUpToOverDown
overDownToOverUp    overDownToOutDown    outDownToOverDown
outDownToIdleidleToOverDownoverDownToIdle
keyPress  

keyPress is used in the form keyPress 'char' or keyPpress const, for example keyPress 'a' or keyPress _SPACE. All constants you can use in flash authoring are defined:

_LEFT _RIGHT_UP_DN_HOME _END_INS
_DEL_BACKSPACE _ENTER _PGUP _PGDN_TAB _SPACE

You are free to change button event conditions in flasm code.

getProperty and setProperty

Well, the handling of getProperty/setProperty operands is in flash somewhat inconsistent. Actionscript function getProperty("a",_y) can be written in couple of ways:

push 'a', 1
getProperty
    or     push 'a', '1'
getProperty
    or     push 'a', Y_PROPERTY
getProperty

You (and flash itself) can push _y as integer, as string and as float. All push types above are interchangeable in flash 5 with no drawbacks, type conversion happens where needed. In flash 4, however, there was no integer-type push - only string-type and float-type. The table shows property constants (single-precision floats) defined in flasm:

flash namenumber     flasm constantconstant value
_x0X_PROPERTY0.0f
_y1Y_PROPERTY1.0f
_xscale2XSCALE_PROPERTY2.0f
_yscale3YSCALE_PROPERTY3.0f
_currentframe4CURRENTFRAME_PROPERTY4.0f
_totalframes5TOTALFRAMES_PROPERTY5.0f
_alpha6ALPHA_PROPERTY6.0f
_visible7VISIBLE_PROPERTY7.0f
_width8WIDTH_PROPERTY8.0f
_height9HEIGHT_PROPERTY9.0f
_rotation10ROTATION_PROPERTY10.0f
_target11TARGET_PROPERTY11.0f
_framesloaded      12FRAMESLOADED_PROPERTY     12.0f
_name13NAME_PROPERTY13.0f
_droptarget14DROPTARGET_PROPERTY14.0f
_url15URL_PROPERTY15.0f
_highquality16HIGHQUALITY_PROPERTY16.0f
_focusrect17FOCUSRECT_PROPERTY17.0f
_soundbuftime18SOUNDBUFTIME_PROPERTY18.0f
_quality19QUALITY_PROPERTY19.0f
_xmouse20XMOUSE_PROPERTY)20.0f
_ymouse21YMOUSE_PROPERTY21.0f

For some reason flash 5 compiles getProperty using float-type push and setProperty using integer-type push: setProperty("box2", _y, getProperty("box1", _y)) compiles to

push 'box2', Y_PROPERTY, 'box1', 1
getProperty
setProperty

If you compile to flash 4 format, string-type push is used instead of integer-type.

Flasm disassembles float-type push to the constant in the table above if possible, because this push type is used by flash in getProperty/setProperty context only. If numbers or strings are used, however, flasm not even tries to find out what is meant. To do this, flasm had to look ahead to determine if, say, push 2 means push _xscale or simply push 2 for calculating 2 + 2 expression. Sadly, but that's beyond the scope of disassembler.

Playhead control

The swf file format describes three actions for this task: gotoFrame (frame number as operand), gotoFrame2 (takes the frame number from stack) and gotoLabel (frame label as operand). While flasm's gotoFrame and gotoLabel actions are named exactly like their swf format pendants, gotoFrame2 action is not present. For your convenience gotoFrame2 is showed as gotoAndPlay/gotoAndStop. In swf gotoFrame2 is a sole action with a byte flag for play/stop. Additionally, if you have multiple scenes flash puts yet another argument here - the total number of frames in all scenes before the one you're jumping to. These frames will be skipped by flash player - in other words, added to the expression on stack. This allows for using gotoAndPlay/gotoAndStop with a frame number inside of current scene instead of absolute frame number which starts from the beginning of swf. Remember, scenes do not exist in swf. In this case flasm will show you something like gotoAndStop skip 10. Note you're in trouble if your expression represents label string instead of integer frame number. flash player doesn't care and will add frames to skip here too - and playhead jumps to the false frame. Try using _root.gotoAndStop(). Here movie clip method will be used instead of single instruction. It does no corrections and will work properly for labels.

Additionally, flash 5 tends to use gotoAndPlay/gotoAndStop methods of movieclip object (passing them as strings) to control movie clips. Compare the disassembly of equivalent actionscript constructs:

// tellTarget("myClip") gotoAndPlay(25);
setTarget 'myClip'
  gotoFrame 24
  play
end
 
     
     
// myClip.gotoAndPlay(25);
push 25, 1, 'myClip'
getVariable
push 'gotoAndPlay'
callMethod
pop

Flash 5 methods are much slower than core old-style actions, but you have the possibility to overwrite them with OOP. Notice gotoFrame starts counting frames from 0, while "high-level" methods count from 1.

gotoLabel is rarely seen in disassembly, because flash replaces it with frame-based actions exporting swf. Only if flash can't resolve the frame number (if label is not on the same timeline?), gotoLabel will be leaved as is. Labels, however, are still present in swf and can be accessed from javascript or whatever hosts the swf, even if jumps to these labels were eliminated.

enumerate and enumerateValue

enumerate action is something very special. How it works:

1. Gets the object's name from stack.
2. Internally evaluates the object referenced by this name (like getVariable).
3. Pushes NULL onto the stack.
4. Pushes all objects children onto the stack.

Flash MX adds enumerateValue action, which enumerates over nameless object that's already on stack (without first getting it by name).

As far I can tell, the two are used by flash with for .. in loops only. Flash loops then through all objects's children until NULL is found. The current child reference is stored in r:0 for access from loop body. The loop is kind of effective compared to the normal for or while loops. Only with very big arrays slowdown may be experienced because of large data being hold on stack during the loop.

setTarget and setTargetExpr

setTarget action corresponds to tellTarget in actionscript. If target is an expression, setTargetExpr is used, which pops the target string from stack. Flasm shows it like

setTarget '/defender'
  gotoFrame 1
  play
end
    or     setTargetExpr
  gotoFrame 1
  play
end

The end statement does not exist in bytecode; flash uses setTarget '' to mark the end of "targeted" statements.

setTarget '/defender'
gotoFrame 1
play
setTarget ''
    or     setTargetExpr
gotoFrame 1
play
setTarget ''

Since every setTarget is handled by flash 5 this way, I decided to make it look more readable. flash 3 or 4 sometimes leaved out setTarget ''. In this case flasm will add it during disassembling, completing setTarget ... end block.
Nesting of setTarget blocks is not allowed.

ifFrameLoaded/ifFrameLoadedExpr

ifFrameLoaded frameNum .. end and ifFrameLoadedExpr .. end blocks correspond to waitForFrame and waitForFrame2 action tags in swf file format. ifFrameLoadedExpr will take the frame number from stack.
I've chosen actionscript-like names because "waitForFrameExpr .. end" just sounds wrong - execute if not yet loaded. Nesting of ifFrameLoaded blocks is not allowed.
 
 

If invoked with -u command (flasm -u foo.swf), flasm processes macros embedded in your actionscript and updates the swf with flasm statements. It's not unlike embedding assembler in C or Pascal. The syntax is a bit special to let flash compile scripts without errors. Presently flasm supports two constructs inside of actionscript: $flasm ... $end and $include(). Example:

$flasm
"push 'Hello world!'"
"push myTextField"
"setVariable"
$end

The above has the same effect as myTextField = "Hello world!"; actionscript statement. Note that $flasm and $end are variables and not functions, so please don't write $flasm() or $end(). All flasm statements between $flasm and $end must be included in double quotes. Semicolons are not required (but will do no harm). $flasm ... $end blocks are allowed everywhere in your scripts, so don't worry about the right placement. Any restrictions? Sure. Don't put normal actionscript inside of $flasm block, it definitely fails. Don't define frames or movie clips in embedded flasm. If you embed, you are already inside of some frame or event definition. Make sure the stack is empty after your code executes. It's not a restriction, but you probably don't want to cause memory leaks.

All flasm actions behave as expected, there is only one important difference to consider - if you use constants declaration in embedded scripts, flasm will add them to the main pool in the action block instead of redefining it. flasm will also rebuild the main pool, removing empty strings and constants used only once, making the swf yet another bit smaller. Note flasm will not touch your embedded constants or strings, only the main pool that was automatically created by flash.

Guess what $include("foo.flm") does? I don't think I have to explain. One important point: use normal slashes and not backslashes in file path. Latter will be escaped, if not deleted by Flash. Another point: don't insert $include() in $flasm .. $end.

If constants declaration is found in foo.flm, the strings will be added to the main pool. Althougth $include("foo.flm") looks like shortcut to $flasm; "#include 'foo.flm'"; $end, it isn't. The latter happens at assembling time and will not add constants from foo.flm to the main pool. The declaration (if any) will replace the main pool instead - be careful.

While flasm 1.41 works just fine with compression enabled in flash MX, update process will require two additional steps: decompressing and compressing back. If your computer is not very fast, you may consider disabling compression in flash publish settings. You can always compress swf with flasm -z as last step before distribution.

Testing embedded actions from within IDE

Of course you can export the swf, update it with flasm, and check for errors then. Testing directly from flash IDE would be nicer. Unfortunately, flash IDE has no interface to insert preprocessing routine like flasm. Fortunately, Sven König has found a way, and I've implemented it in flasm. While proper installation requires some tweaking, it will work like a charm once you got it. I'll describe the procedure for win, but something similar should work on mac too. So here we go:

1. For flash 5, copy flasm.exe, libz.dll and flasm.ini into the Browser subdirectory of your flash installation. For flash MX, all flash settings are stored elsewhere, so first locate where our target directory is and copy files there:
Windows 2000 or XP: C:\Documents and Settings\[username]\Application Data\Macromedia\Flash MX\Configuration\Browser
Windows 98 or ME: C:\Windows\Application Data\Macromedia\Flash MX\Configuration\Browser
Windows NT: [Windows directory]\profiles\[username]\Application Data\Macromedia\Flash MX\Configuration\Browser
Mac OS X: Hard Drive/Users/Library/Application Support/Macromedia/FlashMX/Configuration/Browser

Note embediing on Mac is untested, please drop me a line if you get it to work.

2. Rename flasm.exe to iexplore.exe

3. Create shortcut to your new iexplore.exe in the same subdirectory. Don't worry, it doesn't affect the real browser.

4. Open flasm.ini in a text editor. Change flaplayer and flabrowser values to contain your flash player and internet browser path, respectively. Long file names are not supported in dos, you should first discover what the corresponding short names look like: "C:\PROGRA~1\INTERN~1\IEXPLORE.EXE" or similar. Even if you're on Win 2000, please use short names. Set the value of flatest to "flaplayer" if you want to test your files in player, and to "flabrowser", if you'll test in browser. On my machine flasm.ini looks so:

flaplayer = C:\GRAPHICS\FLASH5~1\PLAYER\FLASHPLA.EXE
flabrowser = C:\PROGRA~1\INTERN~1\IEXPLORE.EXE
flatest = flaplayer

You can change flatest value later while testing without restarting machine or flash IDE.

6. Done. Now open your file in flash, insert flasm code, make sure HTML and "use default names" boxes are checked in flash publish settings, and press F12 (publish preview).

We just made flash believe flasm is a browser. Flash will compile the swf, look for browser shortcut, check the name (iexplore.exe) is ok, give the HTML file name to flasm. After calculating the real swf name (and here is something to calculate, since the path is URL encoded), flasm will update the swf and invoke browser or player to show it. The dos box appears for the short time, but will only stay open if there are error messages to report. I guess (based on my experience) the most popular error would be "Could not start: c:\...\...\foo.exe", because the path in flaplayer or flabrowser is wrong. Correct it and try again.

Doesn't work? Don't worry, your installation should be OK at this point. Sometimes flash just doesn't start flasm. Enter something in actions window. Or uncheck HTML in export settings, publish, check it again, publish. Or delete iexplore shortcut from browser directory, publish, restore shortcut.

$include may fail in flash IDE if you haven't exported the swf to the right location before, because flash sometimes compiles the swf to the default directory.

How to debug? Stack and registers: at resources is a link to the small debugger by Pavils Jurjans, adjust it to fit your needs. Traces and variables: use debugging version of flash player. Or download iolib. Or write custom trace function that will work in browser. Be creative.
 
 

Actionscript optimizations  ·  Flasm optimizations  ·  Double nots  ·  Thanks

Huge bitmaps, not optimized vectors, false frame rates, animating many movie clips at the same time, using XML with large files, dealing with tons of editable text, streaming high quality sound, or simply viewing swf on mac - in 95% of all cases, bad swf performance has nothing to do with actionscript. flasm, although being "yet another cool tool", is no solution for above problems. Optimizing with flasm makes sense for games, 3D engines, path finding, actually converting large amounts of data - computing things in general.

If you're unsure where is the bottle neck: slow drawing or slow calculating, there is a simple trick: make the player or browser window very small. If performance increases significantly, you probably should optimize your graphics or movie clip structure first.

In a standard programming language, most of the program time is spent in loops and functions called from those loops. For flash, frame loops and often or parallel called events should be investigated too. Period. Don't try to optimize every single line of your code - you'll just make it unreadable, probably omit some important places, and nobody will ever notice 10.000 hours of your hard work. After you've found what's critical, find better algorithm first. Can't be improved? Really? Then start to change actionscript.

Actionscript optimizations

The big question: does flash MX help with performance? While the real testing yet has to be done by somebody, here my feeling: while many things like XML performance really improved, the old "obsolete" flash 4 syntax still performs faster than nice dot scripts, and most tips in this chapter are still valid for MX swfs. Besides of that, while the player is better, the compiler isn't: don't hope to get more speed by simply recompiling your source in MX and producing flash 5 swf.

No changes were made to tips for long time; if you find some of them nonsence or not working for MX, please tell me.

Action blocks are always executed from the start to the end, no event or gotoAndPlay() will interrupt execution of other code. That's the reason why any large for loop will hang the player, no screen updates are made.

Why is flash 5-style coding slower than old flash 4 actions? Casper Shuirink found out the funny practice of flash player 5: new classes and methods are created internally using actionscript! Including string algorithms, array and movieclip methods - everything. Most of them are wrapped around flash 4 actions. Look into Caspers's findings. And again: it's not a visual representation, it's actual script used internally in flash 5.

It's not easy to guess what things in flash cause slowness. Math functions including floating point perform well in general. Coding movie clips handling in flash 5 style is very comfortable, but damn slow. The worst example: myMC.gotoAndStop(), which is 25 times slower than tellTarget("myMC") gotoAndStop(). Where performance is important, always speak to objects in flash 4 syntax, write ../:myVar and not _parent.myVar. Also continue to use getProperty for combined paths like a=getProperty("main/mc" add n,_x). and eval. If you decompile some of your files and compare, you'll see why by simply counting the number of compiled actions. Are these actions not "deprecated" in Flash 5? Read this.

eval is something special compared to, say, this or any other actionscript keyword. In fact, eval is kind of macro - it doesn't have a bytecode, but simply writes its argument onto the stack - at compile time. No doubt it's faster.

Use tellTarget instead of with where possible.

Define local variables in functions with var keyword; don't use var in loops though. Local variables are faster (and generally a good practice).

Unfortunately, identifier length matters, so choose short names for variables. This can be extended to built-in functions too. Creating the function t = Math.tan and substituting all Math.tan occurrences with t will serve 2 purposes: no additional lookup is made for object Math, then for method sin; and the name itself is shorter. It works only for flash 5 methods and functions; flash 4 functions will slow down.

The art of string initialization: if you're using flash 4 string functions like ord() in your code, initialize strings with a="my string" before. Flash 5 methods perform better, if initialized with a=new String("my string"). It seems flash player does extra conversion of literals to objects and back if needed, and this is costly. Nevertheless, avoid flash 5 kind of string handling in important places.

Some functions perform bad regardless of how you call them: String.split() is really terrible and sp = String.split does not help. Branden Hall created custom string functions, which will override flash's default. Where you can't use flash 4 syntax for string handling, use Branden's routines.

Another example: random() is faster then Math.random(), while not that bad. Even if Math.random is referenced with short name like described above.

Use a[a.length] = 25 instead of a.push(25). Yes! Array.push() looks nicer; it even produces smaller bytecode; but it's slower.

Multi-dimensional arrays are not slower than one-dimensional, as far I tested, although I've heard people reporting so.

The trick with replacing b = a*4 to b = a«2 (shift) makes no speed difference in actionscript.

Flash tries to precalculate constant parts of your expressions. The calculation order results from operator precedence. As Robert Penner noticed, rad = Math.PI/180 will actually store calculated value in swf, while rad = c*Math.PI/180 will not.
Conclusion: explicitly set the precedence to enable precalculation (rad = c*(Math.PI/180) in this case).

Flash compiler optimizes library function calls with constant arguments too. Calls like f = Math.sin(0.25) or f = Math.max(3,5) are never saved in swf, the calculated values go here. Neither will if parts with condition that always evaluates to false be stored. If you do your own speed tests, don't be confused by above facts.

Some common optimizations:

not optimized     optimized
_____________________________________________________________________
 
b = a+10;
c = b*2;
c = (b = a+10)*2;
// flash will use register here
_____________________________________________________________________
 
d = a*(b/n+c/n);
e = b*(b/n+c/n);
t = (b+c)/n;
d = a*t;
e = b*t;
_____________________________________________________________________
 
for (i=0;i<10;i++) {
   if (a!=0) {
      someFunction(i*10);
   }
}
var d = someFunction;
if (a) {
   for (var i=0;i<100;i+=10) {
      d(i);
   }
}
_____________________________________________________________________
 
for (i=0; i<x.length; i++)
   x[i] *= Math.PI*Math.cos(y);
var len = x.length;
pc = Math.PI*Math.cos(y);
for (var i=0; i<len; i+=2) {
   x[i] *= pc;
   x[i+1] *= pc;
}

for and while loops show no speed difference. It depends on how you write them. The most optimized actionscript examples of both, looping down to 0, produce the same bytecode: for(var i = 10; i--;) {} and i = 10; while (i--) {} The third part of the for loop, absent in my example, is actually in the body of loop, so you can't compare it with a normal while.

for .. in loop, which uses enumerate keyword, will go through an array slightly faster than normal for loop - at least with medium size arrays. With a huge array, the opposite is true.

Avoid multiple parallel hitTest() functions in events - often seen in games. If the player is killed after any touch with an enemy, and you have 100 duplicated enemy clips, don't include any code in the enemy clip enterFrame event. Create the new mc and insert the enemy clip here. Then duplicate inside of this parent clip. Now you can check with only one hitTest() if the collision takes place. If you need to, use some custom math then to calculate what enemy was hit. Since most of the time no collision occurs, you'll make a really big improvement in fps. If you must check for multiple objects colliding with each other, start with this article.

Mac flash player is very slow compared to PC. Test on macs early.

Doing your own tests, remember: the player inside of flash authoring leaks memory and behaves different to standalone player and browser plugins. Internet Explorer seems to perform way better executing swf directly (as opposite to html-embedded). If you run two swfs simultaneously, performance decreases rapidly. Even if the swf window loses focus, swf performance drops.

The list is by no means complete - there are surely tons of things I have yet to discover myself. I mostly do not say "3.45 times slower", because comparisons are very context dependant, exact values will vary. My "slower" just means "noticeably slower, no situation ever makes it faster".

Flasm optimizations

After you're done in actionscript, we can start to optimize with flasm. Basically only two meaningful low-level features are not accessible from actionscript and therefore subject of flasm work: stack and registers. Let's optimize a simple loop first, using stack. Our actionscript is

for (n=0; n<1000; n++) {
   someFunction(n)
}

Flash compiles this loop to the following bytecodes:

constants 'n', 'someFunction'    
push 'n', 0.0
setVariable
  label1:
push 'n'
getVariable
push 1000
lessThan
not
branchIfTrue label2

push 'n'
getVariable
push 1, 'someFunction'
callFunction
pop

push 'n', 'n'
getVariable
increment
setVariable
branch label1
  label2:
// Store all variables in constant pool
// Push the string 'n' and starting 0 onto the stack
// Initialize loop counter: n = 0
// Start of the loop
 
// Get the value of 'n' again
// Push loop bound
// Evaluate boolean condition: "n < 1000 ?"
// Invert: now "n >= 1000?"
// If "true" is on stack, go to the end of the loop

// Loop body
// Get the value of 'n' again
// Push the number of args (1) and function name
// function call is made with n as argument
// Pop the possible function result away - it's unused

// Push 'n' two times
// Evaluate 'n' again
// n+1 on stack now
// n = n+1
// jump to the loop start - unconditional
// end of the loop - adressed with branchIfTrue above

What we immediately see, the n variable is evaluated many times here. getVariable action is slow compared to stack operations, and the n is only used as local counter. Why not discard n, keep the counter on stack and use it over and over, thus eliminating all getVariable calls? We also don't need the constant pool declaration, since n will disappear, and someFunction name will be only used once. The number of jumps can be reduces to one, too. We know we have to call someFunction(0), so there is no need to check for the condition on the top of the loop. Look at opzimized version:

push 0
  loopStart:
dup

push 1, 'someFunction''    
callFunction
pop
 
increment
dup
push 1000
lessThan
branchIfTrue loopStart
pop
// No need for double 0.0, integer 0 will do it
// Choosing meaningful name
// dup the counter - our function will "eat it up"

// Push the number of args (1) and function name
// function call is made with n as argument
// Pop the possible function result away - it's unused
// Now the counter is on top of the stack again
// Increment it
// Dup the counter - condition evaluation will "eat it up"
// Push loop bound
// Condition evaluation: counter < 1000?
// Jump to the loop start, counter is on top
// Should remove counter from stack after the loop

We can go even further. If our function, say, fills an array with some calculated values, it makes no difference to do it from 0 to 999 or from 999 "down to" 0. We can eliminate lessThan action in this case, because branchIfTrue is kind enough to convert 0 to false, and all other numbers to true for us.

push 1000
  loopStart:
decrement
dup
push 1, 'someFunction''    
callFunction
pop
dup
branchIfTrue loopStart
pop

We moved decrement to the top of the loop, because otherwise branchIfTrue would immediately exit loop if the counter value is 0 and not let us execute someFunction(0).

As you see, we end with a pretty clear loop version, which will be much faster than the original flash. How much, depends on what someFunction() does. As the next step you would go there and optimize it.

Now why should one use registers at all? They are faster than variables, but still slower then stack. Why not keep all the values on stack so they go to the top just in the moment you need them?

The problem is, if you're doing this with 2 or more variables, your algorithm may want to access them in a different order than they're stored. If some value is only required, say, at the start and at the end of your routine - no problem, it happily lives somewhere at the bottom, waiting for its time coming, and lets you work with other values on top. But for often needed values it doesn't work. While we have swap action to exchange two top values on stack, we can't directly access the third. Even if you find some illusionistic approach to access many variables, you'll just slow the execution with big amounts of swap commands.

In general: dup is of course faster than setRegister r:1 and push r:1 later. But if you have to swap, situation changes: dup/swap combination is not faster than setRegister/push. In fact it's slightly slower if you merge push r:1 with other push's: push r:3, r:1, X_PROPERTY - push merges do make a real difference.

Enough words, let's go now and optimize some real actionscript. We'll write the function to get min value in a number array. Our code looks so:

Array.prototype.min = function () {
   var m = Number.MAX_VALUE;
   var i = this.length;
   while (i-- > 0) {
       if (this[i] < m)
           m = this[i];
   }
   return m;
}

If we start with var m = this[0]; and then loop down to 1 instead of 0, the function will run a bit faster. It would, however, not help us. We will try to check against 0 later in bytecodes. There is no check against 1 instruction.

We could try this code too:

Array.prototype.min = function () {
   var m = Number.MAX_VALUE;
   for (var i in this) {
       if (this[i] < m)
           if (typeof(this[i]) == 'number')
               m = this[i];
   }
   return m;
}

I think it's the fastest actionscript code possible here, because flash compiles for .. in loops to more effective bytecodes then other loops. At the same time it leaves less room for our optimization. I've optimized both, and our first version is the better one to start with. Look at optimized bytecodes:

push 'Array'
getVariable
push 'prototype'
getMember
push 'min'
 
function ()
  push 'this'
  getVariable
  setRegister r:1
  pop
 
  push 1.79769313486231e+308    
  setRegister r:2
  pop
 
  push 'this.length'
  getVariable
 
loopStart:
  decrement
  setRegister r:0
  push r:0, r:2, r:1, r:0
  getMember
  lessThan
  branchIfTrue loopContinue
 
  push r:1, r:0
  getMember
  setRegister r:2
  pop
 
loopContinue:
  branchIfTrue loopStart
 
  pop
  push r:2
  return
end
 
setMember
// Array.prototype.min  
 
 
 
 
 
 
 
// evaluate 'this'
// store it in r:1
// delete it from stack
 
// Number.MAX_VALUE
// store Number.MAX_VALUE in r:2
// delete it from stack
 
 
// evaluate this.length
// now we loop down to 0
// counter on stack
// counter--
// store counter in r:0
// stack: counter, this, min, counter, counter
// stack: this[counter], min, counter, counter
// stack: min<this[counter]?, counter, counter
// stack: counter, counter
// if no branch happens, new min value found
// stack: counter, this, counter, counter
// stack: this[counter], counter, counter
// store this[counter] in r:2 as new min
// delete it from stack
// stack: counter, counter
// check if 0 is not reached yet:
// stack after branch: counter
// loop is over, now clean up the stack:
// pop last counter value (0)
// get min from r:2
// return min
 
 
// assign Array.prototype.min to function

The function is optimized for large random arrays. The tightest part is optimized most - all pushes are composed into one statement. We even accept multiple assignments of equal values to the min (when min == this[counter]) in order to do this. This is the reason to keep the counter in r:0 and not on stack, too. One of my first versions worked this way - swaps, dups and multiple pushs. No good. Also, if new min is found, this[counter] is calculated one more time. We could store it in r:3 after our first calculation, but it makes the whole thing slower - more instructions execute in the main part of loop.

The optimized version of for .. in loop:

function ()
  push 'this'
  getVariable
  setRegister r:1
  pop
 
  push 1.79769313486231e+308
  setRegister r:2
  pop
 
  push 'this'
  enumerate
 
loopStart:
  setRegister r:0
  push NULL
  equals
  branchIfTrue loopEnd
 
  push r:2, r:1, r:0
  getMember
  lessThan
  branchIfTrue loopStart
 
  push r:1, r:0
  getMember
  setRegister r:2
  pop
 
  branch loopStart
 
loopEnd:
  push r:2
  return
end

As you can see, it's in parts the same. The interesting place is that with enumerate. All references to array elements are pushed to the stack by flash with one action. At the bottom NULL value is stored, loop constantly checks against it as end condition. The code is somehow more elegant than our former version. But with large arrays it gets slower, because the whole array is on the stack, compared to 4-5 values in our first version.

So how fast are our functions after all? The "normal loop" optimized version is 3 times faster than the fastest actionscript. The optimized for .. in loop is approximately 2-2.5 times faster.

And the last note: besides of optimization of existing actionscript, there are well things you simply can't do (or veeery ineffective) in Flash without getting into bytecodes. Look at Robert Penners message on passing a variable number of arguments to functions.

Double nots

In certain cases flash writes double nots in your code. Consider actionscript code if (a<=b) ... else ... Since only lessThan and branchIfTrue exist, but no moreThan or branchIfFalse, two inversions are created:

push 'b'
getVariable
push 'a'
getVariable
lessThan
// a>b?
not // now inverted: a<=b?
not // prepare for branch to the else condition: again a>b?
branchIfTrue elseCondition

As you see, flash 5 is not very flexible compiling your statements and does not change the order of operands in a expression or use another pattern for if statement. It doesn't really make sense. The only purpose here could be an attempt to force type conversion to boolean. The next action you always see in the code, however, is branchIfTrue. And this action does type conversion itself.
So flasm will automatically remove those nots in update mode.

Thanks

My very special thanks go to the people on flashcoders list, whose ideas helped me to the better understanding of optimization and flowed into above examples:

Rasheed Abdal-Aziz, Ralf Bokelberg, Robin Debreuil, Zeh Fernando, Gary Fixler, Branden Hall, Dave Hayden, Damien Morton, Amos Olson, Robert Penner, Casper Schuirink.
 
 

Since Macromedia continues to call some actions deprecated, I decided to leave my statements below unchanged too. They were written long time ago, turned to be true, and now probably refer to flash 7 (or flash XXL). My only error - MX IDE has not dropped support for old actions.

Every time one uses flash 4 actions or syntax, someone says it's deprecated, bad coding practice and better avoided. I think one point has to be made here: there is a difference between actionscript and bytecodes.

When flash 6 is released, it will not affect the playback of swfs you produced. I can't believe Flash 6 will no more be able to play Flash 5 or 4 swfs. Think of millions sites that were paid to us: some of them will be never updated. They will optimize actions and make them perform better than flash 4 actions? Hard to believe, but if so, flash 6 player will have yet to reach the audience - approximately 1 year after release the user base will be big enough to consider. And then, all swfs will be still working.

If flash 4 actionscript will be not supported in flash 6 IDE (very possible), it would just mean you have to change some optimized lines in your code, and only if you update it. Flash 6 will surely be able to do most of it automatically - think of flash 4 syntax conversion in flash 5. If not, there will be finally the possibility to replace over all scripts. If not, someone will write custom utility to do so. Can you imagine flash 6 can't import your flash 5 fla source?

Anyway, at least half year or year after release (and we don't even know what the release date is) must pass before we start to sell flash 6 applications to the clients. And even then: if you'll need to make minor updates to your applications, why use Flash 6 for the task? You'll still have flash 5. And if there will be new fascinating possibilities in 6, you'll probably rewrite the complete code.

I for my part would suggest not bothering of "deprecated" or not and simply writing code more appropriate for your task. I don't encourage you to forget about flash 5 syntax, however. It only makes sense to use flash 4 actions where speed is a problem.
 
 

After assembling flasm source or updating swf with flasm you'll often see your swf having few less bytes even if you haven't changed anything in the bytecode. Besides of trivial optimizations flasm does in update mode, there is one more reason for it. Flash can save block lengthes in the swf as 2 or 6 byte records. 6 bytes are only needed if the block is larger than 62 bytes. Flash, however, often uses 6 bytes where 2 bytes will do. Although flasm does this too in certain cases, most blocks are optimized during assembling. So I have 400 less bytes on the file of 90kB length without optimizing anything. I don't know of any disadvantages of it, so enjoy this unexpected flasm bonus.
 
 

While it's good practice to keep scripts smaller than 64k (compiled) per frame, it's possible to get larger. But the sole action record - constants, push, function and other is limited to 64k because of 2 bytes length field size. Since flash will create (in 99% of cases) the constant pool for all variables and methods, overflow is possible. It's a pity flash itself doesn't tell you the script is too big. Instead flash compiler writes overflowed value to the length field without errors or warnings. If you try to execute the swf, flash player crashes, or actions are omitted. flasm will stop disassembling with an error message if such overflowed constant pool definition is encountered in swf.
 
 

· Adobe LiveMotion 2 puts some strange code into events. flasm will show a couple of unknown actions, which aren't intended to be actions at all but strings so far I can tell (or just random memory garbage). Most likely it's a bug in LiveMotion, because it doesn't look very conform to swf format. Maybe they know something I don't. If you have problems with swfs produced by LiveMotion 2 please contact them :) If you're sure I'm wrong with it, please contact me.

Don't know of other bugs at the moment. If you find any nontrivial bug, fell free to mail me your file. To make it clear: if you disassemble the swf and assemble it back without changes, the resulting swf must run properly. The update mode must work too. There are absolutely no voodoo behaviours or unsupported features in flasm. If you encounter problems, there must be a serious bug in our implementation and your report is highly appreciated.
 
 

This page can always be found at flasm.sourceforge.net or http://www.nowrap.de/flasm.html.

The source is on sourceforge too: http://sourceforge.net/projects/flasm, albeit a bit outdated. Since I'm one single person working on flasm currently, it's pointless to keep CVS in sync all the time. Get newest source from this page.

The original flasm page is http://www.opaque.net/~dave/flasm/. There resides the first version and the useful explanation of flash 5 bytecodes by Dave.

Very nice example of 3D engine by Florian Krüsch, optimized with flasm.

Compare tree animations made by Amos Olson: standard actionscript version and the optimized one.

Look at path finding swf made with flasm by Casper Schuirink. Here is the source.

The sad story: David Emberton maintained flasmaniacs list devoted solely to flasm. His provider dropped list support suddenly, now neither the list nor archives are available. Thanks to Branden Hall, new SWFcoders mailing list just started (19. August 2002). To subscribe send an empty message to swfcoders-subscribe@chattyfig.figleaf.com (normal mode) or swfcoders-digest-subscribe@chattyfig.figleaf.com (digest mode).

Many complex actionscript problems are discussed on the highly frequented flashcoders mailing list, maintained by Branden Hall at http://chattyfig.figleaf.com. It's the place to discover more about flasm optimization too. Never ask there something like "What's an event?" though. Don't expect a polite reaction for this kind of questions. Read the flash help first, and then flashcoders previous messages.

Flashcoders Wiki promises to grow into the most complete and up-to-date actionscript resource available, already very informative.

http://www.openswf.org is the source for swf format related topics. In the moment they have only flash 4 format description, more to come?

You may wish to visit Macromedia flash player format and SDK licensing site at http://www.macromedia.com/software/flash/open/licensing/. Since I have not used their sources, I don't know how detailed is their flash 5 format spec.

At the prototype site you'll find some flash functions redefined for speed or flexibility, and also many new and useful ones. Often it's better to start flasm optimizing from one of them.

Pavils Jurjans has written the litlle debugger for flasm, useful while embedding flasm code in actionscript. The debugger will show stack and register contents. I hope something like that will be integrated in flasm's future releases.

Albert Chosky has created flasm syntax files for EditPlus. If you're using EditPlus, it's a good idea to get them.

For people who don't like to work with command line, and don't like to register flasm as swf handler in windows explorer either, there is a winflasm by Sharriff Aina, simple windows interface to flasm.

Syntax file for UltraEdit, submitted by anonymous russian flasmer. Don't know how complete it is, thanks anyways.
 
 

Yes, flasm is completely free and distributed under the following BSD-style license:

Copyright (c) 2001,2002 Opaque Industries and Igor Kogan
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  • Neither the name of Opaque Industries nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Macromedia(r) and Flash(tm) are trademarks or registered trademarks of Macromedia, Inc. in the United States and/or other countries.

Macromedia(r) does not sponsor, affiliate, or endorse this product and/or services.
 
 

flasm 1.4

flasm 1.4 fully supports new features introduced by flash MX:

· movie clips can handle all button events now, so onClipEvent format has changed inside of SWF. Note buttons do not support movie clip events!

· initialize event - component parameters are no longer set with load event unlike used to be for smartclips in flash 5, but with new initialize event.

· initMovieClip tag - new tag deals with component initialization: look for #initclip .. #endinitclip in flash MX help.

· New actions: greaterThan - the opposite of lessThan, now used in loops and conditional statements; instanceOf - tests if object is an instance of class; strictEquals - look in flash MX help for "===" and "!==" operators; enumerateValue

· Debugger now utilizes enableDebugger2 tag.

· flash MX implements standard Zlib compression. The compressed swf will stream too - great! flasm supports decompressing/compressing now. However, the exact byte counts after decompressing and compressing may differ from original. Sometimes flasm's compression is slightly better, sometimes flash wins. Don't worry, it's not a bug - flash player will play all flasm compressed swfs. flasm will compress older flash 4/5 swf files too, however, only flash player 6 will play them. For windows version of flasm, libz.dll (included in distribution) should be placed in flasm or windows folder. For now, flasm can't handle compressed swf seamlessly. You'll have to decompress with flasm -x foo.swf before performing other actions. You can compress your swf later with flasm -z foo.swf This inability also affects IDE mode, where you need to uncheck "compress" option in "publish settings" dialog.

· Documentation still has to be rewritten - I just added some words about MX here and there.

flasm 1.36

· Support for passwords in protect tag added. Also new enableDebugger tag.

· Now it's easy to include disassemble command in windows context menu for swf extension. Thanks to Pavils Jurjans for suggesting the feature.

flasm 1.35

· After cygwin people have updated their product, and after I've fixed some minor bugs in flasm, flasm executable compiled with cygwin finally supports embedding mode and interacts with flash properly. So I moved from djgpp to cygwin/ming32. What it means to end users: smaller executable (uses windows libraries), no limitation for path length in embedding mode. What it means to developers: djgpp installation on win 2000 was almost impossible. Now you can compile with a standard cygwin environment; djgpp is still supported though.

· I've put the flasm source into CVS on sourceforge. Maybe it becomes useful somewhere, somehow - if other developers join the project.

· flasm now uses ini file instead of environment variables in testing mode. No need to restart machine or flash to change testing target. Look here for details. Currently flasm.ini is only useful in embedding mode. Tell me if you would like to customize flasm behavior here.

· Function parameters are included in quotes in disassembly to avoid name collisions with your actionscript. Of course, you can use syntax without quotes too while writing your flasm code.

· Overflowed constant pools no more crash flasm disassembler. You will be also not allowed to assemble pools larger than 64k.

· Unclosed setTarget/setTargetExpr blocks created by older flash versions will be completed automatically.

· Bug in swfAction fixed.

· The protect tag is shown in disassembly.

· Former strange looking waitForFrameExpr/waitForFrame .. else skip .. actions are renamed to ifFrameLoaded/ifFrameLoadedExpr and support conventional syntax: ifFrameLoaded frameNum .. end, ifFrameLoadedExpr .. end

· As usual, documentation updates. I know it's hard to find what's new because of the structure I've choosen for this page - so sorry. Compare old and new ones with hex editor :)

flasm 1.32

· The handling of floating-point values updated, infinity and not-a-number constants are now supported, Number.MAX_VALUE and Number.MIN_VALUE are correctly resolved.
I have finally understood that push type 1 (property) actually pushes a single-precision float value. It caused confusion before, because property constants had strange meanings if treated as integers. Look at updated push and getProperty/setProperty topics for details.

· "Label defined twice" and other error messages added.

· New FLABROWSER environment variable to enable showing flash help while using flasm in embedded mode.

· Increased portability across systems: Makefile shows no warnings; some problematic math functions redefined.

· Escape characters are recognized in getUrl too.

· Embedding mode: multiple user constant declarations allowed now in one frame. They all will be added to the main pool for this frame. Useful when, say, many functions with independent declarations are inserted with $include. Some bugs with $include fixed.

· On windows 2000 dos box was closing immediately after flasm reported errors in embedding mode. flasm 1.32 now waits for user key press (windows version only). Don't know if this misbehavior occurs on mac too, please report.

· Empty movie clip events compile without errors.

· action keyword used in flasm for unknown actions support renamed to swfAction to prevent collision with Branden Hall's ACK eventEngine.

· The maximum number of labels per action block increased from 256 to 1024. The former caused crashes with very large scripts.

· Numbers as movie clip instance names are supported now. Not necessarily a good practice, but why should flasm stop where flash goes?

· flasm compiled flash 4-style oldAdd action improperly as flash 5-style add - fixed.

· Documentation updates: real-life optimization example etc.

flasm 1.3

· Nested includes supported with maximal depth 10.

· Support for flasm macros in flash IDE is here.

· Finally got line numbers for error messages working! Don't laugh, it's a challenge with flex. Line numbers and file names are reported for included files too.

· Error messages are sent to the console even if the output of disassembler is redirected to the file. Some specific messages added instead of standard "parse error". Please tell me where you would like to see warnings.

· push type 9 discovered, which does 2-byte constant pool lookup.

· Corrected the wrong assembly of gotoAndPlay/gotoAndStop actions thanks to Sven König who noticed the bug.

· Support for unknown actions added.

· Working mac executable (compiled on SourceForge's compiler farm). No more need to compile flasm yourself.

· For those who must compile: Makefile adjusted to work for all systems.

· Problems with large actionscripts fixed. My test file contains 93 kb compiled actionscript in one frame only. It works fine now.

flasm 1.22

· Correct handling of property constants: more properties defined - the list is full now, NAME_PROPERTY fixed.

· Escape characters \b, \f, \n, \r, \t and \\ defined. An actionscript string containing those characters will now be disassembled without producing weird line breaks or other anomalities.

· Backup of original swf is created before update.

flasm 1.21

· The famous double nots written by flash will be now automatically removed at assembling time. So forget them and concentrate on optimization!

· Label handling improved. flasm 1.2 sometimes forgot to write labels at the end of action block.

· Fixed: on windows 2000 flasm 1.2 could not update the original swf and you had to manually rename the temp file created foo.$$$ into foo.swf.

· Flasm 1.21 crashes no more if called without arguments or if input file is missing.

· disassembler no more writes nonsense 53.39999999999964 values instead of 53.4.

· Added toString action corresponding to a = String(b); in actionscript.

flasm 1.2

It was my first "official" version, the main difference to Dave's original was the ability to deal with the whole swf. Event disassembly added, some missing bytecodes, few errors fixed. Don't remember all the changes exactly, this page did not exist yet.
 
 

The bad news: I'm not going to implement exciting new features. Time passes, and I've pretty much lost interest. Now diving into other things: Linux, Python etc. The good news: flasm is pretty stable, and bug fixes (if any) will happen. Now probably is the best time to take over flasm and make it The Most Important Flash Tool for everybody :)

Here are some features I would like to see in future releases:

XML generation

Let flasm parse XML and write actionscript structures for it on the fly. Instead of using server processing, you would create swf containing XML on your machine, which will be "percent loaded" into the main movie. Much faster anyway than flash parsers. One could write a validating parser too.
P.S. Turned obsolete with improved XML performance in flash MX - but only for flash MX player, flash 5 player still suffers badly.

Decompiler protection

Not an easy task, but possible. I can mess current versions of decompilers right now, but it will take them just a week to fix it, so decompiler crash is no good. The best protection scheme would produce code that gives no errors in a decompiler, but simply doesn't work after re-compiling. I'm still unsure how to do it.

The second best way: decompiler is not actually executing code or playing swf, so the presence of properties at run time can be used to produce "unpredictable" branches. If you get something like if (root._width>0) you have the perfect branch, whose result is defined and impossible to resolve by obfuscator at the same time. It can decode the if, but will not know where the branch goes. In else part (dead code) any confusing garbage can be placed. The control flow is now unreadable, speed decrease is minimal - just replacing unconditional jumps with conditional ones.

The third best way: obfuscation. That means renaming all variables, functions and movie clips to unreadable garbage. Robin Debreuil implements this approach in his Viewer Screwer. For various reasons, doing that in flash is very complex. One could create all kinds of objects based on object names at run time. What it means is you're never sure obfuscator will work on any swf without side effects.

The dangerous possibility of placing code in other tags, say images, jumping there and back, will probably kill the flash player first. The decompiler will still be able to follow branches and decode it. So I would better handle branch offsets as expected by the player.

Replacing of register variables in IDE mode

If one writes $r1 = a+b in actionscript, flasm would update swf and store a+b in r1. This clear and nice concept, suggested by Eli Jehoel a while ago, is (against expectation) not that easy to implement. In short, flasm will have to count what's on stack all the time.

Actionscript profiler/tracer

The tracer is badly needed - if you've worked with flasm in flash authoring, you already know that. The profiler: flasm would write profiler code at the start/end of every frame/event/function. Then values (number of calls, max execution times) will be stored in, say, Object.profiler and could be accessed from actionscript. It's rather flash interface task, not hard to implement in C. In flash, we'll need to have a scrollable/draggable/scalable/minimizable window containing traces and profiler entries. For profiler, the user should be able to sort entries by different criteria (you know, like in ms outlook).
 
 

If you more or less understand SWF format, know C and have heard of Flex and Bison, you can help to develop. The current code isn't very clear, feel free to ask me. And, anyways, chances are good you're much better developer than me!

I would like to know about your experience with flasm in real life projects. Something unlogical? Bug reports? Feature requests? Please don't ask for windows IDE though :) Don't forget the mac people (you know, those strange black dressed persons working in design agencies). Add custom definitions to your editor instead and share them. flasm syntax files for EditPlus already exist, made by Albert Chosky. UltraEdit syntax file is here too.

Apropos mac. I don't own one and don't plan to. It would be nice if someone describes his flash/flasm workflow for mac.

Even more interesting are your optimizations. If I've missed something important and common enough, please tell me. If it's rather uncommon, but you think your flasmed swf is inspiring, I may wish to link it from this page.

And the last: I don't think my english is terrible. I don't think it's any good, too (anyway it's damn dry and poor compared to how I used to speak russian or german). If you see anything in this document that should be corrected, described much better, considered bad style, sending wrong political message, hurting the people of good will in the world - tell me.
 
 

Igor Kogan
Dave Hayden