Skip to content

Commit 6914c64

Browse files
committed
patch 8.0.0535: memory leak when exiting from within a user function
Problem: Memory leak when exiting from within a user function. Solution: Clear the function call stack on exit.
1 parent 33ccb24 commit 6914c64

2 files changed

Lines changed: 60 additions & 39 deletions

File tree

src/userfunc.c

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ static garray_T funcargs = GA_EMPTY;
4141
/* pointer to funccal for currently active function */
4242
funccall_T *current_funccal = NULL;
4343

44-
/* pointer to list of previously used funccal, still around because some
44+
/* Pointer to list of previously used funccal, still around because some
4545
* item in it is still being used. */
4646
funccall_T *previous_funccal = NULL;
4747

@@ -627,6 +627,55 @@ free_funccal(
627627
vim_free(fc);
628628
}
629629

630+
/*
631+
* Handle the last part of returning from a function: free the local hashtable.
632+
* Unless it is still in use by a closure.
633+
*/
634+
static void
635+
cleanup_function_call(funccall_T *fc)
636+
{
637+
current_funccal = fc->caller;
638+
639+
/* If the a:000 list and the l: and a: dicts are not referenced and there
640+
* is no closure using it, we can free the funccall_T and what's in it. */
641+
if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
642+
&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
643+
&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT
644+
&& fc->fc_refcount <= 0)
645+
{
646+
free_funccal(fc, FALSE);
647+
}
648+
else
649+
{
650+
hashitem_T *hi;
651+
listitem_T *li;
652+
int todo;
653+
dictitem_T *v;
654+
655+
/* "fc" is still in use. This can happen when returning "a:000",
656+
* assigning "l:" to a global variable or defining a closure.
657+
* Link "fc" in the list for garbage collection later. */
658+
fc->caller = previous_funccal;
659+
previous_funccal = fc;
660+
661+
/* Make a copy of the a: variables, since we didn't do that above. */
662+
todo = (int)fc->l_avars.dv_hashtab.ht_used;
663+
for (hi = fc->l_avars.dv_hashtab.ht_array; todo > 0; ++hi)
664+
{
665+
if (!HASHITEM_EMPTY(hi))
666+
{
667+
--todo;
668+
v = HI2DI(hi);
669+
copy_tv(&v->di_tv, &v->di_tv);
670+
}
671+
}
672+
673+
/* Make a copy of the a:000 items, since we didn't do that above. */
674+
for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next)
675+
copy_tv(&li->li_tv, &li->li_tv);
676+
}
677+
}
678+
630679
/*
631680
* Call a user function.
632681
*/
@@ -982,46 +1031,9 @@ call_user_func(
9821031
}
9831032

9841033
did_emsg |= save_did_emsg;
985-
current_funccal = fc->caller;
9861034
--depth;
9871035

988-
/* If the a:000 list and the l: and a: dicts are not referenced and there
989-
* is no closure using it, we can free the funccall_T and what's in it. */
990-
if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
991-
&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
992-
&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT
993-
&& fc->fc_refcount <= 0)
994-
{
995-
free_funccal(fc, FALSE);
996-
}
997-
else
998-
{
999-
hashitem_T *hi;
1000-
listitem_T *li;
1001-
int todo;
1002-
1003-
/* "fc" is still in use. This can happen when returning "a:000",
1004-
* assigning "l:" to a global variable or defining a closure.
1005-
* Link "fc" in the list for garbage collection later. */
1006-
fc->caller = previous_funccal;
1007-
previous_funccal = fc;
1008-
1009-
/* Make a copy of the a: variables, since we didn't do that above. */
1010-
todo = (int)fc->l_avars.dv_hashtab.ht_used;
1011-
for (hi = fc->l_avars.dv_hashtab.ht_array; todo > 0; ++hi)
1012-
{
1013-
if (!HASHITEM_EMPTY(hi))
1014-
{
1015-
--todo;
1016-
v = HI2DI(hi);
1017-
copy_tv(&v->di_tv, &v->di_tv);
1018-
}
1019-
}
1020-
1021-
/* Make a copy of the a:000 items, since we didn't do that above. */
1022-
for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next)
1023-
copy_tv(&li->li_tv, &li->li_tv);
1024-
}
1036+
cleanup_function_call(fc);
10251037
}
10261038

10271039
/*
@@ -1147,6 +1159,13 @@ free_all_functions(void)
11471159
long_u todo = 1;
11481160
long_u used;
11491161

1162+
/* Clean up the call stack. */
1163+
while (current_funccal != NULL)
1164+
{
1165+
clear_tv(current_funccal->rettv);
1166+
cleanup_function_call(current_funccal);
1167+
}
1168+
11501169
/* First clear what the functions contain. Since this may lower the
11511170
* reference count of a function, it may also free a function and change
11521171
* the hash table. Restart if that happens. */

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,8 @@ static char *(features[]) =
764764

765765
static int included_patches[] =
766766
{ /* Add new patch number below this line */
767+
/**/
768+
535,
767769
/**/
768770
534,
769771
/**/

0 commit comments

Comments
 (0)