aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKunMing Xie <qqzz014@gmail.com>2018-06-25 02:35:25 +0800
committerJustin M. Keyes <justinkz@gmail.com>2018-06-24 20:35:25 +0200
commit83be7cec98713a7a313529b6a0cbadb465800c5c (patch)
treefd800afab8a9fbda9b9879412e84bfdc6ccfee4d
parent89cb304ea0ac7fb94e7ccd319c0f98b8f6d3cb9a (diff)
downloadrneovim-83be7cec98713a7a313529b6a0cbadb465800c5c.tar.gz
rneovim-83be7cec98713a7a313529b6a0cbadb465800c5c.tar.bz2
rneovim-83be7cec98713a7a313529b6a0cbadb465800c5c.zip
vim-patch:8.0.0535: leak when exiting user function (#8574)
Problem: Memory leak when exiting from within a user function. Solution: Clear the function call stack on exit. https://github.com/vim/vim/commit/6914c64ee58ce68f31fb8a8793293a9b3f2f6240
-rw-r--r--src/nvim/eval.c64
1 files changed, 39 insertions, 25 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index b517169e08..d0e312475c 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -693,8 +693,8 @@ int func_level(void *cookie)
/* pointer to funccal for currently active function */
funccall_T *current_funccal = NULL;
-/* pointer to list of previously used funccal, still around because some
- * item in it is still being used. */
+// Pointer to list of previously used funccal, still around because some
+// item in it is still being used.
funccall_T *previous_funccal = NULL;
/*
@@ -20506,6 +20506,12 @@ void free_all_functions(void)
uint64_t todo = 1;
uint64_t used;
+ // Clean up the call stack.
+ while (current_funccal != NULL) {
+ tv_clear(current_funccal->rettv);
+ cleanup_function_call(current_funccal);
+ }
+
// First clear what the functions contain. Since this may lower the
// reference count of a function, it may also free a function and change
// the hash table. Restart if that happens.
@@ -21485,30 +21491,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
}
did_emsg |= save_did_emsg;
- current_funccal = fc->caller;
- --depth;
-
- // If the a:000 list and the l: and a: dicts are not referenced and there
- // is no closure using it, we can free the funccall_T and what's in it.
- if (!fc_referenced(fc)) {
- free_funccal(fc, false);
- } else {
- // "fc" is still in use. This can happen when returning "a:000",
- // assigning "l:" to a global variable or defining a closure.
- // Link "fc" in the list for garbage collection later.
- fc->caller = previous_funccal;
- previous_funccal = fc;
+ depth--;
- // Make a copy of the a: variables, since we didn't do that above.
- TV_DICT_ITER(&fc->l_avars, di, {
- tv_copy(&di->di_tv, &di->di_tv);
- });
-
- // Make a copy of the a:000 items, since we didn't do that above.
- TV_LIST_ITER(&fc->l_varlist, li, {
- tv_copy(TV_LIST_ITEM_TV(li), TV_LIST_ITEM_TV(li));
- });
- }
+ cleanup_function_call(fc);
if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) {
// Function was unreferenced while being used, free it now.
@@ -21601,6 +21586,35 @@ free_funccal(
xfree(fc);
}
+/// Handle the last part of returning from a function: free the local hashtable.
+/// Unless it is still in use by a closure.
+static void cleanup_function_call(funccall_T *fc)
+{
+ current_funccal = fc->caller;
+
+ // If the a:000 list and the l: and a: dicts are not referenced and there
+ // is no closure using it, we can free the funccall_T and what's in it.
+ if (!fc_referenced(fc)) {
+ free_funccal(fc, false);
+ } else {
+ // "fc" is still in use. This can happen when returning "a:000",
+ // assigning "l:" to a global variable or defining a closure.
+ // Link "fc" in the list for garbage collection later.
+ fc->caller = previous_funccal;
+ previous_funccal = fc;
+
+ // Make a copy of the a: variables, since we didn't do that above.
+ TV_DICT_ITER(&fc->l_avars, di, {
+ tv_copy(&di->di_tv, &di->di_tv);
+ });
+
+ // Make a copy of the a:000 items, since we didn't do that above.
+ TV_LIST_ITER(&fc->l_varlist, li, {
+ tv_copy(TV_LIST_ITEM_TV(li), TV_LIST_ITEM_TV(li));
+ });
+ }
+}
+
/*
* Add a number variable "name" to dict "dp" with value "nr".
*/