]> granicus.if.org Git - vim/commitdiff
patch 8.2.0487: Vim9: compiling not sufficiently tested v8.2.0487
authorBram Moolenaar <Bram@vim.org>
Tue, 31 Mar 2020 21:13:10 +0000 (23:13 +0200)
committerBram Moolenaar <Bram@vim.org>
Tue, 31 Mar 2020 21:13:10 +0000 (23:13 +0200)
Problem:    Vim9: compiling not sufficiently tested.
Solution:   Add more tests.  Fix bug with PCALL.

src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_script.vim
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c

index 99bec4337b0f3e37e8546bb71f45b8251be756d6..62602956bad6abaea4582e6e2e434617adf04442 100644 (file)
@@ -224,6 +224,31 @@ def Test_disassemble_call()
 enddef
 
 
+def EchoArg(arg: string): string
+  return arg
+enddef
+def RefThis(): func
+  return function('EchoArg')
+enddef
+def s:ScriptPCall()
+  RefThis()("text")
+enddef
+
+def Test_disassemble_pcall()
+  let res = execute('disass s:ScriptPCall')
+  assert_match('<SNR>\d\+_ScriptPCall.*'
+        \ .. 'RefThis()("text").*'
+        \ .. '\d DCALL RefThis(argc 0).*'
+        \ .. '\d PUSHS "text".*'
+        \ .. '\d PCALL top (argc 1).*'
+        \ .. '\d PCALL end.*'
+        \ .. '\d DROP.*'
+        \ .. '\d PUSHNR 0.*'
+        \ .. '\d RETURN.*'
+        \, res)
+enddef
+
+
 def FuncWithForwardCall(): string
   return DefinedLater("yes")
 enddef
index f6cb15fd4df0643181fbeca72df3e31075c0fa24..19df73c5ef28ee966823266acf7572ba40d897cb 100644 (file)
@@ -260,9 +260,10 @@ func TakesOneArg(arg)
   echo a:arg
 endfunc
 
-def Test_call_wrong_arg_count()
+def Test_call_wrong_args()
   call CheckDefFailure(['TakesOneArg()'], 'E119:')
   call CheckDefFailure(['TakesOneArg(11, 22)'], 'E118:')
+  call CheckDefFailure(['bufnr(xxx)'], 'E1001:')
 enddef
 
 " Default arg and varargs
@@ -1029,6 +1030,14 @@ def Test_while_loop()
   assert_equal('1_3_', result)
 enddef
 
+def Test_for_loop_fails()
+  call CheckDefFailure(['for # in range(5)'], 'E690:')
+  call CheckDefFailure(['for i In range(5)'], 'E690:')
+  call CheckDefFailure(['let x = 5', 'for x in range(5)'], 'E1023:')
+  call CheckScriptFailure(['def Func(arg)', 'for arg in range(5)', 'enddef'], 'E1006:')
+  call CheckDefFailure(['for i in "text"'], 'E1024:')
+enddef
+
 def Test_interrupt_loop()
   let caught = false
   let x = 0
index 17ef596544336c0f6e4042ff30017823d386c8b2..549bf3bed490b7ba4bfeb10ade76843d893bb8df 100644 (file)
@@ -738,6 +738,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    487,
 /**/
     486,
 /**/
index ffa09a727d473cdcdd51310b734b685da9d0d453..7b95c377ef1e66d5185da916fc1f61b3e8183a93 100644 (file)
@@ -57,6 +57,7 @@ typedef enum {
     ISN_DCALL,     // call def function isn_arg.dfunc
     ISN_UCALL,     // call user function or funcref/partial isn_arg.ufunc
     ISN_PCALL,     // call partial, use isn_arg.pfunc
+    ISN_PCALL_END,  // cleanup after ISN_PCALL with cpf_top set
     ISN_RETURN,            // return, result is on top of stack
     ISN_FUNCREF,    // push a function ref to dfunc isn_arg.number
 
@@ -256,7 +257,7 @@ struct dfunc_S {
 // Functions defined with :def are stored in this growarray.
 // They are never removed, so that they can be found by index.
 // Deleted functions have the df_deleted flag set.
-garray_T def_functions = {0, 0, sizeof(dfunc_T), 50, NULL};
+garray_T def_functions = {0, 0, sizeof(dfunc_T), 200, NULL};
 #else
 extern garray_T def_functions;
 #endif
index 9fdfdb3b64ee175dcaf4638da83909245360b5f5..c7f50326444c3a4a78ab6294d432812955314448 100644 (file)
@@ -1196,6 +1196,11 @@ generate_PCALL(cctx_T *cctx, int argcount, int at_top)
     // drop the funcref/partial, get back the return value
     ((type_T **)stack->ga_data)[stack->ga_len - 1] = &t_any;
 
+    // If partial is above the arguments it must be cleared and replaced with
+    // the return value.
+    if (at_top && generate_instr(cctx, ISN_PCALL_END) == NULL)
+       return FAIL;
+
     return OK;
 }
 
@@ -5200,7 +5205,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type)
            p = (*ea.cmd == '&' || *ea.cmd == '$' || *ea.cmd == '@')
                                                         ? ea.cmd + 1 : ea.cmd;
            p = to_name_end(p, TRUE);
-           if ((p > ea.cmd && *p != NUL) || *p == '(')
+           if (p > ea.cmd && *p != NUL)
            {
                int oplen;
                int heredoc;
@@ -5538,6 +5543,7 @@ delete_instr(isn_T *isn)
        case ISN_OPFLOAT:
        case ISN_OPANY:
        case ISN_PCALL:
+       case ISN_PCALL_END:
        case ISN_PUSHF:
        case ISN_PUSHNR:
        case ISN_PUSHBOOL:
index 4846c5521171ca85e04bb196cc063ce7a8d56cce..c082384c605dc59fc259ae581fba457d72635aa9 100644 (file)
@@ -345,7 +345,7 @@ call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
     static int
 call_partial(typval_T *tv, int argcount, ectx_T *ectx)
 {
-    char_u     *name;
+    char_u     *name = NULL;
     int                called_emsg_before = called_emsg;
 
     if (tv->v_type == VAR_PARTIAL)
@@ -356,9 +356,9 @@ call_partial(typval_T *tv, int argcount, ectx_T *ectx)
            return call_ufunc(pt->pt_func, argcount, ectx, NULL);
        name = pt->pt_name;
     }
-    else
+    else if (tv->v_type == VAR_FUNC)
        name = tv->vval.v_string;
-    if (call_by_name(name, argcount, ectx, NULL) == FAIL)
+    if (name == NULL || call_by_name(name, argcount, ectx, NULL) == FAIL)
     {
        if (called_emsg == called_emsg_before)
            semsg(_(e_unknownfunc), name);
@@ -421,7 +421,6 @@ call_def_function(
     typval_T   *tv;
     int                idx;
     int                ret = FAIL;
-    dfunc_T    *dfunc;
     int                defcount = ufunc->uf_args.ga_len - argc;
 
 // Get pointer to item in the stack.
@@ -467,13 +466,17 @@ call_def_function(
        ++ectx.ec_stack.ga_len;
     }
 
-    // Reserve space for local variables.
-    dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx;
-    for (idx = 0; idx < dfunc->df_varcount; ++idx)
-       STACK_TV_VAR(idx)->v_type = VAR_UNKNOWN;
-    ectx.ec_stack.ga_len += dfunc->df_varcount;
+    {
+       // Reserve space for local variables.
+       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+                                                        + ufunc->uf_dfunc_idx;
 
-    ectx.ec_instr = dfunc->df_instr;
+       for (idx = 0; idx < dfunc->df_varcount; ++idx)
+           STACK_TV_VAR(idx)->v_type = VAR_UNKNOWN;
+       ectx.ec_stack.ga_len += dfunc->df_varcount;
+
+       ectx.ec_instr = dfunc->df_instr;
+    }
 
     // Decide where to start execution, handles optional arguments.
     init_instr_idx(ufunc, argc, &ectx);
@@ -1022,18 +1025,18 @@ call_def_function(
                        clear_tv(&partial);
                    if (r == FAIL)
                        goto failed;
-
-                   if (pfunc->cpf_top)
-                   {
-                       // Get the funcref from the stack, overwrite with the
-                       // return value.
-                       clear_tv(tv);
-                       --ectx.ec_stack.ga_len;
-                       *STACK_TV_BOT(-1) = *STACK_TV_BOT(0);
-                   }
                }
                break;
 
+           case ISN_PCALL_END:
+               // PCALL finished, arguments have been consumed and replaced by
+               // the return value.  Now clear the funcref from the stack,
+               // and move the return value in its place.
+               --ectx.ec_stack.ga_len;
+               clear_tv(STACK_TV_BOT(-1));
+               *STACK_TV_BOT(-1) = *STACK_TV_BOT(0);
+               break;
+
            // call a user defined function or funcref/partial
            case ISN_UCALL:
                {
@@ -1078,6 +1081,7 @@ call_def_function(
            case ISN_FUNCREF:
                {
                    partial_T   *pt = NULL;
+                   dfunc_T     *dfunc;
 
                    pt = ALLOC_CLEAR_ONE(partial_T);
                    if (pt == NULL)
@@ -2005,6 +2009,9 @@ ex_disassemble(exarg_T *eap)
                           cpfunc->cpf_top ? " top" : "", cpfunc->cpf_argcount);
                }
                break;
+           case ISN_PCALL_END:
+               smsg("%4d PCALL end", current);
+               break;
            case ISN_RETURN:
                smsg("%4d RETURN", current);
                break;