changeset 21232:3f14e0d4a4dd v8.2.1167

patch 8.2.1167: Vim9: builtin function method call only supports first arg Commit: https://github.com/vim/vim/commit/389df259c49d1ca4f7aa129b702f6083985b1e73 Author: Bram Moolenaar <Bram@vim.org> 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)
author Bram Moolenaar <Bram@vim.org>
date Thu, 09 Jul 2020 21:30:03 +0200
parents fcc215b37837
children 7f90d64e0982
files src/evalfunc.c src/testdir/test_vim9_disassemble.vim src/testdir/test_vim9_expr.vim src/version.c src/vim9.h src/vim9compile.c src/vim9execute.c
diffstat 7 files changed, 94 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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('<SNR>\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
--- 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')
--- 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,
--- 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;
 };
 
--- 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:
--- 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;
 	}
     }