I recently analyzed one of our large Actionscript 2 apps for the possibility of converting it over to Actionscript 3. Doing this is a little bit behind the times, since AS3 came out years ago, but this app is huge, so procrastination set in. The application in question has over 100,000 lines of code, not including comments or other non compiling lines. In short, it is fairly monstrous in size for an AS2 application. The goal was to see how hard it would be to migrate it over to AS3 so that it would compile and function as expected.

For information on why you would want to move to AS3, look at summaries like this. Also, Adobe’s migration site is a good place to start.

Since this app had many output artifacts (swfs and what not), I chose one of them, changed the corresponding fla file’s publish settings to Actionscript 3 and tried to compile. This lead to a lengthy and sometimes frustrating cycle of:

  • Attempt publish, resulting in many compiler errors
  • Fix compilation errors
  • Repeat

After many cycles, I eventually wound up with no compilation errors, and discovered a new, albeit familiar cycle of:

  • Attempt execution of swf, resulting in run-time errors
  • Fix cause of run-time error
  • Repeat

After many cycles of this, I had a working swf. Along the way, I took notes as to the types of changes I was making so that I could later look at the entire code base to estimate how much change was potentially there. Ultimately, it was decided that we would likely not be putting the effort to convert the entire app at this time, but the list of changes may help others who are going through a similar migration.

This list is not complete, for sure. It’s also not guaranteed that I didn’t mess up in taking my notes, since the whole process was fairly frustrating. Your mileage may vary and I offer no warranty of any kind :)

Basic Stuff

These changes applied to all classes that existed in the app.

  • Add package declarations, and make sure classes are public. For example:
            package foo.bar {
              import ...;
              public class Photny extends Blork {
                ...
              }
            }
  • Make sure methods and properties called by external package classes are public
  • private variables in parent classes accessed in children must be marked protected in order to be accessed in the child class.
  • Use the override attribute when overriding a parent class method:
    	override function showMessage() { ... }
  • Include import statements where missing (AS3 requires and will complain when it’s not present), for example:
    • MovieClip: import flash.display.MovieClip;
    • TextFormat: import flash.text.TextFormat;
  • Change mx.controls.* to fl.controls.*
  • Replace Void declarations with void:
    	function set doSomething():Void { ... }

    becomes

    	function set doSomething():void { ... }
  • Functions that have parameters but are called without them, for instance:
        	private function processMode(pCleanup:Boolean) { ... }
            //  and is called like this:
           	processMode();

    This is no longer allowed. Use function defaults in AS3:

        	private function processMode(pCleanup:Boolean=true) { ... }
            //  now you can call it like this:
           	processMode();
  • Default values for objects have changed
    • Number: default values for Number class is now NaN, so beware of this:
                var num:Number;
                if((num==undefined) || (num==null)) { ... }// this will not be true
    • Boolean: default value is false, (not null)
    • int, uint: default is 0
  • Look for use of arguments.length or arguments[] – use the ...rest construct instead

Forms

This app happened to make limited use of Flash MX forms (mx.screens.Form class). Forms support does not seem to be carried over into Actionscript 3 at all, so the use of these had to be refactored out entirely. I simulated forms, placing various input elements (TextInputs, Buttons, etc) into separate layers and then grouping them together so I could make them visible and invisible in the same way that the forms were displayed.

XML

The XML class is different,now based on E4X. For example, look for stuff like:

          xml.firstChild
          // or
          if (allNodes[u].nodeName == "address") { ... }
          // or
          var outputNodes = allNodes[u].childNodes;

and get rid of it in favor of E4X constructs, which allow you to access things directly, like:

          xml..address[0].street_address[0]
          ...
          myFunc( xml..address[0].street_address[0].@type );
          ...
          for each(var address:XML in xml..address) {
              var childNodes:XMLList = address.children();
              ...
          }

MovieClip differences

  • Replace calls to MovieClip properties, for instance:
    Old New
    _visible visible
    _x x
    _y y
    _width width
    _height height
  • The MovieClip.createEmptyMovieClip() is no longer used, given the new DisplayList architecture, so code like this:
            mc.createEmptyMovieClip("myChildMc", 1000);

    Has got to go in favor of something like this:

            var myChildMc:MovieClip = new MovieClip();
            addChild(myMc); // assuming you're a DisplayObject

    Further, if you go and address your child MC with code like this:

            mc.myChildMc

    You’ll have to refactor that too. There is no association outside of what the display list does (via addchild()). As a real hack, since MovieClip is a dynamic AS3 class, you could manually set the child to the parent by doing this after you create the child:

            mc.myChildMc = myChildMc;

    And then it should work. Another alternative is to add myChildMc with a name:

            myChildMc.name="child-mc";
            mc.addChild(myChildMc)

    Then get it later this way:

            // less efficient than mc.getChildAt(int) but it works.
            (mc.getChildByName("child-mc") as MovieClip)
  • Calls that set the depth to the highest possible, like this:
            stage.createEmptyMovieClip("upperMC", 1000);

    Cannot be done with AS3. You will need to make sure that the added DisplayObject has the highest possible index value via DisplayObjectContainer.setChildIndex(). So in other words, either add the thing you want on top last, or change it after you’ve added everything so that it has the highest index value.

  • Drawing related methods have been moved off of the MovieClip class:
    • MovieClip.clear() is no longer available, replace with MovieClip.graphics.clear()
    • MovieClip.lineTo(0,0) is no longer available, replace with MovieClip.graphics.lineTo(0, 0)
    • MovieClip.endFill() is no longer available, replace with MovieClip.graphics.endFill()
    • MovieClip.lineStyle(2,lnColor) is no longer available, replace with MovieClip.graphics.lineStyle(2,lnColor)
    • MovieClip.beginFill(color, fillAlpha) is no longer available, replace with MovieClip.graphics.beginFill(color, fillAlpha)
    • MovieClip.moveTo(50,25) is no longer available, replace with MovieClip.graphics.moveTo(50,25)
  • MovieClipLoader() calls are no longer allowed, so replace this type of stuff:
               var listenerObj = {};
               listenerObj.homeref = this;
               listenerObj.onLoadInit = Delegate.create(this, mcLoaded);
               listenerObj.onLoadError = Delegate.create(this, mcNotLoaded);
    	   loader.addListener(listenerObj);
               loader.loadClip(theUrl, _mainMC);

    with something like this:

               _loader = new Loader();
               _loader.contentLoaderInfo.addEventListener(Event.INIT, onMcLoaded);
               _loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onMcIOError);
               _loader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, onMcStatus);
               // start a timer, if it triggers, we have our timeout condition
               _mcTimer = new Timer(20000, 1); // 20 seconds
               _mcTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onMcTimeout);
               _mcTimer.start();
               trace("about to load mc (" + theUrl + ")");
               _loader.load(new URLRequest(theUrl));
               ...
               // BEGIN: mc loading handlers
               private function onMcLoaded(pEvent:Event):void { /* turn off timer */ }
               private function onMcIOError(pEvent:IOErrorEvent):void { /* turn off timer */ }
               private function onMcStatus(pEvent:HTTPStatusEvent) { /* turn off timer, make sure we got 200 status */ }
               private function onMcTimeout(pEvent:TimerEvent):void { }
               // END: mc loading handlers
  • _root no longer exists the way it did in AS2. This is a topic unto itself. If you absolutely need to have something like the root MC, you could keep a reference to it in some type of global object and set it yourself.

Various Component differences

  • You can no longer create TextFields from MovieClip objects. So instead of stuff like this:
                mc.createTextField("myHeader",mc.getNextHighestDepth(), 60, 35, 200, 30);

    You should be doing something like this:

            var myHeader:TextField = new TextField();
            myHeader.x=60;
            myHeader.y=35;
            myHeader.width=200;
            myHeader.height=30;
            parentMC.addChild(myHeader);
  • Replace calls to TextField.setNewTextFormat(myFmt) with TextField.defaultTextFormat = myFmt
  • Replace calls to
            mc.createClassObject(CheckBox,"myCheck",mc.getNextHighestDepth(), {label:"Enable Logging"});

    with something like this:

            var myCheck:CheckBox = new CheckBox();
            myCheck.label = "Enable Logging";
            mc.addChild(myCheck);
  • Setting the targets to UIScrollBar objects has changed. Change this:
            myScroll.setScrollTarget(mc.someTextArea);

    with something like this:

            myScroll.scrollTarget = someTextArea;
  • TextField (vertical) scroll positions used to be controlled like this:
            _errorMesgBox.scroll = 0;

    but is now this:

            _errorMesgBox.scrollV = 0;
  • Setting html text on mx.controls.Labels is different, so change:
            label.html = true;
            label.text = "hello world";

    to this:

            label.htmlText = "hello world";

Nickels and Dimes

There are alot of smaller changes that were not very frequent, but nonetheless were allowed by AS2 but not AS3.

  • Remove nulls in function definitions. Change this:
    		someHandler.onFocusOut = function(null, mc) { ... }

    to this:

                    someHandler.onFocusOut = function(pUnused:Object, mc) { ... }
  • Operators ‘and’, ‘or’ are no longer valid. Replace with ‘&&’, ‘||’. Change this:
            if ( foo == null or foo == 0) { ... }

    to this:

            if ( foo == null || foo == 0 ) { ... }
  • The CSSStyleDeclartion don’t exist in AS3. Use the flash.text.StyleSheet class instead.
  • Delegates must be removed, so replace and refactor calls like this:
            myCheck.addEventListener("click", Delegate.create(this, toggleStuff));

    into something like this:

           myCheck.addEventListener(MouseEvent.CLICK, toggleStuff);

    then alter

            function toggleStuff() { ... }

    to:

            function toggleStuff(pEvent:MouseEvent) { ... }

    Be sure to examine the inside of the toggleStuff() function and make sure that we use pEvent.target when we need it, etc.

  • References to _global.whatever are no longer allowed. One solution is to use a homebrewed standin Global class, then, something like:
            _global.styles.helloStyle.styleName = "helloStyle";

    becomes:

           Global.global.styles.helloStyle = new mx.styles.CSSStyleDeclaration();
  • The setInterval() is different. What was previously something like this:
            _myTimer = setInterval(this, "checkStuff", 100);

    turns into this:

            import flash.utils.*;
            ...
            _myTimer = setInterval(checkStuff, 100);
  • There is no longer a Color class that can change the color of a MovieClip, so this type of thing cannot be done any longer:
    	var colr = new Color(mc);
    	colr.setRGB(0x000000);

    use this instead:

            import flash.geom.ColorTransform;
            ...
            var nuColor:ColorTransform = new ColorTransform();
            nuColor.color = 0x000000;
            mc.transform.colorTransform = nuColor;
  • Calls to System.capabilities.os were moved, depending on what you’re looking for, replace with:
             import flash.system.Capabilities;
             Capabilities.os...
  • Include import for setting system’s clipboard
              System.setClipboard(myTextArea.text);

    to:

              import flash.system.System;
              ...
              System.setClipboard(myTextArea.text);
  • Calls to fscommand(string, bool) require import, so replace with:
            import flash.system.fscommand;
            fscommand("foo", "true");
  • This now requires import:
            System.security.allowDomain(_theUrl);

    so include this:

            import flash.system.Security;
             ...
            Security.allowDomain(_theUrl);
  • the Key class is no longer available, so this:
    	var keyListener:Object = new Object();
    	keyListener.homeref    = this;
    	keyListener.onKeyDown = function() {
    		if (Key.isDown(Key.CONTROL) && Key.isDown(Key.SHIFT) &&(Key.getCode() == 54) ) {
    			trace("you pushed ctrl-shft-6" );
    			this.homeref.onDKeyPress();
    		}
    	};
    	Key.addListener(keyListener);

    Should be rewritten like this:

            import flash.ui.Keyboard;
            ...
            stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
            ...
            private function onKeyDown(pEvent:KeyboardEvent):void {
              if(pEvent.controlKey && pEvent.shiftKey && Keyboard.NUMBER_6) {
              	trace("you pushed ctrl-shft-6" );
    		onDKeyPress();
              }
            }

And the list, I’m sure, goes well beyond this.  Good luck if you consider migrating such an app. You should carefully weigh the benefit of doing so. If you do, just remember that the code you wind up with will likely be far from ‘best practices’ AS3, but if successful you’ll wind up with a faster app due to the more efficient AS3 virtual machine, among other potential benefits.