patch 9.0.1955: Vim9: lockvar issues with objects/classes
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>
diff --git a/src/evalvars.c b/src/evalvars.c
index 2438993..fc977c6 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -2123,6 +2123,30 @@
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 @@
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 @@
}
*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 @@
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 @@
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));