0
0
mirror of https://github.com/vim/vim.git synced 2025-09-25 03:54:15 -04:00

patch 8.2.2391: memory leak when creating a global function with closure

Problem:    Memory leak when creating a global function with closure.
Solution:   Create a separate partial for every instantiated function.
This commit is contained in:
Bram Moolenaar
2021-01-22 20:46:27 +01:00
parent b3005ce191
commit 0d3de8cb59
3 changed files with 45 additions and 32 deletions

View File

@@ -1352,8 +1352,13 @@ func_clear_items(ufunc_T *fp)
VIM_CLEAR(fp->uf_block_ids); VIM_CLEAR(fp->uf_block_ids);
VIM_CLEAR(fp->uf_va_name); VIM_CLEAR(fp->uf_va_name);
clear_type_list(&fp->uf_type_list); clear_type_list(&fp->uf_type_list);
// Increment the refcount of this function to avoid it being freed
// recursively when the partial is freed.
fp->uf_refcount += 3;
partial_unref(fp->uf_partial); partial_unref(fp->uf_partial);
fp->uf_partial = NULL; fp->uf_partial = NULL;
fp->uf_refcount -= 3;
#ifdef FEAT_LUA #ifdef FEAT_LUA
if (fp->uf_cb_free != NULL) if (fp->uf_cb_free != NULL)
@@ -1446,10 +1451,10 @@ copy_func(char_u *lambda, char_u *global, ectx_T *ectx)
return FAIL; return FAIL;
} }
// TODO: handle ! to overwrite
fp = find_func(global, TRUE, NULL); fp = find_func(global, TRUE, NULL);
if (fp != NULL) if (fp != NULL)
{ {
// TODO: handle ! to overwrite
semsg(_(e_funcexts), global); semsg(_(e_funcexts), global);
return FAIL; return FAIL;
} }
@@ -1501,8 +1506,9 @@ copy_func(char_u *lambda, char_u *global, ectx_T *ectx)
// the referenced dfunc_T is now used one more time // the referenced dfunc_T is now used one more time
link_def_function(fp); link_def_function(fp);
// Create a partial to store the context of the function, if not done // Create a partial to store the context of the function where it was
// already. // instantiated. Only needs to be done once. Do this on the original
// function, "dfunc->df_ufunc" will point to it.
if ((ufunc->uf_flags & FC_CLOSURE) && ufunc->uf_partial == NULL) if ((ufunc->uf_flags & FC_CLOSURE) && ufunc->uf_partial == NULL)
{ {
partial_T *pt = ALLOC_CLEAR_ONE(partial_T); partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
@@ -1510,14 +1516,12 @@ copy_func(char_u *lambda, char_u *global, ectx_T *ectx)
if (pt == NULL) if (pt == NULL)
goto failed; goto failed;
if (fill_partial_and_closure(pt, ufunc, ectx) == FAIL) if (fill_partial_and_closure(pt, ufunc, ectx) == FAIL)
goto failed;
ufunc->uf_partial = pt;
--pt->pt_refcount; // not referenced here yet
}
if (ufunc->uf_partial != NULL)
{ {
fp->uf_partial = ufunc->uf_partial; vim_free(pt);
++fp->uf_partial->pt_refcount; goto failed;
}
ufunc->uf_partial = pt;
--pt->pt_refcount; // not actually referenced here
} }
return OK; return OK;
@@ -4243,23 +4247,21 @@ func_unref(char_u *name)
#endif #endif
internal_error("func_unref()"); internal_error("func_unref()");
} }
if (fp != NULL && --fp->uf_refcount <= 0) func_ptr_unref(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_clear_free(fp, FALSE);
}
} }
/* /*
* Unreference a Function: decrement the reference count and free it when it * Unreference a Function: decrement the reference count and free it when it
* becomes zero. * becomes zero.
* Also when it becomes one and uf_partial points to the function.
*/ */
void void
func_ptr_unref(ufunc_T *fp) func_ptr_unref(ufunc_T *fp)
{ {
if (fp != NULL && --fp->uf_refcount <= 0) if (fp != NULL && (--fp->uf_refcount <= 0
|| (fp->uf_refcount == 1 && fp->uf_partial != NULL
&& fp->uf_partial->pt_refcount <= 1
&& fp->uf_partial->pt_func == fp)))
{ {
// Only delete it when it's not being used. Otherwise it's done // Only delete it when it's not being used. Otherwise it's done
// when "uf_calls" becomes zero. // when "uf_calls" becomes zero.

View File

@@ -750,6 +750,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
2391,
/**/ /**/
2390, 2390,
/**/ /**/

View File

@@ -263,7 +263,8 @@ call_dfunc(int cdf_idx, partial_T *pt, int argcount_arg, ectx_T *ectx)
} }
ectx->ec_stack.ga_len += STACK_FRAME_SIZE + varcount; ectx->ec_stack.ga_len += STACK_FRAME_SIZE + varcount;
if (pt != NULL || ufunc->uf_partial != NULL || ufunc->uf_flags & FC_CLOSURE) if (pt != NULL || ufunc->uf_partial != NULL
|| (ufunc->uf_flags & FC_CLOSURE))
{ {
outer_T *outer = ALLOC_CLEAR_ONE(outer_T); outer_T *outer = ALLOC_CLEAR_ONE(outer_T);
@@ -1062,7 +1063,7 @@ fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
pt->pt_func = ufunc; pt->pt_func = ufunc;
pt->pt_refcount = 1; pt->pt_refcount = 1;
if (pt->pt_func->uf_flags & FC_CLOSURE) if (ufunc->uf_flags & FC_CLOSURE)
{ {
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ectx->ec_dfunc_idx; + ectx->ec_dfunc_idx;
@@ -1093,7 +1094,7 @@ fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
++pt->pt_refcount; ++pt->pt_refcount;
++ectx->ec_funcrefs.ga_len; ++ectx->ec_funcrefs.ga_len;
} }
++pt->pt_func->uf_refcount; ++ufunc->uf_refcount;
return OK; return OK;
} }
@@ -1243,7 +1244,14 @@ call_def_function(
ectx.ec_frame_idx = ectx.ec_stack.ga_len; ectx.ec_frame_idx = ectx.ec_stack.ga_len;
initial_frame_idx = ectx.ec_frame_idx; initial_frame_idx = ectx.ec_frame_idx;
if (partial != NULL || ufunc->uf_partial != NULL) {
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ufunc->uf_dfunc_idx;
ufunc_T *base_ufunc = dfunc->df_ufunc;
// "uf_partial" is on the ufunc that "df_ufunc" points to, as is done
// by copy_func().
if (partial != NULL || base_ufunc->uf_partial != NULL)
{ {
ectx.ec_outer = ALLOC_CLEAR_ONE(outer_T); ectx.ec_outer = ALLOC_CLEAR_ONE(outer_T);
if (ectx.ec_outer == NULL) if (ectx.ec_outer == NULL)
@@ -1259,9 +1267,10 @@ call_def_function(
*ectx.ec_outer = partial->pt_outer; *ectx.ec_outer = partial->pt_outer;
} }
else else
*ectx.ec_outer = ufunc->uf_partial->pt_outer; *ectx.ec_outer = base_ufunc->uf_partial->pt_outer;
ectx.ec_outer->out_up_is_copy = TRUE; ectx.ec_outer->out_up_is_copy = TRUE;
} }
}
// dummy frame entries // dummy frame entries
for (idx = 0; idx < STACK_FRAME_SIZE; ++idx) for (idx = 0; idx < STACK_FRAME_SIZE; ++idx)