aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/eval.c75
-rw-r--r--src/nvim/eval/user_funcs.c91
2 files changed, 115 insertions, 51 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index beea4d05e1..c3ac214d5f 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -2813,22 +2813,23 @@ int do_unlet(const char *const name, const size_t name_len, const int forceit)
hashtab_T *ht = find_var_ht_dict(name, name_len, &varname, &dict);
if (ht != NULL && *varname != NUL) {
- dict_T *d;
- if (ht == &globvarht) {
- d = &globvardict;
- } else if (current_funccal != NULL
- && ht == &current_funccal->l_vars.dv_hashtab) {
- d = &current_funccal->l_vars;
- } else if (ht == &compat_hashtab) {
- d = &vimvardict;
- } else {
- dictitem_T *const di = find_var_in_ht(ht, *name, "", 0, false);
- d = di->di_tv.vval.v_dict;
- }
- if (d == NULL) {
- internal_error("do_unlet()");
- return FAIL;
+ dict_T *d = get_current_funccal_dict(ht);
+ if (d == NULL)
+ {
+ if (ht == &globvarht) {
+ d = &globvardict;
+ } else if (ht == &compat_hashtab) {
+ d = &vimvardict;
+ } else {
+ dictitem_T *const di = find_var_in_ht(ht, *name, "", 0, false);
+ d = di->di_tv.vval.v_dict;
+ }
+ if (d == NULL) {
+ internal_error("do_unlet()");
+ return FAIL;
+ }
}
+
hashitem_T *hi = hash_find(ht, (const char_u *)varname);
if (HASHITEM_EMPTY(hi)) {
hi = find_hi_in_scoped_ht((const char *)name, &ht);
@@ -4878,11 +4879,7 @@ bool garbage_collect(bool testing)
// Don't free variables in the previous_funccal list unless they are only
// referenced through previous_funccal. This must be first, because if
// the item is referenced elsewhere the funccal must not be freed.
- for (funccall_T *fc = previous_funccal; fc != NULL; fc = fc->caller) {
- fc->fc_copyID = copyID + 1;
- ABORTING(set_ref_in_ht)(&fc->l_vars.dv_hashtab, copyID + 1, NULL);
- ABORTING(set_ref_in_ht)(&fc->l_avars.dv_hashtab, copyID + 1, NULL);
- }
+ ABORTING(set_ref_in_previous_funccal)(copyID);
// script-local variables
for (int i = 1; i <= ga_scripts.ga_len; ++i) {
@@ -4959,14 +4956,10 @@ bool garbage_collect(bool testing)
ABORTING(set_ref_in_ht)(&globvarht, copyID, NULL);
// function-local variables
- for (funccall_T *fc = current_funccal; fc != NULL; fc = fc->caller) {
- fc->fc_copyID = copyID;
- ABORTING(set_ref_in_ht)(&fc->l_vars.dv_hashtab, copyID, NULL);
- ABORTING(set_ref_in_ht)(&fc->l_avars.dv_hashtab, copyID, NULL);
- }
+ ABORTING(set_ref_in_call_stack)(copyID);
// named functions (matters for closures)
- ABORTING(set_ref_in_functions(copyID));
+ ABORTING(set_ref_in_functions)(copyID);
// Channels
{
@@ -4987,10 +4980,7 @@ bool garbage_collect(bool testing)
}
// 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);
- }
+ ABORTING(set_ref_in_func_args)(copyID);
// v: vars
ABORTING(set_ref_in_ht)(&vimvarht, copyID, NULL);
@@ -5033,23 +5023,8 @@ bool garbage_collect(bool testing)
did_free = free_unref_items(copyID);
// 3. Check if any funccal can be freed now.
- bool did_free_funccal = false;
- for (funccall_T **pfc = &previous_funccal; *pfc != NULL;) {
- if (can_free_funccal(*pfc, copyID)) {
- funccall_T *fc = *pfc;
- *pfc = fc->caller;
- free_funccal(fc, true);
- did_free = true;
- did_free_funccal = true;
- } else {
- pfc = &(*pfc)->caller;
- }
- }
- if (did_free_funccal) {
- // When a funccal was freed some more items might be garbage
- // collected, so run again.
- (void)garbage_collect(testing);
- }
+ // This may call us back recursively.
+ did_free = did_free || free_unref_funccal(copyID, testing);
} else if (p_verbose > 0) {
verb_msg(_(
"Not enough memory to set references, garbage collection aborted!"));
@@ -8526,10 +8501,8 @@ dictitem_T *find_var_in_ht(hashtab_T *const ht,
case 'b': return (dictitem_T *)&curbuf->b_bufvar;
case 'w': return (dictitem_T *)&curwin->w_winvar;
case 't': return (dictitem_T *)&curtab->tp_winvar;
- case 'l': return (current_funccal == NULL
- ? NULL : (dictitem_T *)&current_funccal->l_vars_var);
- case 'a': return (current_funccal == NULL
- ? NULL : (dictitem_T *)&get_funccal()->l_avars_var);
+ case 'l': return get_funccal_local_var();
+ case 'a': return get_funccal_args_var();
}
return NULL;
}
diff --git a/src/nvim/eval/user_funcs.c b/src/nvim/eval/user_funcs.c
index 7de9922f96..487d290f6b 100644
--- a/src/nvim/eval/user_funcs.c
+++ b/src/nvim/eval/user_funcs.c
@@ -3018,6 +3018,30 @@ int current_func_returned(void)
return current_funccal->returned;
}
+bool free_unref_funccal(int copyID, int testing)
+{
+ bool did_free = false;
+ bool did_free_funccal = false;
+
+ for (funccall_T **pfc = &previous_funccal; *pfc != NULL;) {
+ if (can_free_funccal(*pfc, copyID)) {
+ funccall_T *fc = *pfc;
+ *pfc = fc->caller;
+ free_funccal(fc, true);
+ did_free = true;
+ did_free_funccal = true;
+ } else {
+ pfc = &(*pfc)->caller;
+ }
+ }
+ if (did_free_funccal) {
+ // When a funccal was freed some more items might be garbage
+ // collected, so run again.
+ (void)garbage_collect(testing);
+ }
+ return did_free;
+}
+
// Get function call environment based on backtrace debug level
funccall_T *get_funccal(void)
{
@@ -3047,6 +3071,16 @@ hashtab_T *get_funccal_local_ht(void)
return &get_funccal()->l_vars.dv_hashtab;
}
+/// Return the l: scope variable.
+/// Return NULL if there is no current funccal.
+dictitem_T *get_funccal_local_var(void)
+{
+ if (current_funccal == NULL) {
+ return NULL;
+ }
+ return (dictitem_T *)&get_funccal()->l_vars_var;
+}
+
/// Return the hashtable used for argument in the current funccal.
/// Return NULL if there is no current funccal.
hashtab_T *get_funccal_args_ht(void)
@@ -3057,6 +3091,16 @@ hashtab_T *get_funccal_args_ht(void)
return &get_funccal()->l_avars.dv_hashtab;
}
+/// Return the a: scope variable.
+/// Return NULL if there is no current funccal.
+dictitem_T *get_funccal_args_var(void)
+{
+ if (current_funccal == NULL) {
+ return NULL;
+ }
+ return (dictitem_T *)&current_funccal->l_avars_var;
+}
+
/*
* List function variables, if there is a function.
*/
@@ -3068,6 +3112,17 @@ void list_func_vars(int *first)
}
}
+/// If "ht" is the hashtable for local variables in the current funccal, return
+/// the dict that contains it.
+/// Otherwise return NULL.
+dict_T *get_current_funccal_dict(hashtab_T *ht)
+{
+ if (current_funccal != NULL && ht == &current_funccal->l_vars.dv_hashtab) {
+ return &current_funccal->l_vars;
+ }
+ return NULL;
+}
+
/// Search hashitem in parent scope.
hashitem_T *find_hi_in_scoped_ht(const char *name, hashtab_T **pht)
{
@@ -3134,6 +3189,18 @@ dictitem_T *find_var_in_scoped_ht(const char *name, const size_t namelen,
return v;
}
+/// Set "copyID + 1" in previous_funccal and callers.
+bool set_ref_in_previous_funccal(int copyID)
+{
+ bool abort = false;
+
+ for (funccall_T *fc = previous_funccal; fc != NULL; fc = fc->caller) {
+ abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1, NULL);
+ abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1, NULL);
+ }
+ return abort;
+}
+
static bool set_ref_in_funccal(funccall_T *fc, int copyID)
{
bool abort = false;
@@ -3147,6 +3214,18 @@ static bool set_ref_in_funccal(funccall_T *fc, int copyID)
return abort;
}
+/// Set "copyID" in all local vars and arguments in the call stack.
+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);
+ }
+ return abort;
+}
+
/// Set "copyID" in all functions available by name.
bool set_ref_in_functions(int copyID)
{
@@ -3168,6 +3247,18 @@ bool set_ref_in_functions(int copyID)
return abort;
}
+/// Set "copyID" in all function arguments.
+bool set_ref_in_func_args(int copyID)
+{
+ bool abort = false;
+
+ for (int i = 0; i < funcargs.ga_len; i++) {
+ abort = abort || set_ref_in_item(((typval_T **)funcargs.ga_data)[i],
+ copyID, NULL, NULL);
+ }
+ return abort;
+}
+
/// Mark all lists and dicts referenced through function "name" with "copyID".
/// "list_stack" is used to add lists to be marked. Can be NULL.
/// "ht_stack" is used to add hashtabs to be marked. Can be NULL.