Patch 8.2.2391
Problem: Memory leak when creating a global function with closure.
Solution: Create a separate partial for every instantiated function.
Files: src/userfunc.c, src/vim9execute.c
*** ../vim-8.2.2390/src/userfunc.c 2021-01-16 18:09:48.017277750 +0100
--- src/userfunc.c 2021-01-22 20:32:06.826000087 +0100
***************
*** 1352,1359 ****
--- 1352,1364 ----
VIM_CLEAR(fp->uf_block_ids);
VIM_CLEAR(fp->uf_va_name);
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);
fp->uf_partial = NULL;
+ fp->uf_refcount -= 3;
#ifdef FEAT_LUA
if (fp->uf_cb_free != NULL)
***************
*** 1446,1455 ****
return FAIL;
}
- // TODO: handle ! to overwrite
fp = find_func(global, TRUE, NULL);
if (fp != NULL)
{
semsg(_(e_funcexts), global);
return FAIL;
}
--- 1451,1460 ----
return FAIL;
}
fp = find_func(global, TRUE, NULL);
if (fp != NULL)
{
+ // TODO: handle ! to overwrite
semsg(_(e_funcexts), global);
return FAIL;
}
***************
*** 1501,1508 ****
// the referenced dfunc_T is now used one more time
link_def_function(fp);
! // Create a partial to store the context of the function, if not done
! // already.
if ((ufunc->uf_flags & FC_CLOSURE) && ufunc->uf_partial == NULL)
{
partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
--- 1506,1514 ----
// the referenced dfunc_T is now used one more time
link_def_function(fp);
! // Create a partial to store the context of the function where it was
! // 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)
{
partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
***************
*** 1510,1523 ****
if (pt == NULL)
goto failed;
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;
! ++fp->uf_partial->pt_refcount;
}
return OK;
--- 1516,1527 ----
if (pt == NULL)
goto failed;
if (fill_partial_and_closure(pt, ufunc, ectx) == FAIL)
+ {
+ vim_free(pt);
goto failed;
+ }
ufunc->uf_partial = pt;
! --pt->pt_refcount; // not actually referenced here
}
return OK;
***************
*** 4243,4265 ****
#endif
internal_error("func_unref()");
}
! if (fp != NULL && --fp->uf_refcount <= 0)
! {
! // 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
* becomes zero.
*/
void
func_ptr_unref(ufunc_T *fp)
{
! if (fp != NULL && --fp->uf_refcount <= 0)
{
// Only delete it when it's not being used. Otherwise it's done
// when "uf_calls" becomes zero.
--- 4247,4267 ----
#endif
internal_error("func_unref()");
}
! func_ptr_unref(fp);
}
/*
* Unreference a Function: decrement the reference count and free it when it
* becomes zero.
+ * Also when it becomes one and uf_partial points to the function.
*/
void
func_ptr_unref(ufunc_T *fp)
{
! 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
// when "uf_calls" becomes zero.
*** ../vim-8.2.2390/src/vim9execute.c 2021-01-22 17:51:02.762771043 +0100
--- src/vim9execute.c 2021-01-22 20:39:53.008839344 +0100
***************
*** 263,269 ****
}
ectx->ec_stack.ga_len += STACK_FRAME_SIZE + varcount;
! if (pt != NULL || ufunc->uf_partial != NULL || ufunc->uf_flags &
FC_CLOSURE)
{
outer_T *outer = ALLOC_CLEAR_ONE(outer_T);
--- 263,270 ----
}
ectx->ec_stack.ga_len += STACK_FRAME_SIZE + varcount;
! if (pt != NULL || ufunc->uf_partial != NULL
! || (ufunc->uf_flags & FC_CLOSURE))
{
outer_T *outer = ALLOC_CLEAR_ONE(outer_T);
***************
*** 1062,1068 ****
pt->pt_func = ufunc;
pt->pt_refcount = 1;
! if (pt->pt_func->uf_flags & FC_CLOSURE)
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ectx->ec_dfunc_idx;
--- 1063,1069 ----
pt->pt_func = ufunc;
pt->pt_refcount = 1;
! if (ufunc->uf_flags & FC_CLOSURE)
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ectx->ec_dfunc_idx;
***************
*** 1093,1099 ****
++pt->pt_refcount;
++ectx->ec_funcrefs.ga_len;
}
! ++pt->pt_func->uf_refcount;
return OK;
}
--- 1094,1100 ----
++pt->pt_refcount;
++ectx->ec_funcrefs.ga_len;
}
! ++ufunc->uf_refcount;
return OK;
}
***************
*** 1243,1266 ****
ectx.ec_frame_idx = ectx.ec_stack.ga_len;
initial_frame_idx = ectx.ec_frame_idx;
- if (partial != NULL || ufunc->uf_partial != NULL)
{
! ectx.ec_outer = ALLOC_CLEAR_ONE(outer_T);
! if (ectx.ec_outer == NULL)
! goto failed_early;
! if (partial != NULL)
! {
! if (partial->pt_outer.out_stack == NULL && current_ectx != NULL)
{
! if (current_ectx->ec_outer != NULL)
! *ectx.ec_outer = *current_ectx->ec_outer;
}
else
! *ectx.ec_outer = partial->pt_outer;
}
- else
- *ectx.ec_outer = ufunc->uf_partial->pt_outer;
- ectx.ec_outer->out_up_is_copy = TRUE;
}
// dummy frame entries
--- 1244,1275 ----
ectx.ec_frame_idx = ectx.ec_stack.ga_len;
initial_frame_idx = ectx.ec_frame_idx;
{
! 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);
! if (ectx.ec_outer == NULL)
! goto failed_early;
! if (partial != NULL)
{
! if (partial->pt_outer.out_stack == NULL && current_ectx != NULL)
! {
! if (current_ectx->ec_outer != NULL)
! *ectx.ec_outer = *current_ectx->ec_outer;
! }
! else
! *ectx.ec_outer = partial->pt_outer;
}
else
! *ectx.ec_outer = base_ufunc->uf_partial->pt_outer;
! ectx.ec_outer->out_up_is_copy = TRUE;
}
}
// dummy frame entries
*** ../vim-8.2.2390/src/version.c 2021-01-22 17:51:02.766771030 +0100
--- src/version.c 2021-01-22 20:43:34.500306834 +0100
***************
*** 752,753 ****
--- 752,755 ----
{ /* Add new patch number below this line */
+ /**/
+ 2391,
/**/
--
hundred-and-one symptoms of being an internet addict:
209. Your house stinks because you haven't cleaned it in a week.
/// Bram Moolenaar -- [email protected] -- 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 ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/202101221946.10MJktfV2287034%40masaka.moolenaar.net.