forked from aniani/vim
updated for version 7.3.055
Problem: Recursively nested lists and dictionaries cause a near-endless loop when comparing them with a copy. (ZyX) Solution: Limit recursiveness in a way that non-recursive structures can still be nested very deep. Files: src/eval.c, src/testdir/test55.in, src/testdir/test55.ok
This commit is contained in:
61
src/eval.c
61
src/eval.c
@@ -434,9 +434,9 @@ static listitem_T *listitem_alloc __ARGS((void));
|
|||||||
static void listitem_free __ARGS((listitem_T *item));
|
static void listitem_free __ARGS((listitem_T *item));
|
||||||
static void listitem_remove __ARGS((list_T *l, listitem_T *item));
|
static void listitem_remove __ARGS((list_T *l, listitem_T *item));
|
||||||
static long list_len __ARGS((list_T *l));
|
static long list_len __ARGS((list_T *l));
|
||||||
static int list_equal __ARGS((list_T *l1, list_T *l2, int ic));
|
static int list_equal __ARGS((list_T *l1, list_T *l2, int ic, int recursive));
|
||||||
static int dict_equal __ARGS((dict_T *d1, dict_T *d2, int ic));
|
static int dict_equal __ARGS((dict_T *d1, dict_T *d2, int ic, int recursive));
|
||||||
static int tv_equal __ARGS((typval_T *tv1, typval_T *tv2, int ic));
|
static int tv_equal __ARGS((typval_T *tv1, typval_T *tv2, int ic, int recursive));
|
||||||
static listitem_T *list_find __ARGS((list_T *l, long n));
|
static listitem_T *list_find __ARGS((list_T *l, long n));
|
||||||
static long list_find_nr __ARGS((list_T *l, long idx, int *errorp));
|
static long list_find_nr __ARGS((list_T *l, long idx, int *errorp));
|
||||||
static long list_idx_of_item __ARGS((list_T *l, listitem_T *item));
|
static long list_idx_of_item __ARGS((list_T *l, listitem_T *item));
|
||||||
@@ -4350,7 +4350,8 @@ eval4(arg, rettv, evaluate)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Compare two Lists for being equal or unequal. */
|
/* Compare two Lists for being equal or unequal. */
|
||||||
n1 = list_equal(rettv->vval.v_list, var2.vval.v_list, ic);
|
n1 = list_equal(rettv->vval.v_list, var2.vval.v_list,
|
||||||
|
ic, FALSE);
|
||||||
if (type == TYPE_NEQUAL)
|
if (type == TYPE_NEQUAL)
|
||||||
n1 = !n1;
|
n1 = !n1;
|
||||||
}
|
}
|
||||||
@@ -4379,7 +4380,8 @@ eval4(arg, rettv, evaluate)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Compare two Dictionaries for being equal or unequal. */
|
/* Compare two Dictionaries for being equal or unequal. */
|
||||||
n1 = dict_equal(rettv->vval.v_dict, var2.vval.v_dict, ic);
|
n1 = dict_equal(rettv->vval.v_dict, var2.vval.v_dict,
|
||||||
|
ic, FALSE);
|
||||||
if (type == TYPE_NEQUAL)
|
if (type == TYPE_NEQUAL)
|
||||||
n1 = !n1;
|
n1 = !n1;
|
||||||
}
|
}
|
||||||
@@ -5914,10 +5916,11 @@ list_len(l)
|
|||||||
* Return TRUE when two lists have exactly the same values.
|
* Return TRUE when two lists have exactly the same values.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
list_equal(l1, l2, ic)
|
list_equal(l1, l2, ic, recursive)
|
||||||
list_T *l1;
|
list_T *l1;
|
||||||
list_T *l2;
|
list_T *l2;
|
||||||
int ic; /* ignore case for strings */
|
int ic; /* ignore case for strings */
|
||||||
|
int recursive; /* TRUE when used recursively */
|
||||||
{
|
{
|
||||||
listitem_T *item1, *item2;
|
listitem_T *item1, *item2;
|
||||||
|
|
||||||
@@ -5931,7 +5934,7 @@ list_equal(l1, l2, ic)
|
|||||||
for (item1 = l1->lv_first, item2 = l2->lv_first;
|
for (item1 = l1->lv_first, item2 = l2->lv_first;
|
||||||
item1 != NULL && item2 != NULL;
|
item1 != NULL && item2 != NULL;
|
||||||
item1 = item1->li_next, item2 = item2->li_next)
|
item1 = item1->li_next, item2 = item2->li_next)
|
||||||
if (!tv_equal(&item1->li_tv, &item2->li_tv, ic))
|
if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
return item1 == NULL && item2 == NULL;
|
return item1 == NULL && item2 == NULL;
|
||||||
}
|
}
|
||||||
@@ -5953,10 +5956,11 @@ dict_lookup(hi)
|
|||||||
* Return TRUE when two dictionaries have exactly the same key/values.
|
* Return TRUE when two dictionaries have exactly the same key/values.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
dict_equal(d1, d2, ic)
|
dict_equal(d1, d2, ic, recursive)
|
||||||
dict_T *d1;
|
dict_T *d1;
|
||||||
dict_T *d2;
|
dict_T *d2;
|
||||||
int ic; /* ignore case for strings */
|
int ic; /* ignore case for strings */
|
||||||
|
int recursive; /* TRUE when used recursively */
|
||||||
{
|
{
|
||||||
hashitem_T *hi;
|
hashitem_T *hi;
|
||||||
dictitem_T *item2;
|
dictitem_T *item2;
|
||||||
@@ -5977,7 +5981,7 @@ dict_equal(d1, d2, ic)
|
|||||||
item2 = dict_find(d2, hi->hi_key, -1);
|
item2 = dict_find(d2, hi->hi_key, -1);
|
||||||
if (item2 == NULL)
|
if (item2 == NULL)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic))
|
if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
--todo;
|
--todo;
|
||||||
}
|
}
|
||||||
@@ -5985,41 +5989,54 @@ dict_equal(d1, d2, ic)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int tv_equal_recurse_limit;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return TRUE if "tv1" and "tv2" have the same value.
|
* Return TRUE if "tv1" and "tv2" have the same value.
|
||||||
* Compares the items just like "==" would compare them, but strings and
|
* Compares the items just like "==" would compare them, but strings and
|
||||||
* numbers are different. Floats and numbers are also different.
|
* numbers are different. Floats and numbers are also different.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
tv_equal(tv1, tv2, ic)
|
tv_equal(tv1, tv2, ic, recursive)
|
||||||
typval_T *tv1;
|
typval_T *tv1;
|
||||||
typval_T *tv2;
|
typval_T *tv2;
|
||||||
int ic; /* ignore case */
|
int ic; /* ignore case */
|
||||||
|
int recursive; /* TRUE when used recursively */
|
||||||
{
|
{
|
||||||
char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
|
char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
|
||||||
char_u *s1, *s2;
|
char_u *s1, *s2;
|
||||||
static int recursive = 0; /* cach recursive loops */
|
static int recursive_cnt = 0; /* catch recursive loops */
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (tv1->v_type != tv2->v_type)
|
if (tv1->v_type != tv2->v_type)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
/* Catch lists and dicts that have an endless loop by limiting
|
/* Catch lists and dicts that have an endless loop by limiting
|
||||||
* recursiveness to 1000. We guess they are equal then. */
|
* recursiveness to a limit. We guess they are equal then.
|
||||||
if (recursive >= 1000)
|
* A fixed limit has the problem of still taking an awful long time.
|
||||||
|
* Reduce the limit every time running into it. That should work fine for
|
||||||
|
* deeply linked structures that are not recursively linked and catch
|
||||||
|
* recursiveness quickly. */
|
||||||
|
if (!recursive)
|
||||||
|
tv_equal_recurse_limit = 1000;
|
||||||
|
if (recursive_cnt >= tv_equal_recurse_limit)
|
||||||
|
{
|
||||||
|
--tv_equal_recurse_limit;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
switch (tv1->v_type)
|
switch (tv1->v_type)
|
||||||
{
|
{
|
||||||
case VAR_LIST:
|
case VAR_LIST:
|
||||||
++recursive;
|
++recursive_cnt;
|
||||||
r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic);
|
r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE);
|
||||||
--recursive;
|
--recursive_cnt;
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
case VAR_DICT:
|
case VAR_DICT:
|
||||||
++recursive;
|
++recursive_cnt;
|
||||||
r = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic);
|
r = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, TRUE);
|
||||||
--recursive;
|
--recursive_cnt;
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
case VAR_FUNC:
|
case VAR_FUNC:
|
||||||
@@ -9391,7 +9408,7 @@ f_count(argvars, rettv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for ( ; li != NULL; li = li->li_next)
|
for ( ; li != NULL; li = li->li_next)
|
||||||
if (tv_equal(&li->li_tv, &argvars[1], ic))
|
if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE))
|
||||||
++n;
|
++n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9418,7 +9435,7 @@ f_count(argvars, rettv)
|
|||||||
if (!HASHITEM_EMPTY(hi))
|
if (!HASHITEM_EMPTY(hi))
|
||||||
{
|
{
|
||||||
--todo;
|
--todo;
|
||||||
if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic))
|
if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE))
|
||||||
++n;
|
++n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12574,7 +12591,7 @@ f_index(argvars, rettv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for ( ; item != NULL; item = item->li_next, ++idx)
|
for ( ; item != NULL; item = item->li_next, ++idx)
|
||||||
if (tv_equal(&item->li_tv, &argvars[1], ic))
|
if (tv_equal(&item->li_tv, &argvars[1], ic, FALSE))
|
||||||
{
|
{
|
||||||
rettv->vval.v_number = idx;
|
rettv->vval.v_number = idx;
|
||||||
break;
|
break;
|
||||||
|
@@ -342,7 +342,18 @@ let l = [0, 1, 2, 3]
|
|||||||
:$put =(d == d)
|
:$put =(d == d)
|
||||||
:$put =(l != deepcopy(l))
|
:$put =(l != deepcopy(l))
|
||||||
:$put =(d != deepcopy(d))
|
:$put =(d != deepcopy(d))
|
||||||
|
:"
|
||||||
|
:" compare complex recursively linked list and dict
|
||||||
|
:let l = []
|
||||||
|
:call add(l, l)
|
||||||
|
:let dict4 = {"l": l}
|
||||||
|
:call add(dict4.l, dict4)
|
||||||
|
:let lcopy = deepcopy(l)
|
||||||
|
:let dict4copy = deepcopy(dict4)
|
||||||
|
:$put =(l == lcopy)
|
||||||
|
:$put =(dict4 == dict4copy)
|
||||||
:endfun
|
:endfun
|
||||||
|
:"
|
||||||
:call Test(1, 2, [3, 4], {5: 6}) " This may take a while
|
:call Test(1, 2, [3, 4], {5: 6}) " This may take a while
|
||||||
:"
|
:"
|
||||||
:delfunc Test
|
:delfunc Test
|
||||||
|
@@ -109,3 +109,5 @@ caught a:000[3]
|
|||||||
1
|
1
|
||||||
0
|
0
|
||||||
0
|
0
|
||||||
|
1
|
||||||
|
1
|
||||||
|
@@ -714,6 +714,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
55,
|
||||||
/**/
|
/**/
|
||||||
54,
|
54,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user