From bb6190bec5f18c1f9e2c1d29ef1f7cf7912ea625 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 1 Jun 2024 08:19:41 -0700 Subject: refactor: move shared messages to errors.h #26214 --- src/nvim/eval.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 48a58228ae..a8778b9489 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -24,6 +24,7 @@ #include "nvim/cmdhist.h" #include "nvim/cursor.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/eval/executor.h" -- cgit From a18982cb839d7f05e32b1026bbd99b8aa34ad189 Mon Sep 17 00:00:00 2001 From: James Tirta Halim Date: Mon, 3 Jun 2024 11:00:20 +0700 Subject: refactor: replace '\0' with NUL --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a8778b9489..a4680b62ab 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8910,7 +8910,7 @@ bool eval_has_provider(const char *feat, bool throw_if_fast) char name[32]; // Normalized: "python3_compiled" => "python3". snprintf(name, sizeof(name), "%s", feat); - strchrsub(name, '_', '\0'); // Chop any "_xx" suffix. + strchrsub(name, '_', NUL); // Chop any "_xx" suffix. char buf[256]; typval_T tv; -- cgit From bbd2f340a2895ed59785f952b2585e6590602cad Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 11 Jun 2024 10:25:32 +0200 Subject: refactor(memory): use builtin strcat() instead of STRCAT() The latter was mostly relevant with the past char_u madness. NOTE: STRCAT also functioned as a counterfeit "NOLINT" for clint apparently. But NOLINT-ing every usecase is just the same as disabling the check entirely. --- src/nvim/eval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a4680b62ab..d52e10f61b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7154,8 +7154,8 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex retval = xmalloc(strlen(temp_result) + (size_t)(expr_start - in_start) + (size_t)(in_end - expr_end) + 1); STRCPY(retval, in_start); - STRCAT(retval, temp_result); - STRCAT(retval, expr_end + 1); + strcat(retval, temp_result); + strcat(retval, expr_end + 1); } xfree(temp_result); -- cgit From c37695a5d5f2e8914fff86f3581bed70b4c85d3c Mon Sep 17 00:00:00 2001 From: James <89495599+IAKOBVS@users.noreply.github.com> Date: Tue, 11 Jun 2024 22:40:24 +0700 Subject: refactor: use S_LEN(s) instead of s, n (#29219) --- src/nvim/eval.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a4680b62ab..df5d336cbd 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1219,7 +1219,7 @@ int call_vim_function(const char *func, int argc, typval_T *argv, typval_T *rett int len = (int)strlen(func); partial_T *pt = NULL; - if (len >= 6 && !memcmp(func, "v:lua.", 6)) { + if (len >= 6 && !memcmp(func, S_LEN("v:lua."))) { func += 6; len = check_luafunc_name(func, false); if (len == 0) { @@ -2161,7 +2161,7 @@ void del_menutrans_vars(void) { hash_lock(&globvarht); HASHTAB_ITER(&globvarht, hi, { - if (strncmp(hi->hi_key, "menutrans_", 10) == 0) { + if (strncmp(hi->hi_key, S_LEN("menutrans_")) == 0) { delete_var(&globvarht, hi); } }); @@ -3274,7 +3274,7 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan check_vars(s, (size_t)len); // If evaluate is false rettv->v_type was not set, but it's needed // in handle_subscript() to parse v:lua, so set it here. - if (rettv->v_type == VAR_UNKNOWN && !evaluate && strnequal(s, "v:lua.", 6)) { + if (rettv->v_type == VAR_UNKNOWN && !evaluate && strnequal(s, S_LEN("v:lua."))) { rettv->v_type = VAR_PARTIAL; rettv->vval.v_partial = vvlua_partial; rettv->vval.v_partial->pt_refcount++; @@ -3483,7 +3483,7 @@ static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const int len; char *name = *arg; char *lua_funcname = NULL; - if (strnequal(name, "v:lua.", 6)) { + if (strnequal(name, S_LEN("v:lua."))) { lua_funcname = name + 6; *arg = (char *)skip_luafunc_name(lua_funcname); *arg = skipwhite(*arg); // to detect trailing whitespace later @@ -5618,7 +5618,7 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) int dict_idx = 0; int arg_idx = 0; list_T *list = NULL; - if (strncmp(s, "s:", 2) == 0 || strncmp(s, "", 5) == 0) { + if (strncmp(s, S_LEN("s:")) == 0 || strncmp(s, S_LEN("")) == 0) { // Expand s: and into nr_, so that the function can // also be called from another script. Using trans_function_name() // would also work, but some plugins depend on the name being @@ -6192,7 +6192,7 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co case kCallbackFuncref: name = callback->data.funcref; int len = (int)strlen(name); - if (len >= 6 && !memcmp(name, "v:lua.", 6)) { + if (len >= 6 && !memcmp(name, S_LEN("v:lua."))) { name += 6; len = check_luafunc_name(name, false); if (len == 0) { @@ -6460,7 +6460,7 @@ bool write_list(FileDescriptor *const fp, const list_T *const list, const bool b } } if (!binary || TV_LIST_ITEM_NEXT(list, li) != NULL) { - const ptrdiff_t written = file_write(fp, "\n", 1); + const ptrdiff_t written = file_write(fp, S_LEN("\n")); if (written < 0) { error = (int)written; goto write_list_error; -- cgit From 43d8435cf84468e776e0a6a98d05ae7bd62583b7 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 13 Jun 2024 22:20:06 +0100 Subject: revert: "refactor: use S_LEN macro" (#29319) revert: "refactor: use S_LEN(s) instead of s, n (#29219)" This reverts commit c37695a5d5f2e8914fff86f3581bed70b4c85d3c. --- src/nvim/eval.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4cff5e1582..d52e10f61b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1219,7 +1219,7 @@ int call_vim_function(const char *func, int argc, typval_T *argv, typval_T *rett int len = (int)strlen(func); partial_T *pt = NULL; - if (len >= 6 && !memcmp(func, S_LEN("v:lua."))) { + if (len >= 6 && !memcmp(func, "v:lua.", 6)) { func += 6; len = check_luafunc_name(func, false); if (len == 0) { @@ -2161,7 +2161,7 @@ void del_menutrans_vars(void) { hash_lock(&globvarht); HASHTAB_ITER(&globvarht, hi, { - if (strncmp(hi->hi_key, S_LEN("menutrans_")) == 0) { + if (strncmp(hi->hi_key, "menutrans_", 10) == 0) { delete_var(&globvarht, hi); } }); @@ -3274,7 +3274,7 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan check_vars(s, (size_t)len); // If evaluate is false rettv->v_type was not set, but it's needed // in handle_subscript() to parse v:lua, so set it here. - if (rettv->v_type == VAR_UNKNOWN && !evaluate && strnequal(s, S_LEN("v:lua."))) { + if (rettv->v_type == VAR_UNKNOWN && !evaluate && strnequal(s, "v:lua.", 6)) { rettv->v_type = VAR_PARTIAL; rettv->vval.v_partial = vvlua_partial; rettv->vval.v_partial->pt_refcount++; @@ -3483,7 +3483,7 @@ static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const int len; char *name = *arg; char *lua_funcname = NULL; - if (strnequal(name, S_LEN("v:lua."))) { + if (strnequal(name, "v:lua.", 6)) { lua_funcname = name + 6; *arg = (char *)skip_luafunc_name(lua_funcname); *arg = skipwhite(*arg); // to detect trailing whitespace later @@ -5618,7 +5618,7 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) int dict_idx = 0; int arg_idx = 0; list_T *list = NULL; - if (strncmp(s, S_LEN("s:")) == 0 || strncmp(s, S_LEN("")) == 0) { + if (strncmp(s, "s:", 2) == 0 || strncmp(s, "", 5) == 0) { // Expand s: and into nr_, so that the function can // also be called from another script. Using trans_function_name() // would also work, but some plugins depend on the name being @@ -6192,7 +6192,7 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co case kCallbackFuncref: name = callback->data.funcref; int len = (int)strlen(name); - if (len >= 6 && !memcmp(name, S_LEN("v:lua."))) { + if (len >= 6 && !memcmp(name, "v:lua.", 6)) { name += 6; len = check_luafunc_name(name, false); if (len == 0) { @@ -6460,7 +6460,7 @@ bool write_list(FileDescriptor *const fp, const list_T *const list, const bool b } } if (!binary || TV_LIST_ITEM_NEXT(list, li) != NULL) { - const ptrdiff_t written = file_write(fp, S_LEN("\n")); + const ptrdiff_t written = file_write(fp, "\n", 1); if (written < 0) { error = (int)written; goto write_list_error; -- cgit From bda63d5b97dfb333de6f4bd757dbb978906062a2 Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 25 Jun 2024 15:33:47 +0200 Subject: refactor(typval)!: remove distinction of binary and nonbinary strings This is a breaking change which will make refactor of typval and shada code a lot easier. In particular, code that would use or check for v:msgpack_types.binary in the wild would be broken. This appears to be rarely used in existing plugins. Also some cases where v:msgpack_type.string would be used to represent a binary string of "string" type, we use a BLOB instead, which is vimscripts native type for binary blobs, and already was used for BIN formats when necessary. msgpackdump(msgpackparse(data)) no longer preserves the distinction of BIN and STR strings. This is very common behavior for language-specific msgpack bindings. Nvim uses msgpack as a tool to serialize its data. Nvim is not a tool to bit-perfectly manipulate arbitrary msgpack data out in the wild. The changed tests should indicate how behavior changes in various edge cases. --- src/nvim/eval.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d52e10f61b..b541af23af 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -331,7 +331,6 @@ static const char *const msgpack_type_names[] = { [kMPInteger] = "integer", [kMPFloat] = "float", [kMPString] = "string", - [kMPBinary] = "binary", [kMPArray] = "array", [kMPMap] = "map", [kMPExt] = "ext", @@ -342,7 +341,6 @@ const list_T *eval_msgpack_type_lists[] = { [kMPInteger] = NULL, [kMPFloat] = NULL, [kMPString] = NULL, - [kMPBinary] = NULL, [kMPArray] = NULL, [kMPMap] = NULL, [kMPExt] = NULL, -- cgit From 2a883d9c597e70d25ffc53373731d05d18a89b91 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 5 Jul 2024 15:20:02 +0800 Subject: vim-patch:9.1.0524: the recursive parameter in the *_equal functions can be removed (#29572) Problem: the recursive parameter in the *_equal functions can be removed Solution: Remove the recursive parameter in dict_equal(), list_equal() object_equal and tv_equal(). Use a comparison of the static var recursive_cnt == 0 to determine whether or not tv_equal() has been called recursively (Yinzuo Jiang). closes: vim/vim#15070 https://github.com/vim/vim/commit/7ccd1a2e858dbb2ac7fb09971dfcbfad62baa677 Co-authored-by: Yinzuo Jiang --- src/nvim/eval.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b541af23af..247948ffe9 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4387,7 +4387,7 @@ bool func_equal(typval_T *tv1, typval_T *tv2, bool ic) if (d1 != d2) { return false; } - } else if (!tv_dict_equal(d1, d2, ic, true)) { + } else if (!tv_dict_equal(d1, d2, ic)) { return false; } @@ -4399,7 +4399,7 @@ bool func_equal(typval_T *tv1, typval_T *tv2, bool ic) } for (int i = 0; i < a1; i++) { if (!tv_equal(tv1->vval.v_partial->pt_argv + i, - tv2->vval.v_partial->pt_argv + i, ic, true)) { + tv2->vval.v_partial->pt_argv + i, ic)) { return false; } } @@ -9063,7 +9063,7 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic) return FAIL; } else { // Compare two Lists for being equal or unequal. - n1 = tv_list_equal(typ1->vval.v_list, typ2->vval.v_list, ic, false); + n1 = tv_list_equal(typ1->vval.v_list, typ2->vval.v_list, ic); if (type == EXPR_NEQUAL) { n1 = !n1; } @@ -9086,7 +9086,7 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic) return FAIL; } else { // Compare two Dictionaries for being equal or unequal. - n1 = tv_dict_equal(typ1->vval.v_dict, typ2->vval.v_dict, ic, false); + n1 = tv_dict_equal(typ1->vval.v_dict, typ2->vval.v_dict, ic); if (type == EXPR_NEQUAL) { n1 = !n1; } @@ -9107,14 +9107,14 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic) if (typ1->v_type == VAR_FUNC && typ2->v_type == VAR_FUNC) { // strings are considered the same if their value is // the same - n1 = tv_equal(typ1, typ2, ic, false); + n1 = tv_equal(typ1, typ2, ic); } else if (typ1->v_type == VAR_PARTIAL && typ2->v_type == VAR_PARTIAL) { n1 = typ1->vval.v_partial == typ2->vval.v_partial; } else { n1 = false; } } else { - n1 = tv_equal(typ1, typ2, ic, false); + n1 = tv_equal(typ1, typ2, ic); } if (type == EXPR_NEQUAL || type == EXPR_ISNOT) { n1 = !n1; -- cgit From 43c137c3d8473ab87b5dad5c4c2414dd1016f663 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 31 Jul 2024 09:53:13 +0800 Subject: vim-patch:9.2.1731: Vim9: cannot use += to append to empty NULL list Problem: Vim9: cannot use += to append to empty NULL list. Solution: Copy the list instead of extending it. (closes vim/vim#6998) https://github.com/vim/vim/commit/81ed4960482f8baabdd7f95b4d5e39744be88ae7 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 247948ffe9..b084328f77 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1519,7 +1519,14 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv key = (char *)tv_get_string(&var1); // is number or string } lp->ll_list = NULL; + + // a NULL dict is equivalent with an empty dict + if (lp->ll_tv->vval.v_dict == NULL) { + lp->ll_tv->vval.v_dict = tv_dict_alloc(); + lp->ll_tv->vval.v_dict->dv_refcount++; + } lp->ll_dict = lp->ll_tv->vval.v_dict; + lp->ll_di = tv_dict_find(lp->ll_dict, key, len); // When assigning to a scope dictionary check that a function and -- cgit From 5463b5c9e1ffa753746e7babd7e985f3efac05e3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 30 Jul 2024 13:54:32 +0800 Subject: vim-patch:8.2.4115: cannot use a method with a complex expression Problem: Cannot use a method with a complex expression. Solution: Evaluate the expression after "->" and use the result. https://github.com/vim/vim/commit/c665dabdf4c49a0fbf1dc566253c75c2abe2effa Cherry-pick a "verbose" check from patch 8.2.4123. N/A patches for version.c: vim-patch:8.2.4102: Vim9: import cannot be used after method vim-patch:8.2.4110: Coverity warns for using NULL pointer Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 19 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b084328f77..6bd117c5b2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -122,6 +122,8 @@ static const char e_empty_function_name[] = N_("E1192: Empty function name"); static const char e_argument_of_str_must_be_list_string_dictionary_or_blob[] = N_("E1250: Argument of %s must be a List, String, Dictionary or Blob"); +static const char e_cannot_use_partial_here[] + = N_("E1265: Cannot use a partial here"); static char * const namespace_char = "abglstvw"; @@ -3488,20 +3490,22 @@ static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const int len; char *name = *arg; char *lua_funcname = NULL; + char *alias = NULL; if (strnequal(name, "v:lua.", 6)) { lua_funcname = name + 6; *arg = (char *)skip_luafunc_name(lua_funcname); *arg = skipwhite(*arg); // to detect trailing whitespace later len = (int)(*arg - lua_funcname); } else { - char *alias; len = get_name_len((const char **)arg, &alias, evaluate, true); if (alias != NULL) { name = alias; } } - int ret; + char *tofree = NULL; + int ret = OK; + if (len <= 0) { if (verbose) { if (lua_funcname == NULL) { @@ -3512,25 +3516,79 @@ static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const } ret = FAIL; } else { - if (**arg != '(') { - if (verbose) { - semsg(_(e_missingparen), name); - } - ret = FAIL; - } else if (ascii_iswhite((*arg)[-1])) { - if (verbose) { - emsg(_(e_nowhitespace)); + *arg = skipwhite(*arg); + + // If there is no "(" immediately following, but there is further on, + // it can be "dict.Func()", "list[nr]", etc. + // Does not handle anything where "(" is part of the expression. + char *paren; + if (**arg != '(' && lua_funcname == NULL && alias == NULL + && (paren = vim_strchr(*arg, '(')) != NULL) { + *arg = name; + *paren = NUL; + typval_T ref; + ref.v_type = VAR_UNKNOWN; + if (eval7(arg, &ref, evalarg, false) == FAIL) { + *arg = name + len; + ret = FAIL; + } else if (*skipwhite(*arg) != NUL) { + if (verbose) { + semsg(_(e_trailing_arg), *arg); + } + ret = FAIL; + } else if (ref.v_type == VAR_FUNC && ref.vval.v_string != NULL) { + name = ref.vval.v_string; + ref.vval.v_string = NULL; + tofree = name; + len = (int)strlen(name); + } else if (ref.v_type == VAR_PARTIAL && ref.vval.v_partial != NULL) { + if (ref.vval.v_partial->pt_argc > 0 || ref.vval.v_partial->pt_dict != NULL) { + if (verbose) { + emsg(_(e_cannot_use_partial_here)); + } + ret = FAIL; + } else { + name = xstrdup(partial_name(ref.vval.v_partial)); + tofree = name; + if (name == NULL) { + ret = FAIL; + name = *arg; + } else { + len = (int)strlen(name); + } + } + } else { + if (verbose) { + semsg(_(e_not_callable_type_str), name); + } + ret = FAIL; } - ret = FAIL; - } else if (lua_funcname != NULL) { - if (evaluate) { - rettv->v_type = VAR_PARTIAL; - rettv->vval.v_partial = vvlua_partial; - rettv->vval.v_partial->pt_refcount++; + tv_clear(&ref); + *paren = '('; + } + + if (ret == OK) { + if (**arg != '(') { + if (verbose) { + semsg(_(e_missingparen), name); + } + ret = FAIL; + } else if (ascii_iswhite((*arg)[-1])) { + if (verbose) { + emsg(_(e_nowhitespace)); + } + ret = FAIL; + } else if (lua_funcname != NULL) { + if (evaluate) { + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = vvlua_partial; + rettv->vval.v_partial->pt_refcount++; + } + ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, lua_funcname); + } else { + ret = eval_func(arg, evalarg, name, len, rettv, + evaluate ? EVAL_EVALUATE : 0, &base); } - ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, lua_funcname); - } else { - ret = eval_func(arg, evalarg, name, len, rettv, evaluate ? EVAL_EVALUATE : 0, &base); } } @@ -3539,6 +3597,7 @@ static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const if (evaluate) { tv_clear(&base); } + xfree(tofree); return ret; } -- cgit From a1561cbbea55cc74d4dc880585f942f4a114c4b8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 30 Jul 2024 12:48:15 +0800 Subject: vim-patch:partial:9.1.0411: too long functions in eval.c Problem: too long functions in eval.c Solution: refactor functions (Yegappan Lakshmanan) closes: vim/vim#14755 https://github.com/vim/vim/commit/4ceb4dc825854032eed423ec1fc372317d3420bf Skip the eval_expr_typval() changes. Co-authored-by: Yegappan Lakshmanan --- src/nvim/eval.c | 337 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 208 insertions(+), 129 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6bd117c5b2..176d75b04d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -154,6 +154,12 @@ typedef struct { int fi_byte_idx; // byte index in fi_string } forinfo_T; +typedef enum { + GLV_FAIL, + GLV_OK, + GLV_STOP, +} glv_status_T; + // values for vv_flags: #define VV_COMPAT 1 // compatible, also used without "v:" #define VV_RO 2 // read-only @@ -1383,6 +1389,200 @@ Object eval_foldtext(win_T *wp) return retval; } +/// Get an Dict lval variable that can be assigned a value to: "name", +/// "name[expr]", "name[expr][expr]", "name.key", "name.key[expr]" etc. +/// "name" points to the start of the name. +/// If "rettv" is not NULL it points to the value to be assigned. +/// "unlet" is true for ":unlet": slightly different behavior when something is +/// wrong; must end in space or cmd separator. +/// +/// flags: +/// GLV_QUIET: do not give error messages +/// GLV_READ_ONLY: will not change the variable +/// GLV_NO_AUTOLOAD: do not use script autoloading +/// +/// The Dict is returned in 'lp'. Returns GLV_OK on success and GLV_FAIL on +/// failure. Returns GLV_STOP to stop processing the characters following +/// 'key_end'. +static glv_status_T get_lval_dict_item(char *name, lval_T *lp, char *key, int len, char **key_end, + typval_T *var1, int flags, bool unlet, typval_T *rettv) +{ + bool quiet = flags & GLV_QUIET; + char *p = *key_end; + + if (len == -1) { + // "[key]": get key from "var1" + key = (char *)tv_get_string(var1); // is number or string + } + lp->ll_list = NULL; + + // a NULL dict is equivalent with an empty dict + if (lp->ll_tv->vval.v_dict == NULL) { + lp->ll_tv->vval.v_dict = tv_dict_alloc(); + lp->ll_tv->vval.v_dict->dv_refcount++; + } + lp->ll_dict = lp->ll_tv->vval.v_dict; + + lp->ll_di = tv_dict_find(lp->ll_dict, key, len); + + // When assigning to a scope dictionary check that a function and + // variable name is valid (only variable name unless it is l: or + // g: dictionary). Disallow overwriting a builtin function. + if (rettv != NULL && lp->ll_dict->dv_scope != 0) { + char prevval; + if (len != -1) { + prevval = key[len]; + key[len] = NUL; + } else { + prevval = 0; // Avoid compiler warning. + } + bool wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE + && tv_is_func(*rettv) + && var_wrong_func_name(key, lp->ll_di == NULL)) + || !valid_varname(key)); + if (len != -1) { + key[len] = prevval; + } + if (wrong) { + tv_clear(var1); + return GLV_FAIL; + } + } + + if (lp->ll_di != NULL && tv_is_luafunc(&lp->ll_di->di_tv) + && len == -1 && rettv == NULL) { + tv_clear(var1); + semsg(e_illvar, "v:['lua']"); + return GLV_FAIL; + } + + if (lp->ll_di == NULL) { + // Can't add "v:" or "a:" variable. + if (lp->ll_dict == &vimvardict + || &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) { + semsg(_(e_illvar), name); + tv_clear(var1); + return GLV_FAIL; + } + + // Key does not exist in dict: may need to add it. + if (*p == '[' || *p == '.' || unlet) { + if (!quiet) { + semsg(_(e_dictkey), key); + } + tv_clear(var1); + return GLV_FAIL; + } + if (len == -1) { + lp->ll_newkey = xstrdup(key); + } else { + lp->ll_newkey = xmemdupz(key, (size_t)len); + } + tv_clear(var1); + *key_end = p; + return GLV_STOP; + // existing variable, need to check if it can be changed + } else if (!(flags & GLV_READ_ONLY) + && (var_check_ro(lp->ll_di->di_flags, name, (size_t)(p - name)) + || var_check_lock(lp->ll_di->di_flags, name, (size_t)(p - name)))) { + tv_clear(var1); + return GLV_FAIL; + } + + tv_clear(var1); + lp->ll_tv = &lp->ll_di->di_tv; + + return GLV_OK; +} + +/// Get an blob lval variable that can be assigned a value to: "name", +/// "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]", etc. +/// +/// 'var1' specifies the starting blob index and 'var2' specifies the ending +/// blob index. If the first index is not specified in a range, then 'empty1' +/// is true. If 'quiet' is true, then error messages are not displayed for +/// invalid indexes. +/// +/// The blob is returned in 'lp'. Returns OK on success and FAIL on failure. +static int get_lval_blob(lval_T *lp, typval_T *var1, typval_T *var2, bool empty1, bool quiet) +{ + const int bloblen = tv_blob_len(lp->ll_tv->vval.v_blob); + + // Get the number and item for the only or first index of the List. + if (empty1) { + lp->ll_n1 = 0; + } else { + // Is number or string. + lp->ll_n1 = (int)tv_get_number(var1); + } + tv_clear(var1); + + if (tv_blob_check_index(bloblen, lp->ll_n1, quiet) == FAIL) { + tv_clear(var2); + return FAIL; + } + if (lp->ll_range && !lp->ll_empty2) { + lp->ll_n2 = (int)tv_get_number(var2); + tv_clear(var2); + if (tv_blob_check_range(bloblen, lp->ll_n1, lp->ll_n2, quiet) == FAIL) { + return FAIL; + } + } + + lp->ll_blob = lp->ll_tv->vval.v_blob; + lp->ll_tv = NULL; + + return OK; +} + +/// Get a List lval variable that can be assigned a value to: "name", +/// "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]", etc. +/// +/// 'var1' specifies the starting List index and 'var2' specifies the ending +/// List index. If the first index is not specified in a range, then 'empty1' +/// is true. If 'quiet' is true, then error messages are not displayed for +/// invalid indexes. +/// +/// The List is returned in 'lp'. Returns OK on success and FAIL on failure. +static int get_lval_list(lval_T *lp, typval_T *var1, typval_T *var2, bool empty1, int flags, + bool quiet) +{ + // Get the number and item for the only or first index of the List. + if (empty1) { + lp->ll_n1 = 0; + } else { + // Is number or string. + lp->ll_n1 = (int)tv_get_number(var1); + } + tv_clear(var1); + + lp->ll_dict = NULL; + lp->ll_list = lp->ll_tv->vval.v_list; + lp->ll_li = tv_list_check_range_index_one(lp->ll_list, &lp->ll_n1, quiet); + if (lp->ll_li == NULL) { + tv_clear(var2); + return FAIL; + } + + // May need to find the item or absolute index for the second + // index of a range. + // When no index given: "lp->ll_empty2" is true. + // Otherwise "lp->ll_n2" is set to the second index. + if (lp->ll_range && !lp->ll_empty2) { + lp->ll_n2 = (int)tv_get_number(var2); // Is number or string. + tv_clear(var2); + if (tv_list_check_range_index_two(lp->ll_list, + &lp->ll_n1, lp->ll_li, + &lp->ll_n2, quiet) == FAIL) { + return FAIL; + } + } + + lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li); + + return OK; +} + /// Get the lval of a list/dict/blob subitem starting at "p". Loop /// until no more [idx] or .key is following. /// @@ -1391,9 +1591,9 @@ Object eval_foldtext(win_T *wp) /// @return A pointer to the character after the subscript on success or NULL on /// failure. static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv, hashtab_T *ht, - dictitem_T *v, int unlet, int flags) + dictitem_T *v, bool unlet, int flags) { - int quiet = flags & GLV_QUIET; + bool quiet = flags & GLV_QUIET; typval_T var1; var1.v_type = VAR_UNKNOWN; typval_T var2; @@ -1516,144 +1716,23 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv } if (lp->ll_tv->v_type == VAR_DICT) { - if (len == -1) { - // "[key]": get key from "var1" - key = (char *)tv_get_string(&var1); // is number or string - } - lp->ll_list = NULL; - - // a NULL dict is equivalent with an empty dict - if (lp->ll_tv->vval.v_dict == NULL) { - lp->ll_tv->vval.v_dict = tv_dict_alloc(); - lp->ll_tv->vval.v_dict->dv_refcount++; - } - lp->ll_dict = lp->ll_tv->vval.v_dict; - - lp->ll_di = tv_dict_find(lp->ll_dict, key, len); - - // When assigning to a scope dictionary check that a function and - // variable name is valid (only variable name unless it is l: or - // g: dictionary). Disallow overwriting a builtin function. - if (rettv != NULL && lp->ll_dict->dv_scope != 0) { - char prevval; - if (len != -1) { - prevval = key[len]; - key[len] = NUL; - } else { - prevval = 0; // Avoid compiler warning. - } - bool wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE - && tv_is_func(*rettv) - && var_wrong_func_name(key, lp->ll_di == NULL)) - || !valid_varname(key)); - if (len != -1) { - key[len] = prevval; - } - if (wrong) { - tv_clear(&var1); - return NULL; - } - } - - if (lp->ll_di != NULL && tv_is_luafunc(&lp->ll_di->di_tv) - && len == -1 && rettv == NULL) { - tv_clear(&var1); - semsg(e_illvar, "v:['lua']"); + glv_status_T glv_status = get_lval_dict_item(name, lp, key, len, &p, &var1, + flags, unlet, rettv); + if (glv_status == GLV_FAIL) { return NULL; } - - if (lp->ll_di == NULL) { - // Can't add "v:" or "a:" variable. - if (lp->ll_dict == &vimvardict - || &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) { - semsg(_(e_illvar), name); - tv_clear(&var1); - return NULL; - } - - // Key does not exist in dict: may need to add it. - if (*p == '[' || *p == '.' || unlet) { - if (!quiet) { - semsg(_(e_dictkey), key); - } - tv_clear(&var1); - return NULL; - } - if (len == -1) { - lp->ll_newkey = xstrdup(key); - } else { - lp->ll_newkey = xmemdupz(key, (size_t)len); - } - tv_clear(&var1); + if (glv_status == GLV_STOP) { break; - // existing variable, need to check if it can be changed - } else if (!(flags & GLV_READ_ONLY) - && (var_check_ro(lp->ll_di->di_flags, name, (size_t)(p - name)) - || var_check_lock(lp->ll_di->di_flags, name, (size_t)(p - name)))) { - tv_clear(&var1); - return NULL; } - - tv_clear(&var1); - lp->ll_tv = &lp->ll_di->di_tv; } else if (lp->ll_tv->v_type == VAR_BLOB) { - // Get the number and item for the only or first index of the List. - if (empty1) { - lp->ll_n1 = 0; - } else { - // Is number or string. - lp->ll_n1 = (int)tv_get_number(&var1); - } - tv_clear(&var1); - - const int bloblen = tv_blob_len(lp->ll_tv->vval.v_blob); - if (tv_blob_check_index(bloblen, lp->ll_n1, quiet) == FAIL) { - tv_clear(&var2); + if (get_lval_blob(lp, &var1, &var2, empty1, quiet) == FAIL) { return NULL; } - if (lp->ll_range && !lp->ll_empty2) { - lp->ll_n2 = (int)tv_get_number(&var2); - tv_clear(&var2); - if (tv_blob_check_range(bloblen, lp->ll_n1, lp->ll_n2, quiet) == FAIL) { - return NULL; - } - } - lp->ll_blob = lp->ll_tv->vval.v_blob; - lp->ll_tv = NULL; break; } else { - // Get the number and item for the only or first index of the List. - if (empty1) { - lp->ll_n1 = 0; - } else { - // Is number or string. - lp->ll_n1 = (int)tv_get_number(&var1); - } - tv_clear(&var1); - - lp->ll_dict = NULL; - lp->ll_list = lp->ll_tv->vval.v_list; - lp->ll_li = tv_list_check_range_index_one(lp->ll_list, &lp->ll_n1, quiet); - if (lp->ll_li == NULL) { - tv_clear(&var2); + if (get_lval_list(lp, &var1, &var2, empty1, flags, quiet) == FAIL) { return NULL; } - - // May need to find the item or absolute index for the second - // index of a range. - // When no index given: "lp->ll_empty2" is true. - // Otherwise "lp->ll_n2" is set to the second index. - if (lp->ll_range && !lp->ll_empty2) { - lp->ll_n2 = (int)tv_get_number(&var2); // Is number or string. - tv_clear(&var2); - if (tv_list_check_range_index_two(lp->ll_list, - &lp->ll_n1, lp->ll_li, - &lp->ll_n2, quiet) == FAIL) { - return NULL; - } - } - - lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li); } } -- cgit From 619cb143f93fbf75adde9710415a74d36c8eb63d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 30 Jul 2024 13:38:13 +0800 Subject: vim-patch:9.1.0415: Some functions are not tested Problem: Some functions are not tested Solution: Add a few more tests, fix a few minor problems (Yegappan Lakshmanan) closes: vim/vim#14789 https://github.com/vim/vim/commit/fe424d13ef6e5486923f23f15bb6951e3079412e Co-authored-by: Yegappan Lakshmanan --- src/nvim/eval.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 176d75b04d..d1f9adf2d0 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3678,6 +3678,10 @@ static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const } xfree(tofree); + if (alias != NULL) { + xfree(alias); + } + return ret; } @@ -3815,7 +3819,7 @@ static int check_can_index(typval_T *rettv, bool evaluate, bool verbose) /// slice() function void f_slice(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - if (check_can_index(argvars, true, false) != OK) { + if (check_can_index(&argvars[0], true, false) != OK) { return; } -- cgit From 706a0a4b04507f1b3d45de013cdd8cbe8de62fda Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 31 Jul 2024 09:58:29 +0800 Subject: vim-patch:partial:9.1.0445: Coverity warning after 9.1.0440 Problem: Coverity warning after 9.1.0440 Solution: Fix Coverity warning, add a test and reduce the calls to clear_tv() (Yegappan Lakshmanan). closes: vim/vim#14845 https://github.com/vim/vim/commit/dbac0da631c66869f41c3c573ad7a8cfef95964d Co-authored-by: Yegappan Lakshmanan --- src/nvim/eval.c | 58 +++++++++++++++++++++++++-------------------------------- 1 file changed, 25 insertions(+), 33 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d1f9adf2d0..02694b5f3a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1444,14 +1444,12 @@ static glv_status_T get_lval_dict_item(char *name, lval_T *lp, char *key, int le key[len] = prevval; } if (wrong) { - tv_clear(var1); return GLV_FAIL; } } if (lp->ll_di != NULL && tv_is_luafunc(&lp->ll_di->di_tv) && len == -1 && rettv == NULL) { - tv_clear(var1); semsg(e_illvar, "v:['lua']"); return GLV_FAIL; } @@ -1461,7 +1459,6 @@ static glv_status_T get_lval_dict_item(char *name, lval_T *lp, char *key, int le if (lp->ll_dict == &vimvardict || &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) { semsg(_(e_illvar), name); - tv_clear(var1); return GLV_FAIL; } @@ -1470,7 +1467,6 @@ static glv_status_T get_lval_dict_item(char *name, lval_T *lp, char *key, int le if (!quiet) { semsg(_(e_dictkey), key); } - tv_clear(var1); return GLV_FAIL; } if (len == -1) { @@ -1478,18 +1474,15 @@ static glv_status_T get_lval_dict_item(char *name, lval_T *lp, char *key, int le } else { lp->ll_newkey = xmemdupz(key, (size_t)len); } - tv_clear(var1); *key_end = p; return GLV_STOP; // existing variable, need to check if it can be changed } else if (!(flags & GLV_READ_ONLY) && (var_check_ro(lp->ll_di->di_flags, name, (size_t)(p - name)) || var_check_lock(lp->ll_di->di_flags, name, (size_t)(p - name)))) { - tv_clear(var1); return GLV_FAIL; } - tv_clear(var1); lp->ll_tv = &lp->ll_di->di_tv; return GLV_OK; @@ -1515,15 +1508,12 @@ static int get_lval_blob(lval_T *lp, typval_T *var1, typval_T *var2, bool empty1 // Is number or string. lp->ll_n1 = (int)tv_get_number(var1); } - tv_clear(var1); if (tv_blob_check_index(bloblen, lp->ll_n1, quiet) == FAIL) { - tv_clear(var2); return FAIL; } if (lp->ll_range && !lp->ll_empty2) { lp->ll_n2 = (int)tv_get_number(var2); - tv_clear(var2); if (tv_blob_check_range(bloblen, lp->ll_n1, lp->ll_n2, quiet) == FAIL) { return FAIL; } @@ -1554,13 +1544,11 @@ static int get_lval_list(lval_T *lp, typval_T *var1, typval_T *var2, bool empty1 // Is number or string. lp->ll_n1 = (int)tv_get_number(var1); } - tv_clear(var1); lp->ll_dict = NULL; lp->ll_list = lp->ll_tv->vval.v_list; lp->ll_li = tv_list_check_range_index_one(lp->ll_list, &lp->ll_n1, quiet); if (lp->ll_li == NULL) { - tv_clear(var2); return FAIL; } @@ -1570,7 +1558,6 @@ static int get_lval_list(lval_T *lp, typval_T *var1, typval_T *var2, bool empty1 // Otherwise "lp->ll_n2" is set to the second index. if (lp->ll_range && !lp->ll_empty2) { lp->ll_n2 = (int)tv_get_number(var2); // Is number or string. - tv_clear(var2); if (tv_list_check_range_index_two(lp->ll_list, &lp->ll_n1, lp->ll_li, &lp->ll_n2, quiet) == FAIL) { @@ -1586,6 +1573,9 @@ static int get_lval_list(lval_T *lp, typval_T *var1, typval_T *var2, bool empty1 /// Get the lval of a list/dict/blob subitem starting at "p". Loop /// until no more [idx] or .key is following. /// +/// If "rettv" is not NULL it points to the value to be assigned. +/// "unlet" is true for ":unlet". +/// /// @param[in] flags @see GetLvalFlags. /// /// @return A pointer to the character after the subscript on success or NULL on @@ -1599,6 +1589,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv typval_T var2; var2.v_type = VAR_UNKNOWN; bool empty1 = false; + int rc = FAIL; // Loop until no more [idx] or .key is following. while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.')) { @@ -1628,7 +1619,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv if (!quiet) { emsg(_("E708: [:] must come last")); } - return NULL; + goto done; } int len = -1; @@ -1652,12 +1643,11 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv } else { empty1 = false; if (eval1(&p, &var1, &EVALARG_EVALUATE) == FAIL) { // Recursive! - return NULL; + goto done; } if (!tv_check_str(&var1)) { // Not a number or string. - tv_clear(&var1); - return NULL; + goto done; } p = skipwhite(p); } @@ -1668,8 +1658,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv if (!quiet) { emsg(_(e_cannot_slice_dictionary)); } - tv_clear(&var1); - return NULL; + goto done; } if (rettv != NULL && !(rettv->v_type == VAR_LIST && rettv->vval.v_list != NULL) @@ -1677,8 +1666,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv if (!quiet) { emsg(_("E709: [:] requires a List or Blob value")); } - tv_clear(&var1); - return NULL; + goto done; } p = skipwhite(p + 1); if (*p == ']') { @@ -1687,14 +1675,11 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv lp->ll_empty2 = false; // Recursive! if (eval1(&p, &var2, &EVALARG_EVALUATE) == FAIL) { - tv_clear(&var1); - return NULL; + goto done; } if (!tv_check_str(&var2)) { // Not a number or string. - tv_clear(&var1); - tv_clear(&var2); - return NULL; + goto done; } } lp->ll_range = true; @@ -1706,9 +1691,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv if (!quiet) { emsg(_(e_missbrac)); } - tv_clear(&var1); - tv_clear(&var2); - return NULL; + goto done; } // Skip to past ']'. @@ -1719,25 +1702,34 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv glv_status_T glv_status = get_lval_dict_item(name, lp, key, len, &p, &var1, flags, unlet, rettv); if (glv_status == GLV_FAIL) { - return NULL; + goto done; } if (glv_status == GLV_STOP) { break; } } else if (lp->ll_tv->v_type == VAR_BLOB) { if (get_lval_blob(lp, &var1, &var2, empty1, quiet) == FAIL) { - return NULL; + goto done; } break; } else { if (get_lval_list(lp, &var1, &var2, empty1, flags, quiet) == FAIL) { - return NULL; + goto done; } } + + tv_clear(&var1); + tv_clear(&var2); + var1.v_type = VAR_UNKNOWN; + var2.v_type = VAR_UNKNOWN; } + rc = OK; + +done: tv_clear(&var1); - return p; + tv_clear(&var2); + return rc == OK ? p : NULL; } /// Get an lvalue -- cgit From c9b129a02ab46fc80c81f3f9cabed4040a7462c0 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 31 Jul 2024 11:59:57 +0800 Subject: vim-patch:9.1.0411: too long functions in eval.c (#29925) Problem: too long functions in eval.c Solution: refactor functions (Yegappan Lakshmanan) closes: vim/vim#14755 https://github.com/vim/vim/commit/4ceb4dc825854032eed423ec1fc372317d3420bf The remaining eval_expr_typval() changes. Co-authored-by: Yegappan Lakshmanan --- src/nvim/eval.c | 117 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 39 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 02694b5f3a..be643b7990 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -847,6 +847,80 @@ bool eval_expr_valid_arg(const typval_T *const tv) && (tv->v_type != VAR_STRING || (tv->vval.v_string != NULL && *tv->vval.v_string != NUL)); } +/// Evaluate a partial. +/// Pass arguments "argv[argc]". +/// Return the result in "rettv" and OK or FAIL. +static int eval_expr_partial(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + partial_T *const partial = expr->vval.v_partial; + if (partial == NULL) { + return FAIL; + } + + const char *const s = partial_name(partial); + if (s == NULL || *s == NUL) { + return FAIL; + } + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.fe_evaluate = true; + funcexe.fe_partial = partial; + if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { + return FAIL; + } + + return OK; +} + +/// Evaluate an expression which is a function. +/// Pass arguments "argv[argc]". +/// Return the result in "rettv" and OK or FAIL. +static int eval_expr_func(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + char buf[NUMBUFLEN]; + const char *const s = (expr->v_type == VAR_FUNC + ? expr->vval.v_string + : tv_get_string_buf_chk(expr, buf)); + if (s == NULL || *s == NUL) { + return FAIL; + } + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.fe_evaluate = true; + if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { + return FAIL; + } + + return OK; +} + +/// Evaluate an expression, which is a string. +/// Return the result in "rettv" and OK or FAIL. +static int eval_expr_string(const typval_T *expr, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + char buf[NUMBUFLEN]; + char *s = (char *)tv_get_string_buf_chk(expr, buf); + if (s == NULL) { + return FAIL; + } + + s = skipwhite(s); + if (eval1_emsg(&s, rettv, NULL) == FAIL) { + return FAIL; + } + + if (*skipwhite(s) != NUL) { // check for trailing chars after expr + tv_clear(rettv); + semsg(_(e_invexpr2), s); + return FAIL; + } + + return OK; +} + /// Evaluate an expression, which can be a function, partial or string. /// Pass arguments "argv[argc]". /// Return the result in "rettv" and OK or FAIL. @@ -856,49 +930,14 @@ int eval_expr_typval(const typval_T *expr, bool want_func, typval_T *argv, int a typval_T *rettv) FUNC_ATTR_NONNULL_ALL { - char buf[NUMBUFLEN]; - funcexe_T funcexe = FUNCEXE_INIT; - if (expr->v_type == VAR_PARTIAL) { - partial_T *const partial = expr->vval.v_partial; - if (partial == NULL) { - return FAIL; - } - const char *const s = partial_name(partial); - if (s == NULL || *s == NUL) { - return FAIL; - } - funcexe.fe_evaluate = true; - funcexe.fe_partial = partial; - if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { - return FAIL; - } + return eval_expr_partial(expr, argv, argc, rettv); } else if (expr->v_type == VAR_FUNC || want_func) { - const char *const s = (expr->v_type == VAR_FUNC - ? expr->vval.v_string - : tv_get_string_buf_chk(expr, buf)); - if (s == NULL || *s == NUL) { - return FAIL; - } - funcexe.fe_evaluate = true; - if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { - return FAIL; - } + return eval_expr_func(expr, argv, argc, rettv); } else { - char *s = (char *)tv_get_string_buf_chk(expr, buf); - if (s == NULL) { - return FAIL; - } - s = skipwhite(s); - if (eval1_emsg(&s, rettv, NULL) == FAIL) { - return FAIL; - } - if (*skipwhite(s) != NUL) { // check for trailing chars after expr - tv_clear(rettv); - semsg(_(e_invexpr2), s); - return FAIL; - } + return eval_expr_string(expr, rettv); } + return OK; } -- cgit From 8f47a95addf7387b660ae3b76b303a0f946e4f80 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 1 Aug 2024 08:42:58 +0800 Subject: vim-patch:partial: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: vim/vim#14875 https://github.com/vim/vim/commit/51c45e89b50a4841147b9fbd7c6095ab79a10c71 Co-authored-by: Yegappan Lakshmanan --- src/nvim/eval.c | 189 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 112 insertions(+), 77 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index be643b7990..fa519ad701 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1443,7 +1443,7 @@ Object eval_foldtext(win_T *wp) /// The Dict is returned in 'lp'. Returns GLV_OK on success and GLV_FAIL on /// failure. Returns GLV_STOP to stop processing the characters following /// 'key_end'. -static glv_status_T get_lval_dict_item(char *name, lval_T *lp, char *key, int len, char **key_end, +static glv_status_T get_lval_dict_item(lval_T *lp, char *name, char *key, int len, char **key_end, typval_T *var1, int flags, bool unlet, typval_T *rettv) { bool quiet = flags & GLV_QUIET; @@ -1738,7 +1738,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv } if (lp->ll_tv->v_type == VAR_DICT) { - glv_status_T glv_status = get_lval_dict_item(name, lp, key, len, &p, &var1, + glv_status_T glv_status = get_lval_dict_item(lp, name, key, len, &p, &var1, flags, unlet, rettv); if (glv_status == GLV_FAIL) { goto done; @@ -2950,6 +2950,93 @@ static int eval_addlist(typval_T *tv1, typval_T *tv2) return OK; } +/// Concatenate strings "tv1" and "tv2" and store the result in "tv1". +static int eval_concat_str(typval_T *tv1, typval_T *tv2) +{ + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + // s1 already checked + const char *const s1 = tv_get_string_buf(tv1, buf1); + const char *const s2 = tv_get_string_buf_chk(tv2, buf2); + if (s2 == NULL) { // Type error? + tv_clear(tv1); + tv_clear(tv2); + return FAIL; + } + + char *p = concat_str(s1, s2); + tv_clear(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) +{ + bool error = false; + varnumber_T n1, n2; + float_T f1 = 0; + float_T 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. + tv_clear(tv1); + tv_clear(tv2); + return FAIL; + } + if (tv2->v_type == VAR_FLOAT) { + f1 = (float_T)n1; + } + } + if (tv2->v_type == VAR_FLOAT) { + f2 = tv2->vval.v_float; + n2 = 0; + } else { + n2 = tv_get_number_chk(tv2, &error); + if (error) { + tv_clear(tv1); + tv_clear(tv2); + return FAIL; + } + if (tv1->v_type == VAR_FLOAT) { + f2 = (float_T)n2; + } + } + tv_clear(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 fourth level expression: /// + number addition, concatenation of list or blob /// - number subtraction @@ -3005,20 +3092,9 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) if (evaluate) { // Compute the result. if (op == '.') { - char buf1[NUMBUFLEN]; - char buf2[NUMBUFLEN]; - // s1 already checked - const char *const s1 = tv_get_string_buf(rettv, buf1); - const char *const s2 = tv_get_string_buf_chk(&var2, buf2); - if (s2 == NULL) { // Type error? - tv_clear(rettv); - tv_clear(&var2); + if (eval_concat_str(rettv, &var2) == FAIL) { return FAIL; } - char *p = concat_str(s1, s2); - tv_clear(rettv); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = p; } else if (op == '+' && rettv->v_type == VAR_BLOB && var2.v_type == VAR_BLOB) { eval_addblob(rettv, &var2); } else if (op == '+' && rettv->v_type == VAR_LIST && var2.v_type == VAR_LIST) { @@ -3026,62 +3102,8 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) return FAIL; } } else { - bool error = false; - varnumber_T n1, n2; - float_T f1 = 0; - float_T 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. - tv_clear(rettv); - tv_clear(&var2); - return FAIL; - } - if (var2.v_type == VAR_FLOAT) { - f1 = (float_T)n1; - } - } - if (var2.v_type == VAR_FLOAT) { - f2 = var2.vval.v_float; - n2 = 0; - } else { - n2 = tv_get_number_chk(&var2, &error); - if (error) { - tv_clear(rettv); - tv_clear(&var2); - return FAIL; - } - if (rettv->v_type == VAR_FLOAT) { - f2 = (float_T)n2; - } - } - tv_clear(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; } } tv_clear(&var2); @@ -3301,14 +3323,9 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan ret = eval_list(arg, rettv, evalarg); break; - // Dictionary: #{key: val, key: val} + // Literal Dictionary: #{key: val, key: val} case '#': - if ((*arg)[1] == '{') { - (*arg)++; - ret = eval_dict(arg, rettv, evalarg, true); - } else { - ret = NOTDONE; - } + ret = eval_lit_dict(arg, rettv, evalarg); break; // Lambda: {arg, arg -> expr} @@ -5274,6 +5291,24 @@ failret: return OK; } +/// 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}. +static int eval_lit_dict(char **arg, typval_T *rettv, evalarg_T *const evalarg) +{ + int ret = OK; + + if ((*arg)[1] == '{') { + (*arg)++; + ret = eval_dict(arg, rettv, evalarg, true); + } else { + ret = NOTDONE; + } + + return ret; +} + /// Convert the string to a floating point number /// /// This uses strtod(). setlocale(LC_NUMERIC, "C") has been used earlier to -- cgit From 7f1ba04421516c466ac9c3a63721f1e34abb3db1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 1 Aug 2024 08:57:48 +0800 Subject: vim-patch:partial:9.1.0462: eval5() and eval7 are too complex Problem: eval5() and eval7 are too complex Solution: Refactor eval5() and eval7() in eval.c (Yegappan Lakshmanan) closes: vim/vim#14900 https://github.com/vim/vim/commit/734286e4c626f80ace27eeb252a5e384e798aebf Co-authored-by: Yegappan Lakshmanan --- src/nvim/eval.c | 156 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 83 insertions(+), 73 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index fa519ad701..b8c3df5688 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2974,7 +2974,7 @@ static int eval_concat_str(typval_T *tv1, typval_T *tv2) /// 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) +static int eval_addsub_number(typval_T *tv1, typval_T *tv2, int op) { bool error = false; varnumber_T n1, n2; @@ -3102,7 +3102,7 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) return FAIL; } } else { - if (eval_addsub_num(rettv, &var2, op) == FAIL) { + if (eval_addsub_number(rettv, &var2, op) == FAIL) { return FAIL; } } @@ -3112,6 +3112,85 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) return OK; } +/// Multiply or divide or compute the modulo of numbers "tv1" and "tv2" and +/// store the result in "tv1". The numbers can be whole numbers or floats. +static int eval_multdiv_number(typval_T *tv1, typval_T *tv2, int op) + FUNC_ATTR_NO_SANITIZE_UNDEFINED +{ + varnumber_T n1, n2; + bool use_float = false; + + float_T f1 = 0; + float_T f2 = 0; + bool error = false; + if (tv1->v_type == VAR_FLOAT) { + f1 = tv1->vval.v_float; + use_float = true; + n1 = 0; + } else { + n1 = tv_get_number_chk(tv1, &error); + } + tv_clear(tv1); + if (error) { + tv_clear(tv2); + return FAIL; + } + + if (tv2->v_type == VAR_FLOAT) { + if (!use_float) { + f1 = (float_T)n1; + use_float = true; + } + f2 = tv2->vval.v_float; + n2 = 0; + } else { + n2 = tv_get_number_chk(tv2, &error); + tv_clear(tv2); + if (error) { + return FAIL; + } + if (use_float) { + f2 = (float_T)n2; + } + } + + // Compute the result. + // When either side is a float the result is a float. + if (use_float) { + if (op == '*') { + f1 = f1 * f2; + } else if (op == '/') { + // uncrustify:off + + // Division by zero triggers error from AddressSanitizer + f1 = (f2 == 0 ? ( +#ifdef NAN + f1 == 0 ? (float_T)NAN : +#endif + (f1 > 0 ? (float_T)INFINITY : (float_T)-INFINITY)) : f1 / f2); + + // uncrustify:on + } else { + emsg(_("E804: Cannot use '%' with Float")); + return FAIL; + } + tv1->v_type = VAR_FLOAT; + tv1->vval.v_float = f1; + } else { + if (op == '*') { + n1 = n1 * n2; + } else if (op == '/') { + n1 = num_divide(n1, n2); + } else { + n1 = num_modulus(n1, n2); + } + tv1->v_type = VAR_NUMBER; + tv1->vval.v_number = n1; + } + + return OK; +} + /// Handle fifth level expression: /// - * number multiplication /// - / number division @@ -3125,10 +3204,7 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) /// float /// @return OK or FAIL. static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string) - FUNC_ATTR_NO_SANITIZE_UNDEFINED { - bool use_float = false; - // Get the first variable. if (eval7(arg, rettv, evalarg, want_string) == FAIL) { return FAIL; @@ -3141,26 +3217,7 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan break; } - varnumber_T n1, n2; - float_T f1 = 0; - float_T f2 = 0; - bool error = false; const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE); - if (evaluate) { - if (rettv->v_type == VAR_FLOAT) { - f1 = rettv->vval.v_float; - use_float = true; - n1 = 0; - } else { - n1 = tv_get_number_chk(rettv, &error); - } - tv_clear(rettv); - if (error) { - return FAIL; - } - } else { - n1 = 0; - } // Get the second variable. *arg = skipwhite(*arg + 1); @@ -3170,56 +3227,9 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan } if (evaluate) { - if (var2.v_type == VAR_FLOAT) { - if (!use_float) { - f1 = (float_T)n1; - use_float = true; - } - f2 = var2.vval.v_float; - n2 = 0; - } else { - n2 = tv_get_number_chk(&var2, &error); - tv_clear(&var2); - if (error) { - return FAIL; - } - if (use_float) { - f2 = (float_T)n2; - } - } - // Compute the result. - // When either side is a float the result is a float. - if (use_float) { - if (op == '*') { - f1 = f1 * f2; - } else if (op == '/') { - // uncrustify:off - - // Division by zero triggers error from AddressSanitizer - f1 = (f2 == 0 ? ( -#ifdef NAN - f1 == 0 ? (float_T)NAN : -#endif - (f1 > 0 ? (float_T)INFINITY : (float_T)-INFINITY)) : f1 / f2); - - // uncrustify:on - } else { - emsg(_("E804: Cannot use '%' with Float")); - return FAIL; - } - rettv->v_type = VAR_FLOAT; - rettv->vval.v_float = f1; - } else { - if (op == '*') { - n1 = n1 * n2; - } else if (op == '/') { - n1 = num_divide(n1, n2); - } else { - n1 = num_modulus(n1, n2); - } - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = n1; + if (eval_multdiv_number(rettv, &var2, op) == FAIL) { + return FAIL; } } } -- cgit From f7fde0173af95925e7324b7d3c09776173dab8a7 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 1 Aug 2024 10:13:45 +0800 Subject: vim-patch:9.0.0632: calling a function from an "expr" option has overhead Problem: Calling a function from an "expr" option has too much overhead. Solution: Add call_simple_func() and use it for 'foldexpr' https://github.com/vim/vim/commit/87b4e5c5db9d1cfd6f2e79656e1a6cff3c69d15f Cherry-pick a call_func() change from patch 8.2.1343. Add expr-option-function docs to options.txt. Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b8c3df5688..c8451b7a28 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1349,7 +1349,7 @@ int eval_foldexpr(win_T *wp, int *cp) const sctx_T saved_sctx = current_sctx; const bool use_sandbox = was_set_insecurely(wp, kOptFoldexpr, OPT_LOCAL); - char *arg = wp->w_p_fde; + char *arg = skipwhite(wp->w_p_fde); current_sctx = wp->w_p_script_ctx[WV_FDE].script_ctx; emsg_off++; @@ -1360,8 +1360,23 @@ int eval_foldexpr(win_T *wp, int *cp) *cp = NUL; typval_T tv; + int r = NOTDONE; + + // If the expression is "FuncName()" then we can skip a lot of overhead. + char *parens = strstr(arg, "()"); + if (parens != NULL && *skipwhite(parens + 2) == NUL) { + char *p = strncmp(arg, "", 5) == 0 ? skipdigits(arg + 5) : arg; + if (to_name_end(p, true) == parens) { + r = call_simple_func(arg, (int)(parens - arg), &tv); + } + } + + if (r == NOTDONE) { + r = eval0(arg, &tv, NULL, &EVALARG_EVALUATE); + } + varnumber_T retval; - if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { + if (r == FAIL) { retval = 0; } else { // If the result is a number, just return the number. @@ -1428,6 +1443,31 @@ Object eval_foldtext(win_T *wp) return retval; } +/// Find the end of a variable or function name. Unlike find_name_end() this +/// does not recognize magic braces. +/// When "use_namespace" is true recognize "b:", "s:", etc. +/// Return a pointer to just after the name. Equal to "arg" if there is no +/// valid name. +static char *to_name_end(char *arg, bool use_namespace) +{ + // Quick check for valid starting character. + if (!eval_isnamec1