diff src/eval.c @ 33456:4a62e78803db v9.0.1982

patch 9.0.1982: vim9: clean up from v9.0.1955 Commit: https://github.com/vim/vim/commit/64885645e76b301a6c34fe762c4e29c7a0f63881 Author: Ernie Rael <errael@raelity.com> Date: Wed Oct 4 20:16:22 2023 +0200 patch 9.0.1982: vim9: clean up from v9.0.1955 Problem: vim9: clean up from v9.0.1955 Solution: Fix a few remaining issues, improve error message - Use `cl_exec`, the executing class, to check permissions in `get_lval()`. - Handle lockvar of script variable from class. - Add 'in class "Xxx"' to e_cannot_access_private_variable_str. closes: #13222 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Ernie Rael <errael@raelity.com>
author Christian Brabandt <cb@256bit.org>
date Wed, 04 Oct 2023 20:30:03 +0200
parents 016d8f863230
children 3de472480e91
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -985,6 +985,52 @@ eval_foldexpr(win_T *wp, int *cp)
 }
 #endif
 
+#ifdef LOG_LOCKVAR
+typedef struct flag_string_S
+{
+    int	    flag;
+    char    *str;
+} flag_string_T;
+
+    static char *
+flags_tostring(int flags, flag_string_T *_fstring, char *buf, size_t n)
+{
+    char *p = buf;
+    *p = NUL;
+    for (flag_string_T *fstring = _fstring; fstring->flag; ++fstring)
+    {
+	if ((fstring->flag & flags) != 0)
+	{
+	    size_t len = STRLEN(fstring->str);
+	    if (n > p - buf + len + 7)
+	    {
+		STRCAT(p, fstring->str);
+		p += len;
+		STRCAT(p, " ");
+		++p;
+	    }
+	    else
+	    {
+		STRCAT(buf, "...");
+		break;
+	    }
+	}
+    }
+    return buf;
+}
+
+flag_string_T glv_flag_strings[] = {
+    { GLV_QUIET,		"QUIET" },
+    { GLV_NO_AUTOLOAD,		"NO_AUTOLOAD" },
+    { GLV_READ_ONLY,		"READ_ONLY" },
+    { GLV_NO_DECL,		"NO_DECL" },
+    { GLV_COMPILING,		"COMPILING" },
+    { GLV_ASSIGN_WITH_OP,	"ASSIGN_WITH_OP" },
+    { GLV_PREFER_FUNC,		"PREFER_FUNC" },
+    { 0,			NULL }
+};
+#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.
@@ -1004,30 +1050,30 @@ eval_foldexpr(win_T *wp, int *cp)
  *	execute_instructions: ISN_LOCKUNLOCK - sets lval_root from stack.
  */
     static void
-get_lval_root(lval_T *lp, typval_T *root, int is_arg)
+get_lval_root(lval_T *lp, lval_root_T *lr)
 {
 #ifdef LOG_LOCKVAR
-    ch_log(NULL, "LKVAR: get_lvalroot(): name %s", lp->ll_name);
+    ch_log(NULL, "LKVAR: get_lval_root(): name %s", lp->ll_name);
 #endif
-    if (!is_arg && root->v_type == VAR_CLASS)
-    {
-	if (root->vval.v_class != NULL)
+    if (!lr->lr_is_arg && lr->lr_tv->v_type == VAR_CLASS)
+    {
+	if (lr->lr_tv->vval.v_class != NULL)
 	{
 	    // Special special case. Look for a bare class variable reference.
-	    class_T	*cl = root->vval.v_class;
+	    class_T	*cl = lr->lr_tv->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_class = lr->lr_tv->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);
+		ch_log(NULL, "LKVAR:    ... class member %s.%s",
+					lp->ll_class->class_name, lp->ll_name);
 #endif
 		return;
 	    }
@@ -1035,10 +1081,52 @@ get_lval_root(lval_T *lp, typval_T *root
     }
 
 #ifdef LOG_LOCKVAR
-    ch_log(NULL, "LKVAR: get_lvalroot() any type");
+    ch_log(NULL, "LKVAR:    ... type: %s", vartype_name(lr->lr_tv->v_type));
+#endif
+    lp->ll_tv = lr->lr_tv;
+    lp->ll_is_root = TRUE;
+}
+
+/*
+ * Check if the class has permission to access the member.
+ * Returns OK or FAIL.
+ */
+    static int
+get_lval_check_access(
+    class_T	*cl_exec,   // executing class, NULL if :def or script level
+    class_T	*cl,	    // class which contains the member
+    ocmember_T	*om,	    // member being accessed
+    char_u	*p,	    // char after member name
+    int		flags)	    // GLV flags to check if writing to lval
+{
+#ifdef LOG_LOCKVAR
+    ch_log(NULL, "LKVAR: get_lval_check_access(), cl_exec %p, cl %p, %c",
+						(void*)cl_exec, (void*)cl, *p);
 #endif
-    lp->ll_tv = root;
-    lp->ll_is_root = TRUE;
+    if (cl_exec == NULL || cl_exec != cl)
+    {
+	switch (om->ocm_access)
+	{
+	    case VIM_ACCESS_PRIVATE:
+		semsg(_(e_cannot_access_private_variable_str),
+						om->ocm_name, cl->class_name);
+		return FAIL;
+	    case VIM_ACCESS_READ:
+		// If [idx] or .key following, read only OK.
+		if (*p == '[' || *p == '.')
+		    break;
+		if ((flags & GLV_READ_ONLY) == 0)
+		{
+		    semsg(_(e_variable_is_not_writable_str),
+						om->ocm_name, cl->class_name);
+		    return FAIL;
+		}
+		break;
+	    case VIM_ACCESS_ALL:
+		break;
+	}
+    }
+    return OK;
 }
 
 /*
@@ -1083,10 +1171,19 @@ get_lval(
     int		quiet = flags & GLV_QUIET;
     int		writing = 0;
     int		vim9script = in_vim9script();
+    class_T	*cl_exec = NULL;    // class that is executing, or NULL.
 
 #ifdef LOG_LOCKVAR
-    ch_log(NULL, "LKVAR: get_lval(): name %s, lval_root %p",
-						    name, (void*)lval_root);
+    if (lval_root == NULL)
+	ch_log(NULL,
+	       "LKVAR: get_lval(): name %s, lval_root (nil)", name);
+    else
+	ch_log(NULL,
+	   "LKVAR: get_lval(): name %s, lr_tv %p lr_is_arg %d",
+	    name, (void*)lval_root->lr_tv, lval_root->lr_is_arg);
+    char buf[80];
+    ch_log(NULL, "LKVAR:    ...: GLV flags %s",
+		    flags_tostring(flags, glv_flag_strings, buf, sizeof(buf)));
 #endif
 
     // Clear everything in "lp".
@@ -1221,15 +1318,16 @@ get_lval(
     if ((*p != '[' && *p != '.'))
     {
 	if (lval_root != NULL)
-	    get_lval_root(lp, lval_root, lval_root_is_arg);
+	    get_lval_root(lp, lval_root);
 	return p;
     }
 
     if (vim9script && lval_root != NULL)
     {
 	// using local variable
-	lp->ll_tv = lval_root;
+	lp->ll_tv = lval_root->lr_tv;
 	v = NULL;
+	cl_exec = lval_root->lr_cl_exec;
     }
     else
     {
@@ -1643,26 +1741,9 @@ get_lval(
 		    lp->ll_oi = m_idx;
 		    if (om != NULL)
 		    {
-			switch (om->ocm_access)
-			{
-			    case VIM_ACCESS_PRIVATE:
-				semsg(_(e_cannot_access_private_variable_str),
-					om->ocm_name);
-				return NULL;
-			    case VIM_ACCESS_READ:
-				// If [idx] or .key following, read only OK.
-				if (*p == '[' || *p == '.')
-				    break;
-				if ((flags & GLV_READ_ONLY) == 0)
-				{
-				    semsg(_(e_variable_is_not_writable_str),
-					    om->ocm_name, cl->class_name);
-				    return NULL;
-				}
-				break;
-			    case VIM_ACCESS_ALL:
-				break;
-			}
+			if (get_lval_check_access(cl_exec, cl, om,
+							  p, flags) == FAIL)
+			    return NULL;
 
 			lp->ll_valtype = om->ocm_type;