In order to start addressing our issue with error reporting, I thought I'd start poking around at the source of all Java based exception classes (of which both PROGRAM-ERROR and SIMLE-ERROR are part). This source is the Condition java class.
In the class 3 methods have been defined, all related to printing of (java based) errors:
* getMessage() [overridden 13 times, 8 call sites] * getConditionReport() [not overridden, 1 call site] * writeToString()
While the role of "writeToString()" is clear (it's called by PRINT to print all Java-based objects) and hence should function differently under different circumstances, the role separation between getMessage() and getConditionReport() isn't quite so well defined. This conclusion comes both from the extreme lack of documentation as well as the following:
getConditionReport() calls getMessage() to find an "alternative message" to be reported. getMessage() finds out if there's a formatControl string; if there is, it calls writeToString() to generate a message, returning that to getConditionReport().
If getConditionReport() doesn't get an alternative from getMessage() to return back to the caller, it calls Lisp.format() to generate a formatted string to be presented to the user. However, this second path will never get called, except when there's no format string.... Lisp.format() explicitly checks for a null format control string and returns an empty string - which explains why we haven't seen crashes from the above code paths.
So, how does that tie into the grand scheme of error reporting?
We have two places where errors are being reported:
1. the lisp debugger, which prints errors using the real (lisp based) FORMAT function if it's autoloaded (but falls back to Lisp.format() if FORMAT isn't autoloaded yet) which is invoked by INVOKE-DEBUGGER-REPORT-CONDITION 2. the ERROR place holder Primitives.ERROR() which calls Condition.getConditionReport()
The Lisp based FORMAT uses PRINT-OBJECT to print conditions. PRINT-OBJECT.lisp defines specialized instances for several classes in the condition class hieranchy. I know I added some in order to try to address the exact issue we're talking about here.
The Java based Lisp.format() function uses <condition>.writeToString() to produce its output.
With all these facts on the table, I think we need to do a few things to clean up the mess:
- Any condition overriding getMessage() in order to call FORMAT should be removed - getMessage() should be documented *not* to use writeToString() on its own instance - getConditionReport() should be documented not to be overridden, but to override getMessage() instead (maybe declare 'final'?) - Remove all the special methods for classes derived from CONDITION in the print-object.lisp file: they should all fall back to the general method for printing conditions. - Both INVOKE-DEBUGGER-REPORT-CONDITION and Lisp.ERROR should bind the following variables to these listed values, in order to make sure conditions are printed for humans:
*print-readably* nil --> all other *print-** variables bound like the values specified for WITH-STANDARD-IO-SYNTAX (except maybe *PRINT-ESCAPE*)
The above looks like it should solve our issues.
I'd greatly welcome any comments: it's a rather big change, judging by the description above. Ville, you were going to look at it, maybe you can verify if I'm half way right?
Bye,
Erik.