To: vim_dev@googlegroups.com Subject: Patch 8.2.4322 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4322 Problem: Vim9: crash when using funcref with closure. Solution: Keep a reference to the funcref that has the outer context. (closes #9716) Files: src/evalfunc.c, src/structs.h, src/eval.c, src/vim9execute.c, src/testdir/test_vim9_func.vim *** ../vim-8.2.4321/src/evalfunc.c 2022-02-07 17:50:35.917608561 +0000 --- src/evalfunc.c 2022-02-07 19:32:22.567219215 +0000 *************** *** 4456,4462 **** } if (arg_pt != NULL) ! pt->pt_outer = arg_pt->pt_outer; } rettv->v_type = VAR_PARTIAL; rettv->vval.v_partial = pt; --- 4456,4465 ---- } if (arg_pt != NULL) ! { ! pt->pt_outer_partial = arg_pt; ! ++arg_pt->pt_refcount; ! } } rettv->v_type = VAR_PARTIAL; rettv->vval.v_partial = pt; *** ../vim-8.2.4321/src/structs.h 2022-01-31 14:59:33.526943578 +0000 --- src/structs.h 2022-02-07 19:33:21.827086733 +0000 *************** *** 2051,2056 **** --- 2051,2059 ---- // 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 *** ../vim-8.2.4321/src/eval.c 2022-02-07 13:56:40.592793019 +0000 --- src/eval.c 2022-02-07 19:34:17.682962671 +0000 *************** *** 4526,4531 **** --- 4526,4534 ---- // "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) *** ../vim-8.2.4321/src/vim9execute.c 2022-02-07 15:31:33.081531879 +0000 --- src/vim9execute.c 2022-02-07 19:48:06.348567121 +0000 *************** *** 235,240 **** --- 235,257 ---- } /* + * 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 * called function. *************** *** 421,433 **** return FAIL; if (pt != NULL) { ! ref->or_outer = &pt->pt_outer; ++pt->pt_refcount; ref->or_partial = pt; } else if (ufunc->uf_partial != NULL) { ! ref->or_outer = &ufunc->uf_partial->pt_outer; ++ufunc->uf_partial->pt_refcount; ref->or_partial = ufunc->uf_partial; } --- 438,450 ---- return FAIL; if (pt != NULL) { ! ref->or_outer = get_pt_outer(pt); ++pt->pt_refcount; ref->or_partial = pt; } else if (ufunc->uf_partial != NULL) { ! ref->or_outer = get_pt_outer(ufunc->uf_partial); ++ufunc->uf_partial->pt_refcount; ref->or_partial = ufunc->uf_partial; } *************** *** 5086,5092 **** goto failed_early; if (partial != NULL) { ! if (partial->pt_outer.out_stack == NULL) { if (current_ectx != NULL) { --- 5103,5111 ---- goto failed_early; if (partial != NULL) { ! outer_T *outer = get_pt_outer(partial); ! ! if (outer->out_stack == NULL) { if (current_ectx != NULL) { *************** *** 5099,5105 **** } else { ! ectx.ec_outer_ref->or_outer = &partial->pt_outer; ++partial->pt_refcount; ectx.ec_outer_ref->or_partial = partial; } --- 5118,5124 ---- } else { ! ectx.ec_outer_ref->or_outer = outer; ++partial->pt_refcount; ectx.ec_outer_ref->or_partial = partial; } *** ../vim-8.2.4321/src/testdir/test_vim9_func.vim 2022-02-07 17:50:35.917608561 +0000 --- src/testdir/test_vim9_func.vim 2022-02-07 19:51:47.500000061 +0000 *************** *** 3477,3482 **** --- 3477,3501 ---- 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 *** ../vim-8.2.4321/src/version.c 2022-02-07 17:50:35.917608561 +0000 --- src/version.c 2022-02-07 19:22:47.764579044 +0000 *************** *** 748,749 **** --- 748,751 ---- { /* Add new patch number below this line */ + /**/ + 4322, /**/ -- Computers are useless. They can only give you answers. -- Pablo Picasso /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///