What can possibly go wrong in the simple act of modifying the message of an exception to add some additional diagnostic information and then re-raising it?
Quite a lot actually, and all from one simple mistake.
Here’s a simplified version of a fairly common construct that you will encounter in Delphi code:
var a: Integer; begin try a := 0; Caption := IntToStr(100 div a); except on e: Exception do begin e.Message := 'Oops: ' + e.Message; raise e; end; end; end;
The intention is to add some additional diagnostic information to an exception message to assist in debugging (assuming it is not subsequently handled by some exception handler further up the stack), but without handling the exception itself. i.e. to re-raise the exception.
Fairly innocuous and entirely straightforward – most experienced Delphi developers can probably do this in their sleep.
Which is I think what happened here – sleep-coding I mean – because if you compile and run this code your exception handling will cause an apparent meltdown in your application. After the initial exception is reported – seemingly normally – you will then encounter access violations and even external exceptions.
Some of you may have spotted the problem already, but it took me 20 minutes of trying to figure out why such seemingly simple and harmless code was causing access violations and external exceptions etc etc.
How can raising an exception do that? Perhaps the method was being invoked on an invalid instance or some other similar bad pointer was getting involved in the situation somehow (which was far more complex than this simplified version).
The problem of course lay in the mostly harmless looking:
raise e;
To re-raise an exception you do not specify an exception instance. You only do that when raising a brand new exception. By referencing the “caught” exception instance “e” in the raise statement, the runtime will naively handle that exception (destroying the exception instance) and then goes right ahead and raises that destroyed – dead – instance.
The exception instance gets handled AND re-raised and when the already handled (destroyed) exception arrives at another exception handler, all hell breaks loose.
It strikes me that the compiler/RTL between them should be able to tell when you do this and treat “raise e” as simply “raise” when “e” is a reference to the currently-being handled exception.
But for now, it’s something to be wary of.
Footnote
There’s an entirely clean triple-entendre in this post’s title.
1. The technical aspect covered above – raising a handled (dead) exception
2. Stress related health concerns that have been bothering me for the past 12 months or so and which came to a head at the end of last year, are hopefully if not behind me then at least waving as I sail them by on the freeway. I did not actually die – quite obviously – but believed I was close to it on more than one occasion. and which caused me to take a break from engaging in the Delphi community including this blog. My activity may not be as high as it once was, at least for a while, but I am re-raised at last.
3. It is no exaggeration to say that a factor, albeit a small one, in my recovery has been the undoubted resurgence in the vitality of Delph itself. A raising from the dead (again? some might say) of Delphi.
Jolyon, welcome back!
You should always listen to your body, most of the time it knows better than your brain what’s good for you (BTDTGTT).
And on-topic – I do agree with you that the compiler should note the problem in the code, but flag an error. I’m not much interested in compiler induced changes of the program logic, if I code wrongly, the compiler should tell me, not hide it under the carpet.
Thanks for the comments Anders.
And on-topic – I think you’re right: the compiler should flag the error rather than try to fix it.
Off-topic – I think I once had a Lego brick that looked exactly like your Gravatar! 🙂
Hi Jolyon, good to hear about you! I must say, I was wondering about the energy you used last year for writing the blog and all the coding you’ve been doing on your free-time, as I figured was happening.
I have found myself, that although there would be lots of nice things one could code, there simply is no time or energy left for it. And after all, it is not that important.
But, anyway, if you like it, just go on 🙂
On-topic. I think I have fought with the same issue a couple of times, before noticing what goes wrong… The compiler should help to avoid exactly these kinds of situations, with an error if it is a wrong thing to do.
BTW: Just last week, I managed to reinstall my SeaMonkey RSS plug-in, which died on some update last year (I did not have energy to get it up for several months, so I also missed “your quiet period”). One additional reraised to your list 🙂
OT:
Yeah, the avatar is carfully chosen after a LEGO part I actually have. I very much like the look of that face – most of the time I feel like that face looks 🙂
I’m an AFOL (Adult Fan Of Lego), I even find it relaxing to sort the bricks back into the right bins when the kids have left them on the table.
I’ve also written a LEGO CAD program (in Delphi, of course, started in D1 time), BlockCAD – http://web.telia.com/~u16122508/proglego.htm.