1
0
forked from aniani/vim

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 #6305, closes #6419)
This commit is contained in:
Bram Moolenaar
2020-07-09 21:20:47 +02:00
parent ae97b94176
commit 389df259c4
7 changed files with 94 additions and 10 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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')

View File

@@ -754,6 +754,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1167,
/**/
1166,
/**/

View File

@@ -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;
};

View File

@@ -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:

View File

@@ -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));
@@ -2902,6 +2918,10 @@ ex_disassemble(exarg_T *eap)
(long long)(iptr->isn_arg.number));
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;
}
}