]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.2001: Vim9: segfault with islocked() v9.0.2001
authorErnie Rael <errael@raelity.com>
Sat, 7 Oct 2023 20:05:40 +0000 (22:05 +0200)
committerChristian Brabandt <cb@256bit.org>
Sat, 7 Oct 2023 20:10:26 +0000 (22:10 +0200)
Problem:  Vim9: segfault with islocked()
Solution: Check that the lval pointer is not null for objects and
          class variables

closes: #13295

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Ernie Rael <errael@raelity.com>
src/eval.c
src/evalfunc.c
src/structs.h
src/testdir/test_vim9_class.vim
src/version.c

index d9fbec234cba753a4cc697175466094e269e4467..93109effb92eb378175395dd938c4923c755be1b 100644 (file)
@@ -1375,6 +1375,7 @@ get_lval(
                      && v_type != VAR_OBJECT
                      && v_type != VAR_CLASS)
        {
+           // TODO: have a message with obj/class, not just dict,
            if (!quiet)
                semsg(_(e_dot_can_only_be_used_on_dictionary_str), name);
            return NULL;
@@ -1385,6 +1386,7 @@ get_lval(
                && v_type != VAR_OBJECT
                && v_type != VAR_CLASS)
        {
+           // TODO: have a message with obj/class, not just dict/list/blob,
            if (!quiet)
                emsg(_(e_can_only_index_list_dictionary_or_blob));
            return NULL;
@@ -1739,10 +1741,6 @@ get_lval(
                    }
                }
 
-               // TODO: dont' check access if inside class
-               // TODO: is GLV_READ_ONLY the right thing to use
-               //           for class/object member access?
-               //           Probably in some cases. Need inside class check
                if (lp->ll_valtype == NULL)
                {
                    int         m_idx;
index 501ee03582fa737f5cecac2ede0a0a2a76d230c4..b840220bd0cf49c2b71c9c466025fcf82d237abb 100644 (file)
@@ -7347,6 +7347,22 @@ f_islocked(typval_T *argvars, typval_T *rettv)
                                                   || tv_islocked(&di->di_tv));
                }
            }
+           else if (lv.ll_object != NULL)
+           {
+               typval_T *tv = ((typval_T *)(lv.ll_object + 1)) + lv.ll_oi;
+               rettv->vval.v_number = tv_islocked(tv);
+#ifdef LOG_LOCKVAR
+               ch_log(NULL, "LKVAR: f_islocked(): name %s (obj)", lv.ll_name);
+#endif
+           }
+           else if (lv.ll_class != NULL)
+           {
+               typval_T *tv = &lv.ll_class->class_members_tv[lv.ll_oi];
+               rettv->vval.v_number = tv_islocked(tv);
+#ifdef LOG_LOCKVAR
+               ch_log(NULL, "LKVAR: f_islocked(): name %s (cl)", lv.ll_name);
+#endif
+           }
            else if (lv.ll_range)
                emsg(_(e_range_not_allowed));
            else if (lv.ll_newkey != NULL)
index 680ed6dbd838eeba1d6cacf4b7d18c4c86ad9212..ee688848e74804aeadf45f6c2e0879054369be87 100644 (file)
@@ -4547,11 +4547,18 @@ typedef struct
  *     "tv"        points to the (first) list item value
  *     "li"        points to the (first) list item
  *     "range", "n1", "n2" and "empty2" indicate what items are used.
- * For a member in a class/object: TODO: verify fields
+ * For a plain class or object:
+ *     "name"      points to the variable name.
+ *     "exp_name"  is NULL.
+ *     "tv"        points to the variable
+ *     "is_root"   TRUE
+ * For a variable in a class/object: (class is not NULL)
  *     "name"      points to the (expanded) variable name.
  *     "exp_name"  NULL or non-NULL, to be freed later.
- *     "tv"        points to the (first) list item value
- *     "oi"        index into member array, see _type to determine which array
+ *     "tv"        May point to class/object variable.
+ *     "object"    object containing variable, NULL if class variable
+ *     "class"     class of object or class containing variable
+ *     "oi"        index into class/object of tv
  * For an existing Dict item:
  *     "name"      points to the (expanded) variable name.
  *     "exp_name"  NULL or non-NULL, to be freed later.
@@ -4591,8 +4598,8 @@ typedef struct lval_S
     object_T   *ll_object;     // The object or NULL, class is not NULL
     class_T    *ll_class;      // The class or NULL, object may be NULL
     int                ll_oi;          // The object/class member index
-    int                ll_is_root;     // Special case. ll_tv is lval_root,
-                               // ignore the rest.
+    int                ll_is_root;     // TRUE if ll_tv is the lval_root, like a
+                               // plain object/class. ll_tv is variable.
 } lval_T;
 
 /**
index 0e70c9f6fe3ef076d8e830d450a477f93f8e9af1..555c46cc8e867f0d7a16a442a1ae2543714b971f 100644 (file)
@@ -4161,6 +4161,86 @@ def Test_lockvar_general()
   v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_v1" in class "C"')
 enddef
 
+" Test builtin islocked()
+def Test_lockvar_islocked()
+  # Can't lock class/object variable
+  # Lock class/object variable's value
+  # Lock item of variabl's value (a list item)
+  # varible is at index 1 within class/object
+  var lines =<< trim END
+    vim9script
+
+    class C
+      this.o0: list<list<number>> = [ [0],  [1],  [2]]
+      this.o1: list<list<number>> = [[10], [11], [12]]
+      static c0: list<list<number>> = [[20], [21], [22]]
+      static c1: list<list<number>> = [[30], [31], [32]]
+    endclass
+
+    def LockIt(arg: any)
+      lockvar arg
+    enddef
+
+    def UnlockIt(arg: any)
+      unlockvar arg
+    enddef
+
+    var obj = C.new()
+    #lockvar obj.o1         # can't lock something you can't write to
+
+    try
+      lockvar obj.o1         # can't lock something you can't write to
+      call assert_false(1, '"lockvar obj.o1" should have failed')
+    catch
+      call assert_exception('E1335:')
+    endtry
+
+    LockIt(obj.o1)         # but can lock it's value
+    assert_equal(1, islocked("obj.o1"))
+    assert_equal(1, islocked("obj.o1[0]"))
+    assert_equal(1, islocked("obj.o1[1]"))
+    UnlockIt(obj.o1)
+    assert_equal(0, islocked("obj.o1"))
+    assert_equal(0, islocked("obj.o1[0]"))
+
+    lockvar obj.o1[0]
+    assert_equal(0, islocked("obj.o1"))
+    assert_equal(1, islocked("obj.o1[0]"))
+    assert_equal(0, islocked("obj.o1[1]"))
+    unlockvar obj.o1[0]
+    assert_equal(0, islocked("obj.o1"))
+    assert_equal(0, islocked("obj.o1[0]"))
+
+    # Same thing, but with a static
+
+    try
+      lockvar C.c1         # can't lock something you can't write to
+      call assert_false(1, '"lockvar C.c1" should have failed')
+    catch
+      call assert_exception('E1335:')
+    endtry
+
+    LockIt(C.c1)         # but can lock it's value
+    assert_equal(1, islocked("C.c1"))
+    assert_equal(1, islocked("C.c1[0]"))
+    assert_equal(1, islocked("C.c1[1]"))
+    UnlockIt(C.c1)
+    assert_equal(0, islocked("C.c1"))
+    assert_equal(0, islocked("C.c1[0]"))
+
+    lockvar C.c1[0]
+    assert_equal(0, islocked("C.c1"))
+    assert_equal(1, islocked("C.c1[0]"))
+    assert_equal(0, islocked("C.c1[1]"))
+    unlockvar C.c1[0]
+    assert_equal(0, islocked("C.c1"))
+    assert_equal(0, islocked("C.c1[0]"))
+  END
+  v9.CheckSourceSuccess(lines)
+  lines =<< trim END
+  END
+enddef
+
 " Test for a private object method
 def Test_private_object_method()
   # Try calling a private method using an object (at the script level)
index 5d1c1c9441ccc2b2bb941725148b3a8986862ed7..e408dc1f71db2a43b6853c5cf2b7e1745b45b90a 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2001,
 /**/
     2000,
 /**/