diff --git a/src/error.c b/src/error.c index 6252ca7a8e8431492232a68ccc6ac5284dc96d41..01b76fb1efcaee09304f541d6472b4ba8ef560a3 100644 --- a/src/error.c +++ b/src/error.c @@ -127,6 +127,15 @@ PMOD_EXPORT JMP_BUF *init_recovery(JMP_BUF *r, size_t stack_pop_levels DEBUG_INI PMOD_EXPORT DECLSPEC(noreturn) void pike_throw(void) ATTRIBUTE((noreturn)) { + struct svalue save_throw_value; + + /* Save the value to be thrown in case error handlers etc below + * call eg safe_apply() et al, or cause a thread switch to + * code that does. + */ + move_svalue(&save_throw_value, &throw_value); + mark_free_svalue(&throw_value); + #ifdef TRACE_UNFINISHED_TYPE_FIELDS accept_unfinished_type_fields++; #endif @@ -199,9 +208,23 @@ PMOD_EXPORT DECLSPEC(noreturn) void pike_throw(void) ATTRIBUTE((noreturn)) Pike_fatal("Stack error in error.\n"); #endif - pop_n_elems(Pike_sp - Pike_interpreter.evaluator_stack - Pike_interpreter.recoveries->stack_pointer); + /* Note: The pop_stack() below may trigger destruct callbacks being called + * for objects having PROGRAM_DESTRUCT_IMMEDIATE. These may + * in turn throw (caught) errors and zap throw_value. + * + * Note: We can not use pop_n_elems() here as a nested error in an + * immediate destruct callback may zap the stack for us during + * our popping. + */ + while (Pike_sp > Pike_interpreter.evaluator_stack + Pike_interpreter.recoveries->stack_pointer) { + pop_stack(); + } Pike_mark_sp = Pike_interpreter.mark_stack + Pike_interpreter.recoveries->mark_sp; + /* Move the value to be thrown back so that it can be caught. */ + free_svalue(&throw_value); + move_svalue(&throw_value, &save_throw_value); + #if defined(DEBUG_MALLOC) && defined(PIKE_DEBUG) /* This will tell us where the value was caught (I hope) */ if(REFCOUNTED_TYPE(TYPEOF(throw_value)))