To: vim_dev@googlegroups.com Subject: Patch 8.0.0535 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.0.0535 Problem: Memory leak when exiting from within a user function. Solution: Clear the function call stack on exit. Files: src/userfunc.c *** ../vim-8.0.0534/src/userfunc.c 2017-03-16 17:23:26.839815753 +0100 --- src/userfunc.c 2017-04-01 21:01:57.537818637 +0200 *************** *** 41,47 **** /* 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. */ funccall_T *previous_funccal = NULL; --- 41,47 ---- /* 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. */ funccall_T *previous_funccal = NULL; *************** *** 628,633 **** --- 628,682 ---- } /* + * 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->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->fc_refcount <= 0) + { + free_funccal(fc, FALSE); + } + else + { + hashitem_T *hi; + listitem_T *li; + int todo; + dictitem_T *v; + + /* "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. */ + todo = (int)fc->l_avars.dv_hashtab.ht_used; + for (hi = fc->l_avars.dv_hashtab.ht_array; todo > 0; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + v = HI2DI(hi); + copy_tv(&v->di_tv, &v->di_tv); + } + } + + /* Make a copy of the a:000 items, since we didn't do that above. */ + for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) + copy_tv(&li->li_tv, &li->li_tv); + } + } + + /* * Call a user function. */ static void *************** *** 982,1027 **** } 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->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->fc_refcount <= 0) ! { ! free_funccal(fc, FALSE); ! } ! else ! { ! hashitem_T *hi; ! listitem_T *li; ! int todo; ! ! /* "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. */ ! todo = (int)fc->l_avars.dv_hashtab.ht_used; ! for (hi = fc->l_avars.dv_hashtab.ht_array; todo > 0; ++hi) ! { ! if (!HASHITEM_EMPTY(hi)) ! { ! --todo; ! v = HI2DI(hi); ! copy_tv(&v->di_tv, &v->di_tv); ! } ! } ! ! /* Make a copy of the a:000 items, since we didn't do that above. */ ! for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) ! copy_tv(&li->li_tv, &li->li_tv); ! } } /* --- 1031,1039 ---- } did_emsg |= save_did_emsg; --depth; ! cleanup_function_call(fc); } /* *************** *** 1147,1152 **** --- 1159,1171 ---- long_u todo = 1; long_u used; + /* Clean up the call stack. */ + while (current_funccal != NULL) + { + clear_tv(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. */ *** ../vim-8.0.0534/src/version.c 2017-04-01 16:59:25.194170493 +0200 --- src/version.c 2017-04-01 21:03:28.225268344 +0200 *************** *** 766,767 **** --- 766,769 ---- { /* Add new patch number below this line */ + /**/ + 535, /**/ -- hundred-and-one symptoms of being an internet addict: 250. You've given up the search for the "perfect woman" and instead, sit in front of the PC until you're just too tired to care. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///