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.c54
1 files changed, 45 insertions, 9 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index bbb6565509..8692ddc334 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -389,6 +389,7 @@ static struct vimvar {
VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO),
VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO),
VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO),
+ VV(VV_TESTING, "testing", VAR_NUMBER, 0),
VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO),
VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO),
VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO),
@@ -648,8 +649,8 @@ void eval_clear(void)
xfree(SCRIPT_SV(i));
ga_clear(&ga_scripts);
- /* unreferenced lists and dicts */
- (void)garbage_collect();
+ // unreferenced lists and dicts
+ (void)garbage_collect(false);
/* functions */
free_all_functions();
@@ -5813,6 +5814,9 @@ int get_copyID(void)
return current_copyID;
}
+// Used by get_func_tv()
+static garray_T funcargs = GA_EMPTY_INIT_VALUE;
+
/*
* Garbage collection for lists and dictionaries.
*
@@ -5835,16 +5839,19 @@ int get_copyID(void)
/// Do garbage collection for lists and dicts.
///
+/// @param testing true if called from test_garbagecollect_now().
/// @returns true if some memory was freed.
-bool garbage_collect(void)
+bool garbage_collect(bool testing)
{
bool abort = false;
#define ABORTING(func) abort = abort || func
- // Only do this once.
- want_garbage_collect = false;
- may_garbage_collect = false;
- garbage_collect_at_exit = false;
+ if (!testing) {
+ // Only do this once.
+ want_garbage_collect = false;
+ may_garbage_collect = false;
+ garbage_collect_at_exit = false;
+ }
// We advance by two because we add one for items referenced through
// previous_funccal.
@@ -5954,6 +5961,12 @@ bool garbage_collect(void)
})
}
+ // function call arguments, if v:testing is set.
+ for (int i = 0; i < funcargs.ga_len; i++) {
+ ABORTING(set_ref_in_item)(((typval_T **)funcargs.ga_data)[i],
+ copyID, NULL, NULL);
+ }
+
// v: vars
ABORTING(set_ref_in_ht)(&vimvarht, copyID, NULL);
@@ -6008,7 +6021,7 @@ bool garbage_collect(void)
if (did_free_funccal) {
// When a funccal was freed some more items might be garbage
// collected, so run again.
- (void)garbage_collect();
+ (void)garbage_collect(testing);
}
} else if (p_verbose > 0) {
verb_msg((char_u *)_(
@@ -7085,9 +7098,24 @@ get_func_tv (
ret = FAIL;
if (ret == OK) {
- ret = call_func(name, len, rettv, argcount, argvars,
+ int i = 0;
+
+ if (get_vim_var_nr(VV_TESTING)) {
+ // Prepare for calling garbagecollect_for_testing(), need to know
+ // what variables are used on the call stack.
+ if (funcargs.ga_itemsize == 0) {
+ ga_init(&funcargs, (int)sizeof(typval_T *), 50);
+ }
+ for (i = 0; i < argcount; i++) {
+ ga_grow(&funcargs, 1);
+ ((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = &argvars[i];
+ }
+ }
+ ret = call_func(name, len, rettv, argcount, argvars, NULL,
firstline, lastline, doesrange, evaluate,
partial, selfdict);
+
+ funcargs.ga_len -= i;
} else if (!aborting()) {
if (argcount == MAX_FUNC_ARGS) {
emsg_funcname(N_("E740: Too many arguments for function %s"), name);
@@ -17306,6 +17334,14 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
+// "test_garbagecollect_now()" function
+static void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ // This is dangerous, any Lists and Dicts used internally may be freed
+ // while still in use.
+ garbage_collect(true);
+}
+
static bool callback_from_typval(Callback *callback, typval_T *arg)
{
if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL) {