# HG changeset patch # User Christian Brabandt # Date 1696709709 -7200 # Node ID f72004b37b2ba93800757a68658f9f33b63eeddd # Parent 4c2e97be0fbeddc64e8dd06705ca821947852899 patch 9.0.2001: Vim9: segfault with islocked() Commit: https://github.com/vim/vim/commit/9771b2a67f825bdc6e5c615141d22c665952dc86 Author: Ernie Rael Date: Sat Oct 7 22:05:40 2023 +0200 patch 9.0.2001: Vim9: segfault with islocked() 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 Co-authored-by: Ernie Rael diff --git a/src/eval.c b/src/eval.c --- 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 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -7347,6 +7347,22 @@ f_islocked(typval_T *argvars, typval_T * || 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 --- 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 --- 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> = [ [0], [1], [2]] + this.o1: list> = [[10], [11], [12]] + static c0: list> = [[20], [21], [22]] + static c1: list> = [[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 --- 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,