Comment made by: hiredman
a brief description of exception handling:
a try/catch/finally has 3 parts with those names control flow ends up something like
try:
do stuff and on exception go to catch if it exists, or else finally if it exists
goto finally
catch:
do stuff and on exception go to finally if it exists
goto finally
finally:
do stuff if came here via exception throw exception
some details about exception handling in go blocks:
blocks that should be jumped to in the case of an exception are pushed
on to EXCEPTION-FRAMES
when the try block executes it first pushes the
finally block if it exists and then the catch block (as of now a catch
block always exists even if there are no catch clauses in the
expression). when execution of the try block ends normally it jumps to
the finally block. if an exception is raised in jumps to the block at
the top of EXCEPTION-FRAMES.
discussion of the actual bug:
The bug here had to do with management of pushing and popping of
EXCEPTION-FRAMES. Before this patch the loop around running the state
machine would catch all exceptions, then jump to the block at the top
of EXCEPTION-FRAMES and also pop the block off the top of
EXCEPTION-FRAMES. This was a problem because depending on the
execution path, there are either 1 or 2 frames to pop (either a catch,
or a catch and a finally).
This table shows the problem:
| expr | frames pushed by try | frames popped by try | frames popped by catch | frames popped by finally | frames popped by state machine | sum ex | sum no ex |
|------------------------+----------------------+----------------------+------------------------+--------------------------+--------------------------------+--------+-----------|
| old try/finally | 2 | 2 | 0 | 0 | 1 | 1 | 0 |
| old try/catch/finally | 2 | 2 | 0 | 0 | 1 | 1 | 0 |
| new try/finally | 2 | 1 | 1 | 1 | 0 | 0 | 0 |
| new try/catch/finally | 2 | 1 | 1 | 1 | 0 | 0 | 0 |
the sums are pushs - pops, for the no exception case
the only pops are the frames popped by the try and the finally, for
the exception case the pops are the pops from the catch, the finally,
and the state machine loop.
This patch removes the implicit pop of the exception handler from the
loop running the state machine and replaces it with explicit popping
instructions. The try block pops off one frame on successful execution
and them jumps to the finally if it exists. The finally block pops off
one frame. The catch block pops off one frame.