I’m back from a month off for winter break! Today I’ll start off with a very short article to ease back into things. Today I’ll cover a “gotcha” that got me recently and resulted in a bug report that was pretty tough to solve. Read on to see what it was.

Consider a multi-player game with a Player class that has:

  • A __isLocalPlayer Boolean flag field indicating whether the Player instance represents the player at the computer running the Flash app
  • GameOptions::localPlayer3D and GameOptions::remotePlayers3D compile-time constants indicating if local and remote players should be rendered in 3D, respectively
  • __render3D and __render2D fields for the 3D and 2D representations of the player, respectively. Each may or may not be loaded and, if not loaded, will be null.

Now let’s write a little function that checks whether the Player has a render loaded based on its status as a local or remote player, the game options, and the currently-loaded render(s). I submit the following tiny snippet of code for your careful examination:

public function get hasRender(): Boolean
{
	return ((__isLocalPlayer && GameOptions::localPlayer3D)
		|| (!__isLocalPlayer && GameOptions::remotePlayers3D))
		__render3D : __render2D;
}

Give yourself a pat on the back if you caught the error. If you didn’t, allow me to simplify the above to show a view closer to how the compiler is looking at it:

public function get hasRender(): Boolean
{
	return (X
		|| Y)
		Z : W;
}

See it now? In case you don’t, let’s drop the newline:

public function get hasRender(): Boolean
{
	return (X || Y)
		Z : Y;
}

There are two things going on here. First, I’ve omitted the ? part of the ternary operator, which makes it not a ternary operator at all. This was the bug– a typo– and fixing it by adding the ? back in will fix this problem. But it’s a good idea to take a moment and ask yourself about the second thing going on here: why does it still compile? I like to ask this question because I often feel outraged that the compiler has let me off the hook without an error or even a warning. But it did let me off, so let’s figure it out.

As you no doubt know of AS3 (and AS2 and JavaScript, as this article applies there too), semi-colons are not required at the end of statements. This makes the first statement (return (X || Y)) totally valid, but my intent by adding a newline was simply to format the code in a more pleasing way and not to split apart statements. This is part of why the semicolon was originally required in AS3’s ancestors: C, C++, Objective C, Java, and the rest of the C family. This explains half of why the compiler was OK with my code, but why did the second line work for it?

The second line (Z : W) is a line no sane programmer would write, at least in that area of the function. What I’ve done, albeit inadvertently, is to create a label (Z) identifying a black of code (W). But this label is totally bogus! This function provides no way of ever getting to this label! Labels can only be used in three places: case statements inside a switch and as the target of break and continue statements inside a loop. I have neither a switch nor a loop in the function, so the label should be considered invalid. Further, the label and its code occur after an unavoidable return statement, which happens to be the very first line of the function and the line right before the label!

I lay much blame at MXMLC’s door for these two reasons. A modern compiler simply should not allow such code to pass without at least a compiler warning. Personally, I would configure the compiler to raise an error in this case if I were given the option. Unfortunately, MXMLC does not provide this option and I am left to discover the bug on my own. Lastly, for completeness sake, let’s have a look at the correct version of the code:

public function get hasRender(): Boolean
{
	return ((__isLocalPlayer && GameOptions::localPlayer3D)
		|| (!__isLocalPlayer && GameOptions::remotePlayers3D))
		? __render3D : __render2D;
}

There. I added a question mark. :-/