summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErnie Rael <errael@raelity.com>2023-10-07 22:05:40 +0200
committerChristian Brabandt <cb@256bit.org>2023-10-07 22:10:26 +0200
commit9771b2a67f825bdc6e5c615141d22c665952dc86 (patch)
tree3197766895366d392c92819ddf7e811c5325c317
parent1087b8c29ab521106c5b6cc85d5b38244f0d9c1d (diff)
patch 9.0.2001: Vim9: segfault with islocked()v9.0.2001
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>
-rw-r--r--src/eval.c6
-rw-r--r--src/evalfunc.c16
-rw-r--r--src/structs.h17
-rw-r--r--src/testdir/test_vim9_class.vim80
-rw-r--r--src/version.c2
5 files changed, 112 insertions, 9 deletions
diff --git a/src/eval.c b/src/eval.c
index d9fbec234c..93109effb9 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -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;
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 501ee03582..b840220bd0 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -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)
diff --git a/src/structs.h b/src/structs.h
index 680ed6dbd8..ee688848e7 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -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;
/**
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index 0e70c9f6fe..555c46cc8e 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -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)
diff --git a/src/version.c b/src/version.c
index 5d1c1c9441..e408dc1f71 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 2001,
+/**/
2000,
/**/
1999,