changeset 364:5332dd13733c v7.0094

updated for version 7.0094
author vimboss
date Sun, 26 Jun 2005 22:34:35 +0000
parents dbf2e948e7e6
children bda4394122e0
files runtime/doc/repeat.txt runtime/doc/todo.txt src/eval.c src/ex_cmds.h src/ex_cmds2.c
diffstat 5 files changed, 284 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- 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 compile
 		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:
--- 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 Vi
 							*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).
--- 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 *
 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)
-    {
-	next = item->li_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)
+    {
+	/* 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;
-
-    /* Careful: we free the dictitems while they still appear in the
-     * hashtab.  Must not try to resize the hashtab! */
+    dictitem_T	*di;
+
+    /* 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, v
     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, v
     {
 	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
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -613,6 +613,8 @@ EX(CMD_promptrepl,	"promptrepl",	gui_mch
 			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,
--- 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);
+    }
 }
 
 /*