summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/repeat.txt7
-rw-r--r--runtime/doc/todo.txt7
-rw-r--r--src/eval.c255
-rw-r--r--src/ex_cmds.h2
-rw-r--r--src/ex_cmds2.c46
5 files changed, 281 insertions, 36 deletions
diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt
index c08f12303..20c3673be 100644
--- a/runtime/doc/repeat.txt
+++ b/runtime/doc/repeat.txt
@@ -1,4 +1,4 @@
-*repeat.txt* For Vim version 7.0aa. Last change: 2005 Jun 25
+*repeat.txt* For Vim version 7.0aa. Last change: 2005 Jun 26
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -579,6 +579,11 @@ It is only included when Vim was compiled with "huge" features.
this command.
+:profd[el] ... *:profd* *:profdel*
+ Stop profiling for the arguments specified. See |:breakdel|
+ for the arguments.
+
+
You must always start with a ":profile start fname" command. The resulting
file is written when Vim exits. Here is an example of the output, with line
numbers prepended for the explanation:
diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt
index f9a221754..b41ba4945 100644
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -1,4 +1,4 @@
-*todo.txt* For Vim version 7.0aa. Last change: 2005 Jun 25
+*todo.txt* For Vim version 7.0aa. Last change: 2005 Jun 26
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -30,6 +30,9 @@ be worked on, but only if you sponsor Vim development. See |sponsor|.
*known-bugs*
-------------------- Known bugs and current work -----------------------
+Is the fix for memory leaks in unreferencing dict/list correct? Is there a
+situation where a reference from outside of the structure is not counted?
+
Add extra list of file locations. Can be used with:
:ltag list of matching tags, like :tselect
@@ -116,7 +119,7 @@ PLANNED FOR VERSION 7.0:
- Simple and fast sound-a-like: mapping list for first char and rest
vowel as first char: *
remove other vowels
- - Cleanup spell help.
+ - Proofread and cleanup spell help.
- Use "engspchk" from Charles Campbell for ideas (commands, rare words).
- Make "en-rare" spell file? Ask Charles Campbell.
- References: MySpell library (in OpenOffice.org).
diff --git a/src/eval.c b/src/eval.c
index f3e31e281..b56c34163 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -123,6 +123,12 @@ static dictitem_T globvars_var;
static hashtab_T compat_hashtab;
/*
+ * When recursively copying lists and dicts we need to remember which ones we
+ * have done to avoid endless recursiveness. This unique ID is used for that.
+ */
+static int current_copyID = 0;
+
+/*
* Array to hold the hashtab with variables local to each sourced script.
* Each item holds a variable (nameless) that points to the dict_T.
*/
@@ -185,6 +191,8 @@ struct ufunc
#define FC_RANGE 2 /* function accepts range */
#define FC_DICT 4 /* Dict function, uses "self" */
+#define DEL_REFCOUNT 999999 /* list/dict is being deleted */
+
/*
* All user-defined functions are found in this hash table.
*/
@@ -374,6 +382,11 @@ static void list_remove __ARGS((list_T *l, listitem_T *item, listitem_T *item2))
static char_u *list2string __ARGS((typval_T *tv));
static int list_join __ARGS((garray_T *gap, list_T *l, char_u *sep, int echo));
+static int count_self_ref __ARGS((void *p, int type));
+static int count_ref_in_dict __ARGS((dict_T *d, void *rp, int copyID, garray_T *gap));
+static int count_ref_in_list __ARGS((list_T *l, void *rp, int copyID, garray_T *gap));
+static int count_ref_item __ARGS((typval_T *tv, void *rp, int copyID, garray_T *gap));
+
static void dict_unref __ARGS((dict_T *d));
static void dict_free __ARGS((dict_T *d));
static dictitem_T *dictitem_alloc __ARGS((char_u *key));
@@ -719,7 +732,10 @@ eval_clear()
{
p = &vimvars[i];
if (p->vv_di.di_tv.v_type == VAR_STRING)
+ {
vim_free(p->vv_di.di_tv.vval.v_string);
+ p->vv_di.di_tv.vval.v_string = NULL;
+ }
}
hash_clear(&vimvarht);
hash_clear(&compat_hashtab);
@@ -728,9 +744,12 @@ eval_clear()
for (i = 1; i <= ga_scripts.ga_len; ++i)
vars_clear(&SCRIPT_VARS(i));
ga_clear(&ga_scripts);
+ free_scriptnames();
/* global variables */
vars_clear(&globvarht);
+
+ free_all_functions();
}
#endif
@@ -4937,8 +4956,23 @@ list_alloc()
list_unref(l)
list_T *l;
{
- if (l != NULL && --l->lv_refcount <= 0)
- list_free(l);
+ int selfref;
+
+ if (l != NULL && l->lv_refcount != DEL_REFCOUNT)
+ {
+ if (--l->lv_refcount > 0)
+ {
+ /* Check if the dict contains references to itself. These need to
+ * be subtracted from the reference count to find out if we can
+ * delete the dict. */
+ selfref = count_self_ref(l, VAR_LIST);
+ }
+ else
+ selfref = 0;
+ if (l->lv_refcount - selfref == 0)
+ /* No references to the list now, free it. */
+ list_free(l);
+ }
}
/*
@@ -4950,11 +4984,14 @@ list_free(l)
list_T *l;
{
listitem_T *item;
- listitem_T *next;
- for (item = l->lv_first; item != NULL; item = next)
+ /* Avoid that recursive reference to the list frees us again. */
+ l->lv_refcount = DEL_REFCOUNT;
+
+ for (item = l->lv_first; item != NULL; item = l->lv_first)
{
- next = item->li_next;
+ /* Remove the item before deleting it. */
+ l->lv_first = item->li_next;
listitem_free(item);
}
vim_free(l);
@@ -5531,6 +5568,160 @@ list_join(gap, l, sep, echo)
}
/*
+ * Count the number of references for list/dict "p" inside itself.
+ * This is used to find out if there are no more references elsewhere.
+ * The tricky bit is that we must not count references in lists/dicts that are
+ * used elsewhere, but we can only know by counting their references...
+ * This is a bit slow, but required to avoid leaking memory.
+ */
+ static int
+count_self_ref(p, type)
+ void *p;
+ int type;
+{
+ garray_T ga;
+ typval_T *tv;
+ int selfref;
+ int i;
+ int n;
+
+ ga_init2(&ga, sizeof(typval_T *), 10);
+ if (type == VAR_DICT)
+ selfref = count_ref_in_dict(p, p, ++current_copyID, &ga);
+ else
+ selfref = count_ref_in_list(p, p, ++current_copyID, &ga);
+ for (i = 0; i < ga.ga_len; ++i)
+ {
+ tv = ((typval_T **)ga.ga_data)[i];
+ if (tv->v_type == VAR_DICT)
+ {
+ n = count_ref_in_dict(tv->vval.v_dict, tv->vval.v_dict,
+ ++current_copyID, NULL);
+ if (n < tv->vval.v_dict->dv_refcount)
+ {
+ selfref = 0;
+ break;
+ }
+ }
+ else
+ {
+ n = count_ref_in_list(tv->vval.v_list, tv->vval.v_list,
+ ++current_copyID, NULL);
+ if (n < tv->vval.v_list->lv_refcount)
+ {
+ selfref = 0;
+ break;
+ }
+ }
+ }
+
+ ga_clear(&ga);
+ return selfref;
+}
+
+/*
+ * Count number of references to "rp" in dictionary "d" and its members.
+ * We use "copyID" to avoid recursing into the same list/dict twice.
+ */
+ static int
+count_ref_in_dict(d, rp, copyID, gap)
+ dict_T *d;
+ void *rp;
+ int copyID;
+ garray_T *gap;
+{
+ int todo;
+ hashitem_T *hi;
+ int n = 0;
+
+ todo = d->dv_hashtab.ht_used;
+ for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
+ if (!HASHITEM_EMPTY(hi))
+ {
+ --todo;
+ n += count_ref_item(&HI2DI(hi)->di_tv, rp, copyID, gap);
+ }
+ return n;
+}
+
+/*
+ * Count number of references to "rp" in list "l" and its members.
+ * We use "copyID" to avoid recursing into the same list/dict twice.
+ */
+ static int
+count_ref_in_list(l, rp, copyID, gap)
+ list_T *l;
+ void *rp;
+ int copyID;
+ garray_T *gap;
+{
+ listitem_T *li;
+ int n = 0;
+
+ for (li = l->lv_first; li != NULL; li = li->li_next)
+ n += count_ref_item(&li->li_tv, rp, copyID, gap);
+ return n;
+}
+
+/*
+ * Count number of references to "rp" in item "tv" and any members.
+ * We use "copyID" to avoid recursing into the same list/dict twice.
+ * When "gap" is not NULL store items that require checking for only
+ * references inside the structure.
+ */
+ static int
+count_ref_item(tv, rp, copyID, gap)
+ typval_T *tv;
+ void *rp;
+ int copyID;
+ garray_T *gap;
+{
+ dict_T *dd;
+ list_T *ll;
+ int n;
+
+ switch (tv->v_type)
+ {
+ case VAR_DICT:
+ dd = tv->vval.v_dict;
+ if (dd == rp)
+ return 1; /* match, count it */
+ if (dd->dv_copyID == copyID)
+ return 0; /* already inspected this dict */
+ dd->dv_copyID = copyID;
+ n = count_ref_in_dict(dd, rp, copyID, gap);
+ if (n > 0 && gap != NULL && dd->dv_refcount > 1)
+ {
+ /* We must later check that the references to this dict are
+ * all in the structure we are freeing. */
+ if (ga_grow(gap, 1) == FAIL)
+ return 0;
+ ((typval_T **)gap->ga_data)[gap->ga_len++] = tv;
+ }
+ return n;
+
+ case VAR_LIST:
+ ll = tv->vval.v_list;
+ if (ll == rp)
+ return 1; /* match, count it */
+ if (ll->lv_copyID == copyID)
+ return 0; /* already inspected this list */
+ ll->lv_copyID = copyID;
+ n = count_ref_in_list(ll, rp, copyID, gap);
+ if (n > 0 && gap != NULL && ll->lv_refcount > 1)
+ {
+ /* We must later check that the references to this list are
+ * all in the structure we are freeing. */
+ if (ga_grow(gap, 1) == FAIL)
+ return 0;
+ ((typval_T **)gap->ga_data)[gap->ga_len++] = tv;
+ }
+ return n;
+ }
+ return 0;
+}
+
+/*
* Allocate an empty header for a dictionary.
*/
dict_T *
@@ -5557,8 +5748,23 @@ dict_alloc()
dict_unref(d)
dict_T *d;
{
- if (d != NULL && --d->dv_refcount <= 0)
- dict_free(d);
+ int selfref;
+
+ if (d != NULL && d->dv_refcount != DEL_REFCOUNT)
+ {
+ if (--d->dv_refcount > 0)
+ {
+ /* Check if the dict contains references to itself. These need to
+ * be subtracted from the reference count to find out if we can
+ * delete the dict. */
+ selfref = count_self_ref(d, VAR_DICT);
+ }
+ else
+ selfref = 0;
+ if (d->dv_refcount - selfref == 0)
+ /* No references to the dict now, free it. */
+ dict_free(d);
+ }
}
/*
@@ -5571,15 +5777,24 @@ dict_free(d)
{
int todo;
hashitem_T *hi;
+ dictitem_T *di;
- /* Careful: we free the dictitems while they still appear in the
- * hashtab. Must not try to resize the hashtab! */
+ /* Avoid that recursive reference to the dict frees us again. */
+ d->dv_refcount = DEL_REFCOUNT;
+
+ /* Lock the hashtab, we don't want it to resize while looping through it.
+ * */
+ hash_lock(&d->dv_hashtab);
todo = d->dv_hashtab.ht_used;
for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
{
if (!HASHITEM_EMPTY(hi))
{
- dictitem_free(HI2DI(hi));
+ /* Remove the item before deleting it, just in case there is
+ * something recursive causing trouble. */
+ di = HI2DI(hi);
+ hash_remove(&d->dv_hashtab, hi);
+ dictitem_free(di);
--todo;
}
}
@@ -7663,7 +7878,6 @@ f_deepcopy(argvars, rettv)
typval_T *argvars;
typval_T *rettv;
{
- static int copyID = 0;
int noref = 0;
if (argvars[1].v_type != VAR_UNKNOWN)
@@ -7671,7 +7885,7 @@ f_deepcopy(argvars, rettv)
if (noref < 0 || noref > 1)
EMSG(_(e_invarg));
else
- item_copy(&argvars[0], rettv, TRUE, noref == 0 ? ++copyID : 0);
+ item_copy(&argvars[0], rettv, TRUE, noref == 0 ? ++current_copyID : 0);
}
/*
@@ -8040,7 +8254,6 @@ f_extend(argvars, rettv)
item = NULL;
list_extend(l1, l2, item);
- ++l1->lv_refcount;
copy_tv(&argvars[0], rettv);
}
}
@@ -8106,7 +8319,6 @@ f_extend(argvars, rettv)
}
}
- ++d1->dv_refcount;
copy_tv(&argvars[0], rettv);
}
}
@@ -10421,7 +10633,6 @@ f_insert(argvars, rettv)
if (l != NULL)
{
list_insert_tv(l, &argvars[1], item);
- ++l->lv_refcount;
copy_tv(&argvars[0], rettv);
}
}
@@ -14934,6 +15145,7 @@ handle_subscript(arg, rettv, evaluate, verbose)
dict_T *selfdict = NULL;
char_u *s;
int len;
+ typval_T functv;
while (ret == OK
&& (**arg == '['
@@ -14943,12 +15155,19 @@ handle_subscript(arg, rettv, evaluate, verbose)
{
if (**arg == '(')
{
- s = rettv->vval.v_string;
+ /* need to copy the funcref so that we can clear rettv */
+ functv = *rettv;
+ rettv->v_type = VAR_UNKNOWN;
/* Invoke the function. Recursive! */
+ s = functv.vval.v_string;
ret = get_func_tv(s, STRLEN(s), rettv, arg,
- curwin->w_cursor.lnum, curwin->w_cursor.lnum,
- &len, evaluate, selfdict);
+ curwin->w_cursor.lnum, curwin->w_cursor.lnum,
+ &len, evaluate, selfdict);
+
+ /* Clear the funcref afterwards, so that deleting it while
+ * evaluating the arguments is possible (see test55). */
+ clear_tv(&functv);
/* Stop the expression evaluation when immediately aborting on
* error, or when an interrupt occurred or an exception was thrown
diff --git a/src/ex_cmds.h b/src/ex_cmds.h
index 4acf18776..6a06be5a4 100644
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -613,6 +613,8 @@ EX(CMD_promptrepl, "promptrepl", gui_mch_replace_dialog,
EXTRA|NOTRLCOM|CMDWIN),
EX(CMD_profile, "profile", ex_profile,
BANG|EXTRA|TRLBAR|CMDWIN),
+EX(CMD_profdel, "profdel", ex_breakdel,
+ EXTRA|TRLBAR|CMDWIN),
EX(CMD_psearch, "psearch", ex_psearch,
BANG|RANGE|WHOLEFOLD|DFLALL|EXTRA),
EX(CMD_ptag, "ptag", ex_ptag,
diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c
index 45bd2ee63..29687d2fc 100644
--- a/src/ex_cmds2.c
+++ b/src/ex_cmds2.c
@@ -591,7 +591,7 @@ ex_debuggreedy(eap)
}
/*
- * ":breakdel".
+ * ":breakdel" and ":profdel".
*/
void
ex_breakdel(eap)
@@ -603,13 +603,20 @@ ex_breakdel(eap)
int del_all = FALSE;
int i;
linenr_T best_lnum = 0;
+ garray_T *gap;
+
+ gap = &dbg_breakp;
+#ifdef FEAT_PROFILE
+ if (eap->cmdidx == CMD_profdel)
+ gap = &prof_ga;
+#endif
if (vim_isdigit(*eap->arg))
{
/* ":breakdel {nr}" */
nr = atol((char *)eap->arg);
- for (i = 0; i < dbg_breakp.ga_len; ++i)
- if (BREAKP(i).dbg_nr == nr)
+ for (i = 0; i < gap->ga_len; ++i)
+ if (DEBUGGY(gap, i).dbg_nr == nr)
{
todel = i;
break;
@@ -623,12 +630,12 @@ ex_breakdel(eap)
else
{
/* ":breakdel {func|file} [lnum] {name}" */
- if (dbg_parsearg(eap->arg, &dbg_breakp) == FAIL)
+ if (dbg_parsearg(eap->arg, gap) == FAIL)
return;
- bp = &BREAKP(dbg_breakp.ga_len);
- for (i = 0; i < dbg_breakp.ga_len; ++i)
+ bp = &DEBUGGY(gap, gap->ga_len);
+ for (i = 0; i < gap->ga_len; ++i)
{
- bpi = &BREAKP(i);
+ bpi = &DEBUGGY(gap, i);
if (bp->dbg_type == bpi->dbg_type
&& STRCMP(bp->dbg_name, bpi->dbg_name) == 0
&& (bp->dbg_lnum == bpi->dbg_lnum
@@ -646,18 +653,27 @@ ex_breakdel(eap)
if (todel < 0)
EMSG2(_("E161: Breakpoint not found: %s"), eap->arg);
else
- while (dbg_breakp.ga_len > 0)
+ {
+ while (gap->ga_len > 0)
{
- vim_free(BREAKP(todel).dbg_name);
- vim_free(BREAKP(todel).dbg_prog);
- --dbg_breakp.ga_len;
- if (todel < dbg_breakp.ga_len)
- mch_memmove(&BREAKP(todel), &BREAKP(todel + 1),
- (dbg_breakp.ga_len - todel) * sizeof(struct debuggy));
- ++debug_tick;
+ vim_free(DEBUGGY(gap, todel).dbg_name);
+ vim_free(DEBUGGY(gap, todel).dbg_prog);
+ --gap->ga_len;
+ if (todel < gap->ga_len)
+ mch_memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1),
+ (gap->ga_len - todel) * sizeof(struct debuggy));
+#ifdef FEAT_PROFILE
+ if (eap->cmdidx == CMD_breakdel)
+#endif
+ ++debug_tick;
if (!del_all)
break;
}
+
+ /* If all breakpoints were removed clear the array. */
+ if (gap->ga_len == 0)
+ ga_clear(gap);
+ }
}
/*