Problem: Vim9: function is deleted while executing.
Solution: increment the call count, when more than zero do not delete the
function but mark it as dead. (closes #7977)
delete('Xreload.vim')
enddef
+" Define CallFunc so that the test can be compiled
+command CallFunc echo 'nop'
+
+def Test_script_reload_from_function()
+ var lines =<< trim END
+ vim9script
+
+ if exists('g:loaded')
+ finish
+ endif
+ g:loaded = 1
+ delcommand CallFunc
+ command CallFunc Func()
+ def Func()
+ so /tmp/test.vim
+ g:didTheFunc = 1
+ enddef
+ END
+ writefile(lines, 'XreloadFunc.vim')
+ source XreloadFunc.vim
+ CallFunc
+ assert_equal(1, g:didTheFunc)
+
+ delete('XreloadFunc.vim')
+ delcommand CallFunc
+ unlet g:loaded
+ unlet g:didTheFunc
+enddef
+
def Test_script_var_shadows_function()
var lines =<< trim END
vim9script
// function, so we can find the index when defining the function again.
// Do remove it when it's a copy.
if (fp->uf_def_status == UF_COMPILED && (fp->uf_flags & FC_COPY) == 0)
+ {
fp->uf_flags |= FC_DEAD;
- else
- hash_remove(&func_hashtab, hi);
+ return FALSE;
+ }
+ hash_remove(&func_hashtab, hi);
+ fp->uf_flags |= FC_DELETED;
return TRUE;
}
return FALSE;
int changed = func_hashtab.ht_changed;
fp->uf_flags |= FC_DEAD;
- func_clear(fp, TRUE);
- // When clearing a function another function can be cleared
- // as a side effect. When that happens start over.
- if (changed != func_hashtab.ht_changed)
- break;
+
+ if (fp->uf_calls > 0)
+ {
+ // Function is executing, don't free it but do remove
+ // it from the hashtable.
+ if (func_remove(fp))
+ fp->uf_refcount--;
+ }
+ else
+ {
+ func_clear(fp, TRUE);
+ // When clearing a function another function can be
+ // cleared as a side effect. When that happens start
+ // over.
+ if (changed != func_hashtab.ht_changed)
+ break;
+ }
}
--todo;
}
// do remove it from the hashtable.
if (func_remove(fp))
fp->uf_refcount--;
- fp->uf_flags |= FC_DELETED;
}
else
func_clear_free(fp, FALSE);
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 2614,
/**/
2613,
/**/
else
ectx->ec_outer = NULL;
+ ++ufunc->uf_calls;
+
// Set execution state to the start of the called function.
ectx->ec_dfunc_idx = cdf_idx;
ectx->ec_instr = INSTRUCTIONS(dfunc);
}
}
#endif
+ // TODO: when is it safe to delete the function when it is no longer used?
+ --dfunc->df_ufunc->uf_calls;
+
// execution context goes one level up
entry = estack_pop();
if (entry != NULL)
++ectx.ec_stack.ga_len;
}
if (ufunc->uf_va_name != NULL)
- ++ectx.ec_stack.ga_len;
+ ++ectx.ec_stack.ga_len;
// Frame pointer points to just after arguments.
ectx.ec_frame_idx = ectx.ec_stack.ga_len;
// Do turn errors into exceptions.
suppress_errthrow = FALSE;
+ // Do not delete the function while executing it.
+ ++ufunc->uf_calls;
+
// When ":silent!" was used before calling then we still abort the
// function. If ":silent!" is used in the function then we don't.
emsg_silent_def = emsg_silent;
estack_pop();
current_sctx = save_current_sctx;
+ // TODO: when is it safe to delete the function if it is no longer used?
+ --ufunc->uf_calls;
+
if (*msg_list != NULL && saved_msg_list != NULL)
{
msglist_T **plist = saved_msg_list;