Strategies for Debugging PDF JavaScript
Hunt bugs down and zap them!

Ordinary mortals need only worry about two of life's certainties: death and taxes. If you're a programmer, you worry about three things: death, taxes and bugs.

In an earlier column, I suggested strategies for avoiding bugs in your JavaScript code. But avoiding bugs and finding them are two different tasks. Sooner or later, you're going to have to go on an expedition to locate the source of a problem in your code. That's when the real fun begins.

Heed the Error Message

Often, the nature of the bug will be apparent from the error message that you get on the JavaScript console. Consider the following examples:

  • "JavaScript error in [something] script of field '[whatever]' at line [number]": This message points you to the bug location in a script attached to a form field, providing the script is self-contained. If it hands control to a document-level (or other) script, the bug may actually be in the externally referenced function rather than in the field script.
  • "Unterminated string literal": Means you probably broke a string across two or more lines (using a carriage return) without using a continuation backslash at the end of the broken line(s).
  • "Missing ; before statement": Can mean (among other things) that you nested too many quotation marks in a string, causing premature string termination.

  • "NaN": Not a number. Means you probably tried to do a numeric operation on something that couldn't be converted to a number.
  • "Invalid or unknown property, set not possible": Means you tried to do something like this.getField('abc').value = 99.9, but there is no field with the name 'abc'.
  • "[variable name] has no properties": Means you used a dot operator on something that was not an Object.
  • "[variable name] is not defined": Means you tried to manipulate a variable that was never declared.

If you're lucky, most bugs of the above kind(s) will be due to simple typing mistakes; debugging will come down to finding (and eliminating) a stray quotation mark or carriage return. But that won't always be the case.

Developing Good Intuition

What about errors that don't generate any runtime console messages? Your first indication of a problem may simply be that your program doesn't take the desired action on a field value or fails, in some way, to do what it's supposed to do. (In rare cases, you may actually lock up your computer or cause Acrobat to crash.) Without any console error messages to guide you, how are you supposed to locate the source of a hidden problem?

This is where debugging becomes as much of an art as a science. With experience, you'll come to develop a pretty good sense of intuition as to where a given type of bug in your code is most likely to be found. Of course, having good intuition is partly a matter of knowing how to "factor out" your code into short, well-defined functions, so that bugs are more likely to reveal themselves in terms of functionality. (Which would you rather do: Find a bug in a 100-line function, or find a bug in a 5-line function?) This, in itself, suggests a useful heuristic: Divide a buggy function up into shorter functions and see if the bug goes away. If it's still present, at least it should be easier to locate.

The Binary-Search Technique

I like to think of the debugging process as a matter of forcing the computer to show me what's wrong with my code. I like to make the computer point me to the exact spot in my code where things start going haywire. One way of doing this is by making the program show you (in an alert dialog) key variable values at various breakpoints in the code. The app.alert() method is great for this, since it interrupts program flow at whatever point you insert it, and doesn't return control to the program until you dismiss the alert dialog. Even more convenient is the fact that JavaScript tries to convert whatever you put as an argument to app.alert() into a String.

But it can be inconvenient, when you're dealing with large amounts of code and don't have the slightest idea where a bug is, to insert app.alert() statements throughout your code in a million different places. Fortunately, there's an easy way out: the binary search technique.

The binary search method goes something like this. Suppose you have a complicated function that's 125 lines long (yeeouch!) and you suspect the bug is lurking somewhere in that dense tangle of expressions. First, insert app.alert() at a breakpoint that's near the middle of the code. Make the argument to app.alert() a key variable whose value will tell you unambiguously whether execution has proceeded normally (or not) up to that point. Run the code and see what the variable's value is at the breakpoint. If the value is defective, take out app.alert() and reinsert it at a point that's midway between the beginning of the function and the last place where you used app.alert(). (If the variable's value was normal, insert app.alert() midway between the end of the function and the last breakpoint.) Run the code again. Repeat the process until you've found the bug location.

The idea is that each time you establish a new breakpoint, you partition the code in such a way as to narrow down the location of the bug, ruling out 50% of your code as possible bug territory each time. In this fashion, you can zero-in on a bug's location in log-N attempts, where N is the number of lines of code and "log" means the base-2 logarithm. In the example above, it should take no more than 7 tries to find a bug in 125 lines of code. (The base-2 log of 125 is six-point-something.) That's much better than moving the breakpoint a few lines at a time and hoping for the best...a process that could take all day.

Intermittent Errors

Bugs that show up intermittently can be extremely difficult to find, for obvious reasons. The first order of business is usually to find a reliable way to replicate the error. Often that can be done by substituting a blatantly extreme value for a key variable. (Hint: Some good values to try are zero, 'null', and 'undefined'.)

Frequently, when something goes awry on an intermittent basis, it's because a variable acquired a value that's either downright nonsensical (or undefined) or outside some minimum or maximum limit. For example, if you have a routine that relies on taking the square root of a value, and that value turns out, on occasion, to be a negative number, you've got a problem of the 'NaN' variety. An even more common situation is one in which one variable is divided by another, but no check is ever done as to whether the denominator might be zero. (Dividing by zero is a no-no. It gives rise, in JavaScript, to the value Number.POSITIVE_INFINITY or Number.NEGATIVE_INFINITY.) But the situation that gives rise to the number error may be something that only happens under very special conditions; hence the intermittent-ness

As a rule, if you're experiencing a bug that's intermittent, you should start thinking in terms of key variables having taken on on pathological values under "boundary conditions." Look through your code to see if there are any places where you should be doing "sanity checks" on variables. If you have variables whose values must fall between set limits at runtime, write a routine called Clamp() that simply enforces a bounds-check on any passed-in variable:


function Clamp(n,lowlimit,highlimit) {

if (n < lowlimit) return lowlimit;
if (n > highlimit) return highlimit;
return n;
}

At the beginning of every routine, do a sanity check on incoming parameters (arguments). If it doesn't make sense to continue execution when an argument has a null value or is equal to zero, check for this possibility and return control to the caller as necessary. Don't assume that incoming parameters have logical values! In fact, in JavaScript, you shouldn't even assume that incoming parameters have a particular type.

Misbehaving Operands

JavaScript is a loosely typed language, meaning that variables can behave like Strings or like Numbers depending on context; the interpreter will do automate type conversion to fit the circumstances, whether that's what you intended or not. So when variables are misbehaving, always consider the possibility that what you thought was a Number is actually being treated like a String, or vice versa. Remember that the plus sign is not just an arithmetic addition operator; it is also a string-catenation operator. Under some conditions, the interpreter, on encountering a plus sign, may not know whether you're trying to do string-catenation or addition. And it may pick the wrong thing to do.

The best rule of thumb is, when it's critical that a variable behave as a Number, explicitly cast it to a Number. When it's critical that a variable behave as a String, cast it to a String:


var subtotal = Number( userValue1 ) + Number( userValue2 );
var bigString = String( userValue1 ) + String( userValue2 );

Sometimes It's Not Your Code That's Buggy

If you spend enough time programming (and debugging), you'll eventually encounter a situation where the machine's misbehavior is tied not to your code, but to Acrobat's (or even more rarely, to the JavaScript interpreter itself). For example, on the Mac version of Acrobat, the charCodeAt() method of JavaScript's String class reports negative values for high-bit ASCII characters. (I haven't checked whether this is also the case on the Windows version.) This is not in keeping with the Netscape implementation of JavaScript, where character codes are always positive, in the range zero to 255. The Netscape implementation is correct. The implementation in Acrobat is not. I discovered this bug when I was using the return value of charCodeAt() to index into a table (array) in a JavaScript routine I was using for encryption. Using a negative number as an array index is an automatic bug in every language I know of, although it will get past the compiler since there is no syntax error.

The Machine Must Do What You Tell It to Do

When a debugging situation arises where you're absolutely convinced that you've entered some kind of metaphysical reality warp, because you KNOW the code can't be doing what it's doing, and you're ready to pull your hair out because there is NO WAY the code you've written could be incorrect, yet the program is misbehaving, sit back and take a deep breath, and remind yourself that it's just a machine; it can only do what you tell it to do. If there's a bug, you must find a way to tell the machine to reveal it to you. Write "monitors" into your code so you can observe variable values at runtime. Set up breakpoints at critical spots. Make the machine prove to you that your code is incorrect, and make it show you where the defect is. If you're a good programmer, that should be a straightforward programming task, like any other.


PDF In-Depth Free Product Trials Ubiquitous PDF

Pitstop Pro

Now graphic arts professionals have even broader and more expert control over their PDF documents. With...

Download free demo

ARTS PDF Aerialist

The ultimate plug-in for Adobe Acrobat and #1 selling product at PDF Store. Advanced splitting, merging,...

Download free demo

Ubiquitous PDF: Planning for unexpected cash

It's the end of the financial year and some lucky souls are expecting a tax return. Whether or not the dollars are stacking up for you, it's worth keeping in mind this new PDF tool from Squawkfox.

July 29, 2010
Search Planet PDF
more searching options...







Download PDF Creator

PDF Resources
Platinum Sponsor
Create & Edit PDF - Nitro PDF Software

ARTS PDF

Silver Sponsors

PDF-Tools QuickPDF: The Unrivaled PDF Developer Toolkit

Get Nitro PDF Professional
Featured Product

NITRO PDF Professional

Built from the ground up, the perfect desktop PDF product for business and enterprise. Nitro PDF Professional has an uncompromising feature set so you can create, combine, edit, collaborate on and...

Featured Event

PDF/A Conference, Rome, 09/29/10

The 4th International PDF/A Conference, organized by the PDF/A Competence Center in Rome, September 29th - October 1st, 2010, features a gathering of the world’s experts on PDF/A. Different tracks are offered, ranging from novice to industry-specific sessions and use cases from different industries.

PDF Store Categories