summaryrefslogtreecommitdiffstats
path: root/src/evalvars.c
diff options
context:
space:
mode:
authorErnie Rael <errael@raelity.com>2023-09-29 19:53:55 +0200
committerChristian Brabandt <cb@256bit.org>2023-09-29 19:53:55 +0200
commitee865f37acab6cac2cee6a171d60e1b365f852b0 (patch)
tree37522d42aa745a1b355a71efc979651e7500f9b2 /src/evalvars.c
parent112431f21762bdcb3ee4ce2d7d8f91da07f3bf71 (diff)
patch 9.0.1955: Vim9: lockvar issues with objects/classesv9.0.1955
Problem: Vim9: lockvar issues with objects/classes Solution: fix `get_lhs()` object/class access and avoid `SEGV`, make error messages more accurate. - `get_lval()` detects/returns object/class access - `compile_lock_unlock()` generate code for bare static and obj_arg access - `do_lock_var()` check lval for `ll_object`/`ll_class` and fail if so. Details: - Add `ll_object`/`ll_class`/`ll_oi` to `lval_T`. - Add `lockunlock_T` to `isn_T` for `is_arg` to specify handling of `lval_root` in `get_lval()`. - In `get_lval()`, fill in `ll_object`/`ll_class`/`ll_oi` as needed; when no `[idx] or .key`, check lval_root on the way out. - In `do_lock_var()` check for `ll_object`/`ll_class`; also bullet proof ll_dict case and give `Dictionay required` if problem. (not needed to avoid lockvar crash anymore) - In `compile_lock_unlock()` compile for the class variable and func arg cases. closes: #13174 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Ernie Rael <errael@raelity.com>
Diffstat (limited to 'src/evalvars.c')
-rw-r--r--src/evalvars.c83
1 files changed, 81 insertions, 2 deletions
diff --git a/src/evalvars.c b/src/evalvars.c
index 2438993942..fc977c66ae 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -2123,6 +2123,30 @@ do_unlet(char_u *name, int forceit)
return FAIL;
}
+ static void
+report_lockvar_member(char *msg, lval_T *lp)
+{
+ int did_alloc = FALSE;
+ char_u *vname = (char_u *)"";
+ char_u *class_name = lp->ll_class != NULL
+ ? lp->ll_class->class_name : (char_u *)"";
+ if (lp->ll_name != NULL)
+ {
+ if (lp->ll_name_end == NULL)
+ vname = lp->ll_name;
+ else
+ {
+ vname = vim_strnsave(lp->ll_name, lp->ll_name_end - lp->ll_name);
+ if (vname == NULL)
+ return;
+ did_alloc = TRUE;
+ }
+ }
+ semsg(_(msg), vname, class_name);
+ if (did_alloc)
+ vim_free(vname);
+}
+
/*
* Lock or unlock variable indicated by "lp".
* "deep" is the levels to go (-1 for unlimited);
@@ -2141,6 +2165,10 @@ do_lock_var(
int cc;
dictitem_T *di;
+#ifdef LOG_LOCKVAR
+ ch_log(NULL, "LKVAR: do_lock_var(): name %s, is_root %d", lp->ll_name, lp->ll_is_root);
+#endif
+
if (lp->ll_tv == NULL)
{
cc = *name_end;
@@ -2201,10 +2229,13 @@ do_lock_var(
}
*name_end = cc;
}
- else if (deep == 0)
+ else if (deep == 0 && lp->ll_object == NULL && lp->ll_class == NULL)
{
// nothing to do
}
+ else if (lp->ll_is_root)
+ // (un)lock the item.
+ item_lock(lp->ll_tv, deep, lock, FALSE);
else if (lp->ll_range)
{
listitem_T *li = lp->ll_li;
@@ -2220,13 +2251,57 @@ do_lock_var(
else if (lp->ll_list != NULL)
// (un)lock a List item.
item_lock(&lp->ll_li->li_tv, deep, lock, FALSE);
+ else if (lp->ll_object != NULL) // This check must be before ll_class.
+ {
+ // (un)lock an object variable.
+ report_lockvar_member(e_cannot_lock_object_variable_str, lp);
+ ret = FAIL;
+ }
+ else if (lp->ll_class != NULL)
+ {
+ // (un)lock a class variable.
+ report_lockvar_member(e_cannot_lock_class_variable_str, lp);
+ ret = FAIL;
+ }
else
+ {
// (un)lock a Dictionary item.
- item_lock(&lp->ll_di->di_tv, deep, lock, FALSE);
+ if (lp->ll_di == NULL)
+ {
+ emsg(_(e_dictionary_required));
+ ret = FAIL;
+ }
+ else
+ item_lock(&lp->ll_di->di_tv, deep, lock, FALSE);
+ }
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
@@ -2243,6 +2318,10 @@ item_lock(typval_T *tv, int deep, int lock, int check_refcount)
hashitem_T *hi;
int todo;
+#ifdef LOG_LOCKVAR
+ ch_log(NULL, "LKVAR: item_lock(): type %s", vartype_tostring(tv->v_type));
+#endif
+
if (recurse >= DICT_MAXNEST)
{
emsg(_(e_variable_nested_too_deep_for_unlock));