diff options
author | KunMing Xie <qqzz014@gmail.com> | 2018-06-25 02:35:25 +0800 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2018-06-24 20:35:25 +0200 |
commit | 83be7cec98713a7a313529b6a0cbadb465800c5c (patch) | |
tree | fd800afab8a9fbda9b9879412e84bfdc6ccfee4d | |
parent | 89cb304ea0ac7fb94e7ccd319c0f98b8f6d3cb9a (diff) | |
download | rneovim-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.c | 64 |
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". */ |