]> granicus.if.org Git - vim/commitdiff
patch 8.2.1865: Vim9: add() does not check type of argument v8.2.1865
authorBram Moolenaar <Bram@vim.org>
Mon, 19 Oct 2020 17:02:42 +0000 (19:02 +0200)
committerBram Moolenaar <Bram@vim.org>
Mon, 19 Oct 2020 17:02:42 +0000 (19:02 +0200)
Problem:    Vim9: add() does not check type of argument.
Solution:   Inline the add() call. (closes #7160)

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

index 664bcd93258f8c7ca2bf931b558d01f7a5b748ca..8ddf4415732466be3082c73f45597f4d8248ef3c 100644 (file)
@@ -282,4 +282,6 @@ EXTERN char e_endblock_without_block[]
        INIT(= N_("E1128: } without {"));
 EXTERN char e_throw_with_empty_string[]
        INIT(= N_("E1129: Throw with empty string"));
+EXTERN char e_cannot_add_to_null_list[]
+       INIT(= N_("E1130: Cannot add to null list"));
 #endif
index 53bc4bad7002b396017053f11da0a7679acb8df4..fe1ce66ec5b3c71c82fb875d951333e704a1f1fa 100644 (file)
@@ -273,6 +273,34 @@ def Test_disassemble_list_assign()
         res)
 enddef
 
+def s:ListAdd()
+  var l: list<number> = []
+  add(l, 123)
+  add(l, g:aNumber)
+enddef
+
+def Test_disassemble_list_add()
+  var res = execute('disass s:ListAdd')
+  assert_match('<SNR>\d*_ListAdd\_s*' ..
+        'var l: list<number> = []\_s*' ..
+        '\d NEWLIST size 0\_s*' ..
+        '\d STORE $0\_s*' ..
+        'add(l, 123)\_s*' ..
+        '\d LOAD $0\_s*' ..
+        '\d PUSHNR 123\_s*' ..
+        '\d LISTAPPEND\_s*' ..
+        '\d DROP\_s*' ..
+        'add(l, g:aNumber)\_s*' ..
+        '\d LOAD $0\_s*' ..
+        '\d\+ LOADG g:aNumber\_s*' ..
+        '\d\+ CHECKTYPE number stack\[-1\]\_s*' ..
+        '\d\+ LISTAPPEND\_s*' ..
+        '\d\+ DROP\_s*' ..
+        '\d\+ PUSHNR 0\_s*' ..
+        '\d\+ RETURN',
+        res)
+enddef
+
 def s:ScriptFuncUnlet()
   g:somevar = "value"
   unlet g:somevar
@@ -803,7 +831,7 @@ def Test_disassemble_for_loop()
         'res->add(i)\_s*' ..
         '\d LOAD $0\_s*' ..
         '\d LOAD $2\_s*' ..
-        '\d\+ BCALL add(argc 2)\_s*' ..
+        '\d\+ LISTAPPEND\_s*' ..
         '\d\+ DROP\_s*' ..
         'endfor\_s*' ..
         '\d\+ JUMP -> \d\+\_s*' ..
index cc6af6a86a789c9ac7c242644566cd759f5b6d1e..bded91f78406ceeb3c39df59bc53a7524243dee1 100644 (file)
@@ -1772,6 +1772,24 @@ def Test_list2str_str2list_utf8()
   list2str(l, true)->assert_equal(s)
 enddef
 
+def Test_list_add()
+  var l: list<number>  # defaults to empty list
+  add(l, 9)
+  assert_equal([9], l)
+
+  var lines =<< trim END
+      var l: list<number>
+      add(l, "x")
+  END
+  CheckDefFailure(lines, 'E1012:', 2)
+
+  lines =<< trim END
+      var l: list<number> = test_null_list()
+      add(l, 123)
+  END
+  CheckDefExecFailure(lines, 'E1130:', 2)
+enddef
+
 def SID(): number
   return expand('<SID>')
           ->matchstr('<SNR>\zs\d\+\ze_$')
index ef5ea876f572bb5addc1d20b39cffdb454ed6936..2894dedb8c71bc13cf263de2c4d4e275e5166adc 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1865,
 /**/
     1864,
 /**/
index 537e3abe084441eb5392bb878740b4f0a7816987..4cece6d81476f4da6e83c76f82c68e29fc52061d 100644 (file)
@@ -96,8 +96,8 @@ typedef enum {
     ISN_ENDTRY,            // take entry off from ec_trystack
 
     // more expression operations
-    ISN_ADDLIST,
-    ISN_ADDBLOB,
+    ISN_ADDLIST,    // add two lists
+    ISN_ADDBLOB,    // add two blobs
 
     // operation with two arguments; isn_arg.op.op_type is exptype_T
     ISN_OPNR,
@@ -120,6 +120,7 @@ typedef enum {
     ISN_CONCAT,
     ISN_STRINDEX,   // [expr] string index
     ISN_STRSLICE,   // [expr:expr] string slice
+    ISN_LISTAPPEND, // append to a list, like add()
     ISN_LISTINDEX,  // [expr] list index
     ISN_LISTSLICE,  // [expr:expr] list slice
     ISN_ANYINDEX,   // [expr] runtime index
index a891006f97fd4e062aaf08015f9fb4cf1cfd7cf6..bb4807eeb7e63a8a5f7ae2458a99e674e982126b 100644 (file)
@@ -1494,6 +1494,32 @@ generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call)
     return OK;
 }
 
+/*
+ * Generate an ISN_LISTAPPEND instruction.  Works like add().
+ * Argument count is already checked.
+ */
+    static int
+generate_LISTAPPEND(cctx_T *cctx)
+{
+    garray_T   *stack = &cctx->ctx_type_stack;
+    type_T     *list_type;
+    type_T     *item_type;
+    type_T     *expected;
+
+    // Caller already checked that list_type is a list.
+    list_type = ((type_T **)stack->ga_data)[stack->ga_len - 2];
+    item_type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
+    expected = list_type->tt_member;
+    if (need_type(item_type, expected, -1, cctx, FALSE, FALSE) == FAIL)
+       return FAIL;
+
+    if (generate_instr(cctx, ISN_LISTAPPEND) == NULL)
+       return FAIL;
+
+    --stack->ga_len;       // drop the argument
+    return OK;
+}
+
 /*
  * Generate an ISN_DCALL or ISN_UCALL instruction.
  * Return FAIL if the number of arguments is wrong.
@@ -2537,7 +2563,25 @@ compile_call(
        // builtin function
        idx = find_internal_func(name);
        if (idx >= 0)
-           res = generate_BCALL(cctx, idx, argcount, argcount_init == 1);
+       {
+           if (STRCMP(name, "add") == 0 && argcount == 2)
+           {
+               garray_T    *stack = &cctx->ctx_type_stack;
+               type_T      *type = ((type_T **)stack->ga_data)[
+                                                           stack->ga_len - 2];
+
+               // TODO: also check for VAR_BLOB
+               if (type->tt_type == VAR_LIST)
+               {
+                   // inline "add(list, item)" so that the type can be checked
+                   res = generate_LISTAPPEND(cctx);
+                   idx = -1;
+               }
+           }
+
+           if (idx >= 0)
+               res = generate_BCALL(cctx, idx, argcount, argcount_init == 1);
+       }
        else
            semsg(_(e_unknownfunc), namebuf);
        goto theend;
@@ -7656,6 +7700,7 @@ delete_instr(isn_T *isn)
        case ISN_FOR:
        case ISN_GETITEM:
        case ISN_JUMP:
+       case ISN_LISTAPPEND:
        case ISN_LISTINDEX:
        case ISN_LISTSLICE:
        case ISN_LOAD:
index 673bf91c43be399f8a39fc4e40789e53aa03527b..031761d422de3e792de1a86f5edbae17ccdd3624 100644 (file)
@@ -2095,6 +2095,7 @@ call_def_function(
                                       || *skipwhite(tv->vval.v_string) == NUL)
                {
                    vim_free(tv->vval.v_string);
+                   SOURCING_LNUM = iptr->isn_lnum;
                    emsg(_(e_throw_with_empty_string));
                    goto failed;
                }
@@ -2282,6 +2283,7 @@ call_def_function(
                    typval_T *tv1 = STACK_TV_BOT(-2);
                    typval_T *tv2 = STACK_TV_BOT(-1);
 
+                   // add two lists or blobs
                    if (iptr->isn_type == ISN_ADDLIST)
                        eval_addlist(tv1, tv2);
                    else
@@ -2291,6 +2293,25 @@ call_def_function(
                }
                break;
 
+           case ISN_LISTAPPEND:
+               {
+                   typval_T    *tv1 = STACK_TV_BOT(-2);
+                   typval_T    *tv2 = STACK_TV_BOT(-1);
+                   list_T      *l = tv1->vval.v_list;
+
+                   // add an item to a list
+                   if (l == NULL)
+                   {
+                       SOURCING_LNUM = iptr->isn_lnum;
+                       emsg(_(e_cannot_add_to_null_list));
+                       goto on_error;
+                   }
+                   if (list_append_tv(l, tv2) == FAIL)
+                       goto failed;
+                   --ectx.ec_stack.ga_len;
+               }
+               break;
+
            // Computation with two arguments of unknown type
            case ISN_OPANY:
                {
@@ -3410,6 +3431,7 @@ ex_disassemble(exarg_T *eap)
            case ISN_CONCAT: smsg("%4d CONCAT", current); break;
            case ISN_STRINDEX: smsg("%4d STRINDEX", current); break;
            case ISN_STRSLICE: smsg("%4d STRSLICE", current); break;
+           case ISN_LISTAPPEND: smsg("%4d LISTAPPEND", current); break;
            case ISN_LISTINDEX: smsg("%4d LISTINDEX", current); break;
            case ISN_LISTSLICE: smsg("%4d LISTSLICE", current); break;
            case ISN_ANYINDEX: smsg("%4d ANYINDEX", current); break;