]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.2.1167: Vim9: builtin function method call only supports first arg v8.2.1167
authorBram Moolenaar <Bram@vim.org>
Thu, 9 Jul 2020 19:20:47 +0000 (21:20 +0200)
committerBram Moolenaar <Bram@vim.org>
Thu, 9 Jul 2020 19:20:47 +0000 (21:20 +0200)
Problem:    Vim9: builtin function method call only supports first argument.
Solution:   Shift arguments when needed. (closes #6305, closes #6419)

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

index 0a100748d921f546dbc5db1114ee63cb1845e465..86a676ca864c43f321b2ca27da4747f625174636 100644 (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
index 27d16a5802e904025254e1f2be52ade089af4e4c..a63779a7e89d7f12296d2afdcf387fed637de605 100644 (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
index b4e060494307d320741495d631dea18b535e1b31..83b7692090ac5db0d0dd347170a7ba86c03dd0cd 100644 (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')
index 9fcf578ffc0bf20eb9cd6b7ed57e575398bd20a9..c607f4ededf6138569167761443147ec11dc9085 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1167,
 /**/
     1166,
 /**/
index 259bdb91104dd6de50089c310370add1baff6259..10f983ca976de33962986e1f1a0c2ea058e237e6 100644 (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;
 };
 
index 941309ffbd2c0c96b15025b0b33db47ae6b35fba..93ab3c8c05b128c935a9d7546e7cc06ce255585e 100644 (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:
index a3cea806ee31a668c6a142ed89dd3661fff6d0d0..544c4226b2a91e31c33f965bb7ae1e5f849f83d2 100644 (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));
@@ -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;
        }
     }