From 7aca5ca6763e50d2c23953b20e30fca7457c9abf Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 7 Feb 2022 19:56:43 +0000 Subject: [PATCH] patch 8.2.4322: Vim9: crash when using funcref with closure Problem: Vim9: crash when using funcref with closure. Solution: Keep a reference to the funcref that has the outer context. (closes #9716) --- src/eval.c | 3 +++ src/evalfunc.c | 5 ++++- src/structs.h | 3 +++ src/testdir/test_vim9_func.vim | 19 +++++++++++++++++++ src/version.c | 2 ++ src/vim9execute.c | 27 +++++++++++++++++++++++---- 6 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/eval.c b/src/eval.c index 7ab05be12..2942d0fe1 100644 --- a/src/eval.c +++ b/src/eval.c @@ -4526,6 +4526,9 @@ partial_free(partial_T *pt) // "out_up" is no longer used, decrement refcount on partial that owns it. partial_unref(pt->pt_outer.out_up_partial); + // Using pt_outer from another partial. + partial_unref(pt->pt_outer_partial); + // Decrease the reference count for the context of a closure. If down // to the minimum it may be time to free it. if (pt->pt_funcstack != NULL) diff --git a/src/evalfunc.c b/src/evalfunc.c index eb12e75d0..b031369ef 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -4456,7 +4456,10 @@ common_function(typval_T *argvars, typval_T *rettv, int is_funcref) } if (arg_pt != NULL) - pt->pt_outer = arg_pt->pt_outer; + { + pt->pt_outer_partial = arg_pt; + ++arg_pt->pt_refcount; + } } rettv->v_type = VAR_PARTIAL; rettv->vval.v_partial = pt; diff --git a/src/structs.h b/src/structs.h index ecab3541d..1e759f5df 100644 --- a/src/structs.h +++ b/src/structs.h @@ -2051,6 +2051,9 @@ struct partial_S // For a compiled closure: the arguments and local variables scope outer_T pt_outer; + // For a partial of a partial: use pt_outer values of this partial. + partial_T *pt_outer_partial; + funcstack_T *pt_funcstack; // copy of stack, used after context // function returns diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 7ab15f72f..4ac4643e0 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -3477,6 +3477,25 @@ def Test_nested_closure_funcref() unlet g:result_one g:result_two enddef +def Test_nested_closure_in_dict() + var lines =<< trim END + vim9script + def Func(): dict + var n: number + def Inc(): number + ++n + return n + enddef + return {inc: function(Inc)} + enddef + disas Func + var d = Func() + assert_equal(1, d.inc()) + assert_equal(2, d.inc()) + END + v9.CheckScriptSuccess(lines) +enddef + def Test_check_func_arg_types() var lines =<< trim END vim9script diff --git a/src/version.c b/src/version.c index 97bcfdef9..c1f90cdd0 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4322, /**/ 4321, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index 961e4507c..1412d0836 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -234,6 +234,23 @@ dict_stack_clear(int len) dict_stack_drop(); } +/* + * Get a pointer to useful "pt_outer" of "pt". + */ + static outer_T * +get_pt_outer(partial_T *pt) +{ + partial_T *ptref = pt->pt_outer_partial; + + if (ptref == NULL) + return &pt->pt_outer; + + // partial using partial (recursively) + while (ptref->pt_outer_partial != NULL) + ptref = ptref->pt_outer_partial; + return &ptref->pt_outer; +} + /* * Call compiled function "cdf_idx" from compiled code. * This adds a stack frame and sets the instruction pointer to the start of the @@ -421,13 +438,13 @@ call_dfunc( return FAIL; if (pt != NULL) { - ref->or_outer = &pt->pt_outer; + ref->or_outer = get_pt_outer(pt); ++pt->pt_refcount; ref->or_partial = pt; } else if (ufunc->uf_partial != NULL) { - ref->or_outer = &ufunc->uf_partial->pt_outer; + ref->or_outer = get_pt_outer(ufunc->uf_partial); ++ufunc->uf_partial->pt_refcount; ref->or_partial = ufunc->uf_partial; } @@ -5086,7 +5103,9 @@ call_def_function( goto failed_early; if (partial != NULL) { - if (partial->pt_outer.out_stack == NULL) + outer_T *outer = get_pt_outer(partial); + + if (outer->out_stack == NULL) { if (current_ectx != NULL) { @@ -5099,7 +5118,7 @@ call_def_function( } else { - ectx.ec_outer_ref->or_outer = &partial->pt_outer; + ectx.ec_outer_ref->or_outer = outer; ++partial->pt_refcount; ectx.ec_outer_ref->or_partial = partial; } -- 2.40.0