diff src/vim9cmds.c @ 33393:016d8f863230 v9.0.1955

patch 9.0.1955: Vim9: lockvar issues with objects/classes Commit: https://github.com/vim/vim/commit/ee865f37acab6cac2cee6a171d60e1b365f852b0 Author: Ernie Rael <errael@raelity.com> Date: Fri Sep 29 19:53:55 2023 +0200 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>
author Christian Brabandt <cb@256bit.org>
date Fri, 29 Sep 2023 20:00:07 +0200
parents 6340c608ca54
children 4a62e78803db
line wrap: on
line diff
--- a/src/vim9cmds.c
+++ b/src/vim9cmds.c
@@ -189,10 +189,15 @@ compile_lock_unlock(
     int		cc = *name_end;
     char_u	*p = lvp->ll_name;
     int		ret = OK;
-    size_t	len;
     char_u	*buf;
     isntype_T	isn = ISN_EXEC;
     char	*cmd = eap->cmdidx == CMD_lockvar ? "lockvar" : "unlockvar";
+    int		is_arg = FALSE;
+
+#ifdef LOG_LOCKVAR
+    ch_log(NULL, "LKVAR: compile_lock_unlock(): cookie %p, name %s",
+								coookie, p);
+#endif
 
     if (cctx->ctx_skip == SKIP_YES)
 	return OK;
@@ -207,20 +212,68 @@ compile_lock_unlock(
     if (p[1] != ':')
     {
 	char_u *end = find_name_end(p, NULL, NULL, FNE_CHECK_START);
+	// If name is is locally accessible, except for local var,
+	// then put it on the stack to use with ISN_LOCKUNLOCK.
+	// This could be v.memb, v[idx_key]; bare class variable,
+	// function arg. The local variable on the stack, will be passed
+	// to ex_lockvar() indirectly.
 
-	if (lookup_local(p, end - p, NULL, cctx) == OK)
+	char_u	*name = NULL;
+	int	len = end - p;
+
+	if (lookup_local(p, len, NULL, cctx) == OK)
 	{
-	    char_u *s = p;
-
-	    if (*end != '.' && *end != '[')
+	    // Handle "this", "this.val", "anyvar[idx]"
+	    if (*end != '.' && *end != '['
+				&& (len != 4 || STRNCMP("this", p, len) != 0))
 	    {
 		emsg(_(e_cannot_lock_unlock_local_variable));
 		return FAIL;
 	    }
-
-	    // For "d.member" put the local variable on the stack, it will be
-	    // passed to ex_lockvar() indirectly.
-	    if (compile_load(&s, end, cctx, FALSE, FALSE) == FAIL)
+	    // Push the local on the stack, could be "this".
+	    name = p;
+#ifdef LOG_LOCKVAR
+	    ch_log(NULL, "LKVAR: compile... lookup_local: name %s", name);
+#endif
+	}
+	if (name == NULL)
+	{
+	    class_T *cl;
+	    if (cctx_class_member_idx(cctx, p, len, &cl) >= 0)
+	    {
+		if (*end != '.' && *end != '[')
+		{
+		    // Push the class of the bare class variable name
+		    name = cl->class_name;
+		    len = STRLEN(name);
+#ifdef LOG_LOCKVAR
+		    ch_log(NULL, "LKVAR: compile... cctx_class_member: name %s",
+			   name);
+#endif
+		}
+	    }
+	}
+	if (name == NULL)
+	{
+	    int	    idx;
+	    type_T  *type;
+	    // Can lockvar any function arg.
+	    // TODO: test arg[idx]/arg.member
+	    if (arg_exists(p, len, &idx, &type, NULL, cctx) == OK)
+	    {
+		name = p;
+		is_arg = TRUE;
+#ifdef LOG_LOCKVAR
+		ch_log(NULL, "LKVAR: compile... arg_exists: name %s", name);
+#endif
+	    }
+	}
+	if (name != NULL)
+	{
+#ifdef LOG_LOCKVAR
+	    ch_log(NULL, "LKVAR: compile... INS_LOCKUNLOCK %s", name);
+#endif
+	    if (compile_load(&name, name + len, cctx, FALSE, FALSE) == FAIL)
 		return FAIL;
 	    isn = ISN_LOCKUNLOCK;
 	}
@@ -228,7 +281,7 @@ compile_lock_unlock(
 
     // Checking is done at runtime.
     *name_end = NUL;
-    len = name_end - p + 20;
+    size_t len = name_end - p + 20;
     buf = alloc(len);
     if (buf == NULL)
 	ret = FAIL;
@@ -238,7 +291,13 @@ compile_lock_unlock(
 	    vim_snprintf((char *)buf, len, "%s! %s", cmd, p);
 	else
 	    vim_snprintf((char *)buf, len, "%s %d %s", cmd, deep, p);
-	ret = generate_EXEC_copy(cctx, isn, buf);
+#ifdef LOG_LOCKVAR
+	ch_log(NULL, "LKVAR: compile... buf %s", buf);
+#endif
+	if (isn == ISN_LOCKUNLOCK)
+	    ret = generate_LOCKUNLOCK(cctx, buf, is_arg);
+	else
+	    ret = generate_EXEC_copy(cctx, isn, buf);
 
 	vim_free(buf);
 	*name_end = cc;