diff options
author | Ernie Rael <errael@raelity.com> | 2023-09-29 19:53:55 +0200 |
---|---|---|
committer | Christian Brabandt <cb@256bit.org> | 2023-09-29 19:53:55 +0200 |
commit | ee865f37acab6cac2cee6a171d60e1b365f852b0 (patch) | |
tree | 37522d42aa745a1b355a71efc979651e7500f9b2 /src/evalvars.c | |
parent | 112431f21762bdcb3ee4ce2d7d8f91da07f3bf71 (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.c | 83 |
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)); |