Mercurial > vim
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: |