mirror of
https://github.com/vim/vim.git
synced 2025-09-27 04:14:06 -04:00
patch 9.0.0577: buffer underflow with unexpected :finally
Problem: Buffer underflow with unexpected :finally. Solution: Check CSF_TRY can be found.
This commit is contained in:
537
src/ex_eval.c
537
src/ex_eval.c
@@ -1935,128 +1935,127 @@ ex_finally(exarg_T *eap)
|
|||||||
if (cmdmod_error(FALSE))
|
if (cmdmod_error(FALSE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0)
|
for (idx = cstack->cs_idx; idx >= 0; --idx)
|
||||||
eap->errmsg = _(e_finally_without_try);
|
if (cstack->cs_flags[idx] & CSF_TRY)
|
||||||
else
|
break;
|
||||||
|
if (cstack->cs_trylevel <= 0 || idx < 0)
|
||||||
{
|
{
|
||||||
if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY))
|
eap->errmsg = _(e_finally_without_try);
|
||||||
{
|
return;
|
||||||
eap->errmsg = get_end_emsg(cstack);
|
}
|
||||||
for (idx = cstack->cs_idx - 1; idx > 0; --idx)
|
|
||||||
if (cstack->cs_flags[idx] & CSF_TRY)
|
|
||||||
break;
|
|
||||||
// Make this error pending, so that the commands in the following
|
|
||||||
// finally clause can be executed. This overrules also a pending
|
|
||||||
// ":continue", ":break", ":return", or ":finish".
|
|
||||||
pending = CSTP_ERROR;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
idx = cstack->cs_idx;
|
|
||||||
|
|
||||||
if (cstack->cs_flags[idx] & CSF_FINALLY)
|
if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY))
|
||||||
|
{
|
||||||
|
eap->errmsg = get_end_emsg(cstack);
|
||||||
|
// Make this error pending, so that the commands in the following
|
||||||
|
// finally clause can be executed. This overrules also a pending
|
||||||
|
// ":continue", ":break", ":return", or ":finish".
|
||||||
|
pending = CSTP_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cstack->cs_flags[idx] & CSF_FINALLY)
|
||||||
|
{
|
||||||
|
// Give up for a multiple ":finally" and ignore it.
|
||||||
|
eap->errmsg = _(e_multiple_finally);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR,
|
||||||
|
&cstack->cs_looplevel);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't do something when the corresponding try block never got active
|
||||||
|
* (because of an inactive surrounding conditional or after an error or
|
||||||
|
* interrupt or throw) or for a ":finally" without ":try" or a multiple
|
||||||
|
* ":finally". After every other error (did_emsg or the conditional
|
||||||
|
* errors detected above) or after an interrupt (got_int) or an
|
||||||
|
* exception (did_throw), the finally clause must be executed.
|
||||||
|
*/
|
||||||
|
skip = !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
|
||||||
|
|
||||||
|
if (!skip)
|
||||||
|
{
|
||||||
|
// When debugging or a breakpoint was encountered, display the
|
||||||
|
// debug prompt (if not already done). The user then knows that the
|
||||||
|
// finally clause is executed.
|
||||||
|
if (dbg_check_skipped(eap))
|
||||||
{
|
{
|
||||||
// Give up for a multiple ":finally" and ignore it.
|
// Handle a ">quit" debug command as if an interrupt had
|
||||||
eap->errmsg = _(e_multiple_finally);
|
// occurred before the ":finally". That is, discard the
|
||||||
return;
|
// original exception and replace it by an interrupt
|
||||||
|
// exception.
|
||||||
|
(void)do_intthrow(cstack);
|
||||||
}
|
}
|
||||||
rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR,
|
|
||||||
&cstack->cs_looplevel);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Don't do something when the corresponding try block never got active
|
* If there is a preceding catch clause and it caught the exception,
|
||||||
* (because of an inactive surrounding conditional or after an error or
|
* finish the exception now. This happens also after errors except
|
||||||
* interrupt or throw) or for a ":finally" without ":try" or a multiple
|
* when this is a multiple ":finally" or one not within a ":try".
|
||||||
* ":finally". After every other error (did_emsg or the conditional
|
* After an error or interrupt, this also discards a pending
|
||||||
* errors detected above) or after an interrupt (got_int) or an
|
* ":continue", ":break", ":finish", or ":return" from the preceding
|
||||||
* exception (did_throw), the finally clause must be executed.
|
* try block or catch clause.
|
||||||
*/
|
*/
|
||||||
skip = !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
|
cleanup_conditionals(cstack, CSF_TRY, FALSE);
|
||||||
|
|
||||||
if (!skip)
|
if (cstack->cs_idx >= 0
|
||||||
|
&& (cstack->cs_flags[cstack->cs_idx] & CSF_TRY))
|
||||||
{
|
{
|
||||||
// When debugging or a breakpoint was encountered, display the
|
// Variables declared in the previous block can no longer be
|
||||||
// debug prompt (if not already done). The user then knows that the
|
// used.
|
||||||
// finally clause is executed.
|
leave_block(cstack);
|
||||||
if (dbg_check_skipped(eap))
|
enter_block(cstack);
|
||||||
{
|
|
||||||
// Handle a ">quit" debug command as if an interrupt had
|
|
||||||
// occurred before the ":finally". That is, discard the
|
|
||||||
// original exception and replace it by an interrupt
|
|
||||||
// exception.
|
|
||||||
(void)do_intthrow(cstack);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If there is a preceding catch clause and it caught the exception,
|
|
||||||
* finish the exception now. This happens also after errors except
|
|
||||||
* when this is a multiple ":finally" or one not within a ":try".
|
|
||||||
* After an error or interrupt, this also discards a pending
|
|
||||||
* ":continue", ":break", ":finish", or ":return" from the preceding
|
|
||||||
* try block or catch clause.
|
|
||||||
*/
|
|
||||||
cleanup_conditionals(cstack, CSF_TRY, FALSE);
|
|
||||||
|
|
||||||
if (cstack->cs_idx >= 0
|
|
||||||
&& (cstack->cs_flags[cstack->cs_idx] & CSF_TRY))
|
|
||||||
{
|
|
||||||
// Variables declared in the previous block can no longer be
|
|
||||||
// used.
|
|
||||||
leave_block(cstack);
|
|
||||||
enter_block(cstack);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make did_emsg, got_int, did_throw pending. If set, they overrule
|
|
||||||
* a pending ":continue", ":break", ":return", or ":finish". Then
|
|
||||||
* we have particularly to discard a pending return value (as done
|
|
||||||
* by the call to cleanup_conditionals() above when did_emsg or
|
|
||||||
* got_int is set). The pending values are restored by the
|
|
||||||
* ":endtry", except if there is a new error, interrupt, exception,
|
|
||||||
* ":continue", ":break", ":return", or ":finish" in the following
|
|
||||||
* finally clause. A missing ":endwhile", ":endfor" or ":endif"
|
|
||||||
* detected here is treated as if did_emsg and did_throw had
|
|
||||||
* already been set, respectively in case that the error is not
|
|
||||||
* converted to an exception, did_throw had already been unset.
|
|
||||||
* We must not set did_emsg here since that would suppress the
|
|
||||||
* error message.
|
|
||||||
*/
|
|
||||||
if (pending == CSTP_ERROR || did_emsg || got_int || did_throw)
|
|
||||||
{
|
|
||||||
if (cstack->cs_pending[cstack->cs_idx] == CSTP_RETURN)
|
|
||||||
{
|
|
||||||
report_discard_pending(CSTP_RETURN,
|
|
||||||
cstack->cs_rettv[cstack->cs_idx]);
|
|
||||||
discard_pending_return(cstack->cs_rettv[cstack->cs_idx]);
|
|
||||||
}
|
|
||||||
if (pending == CSTP_ERROR && !did_emsg)
|
|
||||||
pending |= (THROW_ON_ERROR) ? CSTP_THROW : 0;
|
|
||||||
else
|
|
||||||
pending |= did_throw ? CSTP_THROW : 0;
|
|
||||||
pending |= did_emsg ? CSTP_ERROR : 0;
|
|
||||||
pending |= got_int ? CSTP_INTERRUPT : 0;
|
|
||||||
cstack->cs_pending[cstack->cs_idx] = pending;
|
|
||||||
|
|
||||||
// It's mandatory that the current exception is stored in the
|
|
||||||
// cstack so that it can be rethrown at the ":endtry" or be
|
|
||||||
// discarded if the finally clause is left by a ":continue",
|
|
||||||
// ":break", ":return", ":finish", error, interrupt, or another
|
|
||||||
// exception. When emsg() is called for a missing ":endif" or
|
|
||||||
// a missing ":endwhile"/":endfor" detected here, the
|
|
||||||
// exception will be discarded.
|
|
||||||
if (did_throw && cstack->cs_exception[cstack->cs_idx]
|
|
||||||
!= current_exception)
|
|
||||||
internal_error("ex_finally()");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set CSL_HAD_FINA, so do_cmdline() will reset did_emsg,
|
|
||||||
* got_int, and did_throw and make the finally clause active.
|
|
||||||
* This will happen after emsg() has been called for a missing
|
|
||||||
* ":endif" or a missing ":endwhile"/":endfor" detected here, so
|
|
||||||
* that the following finally clause will be executed even then.
|
|
||||||
*/
|
|
||||||
cstack->cs_lflags |= CSL_HAD_FINA;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make did_emsg, got_int, did_throw pending. If set, they overrule
|
||||||
|
* a pending ":continue", ":break", ":return", or ":finish". Then
|
||||||
|
* we have particularly to discard a pending return value (as done
|
||||||
|
* by the call to cleanup_conditionals() above when did_emsg or
|
||||||
|
* got_int is set). The pending values are restored by the
|
||||||
|
* ":endtry", except if there is a new error, interrupt, exception,
|
||||||
|
* ":continue", ":break", ":return", or ":finish" in the following
|
||||||
|
* finally clause. A missing ":endwhile", ":endfor" or ":endif"
|
||||||
|
* detected here is treated as if did_emsg and did_throw had
|
||||||
|
* already been set, respectively in case that the error is not
|
||||||
|
* converted to an exception, did_throw had already been unset.
|
||||||
|
* We must not set did_emsg here since that would suppress the
|
||||||
|
* error message.
|
||||||
|
*/
|
||||||
|
if (pending == CSTP_ERROR || did_emsg || got_int || did_throw)
|
||||||
|
{
|
||||||
|
if (cstack->cs_pending[cstack->cs_idx] == CSTP_RETURN)
|
||||||
|
{
|
||||||
|
report_discard_pending(CSTP_RETURN,
|
||||||
|
cstack->cs_rettv[cstack->cs_idx]);
|
||||||
|
discard_pending_return(cstack->cs_rettv[cstack->cs_idx]);
|
||||||
|
}
|
||||||
|
if (pending == CSTP_ERROR && !did_emsg)
|
||||||
|
pending |= (THROW_ON_ERROR) ? CSTP_THROW : 0;
|
||||||
|
else
|
||||||
|
pending |= did_throw ? CSTP_THROW : 0;
|
||||||
|
pending |= did_emsg ? CSTP_ERROR : 0;
|
||||||
|
pending |= got_int ? CSTP_INTERRUPT : 0;
|
||||||
|
cstack->cs_pending[cstack->cs_idx] = pending;
|
||||||
|
|
||||||
|
// It's mandatory that the current exception is stored in the
|
||||||
|
// cstack so that it can be rethrown at the ":endtry" or be
|
||||||
|
// discarded if the finally clause is left by a ":continue",
|
||||||
|
// ":break", ":return", ":finish", error, interrupt, or another
|
||||||
|
// exception. When emsg() is called for a missing ":endif" or
|
||||||
|
// a missing ":endwhile"/":endfor" detected here, the
|
||||||
|
// exception will be discarded.
|
||||||
|
if (did_throw && cstack->cs_exception[cstack->cs_idx]
|
||||||
|
!= current_exception)
|
||||||
|
internal_error("ex_finally()");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set CSL_HAD_FINA, so do_cmdline() will reset did_emsg,
|
||||||
|
* got_int, and did_throw and make the finally clause active.
|
||||||
|
* This will happen after emsg() has been called for a missing
|
||||||
|
* ":endif" or a missing ":endwhile"/":endfor" detected here, so
|
||||||
|
* that the following finally clause will be executed even then.
|
||||||
|
*/
|
||||||
|
cstack->cs_lflags |= CSL_HAD_FINA;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2076,185 +2075,183 @@ ex_endtry(exarg_T *eap)
|
|||||||
if (cmdmod_error(FALSE))
|
if (cmdmod_error(FALSE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0)
|
for (idx = cstack->cs_idx; idx >= 0; --idx)
|
||||||
eap->errmsg = _(e_endtry_without_try);
|
if (cstack->cs_flags[idx] & CSF_TRY)
|
||||||
else
|
break;
|
||||||
|
if (cstack->cs_trylevel <= 0 || idx < 0)
|
||||||
{
|
{
|
||||||
/*
|
eap->errmsg = _(e_endtry_without_try);
|
||||||
* Don't do something after an error, interrupt or throw in the try
|
return;
|
||||||
* block, catch clause, or finally clause preceding this ":endtry" or
|
}
|
||||||
* when an error or interrupt occurred after a ":continue", ":break",
|
|
||||||
* ":return", or ":finish" in a try block or catch clause preceding this
|
/*
|
||||||
* ":endtry" or when the try block never got active (because of an
|
* Don't do something after an error, interrupt or throw in the try
|
||||||
* inactive surrounding conditional or after an error or interrupt or
|
* block, catch clause, or finally clause preceding this ":endtry" or
|
||||||
* throw) or when there is a surrounding conditional and it has been
|
* when an error or interrupt occurred after a ":continue", ":break",
|
||||||
* made inactive by a ":continue", ":break", ":return", or ":finish" in
|
* ":return", or ":finish" in a try block or catch clause preceding this
|
||||||
* the finally clause. The latter case need not be tested since then
|
* ":endtry" or when the try block never got active (because of an
|
||||||
* anything pending has already been discarded. */
|
* inactive surrounding conditional or after an error or interrupt or
|
||||||
skip = did_emsg || got_int || did_throw
|
* throw) or when there is a surrounding conditional and it has been
|
||||||
|
* made inactive by a ":continue", ":break", ":return", or ":finish" in
|
||||||
|
* the finally clause. The latter case need not be tested since then
|
||||||
|
* anything pending has already been discarded. */
|
||||||
|
skip = did_emsg || got_int || did_throw
|
||||||
|| !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
|
|| !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
|
||||||
|
|
||||||
if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY))
|
if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY))
|
||||||
{
|
{
|
||||||
eap->errmsg = get_end_emsg(cstack);
|
eap->errmsg = get_end_emsg(cstack);
|
||||||
|
|
||||||
// Find the matching ":try" and report what's missing.
|
// Find the matching ":try" and report what's missing.
|
||||||
idx = cstack->cs_idx;
|
rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR,
|
||||||
do
|
&cstack->cs_looplevel);
|
||||||
--idx;
|
skip = TRUE;
|
||||||
while (idx > 0 && !(cstack->cs_flags[idx] & CSF_TRY));
|
|
||||||
rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR,
|
|
||||||
&cstack->cs_looplevel);
|
|
||||||
skip = TRUE;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If an exception is being thrown, discard it to prevent it from
|
* If an exception is being thrown, discard it to prevent it from
|
||||||
* being rethrown at the end of this function. It would be
|
* being rethrown at the end of this function. It would be
|
||||||
* discarded by the error message, anyway. Resets did_throw.
|
* discarded by the error message, anyway. Resets did_throw.
|
||||||
* This does not affect the script termination due to the error
|
* This does not affect the script termination due to the error
|
||||||
* since "trylevel" is decremented after emsg() has been called.
|
* since "trylevel" is decremented after emsg() has been called.
|
||||||
*/
|
*/
|
||||||
if (did_throw)
|
if (did_throw)
|
||||||
discard_current_exception();
|
discard_current_exception();
|
||||||
|
|
||||||
// report eap->errmsg, also when there already was an error
|
// report eap->errmsg, also when there already was an error
|
||||||
did_emsg = FALSE;
|
did_emsg = FALSE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
idx = cstack->cs_idx;
|
idx = cstack->cs_idx;
|
||||||
|
|
||||||
// Check the flags only when not in a skipped block.
|
// Check the flags only when not in a skipped block.
|
||||||
if (!skip && in_vim9script()
|
if (!skip && in_vim9script()
|
||||||
&& (cstack->cs_flags[idx] & (CSF_CATCH|CSF_FINALLY)) == 0)
|
&& (cstack->cs_flags[idx] & (CSF_CATCH|CSF_FINALLY)) == 0)
|
||||||
{
|
{
|
||||||
// try/endtry without any catch or finally: give an error and
|
// try/endtry without any catch or finally: give an error and
|
||||||
// continue.
|
// continue.
|
||||||
eap->errmsg = _(e_missing_catch_or_finally);
|
eap->errmsg = _(e_missing_catch_or_finally);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we stopped with the exception currently being thrown at this
|
* If we stopped with the exception currently being thrown at this
|
||||||
* try conditional since we didn't know that it doesn't have
|
* try conditional since we didn't know that it doesn't have
|
||||||
* a finally clause, we need to rethrow it after closing the try
|
* a finally clause, we need to rethrow it after closing the try
|
||||||
* conditional.
|
* conditional.
|
||||||
*/
|
*/
|
||||||
if (did_throw && (cstack->cs_flags[idx] & CSF_TRUE)
|
if (did_throw && (cstack->cs_flags[idx] & CSF_TRUE)
|
||||||
&& !(cstack->cs_flags[idx] & CSF_FINALLY))
|
&& !(cstack->cs_flags[idx] & CSF_FINALLY))
|
||||||
|
rethrow = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there was no finally clause, show the user when debugging or
|
||||||
|
// a breakpoint was encountered that the end of the try conditional has
|
||||||
|
// been reached: display the debug prompt (if not already done). Do
|
||||||
|
// this on normal control flow or when an exception was thrown, but not
|
||||||
|
// on an interrupt or error not converted to an exception or when
|
||||||
|
// a ":break", ":continue", ":return", or ":finish" is pending. These
|
||||||
|
// actions are carried out immediately.
|
||||||
|
if ((rethrow || (!skip && !(cstack->cs_flags[idx] & CSF_FINALLY)
|
||||||
|
&& !cstack->cs_pending[idx]))
|
||||||
|
&& dbg_check_skipped(eap))
|
||||||
|
{
|
||||||
|
// Handle a ">quit" debug command as if an interrupt had occurred
|
||||||
|
// before the ":endtry". That is, throw an interrupt exception and
|
||||||
|
// set "skip" and "rethrow".
|
||||||
|
if (got_int)
|
||||||
|
{
|
||||||
|
skip = TRUE;
|
||||||
|
(void)do_intthrow(cstack);
|
||||||
|
// The do_intthrow() call may have reset did_throw or
|
||||||
|
// cstack->cs_pending[idx].
|
||||||
|
rethrow = FALSE;
|
||||||
|
if (did_throw && !(cstack->cs_flags[idx] & CSF_FINALLY))
|
||||||
rethrow = TRUE;
|
rethrow = TRUE;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If there was no finally clause, show the user when debugging or
|
/*
|
||||||
// a breakpoint was encountered that the end of the try conditional has
|
* If a ":return" is pending, we need to resume it after closing the
|
||||||
// been reached: display the debug prompt (if not already done). Do
|
* try conditional; remember the return value. If there was a finally
|
||||||
// this on normal control flow or when an exception was thrown, but not
|
* clause making an exception pending, we need to rethrow it. Make it
|
||||||
// on an interrupt or error not converted to an exception or when
|
* the exception currently being thrown.
|
||||||
// a ":break", ":continue", ":return", or ":finish" is pending. These
|
*/
|
||||||
// actions are carried out immediately.
|
if (!skip)
|
||||||
if ((rethrow || (!skip
|
{
|
||||||
&& !(cstack->cs_flags[idx] & CSF_FINALLY)
|
pending = cstack->cs_pending[idx];
|
||||||
&& !cstack->cs_pending[idx]))
|
cstack->cs_pending[idx] = CSTP_NONE;
|
||||||
&& dbg_check_skipped(eap))
|
if (pending == CSTP_RETURN)
|
||||||
{
|
rettv = cstack->cs_rettv[idx];
|
||||||
// Handle a ">quit" debug command as if an interrupt had occurred
|
else if (pending & CSTP_THROW)
|
||||||
// before the ":endtry". That is, throw an interrupt exception and
|
current_exception = cstack->cs_exception[idx];
|
||||||
// set "skip" and "rethrow".
|
}
|
||||||
if (got_int)
|
|
||||||
{
|
|
||||||
skip = TRUE;
|
|
||||||
(void)do_intthrow(cstack);
|
|
||||||
// The do_intthrow() call may have reset did_throw or
|
|
||||||
// cstack->cs_pending[idx].
|
|
||||||
rethrow = FALSE;
|
|
||||||
if (did_throw && !(cstack->cs_flags[idx] & CSF_FINALLY))
|
|
||||||
rethrow = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If a ":return" is pending, we need to resume it after closing the
|
* Discard anything pending on an error, interrupt, or throw in the
|
||||||
* try conditional; remember the return value. If there was a finally
|
* finally clause. If there was no ":finally", discard a pending
|
||||||
* clause making an exception pending, we need to rethrow it. Make it
|
* ":continue", ":break", ":return", or ":finish" if an error or
|
||||||
* the exception currently being thrown.
|
* interrupt occurred afterwards, but before the ":endtry" was reached.
|
||||||
*/
|
* If an exception was caught by the last of the catch clauses and there
|
||||||
if (!skip)
|
* was no finally clause, finish the exception now. This happens also
|
||||||
{
|
* after errors except when this ":endtry" is not within a ":try".
|
||||||
pending = cstack->cs_pending[idx];
|
* Restore "emsg_silent" if it has been reset by this try conditional.
|
||||||
cstack->cs_pending[idx] = CSTP_NONE;
|
*/
|
||||||
if (pending == CSTP_RETURN)
|
(void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, TRUE);
|
||||||
rettv = cstack->cs_rettv[idx];
|
|
||||||
else if (pending & CSTP_THROW)
|
|
||||||
current_exception = cstack->cs_exception[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
if (cstack->cs_idx >= 0 && (cstack->cs_flags[cstack->cs_idx] & CSF_TRY))
|
||||||
* Discard anything pending on an error, interrupt, or throw in the
|
leave_block(cstack);
|
||||||
* finally clause. If there was no ":finally", discard a pending
|
--cstack->cs_trylevel;
|
||||||
* ":continue", ":break", ":return", or ":finish" if an error or
|
|
||||||
* interrupt occurred afterwards, but before the ":endtry" was reached.
|
|
||||||
* If an exception was caught by the last of the catch clauses and there
|
|
||||||
* was no finally clause, finish the exception now. This happens also
|
|
||||||
* after errors except when this ":endtry" is not within a ":try".
|
|
||||||
* Restore "emsg_silent" if it has been reset by this try conditional.
|
|
||||||
*/
|
|
||||||
(void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, TRUE);
|
|
||||||
|
|
||||||
if (cstack->cs_idx >= 0
|
if (!skip)
|
||||||
&& (cstack->cs_flags[cstack->cs_idx] & CSF_TRY))
|
{
|
||||||
leave_block(cstack);
|
report_resume_pending(pending,
|
||||||
--cstack->cs_trylevel;
|
|
||||||
|
|
||||||
if (!skip)
|
|
||||||
{
|
|
||||||
report_resume_pending(pending,
|
|
||||||
(pending == CSTP_RETURN) ? rettv :
|
(pending == CSTP_RETURN) ? rettv :
|
||||||
(pending & CSTP_THROW) ? (void *)current_exception : NULL);
|
(pending & CSTP_THROW) ? (void *)current_exception : NULL);
|
||||||
switch (pending)
|
switch (pending)
|
||||||
{
|
{
|
||||||
case CSTP_NONE:
|
case CSTP_NONE:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Reactivate a pending ":continue", ":break", ":return",
|
// Reactivate a pending ":continue", ":break", ":return",
|
||||||
// ":finish" from the try block or a catch clause of this try
|
// ":finish" from the try block or a catch clause of this try
|
||||||
// conditional. This is skipped, if there was an error in an
|
// conditional. This is skipped, if there was an error in an
|
||||||
// (unskipped) conditional command or an interrupt afterwards
|
// (unskipped) conditional command or an interrupt afterwards
|
||||||
// or if the finally clause is present and executed a new error,
|
// or if the finally clause is present and executed a new error,
|
||||||
// interrupt, throw, ":continue", ":break", ":return", or
|
// interrupt, throw, ":continue", ":break", ":return", or
|
||||||
// ":finish".
|
// ":finish".
|
||||||
case CSTP_CONTINUE:
|
case CSTP_CONTINUE:
|
||||||
ex_continue(eap);
|
ex_continue(eap);
|
||||||
break;
|
break;
|
||||||
case CSTP_BREAK:
|
case CSTP_BREAK:
|
||||||
ex_break(eap);
|
ex_break(eap);
|
||||||
break;
|
break;
|
||||||
case CSTP_RETURN:
|
case CSTP_RETURN:
|
||||||
do_return(eap, FALSE, FALSE, rettv);
|
do_return(eap, FALSE, FALSE, rettv);
|
||||||
break;
|
break;
|
||||||
case CSTP_FINISH:
|
case CSTP_FINISH:
|
||||||
do_finish(eap, FALSE);
|
do_finish(eap, FALSE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// When the finally clause was entered due to an error,
|
// When the finally clause was entered due to an error,
|
||||||
// interrupt or throw (as opposed to a ":continue", ":break",
|
// interrupt or throw (as opposed to a ":continue", ":break",
|
||||||
// ":return", or ":finish"), restore the pending values of
|
// ":return", or ":finish"), restore the pending values of
|
||||||
// did_emsg, got_int, and did_throw. This is skipped, if there
|
// did_emsg, got_int, and did_throw. This is skipped, if there
|
||||||
// was a new error, interrupt, throw, ":continue", ":break",
|
// was a new error, interrupt, throw, ":continue", ":break",
|
||||||
// ":return", or ":finish". in the finally clause.
|
// ":return", or ":finish". in the finally clause.
|
||||||
default:
|
default:
|
||||||
if (pending & CSTP_ERROR)
|
if (pending & CSTP_ERROR)
|
||||||
did_emsg = TRUE;
|
did_emsg = TRUE;
|
||||||
if (pending & CSTP_INTERRUPT)
|
if (pending & CSTP_INTERRUPT)
|
||||||
got_int = TRUE;
|
got_int = TRUE;
|
||||||
if (pending & CSTP_THROW)
|
if (pending & CSTP_THROW)
|
||||||
rethrow = TRUE;
|
rethrow = TRUE;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rethrow)
|
|
||||||
// Rethrow the current exception (within this cstack).
|
|
||||||
do_throw(cstack);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rethrow)
|
||||||
|
// Rethrow the current exception (within this cstack).
|
||||||
|
do_throw(cstack);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
source check.vim
|
source check.vim
|
||||||
source shared.vim
|
source shared.vim
|
||||||
|
import './vim9.vim' as v9
|
||||||
|
|
||||||
"-------------------------------------------------------------------------------
|
"-------------------------------------------------------------------------------
|
||||||
" Test environment {{{1
|
" Test environment {{{1
|
||||||
@@ -2008,6 +2009,27 @@ func Test_try_catch_errors()
|
|||||||
call assert_fails('try | for i in range(5) | endif | endtry', 'E580:')
|
call assert_fails('try | for i in range(5) | endif | endtry', 'E580:')
|
||||||
call assert_fails('try | while v:true | endtry', 'E170:')
|
call assert_fails('try | while v:true | endtry', 'E170:')
|
||||||
call assert_fails('try | if v:true | endtry', 'E171:')
|
call assert_fails('try | if v:true | endtry', 'E171:')
|
||||||
|
|
||||||
|
" this was using a negative index in cstack[]
|
||||||
|
let lines =<< trim END
|
||||||
|
try
|
||||||
|
for
|
||||||
|
if
|
||||||
|
endwhile
|
||||||
|
if
|
||||||
|
finally
|
||||||
|
END
|
||||||
|
call v9.CheckScriptFailure(lines, 'E690:')
|
||||||
|
|
||||||
|
let lines =<< trim END
|
||||||
|
try
|
||||||
|
for
|
||||||
|
if
|
||||||
|
endwhile
|
||||||
|
if
|
||||||
|
endtry
|
||||||
|
END
|
||||||
|
call v9.CheckScriptFailure(lines, 'E690:')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" Test for verbose messages with :try :catch, and :finally {{{1
|
" Test for verbose messages with :try :catch, and :finally {{{1
|
||||||
|
@@ -699,6 +699,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
577,
|
||||||
/**/
|
/**/
|
||||||
576,
|
576,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user