]> granicus.if.org Git - vim/commitdiff
patch 8.2.3339: Vim9: cannot lock a member in a local dict v8.2.3339
authorBram Moolenaar <Bram@vim.org>
Fri, 13 Aug 2021 17:40:51 +0000 (19:40 +0200)
committerBram Moolenaar <Bram@vim.org>
Fri, 13 Aug 2021 17:40:51 +0000 (19:40 +0200)
Problem:    Vim9: cannot lock a member in a local dict.
Solution:   Get the local dict from the stack and pass it to get_lval().

src/eval.c
src/globals.h
src/testdir/test_vim9_cmd.vim
src/testdir/test_vim9_disassemble.vim
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c

index edef8785d5f04b0771ceb33bf4c53b58a8d47fda..0dd2058872da266a93c77a755f0e28f522cde9c3 100644 (file)
@@ -902,17 +902,26 @@ get_lval(
     if ((*p != '[' && *p != '.') || lp->ll_name == NULL)
        return p;
 
-    cc = *p;
-    *p = NUL;
-    // When we would write to the variable pass &ht and prevent autoload.
-    writing = !(flags & GLV_READ_ONLY);
-    v = find_var(lp->ll_name, writing ? &ht : NULL,
+    if (in_vim9script() && lval_root != NULL)
+    {
+       // using local variable
+       lp->ll_tv = lval_root;
+    }
+    else
+    {
+       cc = *p;
+       *p = NUL;
+       // When we would write to the variable pass &ht and prevent autoload.
+       writing = !(flags & GLV_READ_ONLY);
+       v = find_var(lp->ll_name, writing ? &ht : NULL,
                                         (flags & GLV_NO_AUTOLOAD) || writing);
-    if (v == NULL && !quiet)
-       semsg(_(e_undefined_variable_str), lp->ll_name);
-    *p = cc;
-    if (v == NULL)
-       return NULL;
+       if (v == NULL && !quiet)
+           semsg(_(e_undefined_variable_str), lp->ll_name);
+       *p = cc;
+       if (v == NULL)
+           return NULL;
+       lp->ll_tv = &v->di_tv;
+    }
 
     if (in_vim9script() && (flags & GLV_NO_DECL) == 0)
     {
@@ -924,7 +933,6 @@ get_lval(
     /*
      * Loop until no more [idx] or .key is following.
      */
-    lp->ll_tv = &v->di_tv;
     var1.v_type = VAR_UNKNOWN;
     var2.v_type = VAR_UNKNOWN;
     while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.'))
index ebf236b0003360014ed2cb36ad4c21118505c5b4..f09ad482fbf7dc737e0bff7a84f7c42f5a594851 100644 (file)
@@ -1835,6 +1835,8 @@ EXTERN int  timer_busy INIT(= 0);   // when timer is inside vgetc() then > 0
 #endif
 #ifdef FEAT_EVAL
 EXTERN int  input_busy INIT(= 0);   // when inside get_user_input() then > 0
+
+EXTERN typval_T        *lval_root INIT(= NULL);
 #endif
 
 #ifdef FEAT_BEVAL_TERM
index 84cb6d665057739d9c0ea118696345e13cf47465..7ec43e9d58c298ceb04b7341b1e0ae86f9faddef 100644 (file)
@@ -1195,6 +1195,23 @@ def Test_lockvar()
   s:theList[1] = 44
   assert_equal([1, 44, 3], s:theList)
 
+  var d = {a: 1, b: 2}
+  d.a = 3
+  d.b = 4
+  assert_equal({a: 3, b: 4}, d)
+  lockvar d.a
+  d.b = 5
+  var ex = ''
+  try
+    d.a = 6
+  catch
+    ex = v:exception
+  endtry
+  assert_match('E1121:', ex)
+  unlockvar d.a
+  d.a = 7
+  assert_equal({a: 7, b: 5}, d)
+
   var lines =<< trim END
       vim9script
       var theList = [1, 2, 3]
index 1d1730e00426ecc6036f68b80e4e0a2b44eb3e13..15a68dd502da2fbf78ad9d58a3476486a983dce7 100644 (file)
@@ -588,6 +588,25 @@ def Test_disassemble_unlet()
         res)
 enddef
 
+def s:LockLocal()
+  var d = {a: 1}
+  lockvar d.a
+enddef
+
+def Test_disassemble_locl_local()
+  var res = execute('disass s:LockLocal')
+  assert_match('<SNR>\d*_LockLocal\_s*' ..
+        'var d = {a: 1}\_s*' ..
+        '\d PUSHS "a"\_s*' ..
+        '\d PUSHNR 1\_s*' ..
+        '\d NEWDICT size 1\_s*' ..
+        '\d STORE $0\_s*' ..
+        'lockvar d.a\_s*' ..
+        '\d LOAD $0\_s*' ..
+        '\d LOCKUNLOCK lockvar d.a\_s*',
+        res)
+enddef
+
 def s:ScriptFuncTry()
   try
     echo "yes"
index 1acc17adf4dbd5e95e8bd1bb724b58df92b1dbc8..4c1be4ecf101d3ce111fafb4cf99fa65b5525b58 100644 (file)
@@ -755,6 +755,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3339,
 /**/
     3338,
 /**/
index da39949fa34271ce7ae98caa49c88f1b63d94a5c..67c9a710a58b615198e8b8129da77ce197da18da 100644 (file)
@@ -70,6 +70,7 @@ typedef enum {
     ISN_UNLETINDEX,    // unlet item of list or dict
     ISN_UNLETRANGE,    // unlet items of list
 
+    ISN_LOCKUNLOCK,    // :lock and :unlock for local variable member
     ISN_LOCKCONST,     // lock constant value
 
     // constants
index d6a7e0aa377d398af8c7f0d9c99d5139d8a0fa33..e29d963778db54990d94a3f42f8c3a1f7e510442 100644 (file)
@@ -2262,12 +2262,12 @@ generate_PUT(cctx_T *cctx, int regname, linenr_T lnum)
 }
 
     static int
-generate_EXEC(cctx_T *cctx, char_u *line)
+generate_EXEC(cctx_T *cctx, isntype_T isntype, char_u *line)
 {
     isn_T      *isn;
 
     RETURN_OK_IF_SKIP(cctx);
-    if ((isn = generate_instr(cctx, ISN_EXEC)) == NULL)
+    if ((isn = generate_instr(cctx, isntype)) == NULL)
        return FAIL;
     isn->isn_arg.string = vim_strsave(line);
     return OK;
@@ -7426,6 +7426,7 @@ compile_lock_unlock(
     int                ret = OK;
     size_t     len;
     char_u     *buf;
+    isntype_T  isn = ISN_EXEC;
 
     if (cctx->ctx_skip == SKIP_YES)
        return OK;
@@ -7437,8 +7438,19 @@ compile_lock_unlock(
 
        if (lookup_local(p, end - p, NULL, cctx) == OK)
        {
-           emsg(_(e_cannot_lock_unlock_local_variable));
-           return FAIL;
+           char_u *s = p;
+
+           if (*end != '.' && *end != '[')
+           {
+               emsg(_(e_cannot_lock_unlock_local_variable));
+               return FAIL;
+           }
+
+           // For "d.member" put the local variable on the stack, it will be
+           // passed to ex_lockvar() indirectly.
+           if (compile_load(&s, end, cctx, FALSE, FALSE) == FAIL)
+               return FAIL;
+           isn = ISN_LOCKUNLOCK;
        }
     }
 
@@ -7453,7 +7465,7 @@ compile_lock_unlock(
        vim_snprintf((char *)buf, len, "%s %s",
                eap->cmdidx == CMD_lockvar ? "lockvar" : "unlockvar",
                p);
-       ret = generate_EXEC(cctx, buf);
+       ret = generate_EXEC(cctx, isn, buf);
 
        vim_free(buf);
        *name_end = cc;
@@ -9110,7 +9122,7 @@ compile_exec(char_u *line_arg, exarg_T *eap, cctx_T *cctx)
        generate_EXECCONCAT(cctx, count);
     }
     else
-       generate_EXEC(cctx, line);
+       generate_EXEC(cctx, ISN_EXEC, line);
 
 theend:
     if (*nextcmd != NUL)
@@ -10198,6 +10210,7 @@ delete_instr(isn_T *isn)
        case ISN_LOADOPT:
        case ISN_LOADT:
        case ISN_LOADW:
+       case ISN_LOCKUNLOCK:
        case ISN_PUSHEXC:
        case ISN_PUSHFUNC:
        case ISN_PUSHS:
index 84de4fee5a3e891275d79e06c819cf4d254d3848..04d5e5f27cea3007ee8fa6b38ac756851d65eae5 100644 (file)
@@ -1396,6 +1396,27 @@ fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
     return OK;
 }
 
+/*
+ * Execute iptr->isn_arg.string as an Ex command.
+ */
+    static int
+exec_command(isn_T *iptr)
+{
+    source_cookie_T cookie;
+
+    SOURCING_LNUM = iptr->isn_lnum;
+    // Pass getsourceline to get an error for a missing ":end"
+    // command.
+    CLEAR_FIELD(cookie);
+    cookie.sourcing_lnum = iptr->isn_lnum - 1;
+    if (do_cmdline(iptr->isn_arg.string,
+               getsourceline, &cookie,
+                            DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED) == FAIL
+               || did_emsg)
+       return FAIL;
+    return OK;
+}
+
 // used for v_instr of typval of VAR_INSTR
 struct instr_S {
     ectx_T     *instr_ectx;
@@ -1637,21 +1658,8 @@ exec_instructions(ectx_T *ectx)
        {
            // execute Ex command line
            case ISN_EXEC:
-               {
-                   source_cookie_T cookie;
-
-                   SOURCING_LNUM = iptr->isn_lnum;
-                   // Pass getsourceline to get an error for a missing ":end"
-                   // command.
-                   CLEAR_FIELD(cookie);
-                   cookie.sourcing_lnum = iptr->isn_lnum - 1;
-                   if (do_cmdline(iptr->isn_arg.string,
-                               getsourceline, &cookie,
-                                  DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED)
-                                                                       == FAIL
-                               || did_emsg)
-                       goto on_error;
-               }
+               if (exec_command(iptr) == FAIL)
+                   goto on_error;
                break;
 
            // execute Ex command line split at NL characters.
@@ -2880,6 +2888,23 @@ exec_instructions(ectx_T *ectx)
                vim_unsetenv(iptr->isn_arg.unlet.ul_name);
                break;
 
+           case ISN_LOCKUNLOCK:
+               {
+                   typval_T    *lval_root_save = lval_root;
+                   int         res;
+
+                   // Stack has the local variable, argument the whole :lock
+                   // or :unlock command, like ISN_EXEC.
+                   --ectx->ec_stack.ga_len;
+                   lval_root = STACK_TV_BOT(0);
+                   res = exec_command(iptr);
+                   clear_tv(lval_root);
+                   lval_root = lval_root_save;
+                   if (res == FAIL)
+                       goto on_error;
+               }
+               break;
+
            case ISN_LOCKCONST:
                item_lock(STACK_TV_BOT(-1), 100, TRUE, TRUE);
                break;
@@ -5244,6 +5269,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
            case ISN_UNLETRANGE:
                smsg("%s%4d UNLETRANGE", pfx, current);
                break;
+           case ISN_LOCKUNLOCK:
+               smsg("%s%4d LOCKUNLOCK %s", pfx, current, iptr->isn_arg.string);
+               break;
            case ISN_LOCKCONST:
                smsg("%s%4d LOCKCONST", pfx, current);
                break;