Patch 8.2.2179
Problem:    Vim9: crash when indexing a dict with a number.
Solution:   Add ISN_STOREINDEX. (closes #7513)
Files:      src/vim9compile.c, src/vim9execute.c, src/vim9.h,
            src/errors.h, src/testdir/test_vim9_assign.vim,
            src/testdir/test_vim9_disassemble.vim


*** ../vim-8.2.2178/src/vim9compile.c   2020-12-20 21:43:31.704882194 +0100
--- src/vim9compile.c   2020-12-21 16:58:04.803979235 +0100
***************
*** 5904,5929 ****
  
            if (type == &t_any)
            {
!               type_T      *idx_type = ((type_T **)stack->ga_data)[
!                                                           stack->ga_len - 1];
!               // Index on variable of unknown type: guess the type from the
!               // index type: number is dict, otherwise dict.
!               // TODO: should do the assignment at runtime
!               if (idx_type->tt_type == VAR_NUMBER)
!                   type = &t_list_any;
!               else
!                   type = &t_dict_any;
            }
!           dest_type = type->tt_type;
!           if (dest_type == VAR_DICT
!                   && may_generate_2STRING(-1, cctx) == FAIL)
!               goto theend;
!           if (dest_type == VAR_LIST
!                   && ((type_T **)stack->ga_data)[stack->ga_len - 1]->tt_type
!                                                                != VAR_NUMBER)
            {
!               emsg(_(e_number_exp));
!               goto theend;
            }
  
            // Load the dict or list.  On the stack we then have:
--- 5904,5925 ----
  
            if (type == &t_any)
            {
!               // Index on variable of unknown type: check at runtime.
!               dest_type = VAR_ANY;
            }
!           else
            {
!               dest_type = type->tt_type;
!               if (dest_type == VAR_DICT
!                       && may_generate_2STRING(-1, cctx) == FAIL)
!                   goto theend;
!               if (dest_type == VAR_LIST
!                    && ((type_T **)stack->ga_data)[stack->ga_len - 1]->tt_type
!                                                                != VAR_NUMBER)
!               {
!                   emsg(_(e_number_exp));
!                   goto theend;
!               }
            }
  
            // Load the dict or list.  On the stack we then have:
***************
*** 5956,5970 ****
            else
                generate_loadvar(cctx, dest, name, lvar, type);
  
!           if (dest_type == VAR_LIST)
            {
!               if (generate_instr_drop(cctx, ISN_STORELIST, 3) == FAIL)
!                   goto theend;
!           }
!           else if (dest_type == VAR_DICT)
!           {
!               if (generate_instr_drop(cctx, ISN_STOREDICT, 3) == FAIL)
                    goto theend;
            }
            else
            {
--- 5952,5965 ----
            else
                generate_loadvar(cctx, dest, name, lvar, type);
  
!           if (dest_type == VAR_LIST || dest_type == VAR_DICT
!                                                      || dest_type == VAR_ANY)
            {
!               isn_T   *isn = generate_instr_drop(cctx, ISN_STOREINDEX, 3);
! 
!               if (isn == NULL)
                    goto theend;
+               isn->isn_arg.vartype = dest_type;
            }
            else
            {
***************
*** 8194,8201 ****
        case ISN_SHUFFLE:
        case ISN_SLICE:
        case ISN_STORE:
!       case ISN_STOREDICT:
!       case ISN_STORELIST:
        case ISN_STORENR:
        case ISN_STOREOUTER:
        case ISN_STOREREG:
--- 8189,8195 ----
        case ISN_SHUFFLE:
        case ISN_SLICE:
        case ISN_STORE:
!       case ISN_STOREINDEX:
        case ISN_STORENR:
        case ISN_STOREOUTER:
        case ISN_STOREREG:
*** ../vim-8.2.2178/src/vim9execute.c   2020-12-20 21:43:31.704882194 +0100
--- src/vim9execute.c   2020-12-21 17:14:17.544959681 +0100
***************
*** 802,807 ****
--- 802,839 ----
  }
  
  /*
+  * Convert "tv" to a string.
+  * Return FAIL if not allowed.
+  */
+     static int
+ do_2string(typval_T *tv, int is_2string_any)
+ {
+     if (tv->v_type != VAR_STRING)
+     {
+       char_u *str;
+ 
+       if (is_2string_any)
+       {
+           switch (tv->v_type)
+           {
+               case VAR_SPECIAL:
+               case VAR_BOOL:
+               case VAR_NUMBER:
+               case VAR_FLOAT:
+               case VAR_BLOB:  break;
+               default:        to_string_error(tv->v_type);
+                               return FAIL;
+           }
+       }
+       str = typval_tostring(tv);
+       clear_tv(tv);
+       tv->v_type = VAR_STRING;
+       tv->vval.v_string = str;
+     }
+     return OK;
+ }
+ 
+ /*
   * When the value of "sv" is a null list of dict, allocate it.
   */
      static void
***************
*** 1700,1791 ****
                tv->vval.v_number = iptr->isn_arg.storenr.stnr_val;
                break;
  
!           // store value in list variable
!           case ISN_STORELIST:
                {
                    typval_T    *tv_idx = STACK_TV_BOT(-2);
!                   varnumber_T lidx = tv_idx->vval.v_number;
!                   typval_T    *tv_list = STACK_TV_BOT(-1);
!                   list_T      *list = tv_list->vval.v_list;
  
                    SOURCING_LNUM = iptr->isn_lnum;
!                   if (lidx < 0 && list->lv_len + lidx >= 0)
!                       // negative index is relative to the end
!                       lidx = list->lv_len + lidx;
!                   if (lidx < 0 || lidx > list->lv_len)
                    {
!                       semsg(_(e_listidx), lidx);
!                       goto on_error;
                    }
!                   tv = STACK_TV_BOT(-3);
!                   if (lidx < list->lv_len)
                    {
!                       listitem_T *li = list_find(list, lidx);
! 
!                       if (error_if_locked(li->li_tv.v_lock,
!                                                   e_cannot_change_list_item))
!                           goto failed;
!                       // overwrite existing list item
!                       clear_tv(&li->li_tv);
!                       li->li_tv = *tv;
                    }
!                   else
                    {
!                       if (error_if_locked(list->lv_lock,
!                                                        e_cannot_change_list))
!                           goto failed;
!                       // append to list, only fails when out of memory
!                       if (list_append_tv(list, tv) == FAIL)
!                           goto failed;
!                       clear_tv(tv);
!                   }
!                   clear_tv(tv_idx);
!                   clear_tv(tv_list);
!                   ectx.ec_stack.ga_len -= 3;
!               }
!               break;
  
!           // store value in dict variable
!           case ISN_STOREDICT:
!               {
!                   typval_T    *tv_key = STACK_TV_BOT(-2);
!                   char_u      *key = tv_key->vval.v_string;
!                   typval_T    *tv_dict = STACK_TV_BOT(-1);
!                   dict_T      *dict = tv_dict->vval.v_dict;
!                   dictitem_T  *di;
  
!                   SOURCING_LNUM = iptr->isn_lnum;
!                   if (dict == NULL)
!                   {
!                       emsg(_(e_dictionary_not_set));
!                       goto on_error;
                    }
!                   if (key == NULL)
!                       key = (char_u *)"";
!                   tv = STACK_TV_BOT(-3);
!                   di = dict_find(dict, key, -1);
!                   if (di != NULL)
                    {
!                       if (error_if_locked(di->di_tv.v_lock,
                                                    e_cannot_change_dict_item))
!                           goto failed;
!                       // overwrite existing value
!                       clear_tv(&di->di_tv);
!                       di->di_tv = *tv;
                    }
                    else
                    {
!                       if (error_if_locked(dict->dv_lock,
!                                                        e_cannot_change_dict))
!                           goto failed;
!                       // add to dict, only fails when out of memory
!                       if (dict_add_tv(dict, (char *)key, tv) == FAIL)
!                           goto failed;
!                       clear_tv(tv);
                    }
!                   clear_tv(tv_key);
!                   clear_tv(tv_dict);
                    ectx.ec_stack.ga_len -= 3;
                }
                break;
  
--- 1732,1857 ----
                tv->vval.v_number = iptr->isn_arg.storenr.stnr_val;
                break;
  
!           // store value in list or dict variable
!           case ISN_STOREINDEX:
                {
+                   vartype_T   dest_type = iptr->isn_arg.vartype;
                    typval_T    *tv_idx = STACK_TV_BOT(-2);
!                   typval_T    *tv_dest = STACK_TV_BOT(-1);
!                   int         status = OK;
  
+                   tv = STACK_TV_BOT(-3);
                    SOURCING_LNUM = iptr->isn_lnum;
!                   if (dest_type == VAR_ANY)
                    {
!                       dest_type = tv_dest->v_type;
!                       if (dest_type == VAR_DICT)
!                           status = do_2string(tv_idx, TRUE);
!                       else if (dest_type == VAR_LIST
!                                              && tv_idx->v_type != VAR_NUMBER)
!                       {
!                           emsg(_(e_number_exp));
!                           status = FAIL;
!                       }
                    }
!                   else if (dest_type != tv_dest->v_type)
                    {
!                       // just in case, should be OK
!                       semsg(_(e_expected_str_but_got_str),
!                                   vartype_name(dest_type),
!                                   vartype_name(tv_dest->v_type));
!                       status = FAIL;
                    }
! 
!                   if (status == OK && dest_type == VAR_LIST)
                    {
!                       varnumber_T     lidx = tv_idx->vval.v_number;
!                       list_T          *list = tv_dest->vval.v_list;
  
!                       if (list == NULL)
!                       {
!                           emsg(_(e_list_not_set));
!                           goto on_error;
!                       }
!                       if (lidx < 0 && list->lv_len + lidx >= 0)
!                           // negative index is relative to the end
!                           lidx = list->lv_len + lidx;
!                       if (lidx < 0 || lidx > list->lv_len)
!                       {
!                           semsg(_(e_listidx), lidx);
!                           goto on_error;
!                       }
!                       if (lidx < list->lv_len)
!                       {
!                           listitem_T *li = list_find(list, lidx);
  
!                           if (error_if_locked(li->li_tv.v_lock,
!                                                   e_cannot_change_list_item))
!                               goto on_error;
!                           // overwrite existing list item
!                           clear_tv(&li->li_tv);
!                           li->li_tv = *tv;
!                       }
!                       else
!                       {
!                           if (error_if_locked(list->lv_lock,
!                                                        e_cannot_change_list))
!                               goto on_error;
!                           // append to list, only fails when out of memory
!                           if (list_append_tv(list, tv) == FAIL)
!                               goto failed;
!                           clear_tv(tv);
!                       }
                    }
!                   else if (status == OK && dest_type == VAR_DICT)
                    {
!                       char_u          *key = tv_idx->vval.v_string;
!                       dict_T          *dict = tv_dest->vval.v_dict;
!                       dictitem_T      *di;
! 
!                       SOURCING_LNUM = iptr->isn_lnum;
!                       if (dict == NULL)
!                       {
!                           emsg(_(e_dictionary_not_set));
!                           goto on_error;
!                       }
!                       if (key == NULL)
!                           key = (char_u *)"";
!                       di = dict_find(dict, key, -1);
!                       if (di != NULL)
!                       {
!                           if (error_if_locked(di->di_tv.v_lock,
                                                    e_cannot_change_dict_item))
!                               goto on_error;
!                           // overwrite existing value
!                           clear_tv(&di->di_tv);
!                           di->di_tv = *tv;
!                       }
!                       else
!                       {
!                           if (error_if_locked(dict->dv_lock,
!                                                        e_cannot_change_dict))
!                               goto on_error;
!                           // add to dict, only fails when out of memory
!                           if (dict_add_tv(dict, (char *)key, tv) == FAIL)
!                               goto failed;
!                           clear_tv(tv);
!                       }
                    }
                    else
                    {
!                       status = FAIL;
!                       semsg(_(e_cannot_index_str), vartype_name(dest_type));
                    }
! 
!                   clear_tv(tv_idx);
!                   clear_tv(tv_dest);
                    ectx.ec_stack.ga_len -= 3;
+                   if (status == FAIL)
+                   {
+                       clear_tv(tv);
+                       goto on_error;
+                   }
                }
                break;
  
***************
*** 2921,2951 ****
  
            case ISN_2STRING:
            case ISN_2STRING_ANY:
!               {
!                   char_u *str;
! 
!                   tv = STACK_TV_BOT(iptr->isn_arg.number);
!                   if (tv->v_type != VAR_STRING)
!                   {
!                       if (iptr->isn_type == ISN_2STRING_ANY)
!                       {
!                           switch (tv->v_type)
!                           {
!                               case VAR_SPECIAL:
!                               case VAR_BOOL:
!                               case VAR_NUMBER:
!                               case VAR_FLOAT:
!                               case VAR_BLOB:  break;
!                               default:        to_string_error(tv->v_type);
!                                               goto on_error;
!                           }
!                       }
!                       str = typval_tostring(tv);
!                       clear_tv(tv);
!                       tv->v_type = VAR_STRING;
!                       tv->vval.v_string = str;
!                   }
!               }
                break;
  
            case ISN_RANGE:
--- 2987,2995 ----
  
            case ISN_2STRING:
            case ISN_2STRING_ANY:
!               if (do_2string(STACK_TV_BOT(iptr->isn_arg.number),
!                       iptr->isn_type == ISN_2STRING_ANY) == FAIL)
!                           goto on_error;
                break;
  
            case ISN_RANGE:
***************
*** 3462,3473 ****
                                iptr->isn_arg.storenr.stnr_idx);
                break;
  
!           case ISN_STORELIST:
!               smsg("%4d STORELIST", current);
!               break;
! 
!           case ISN_STOREDICT:
!               smsg("%4d STOREDICT", current);
                break;
  
            // constants
--- 3506,3525 ----
                                iptr->isn_arg.storenr.stnr_idx);
                break;
  
!           case ISN_STOREINDEX:
!               switch (iptr->isn_arg.vartype)
!               {
!                   case VAR_LIST:
!                           smsg("%4d STORELIST", current);
!                           break;
!                   case VAR_DICT:
!                           smsg("%4d STOREDICT", current);
!                           break;
!                   case VAR_ANY:
!                           smsg("%4d STOREINDEX", current);
!                           break;
!                   default: break;
!               }
                break;
  
            // constants
*** ../vim-8.2.2178/src/vim9.h  2020-12-19 16:30:39.439810130 +0100
--- src/vim9.h  2020-12-21 16:44:31.186583018 +0100
***************
*** 55,62 ****
      // ISN_STOREOTHER, // pop into other script variable isn_arg.other.
  
      ISN_STORENR,    // store number into local variable 
isn_arg.storenr.stnr_idx
!     ISN_STORELIST,    // store into list, value/index/variable on stack
!     ISN_STOREDICT,    // store into dictionary, value/index/variable on stack
  
      ISN_UNLET,                // unlet variable isn_arg.unlet.ul_name
      ISN_UNLETENV,     // unlet environment variable isn_arg.unlet.ul_name
--- 55,62 ----
      // ISN_STOREOTHER, // pop into other script variable isn_arg.other.
  
      ISN_STORENR,    // store number into local variable 
isn_arg.storenr.stnr_idx
!     ISN_STOREINDEX,   // store into list or dictionary, type isn_arg.vartype,
!                       // value/index/variable on stack
  
      ISN_UNLET,                // unlet variable isn_arg.unlet.ul_name
      ISN_UNLETENV,     // unlet environment variable isn_arg.unlet.ul_name
***************
*** 304,309 ****
--- 304,310 ----
        char_u              *string;
        varnumber_T         number;
        blob_T              *blob;
+       vartype_T           vartype;
  #ifdef FEAT_FLOAT
        float_T             fnumber;
  #endif
*** ../vim-8.2.2178/src/errors.h        2020-12-20 21:43:31.704882194 +0100
--- src/errors.h        2020-12-21 16:58:58.399828342 +0100
***************
*** 323,325 ****
--- 323,329 ----
        INIT(= N_("E1145: Missing heredoc end marker: %s"));
  EXTERN char e_command_not_recognized_str[]
        INIT(= N_("E1146: Command not recognized: %s"));
+ EXTERN char e_list_not_set[]
+       INIT(= N_("E1147: List not set"));
+ EXTERN char e_cannot_index_str[]
+       INIT(= N_("E1148: Cannot index a %s"));
*** ../vim-8.2.2178/src/testdir/test_vim9_assign.vim    2020-12-20 
15:20:53.326899494 +0100
--- src/testdir/test_vim9_assign.vim    2020-12-21 17:30:16.037772894 +0100
***************
*** 533,538 ****
--- 533,544 ----
  
    # type becomes list<any>
    var somelist = rand() > 0 ? [1, 2, 3] : ['a', 'b', 'c']
+ 
+   var lines =<< trim END
+     var d = {dd: test_null_list()}
+     d.dd[0] = 0
+   END
+   CheckDefExecFailure(lines, 'E1147:', 2)
  enddef
  
  def Test_assignment_list_vim9script()
***************
*** 567,578 ****
    assert_equal({nest: {this: 123, that: 456}, nr: 0}, anydict)
  
    var lines =<< trim END
-     vim9script
      var dd = {}
      dd.two = 2
      assert_equal({two: 2}, dd)
    END
!   CheckScriptSuccess(lines)
  
    lines =<< trim END
      var dd = {one: 1}
--- 573,592 ----
    assert_equal({nest: {this: 123, that: 456}, nr: 0}, anydict)
  
    var lines =<< trim END
      var dd = {}
      dd.two = 2
      assert_equal({two: 2}, dd)
    END
!   CheckDefAndScriptSuccess(lines)
! 
!   lines =<< trim END
!     var d = {dd: {}}
!     d.dd[0] = 2
!     d.dd['x'] = 3
!     d.dd.y = 4
!     assert_equal({dd: {0: 2, x: 3, y: 4}}, d)
!   END
!   CheckDefAndScriptSuccess(lines)
  
    lines =<< trim END
      var dd = {one: 1}
***************
*** 641,646 ****
--- 655,672 ----
      assert_equal({a: 43}, FillDict())
    END
    CheckScriptSuccess(lines)
+ 
+   lines =<< trim END
+     var d = {dd: test_null_dict()}
+     d.dd[0] = 0
+   END
+   CheckDefExecFailure(lines, 'E1103:', 2)
+ 
+   lines =<< trim END
+     var d = {dd: 'string'}
+     d.dd[0] = 0
+   END
+   CheckDefExecFailure(lines, 'E1148:', 2)
  enddef
  
  def Test_assignment_local()
*** ../vim-8.2.2178/src/testdir/test_vim9_disassemble.vim       2020-12-19 
16:30:39.439810130 +0100
--- src/testdir/test_vim9_disassemble.vim       2020-12-21 17:22:05.995410794 
+0100
***************
*** 276,281 ****
--- 276,305 ----
          res)
  enddef
  
+ def s:ScriptFuncStoreIndex()
+   var d = {dd: {}}
+   d.dd[0] = 0
+ enddef
+ 
+ def Test_disassemble_store_index()
+   var res = execute('disass s:ScriptFuncStoreIndex')
+   assert_match('<SNR>\d*_ScriptFuncStoreIndex\_s*' ..
+         'var d = {dd: {}}\_s*' ..
+         '\d PUSHS "dd"\_s*' ..
+         '\d NEWDICT size 0\_s*' ..
+         '\d NEWDICT size 1\_s*' ..
+         '\d STORE $0\_s*' ..
+         'd.dd\[0\] = 0\_s*' ..
+         '\d PUSHNR 0\_s*' ..
+         '\d PUSHNR 0\_s*' ..
+         '\d LOAD $0\_s*' ..
+         '\d MEMBER dd\_s*' ..
+         '\d STOREINDEX\_s*' ..
+         '\d\+ PUSHNR 0\_s*' ..
+         '\d\+ RETURN',
+         res)
+ enddef
+ 
  def s:ListAssign()
    var x: string
    var y: string
*** ../vim-8.2.2178/src/version.c       2020-12-21 16:02:58.486392542 +0100
--- src/version.c       2020-12-21 16:32:12.372718497 +0100
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     2179,
  /**/

-- 
EXPERIENCE - experience is a wonderful thing. It enables you to 
recognise a mistake when you make it again.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202012211631.0BLGVJsl2275362%40masaka.moolenaar.net.

Raspunde prin e-mail lui