Mercurial > vim
comparison src/vim9execute.c @ 19975:4e8e0ce576af v8.2.0543
patch 8.2.0543: Vim9: function with varargs does not work properly
Commit: https://github.com/vim/vim/commit/1378fbc4591b77186c90beda37bdac628add4cb6
Author: Bram Moolenaar <Bram@vim.org>
Date: Sat Apr 11 20:50:33 2020 +0200
patch 8.2.0543: Vim9: function with varargs does not work properly
Problem: Vim9: function with varargs does not work properly.
Solution: Improve function type spec and add tests. Fix bugs.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sat, 11 Apr 2020 21:00:04 +0200 |
parents | 6765a88e72a5 |
children | 014daa59ba50 |
comparison
equal
deleted
inserted
replaced
19974:7b71d96d6582 | 19975:4e8e0ce576af |
---|---|
118 * - Index of next instruction in calling function | 118 * - Index of next instruction in calling function |
119 * - previous frame pointer | 119 * - previous frame pointer |
120 * - reserved space for local variables | 120 * - reserved space for local variables |
121 */ | 121 */ |
122 static int | 122 static int |
123 call_dfunc(int cdf_idx, int argcount, ectx_T *ectx) | 123 call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx) |
124 { | 124 { |
125 int argcount = argcount_arg; | |
125 dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + cdf_idx; | 126 dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + cdf_idx; |
126 ufunc_T *ufunc = dfunc->df_ufunc; | 127 ufunc_T *ufunc = dfunc->df_ufunc; |
127 int optcount = ufunc_argcount(ufunc) - argcount; | 128 int arg_to_add; |
129 int vararg_count = 0; | |
128 int idx; | 130 int idx; |
129 | 131 |
130 if (dfunc->df_deleted) | 132 if (dfunc->df_deleted) |
131 { | 133 { |
132 emsg_funcname(e_func_deleted, ufunc->uf_name); | 134 emsg_funcname(e_func_deleted, ufunc->uf_name); |
133 return FAIL; | 135 return FAIL; |
134 } | 136 } |
135 | 137 |
136 if (ga_grow(&ectx->ec_stack, optcount + 3 + dfunc->df_varcount) == FAIL) | 138 if (ufunc->uf_va_name != NULL) |
137 return FAIL; | |
138 | |
139 if (optcount < 0) | |
140 { | 139 { |
141 emsg("argument count wrong?"); | 140 int iidx; |
141 isn_T *iptr; | |
142 | |
143 // Need to make a list out of the vararg arguments. There is a | |
144 // ISN_NEWLIST instruction at the start of the function body, we need | |
145 // to move the arguments below the stack frame and pass the count. | |
146 // Stack at time of call with 2 varargs: | |
147 // normal_arg | |
148 // optional_arg | |
149 // vararg_1 | |
150 // vararg_2 | |
151 // When starting execution: | |
152 // normal_arg | |
153 // optional_arg | |
154 // space list of varargs | |
155 // STACK_FRAME | |
156 // [local variables] | |
157 // vararg_1 | |
158 // vararg_2 | |
159 // TODO: This doesn't work if the same function is used for a default | |
160 // argument value. Forbid that somehow? | |
161 vararg_count = argcount - ufunc->uf_args.ga_len; | |
162 if (vararg_count < 0) | |
163 vararg_count = 0; | |
164 else | |
165 argcount -= vararg_count; | |
166 if (ufunc->uf_def_arg_idx == NULL) | |
167 iidx = 0; | |
168 else | |
169 iidx = ufunc->uf_def_arg_idx[ufunc->uf_def_args.ga_len]; | |
170 iptr = &dfunc->df_instr[iidx]; | |
171 if (iptr->isn_type != ISN_NEWLIST) | |
172 { | |
173 iemsg("Not a ISN_NEWLIST instruction"); | |
174 return FAIL; | |
175 } | |
176 iptr->isn_arg.number = vararg_count; | |
177 } | |
178 | |
179 arg_to_add = ufunc_argcount(ufunc) - argcount; | |
180 if (arg_to_add < 0) | |
181 { | |
182 iemsg("Argument count wrong?"); | |
142 return FAIL; | 183 return FAIL; |
143 } | 184 } |
185 if (ga_grow(&ectx->ec_stack, arg_to_add + 3 + dfunc->df_varcount) == FAIL) | |
186 return FAIL; | |
187 | |
188 if (vararg_count > 0) | |
189 { | |
190 int stack_added = arg_to_add + STACK_FRAME_SIZE + dfunc->df_varcount; | |
191 | |
192 // Move the varargs to below the stack frame. | |
193 // TODO: use mch_memmove() | |
194 for (idx = 1; idx <= vararg_count; ++idx) | |
195 *STACK_TV_BOT(stack_added - idx) = *STACK_TV_BOT(-idx); | |
196 ectx->ec_stack.ga_len -= vararg_count; | |
197 } | |
144 | 198 |
145 // Reserve space for omitted optional arguments, filled in soon. | 199 // Reserve space for omitted optional arguments, filled in soon. |
146 // Also any empty varargs argument. | 200 // Also room for a list of varargs, if there is one. |
147 ectx->ec_stack.ga_len += optcount; | 201 for (idx = 0; idx < arg_to_add; ++idx) |
202 STACK_TV_BOT(idx)->v_type = VAR_UNKNOWN; | |
203 ectx->ec_stack.ga_len += arg_to_add; | |
148 | 204 |
149 // Store current execution state in stack frame for ISN_RETURN. | 205 // Store current execution state in stack frame for ISN_RETURN. |
150 // TODO: If the actual number of arguments doesn't match what the called | 206 // TODO: If the actual number of arguments doesn't match what the called |
151 // function expects things go bad. | 207 // function expects things go bad. |
152 STACK_TV_BOT(0)->vval.v_number = ectx->ec_dfunc_idx; | 208 STACK_TV_BOT(0)->vval.v_number = ectx->ec_dfunc_idx; |
155 ectx->ec_frame = ectx->ec_stack.ga_len; | 211 ectx->ec_frame = ectx->ec_stack.ga_len; |
156 | 212 |
157 // Initialize local variables | 213 // Initialize local variables |
158 for (idx = 0; idx < dfunc->df_varcount; ++idx) | 214 for (idx = 0; idx < dfunc->df_varcount; ++idx) |
159 STACK_TV_BOT(STACK_FRAME_SIZE + idx)->v_type = VAR_UNKNOWN; | 215 STACK_TV_BOT(STACK_FRAME_SIZE + idx)->v_type = VAR_UNKNOWN; |
160 ectx->ec_stack.ga_len += STACK_FRAME_SIZE + dfunc->df_varcount; | 216 ectx->ec_stack.ga_len += STACK_FRAME_SIZE + dfunc->df_varcount |
217 + vararg_count; | |
161 | 218 |
162 // Set execution state to the start of the called function. | 219 // Set execution state to the start of the called function. |
163 ectx->ec_dfunc_idx = cdf_idx; | 220 ectx->ec_dfunc_idx = cdf_idx; |
164 ectx->ec_instr = dfunc->df_instr; | 221 ectx->ec_instr = dfunc->df_instr; |
165 estack_push_ufunc(ETYPE_UFUNC, dfunc->df_ufunc, 1); | 222 estack_push_ufunc(ETYPE_UFUNC, dfunc->df_ufunc, 1); |
428 | 485 |
429 // Get pointer to item at the bottom of the stack, -1 is the bottom. | 486 // Get pointer to item at the bottom of the stack, -1 is the bottom. |
430 #undef STACK_TV_BOT | 487 #undef STACK_TV_BOT |
431 #define STACK_TV_BOT(idx) (((typval_T *)ectx.ec_stack.ga_data) + ectx.ec_stack.ga_len + idx) | 488 #define STACK_TV_BOT(idx) (((typval_T *)ectx.ec_stack.ga_data) + ectx.ec_stack.ga_len + idx) |
432 | 489 |
433 // Get pointer to local variable on the stack. | 490 // Get pointer to a local variable on the stack. Negative for arguments. |
434 #define STACK_TV_VAR(idx) (((typval_T *)ectx.ec_stack.ga_data) + ectx.ec_frame + STACK_FRAME_SIZE + idx) | 491 #define STACK_TV_VAR(idx) (((typval_T *)ectx.ec_stack.ga_data) + ectx.ec_frame + STACK_FRAME_SIZE + idx) |
435 | 492 |
436 vim_memset(&ectx, 0, sizeof(ectx)); | 493 vim_memset(&ectx, 0, sizeof(ectx)); |
437 ga_init2(&ectx.ec_stack, sizeof(typval_T), 500); | 494 ga_init2(&ectx.ec_stack, sizeof(typval_T), 500); |
438 if (ga_grow(&ectx.ec_stack, 20) == FAIL) | 495 if (ga_grow(&ectx.ec_stack, 20) == FAIL) |