diff options
-rw-r--r-- | src/nvim/eval.c | 121 | ||||
-rw-r--r-- | src/nvim/version.c | 2 |
2 files changed, 81 insertions, 42 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f301753996..8ce9c7712a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5938,6 +5938,9 @@ bool garbage_collect(bool testing) ABORTING(set_ref_in_ht)(&fc->l_avars.dv_hashtab, copyID, NULL); } + // named functions (matters for closures) + ABORTING(set_ref_in_functions(copyID)); + // Jobs { TerminalJobData *data; @@ -6275,12 +6278,33 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, return abort; } +/// Set "copyID" in all functions available by name. +bool set_ref_in_functions(int copyID) +{ + int todo; + hashitem_T *hi = NULL; + bool abort = false; + ufunc_T *fp; + + todo = (int)func_hashtab.ht_used; + for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) { + if (!HASHITEM_EMPTY(hi)) { + todo--; + fp = HI2UF(hi); + if (!func_name_refcount(fp->uf_name)) { + abort = abort || set_ref_in_func(NULL, fp, copyID); + } + } + } + return abort; +} + /// Mark all lists and dicts referenced through function "name" with "copyID". /// "list_stack" is used to add lists to be marked. Can be NULL. /// "ht_stack" is used to add hashtabs to be marked. Can be NULL. /// -/// @return TRUE if setting references failed somehow. -int set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) +/// @return true if setting references failed somehow. +bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) { ufunc_T *fp = fp_in; funccall_T *fc; @@ -6299,11 +6323,7 @@ int set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) } if (fp != NULL) { for (fc = fp->uf_scoped; fc != NULL; fc = fc->func->uf_scoped) { - if (fc->fc_copyID != copyID) { - fc->fc_copyID = copyID; - abort = set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL); - abort = set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL); - } + abort = abort || set_ref_in_funccal(fc, copyID); } } xfree(tofree); @@ -6356,9 +6376,20 @@ static inline bool set_ref_dict(dict_T *dict, int copyID) return false; } -/* - * Allocate an empty header for a dictionary. - */ +static bool set_ref_in_funccal(funccall_T *fc, int copyID) +{ + int abort = false; + + if (fc->fc_copyID != copyID) { + fc->fc_copyID = copyID; + abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL); + abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL); + abort = abort || set_ref_in_func(NULL, fc->func, copyID); + } + return abort; +} + +/// Allocate an empty header for a dictionary. dict_T *dict_alloc(void) FUNC_ATTR_NONNULL_RET { dict_T *d = xmalloc(sizeof(dict_T)); @@ -6999,10 +7030,9 @@ static int register_closure(ufunc_T *fp) { // no change return OK; } - funccal_unref(fp->uf_scoped, fp); + funccal_unref(fp->uf_scoped, fp, false); fp->uf_scoped = current_funccal; current_funccal->fc_refcount++; - func_ptr_ref(current->funccal->func); ga_grow(¤t_funccal->fc_funcs, 1); ((ufunc_T **)current_funccal->fc_funcs.ga_data) [current_funccal->fc_funcs.ga_len++] = fp; @@ -20952,8 +20982,9 @@ void ex_function(exarg_T *eap) if (!HASHITEM_EMPTY(hi)) { --todo; fp = HI2UF(hi); - if (!isdigit(*fp->uf_name)) - list_func_head(fp, FALSE); + if (!func_name_refcount(fp->uf_name)) { + list_func_head(fp, false); + } } } } @@ -21756,8 +21787,17 @@ void free_all_functions(void) while (func_hashtab.ht_used > 0) for (hi = func_hashtab.ht_array;; ++hi) if (!HASHITEM_EMPTY(hi)) { - func_free(HI2UF(hi)); - break; + todo--; + // Only free functions that are not refcounted, those are + // supposed to be freed when no longer referenced. + fp = HI2UF(hi); + if (func_name_refcount(fp->uf_name)) { + skipped++; + } else { + func_free(fp, true); + skipped = 0; + break; + } } } @@ -22177,7 +22217,7 @@ void ex_delfunction(exarg_T *eap) } fp->uf_flags |= FC_DELETED; } else { - func_free(fp); + func_free(fp, false); } } } @@ -22199,10 +22239,10 @@ static bool func_remove(ufunc_T *fp) return false; } -/* - * Free a function and remove it from the list of functions. - */ -static void func_free(ufunc_T *fp) +/// Free a function and remove it from the list of functions. +/// +/// param[in] force When true, we are exiting. +static void func_free(ufunc_T *fp, bool force) { // clear this function ga_clear_strings(&(fp->uf_args)); @@ -22216,7 +22256,7 @@ static void func_free(ufunc_T *fp) if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0) { func_remove(fp); } - funccal_unref(fp->uf_scoped, fp); + funccal_unref(fp->uf_scoped, fp, force); func_remove(fp); xfree(fp); } @@ -22257,7 +22297,7 @@ void func_unref(char_u *name) // Only delete it when it's not being used. Otherwise it's done // when "uf_calls" becomes zero. if (fp->uf_calls == 0) { - func_free(fp); + func_free(fp, false); } } } @@ -22270,7 +22310,7 @@ void func_ptr_unref(ufunc_T *fp) // Only delete it when it's not being used. Otherwise it's done // when "uf_calls" becomes zero. if (fp->uf_calls == 0) { - func_free(fp); + func_free(fp, false); } } } @@ -22681,7 +22721,7 @@ call_user_func( if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) { // Function was unreferenced while being used, free it now. - func_free(fp); + func_free(fp, false); } // restore search patterns and redo buffer if (did_save_redo) { @@ -22692,26 +22732,26 @@ call_user_func( /// Unreference "fc": decrement the reference count and free it when it /// becomes zero. "fp" is detached from "fc". -static void funccal_unref(funccall_T *fc, ufunc_T *fp) +/// +/// @param[in] force When true, we are exiting. +static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force) { funccall_T **pfc; int i; - int freed = false; if (fc == NULL) { return; } - if (--fc->fc_refcount <= 0 - && fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT + if (--fc->fc_refcount <= 0 && (force || ( + fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT - && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT) { + && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT))) { for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) { if (fc == *pfc) { - *pfc = fc->caller; - free_funccal(fc, true); - return; - } + *pfc = fc->caller; + free_funccal(fc, true); + return; } } } @@ -22747,13 +22787,12 @@ free_funccal ( for (int i = 0; i < fc->fc_funcs.ga_len; i++) { ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i]; - if (fp != NULL) { - // Function may have been redefined and point to another - // funccall_T, don't clear it then. - if (fp->uf_scoped == fc) { - fp->uf_scoped = NULL; - } - func_ptr_unref(fc->func); + // When garbage collecting a funccall_T may be freed before the + // function that references it, clear its uf_scoped field. + // The function may have been redefined and point to another + // funccal_T, don't clear it then. + if (fp != NULL && fp->uf_scoped == fc) { + fp->uf_scoped = NULL; } } ga_clear(&fc->fc_funcs); diff --git a/src/nvim/version.c b/src/nvim/version.c index 059b6f3bd6..89cd61fc6e 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -297,7 +297,7 @@ static int included_patches[] = { 2146, // 2145 NA // 2144, - // 2143, + 2143, 2142, 2141, // 2140 NA |