aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval')
-rw-r--r--src/nvim/eval/decode.c12
-rw-r--r--src/nvim/eval/encode.c7
-rw-r--r--src/nvim/eval/encode.h1
-rw-r--r--src/nvim/eval/executor.c4
-rw-r--r--src/nvim/eval/funcs.c63
-rw-r--r--src/nvim/eval/typval.c44
-rw-r--r--src/nvim/eval/typval.h10
-rw-r--r--src/nvim/eval/typval_encode.c.h17
-rw-r--r--src/nvim/eval/userfunc.c160
-rw-r--r--src/nvim/eval/userfunc.h6
10 files changed, 214 insertions, 110 deletions
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
index 42999ddd62..daba304f00 100644
--- a/src/nvim/eval/decode.c
+++ b/src/nvim/eval/decode.c
@@ -795,9 +795,9 @@ json_decode_string_cycle_start:
}
p += 3;
POP(((typval_T) {
- .v_type = VAR_SPECIAL,
+ .v_type = VAR_BOOL,
.v_lock = VAR_UNLOCKED,
- .vval = { .v_special = kSpecialVarTrue },
+ .vval = { .v_bool = kBoolVarTrue },
}), false);
break;
}
@@ -808,9 +808,9 @@ json_decode_string_cycle_start:
}
p += 4;
POP(((typval_T) {
- .v_type = VAR_SPECIAL,
+ .v_type = VAR_BOOL,
.v_lock = VAR_UNLOCKED,
- .vval = { .v_special = kSpecialVarFalse },
+ .vval = { .v_bool = kBoolVarFalse },
}), false);
break;
}
@@ -954,10 +954,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
}
case MSGPACK_OBJECT_BOOLEAN: {
*rettv = (typval_T) {
- .v_type = VAR_SPECIAL,
+ .v_type = VAR_BOOL,
.v_lock = VAR_UNLOCKED,
.vval = {
- .v_special = mobj.via.boolean ? kSpecialVarTrue : kSpecialVarFalse
+ .v_bool = mobj.via.boolean ? kBoolVarTrue : kBoolVarFalse
},
};
break;
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
index 138f638eb2..137f099df6 100644
--- a/src/nvim/eval/encode.c
+++ b/src/nvim/eval/encode.c
@@ -34,10 +34,13 @@
#define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b))
#define utf_char2len(b) ((size_t)utf_char2len(b))
+const char *const encode_bool_var_names[] = {
+ [kBoolVarTrue] = "true",
+ [kBoolVarFalse] = "false",
+};
+
const char *const encode_special_var_names[] = {
[kSpecialVarNull] = "null",
- [kSpecialVarTrue] = "true",
- [kSpecialVarFalse] = "false",
};
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h
index ccea245ab3..596bb49ae0 100644
--- a/src/nvim/eval/encode.h
+++ b/src/nvim/eval/encode.h
@@ -55,6 +55,7 @@ static inline ListReaderState encode_init_lrstate(const list_T *const list)
}
/// Array mapping values from SpecialVarValue enum to names
+extern const char *const encode_bool_var_names[];
extern const char *const encode_special_var_names[];
/// First codepoint in high surrogates block
diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c
index 8cd21f8d62..da05ecda43 100644
--- a/src/nvim/eval/executor.c
+++ b/src/nvim/eval/executor.c
@@ -28,11 +28,13 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2,
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NO_SANITIZE_UNDEFINED
{
// Can't do anything with a Funcref, a Dict or special value on the right.
- if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) {
+ if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT
+ && tv2->v_type != VAR_BOOL && tv2->v_type != VAR_SPECIAL) {
switch (tv1->v_type) {
case VAR_DICT:
case VAR_FUNC:
case VAR_PARTIAL:
+ case VAR_BOOL:
case VAR_SPECIAL: {
break;
}
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 79a52d9779..1071e75c06 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -175,8 +175,8 @@ static int non_zero_arg(typval_T *argvars)
{
return ((argvars[0].v_type == VAR_NUMBER
&& argvars[0].vval.v_number != 0)
- || (argvars[0].v_type == VAR_SPECIAL
- && argvars[0].vval.v_special == kSpecialVarTrue)
+ || (argvars[0].v_type == VAR_BOOL
+ && argvars[0].vval.v_bool == kBoolVarTrue)
|| (argvars[0].v_type == VAR_STRING
&& argvars[0].vval.v_string != NULL
&& *argvars[0].vval.v_string != NUL));
@@ -1758,21 +1758,23 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr)
n = (tv_dict_len(argvars[0].vval.v_dict) == 0);
break;
}
- case VAR_SPECIAL: {
- // Using switch to get warning if SpecialVarValue receives more values.
- switch (argvars[0].vval.v_special) {
- case kSpecialVarTrue: {
+ case VAR_BOOL: {
+ switch (argvars[0].vval.v_bool) {
+ case kBoolVarTrue: {
n = false;
break;
}
- case kSpecialVarFalse:
- case kSpecialVarNull: {
+ case kBoolVarFalse: {
n = true;
break;
}
}
break;
}
+ case VAR_SPECIAL: {
+ n = argvars[0].vval.v_special == kSpecialVarNull;
+ break;
+ }
case VAR_UNKNOWN: {
internal_error("f_empty(UNKNOWN)");
break;
@@ -4860,6 +4862,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
bool rpc = false;
bool pty = false;
bool clear_env = false;
+ bool overlapped = false;
CallbackReader on_stdout = CALLBACK_READER_INIT,
on_stderr = CALLBACK_READER_INIT;
Callback on_exit = CALLBACK_NONE;
@@ -4871,12 +4874,23 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rpc = tv_dict_get_number(job_opts, "rpc") != 0;
pty = tv_dict_get_number(job_opts, "pty") != 0;
clear_env = tv_dict_get_number(job_opts, "clear_env") != 0;
+ overlapped = tv_dict_get_number(job_opts, "overlapped") != 0;
+
if (pty && rpc) {
EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set");
shell_free_argv(argv);
return;
}
+#ifdef WIN32
+ if (pty && overlapped) {
+ EMSG2(_(e_invarg2),
+ "job cannot have both 'pty' and 'overlapped' options set");
+ shell_free_argv(argv);
+ return;
+ }
+#endif
+
char *new_cwd = tv_dict_get_string(job_opts, "cwd", false);
if (new_cwd && strlen(new_cwd) > 0) {
cwd = new_cwd;
@@ -4943,7 +4957,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty,
- rpc, detach, cwd, width, height,
+ rpc, overlapped, detach, cwd, width, height,
term_name, env, &rettv->vval.v_number);
if (chan) {
channel_create_event(chan, NULL);
@@ -5189,6 +5203,7 @@ static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr)
break;
}
case VAR_UNKNOWN:
+ case VAR_BOOL:
case VAR_SPECIAL:
case VAR_FLOAT:
case VAR_PARTIAL:
@@ -7243,7 +7258,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
uint8_t *save_sourcing_name, *save_autocmd_fname, *save_autocmd_match;
linenr_T save_sourcing_lnum;
int save_autocmd_bufnr;
- void *save_funccalp;
+ funccal_entry_T funccal_entry;
if (l_provider_call_nesting) {
// If this is called from a provider function, restore the scope
@@ -7254,7 +7269,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
save_autocmd_fname = autocmd_fname;
save_autocmd_match = autocmd_match;
save_autocmd_bufnr = autocmd_bufnr;
- save_funccalp = save_funccal();
+ save_funccal(&funccal_entry);
current_sctx = provider_caller_scope.script_ctx;
sourcing_name = provider_caller_scope.sourcing_name;
@@ -7262,7 +7277,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
autocmd_fname = provider_caller_scope.autocmd_fname;
autocmd_match = provider_caller_scope.autocmd_match;
autocmd_bufnr = provider_caller_scope.autocmd_bufnr;
- restore_funccal(provider_caller_scope.funccalp);
+ set_current_funccal((funccall_T *)(provider_caller_scope.funccalp));
}
@@ -7280,7 +7295,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
autocmd_fname = save_autocmd_fname;
autocmd_match = save_autocmd_match;
autocmd_bufnr = save_autocmd_bufnr;
- restore_funccal(save_funccalp);
+ restore_funccal();
}
if (ERROR_SET(&err)) {
@@ -7369,8 +7384,8 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT,
CALLBACK_READER_INIT, CALLBACK_NONE,
- false, true, false, NULL, 0, 0, NULL, NULL,
- &rettv->vval.v_number);
+ false, true, false, false, NULL, 0, 0,
+ NULL, NULL, &rettv->vval.v_number);
if (chan) {
channel_create_event(chan, NULL);
}
@@ -10458,7 +10473,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
uint16_t term_width = MAX(0, curwin->w_width_inner - win_col_off(curwin));
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit,
- true, false, false, cwd,
+ true, false, false, false, cwd,
term_width, curwin->w_height_inner,
xstrdup("xterm-256color"), NULL,
&rettv->vval.v_number);
@@ -10808,20 +10823,8 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr)
case VAR_LIST: n = VAR_TYPE_LIST; break;
case VAR_DICT: n = VAR_TYPE_DICT; break;
case VAR_FLOAT: n = VAR_TYPE_FLOAT; break;
- case VAR_SPECIAL: {
- switch (argvars[0].vval.v_special) {
- case kSpecialVarTrue:
- case kSpecialVarFalse: {
- n = VAR_TYPE_BOOL;
- break;
- }
- case kSpecialVarNull: {
- n = 7;
- break;
- }
- }
- break;
- }
+ case VAR_BOOL: n = VAR_TYPE_BOOL; break;
+ case VAR_SPECIAL:n = VAR_TYPE_SPECIAL; break;
case VAR_UNKNOWN: {
internal_error("f_type(UNKNOWN)");
break;
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 35130f6f40..0daaf6c878 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -1693,21 +1693,21 @@ int tv_dict_add_float(dict_T *const d, const char *const key,
return OK;
}
-/// Add a special entry to dictionary
+/// Add a boolean entry to dictionary
///
/// @param[out] d Dictionary to add entry to.
/// @param[in] key Key to add.
/// @param[in] key_len Key length.
-/// @param[in] val SpecialVarValue to add.
+/// @param[in] val BoolVarValue to add.
///
/// @return OK in case of success, FAIL when key already exists.
-int tv_dict_add_special(dict_T *const d, const char *const key,
- const size_t key_len, SpecialVarValue val)
+int tv_dict_add_bool(dict_T *const d, const char *const key,
+ const size_t key_len, BoolVarValue val)
{
dictitem_T *const item = tv_dict_item_alloc_len(key, key_len);
- item->di_tv.v_type = VAR_SPECIAL;
- item->di_tv.vval.v_special = val;
+ item->di_tv.v_type = VAR_BOOL;
+ item->di_tv.vval.v_bool = val;
if (tv_dict_add(d, item) == FAIL) {
tv_dict_item_free(item);
return FAIL;
@@ -2013,12 +2013,15 @@ void tv_dict_alloc_ret(typval_T *const ret_tv)
#define TYPVAL_ENCODE_CONV_NIL(tv) \
do { \
- tv->vval.v_special = kSpecialVarFalse; \
+ tv->vval.v_special = kSpecialVarNull; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
- TYPVAL_ENCODE_CONV_NIL(tv)
+ do { \
+ tv->vval.v_bool = kBoolVarFalse; \
+ tv->v_lock = VAR_UNLOCKED; \
+ } while (0)
#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
do { \
@@ -2293,6 +2296,7 @@ void tv_free(typval_T *tv)
tv_dict_unref(tv->vval.v_dict);
break;
}
+ case VAR_BOOL:
case VAR_SPECIAL:
case VAR_NUMBER:
case VAR_FLOAT:
@@ -2324,6 +2328,7 @@ void tv_copy(const typval_T *const from, typval_T *const to)
switch (from->v_type) {
case VAR_NUMBER:
case VAR_FLOAT:
+ case VAR_BOOL:
case VAR_SPECIAL: {
break;
}
@@ -2425,6 +2430,7 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock)
case VAR_STRING:
case VAR_FUNC:
case VAR_PARTIAL:
+ case VAR_BOOL:
case VAR_SPECIAL: {
break;
}
@@ -2588,6 +2594,9 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic,
const char *s2 = tv_get_string_buf(tv2, buf2);
return mb_strcmp_ic((bool)ic, s1, s2) == 0;
}
+ case VAR_BOOL: {
+ return tv1->vval.v_bool == tv2->vval.v_bool;
+ }
case VAR_SPECIAL: {
return tv1->vval.v_special == tv2->vval.v_special;
}
@@ -2638,6 +2647,10 @@ bool tv_check_str_or_nr(const typval_T *const tv)
EMSG(_("E728: Expected a Number or a String, Dictionary found"));
return false;
}
+ case VAR_BOOL: {
+ EMSG(_("E5299: Expected a Number or a String, Boolean found"));
+ return false;
+ }
case VAR_SPECIAL: {
EMSG(_("E5300: Expected a Number or a String"));
return false;
@@ -2677,6 +2690,7 @@ bool tv_check_num(const typval_T *const tv)
{
switch (tv->v_type) {
case VAR_NUMBER:
+ case VAR_BOOL:
case VAR_SPECIAL:
case VAR_STRING: {
return true;
@@ -2721,6 +2735,7 @@ bool tv_check_str(const typval_T *const tv)
{
switch (tv->v_type) {
case VAR_NUMBER:
+ case VAR_BOOL:
case VAR_SPECIAL:
case VAR_STRING: {
return true;
@@ -2791,8 +2806,11 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error)
}
return n;
}
+ case VAR_BOOL: {
+ return tv->vval.v_bool == kBoolVarTrue ? 1 : 0;
+ }
case VAR_SPECIAL: {
- return tv->vval.v_special == kSpecialVarTrue ? 1 : 0;
+ return 0;
}
case VAR_UNKNOWN: {
emsgf(_(e_intern2), "tv_get_number(UNKNOWN)");
@@ -2860,6 +2878,10 @@ float_T tv_get_float(const typval_T *const tv)
EMSG(_("E894: Using a Dictionary as a Float"));
break;
}
+ case VAR_BOOL: {
+ EMSG(_("E362: Using a boolean value as a Float"));
+ break;
+ }
case VAR_SPECIAL: {
EMSG(_("E907: Using a special value as a Float"));
break;
@@ -2897,6 +2919,10 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf)
}
return "";
}
+ case VAR_BOOL: {
+ STRCPY(buf, encode_bool_var_names[tv->vval.v_bool]);
+ return buf;
+ }
case VAR_SPECIAL: {
STRCPY(buf, encode_special_var_names[tv->vval.v_special]);
return buf;
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index 4390db1b71..343dd205ff 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -91,10 +91,14 @@ typedef struct dict_watcher {
bool busy; // prevent recursion if the dict is changed in the callback
} DictWatcher;
+/// Bool variable values
+typedef enum {
+ kBoolVarFalse, ///< v:false
+ kBoolVarTrue, ///< v:true
+} BoolVarValue;
+
/// Special variable values
typedef enum {
- kSpecialVarFalse, ///< v:false
- kSpecialVarTrue, ///< v:true
kSpecialVarNull, ///< v:null
} SpecialVarValue;
@@ -114,6 +118,7 @@ typedef enum {
VAR_LIST, ///< List, .v_list is used.
VAR_DICT, ///< Dictionary, .v_dict is used.
VAR_FLOAT, ///< Floating-point value, .v_float is used.
+ VAR_BOOL, ///< true, false
VAR_SPECIAL, ///< Special value (true, false, null), .v_special
///< is used.
VAR_PARTIAL, ///< Partial, .v_partial is used.
@@ -125,6 +130,7 @@ typedef struct {
VarLockStatus v_lock; ///< Variable lock status.
union typval_vval_union {
varnumber_T v_number; ///< Number, for VAR_NUMBER.
+ BoolVarValue v_bool; ///< Bool value, for VAR_BOOL
SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL.
float_T v_float; ///< Floating-point number, for VAR_FLOAT.
char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL.
diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h
index af21a6fbe3..0aa64b1d5f 100644
--- a/src/nvim/eval/typval_encode.c.h
+++ b/src/nvim/eval/typval_encode.c.h
@@ -379,17 +379,22 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, _mp_last(*mpstack));
break;
}
+ case VAR_BOOL: {
+ switch (tv->vval.v_bool) {
+ case kBoolVarTrue:
+ case kBoolVarFalse: {
+ TYPVAL_ENCODE_CONV_BOOL(tv, tv->vval.v_bool == kBoolVarTrue);
+ break;
+ }
+ }
+ break;
+ }
case VAR_SPECIAL: {
switch (tv->vval.v_special) {
case kSpecialVarNull: {
TYPVAL_ENCODE_CONV_NIL(tv); // -V1037
break;
}
- case kSpecialVarTrue:
- case kSpecialVarFalse: {
- TYPVAL_ENCODE_CONV_BOOL(tv, tv->vval.v_special == kSpecialVarTrue);
- break;
- }
}
break;
}
@@ -607,7 +612,7 @@ _convert_one_value_regular_dict: {}
kMPConvDict);
TYPVAL_ENCODE_CONV_DICT_START(tv, tv->vval.v_dict,
tv->vval.v_dict->dv_hashtab.ht_used);
- assert(saved_copyID != copyID && saved_copyID != copyID - 1);
+ assert(saved_copyID != copyID);
_mp_push(*mpstack, ((MPConvStackVal) {
.tv = tv,
.type = kMPConvDict,
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index ae8557a8bc..4d658498c1 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -43,11 +43,11 @@ hashtab_T func_hashtab;
static garray_T funcargs = GA_EMPTY_INIT_VALUE;
// pointer to funccal for currently active function
-funccall_T *current_funccal = NULL;
+static funccall_T *current_funccal = NULL;
// Pointer to list of previously used funccal, still around because some
// item in it is still being used.
-funccall_T *previous_funccal = NULL;
+static funccall_T *previous_funccal = NULL;
static char *e_funcexts = N_(
"E122: Function %s already exists, add ! to replace it");
@@ -170,6 +170,7 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
garray_T newargs = GA_EMPTY_INIT_VALUE;
garray_T *pnewargs;
ufunc_T *fp = NULL;
+ partial_T *pt = NULL;
int varargs;
int ret;
char_u *start = skipwhite(*arg + 1);
@@ -219,7 +220,6 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
int len, flags = 0;
char_u *p;
char_u name[20];
- partial_T *pt;
garray_T newlines;
lambda_no++;
@@ -274,6 +274,7 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
errret:
ga_clear_strings(&newargs);
xfree(fp);
+ xfree(pt);
eval_lavars_used = old_eval_lavars;
return FAIL;
}
@@ -541,14 +542,8 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr)
v->di_tv.vval.v_number = nr;
}
-/*
- * Free "fc" and what it contains.
- */
-static void
-free_funccal(
- funccall_T *fc,
- int free_val // a: vars were allocated
-)
+// Free "fc"
+static void free_funccal(funccall_T *fc)
{
for (int i = 0; i < fc->fc_funcs.ga_len; i++) {
ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i];
@@ -563,56 +558,89 @@ free_funccal(
}
ga_clear(&fc->fc_funcs);
- // The a: variables typevals may not have been allocated, only free the
- // allocated variables.
- vars_clear_ext(&fc->l_avars.dv_hashtab, free_val);
+ func_ptr_unref(fc->func);
+ xfree(fc);
+}
+// Free "fc" and what it contains.
+// Can be called only when "fc" is kept beyond the period of it called,
+// i.e. after cleanup_function_call(fc).
+static void free_funccal_contents(funccall_T *fc)
+{
// Free all l: variables.
vars_clear(&fc->l_vars.dv_hashtab);
- // Free the a:000 variables if they were allocated.
- if (free_val) {
- TV_LIST_ITER(&fc->l_varlist, li, {
- tv_clear(TV_LIST_ITEM_TV(li));
- });
- }
+ // Free all a: variables.
+ vars_clear(&fc->l_avars.dv_hashtab);
- func_ptr_unref(fc->func);
- xfree(fc);
+ // Free the a:000 variables.
+ TV_LIST_ITER(&fc->l_varlist, li, {
+ tv_clear(TV_LIST_ITEM_TV(li));
+ });
+
+ free_funccal(fc);
}
/// Handle the last part of returning from a function: free the local hashtable.
/// Unless it is still in use by a closure.
static void cleanup_function_call(funccall_T *fc)
{
+ bool may_free_fc = fc->fc_refcount <= 0;
+ bool free_fc = true;
+
current_funccal = fc->caller;
- // If the a:000 list and the l: and a: dicts are not referenced and there
- // is no closure using it, we can free the funccall_T and what's in it.
- if (!fc_referenced(fc)) {
- free_funccal(fc, false);
+ // Free all l: variables if not referred.
+ if (may_free_fc && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT) {
+ vars_clear(&fc->l_vars.dv_hashtab);
} else {
- static int made_copy = 0;
+ free_fc = false;
+ }
- // "fc" is still in use. This can happen when returning "a:000",
- // assigning "l:" to a global variable or defining a closure.
- // Link "fc" in the list for garbage collection later.
- fc->caller = previous_funccal;
- previous_funccal = fc;
+ // If the a:000 list and the l: and a: dicts are not referenced and
+ // there is no closure using it, we can free the funccall_T and what's
+ // in it.
+ if (may_free_fc && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT) {
+ vars_clear_ext(&fc->l_avars.dv_hashtab, false);
+ } else {
+ free_fc = false;
// Make a copy of the a: variables, since we didn't do that above.
TV_DICT_ITER(&fc->l_avars, di, {
tv_copy(&di->di_tv, &di->di_tv);
});
+ }
+
+ if (may_free_fc && fc->l_varlist.lv_refcount // NOLINT(runtime/deprecated)
+ == DO_NOT_FREE_CNT) {
+ fc->l_varlist.lv_first = NULL; // NOLINT(runtime/deprecated)
+
+ } else {
+ free_fc = false;
// Make a copy of the a:000 items, since we didn't do that above.
TV_LIST_ITER(&fc->l_varlist, li, {
tv_copy(TV_LIST_ITEM_TV(li), TV_LIST_ITEM_TV(li));
});
+ }
- if (++made_copy == 10000) {
- // We have made a lot of copies. This can happen when
- // repetitively calling a function that creates a reference to
+ if (free_fc) {
+ free_funccal(fc);
+ } else {
+ static int made_copy = 0;
+
+ // "fc" is still in use. This can happen when returning "a:000",
+ // assigning "l:" to a global variable or defining a closure.
+ // Link "fc" in the list for garbage collection later.
+ fc->caller = previous_funccal;
+ previous_funccal = fc;
+
+ if (want_garbage_collect) {
+ // If garbage collector is ready, clear count.
+ made_copy = 0;
+ } else if (++made_copy >= (int)((4096 * 1024) / sizeof(*fc))) {
+ // We have made a lot of copies, worth 4 Mbyte. This can happen
+ // when repetitively calling a function that creates a reference to
// itself somehow. Call the garbage collector soon to avoid using
// too much memory.
made_copy = 0;
@@ -639,7 +667,7 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force)
for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) {
if (fc == *pfc) {
*pfc = fc->caller;
- free_funccal(fc, true);
+ free_funccal_contents(fc);
return;
}
}
@@ -766,7 +794,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
// check for CTRL-C hit
line_breakcheck();
// prepare the funccall_T structure
- fc = xmalloc(sizeof(funccall_T));
+ fc = xcalloc(1, sizeof(funccall_T));
fc->caller = current_funccal;
current_funccal = fc;
fc->func = fp;
@@ -881,9 +909,11 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
}
if (ai >= 0 && ai < MAX_FUNC_ARGS) {
- tv_list_append(&fc->l_varlist, &fc->l_listitems[ai]);
- *TV_LIST_ITEM_TV(&fc->l_listitems[ai]) = argvars[i];
- TV_LIST_ITEM_TV(&fc->l_listitems[ai])->v_lock = VAR_FIXED;
+ listitem_T *li = &fc->l_listitems[ai];
+
+ *TV_LIST_ITEM_TV(li) = argvars[i];
+ TV_LIST_ITEM_TV(li)->v_lock = VAR_FIXED;
+ tv_list_append(&fc->l_varlist, li);
}
}
@@ -1106,21 +1136,26 @@ static bool func_name_refcount(char_u *name)
return isdigit(*name) || *name == '<';
}
-/*
- * Save the current function call pointer, and set it to NULL.
- * Used when executing autocommands and for ":source".
- */
-void *save_funccal(void)
-{
- funccall_T *fc = current_funccal;
+static funccal_entry_T *funccal_stack = NULL;
+// Save the current function call pointer, and set it to NULL.
+// Used when executing autocommands and for ":source".
+void save_funccal(funccal_entry_T *entry)
+{
+ entry->top_funccal = current_funccal;
+ entry->next = funccal_stack;
+ funccal_stack = entry;
current_funccal = NULL;
- return (void *)fc;
}
-void restore_funccal(void *vfc)
+void restore_funccal(void)
{
- current_funccal = (funccall_T *)vfc;
+ if (funccal_stack == NULL) {
+ IEMSG("INTERNAL: restore_funccal()");
+ } else {
+ current_funccal = funccal_stack->top_funccal;
+ funccal_stack = funccal_stack->next;
+ }
}
funccall_T *get_current_funccal(void)
@@ -1128,6 +1163,11 @@ funccall_T *get_current_funccal(void)
return current_funccal;
}
+void set_current_funccal(funccall_T *fc)
+{
+ current_funccal = fc;
+}
+
#if defined(EXITFREE)
void free_all_functions(void)
{
@@ -1137,10 +1177,13 @@ void free_all_functions(void)
uint64_t todo = 1;
uint64_t used;
- // Clean up the call stack.
+ // Clean up the current_funccal chain and the funccal stack.
while (current_funccal != NULL) {
tv_clear(current_funccal->rettv);
cleanup_function_call(current_funccal);
+ if (current_funccal == NULL && funccal_stack != NULL) {
+ restore_funccal();
+ }
}
// First clear what the functions contain. Since this may lower the
@@ -3121,7 +3164,7 @@ bool free_unref_funccal(int copyID, int testing)
if (can_free_funccal(*pfc, copyID)) {
funccall_T *fc = *pfc;
*pfc = fc->caller;
- free_funccal(fc, true);
+ free_funccal_contents(fc);
did_free = true;
did_free_funccal = true;
} else {
@@ -3314,9 +3357,18 @@ bool set_ref_in_call_stack(int copyID)
bool abort = false;
for (funccall_T *fc = current_funccal; fc != NULL; fc = fc->caller) {
- abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL);
- abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL);
+ abort = abort || set_ref_in_funccal(fc, copyID);
}
+
+ // Also go through the funccal_stack.
+ for (funccal_entry_T *entry = funccal_stack; entry != NULL;
+ entry = entry->next) {
+ for (funccall_T *fc = entry->top_funccal; !abort && fc != NULL;
+ fc = fc->caller) {
+ abort = abort || set_ref_in_funccal(fc, copyID);
+ }
+ }
+
return abort;
}
diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h
index ad8e071548..e8ad0bf1da 100644
--- a/src/nvim/eval/userfunc.h
+++ b/src/nvim/eval/userfunc.h
@@ -11,6 +11,12 @@ typedef struct {
dictitem_T *fd_di; ///< Dictionary item used.
} funcdict_T;
+typedef struct funccal_entry funccal_entry_T;
+struct funccal_entry {
+ void *top_funccal;
+ funccal_entry_T *next;
+};
+
/// errors for when calling a function
typedef enum {
ERROR_UNKNOWN = 0,