Mercurial > vim
diff 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 |
line wrap: on
line diff
--- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -120,11 +120,13 @@ init_instr_idx(ufunc_T *ufunc, int argco * - reserved space for local variables */ static int -call_dfunc(int cdf_idx, int argcount, ectx_T *ectx) +call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx) { + int argcount = argcount_arg; dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + cdf_idx; ufunc_T *ufunc = dfunc->df_ufunc; - int optcount = ufunc_argcount(ufunc) - argcount; + int arg_to_add; + int vararg_count = 0; int idx; if (dfunc->df_deleted) @@ -133,18 +135,72 @@ call_dfunc(int cdf_idx, int argcount, ec return FAIL; } - if (ga_grow(&ectx->ec_stack, optcount + 3 + dfunc->df_varcount) == FAIL) + if (ufunc->uf_va_name != NULL) + { + int iidx; + isn_T *iptr; + + // Need to make a list out of the vararg arguments. There is a + // ISN_NEWLIST instruction at the start of the function body, we need + // to move the arguments below the stack frame and pass the count. + // Stack at time of call with 2 varargs: + // normal_arg + // optional_arg + // vararg_1 + // vararg_2 + // When starting execution: + // normal_arg + // optional_arg + // space list of varargs + // STACK_FRAME + // [local variables] + // vararg_1 + // vararg_2 + // TODO: This doesn't work if the same function is used for a default + // argument value. Forbid that somehow? + vararg_count = argcount - ufunc->uf_args.ga_len; + if (vararg_count < 0) + vararg_count = 0; + else + argcount -= vararg_count; + if (ufunc->uf_def_arg_idx == NULL) + iidx = 0; + else + iidx = ufunc->uf_def_arg_idx[ufunc->uf_def_args.ga_len]; + iptr = &dfunc->df_instr[iidx]; + if (iptr->isn_type != ISN_NEWLIST) + { + iemsg("Not a ISN_NEWLIST instruction"); + return FAIL; + } + iptr->isn_arg.number = vararg_count; + } + + arg_to_add = ufunc_argcount(ufunc) - argcount; + if (arg_to_add < 0) + { + iemsg("Argument count wrong?"); + return FAIL; + } + if (ga_grow(&ectx->ec_stack, arg_to_add + 3 + dfunc->df_varcount) == FAIL) return FAIL; - if (optcount < 0) + if (vararg_count > 0) { - emsg("argument count wrong?"); - return FAIL; + int stack_added = arg_to_add + STACK_FRAME_SIZE + dfunc->df_varcount; + + // Move the varargs to below the stack frame. + // TODO: use mch_memmove() + for (idx = 1; idx <= vararg_count; ++idx) + *STACK_TV_BOT(stack_added - idx) = *STACK_TV_BOT(-idx); + ectx->ec_stack.ga_len -= vararg_count; } // Reserve space for omitted optional arguments, filled in soon. - // Also any empty varargs argument. - ectx->ec_stack.ga_len += optcount; + // Also room for a list of varargs, if there is one. + for (idx = 0; idx < arg_to_add; ++idx) + STACK_TV_BOT(idx)->v_type = VAR_UNKNOWN; + ectx->ec_stack.ga_len += arg_to_add; // Store current execution state in stack frame for ISN_RETURN. // TODO: If the actual number of arguments doesn't match what the called @@ -157,7 +213,8 @@ call_dfunc(int cdf_idx, int argcount, ec // Initialize local variables for (idx = 0; idx < dfunc->df_varcount; ++idx) STACK_TV_BOT(STACK_FRAME_SIZE + idx)->v_type = VAR_UNKNOWN; - ectx->ec_stack.ga_len += STACK_FRAME_SIZE + dfunc->df_varcount; + ectx->ec_stack.ga_len += STACK_FRAME_SIZE + dfunc->df_varcount + + vararg_count; // Set execution state to the start of the called function. ectx->ec_dfunc_idx = cdf_idx; @@ -430,7 +487,7 @@ call_def_function( #undef STACK_TV_BOT #define STACK_TV_BOT(idx) (((typval_T *)ectx.ec_stack.ga_data) + ectx.ec_stack.ga_len + idx) -// Get pointer to local variable on the stack. +// Get pointer to a local variable on the stack. Negative for arguments. #define STACK_TV_VAR(idx) (((typval_T *)ectx.ec_stack.ga_data) + ectx.ec_frame + STACK_FRAME_SIZE + idx) vim_memset(&ectx, 0, sizeof(ectx));