# HG changeset patch # User Bram Moolenaar # Date 1594323003 -7200 # Node ID 3f14e0d4a4dd08d91b5ca9e0410d8a1ed8b30ee2 # Parent fcc215b37837d20a68d9c168f96e1a3b1f248c82 patch 8.2.1167: Vim9: builtin function method call only supports first arg Commit: https://github.com/vim/vim/commit/389df259c49d1ca4f7aa129b702f6083985b1e73 Author: Bram Moolenaar Date: Thu Jul 9 21:20:47 2020 +0200 patch 8.2.1167: Vim9: builtin function method call only supports first arg Problem: Vim9: builtin function method call only supports first argument. Solution: Shift arguments when needed. (closes https://github.com/vim/vim/issues/6305, closes https://github.com/vim/vim/issues/6419) diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -465,8 +465,8 @@ static funcentry_T global_functions[] = {"acos", 1, 1, FEARG_1, ret_float, FLOAT_FUNC(f_acos)}, {"add", 2, 2, FEARG_1, ret_first_arg, f_add}, {"and", 2, 2, FEARG_1, ret_number, f_and}, - {"append", 2, 2, FEARG_LAST, ret_number, f_append}, - {"appendbufline", 3, 3, FEARG_LAST, ret_number, f_appendbufline}, + {"append", 2, 2, FEARG_2, ret_number, f_append}, + {"appendbufline", 3, 3, FEARG_2, ret_number, f_appendbufline}, {"argc", 0, 1, 0, ret_number, f_argc}, {"argidx", 0, 0, 0, ret_number, f_argidx}, {"arglistid", 0, 2, 0, ret_number, f_arglistid}, @@ -1191,7 +1191,9 @@ internal_func_ret_type(int idx, int argc /* * Check the argument count to use for internal function "idx". - * Returns OK or FAIL; + * Returns -1 for failure, 0 if no method base accepted, 1 if method base is + * first argument, 2 if method base is second argument, etc. 9 if method base + * is last argument. */ int check_internal_func(int idx, int argcount) @@ -1204,14 +1206,14 @@ check_internal_func(int idx, int argcoun else if (argcount > global_functions[idx].f_max_argc) res = FCERR_TOOMANY; else - return OK; + return global_functions[idx].f_argtype; name = internal_func_name(idx); if (res == FCERR_TOOMANY) semsg(_(e_toomanyarg), name); else semsg(_(e_toofewarg), name); - return FAIL; + return -1; } int diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -1278,4 +1278,22 @@ def Test_simplify_const_expr() res) enddef +def s:CallAppend() + eval "some text"->append(2) +enddef + +def Test_shuffle() + let res = execute('disass s:CallAppend') + assert_match('\d*_CallAppend\_s*' .. + 'eval "some text"->append(2)\_s*' .. + '\d PUSHS "some text"\_s*' .. + '\d PUSHNR 2\_s*' .. + '\d SHUFFLE 2 up 1\_s*' .. + '\d BCALL append(argc 2)\_s*' .. + '\d DROP\_s*' .. + '\d PUSHNR 0\_s*' .. + '\d RETURN', + res) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1411,6 +1411,28 @@ def Test_expr7_subscript_linebreak() one) enddef +def Test_expr7_method_call() + new + setline(1, ['first', 'last']) + eval 'second'->append(1) + assert_equal(['first', 'second', 'last'], getline(1, '$')) + bwipe! + + let bufnr = bufnr() + let loclist = [#{bufnr: bufnr, lnum: 42, col: 17, text: 'wrong'}] + loclist->setloclist(0) + assert_equal([#{bufnr: bufnr, + lnum: 42, + col: 17, + text: 'wrong', + pattern: '', + valid: 1, + vcol: 0, + nr: 0, + type: '', + module: ''} + ], getloclist(0)) +enddef func Test_expr7_trailing_fails() call CheckDefFailure(['let l = [2]', 'l->{l -> add(l, 8)}'], 'E107') diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1167, +/**/ 1166, /**/ 1165, diff --git a/src/vim9.h b/src/vim9.h --- a/src/vim9.h +++ b/src/vim9.h @@ -124,6 +124,7 @@ typedef enum { ISN_CHECKTYPE, // check value type is isn_arg.type.tc_type ISN_CHECKLEN, // check list length is isn_arg.checklen.cl_min_len + ISN_SHUFFLE, // move item on stack up or down ISN_DROP // pop stack and discard value } isntype_T; @@ -237,6 +238,12 @@ typedef struct { int cl_more_OK; // longer is allowed } checklen_T; +// arguments to ISN_SHUFFLE +typedef struct { + int shfl_item; // item to move (relative to top of stack) + int shfl_up; // places to move upwards +} shuffle_T; + /* * Instruction */ @@ -270,6 +277,7 @@ struct isn_S { unlet_T unlet; funcref_T funcref; checklen_T checklen; + shuffle_T shuffle; } isn_arg; }; diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1445,20 +1445,31 @@ generate_FOR(cctx_T *cctx, int loop_idx) /* * Generate an ISN_BCALL instruction. + * "method_call" is TRUE for "value->method()" * Return FAIL if the number of arguments is wrong. */ static int -generate_BCALL(cctx_T *cctx, int func_idx, int argcount) +generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call) { isn_T *isn; garray_T *stack = &cctx->ctx_type_stack; + int argoff; type_T *argtypes[MAX_FUNC_ARGS]; int i; RETURN_OK_IF_SKIP(cctx); - if (check_internal_func(func_idx, argcount) == FAIL) + argoff = check_internal_func(func_idx, argcount); + if (argoff < 0) return FAIL; + if (method_call && argoff > 1) + { + if ((isn = generate_instr(cctx, ISN_SHUFFLE)) == NULL) + return FAIL; + isn->isn_arg.shuffle.shfl_item = argcount; + isn->isn_arg.shuffle.shfl_up = argoff - 1; + } + if ((isn = generate_instr(cctx, ISN_BCALL)) == NULL) return FAIL; isn->isn_arg.bfunc.cbf_idx = func_idx; @@ -2930,7 +2941,7 @@ compile_call( // builtin function idx = find_internal_func(name); if (idx >= 0) - res = generate_BCALL(cctx, idx, argcount); + res = generate_BCALL(cctx, idx, argcount, argcount_init == 1); else semsg(_(e_unknownfunc), namebuf); goto theend; @@ -7397,6 +7408,7 @@ delete_instr(isn_T *isn) case ISN_COMPARESTRING: case ISN_CONCAT: case ISN_DCALL: + case ISN_SHUFFLE: case ISN_DROP: case ISN_ECHO: case ISN_ECHOERR: diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -554,7 +554,7 @@ call_by_name(char_u *name, int argcount, if (func_idx < 0) return FAIL; - if (check_internal_func(func_idx, argcount) == FAIL) + if (check_internal_func(func_idx, argcount) < 0) return FAIL; return call_bfunc(func_idx, argcount, ectx); } @@ -2333,6 +2333,22 @@ call_def_function( } break; + case ISN_SHUFFLE: + { + typval_T tmp_tv; + int item = iptr->isn_arg.shuffle.shfl_item; + int up = iptr->isn_arg.shuffle.shfl_up; + + tmp_tv = *STACK_TV_BOT(-item); + for ( ; up > 0 && item > 1; --up) + { + *STACK_TV_BOT(-item) = *STACK_TV_BOT(-item + 1); + --item; + } + *STACK_TV_BOT(-item) = tmp_tv; + } + break; + case ISN_DROP: --ectx.ec_stack.ga_len; clear_tv(STACK_TV_BOT(0)); @@ -2900,8 +2916,12 @@ ex_disassemble(exarg_T *eap) break; case ISN_2STRING: smsg("%4d 2STRING stack[%lld]", current, (long long)(iptr->isn_arg.number)); - break; + break; + case ISN_SHUFFLE: smsg("%4d SHUFFLE %d up %d", current, + iptr->isn_arg.shuffle.shfl_item, + iptr->isn_arg.shuffle.shfl_up); + break; case ISN_DROP: smsg("%4d DROP", current); break; } }