Advertisement

Quick Tip: How to Debug an AS3 Error #1063

by

It's time for another debugging Quick Tip. We'll continue our focus on specific (and common) errors that tend to stymie less-experienced ActionScripters. In this case, we'll cover Error #1063, the argument count mismatch error.


Step 1: Being Bad

First, let's create a situation where the error occurs. Open up a new Flash document (assuming you're following along with Flash Pro CS3+, otherwise this sample is easily adapted to a Flex project). Open the Script Editor and enter the following code:

import flash.events.MouseEvent;

function onClick():void {
    trace("click.");
}

stage.addEventListener(MouseEvent.CLICK, onClick);

You can probably spot the problem already; if you've spent any amount of time writing ActionScript 3 you'll have found yourself writing event handlers. If not, don't feel bad -- we'll go over it all in due course.

If you run the Flash file as is, and then click on the stage, you'll produce the following run-time error message:

ArgumentError: Error #1063: Argument count mismatch on Untitled_fla::MainTimeline/onClick(). Expected 0, got 1.

And we never get to the trace statement that should have run after clicking.


Step 2: Breaking It Down

So what's going on? In this case, Adobe's verbiage for the error is actually not so bad, and if you've gotten used to parsing a run-time error message, its meaning might be pretty clear. But not everyone is as smart as you, so here's the breakdown for everyone else.

ArgumentError -- This is somewhat inconsequential information, but it shows the specific Error class that was thrown. We've got something that's not as general as a simple Error, and we've entered a specific categorization of error associated with arguments (to functions and methods).

Error #1063 -- Here we're just giving the formal error number, like all good run-time errors. You can use this code to more easily locate it in Adobe's run-time error documentation.

Argument count mismatch... -- In more proletarian terms, there were the wrong number of arguments sent to a function. Which function? It's...

...on Untitled_fla::MainTimeline/onClick(). -- This simply identifies the function that received the wrong number of arguments.

Expected 0, got 1. -- We get a bit of extra information in this error description. This details the count mismatch. This phrase will change according to the nature of the specific error, but in our case it's saying that the function was written without any arguments in the signature, but a single argument got sent to it anyway.

Flash likes its ducks in a row. So, it notices this discrepancy and decides to throw a tantrum error, because it would rather you (the developer) figured out what went wrong than that you simply ignored the problem. This is good, because if the count mismatch went the other way (expected 1, got 0), then we'd be stuck without an argument for a required parameter, and the function would do Dog knows what.


Step 3: The Root of the Problem

The nature of the error should be clear at this point, but you may still be wondering why it occurred at all. Where did that superfluous argument come from?

The argument isn't exactly superfluous. It's expected, actually, since we've hooked up our function to be an event listener. The event system in Flash has the notion of an event object that encapsulates aspects of the event that occurred. This object gets passed to the listener function as the sole argument. So, we expected 0 because we wrote our function without any parameters, but we got 1 because the event dispatcher sent along an event object.

Now you may be wondering why the compiler didn't catch this error. It's true: if you wrote this:

function sayClick():void {
    trace("click.");
}
sayClick(42);

Then the SWF won't even compile, because you'll get this error:

1137: Incorrect number of arguments.  Expected no more than 0.

The difference is that in the latter example, we have actual code that calls the function with the wrong number of arguments. That is, we wrote the line down that calls the function incorrectly. The compiler can look at the line that defines the function, and the line that calls the function, and compare them for discrepancies, and sound the alarm when they occur.

However, in the original example, there is no line of code written down that literally calls the function by name. Instead, the function is called by reference. When we add the event listener, we pass in the function, and at that point it's a variable, not a function call. This reference gets stored by the event dispatcher, and then executed dynamically when the event occurs (that's a real high-level overview of how the event system works, but we don't have time to go deeper). So, the line of code that ultimately calls the error-causing function is a rather generic line of code that uses indirection to get the job done, and therefore something much harder for the compiler to catch.

(In my opinion, Adobe could at least register the addEventListener line at compile time, and go looking for the function reference by name. If it finds a match, it could check the function signature for a proper event argument, and produce errors accordingly. It still couldn't be done in a foolproof way, but it might go a long way to catching these errors before actually running the SWF.

The main point, though, is that this run-time error has a compile-time counterpart, but that the run-time error occurs when the function is called by reference and not directly by name.


Step 4: Fixing the Hole

The rain is getting in when we call the function by reference, and have a discrepancy in the number of arguments. We generally have two options: we can modify the call, or modify the function's arguments. In this particular example, we can't modify the call, as that happens inside EventDispatcher, code to which we don't have access. That leaves us with modifying the arguments.

This, again, has two options. First, we can simply add the argument. This lines up the number of arguments and from here on out, everything will be copacetic. We don't need to use the argument, we just need to have the function “catch” it when it's called.

function onClick(e:MouseEvent):void {

The second option is to, again, add the argument (no way around that, I'm afraid). However, if you originally wrote the function as a regular function and not an event listener, and are calling it from elsewhere in your code without arguments, you may appreciate this variation. Make the argument optional, and default it to null:

function onClick(e:MouseEvent=null):void {

This will work well with the event system: it gets sent an event object and can catch it. It also works well with your existing code; if no argument is sent, the parameter default is used and the function proceeds.


Step 5: Callbacks

Note that this error is not limited to event listeners, although that's probably the most common context where you'll experience it. Ultimately it's the using of functions stored in variables, as opposed to called by name, that leads to the error. This is how the event system works. We can rework the original example to produce more or less the same error, only without the click:

function sayMyName(name:String):void {
    trace("Hello, " + name);
}

var funcRef:Function = sayMyName;

funcRef();

Again, we get past the compiler error because we have a layer of indirection between the function definition and the function call. Thus, we get the run-time error (expected 1, got 0).

It's not always this cut and dry, though. If you utilize callbacks in your code, you might fall prey to error 1063. Callbacks are sort of like event listeners, only there is no formal, built-in mechanism for implementing them. They're basically just functions you pass around by reference, which are stored (either temporarily or long-term) by some other process, which then calls the callback function at the appropriate time.

Tweening engines typically implement these. Some go for a more formal event-driven system, but TweenLite, for example, utilizes callbacks for receiving notifications about the tween progress. This line:

TweenLite.to(someClip, 1, {onComplete:tweenFinished, onCompleteParams:[42, "answer"]});

...would call a function named tweenFinished at the end of the tween, passing in two parameters to the function. This technique is ultimately more flexible then events, as you are not limited to just the single event object as a parameter. But it does yield itself to similar vulnerabilities to error 1063 due to the nature of passing functions around by reference.


That Is All

That wraps up another Debugging Quick Tip. Thanks for reading, and I hope you learned something along the way!

Advertisement