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:
@@ -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.
|
||||||
|
@@ -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,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -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)
|
||||||
|
Reference in New Issue
Block a user