]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.1982: vim9: clean up from v9.0.1955 v9.0.1982
authorErnie Rael <errael@raelity.com>
Wed, 4 Oct 2023 18:16:22 +0000 (20:16 +0200)
committerChristian Brabandt <cb@256bit.org>
Wed, 4 Oct 2023 18:16:22 +0000 (20:16 +0200)
Problem:  vim9: clean up from v9.0.1955
Solution: Fix a few remaining issues, improve error message

- Use `cl_exec`, the executing class, to check permissions in `get_lval()`.
- Handle lockvar of script variable from class.
- Add 'in class "Xxx"' to e_cannot_access_private_variable_str.

closes: #13222

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Ernie Rael <errael@raelity.com>
13 files changed:
src/errors.h
src/eval.c
src/evalvars.c
src/globals.h
src/structs.h
src/testdir/test_vim9_class.vim
src/version.c
src/vim9.h
src/vim9class.c
src/vim9cmds.c
src/vim9execute.c
src/vim9expr.c
src/vim9instr.c

index 817589288174d83152edb6a3dc5cfe402f60196e..21495cd879183ba41bf8a80670b00f2b569589d4 100644 (file)
@@ -3411,7 +3411,7 @@ EXTERN char e_public_must_be_followed_by_this_or_static[]
 EXTERN char e_public_variable_name_cannot_start_with_underscore_str[]
        INIT(= N_("E1332: Public variable name cannot start with underscore: %s"));
 EXTERN char e_cannot_access_private_variable_str[]
-       INIT(= N_("E1333: Cannot access private variable: %s"));
+       INIT(= N_("E1333: Cannot access private variable \"%s\" in class \"%s\""));
 // E1334 unused
 EXTERN char e_variable_is_not_writable_str[]
        INIT(= N_("E1335: Variable \"%s\" in class \"%s\" is not writable"));
index ae48a6085d45a5113d3008d6fd4810ec91d796eb..0a335df071c888dd2c9daf74f7184b05f4149104 100644 (file)
@@ -985,6 +985,52 @@ eval_foldexpr(win_T *wp, int *cp)
 }
 #endif
 
+#ifdef LOG_LOCKVAR
+typedef struct flag_string_S
+{
+    int            flag;
+    char    *str;
+} flag_string_T;
+
+    static char *
+flags_tostring(int flags, flag_string_T *_fstring, char *buf, size_t n)
+{
+    char *p = buf;
+    *p = NUL;
+    for (flag_string_T *fstring = _fstring; fstring->flag; ++fstring)
+    {
+       if ((fstring->flag & flags) != 0)
+       {
+           size_t len = STRLEN(fstring->str);
+           if (n > p - buf + len + 7)
+           {
+               STRCAT(p, fstring->str);
+               p += len;
+               STRCAT(p, " ");
+               ++p;
+           }
+           else
+           {
+               STRCAT(buf, "...");
+               break;
+           }
+       }
+    }
+    return buf;
+}
+
+flag_string_T glv_flag_strings[] = {
+    { GLV_QUIET,               "QUIET" },
+    { GLV_NO_AUTOLOAD,         "NO_AUTOLOAD" },
+    { GLV_READ_ONLY,           "READ_ONLY" },
+    { GLV_NO_DECL,             "NO_DECL" },
+    { GLV_COMPILING,           "COMPILING" },
+    { GLV_ASSIGN_WITH_OP,      "ASSIGN_WITH_OP" },
+    { GLV_PREFER_FUNC,         "PREFER_FUNC" },
+    { 0,                       NULL }
+};
+#endif
+
 /*
  * Fill in "lp" using "root". This is used in a special case when
  * "get_lval()" parses a bare word when "lval_root" is not NULL.
@@ -1004,30 +1050,30 @@ eval_foldexpr(win_T *wp, int *cp)
  *     execute_instructions: ISN_LOCKUNLOCK - sets lval_root from stack.
  */
     static void
-get_lval_root(lval_T *lp, typval_T *root, int is_arg)
+get_lval_root(lval_T *lp, lval_root_T *lr)
 {
 #ifdef LOG_LOCKVAR
-    ch_log(NULL, "LKVAR: get_lvalroot(): name %s", lp->ll_name);
+    ch_log(NULL, "LKVAR: get_lval_root(): name %s", lp->ll_name);
 #endif
-    if (!is_arg && root->v_type == VAR_CLASS)
+    if (!lr->lr_is_arg && lr->lr_tv->v_type == VAR_CLASS)
     {
-       if (root->vval.v_class != NULL)
+       if (lr->lr_tv->vval.v_class != NULL)
        {
            // Special special case. Look for a bare class variable reference.
-           class_T     *cl = root->vval.v_class;
+           class_T     *cl = lr->lr_tv->vval.v_class;
            int         m_idx;
            ocmember_T  *m = class_member_lookup(cl, lp->ll_name,
                                        lp->ll_name_end - lp->ll_name, &m_idx);
            if (m != NULL)
            {
                // Assuming "inside class" since bare reference.
-               lp->ll_class = root->vval.v_class;
+               lp->ll_class = lr->lr_tv->vval.v_class;
                lp->ll_oi = m_idx;
                lp->ll_valtype = m->ocm_type;
                lp->ll_tv = &lp->ll_class->class_members_tv[m_idx];
 #ifdef LOG_LOCKVAR
-               ch_log(NULL, "LKVAR: get_lvalroot() class member: name %s",
-                                                               lp->ll_name);
+               ch_log(NULL, "LKVAR:    ... class member %s.%s",
+                                       lp->ll_class->class_name, lp->ll_name);
 #endif
                return;
            }
@@ -1035,12 +1081,54 @@ get_lval_root(lval_T *lp, typval_T *root, int is_arg)
     }
 
 #ifdef LOG_LOCKVAR
-    ch_log(NULL, "LKVAR: get_lvalroot() any type");
+    ch_log(NULL, "LKVAR:    ... type: %s", vartype_name(lr->lr_tv->v_type));
 #endif
-    lp->ll_tv = root;
+    lp->ll_tv = lr->lr_tv;
     lp->ll_is_root = TRUE;
 }
 
+/*
+ * Check if the class has permission to access the member.
+ * Returns OK or FAIL.
+ */
+    static int
+get_lval_check_access(
+    class_T    *cl_exec,   // executing class, NULL if :def or script level
+    class_T    *cl,        // class which contains the member
+    ocmember_T *om,        // member being accessed
+    char_u     *p,         // char after member name
+    int                flags)      // GLV flags to check if writing to lval
+{
+#ifdef LOG_LOCKVAR
+    ch_log(NULL, "LKVAR: get_lval_check_access(), cl_exec %p, cl %p, %c",
+                                               (void*)cl_exec, (void*)cl, *p);
+#endif
+    if (cl_exec == NULL || cl_exec != cl)
+    {
+       switch (om->ocm_access)
+       {
+           case VIM_ACCESS_PRIVATE:
+               semsg(_(e_cannot_access_private_variable_str),
+                                               om->ocm_name, cl->class_name);
+               return FAIL;
+           case VIM_ACCESS_READ:
+               // If [idx] or .key following, read only OK.
+               if (*p == '[' || *p == '.')
+                   break;
+               if ((flags & GLV_READ_ONLY) == 0)
+               {
+                   semsg(_(e_variable_is_not_writable_str),
+                                               om->ocm_name, cl->class_name);
+                   return FAIL;
+               }
+               break;
+           case VIM_ACCESS_ALL:
+               break;
+       }
+    }
+    return OK;
+}
+
 /*
  * Get an lval: variable, Dict item or List item that can be assigned a value
  * to: "name", "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]",
@@ -1083,10 +1171,19 @@ get_lval(
     int                quiet = flags & GLV_QUIET;
     int                writing = 0;
     int                vim9script = in_vim9script();
+    class_T    *cl_exec = NULL;    // class that is executing, or NULL.
 
 #ifdef LOG_LOCKVAR
-    ch_log(NULL, "LKVAR: get_lval(): name %s, lval_root %p",
-                                                   name, (void*)lval_root);
+    if (lval_root == NULL)
+       ch_log(NULL,
+              "LKVAR: get_lval(): name %s, lval_root (nil)", name);
+    else
+       ch_log(NULL,
+          "LKVAR: get_lval(): name %s, lr_tv %p lr_is_arg %d",
+           name, (void*)lval_root->lr_tv, lval_root->lr_is_arg);
+    char buf[80];
+    ch_log(NULL, "LKVAR:    ...: GLV flags %s",
+                   flags_tostring(flags, glv_flag_strings, buf, sizeof(buf)));
 #endif
 
     // Clear everything in "lp".
@@ -1221,15 +1318,16 @@ get_lval(
     if ((*p != '[' && *p != '.'))
     {
        if (lval_root != NULL)
-           get_lval_root(lp, lval_root, lval_root_is_arg);
+           get_lval_root(lp, lval_root);
        return p;
     }
 
     if (vim9script && lval_root != NULL)
     {
        // using local variable
-       lp->ll_tv = lval_root;
+       lp->ll_tv = lval_root->lr_tv;
        v = NULL;
+       cl_exec = lval_root->lr_cl_exec;
     }
     else
     {
@@ -1643,26 +1741,9 @@ get_lval(
                    lp->ll_oi = m_idx;
                    if (om != NULL)
                    {
-                       switch (om->ocm_access)
-                       {
-                           case VIM_ACCESS_PRIVATE:
-                               semsg(_(e_cannot_access_private_variable_str),
-                                       om->ocm_name);
-                               return NULL;
-                           case VIM_ACCESS_READ:
-                               // If [idx] or .key following, read only OK.
-                               if (*p == '[' || *p == '.')
-                                   break;
-                               if ((flags & GLV_READ_ONLY) == 0)
-                               {
-                                   semsg(_(e_variable_is_not_writable_str),
-                                           om->ocm_name, cl->class_name);
-                                   return NULL;
-                               }
-                               break;
-                           case VIM_ACCESS_ALL:
-                               break;
-                       }
+                       if (get_lval_check_access(cl_exec, cl, om,
+                                                         p, flags) == FAIL)
+                           return NULL;
 
                        lp->ll_valtype = om->ocm_type;
 
index fc977c66aeb085cf030b21938dd2bcd291fbec6e..14b7dc39a4e9b248ee38d12466c58bc15ab4b85b 100644 (file)
@@ -2278,30 +2278,6 @@ do_lock_var(
     return ret;
 }
 
-#ifdef LOG_LOCKVAR
-    static char *
-vartype_tostring(vartype_T vartype)
-{
-    return
-               vartype == VAR_BOOL ? "v_number"
-             : vartype == VAR_SPECIAL ? "v_number"
-             : vartype == VAR_NUMBER ? "v_number"
-             : vartype == VAR_FLOAT ? "v_float"
-             : vartype == VAR_STRING ? "v_string"
-             : vartype == VAR_BLOB ? "v_blob"
-             : vartype == VAR_FUNC ? "v_string"
-             : vartype == VAR_PARTIAL ? "v_partial"
-             : vartype == VAR_LIST ? "v_list"
-             : vartype == VAR_DICT ? "v_dict"
-             : vartype == VAR_JOB ? "v_job"
-             : vartype == VAR_CHANNEL ? "v_channel"
-             : vartype == VAR_INSTR ? "v_instr"
-             : vartype == VAR_CLASS ? "v_class"
-             : vartype == VAR_OBJECT ? "v_object"
-             : "";
-}
-#endif
-
 /*
  * Lock or unlock an item.  "deep" is nr of levels to go.
  * When "check_refcount" is TRUE do not lock a list or dict with a reference
@@ -2319,7 +2295,7 @@ item_lock(typval_T *tv, int deep, int lock, int check_refcount)
     int                todo;
 
 #ifdef LOG_LOCKVAR
-    ch_log(NULL, "LKVAR: item_lock(): type %s", vartype_tostring(tv->v_type));
+    ch_log(NULL, "LKVAR: item_lock(): type %s", vartype_name(tv->v_type));
 #endif
 
     if (recurse >= DICT_MAXNEST)
index 993783957fb3475bd84184d69bb808e7220bb176..d54e3118371e22c511a26e89860877d3e0ee20b8 100644 (file)
@@ -1953,8 +1953,7 @@ EXTERN int  timer_busy INIT(= 0);   // when timer is inside vgetc() then > 0
 #ifdef FEAT_EVAL
 EXTERN int  input_busy INIT(= 0);   // when inside get_user_input() then > 0
 
-EXTERN typval_T        *lval_root INIT(= NULL);
-EXTERN int     lval_root_is_arg INIT(= 0);
+EXTERN lval_root_T     *lval_root INIT(= NULL);
 #endif
 
 #ifdef FEAT_BEVAL_TERM
index c7360a30cc6ba79a62e5d81fd73938cb85c4f591..680ed6dbd838eeba1d6cacf4b7d18c4c86ad9212 100644 (file)
@@ -4595,6 +4595,16 @@ typedef struct lval_S
                                // ignore the rest.
 } lval_T;
 
+/**
+ * This may be used to specify the base type that get_lval() uses when
+ * following a chain, for example a[idx1][idx2].
+ */
+typedef struct lval_root_S {
+    typval_T   *lr_tv;
+    class_T    *lr_cl_exec;    // executing class for access checking
+    int                lr_is_arg;
+} lval_root_T;
+
 // Structure used to save the current state.  Used when executing Normal mode
 // commands while in any other mode.
 typedef struct {
index 2aa5c08339e4af863e9a7a4711e4d1e6c5267adb..3226de1c65944ea637f5c8c87976ec51129b75e9 100644 (file)
@@ -626,7 +626,7 @@ def Test_member_any_used_as_object()
     var outer_obj = Outer.new(inner_obj)
     F(outer_obj)
   END
-  v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable: _value', 1)
+  v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_value" in class "Inner"', 1)
 
   # Try modifying a non-existing variable using an "any" object
   lines =<< trim END
@@ -1063,9 +1063,9 @@ def Test_instance_variable_access()
     assert_equal(1, trip.GetOne())
     assert_equal(2, trip.two)
     assert_equal(3, trip.three)
-    assert_fails('echo trip._one', 'E1333: Cannot access private variable: _one')
+    assert_fails('echo trip._one', 'E1333: Cannot access private variable "_one" in class "Triple"')
 
-    assert_fails('trip._one = 11', 'E1333: Cannot access private variable: _one')
+    assert_fails('trip._one = 11', 'E1333: Cannot access private variable "_one" in class "Triple"')
     assert_fails('trip.two = 22', 'E1335: Variable "two" in class "Triple" is not writable')
     trip.three = 33
     assert_equal(33, trip.three)
@@ -1315,7 +1315,7 @@ def Test_class_variable_access()
     var b = B.new()
     b.Foo()
   END
-  v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable: _priv_class_var', 1)
+  v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_priv_class_var" in class "A"', 1)
 
   # A private class variable cannot be modified from a child class
   lines =<< trim END
@@ -1333,7 +1333,7 @@ def Test_class_variable_access()
     var b = B.new()
     b.Foo()
   END
-  v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable: _priv_class_var', 1)
+  v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_priv_class_var" in class "A"', 1)
 
   # Access from child class extending a class and from script context
   lines =<< trim END
@@ -1514,8 +1514,8 @@ def Test_class_member()
     assert_fails('TextPos.counter = 5', 'E1335: Variable "counter" in class "TextPos" is not writable')
     assert_fails('TextPos.counter += 5', 'E1335: Variable "counter" in class "TextPos" is not writable')
 
-    assert_fails('echo TextPos._secret', 'E1333: Cannot access private variable: _secret')
-    assert_fails('TextPos._secret = 8', 'E1333: Cannot access private variable: _secret')
+    assert_fails('echo TextPos._secret', 'E1333: Cannot access private variable "_secret" in class "TextPos"')
+    assert_fails('TextPos._secret = 8', 'E1333: Cannot access private variable "_secret" in class "TextPos"')
 
     assert_equal(42, TextPos.anybody)
     TextPos.anybody = 12
@@ -3522,9 +3522,6 @@ def Test_lockvar_object_variable()
   # method arg, static method arg.
   # Also different depths
 
-  # TODO: handle inside_class in vim9class
-  # lockvar of a read-only currently fails even if inside
-
   #
   # lockvar of read-only object variable
   #
@@ -3542,8 +3539,7 @@ def Test_lockvar_object_variable()
     var o = C.new(3)
     o.Lock()
   END
-  # TODO: wrong error
-  v9.CheckSourceFailure(lines, 'E1335: Variable "val1" in class "C" is not writable')
+  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"')
 
   # read-only lockvar from scriptlevel
   lines =<< trim END
@@ -3602,8 +3598,7 @@ def Test_lockvar_object_variable()
     var o = C.new(3)
     o.Lock(C.new(5))
   END
-  # TODO: wrong error, tricky since type "any"
-  v9.CheckSourceFailure(lines, 'E1335: Variable "val5" in class "C" is not writable')
+  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o_any.val5" in class "C"')
 
   # read-only lockvar from class method arg
   lines =<< trim END
@@ -3618,8 +3613,7 @@ def Test_lockvar_object_variable()
     var o = C.new(3)
     C.Lock(o)
   END
-  # TODO: wrong error, tricky since type "any"
-  v9.CheckSourceFailure(lines, 'E1335: Variable "val6" in class "C" is not writable')
+  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o_any.val6" in class "C"')
 
   #
   # lockvar of public object variable
@@ -3987,6 +3981,71 @@ def Test_lockvar_general()
     assert_equal([ [9], [2], [8] ], o.val)
   END
   v9.CheckSourceSuccess(lines)
+
+  # lock a script level variable from an object method
+  lines =<< trim END
+    vim9script
+
+    class C
+      def Lock()
+        lockvar l
+      enddef
+    endclass
+
+    var l = [1]
+    C.new().Lock()
+    l[0] = 11
+  END
+  v9.CheckSourceFailure(lines, 'E741: Value is locked: l[0] = 11', 11)
+
+  # lock a list element referenced by a private object variable
+  # in an object fetched via a script level list
+  lines =<< trim END
+    vim9script
+
+    class C
+      this._v1: list<list<number>>
+      def Lock()
+        lockvar lc[0]._v1[1]
+      enddef
+    endclass
+
+    var l = [[1], [2], [3]]
+    var o = C.new(l)
+    var lc: list<C> = [ o ]
+
+    o.Lock()
+    l[0] = [22]
+    l[1] = [33]
+  END
+  v9.CheckSourceFailure(lines, 'E741: Value is locked: l[1] = [33]', 16)
+
+  # similar to the previous test, except the locking code is executing
+  # in a class that does not own the private variable.
+  # Note that the locking code is in a class has a private variable of
+  # the same name.
+  lines =<< trim END
+    vim9script
+
+    class C2
+      this._v1: list<list<number>>
+      def Lock(obj: any)
+        lockvar lc[0]._v1[1]
+      enddef
+    endclass
+
+    class C
+      this._v1: list<list<number>>
+    endclass
+
+    var l = [[1], [2], [3]]
+    var o = C.new(l)
+    var lc: list<C> = [ o ]
+
+    var o2 = C2.new()
+    o2.Lock(o)
+  END
+  v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_v1" in class "C"')
 enddef
 
 " Test for a private object method
@@ -4518,7 +4577,7 @@ def Test_static_inheritence()
     assert_equal(102, ob.AccessObject())
     assert_equal(103, oc.AccessObject())
 
-    assert_fails('echo oc.AccessPrivateStaticThroughClassName()', 'E1333: Cannot access private variable: _svar')
+    assert_fails('echo oc.AccessPrivateStaticThroughClassName()', 'E1333: Cannot access private variable "_svar" in class "A"')
 
     # verify object properly resolves to correct static
     assert_equal(1, oa.AccessStaticThroughObject())
@@ -4699,7 +4758,7 @@ def Test_private_member_access_outside_class()
     enddef
     T()
   END
-  v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable: _val', 2)
+  v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_val" in class "A"', 2)
 
   # access a non-existing private object member variable
   lines =<< trim END
@@ -4754,7 +4813,7 @@ def Test_private_member_access_outside_class()
     enddef
     T()
   END
-  v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable: _val', 1)
+  v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_val" in class "A"', 1)
 
   # private static class variable
   lines =<< trim END
@@ -4767,7 +4826,7 @@ def Test_private_member_access_outside_class()
     enddef
     T()
   END
-  v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable: _val', 1)
+  v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_val" in class "A"', 1)
 enddef
 
 " Test for changing the member access of an interface in a implementation class
@@ -4828,7 +4887,7 @@ def Test_modify_class_member_from_def_function()
       A.var3 = {c: 3, d: 4}
       assert_equal([3, 4], A.var2)
       assert_equal({c: 3, d: 4}, A.var3)
-      assert_fails('echo A._priv_var4', 'E1333: Cannot access private variable: _priv_var4')
+      assert_fails('echo A._priv_var4', 'E1333: Cannot access private variable "_priv_var4" in class "A"')
     enddef
     T()
   END
index 15e87d62af2ea0e67735cf2322c8146b687d1b8a..7fdb1e621d96502de4ef15b07056879d637b4ad1 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1982,
 /**/
     1981,
 /**/
index 320e35bde0d1c8e731a34238de726afbc889a838..785486963a6d89989187d169c86b294a8857df83 100644 (file)
@@ -508,8 +508,9 @@ typedef struct {
 
 // arguments to ISN_LOCKUNLOCK
 typedef struct {
-    char_u     *string;        // for exec_command
-    int                is_arg;         // is lval_root a function arg
+    char_u     *lu_string;     // for exec_command
+    class_T    *lu_cl_exec;    // executing, null if not class/obj method
+    int                lu_is_arg;      // is lval_root a function arg
 } lockunlock_T;
 
 /*
index 05d9afcf292b854fc16bb301901b8158049f0c00..ce231600442fea73ae7b315dd5ee043091d493a7 100644 (file)
@@ -2172,7 +2172,8 @@ get_member_tv(
 
     if (*name == '_')
     {
-       semsg(_(e_cannot_access_private_variable_str), m->ocm_name);
+       semsg(_(e_cannot_access_private_variable_str), m->ocm_name,
+                                                           cl->class_name);
        return FAIL;
     }
 
@@ -2331,7 +2332,8 @@ class_object_index(
 
        if (*name == '_')
        {
-           semsg(_(e_cannot_access_private_variable_str), m->ocm_name);
+           semsg(_(e_cannot_access_private_variable_str), m->ocm_name,
+                                                           cl->class_name);
            return FAIL;
        }
 
index 0d6dc01f9f46b39040ed1dbfef416065c0aa3862..0be207795fce6e84c1bca069d7ad5e6d8b456eb5 100644 (file)
@@ -212,11 +212,20 @@ compile_lock_unlock(
     if (p[1] != ':')
     {
        char_u *end = find_name_end(p, NULL, NULL, FNE_CHECK_START);
-       // If name is is locally accessible, except for local var,
+
+       // The most important point is that something like
+       // name[idx].member... needs to be resolved at runtime, get_lval(),
+       // starting from the root "name".
+
+       // These checks are reminiscent of the variable_exists function.
+       // But most of the matches require special handling.
+
+       // If bare name is is locally accessible, except for local var,
        // then put it on the stack to use with ISN_LOCKUNLOCK.
        // This could be v.memb, v[idx_key]; bare class variable,
-       // function arg. The local variable on the stack, will be passed
-       // to ex_lockvar() indirectly.
+       // function arg. The item on the stack, will be passed
+       // to ex_lockvar() indirectly and be used as the root for get_lval.
+       // A bare script variable name needs no special handling.
 
        char_u  *name = NULL;
        int     len = end - p;
@@ -233,7 +242,7 @@ compile_lock_unlock(
            // Push the local on the stack, could be "this".
            name = p;
 #ifdef LOG_LOCKVAR
-           ch_log(NULL, "LKVAR: compile... lookup_local: name %s", name);
+           ch_log(NULL, "LKVAR:    ... lookup_local: name %s", name);
 #endif
        }
        if (name == NULL)
@@ -247,7 +256,7 @@ compile_lock_unlock(
                    name = cl->class_name;
                    len = STRLEN(name);
 #ifdef LOG_LOCKVAR
-                   ch_log(NULL, "LKVAR: compile... cctx_class_member: name %s",
+                   ch_log(NULL, "LKVAR:    ... cctx_class_member: name %s",
                           name);
 #endif
                }
@@ -255,23 +264,33 @@ compile_lock_unlock(
        }
        if (name == NULL)
        {
-           int     idx;
-           type_T  *type;
            // Can lockvar any function arg.
-           // TODO: test arg[idx]/arg.member
-           if (arg_exists(p, len, &idx, &type, NULL, cctx) == OK)
+           if (arg_exists(p, len, NULL, NULL, NULL, cctx) == OK)
            {
                name = p;
                is_arg = TRUE;
 #ifdef LOG_LOCKVAR
-               ch_log(NULL, "LKVAR: compile... arg_exists: name %s", name);
+               ch_log(NULL, "LKVAR:    ... arg_exists: name %s", name);
+#endif
+           }
+       }
+       if (name == NULL)
+       {
+           // No special handling for a bare script variable; but
+           // if followed by '[' or '.', it's a root for get_lval().
+           if (script_var_exists(p, len, cctx, NULL) == OK
+               && (*end == '.' || *end == '['))
+           {
+               name = p;
+#ifdef LOG_LOCKVAR
+               ch_log(NULL, "LKVAR:    ... script_var_exists: name %s", name);
 #endif
            }
        }
        if (name != NULL)
        {
 #ifdef LOG_LOCKVAR
-           ch_log(NULL, "LKVAR: compile... INS_LOCKUNLOCK %s", name);
+           ch_log(NULL, "LKVAR:    ... INS_LOCKUNLOCK %s", name);
 #endif
            if (compile_load(&name, name + len, cctx, FALSE, FALSE) == FAIL)
                return FAIL;
@@ -292,7 +311,7 @@ compile_lock_unlock(
        else
            vim_snprintf((char *)buf, len, "%s %d %s", cmd, deep, p);
 #ifdef LOG_LOCKVAR
-       ch_log(NULL, "LKVAR: compile... buf %s", buf);
+       ch_log(NULL, "LKVAR:    ... buf %s", buf);
 #endif
        if (isn == ISN_LOCKUNLOCK)
            ret = generate_LOCKUNLOCK(cctx, buf, is_arg);
index 209f86fb5303752ad1ff77df211dbf2a47053ec8..a899745207a7ed1ee822b1e8266015f0472ddb53 100644 (file)
@@ -2180,7 +2180,8 @@ execute_storeindex(isn_T *iptr, ectx_T *ectx)
            {
                if (*member == '_')
                {
-                   semsg(_(e_cannot_access_private_variable_str), m->ocm_name);
+                   semsg(_(e_cannot_access_private_variable_str),
+                                               m->ocm_name, cl->class_name);
                    status = FAIL;
                }
 
@@ -4178,9 +4179,7 @@ exec_instructions(ectx_T *ectx)
 
            case ISN_LOCKUNLOCK:
                {
-                   // TODO: could put lval_root info in struct
-                   typval_T    *lval_root_save = lval_root;
-                   int         lval_root_is_arg_save = lval_root_is_arg;
+                   lval_root_T *lval_root_save = lval_root;
                    int         res;
 #ifdef LOG_LOCKVAR
                    ch_log(NULL, "LKVAR: execute INS_LOCKUNLOCK isn_arg %s",
@@ -4190,12 +4189,14 @@ exec_instructions(ectx_T *ectx)
                    // 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);
-                   lval_root_is_arg = iptr->isn_arg.lockunlock.is_arg;
-                   res = exec_command(iptr, iptr->isn_arg.lockunlock.string);
-                   clear_tv(lval_root);
+                   lval_root_T root = { STACK_TV_BOT(0),
+                                       iptr->isn_arg.lockunlock.lu_cl_exec,
+                                       iptr->isn_arg.lockunlock.lu_is_arg };
+                   lval_root = &root;
+                   res = exec_command(iptr,
+                                       iptr->isn_arg.lockunlock.lu_string);
+                   clear_tv(root.lr_tv);
                    lval_root = lval_root_save;
-                   lval_root_is_arg = lval_root_is_arg_save;
                    if (res == FAIL)
                        goto on_error;
                }
index 5a302d68843f596fb8eb62371c52d13d727f836a..05eb5241801a3eedccb5440909dbec7c17d9fa3f 100644 (file)
@@ -407,7 +407,8 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
        {
            if (*name == '_' && !inside_class(cctx, cl))
            {
-               semsg(_(e_cannot_access_private_variable_str), m->ocm_name);
+               semsg(_(e_cannot_access_private_variable_str), m->ocm_name,
+                                                           cl->class_name);
                return FAIL;
            }
 
@@ -442,7 +443,8 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
            // it is defined.
            if (*name == '_' && cctx->ctx_ufunc->uf_class != cl)
            {
-               semsg(_(e_cannot_access_private_variable_str), m->ocm_name);
+               semsg(_(e_cannot_access_private_variable_str), m->ocm_name,
+                                                           cl->class_name);
                return FAIL;
            }
 
index 697c83d8ae6cb3dfd9fd2f64b46e09eed7038a8c..6de29b617f0c27a6df7799f3844ad179841eae84 100644 (file)
@@ -2170,7 +2170,11 @@ generate_PUT(cctx_T *cctx, int regname, linenr_T lnum)
 }
 
 /*
- * Generate an EXEC instruction that takes a string argument.
+ * Generate a LOCKUNLOCK instruction.The root item, where the indexing starts
+ * to find the variable, is on the stack. The instr takes
+ * - the string to parse, "root.b[idx1][idx2].d.val", to find the variable
+ * - the class, if any, in which the string executes.
+ * - if the root item is a function argument
  * A copy is made of "line".
  */
     int
@@ -2181,8 +2185,12 @@ generate_LOCKUNLOCK(cctx_T *cctx, char_u *line, int is_arg)
     RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_LOCKUNLOCK)) == NULL)
        return FAIL;
-    isn->isn_arg.lockunlock.string = vim_strsave(line);
-    isn->isn_arg.lockunlock.is_arg = is_arg;
+    class_T *cl = cctx->ctx_ufunc != NULL ? cctx->ctx_ufunc->uf_class : NULL;
+    isn->isn_arg.lockunlock.lu_string = vim_strsave(line);
+    isn->isn_arg.lockunlock.lu_cl_exec = cl;
+    if (cl != NULL)
+       ++cl->class_refcount;
+    isn->isn_arg.lockunlock.lu_is_arg = is_arg;
     return OK;
 }
 
@@ -2490,7 +2498,6 @@ 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:
@@ -2505,6 +2512,11 @@ delete_instr(isn_T *isn)
            vim_free(isn->isn_arg.string);
            break;
 
+       case ISN_LOCKUNLOCK:
+           class_unref(isn->isn_arg.lockunlock.lu_cl_exec);
+           vim_free(isn->isn_arg.lockunlock.lu_string);
+           break;
+
        case ISN_SUBSTITUTE:
            {
                int     idx;