comparison src/vim9execute.c @ 23285:112fa621b127 v8.2.2188

patch 8.2.2188: Vim9: crash when calling global function from :def function Commit: https://github.com/vim/vim/commit/cd45ed03bfdd7fac53d562ad402df74bd26e7754 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Dec 22 17:35:54 2020 +0100 patch 8.2.2188: Vim9: crash when calling global function from :def function Problem: Vim9: crash when calling global function from :def function. Solution: Set the outer context. Define the partial for the context on the original function. Use a refcount to keep track of which ufunc is using a dfunc. (closes #7525)
author Bram Moolenaar <Bram@vim.org>
date Tue, 22 Dec 2020 17:45:03 +0100
parents 00f7cd9b6033
children d9ae7dd3a0f2
comparison
equal deleted inserted replaced
23284:3d54c7fa353c 23285:112fa621b127
52 // .... flexible space for temporary values (can grow big) 52 // .... flexible space for temporary values (can grow big)
53 53
54 /* 54 /*
55 * Execution context. 55 * Execution context.
56 */ 56 */
57 typedef struct { 57 struct ectx_S {
58 garray_T ec_stack; // stack of typval_T values 58 garray_T ec_stack; // stack of typval_T values
59 int ec_frame_idx; // index in ec_stack: context of ec_dfunc_idx 59 int ec_frame_idx; // index in ec_stack: context of ec_dfunc_idx
60 60
61 garray_T *ec_outer_stack; // stack used for closures 61 garray_T *ec_outer_stack; // stack used for closures
62 int ec_outer_frame; // stack frame in ec_outer_stack 62 int ec_outer_frame; // stack frame in ec_outer_stack
67 int ec_dfunc_idx; // current function index 67 int ec_dfunc_idx; // current function index
68 isn_T *ec_instr; // array with instructions 68 isn_T *ec_instr; // array with instructions
69 int ec_iidx; // index in ec_instr: instruction to execute 69 int ec_iidx; // index in ec_instr: instruction to execute
70 70
71 garray_T ec_funcrefs; // partials that might be a closure 71 garray_T ec_funcrefs; // partials that might be a closure
72 } ectx_T; 72 };
73 73
74 // Get pointer to item relative to the bottom of the stack, -1 is the last one. 74 // Get pointer to item relative to the bottom of the stack, -1 is the last one.
75 #define STACK_TV_BOT(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_stack.ga_len + (idx)) 75 #define STACK_TV_BOT(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_stack.ga_len + (idx))
76 76
77 void 77 void
171 int idx; 171 int idx;
172 estack_T *entry; 172 estack_T *entry;
173 173
174 if (dfunc->df_deleted) 174 if (dfunc->df_deleted)
175 { 175 {
176 emsg_funcname(e_func_deleted, ufunc->uf_name); 176 // don't use ufunc->uf_name, it may have been freed
177 emsg_funcname(e_func_deleted,
178 dfunc->df_name == NULL ? (char_u *)"unknown" : dfunc->df_name);
177 return FAIL; 179 return FAIL;
178 } 180 }
179 181
180 if (ufunc->uf_va_name != NULL) 182 if (ufunc->uf_va_name != NULL)
181 { 183 {
258 tv->v_type = VAR_NUMBER; 260 tv->v_type = VAR_NUMBER;
259 tv->vval.v_number = 0; 261 tv->vval.v_number = 0;
260 } 262 }
261 ectx->ec_stack.ga_len += STACK_FRAME_SIZE + varcount; 263 ectx->ec_stack.ga_len += STACK_FRAME_SIZE + varcount;
262 264
265 if (ufunc->uf_partial != NULL)
266 {
267 ectx->ec_outer_stack = ufunc->uf_partial->pt_ectx_stack;
268 ectx->ec_outer_frame = ufunc->uf_partial->pt_ectx_frame;
269 }
270
263 // Set execution state to the start of the called function. 271 // Set execution state to the start of the called function.
264 ectx->ec_dfunc_idx = cdf_idx; 272 ectx->ec_dfunc_idx = cdf_idx;
265 ectx->ec_instr = dfunc->df_instr; 273 ectx->ec_instr = dfunc->df_instr;
266 entry = estack_push_ufunc(dfunc->df_ufunc, 1); 274 entry = estack_push_ufunc(dfunc->df_ufunc, 1);
267 if (entry != NULL) 275 if (entry != NULL)
616 return FAIL; 624 return FAIL;
617 } 625 }
618 626
619 // The function has been compiled, can call it quickly. For a function 627 // The function has been compiled, can call it quickly. For a function
620 // that was defined later: we can call it directly next time. 628 // that was defined later: we can call it directly next time.
629 // TODO: what if the function was deleted and then defined again?
621 if (iptr != NULL) 630 if (iptr != NULL)
622 { 631 {
623 delete_instr(iptr); 632 delete_instr(iptr);
624 iptr->isn_type = ISN_DCALL; 633 iptr->isn_type = ISN_DCALL;
625 iptr->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx; 634 iptr->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
888 897
889 /* 898 /*
890 * When a function reference is used, fill a partial with the information 899 * When a function reference is used, fill a partial with the information
891 * needed, especially when it is used as a closure. 900 * needed, especially when it is used as a closure.
892 */ 901 */
893 static int 902 int
894 fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx) 903 fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
895 { 904 {
896 pt->pt_func = ufunc; 905 pt->pt_func = ufunc;
897 pt->pt_refcount = 1; 906 pt->pt_refcount = 1;
898 907
2118 2127
2119 // Create a global function from a lambda. 2128 // Create a global function from a lambda.
2120 case ISN_NEWFUNC: 2129 case ISN_NEWFUNC:
2121 { 2130 {
2122 newfunc_T *newfunc = &iptr->isn_arg.newfunc; 2131 newfunc_T *newfunc = &iptr->isn_arg.newfunc;
2123 ufunc_T *new_ufunc; 2132
2124 2133 if (copy_func(newfunc->nf_lambda, newfunc->nf_global,
2125 new_ufunc = copy_func(
2126 newfunc->nf_lambda, newfunc->nf_global);
2127 if (new_ufunc != NULL
2128 && (new_ufunc->uf_flags & FC_CLOSURE))
2129 {
2130 partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
2131
2132 // Need to create a partial to store the context of the
2133 // function.
2134 if (pt == NULL)
2135 goto failed;
2136 if (fill_partial_and_closure(pt, new_ufunc,
2137 &ectx) == FAIL) 2134 &ectx) == FAIL)
2138 goto failed; 2135 goto failed;
2139 new_ufunc->uf_partial = pt;
2140 --pt->pt_refcount; // not referenced here
2141 }
2142 } 2136 }
2143 break; 2137 break;
2144 2138
2145 // List functions 2139 // List functions
2146 case ISN_DEF: 2140 case ISN_DEF: