1
1
mirror of https://github.com/kanaka/mal.git synced 2024-10-26 14:22:25 +03:00

ps: Fix handling of exceptions thrown from catch* clause

PostScript's exception handling doesn't restore the dictionary or
operand stacks to the state they were in when the "stopped" operator
started, so mal's EVAL needs to do that itself.  To do this, it records
the current height of the stacks, but of course it does that in a
dictionary.  This means that when catching at exception, it relies on
the highest instance of "dictcnt" on the dictionary stack being the
correct one.  EVAL, however, was failing to restore the dictionary stack
at the right time.

For instance, conside this code (from the tests):

  (try* (try* (throw "e1") (catch* e (throw "e2"))) (catch* e "c2"))

Each "try*" clause saves "dictcnt" into a dictionary on the dictionary
stack.  When the inner "catch*" clause fires, it pops the dictionary and
operand stacks to the correct point, but then calls the second "throw"
with the wrong value of "dictcnt" still visible.  The result is that the
"catch*" clause ends up running with the wrong value of "dictcnt" and
restoring the stacks to the wrong place, essentially executing the
_inner_ "catch*" clause again, whereupon the error doesn't get caught
because there's no "stopped" left on the PostScript execution stack.

The fix is to add another dictionary that's just used to hold "dictcnt"
and "stackcnt", and to pop that from the dictionary stack as soon as the
stacks have been restored (or when it becomes unnecessary because the
"try*" clause has returned normally).
This commit is contained in:
Ben Harris 2020-05-21 20:50:42 +01:00
parent 89b940ae62
commit e069ddff67
2 changed files with 6 additions and 0 deletions

View File

@ -156,9 +156,11 @@ end } def
}{ /try* a0 eq { %if try*
ast _count 2 gt { %if has catch* block
{ %try
2 dict begin % special dict for dict stack count
countdictstack /dictcnt exch def
count /stackcnt exch def
ast 1 _nth env EVAL
end
} stopped { %catch
% clean up the dictionary stack
1 1 countdictstack dictcnt sub { %foreach added dict
@ -172,6 +174,7 @@ end } def
pop pop % pop idx and operand
%(popped op stack\n) print pstack
} for
end % remove special dict
% get error data and reset $error dict
/errdata get_error_data def
$error /newerror false put

View File

@ -165,9 +165,11 @@ end } def
}{ /try* a0 eq { %if try*
ast _count 2 gt { %if has catch* block
{ %try
2 dict begin % special dict for dict stack count
countdictstack /dictcnt exch def
count /stackcnt exch def
ast 1 _nth env EVAL
end
} stopped { %catch
% clean up the dictionary stack
1 1 countdictstack dictcnt sub { %foreach added dict
@ -181,6 +183,7 @@ end } def
pop pop % pop idx and operand
%(popped op stack\n) print pstack
} for
end % remove special dict
% get error data and reset $error dict
/errdata get_error_data def
$error /newerror false put