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/eval.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/eval.c')
-rw-r--r-- | src/eval.c | 101 |
1 files changed, 94 insertions, 7 deletions
diff --git a/src/eval.c b/src/eval.c index 5edda68206..ae48a6085d 100644 --- a/src/eval.c +++ b/src/eval.c @@ -986,6 +986,62 @@ eval_foldexpr(win_T *wp, int *cp) #endif /* + * Fill in "lp" using "root". This is used in a special case when + * "get_lval()" parses a bare word when "lval_root" is not NULL. + * + * This is typically called with "lval_root" as "root". For a class, find + * the name from lp in the class from root, fill in lval_T if found. For a + * complex type, list/dict use it as the result; just put the root into + * ll_tv. + * + * "lval_root" is a hack used during run-time/instr-execution to provide the + * starting point for "get_lval()" to traverse a chain of indexes. In some + * cases get_lval sees a bare name and uses this function to populate the + * lval_T. + * + * For setting up "lval_root" (currently only used with lockvar) + * compile_lock_unlock - pushes object on stack (which becomes lval_root) + * execute_instructions: ISN_LOCKUNLOCK - sets lval_root from stack. + */ + static void +get_lval_root(lval_T *lp, typval_T *root, int is_arg) +{ +#ifdef LOG_LOCKVAR + ch_log(NULL, "LKVAR: get_lvalroot(): name %s", lp->ll_name); +#endif + if (!is_arg && root->v_type == VAR_CLASS) + { + if (root->vval.v_class != NULL) + { + // Special special case. Look for a bare class variable reference. + class_T *cl = root->vval.v_class; + int m_idx; + ocmember_T *m = class_member_lookup(cl, lp->ll_name, + lp->ll_name_end - lp->ll_name, &m_idx); + if (m != NULL) + { + // Assuming "inside class" since bare reference. + lp->ll_class = root->vval.v_class; + lp->ll_oi = m_idx; + lp->ll_valtype = m->ocm_type; + lp->ll_tv = &lp->ll_class->class_members_tv[m_idx]; +#ifdef LOG_LOCKVAR + ch_log(NULL, "LKVAR: get_lvalroot() class member: name %s", + lp->ll_name); +#endif + return; + } + } + } + +#ifdef LOG_LOCKVAR + ch_log(NULL, "LKVAR: get_lvalroot() any type"); +#endif + lp->ll_tv = root; + lp->ll_is_root = TRUE; +} + +/* * Get an lval: variable, Dict item or List item that can be assigned a value * to: "name", "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]", * "name.key", "name.key[expr]" etc. @@ -1028,6 +1084,11 @@ get_lval( int writing = 0; int vim9script = in_vim9script(); +#ifdef LOG_LOCKVAR + ch_log(NULL, "LKVAR: get_lval(): name %s, lval_root %p", + name, (void*)lval_root); +#endif + // Clear everything in "lp". CLEAR_POINTER(lp); @@ -1122,6 +1183,7 @@ get_lval( return NULL; lp->ll_name_end = tp; } + // TODO: check inside class? } } if (lp->ll_name == NULL) @@ -1157,7 +1219,11 @@ get_lval( // Without [idx] or .key we are done. if ((*p != '[' && *p != '.')) + { + if (lval_root != NULL) + get_lval_root(lp, lval_root, lval_root_is_arg); return p; + } if (vim9script && lval_root != NULL) { @@ -1350,6 +1416,8 @@ get_lval( } } lp->ll_list = NULL; + lp->ll_object = NULL; + lp->ll_class = NULL; // a NULL dict is equivalent with an empty dict if (lp->ll_tv->vval.v_dict == NULL) @@ -1482,6 +1550,8 @@ get_lval( clear_tv(&var1); lp->ll_dict = NULL; + lp->ll_object = NULL; + lp->ll_class = NULL; lp->ll_list = lp->ll_tv->vval.v_list; lp->ll_li = check_range_index_one(lp->ll_list, &lp->ll_n1, (flags & GLV_ASSIGN_WITH_OP) == 0, quiet); @@ -1516,10 +1586,22 @@ get_lval( } else // v_type == VAR_CLASS || v_type == VAR_OBJECT { - class_T *cl = (v_type == VAR_OBJECT - && lp->ll_tv->vval.v_object != NULL) - ? lp->ll_tv->vval.v_object->obj_class - : lp->ll_tv->vval.v_class; + lp->ll_dict = NULL; + lp->ll_list = NULL; + + class_T *cl; + if (v_type == VAR_OBJECT && lp->ll_tv->vval.v_object != NULL) + { + cl = lp->ll_tv->vval.v_object->obj_class; + lp->ll_object = lp->ll_tv->vval.v_object; + } + else + { + cl = lp->ll_tv->vval.v_class; + lp->ll_object = NULL; + } + lp->ll_class = cl; + // TODO: what if class is NULL? if (cl != NULL) { @@ -1539,6 +1621,7 @@ get_lval( fp = method_lookup(cl, round == 1 ? VAR_CLASS : VAR_OBJECT, key, p - key, &m_idx); + lp->ll_oi = m_idx; if (fp != NULL) { lp->ll_ufunc = fp; @@ -1548,12 +1631,16 @@ 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; - ocmember_T *om; - - om = member_lookup(cl, v_type, key, p - key, &m_idx); + ocmember_T *om + = member_lookup(cl, v_type, key, p - key, &m_idx); + lp->ll_oi = m_idx; if (om != NULL) { switch (om->ocm_access) |