Mercurial > vim
diff src/evalfunc.c @ 33532:f99f5a56ff27 v9.0.2015
patch 9.0.2015: Vim9: does not handle islocked() from a method correctly
Commit: https://github.com/vim/vim/commit/4c8da025ef8140168b7a09d9fe922ce4bb40f19d
Author: Ernie Rael <errael@raelity.com>
Date: Wed Oct 11 21:35:11 2023 +0200
patch 9.0.2015: Vim9: does not handle islocked() from a method correctly
Problem: Vim9: does not handle islocked() from a method correctly
Solution: Handle islocked() builtin from a method.
- Setup `lval_root` from `f_islocked()`.
- Add function `fill_exec_lval_root()` to get info about executing method.
- `sync_root` added in get_lval to handle method member access.
- Conservative approach to reference counting.
closes: #13309
Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Ernie Rael <errael@raelity.com>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Wed, 11 Oct 2023 21:45:04 +0200 |
parents | f72004b37b2b |
children | 86dbcbb94fdb |
line wrap: on
line diff
--- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -7308,6 +7308,83 @@ f_invert(typval_T *argvars, typval_T *re } /* + * Free resources in lval_root allocated by fill_exec_lval_root(). + */ + static void +free_lval_root(lval_root_T *root) +{ + if (root->lr_tv != NULL) + free_tv(root->lr_tv); + class_unref(root->lr_cl_exec); + root->lr_tv = NULL; + root->lr_cl_exec = NULL; +} + +/* + * This is used if executing in a method, the argument string is a + * variable/item expr/reference. If it starts with a potential class/object + * variable then return OK, may get later errors in get_lval. + * + * Adjust "root" as needed. Note that name may change (for example to skip + * "this") and is returned. lr_tv may be changed or freed. + * + * Always returns OK. + * Free resources and return FAIL if the root should not be used. Otherwise OK. + */ + + static int +fix_variable_reference_lval_root(lval_root_T *root, char_u **p_name) +{ + char_u *name = *p_name; + char_u *end; + dictitem_T *di; + + // Only set lr_sync_root and lr_tv if the name is an object/class + // reference: object ("this.") or class because name is class variable. + if (root->lr_tv->v_type == VAR_OBJECT) + { + if (STRNCMP("this.", name, 5) == 0) + { + name += 5; + root->lr_sync_root = TRUE; + } + else if (STRCMP("this", name) == 0) + { + name += 4; + root->lr_sync_root = TRUE; + } + } + if (!root->lr_sync_root) // not object member, try class member + { + // Explicitly check if the name is a class member. + // If it's not then do nothing. + for (end = name; ASCII_ISALNUM(*end) || *end == '_'; ++end) + ; + if (class_member_lookup(root->lr_cl_exec, name, end - name, NULL) + != NULL) + { + // Using a class, so reference the class tv. + di = find_var(root->lr_cl_exec->class_name, NULL, FALSE); + if (di != NULL) + { + // replace the lr_tv + clear_tv(root->lr_tv); + copy_tv(&di->di_tv, root->lr_tv); + root->lr_sync_root = TRUE; + } + } + } + if (!root->lr_sync_root) + { + free_tv(root->lr_tv); + root->lr_tv = NULL; // Not a member variable + } + *p_name = name; + // If FAIL, then must free_lval_root(root); + return OK; +} + +/* * "islocked()" function */ static void @@ -7322,9 +7399,34 @@ f_islocked(typval_T *argvars, typval_T * if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) return; - end = get_lval(tv_get_string(&argvars[0]), NULL, &lv, FALSE, FALSE, + char_u *name = tv_get_string(&argvars[0]); +#ifdef LOG_LOCKVAR + ch_log(NULL, "LKVAR: f_islocked(): name: %s", name); +#endif + + lval_root_T aroot; // fully initialized in fill_exec_lval_root + lval_root_T *root = NULL; + + // Set up lval_root if executing in a method. + if (fill_exec_lval_root(&aroot) == OK) + { + // Almost always produces a valid lval_root since lr_cl_exec is used + // for access verification, lr_tv may be set to NULL. + char_u *tname = name; + if (fix_variable_reference_lval_root(&aroot, &tname) == OK) + { + name = tname; + root = &aroot; + } + } + + lval_root_T *lval_root_save = lval_root; + lval_root = root; + end = get_lval(name, NULL, &lv, FALSE, FALSE, GLV_NO_AUTOLOAD | GLV_READ_ONLY | GLV_NO_DECL, FNE_CHECK_START); + lval_root = lval_root_save; + if (end != NULL && lv.ll_name != NULL) { if (*end != NUL) @@ -7347,6 +7449,10 @@ f_islocked(typval_T *argvars, typval_T * || tv_islocked(&di->di_tv)); } } + else if (lv.ll_is_root) + { + rettv->vval.v_number = tv_islocked(lv.ll_tv); + } else if (lv.ll_object != NULL) { typval_T *tv = ((typval_T *)(lv.ll_object + 1)) + lv.ll_oi; @@ -7376,6 +7482,8 @@ f_islocked(typval_T *argvars, typval_T * } } + if (root != NULL) + free_lval_root(root); clear_lval(&lv); }