aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r--src/nvim/eval.c180
1 files changed, 130 insertions, 50 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 96fc79504f..0e5da13242 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -378,6 +378,7 @@ static struct vimvar {
{ VV_NAME("option_type", VAR_STRING), VV_RO },
{ VV_NAME("errors", VAR_LIST), 0 },
{ VV_NAME("msgpack_types", VAR_DICT), VV_RO },
+ { VV_NAME("event", VAR_DICT), VV_RO },
};
/* shorthand */
@@ -545,6 +546,10 @@ void eval_init(void)
set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict);
set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc());
+
+ dict_T *v_event = dict_alloc();
+ v_event->dv_lock = VAR_FIXED;
+ set_vim_var_dict(VV_EVENT, v_event);
set_vim_var_list(VV_ERRORS, list_alloc());
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
set_vim_var_nr(VV_HLSEARCH, 1L);
@@ -4010,12 +4015,22 @@ eval6 (
* When either side is a float the result is a float.
*/
if (use_float) {
- if (op == '*')
+ if (op == '*') {
f1 = f1 * f2;
- else if (op == '/') {
- /* We rely on the floating point library to handle divide
- * by zero to result in "inf" and not a crash. */
- f1 = f2 != 0 ? f1 / f2 : INFINITY;
+ } else if (op == '/') {
+ // Division by zero triggers error from AddressSanitizer
+ f1 = (f2 == 0
+ ? (
+#ifdef NAN
+ f1 == 0
+ ? NAN
+ :
+#endif
+ (f1 > 0
+ ? INFINITY
+ : -INFINITY)
+ )
+ : f1 / f2);
} else {
EMSG(_("E804: Cannot use '%' with Float"));
return FAIL;
@@ -6007,6 +6022,27 @@ static void rettv_dict_alloc(typval_T *rettv)
++d->dv_refcount;
}
+/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary.
+///
+/// @param d The Dictionary to clear
+void dict_clear(dict_T *d)
+ FUNC_ATTR_NONNULL_ALL
+{
+ hash_lock(&d->dv_hashtab);
+ assert(d->dv_hashtab.ht_locked > 0);
+
+ size_t todo = d->dv_hashtab.ht_used;
+ for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) {
+ if (!HASHITEM_EMPTY(hi)) {
+ dictitem_free(HI2DI(hi));
+ hash_remove(&d->dv_hashtab, hi);
+ todo--;
+ }
+ }
+
+ hash_unlock(&d->dv_hashtab);
+}
+
/*
* Unreference a Dictionary: decrement the reference count and free it when it
@@ -6248,6 +6284,24 @@ int dict_add_list(dict_T *d, char *key, list_T *list)
return OK;
}
+/// Set all existing keys in "dict" as read-only.
+///
+/// This does not protect against adding new keys to the Dictionary.
+///
+/// @param dict The dict whose keys should be frozen
+void dict_set_keys_readonly(dict_T *dict)
+ FUNC_ATTR_NONNULL_ALL
+{
+ size_t todo = dict->dv_hashtab.ht_used;
+ for (hashitem_T *hi = dict->dv_hashtab.ht_array; todo > 0 ; hi++) {
+ if (HASHITEM_EMPTY(hi)) {
+ continue;
+ }
+ todo--;
+ HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
+ }
+}
+
/*
* Get the number of items in a Dictionary.
*/
@@ -6848,9 +6902,25 @@ vim_to_msgpack_error_ret: \
#define CONV_FLOAT(flt) \
do { \
- char numbuf[NUMBUFLEN]; \
- vim_snprintf(numbuf, NUMBUFLEN - 1, "%g", (flt)); \
- ga_concat(gap, (char_u *) numbuf); \
+ const float_T flt_ = (flt); \
+ switch (fpclassify(flt_)) { \
+ case FP_NAN: { \
+ ga_concat(gap, (char_u *) "str2float('nan')"); \
+ break; \
+ } \
+ case FP_INFINITE: { \
+ if (flt_ < 0) { \
+ ga_append(gap, '-'); \
+ } \
+ ga_concat(gap, (char_u *) "str2float('inf')"); \
+ break; \
+ } \
+ default: { \
+ char numbuf[NUMBUFLEN]; \
+ vim_snprintf(numbuf, NUMBUFLEN - 1, "%g", flt_); \
+ ga_concat(gap, (char_u *) numbuf); \
+ } \
+ } \
} while (0)
#define CONV_FUNC(fun) \
@@ -7290,7 +7360,7 @@ static struct fst {
{ "pathshorten", 1, 1, f_pathshorten },
{ "pow", 2, 2, f_pow },
{ "prevnonblank", 1, 1, f_prevnonblank },
- { "printf", 2, 19, f_printf },
+ { "printf", 2, MAX_FUNC_ARGS, f_printf },
{ "pumvisible", 0, 0, f_pumvisible },
{ "py3eval", 1, 1, f_py3eval },
{ "pyeval", 1, 1, f_pyeval },
@@ -7304,8 +7374,8 @@ static struct fst {
{ "resolve", 1, 1, f_resolve },
{ "reverse", 1, 1, f_reverse },
{ "round", 1, 1, f_round },
- { "rpcnotify", 2, 64, f_rpcnotify },
- { "rpcrequest", 2, 64, f_rpcrequest },
+ { "rpcnotify", 2, MAX_FUNC_ARGS, f_rpcnotify },
+ { "rpcrequest", 2, MAX_FUNC_ARGS, f_rpcrequest },
{ "rpcstart", 1, 2, f_rpcstart },
{ "rpcstop", 1, 1, f_rpcstop },
{ "screenattr", 2, 2, f_screenattr },
@@ -7324,10 +7394,10 @@ static struct fst {
{ "setcharsearch", 1, 1, f_setcharsearch },
{ "setcmdpos", 1, 1, f_setcmdpos },
{ "setline", 2, 2, f_setline },
- { "setloclist", 2, 3, f_setloclist },
+ { "setloclist", 2, 4, f_setloclist },
{ "setmatches", 1, 1, f_setmatches },
{ "setpos", 2, 2, f_setpos },
- { "setqflist", 1, 2, f_setqflist },
+ { "setqflist", 1, 3, f_setqflist },
{ "setreg", 2, 3, f_setreg },
{ "settabvar", 3, 3, f_settabvar },
{ "settabwinvar", 4, 4, f_settabwinvar },
@@ -10553,8 +10623,6 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv)
{
char_u *strregname;
int regname;
- char_u buf[NUMBUFLEN + 2];
- long reglen = 0;
if (argvars[0].v_type != VAR_UNKNOWN) {
strregname = get_tv_string_chk(&argvars[0]);
@@ -10571,18 +10639,13 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv)
if (regname == 0)
regname = '"';
- buf[0] = NUL;
- buf[1] = NUL;
- switch (get_reg_type(regname, &reglen)) {
- case MLINE: buf[0] = 'V'; break;
- case MCHAR: buf[0] = 'v'; break;
- case MBLOCK:
- buf[0] = Ctrl_V;
- sprintf((char *)buf + 1, "%" PRId64, (int64_t)(reglen + 1));
- break;
- }
+ colnr_T reglen = 0;
+ char buf[NUMBUFLEN + 2];
+ char_u reg_type = get_reg_type(regname, &reglen);
+ format_reg_type(reg_type, reglen, buf, ARRAY_SIZE(buf));
+
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = vim_strsave(buf);
+ rettv->vval.v_string = (char_u *)xstrdup(buf);
}
/*
@@ -15215,14 +15278,26 @@ static void f_setline(typval_T *argvars, typval_T *rettv)
appended_lines_mark(lcount, added);
}
-
-/*
- * Used by "setqflist()" and "setloclist()" functions
- */
-static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, typval_T *rettv)
+/// Create quickfix/location list from VimL values
+///
+/// Used by `setqflist()` and `setloclist()` functions. Accepts invalid
+/// list_arg, action_arg and title_arg arguments in which case errors out,
+/// including VAR_UNKNOWN parameters.
+///
+/// @param[in,out] wp Window to create location list for. May be NULL in
+/// which case quickfix list will be created.
+/// @param[in] list_arg Quickfix list contents.
+/// @param[in] action_arg Action to perform: append to an existing list,
+/// replace its content or create a new one.
+/// @param[in] title_arg New list title. Defaults to caller function name.
+/// @param[out] rettv Return value: 0 in case of success, -1 otherwise.
+static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg,
+ typval_T *title_arg, typval_T *rettv)
+ FUNC_ATTR_NONNULL_ARG(2, 3, 4, 5)
{
char_u *act;
int action = ' ';
+ char_u *title = NULL;
rettv->vval.v_number = -1;
@@ -15231,7 +15306,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg,
else {
list_T *l = list_arg->vval.v_list;
- if (action_arg->v_type == VAR_STRING) {
+ if (action_arg->v_type != VAR_UNKNOWN) {
act = get_tv_string_chk(action_arg);
if (act == NULL)
return; /* type error; errmsg already given */
@@ -15239,9 +15314,20 @@ static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg,
action = *act;
}
- if (l != NULL && set_errorlist(wp, l, action,
- (char_u *)(wp == NULL ? "setqflist()" : "setloclist()")) == OK)
+ if (title_arg->v_type == VAR_STRING) {
+ title = get_tv_string_chk(title_arg);
+ if (!title) {
+ return; // type error; errmsg already given
+ }
+ }
+
+ if (!title) {
+ title = (char_u*)(wp ? "setloclist()" : "setqflist()");
+ }
+
+ if (l && set_errorlist(wp, l, action, title) == OK) {
rettv->vval.v_number = 0;
+ }
}
}
@@ -15255,8 +15341,9 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = -1;
win = find_win_by_nr(&argvars[0], NULL);
- if (win != NULL)
- set_qf_ll_list(win, &argvars[1], &argvars[2], rettv);
+ if (win != NULL) {
+ set_qf_ll_list(win, &argvars[1], &argvars[2], &argvars[3], rettv);
+ }
}
/*
@@ -15392,7 +15479,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv)
*/
static void f_setqflist(typval_T *argvars, typval_T *rettv)
{
- set_qf_ll_list(NULL, &argvars[0], &argvars[1], rettv);
+ set_qf_ll_list(NULL, &argvars[0], &argvars[1], &argvars[2], rettv);
}
/*
@@ -18104,23 +18191,16 @@ void set_vim_var_list(int idx, list_T *val)
}
/// Set Dictionary v: variable to "val".
-void set_vim_var_dict(int idx, dict_T *val) FUNC_ATTR_NONNULL_ALL
+void set_vim_var_dict(int idx, dict_T *val)
{
dict_unref(vimvars[idx].vv_dict);
+ vimvars[idx].vv_dict = val;
- // Set readonly
- int todo = (int)val->dv_hashtab.ht_used;
- for (hashitem_T *hi = val->dv_hashtab.ht_array; todo > 0 ; ++hi) {
- if (HASHITEM_EMPTY(hi)) {
- continue;
- }
-
- --todo;
- HI2DI(hi)->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
+ if (val != NULL) {
+ ++val->dv_refcount;
+ // Set readonly
+ dict_set_keys_readonly(val);
}
-
- vimvars[idx].vv_dict = val;
- ++val->dv_refcount;
}
/*