changeset 35291:02fe9a889b04 v9.1.0450

patch 9.1.0450: evalc. code too complex Commit: https://github.com/vim/vim/commit/51c45e89b50a4841147b9fbd7c6095ab79a10c71 Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Thu May 30 07:50:08 2024 +0200 patch 9.1.0450: evalc. code too complex Problem: eval.c code too complex Solution: refactor eval6() and eval9() functions into several smaller functions (Yegappan Lakshmanan) closes: #14875 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Thu, 30 May 2024 08:00:07 +0200
parents 7fdc42388d71
children 2ee0f1da194f
files src/dict.c src/eval.c src/proto/dict.pro src/version.c
diffstat 4 files changed, 429 insertions(+), 294 deletions(-) [+]
line wrap: on
line diff
--- a/src/dict.c
+++ b/src/dict.c
@@ -1092,6 +1092,33 @@ failret:
 }
 
 /*
+ * Evaluate a literal dictionary: #{key: val, key: val}
+ * "*arg" points to the "#".
+ * On return, "*arg" points to the character after the Dict.
+ * Return OK or FAIL.  Returns NOTDONE for {expr}.
+ */
+    int
+eval_lit_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
+{
+    int		vim9script = in_vim9script();
+    int		ret = OK;
+
+    if (vim9script)
+    {
+	ret = vim9_bad_comment(*arg) ? FAIL : NOTDONE;
+    }
+    else if ((*arg)[1] == '{')
+    {
+	++*arg;
+	ret = eval_dict(arg, rettv, evalarg, TRUE);
+    }
+    else
+	ret = NOTDONE;
+
+    return ret;
+}
+
+/*
  * Go over all entries in "d2" and add them to "d1".
  * When "action" is "error" then a duplicate key is an error.
  * When "action" is "force" then a duplicate key is overwritten.
--- a/src/eval.c
+++ b/src/eval.c
@@ -1299,8 +1299,8 @@ typedef enum {
  */
     static int
 get_lval_dict_item(
+    lval_T	*lp,
     char_u	*name,
-    lval_T	*lp,
     char_u	*key,
     int		len,
     char_u	**key_end,
@@ -1513,24 +1513,107 @@ get_lval_list(
 }
 
 /*
- * Get a Class or Object lval variable that can be assigned a value to:
- * "name", "name.key", "name.key[expr]" etc.
+ * Get a class or object lval method in class "cl".  The 'key' argument points
+ * to the method name and 'key_end' points to the character after 'key'.
+ * 'v_type' is VAR_CLASS or VAR_OBJECT.
+ *
+ * The method index, method function pointer and method type are returned in
+ * "lp".
+ */
+    static void
+get_lval_oc_method(
+    lval_T	*lp,
+    class_T	*cl,
+    char_u	*key,
+    char_u	*key_end,
+    vartype_T	v_type)
+{
+    // Look for a method with this name.
+    // round 1: class functions (skipped for an object)
+    // round 2: object methods
+    for (int round = v_type == VAR_OBJECT ? 2 : 1; round <= 2; ++round)
+    {
+	int	m_idx;
+	ufunc_T	*fp;
+
+	fp = method_lookup(cl, round == 1 ? VAR_CLASS : VAR_OBJECT,
+						key, key_end - key, &m_idx);
+	lp->ll_oi = m_idx;
+	if (fp != NULL)
+	{
+	    lp->ll_ufunc = fp;
+	    lp->ll_valtype = fp->uf_func_type;
+	    break;
+	}
+    }
+}
+
+/*
+ * Get a class or object lval variable in class "cl".  The "key" argument
+ * points to the variable name and "key_end" points to the character after
+ * "key".  "v_type" is VAR_CLASS or VAR_OBJECT.  "cl_exec" is the class that is
+ * executing, or NULL.
  *
- * 'cl_exec' is the class that is executing, or NULL. 'v_type' is VAR_CLASS or
- * VAR_OBJECT.  'key' points to the member variable name and 'key_end' points
- * to the character after 'key'.  If 'quiet' is TRUE, then error messages
- * are not displayed for invalid indexes.
+ * The variable index, typval and type are returned in "lp".  Returns FAIL if
+ * the variable is not writable.  Otherwise returns OK.
+ */
+    static int
+get_lval_oc_variable(
+    lval_T	*lp,
+    class_T	*cl,
+    char_u	*key,
+    char_u	*key_end,
+    vartype_T	v_type,
+    class_T	*cl_exec,
+    int		flags)
+{
+    int		m_idx;
+    ocmember_T	*om;
+
+    om = member_lookup(cl, v_type, key, key_end - key, &m_idx);
+    lp->ll_oi = m_idx;
+    if (om == NULL)
+	return OK;
+
+    // Check variable is accessible
+    if (get_lval_check_access(cl_exec, cl, om, key_end, flags) == FAIL)
+	return FAIL;
+
+    // When lhs is used to modify the variable, check it is not a read-only
+    // variable.
+    if ((flags & GLV_READ_ONLY) == 0 && (*key_end != '.' && *key_end != '[')
+	    && oc_var_check_ro(cl, om))
+	return FAIL;
+
+    lp->ll_valtype = om->ocm_type;
+
+    if (v_type == VAR_OBJECT)
+	lp->ll_tv = ((typval_T *)(lp->ll_tv->vval.v_object + 1)) + m_idx;
+    else
+	lp->ll_tv = &cl->class_members_tv[m_idx];
+
+    return OK;
+}
+
+/*
+ * Get a Class or Object lval variable or method that can be assigned a value
+ * to: "name", "name.key", "name.key[expr]" etc.
+ *
+ * The 'key' argument points to the member name and 'key_end' points to the
+ * character after 'key'.  'v_type' is VAR_CLASS or VAR_OBJECT.  'cl_exec' is
+ * the class that is executing, or NULL.  If 'quiet' is TRUE, then error
+ * messages are not displayed for invalid indexes.
  *
  * The Class or Object is returned in 'lp'.  Returns OK on success and FAIL on
  * failure.
  */
     static int
 get_lval_class_or_obj(
-    class_T	*cl_exec,
-    vartype_T	v_type,
     lval_T	*lp,
     char_u	*key,
     char_u	*key_end,
+    vartype_T	v_type,
+    class_T	*cl_exec,
     int		flags,
     int		quiet)
 {
@@ -1556,69 +1639,27 @@ get_lval_class_or_obj(
     }
     lp->ll_class = cl;
 
-    // TODO: what if class is NULL?
-    if (cl != NULL)
-    {
-	lp->ll_valtype = NULL;
-
-	if (flags & GLV_PREFER_FUNC)
-	{
-	    // First look for a function with this name.
-	    // round 1: class functions (skipped for an object)
-	    // round 2: object methods
-	    for (int round = v_type == VAR_OBJECT ? 2 : 1;
-		    round <= 2; ++round)
-	    {
-		int	m_idx;
-		ufunc_T	*fp;
-
-		fp = method_lookup(cl,
-			round == 1 ? VAR_CLASS : VAR_OBJECT,
-			key, key_end - key, &m_idx);
-		lp->ll_oi = m_idx;
-		if (fp != NULL)
-		{
-		    lp->ll_ufunc = fp;
-		    lp->ll_valtype = fp->uf_func_type;
-		    break;
-		}
-	    }
-	}
-
-	if (lp->ll_valtype == NULL)
-	{
-	    int		m_idx;
-	    ocmember_T	*om
-		= member_lookup(cl, v_type, key, key_end - key, &m_idx);
-	    lp->ll_oi = m_idx;
-	    if (om != NULL)
-	    {
-		if (get_lval_check_access(cl_exec, cl, om,
-			    key_end, flags) == FAIL)
-		    return FAIL;
-
-		// When lhs is used to modify the variable, check it is
-		// not a read-only variable.
-		if ((flags & GLV_READ_ONLY) == 0
-			&& (*key_end != '.' && *key_end != '[')
-			&& oc_var_check_ro(cl, om))
-		    return FAIL;
-
-		lp->ll_valtype = om->ocm_type;
-
-		if (v_type == VAR_OBJECT)
-		    lp->ll_tv = ((typval_T *)(
-				lp->ll_tv->vval.v_object + 1)) + m_idx;
-		else
-		    lp->ll_tv = &cl->class_members_tv[m_idx];
-	    }
-	}
-
-	if (lp->ll_valtype == NULL)
-	{
-	    member_not_found_msg(cl, v_type, key, key_end - key);
+    if (cl == NULL)
+	// TODO: what if class is NULL?
+	return OK;
+
+    lp->ll_valtype = NULL;
+
+    if (flags & GLV_PREFER_FUNC)
+	get_lval_oc_method(lp, cl, key, key_end, v_type);
+
+    // Look for object/class member variable
+    if (lp->ll_valtype == NULL)
+    {
+	if (get_lval_oc_variable(lp, cl, key, key_end, v_type, cl_exec, flags)
+								== FAIL)
 	    return FAIL;
-	}
+    }
+
+    if (lp->ll_valtype == NULL)
+    {
+	member_not_found_msg(cl, v_type, key, key_end - key);
+	return FAIL;
     }
 
     return OK;
@@ -1861,7 +1902,7 @@ get_lval_subscript(
 	{
 	    glv_status_T glv_status;
 
-	    glv_status = get_lval_dict_item(name, lp, key, len, &p, &var1,
+	    glv_status = get_lval_dict_item(lp, name, key, len, &p, &var1,
 							flags, unlet, rettv);
 	    if (glv_status == GLV_FAIL)
 		goto done;
@@ -1882,8 +1923,8 @@ get_lval_subscript(
 	}
 	else  // v_type == VAR_CLASS || v_type == VAR_OBJECT
 	{
-	    if (get_lval_class_or_obj(cl_exec, v_type, lp, key, p, flags,
-			quiet) == FAIL)
+	    if (get_lval_class_or_obj(lp, key, p, v_type, cl_exec, flags,
+							quiet) == FAIL)
 		goto done;
 	}
 
@@ -4015,6 +4056,120 @@ eval5(char_u **arg, typval_T *rettv, eva
 }
 
 /*
+ * Concatenate strings "tv1" and "tv2" and store the result in "tv1".
+ */
+    static int
+eval_concat_str(typval_T *tv1, typval_T *tv2)
+{
+    char_u	buf1[NUMBUFLEN], buf2[NUMBUFLEN];
+    char_u	*s1 = tv_get_string_buf(tv1, buf1);
+    char_u	*s2 = NULL;
+    char_u	*p;
+    int		vim9script = in_vim9script();
+
+    if (vim9script && (tv2->v_type == VAR_VOID
+		|| tv2->v_type == VAR_CHANNEL
+		|| tv2->v_type == VAR_JOB))
+	semsg(_(e_using_invalid_value_as_string_str),
+		vartype_name(tv2->v_type));
+    else if (vim9script && tv2->v_type == VAR_FLOAT)
+    {
+	vim_snprintf((char *)buf2, NUMBUFLEN, "%g",
+		tv2->vval.v_float);
+	s2 = buf2;
+    }
+    else
+	s2 = tv_get_string_buf_chk(tv2, buf2);
+    if (s2 == NULL)		// type error ?
+    {
+	clear_tv(tv1);
+	clear_tv(tv2);
+	return FAIL;
+    }
+
+    p = concat_str(s1, s2);
+    clear_tv(tv1);
+    tv1->v_type = VAR_STRING;
+    tv1->vval.v_string = p;
+
+    return OK;
+}
+
+/*
+ * Add or subtract numbers "tv1" and "tv2" and store the result in "tv1".
+ * The numbers can be whole numbers or floats.
+ */
+    static int
+eval_addsub_num(typval_T *tv1, typval_T *tv2, int op)
+{
+    int		error = FALSE;
+    varnumber_T	n1, n2;
+    float_T	f1 = 0, f2 = 0;
+
+    if (tv1->v_type == VAR_FLOAT)
+    {
+	f1 = tv1->vval.v_float;
+	n1 = 0;
+    }
+    else
+    {
+	n1 = tv_get_number_chk(tv1, &error);
+	if (error)
+	{
+	    // This can only happen for "list + non-list" or
+	    // "blob + non-blob".  For "non-list + ..." or
+	    // "something - ...", we returned before evaluating the
+	    // 2nd operand.
+	    clear_tv(tv1);
+	    clear_tv(tv2);
+	    return FAIL;
+	}
+	if (tv2->v_type == VAR_FLOAT)
+	    f1 = n1;
+    }
+    if (tv2->v_type == VAR_FLOAT)
+    {
+	f2 = tv2->vval.v_float;
+	n2 = 0;
+    }
+    else
+    {
+	n2 = tv_get_number_chk(tv2, &error);
+	if (error)
+	{
+	    clear_tv(tv1);
+	    clear_tv(tv2);
+	    return FAIL;
+	}
+	if (tv1->v_type == VAR_FLOAT)
+	    f2 = n2;
+    }
+    clear_tv(tv1);
+
+    // If there is a float on either side the result is a float.
+    if (tv1->v_type == VAR_FLOAT || tv2->v_type == VAR_FLOAT)
+    {
+	if (op == '+')
+	    f1 = f1 + f2;
+	else
+	    f1 = f1 - f2;
+	tv1->v_type = VAR_FLOAT;
+	tv1->vval.v_float = f1;
+    }
+    else
+    {
+	if (op == '+')
+	    n1 = n1 + n2;
+	else
+	    n1 = n1 - n2;
+	tv1->v_type = VAR_NUMBER;
+	tv1->vval.v_number = n1;
+    }
+
+    return OK;
+}
+
+/*
  * Handle fifth level expression:
  *	+	number addition, concatenation of list or blob
  *	-	number subtraction
@@ -4121,33 +4276,8 @@ eval6(char_u **arg, typval_T *rettv, eva
 	     */
 	    if (op == '.')
 	    {
-		char_u	buf1[NUMBUFLEN], buf2[NUMBUFLEN];
-		char_u	*s1 = tv_get_string_buf(rettv, buf1);
-		char_u	*s2 = NULL;
-
-		if (vim9script && (var2.v_type == VAR_VOID
-			|| var2.v_type == VAR_CHANNEL
-			|| var2.v_type == VAR_JOB))
-		    semsg(_(e_using_invalid_value_as_string_str),
-						   vartype_name(var2.v_type));
-		else if (vim9script && var2.v_type == VAR_FLOAT)
-		{
-		    vim_snprintf((char *)buf2, NUMBUFLEN, "%g",
-							    var2.vval.v_float);
-		    s2 = buf2;
-		}
-		else
-		    s2 = tv_get_string_buf_chk(&var2, buf2);
-		if (s2 == NULL)		// type error ?
-		{
-		    clear_tv(rettv);
-		    clear_tv(&var2);
+		if (eval_concat_str(rettv, &var2) == FAIL)
 		    return FAIL;
-		}
-		p = concat_str(s1, s2);
-		clear_tv(rettv);
-		rettv->v_type = VAR_STRING;
-		rettv->vval.v_string = p;
 	    }
 	    else if (op == '+' && rettv->v_type == VAR_BLOB
 						   && var2.v_type == VAR_BLOB)
@@ -4160,69 +4290,8 @@ eval6(char_u **arg, typval_T *rettv, eva
 	    }
 	    else
 	    {
-		int		error = FALSE;
-		varnumber_T	n1, n2;
-		float_T		f1 = 0, f2 = 0;
-
-		if (rettv->v_type == VAR_FLOAT)
-		{
-		    f1 = rettv->vval.v_float;
-		    n1 = 0;
-		}
-		else
-		{
-		    n1 = tv_get_number_chk(rettv, &error);
-		    if (error)
-		    {
-			// This can only happen for "list + non-list" or
-			// "blob + non-blob".  For "non-list + ..." or
-			// "something - ...", we returned before evaluating the
-			// 2nd operand.
-			clear_tv(rettv);
-			clear_tv(&var2);
-			return FAIL;
-		    }
-		    if (var2.v_type == VAR_FLOAT)
-			f1 = n1;
-		}
-		if (var2.v_type == VAR_FLOAT)
-		{
-		    f2 = var2.vval.v_float;
-		    n2 = 0;
-		}
-		else
-		{
-		    n2 = tv_get_number_chk(&var2, &error);
-		    if (error)
-		    {
-			clear_tv(rettv);
-			clear_tv(&var2);
-			return FAIL;
-		    }
-		    if (rettv->v_type == VAR_FLOAT)
-			f2 = n2;
-		}
-		clear_tv(rettv);
-
-		// If there is a float on either side the result is a float.
-		if (rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT)
-		{
-		    if (op == '+')
-			f1 = f1 + f2;
-		    else
-			f1 = f1 - f2;
-		    rettv->v_type = VAR_FLOAT;
-		    rettv->vval.v_float = f1;
-		}
-		else
-		{
-		    if (op == '+')
-			n1 = n1 + n2;
-		    else
-			n1 = n1 - n2;
-		    rettv->v_type = VAR_NUMBER;
-		    rettv->vval.v_number = n1;
-		}
+		if (eval_addsub_num(rettv, &var2, op) == FAIL)
+		    return FAIL;
 	    }
 	    clear_tv(&var2);
 	}
@@ -4624,6 +4693,158 @@ handle_predefined(char_u *s, int len, ty
 }
 
 /*
+ * Handle register contents: @r.
+ */
+    static void
+eval9_reg_contents(
+    char_u	**arg,
+    typval_T	*rettv,
+    int		evaluate)
+{
+    int		vim9script = in_vim9script();
+
+    ++*arg;	// skip '@'
+    if (evaluate)
+    {
+	if (vim9script && IS_WHITE_OR_NUL(**arg))
+	    semsg(_(e_syntax_error_at_str), *arg);
+	else if (vim9script && !valid_yank_reg(**arg, FALSE))
+	    emsg_invreg(**arg);
+	else
+	{
+	    rettv->v_type = VAR_STRING;
+	    rettv->vval.v_string = get_reg_contents(**arg,
+		    GREG_EXPR_SRC);
+	}
+    }
+    if (**arg != NUL)
+	++*arg;
+}
+
+/*
+ * Handle a nested expression: (expression) or lambda: (arg) => expr
+ */
+    static int
+eval9_nested_expr(
+    char_u	**arg,
+    typval_T	*rettv,
+    evalarg_T	*evalarg,
+    int		evaluate)
+{
+    int		ret = NOTDONE;
+    int		vim9script = in_vim9script();
+
+    if (vim9script)
+    {
+	ret = get_lambda_tv(arg, rettv, TRUE, evalarg);
+	if (ret == OK && evaluate)
+	{
+	    ufunc_T *ufunc = rettv->vval.v_partial->pt_func;
+
+	    // Compile it here to get the return type.  The return
+	    // type is optional, when it's missing use t_unknown.
+	    // This is recognized in compile_return().
+	    if (ufunc->uf_ret_type->tt_type == VAR_VOID)
+		ufunc->uf_ret_type = &t_unknown;
+	    if (compile_def_function(ufunc, FALSE,
+				get_compile_type(ufunc), NULL) == FAIL)
+	    {
+		clear_tv(rettv);
+		ret = FAIL;
+	    }
+	}
+    }
+    if (ret == NOTDONE)
+    {
+	*arg = skipwhite_and_linebreak(*arg + 1, evalarg);
+	ret = eval1(arg, rettv, evalarg);	// recursive!
+
+	*arg = skipwhite_and_linebreak(*arg, evalarg);
+	if (**arg == ')')
+	    ++*arg;
+	else if (ret == OK)
+	{
+	    emsg(_(e_missing_closing_paren));
+	    clear_tv(rettv);
+	    ret = FAIL;
+	}
+    }
+
+    return ret;
+}
+
+/*
+* Handle be a variable or function name.
+* Can also be a curly-braces kind of name: {expr}.
+*/
+    static int
+eval9_var_func_name(
+    char_u	**arg,
+    typval_T	*rettv,
+    evalarg_T	*evalarg,
+    int		evaluate,
+    char_u	**name_start)
+{
+    char_u	*s;
+    int		len;
+    char_u	*alias;
+    int		ret = OK;
+    int		vim9script = in_vim9script();
+
+    s = *arg;
+    len = get_name_len(arg, &alias, evaluate, TRUE);
+    if (alias != NULL)
+	s = alias;
+
+    if (len <= 0)
+	ret = FAIL;
+    else
+    {
+	int	flags = evalarg == NULL ? 0 : evalarg->eval_flags;
+
+	if (evaluate && vim9script && len == 1 && *s == '_')
+	{
+	    emsg(_(e_cannot_use_underscore_here));
+	    ret = FAIL;
+	}
+	else if (evaluate && vim9script && len > 2
+						&& s[0] == 's' && s[1] == ':')
+	{
+	    semsg(_(e_cannot_use_s_colon_in_vim9_script_str), s);
+	    ret = FAIL;
+	}
+	else if ((vim9script ? **arg : *skipwhite(*arg)) == '(')
+	{
+	    // "name(..."  recursive!
+	    *arg = skipwhite(*arg);
+	    ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL);
+	}
+	else if (evaluate)
+	{
+	    // get the value of "true", "false", etc. or a variable
+	    ret = FAIL;
+	    if (vim9script)
+		ret = handle_predefined(s, len, rettv);
+	    if (ret == FAIL)
+	    {
+		*name_start = s;
+		ret = eval_variable(s, len, 0, rettv, NULL,
+					EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT);
+	    }
+	}
+	else
+	{
+	    // skip the name
+	    check_vars(s, len);
+	    ret = OK;
+	}
+    }
+    vim_free(alias);
+
+    return ret;
+}
+
+/*
  * Handle sixth level expression:
  *  number		number constant
  *  0zFFFFFFFF		Blob constant
@@ -4662,12 +4883,9 @@ eval9(
 {
     int		evaluate = evalarg != NULL
 				      && (evalarg->eval_flags & EVAL_EVALUATE);
-    int		len;
-    char_u	*s;
     char_u	*name_start = NULL;
     char_u	*start_leader, *end_leader;
     int		ret = OK;
-    char_u	*alias;
     static int	recurse = 0;
     int		vim9script = in_vim9script();
 
@@ -4750,19 +4968,9 @@ eval9(
 		break;
 
     /*
-     * Dictionary: #{key: val, key: val}
+     * Literal Dictionary: #{key: val, key: val}
      */
-    case '#':	if (vim9script)
-		{
-		    ret = vim9_bad_comment(*arg) ? FAIL : NOTDONE;
-		}
-		else if ((*arg)[1] == '{')
-		{
-		    ++*arg;
-		    ret = eval_dict(arg, rettv, evalarg, TRUE);
-		}
-		else
-		    ret = NOTDONE;
+    case '#':	ret = eval_lit_dict(arg, rettv, evalarg);
 		break;
 
     /*
@@ -4796,64 +5004,14 @@ eval9(
     /*
      * Register contents: @r.
      */
-    case '@':	++*arg;
-		if (evaluate)
-		{
-		    if (vim9script && IS_WHITE_OR_NUL(**arg))
-			semsg(_(e_syntax_error_at_str), *arg);
-		    else if (vim9script && !valid_yank_reg(**arg, FALSE))
-			emsg_invreg(**arg);
-		    else
-		    {
-			rettv->v_type = VAR_STRING;
-			rettv->vval.v_string = get_reg_contents(**arg,
-								GREG_EXPR_SRC);
-		    }
-		}
-		if (**arg != NUL)
-		    ++*arg;
+    case '@':	eval9_reg_contents(arg, rettv, evaluate);
 		break;
 
     /*
      * nested expression: (expression).
      * or lambda: (arg) => expr
      */
-    case '(':	ret = NOTDONE;
-		if (vim9script)
-		{
-		    ret = get_lambda_tv(arg, rettv, TRUE, evalarg);
-		    if (ret == OK && evaluate)
-		    {
-			ufunc_T *ufunc = rettv->vval.v_partial->pt_func;
-
-			// Compile it here to get the return type.  The return
-			// type is optional, when it's missing use t_unknown.
-			// This is recognized in compile_return().
-			if (ufunc->uf_ret_type->tt_type == VAR_VOID)
-			    ufunc->uf_ret_type = &t_unknown;
-			if (compile_def_function(ufunc, FALSE,
-					get_compile_type(ufunc), NULL) == FAIL)
-			{
-			    clear_tv(rettv);
-			    ret = FAIL;
-			}
-		    }
-		}
-		if (ret == NOTDONE)
-		{
-		    *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
-		    ret = eval1(arg, rettv, evalarg);	// recursive!
-
-		    *arg = skipwhite_and_linebreak(*arg, evalarg);
-		    if (**arg == ')')
-			++*arg;
-		    else if (ret == OK)
-		    {
-			emsg(_(e_missing_closing_paren));
-			clear_tv(rettv);
-			ret = FAIL;
-		    }
-		}
+    case '(':	ret = eval9_nested_expr(arg, rettv, evalarg, evaluate);
 		break;
 
     default:	ret = NOTDONE;
@@ -4866,55 +5024,7 @@ eval9(
 	 * Must be a variable or function name.
 	 * Can also be a curly-braces kind of name: {expr}.
 	 */
-	s = *arg;
-	len = get_name_len(arg, &alias, evaluate, TRUE);
-	if (alias != NULL)
-	    s = alias;
-
-	if (len <= 0)
-	    ret = FAIL;
-	else
-	{
-	    int	    flags = evalarg == NULL ? 0 : evalarg->eval_flags;
-
-	    if (evaluate && vim9script && len == 1 && *s == '_')
-	    {
-		emsg(_(e_cannot_use_underscore_here));
-		ret = FAIL;
-	    }
-	    else if (evaluate && vim9script && len > 2
-						 && s[0] == 's' && s[1] == ':')
-	    {
-		semsg(_(e_cannot_use_s_colon_in_vim9_script_str), s);
-		ret = FAIL;
-	    }
-	    else if ((vim9script ? **arg : *skipwhite(*arg)) == '(')
-	    {
-		// "name(..."  recursive!
-		*arg = skipwhite(*arg);
-		ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL);
-	    }
-	    else if (evaluate)
-	    {
-		// get the value of "true", "false", etc. or a variable
-		ret = FAIL;
-		if (vim9script)
-		    ret = handle_predefined(s, len, rettv);
-		if (ret == FAIL)
-		{
-		    name_start = s;
-		    ret = eval_variable(s, len, 0, rettv, NULL,
-					   EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT);
-		}
-	    }
-	    else
-	    {
-		// skip the name
-		check_vars(s, len);
-		ret = OK;
-	    }
-	}
-	vim_free(alias);
+	ret = eval9_var_func_name(arg, rettv, evalarg, evaluate, &name_start);
     }
 
     // Handle following '[', '(' and '.' for expr[expr], expr.name,
@@ -5761,32 +5871,27 @@ func_tv2string(typval_T *tv, char_u **to
 
     if (echo_style)
     {
+	*tofree = NULL;
+
 	if (tv->vval.v_string == NULL)
-	{
 	    r = (char_u *)"function()";
-	    *tofree = NULL;
-	}
 	else
 	{
 	    r = make_ufunc_name_readable(tv->vval.v_string, buf,
 							MAX_FUNC_NAME_LEN);
 	    if (r == buf)
-	    {
-		r = vim_strsave(buf);
-		*tofree = r;
-	    }
-	    else
-		*tofree = NULL;
+		r = *tofree = vim_strsave(buf);
 	}
     }
     else
     {
-	if (tv->vval.v_string == NULL)
-	    *tofree = string_quote(NULL, TRUE);
-	else
-	    *tofree = string_quote(make_ufunc_name_readable(tv->vval.v_string,
-						buf, MAX_FUNC_NAME_LEN), TRUE);
-	r = *tofree;
+	char_u *s = NULL;
+
+	if (tv->vval.v_string != NULL)
+	    s = make_ufunc_name_readable(tv->vval.v_string, buf,
+							MAX_FUNC_NAME_LEN);
+
+	r = *tofree = string_quote(s, TRUE);
     }
 
     return r;
--- a/src/proto/dict.pro
+++ b/src/proto/dict.pro
@@ -37,6 +37,7 @@ varnumber_T dict_get_bool(dict_T *d, cha
 char_u *dict2string(typval_T *tv, int copyID, int restore_copyID);
 char_u *get_literal_key(char_u **arg);
 int eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal);
+int eval_lit_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
 void dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name);
 dictitem_T *dict_lookup(hashitem_T *hi);
 int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive);
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    450,
+/**/
     449,
 /**/
     448,