LogoLogo
  • Home
  • Projects
  • About
  • Contact

Logical Or Assignment Bug in ASC2

Devon O. · August 25, 2013 · Actionscript, AIR, Flash · 6 comments
21

So, here’s something to keep an eye on if switching to AIR 3.8 (or 3.7 with the ASC2 compiler).

Last week at work we decided to finally make the jump from AIR 3.5 to AIR 3.8; something I was pretty excited about and had been looking forward to for awhile. After applying the update, though, I noticed some unit tests failing and sat down to investigate. Several places in our app, we map loaded xml files to data objects – a pretty common thing to do. In the parse methods we may use typical logical or assignment to fill properties with default values if they aren’t present in the xml. So it’s not uncommon to run across a line like this:

this.name = xml.@name || “Default Name”;

Now, in the ASC1 compiler this always worked fine. After switching to the ASC2 compiler in AIR 3.8, though, this is where our unit tests began falling down. In the case above, rather than assigning the “Default Name”, the name property was being assigned an empty string. So I decided to have a little look under the hood and see exactly what was going on. So here’s a real quick test class file I compiled to examine closer:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package
{
 
import flash.display.Sprite;
import flash.events.Event;
 
public class Main extends Sprite
{
 
    public function Main():void
    {
        if (stage) init();
        else addEventListener(Event.ADDED_TO_STAGE, init);
    }
 
    private function init(event:Event = null):void
    {
        removeEventListener(Event.ADDED_TO_STAGE, init);
 
        testXML();
    }
 
    private function testXML():void
    {
        var xml:XML = <test/>
 
        var name:String = xml.@name || "Default";
 
        trace("Name=(" + name + ")");
        // output:
        // ASC1: Name=(Default)
        // ASC2: Name=()
 
    }
}
 
}

As you can see in the comments, compiling with ASC1, I got the results I was looking for and used to. In ASC2 though, I got an empty string. Using Adobe’s SWF Investigator, I decompiled the two swf files to the Actionscript Bytecode (ABC). With the ASC1, here is what’s going on at the point you try to get the xml’s name attribute:

Assembly (x86)
1
2
3
4
5
6
7
8
9
10
11
12
13
32   getproperty       name //nameIndex = 10
34   coerce_s          
35   dup               
36   convert_b         
37   iftrue            L1
 
41   pop               
42   pushstring        "Default"  //stringIndex = 27
44   coerce_s          
 
L1:
45   coerce_s          
46   setlocal2

Now, I’m no expert in ABC, but it’s easy enough to follow along with what’s happening. We get the property name from the xml and type it to a String (‘coerce_s’). Since there is no property ‘name’, that will be coerced to an empty String. We then convert that to a Boolean (‘convert_b’). Of course an empty string converted to a boolean is false so the ‘iftrue’ fails, we push the string “Default” on to the stack, type it as a string (‘coerce_s’), then set the local variable ‘name’ to that value.

Compiled in ASC2 though, we see a different story:

Assembly (x86)
1
2
3
4
5
6
7
8
9
10
11
28   getproperty       name //nameIndex = 9
30   dup               
31   iftrue            L1
 
35   pop               
36   pushstring        "Default"  //stringIndex = 31
38   coerce_a          
 
L1:
39   coerce_s          
40   setlocal1

This time, we get the property name but do no coercion or conversion – we just wind up with an an untyped ‘something’. But an untyped ‘something’ is not a ‘nothing’, so the iftrue passes, we jump to the L1 block where we then coerce that ‘something’ into an empty string and set set the local variable ‘name’ to that.

So, we wind up with two very different results which may not be what you actually want. In this case, though, we only get an empty string where we may expect some default text. Worst case scenario, our app displays some blank text. Annoying, yeah, but not the end of the world.

But wait, it gets worse.

If you use logical or assignment with interfaces, things get really crazy. Here’s another test class file to test:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package
{
    
import flash.display.Sprite;
import flash.events.Event;
 
public class Main extends Sprite
{
    
    public function Main():void
    {
        if (stage) init();
        else addEventListener(Event.ADDED_TO_STAGE, init);
    }
    
    private function init(event:Event = null):void
    {
        removeEventListener(Event.ADDED_TO_STAGE, init);
        
        var f:Test = new Test("blah");
        testClass(f);
    }
    
    private function testClass(someArg:ITest=null):void
    {
        someArg ||= new Test("yadda");
        
        trace("someArg=(" + someArg + ")");
        // output:
        // ASC1: someArg=(Test [foobar=blah])
        // ASC2: someArg=(-2.1750519003895844e-311)
        
        var name:String = someArg.foobar;
        
        trace("Name=(" + name  + ")");
        // output:
        // ASC1: Name=(blah)
        // ASC2: Null Object Error is thrown at line above
    }
}
    
}
 
interface ITest
{
    function get foobar():String;
    function toString():String;
}
 
class Test implements ITest
{
    private var _foobar:String;
 
    public function Test(foobar:String)
    {
        _foobar = foobar;
    }
 
    public function get foobar():String { return _foobar; }
 
    public function toString():String
    {
        return "Test [foobar=" + this._foobar + "]";
    }
}

Let’s look at the byte code for ASC1 again.

Assembly (x86)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
18   getlocal1         
19   coerce            private::ITest //nameIndex = 10
21   dup               
22   convert_b         
23   iftrue            L1
 
27   pop               
28   findpropstrict    private::Test //nameIndex = 8
30   pushstring        "yadda"  //stringIndex = 23
32   constructprop     private::Test (1) //nameIndex = 8
35   coerce            private::ITest //nameIndex = 10
 
L1:
37   coerce            private::ITest //nameIndex = 10
39   setlocal1         
40   debugline         28
42   findpropstrict    trace //nameIndex = 11
44   pushstring        "someArg=("  //stringIndex = 25
46   getlocal1         
47   add               
48   pushstring        ")"  //stringIndex = 26    

We get our local ‘someArg’ variable, type it as an ITest object (‘coerce’), and convert that to a Boolean. Of course that boolean is true, so the ‘iftrue’ passes and we jump to the L1 block, type the variable to an ITest once again, set our local variable again, then do the trace.

But again, with the ASC2 compiler we see something very different:

Assembly (x86)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
14   getlocal1         
15   iftrue            L1
 
19   findpropstrict    private::Test //nameIndex = 12
21   debugline         26
23   pushstring        "yadda"  //stringIndex = 36
25   constructprop     private::Test (1) //nameIndex = 12
28   setlocal1         
 
L1:
29   getlex            trace //nameIndex = 17
31   getglobalscope    
32   debugline         28
34   pushstring        "someArg=("  //stringIndex = 38
36   getlocal1         
37   add               
38   pushstring        ")"  //stringIndex = 39

Again, with ASC2 we get our local variable but do no coercion or conversion, so again we wind up with an ‘untyped something’. But, again, an ‘untyped something’ is not nothing, so the ‘iftrue’ passes and we jump to the L1 block. This time though, we still don’t coerce the ‘untyped something’ into an object and instead jump right into the trace statement and we see that our ‘untyped something’ is actually represented as an infinitesimally small number in memory (see the output comments in the actionscript above). But as you can see in the comments above, when we try to access properties of this ‘untyped something’ we wind up with a null object error.

As you can imagine, compared to the minor annoyance of an empty string, a null object error is a real ball deal breaker.

I’ve filed a bug report on this behavior with Adobe which you can check out here. In the meantime, though, if you’re making the jump into AIR 3.8 (or AIR 3.7 with the ASC2 compiler), I highly recommend avoiding using OR assignment like the plague.

  Facebook   Pinterest   Twitter   Google+
ASC2.0
  • Google’s Text To Speech Engine in Flash
    May 27, 2010 · 21 comments
    9640
    5
    Read more
  • Laws of Nature and of Nature’s God
    July 04, 2008 · 0 comments
    1615
    3
    Read more
  • Logical Or Assignment Bug in ASC2
    August 25, 2013 · 6 comments
    6108
    21
    Read more
6 Comments:
  1. Thanks for this information. I voted for your bug.

    Since Adobe does not accept the usual premise that a defective product needs to be fixed, rather they sit back and wait to see how many people report the bug, and only expend resources after some cockamamie voting process, I would appreciate your looking at my bug, and voting for it, as well, otherwise, I am sure I will never see a fix.

    https://bugbase.adobe.com/index.cfm?event=bug&id=3609616

    Thanks

    Terry Corbet · August 25, 2013
  2. Hi Terry,

    Thank you for the vote. I went over to check out your bug report, but I couldn’t figure out the error. I’m obviously not understanding something. Are you saying that class A cannot be set as the document class? If I try to compile a class with a package that doesn’t actually mirror the file structure (i.e. if I have a class with package any that isn’t inside a directory named ‘any’), I get the error you describe in ASC2, but I get a similar error in ASC1: “Error: A file found in a source-path must have the same package structure ”, as the definition’s package, ‘any’.” I have a feeling I’m just not understanding the problem correctly. Maybe you could re-explain it.

    Devon O. · August 27, 2013
  3. How do people still believe voting for bugs help anything?

    makc · September 04, 2013
  4. It’s a crazy system. Voting for features I can understand, but voting for bug fixes?? Just prioritize and start working the damn things..

    Devon O. · September 04, 2013
  5. Just by coincidence, I started using ||= in one of my latest projects using 3.8, and ran into null errors. Figured I was doing something wrong, so just switched back to the ‘longhand’ way.

    Voted.

    T · September 12, 2013
  6. Thanks, T. Appreciated.

    Devon O. · September 12, 2013

Leave a Comment! Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Devon O. Wolfgang

AIR | Unity3D | AR/VR

Unity Certified Developer

Technical Reviewer of “The Essential Guide to Flash CS4 AIR Development” and “Starling Game Development Essentials”

Reviewer of “The Starling Handbook”

Unity Engineer at Touch Press.

Categories
  • Actionscript (95)
  • AIR (16)
  • Flash (99)
  • Games (7)
  • Liberty (13)
  • Life (53)
  • Shaders (20)
  • Unity3D (21)
Recent Comments
  • Devon O. on Unity Ripple or Shock Wave Effect
  • Feral_Pug on Unity Ripple or Shock Wave Effect
  • bavvireal on Unity3D Endless Runner Part I – Curved Worlds
  • Danielius Vargonas on Custom Post Processing with the LWRP
  • Luca G on Unity Ripple or Shock Wave Effect
Archives
  • December 2020 (1)
  • December 2019 (1)
  • September 2019 (1)
  • February 2019 (2)
  • December 2018 (1)
  • July 2018 (1)
  • June 2018 (1)
  • May 2018 (2)
  • January 2018 (1)
  • December 2017 (2)
  • October 2017 (1)
  • September 2017 (2)
  • January 2017 (1)
  • July 2016 (1)
  • December 2015 (2)
  • March 2015 (1)
  • September 2014 (1)
  • January 2014 (1)
  • August 2013 (1)
  • July 2013 (1)
  • May 2013 (1)
  • March 2013 (2)
  • December 2012 (1)
  • November 2012 (1)
  • September 2012 (3)
  • June 2012 (2)
  • May 2012 (1)
  • April 2012 (1)
  • December 2011 (2)
  • October 2011 (3)
  • September 2011 (1)
  • August 2011 (1)
  • July 2011 (1)
  • May 2011 (2)
  • April 2011 (2)
  • March 2011 (1)
  • February 2011 (1)
  • January 2011 (2)
  • December 2010 (3)
  • October 2010 (5)
  • September 2010 (1)
  • July 2010 (2)
  • May 2010 (5)
  • April 2010 (2)
  • March 2010 (7)
  • February 2010 (5)
  • January 2010 (5)
  • December 2009 (3)
  • November 2009 (1)
  • October 2009 (5)
  • September 2009 (5)
  • August 2009 (1)
  • July 2009 (1)
  • June 2009 (2)
  • May 2009 (6)
  • April 2009 (4)
  • March 2009 (2)
  • February 2009 (4)
  • January 2009 (1)
  • December 2008 (5)
  • November 2008 (2)
  • September 2008 (1)
  • August 2008 (6)
  • July 2008 (6)
  • June 2008 (9)
  • May 2008 (4)
  • April 2008 (3)
  • March 2008 (4)
  • February 2008 (9)
  • January 2008 (7)
  • December 2007 (6)
Copyright © 2017 Devon O. Wolfgang