Tracing Function Arguments

Given that we don’t have a script debugger, I make extensive use of the Acrobat console object to dump script context information.  I’ve discovered that getting the right information out is a bit of an art form.  When you add a call to console.println() you want to make sure you include enough information so that you know the context of the output.  Your output needs to handle conditions such as empty strings, null values, variable numbers of parameters…  But who wants to spend time coding a really clever call to console.println() when you know full well that you’re just going to delete it five minutes later?

Given my lazy nature I went searching for an easier way.  The place where I most often add calls to console.println() is at the beginning of a function call.  For example, here’s how I might debug a method to convert a measurement to inches:

/**
* Convert a measurement to inches.
* @param measurement – a measurement property
*                 e.g. "49.3mm".  Input units can be mm, cm, pt, in
* @param digitsPecision – (optional) how many digits to preserve
*                          after the decimal place. Default is 3.
* @return the corresponding number of inches.
*/
function getInches(measurement, digitsPrecision)
{
  console.println("getInches: " + measurement + " " +
     
  ((digitsPrecision) ?  digitsPrecision : "undefined"));
  …
}

That console.println() method call didn’t exactly roll off the fingertips.  And no, I didn’t get it right first try. In my test form, the output dumped to the console was:

getInches: 9.525mm 4
getInches: 60.325mm undefined

Then I started looking at what I might be able to get for free by extracting information from the "arguments" object.  (If you’re not familiar with the arguments object, read chapter 8.2 of O’Reilly JavaScript The Definitive Guide.)

I wrote a utility function that dumps the contents of the arguments object.  My debug statement now looks like:

function getInches(measurement, digitsPrecision)

  dbg.trace(arguments);
  …
}

Far easier to code.  The output produced by the dbg.trace() call:

function getInches() :
  measurement: [9.525mm] (string)
  digitsPrecision: [4] (number)

function getInches() :
  measurement: [60.325mm] (string)
  digitsPrecision: undefined

You can look in the sample form to see the details on how the script extracts all this information.  The tricky part was extracting the function name and parameter names.  The script uses arguments.callee.toString() to get a string representation of the function syntax and then uses a regular expression to extract the function name and parameter names.

Other features of the dbg.trace() method:

  • handles variable numbers of parameters
  • If a parameter is an object, it will attempt to extract the className, name and rawValue properties (if defined).
  • If the form is running on the server, the trace output is dumped to the log object instead of the console.
  • There are a couple of global variables in the dbg script object to control whether trace output is enabled and to control the maximum number characters of data to dump for parameter values.

Have a look.  I’m sure you’ll find ways to customize the trace to suit your specific needs.

August 14 Update

As explained here, the trace function has been updated to handle parameters.  It is now also available as a downloadable fragment.

4 Responses to Tracing Function Arguments

  1. Bruce Robertson says:

    Hi John,Thanks for all the good posts. Debugging is one of the weak areas in XML Forms and this sort of routines help a lot.I did have trouble with this one though when one of my functions was passed an argument with a null value.I have made a change to the trace function so lines 37 to 41 now look like;if (typeof(vArgs[i]) != “undefined”)if (vArgs[i] != null)sDBG += traceArg(sArgNames[i], vArgs[i]);// Argument is nullelsesDBG += ” ” + sArgNames[i] + “: null” + “\n”;// Argument is undefinedelsesDBG += ” ” + sArgNames[i] + “: undefined” + “\n”;Thanks againBruce

  2. Bruce:Your suggested changes make sense. I’ll include them when I send an update for this sample. Thanks!John

  3. Bruce says:

    Hi John,I continue to find this a very useful in debugging my forms and another suggestion that might help others. If the function argument is one of my JavaScript objects then I found it useful to display it as an object literal (using the toSource() method).Thanks again for the great blogs.function traceArg(vName, vValue){// simple case is where it’s not an object. Just use toString() on the value.// Eventually we might want special handling for other parameter types:// e.g. Arrays, Functionsif (typeof(vValue) != “object”)return ” ” + vName + “: [” + vValue.toString().substring(0,VALUE_LENGTH) + “] (” + typeof(vValue) + “)\n”;// if it’s an object, it might be an XFA object.// So try and extract name, className and rawValue (if defined)var sReturn = [];sReturn.push(” ” + vName + “:”);if (vValue.className) // then probably a XFA Object{if (vValue.name && vValue.name != “”)sReturn.push(” ” + vValue.name);if (vValue.rawValue)sReturn.push(” [” + vValue.rawValue.toString().substring(0,VALUE_LENGTH) + “]”);if (vValue.className)sReturn.push(” (” + vValue.className + “)”);}else{sReturn.push(” ” + vValue.toSource());}sReturn.push(“\n”);return sReturn.join(“”);;}

  4. Bruce:Nice improvement. I’ve incorporated your code into my copy of this function.thanks,John