A bug in Exception>>#outer causing a return to an incorrect handler context

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

A bug in Exception>>#outer causing a return to an incorrect handler context

Jaromir Matas
Try this simple test:

resumedCorrectly := false.
[
        [ Warning signal ] on: Warning do: [ :ex | ex outer ]. "returns here"
        resumedCorrectly := true.
] on: Warning do: [ :ex | ex resume ].
resumedCorrectly "---> true"

But in this equivalent case it'll return incorrectly:

resumedCorrectly := false.
[
        [ Warning signal ] on: Warning do: [ :ex | ex outer. ex return ].
        resumedCorrectly := true.
] on: Warning do: [ :ex | ex resume ]. "returns here"
resumedCorrectly "---> false"

The root cause is #outer saves the previous outerContext into a local temp but doesn't save the corresponding handlerContext. As a result, after returning from ex outer, the exception object's handlerContext points to the handler that resumed the exception instead of the current handler and thus ex return leads the execution to the ex resume handler context.

The fix is to add a second local temp to #outer and save the current handlerContext and reverse the operation in #resumeUnchecked.

outer
        "Evaluate the enclosing exception action and return to here instead of signal if it resumes (see #resumeUnchecked:)."

        | prevOuterContext currHandlerContext |
        self isResumable ifTrue: [
                currHandlerContext := handlerContext.
                prevOuterContext := outerContext.
                outerContext := thisContext contextTag.
        ].
        self pass.

resumeUnchecked: resumptionValue
        "Return resumptionValue as the value of #signal, unless this was called after an #outer message, then return resumptionValue as the value of #outer."

        | ctxt |
        outerContext ifNil: [
                signalContext return: resumptionValue
        ] ifNotNil: [
                ctxt := outerContext.
                outerContext := ctxt tempAt: 1. "prevOuterContext in #outer"
                handlerContext := ctxt tempAt: 2. "currHandlerContext in #outer"
                ctxt return: resumptionValue
        ].

Updated tests will follow.
best,
^[^ Jaromir