summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-07-09 21:20:47 +0200
committerBram Moolenaar <Bram@vim.org>2020-07-09 21:20:47 +0200
commit389df259c49d1ca4f7aa129b702f6083985b1e73 (patch)
tree8c9d44999305e3660ccf09dbd2d4b7aa935744a9
parentae97b94176062d30ea8c68bb83cde034c5150c78 (diff)
patch 8.2.1167: Vim9: builtin function method call only supports first argv8.2.1167
Problem: Vim9: builtin function method call only supports first argument. Solution: Shift arguments when needed. (closes #6305, closes #6419)
-rw-r--r--src/evalfunc.c12
-rw-r--r--src/testdir/test_vim9_disassemble.vim18
-rw-r--r--src/testdir/test_vim9_expr.vim22
-rw-r--r--src/version.c2
-rw-r--r--src/vim9.h8
-rw-r--r--src/vim9compile.c18
-rw-r--r--src/vim9execute.c24
7 files changed, 94 insertions, 10 deletions
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 0a100748d9..86a676ca86 100644
--- 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 argcount, type_T **argtypes)
/*
* 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 argcount)
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
index 27d16a5802..a63779a7e8 100644
--- 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
diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim
index b4e0604943..83b7692090 100644
--- 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
index 9fcf578ffc..c607f4eded 100644
--- 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
index 259bdb9110..10f983ca97 100644
--- 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
index 941309ffbd..93ab3c8c05 100644
--- 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
index a3cea806ee..544c4226b2 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -554,7 +554,7 @@ call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
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;
}
}