mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 9.1.0522: Vim9: string(object) hangs for recursive references
Problem: Vim9: string(object) hangs for recursive references Solution: Handle recursive objects specifically, add a hang test and a compare test (Ernie Rael) fixes: #15080 closes: #15082 Signed-off-by: Ernie Rael <errael@raelity.com> Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
parent
4179f193cc
commit
05ff4e42fb
59
src/eval.c
59
src/eval.c
@ -6138,6 +6138,58 @@ class_tv2string(typval_T *tv, char_u **tofree)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a textual representation of an Object in "tv".
|
||||||
|
* If the memory is allocated "tofree" is set to it, otherwise NULL.
|
||||||
|
* When "copyID" is not zero replace recursive object with "...".
|
||||||
|
* When "restore_copyID" is FALSE, repeated items in the object are
|
||||||
|
* replaced with "...". May return NULL.
|
||||||
|
*/
|
||||||
|
static char_u *
|
||||||
|
object_tv2string(
|
||||||
|
typval_T *tv,
|
||||||
|
char_u **tofree,
|
||||||
|
int copyID,
|
||||||
|
int restore_copyID,
|
||||||
|
char_u *numbuf,
|
||||||
|
int echo_style,
|
||||||
|
int composite_val)
|
||||||
|
{
|
||||||
|
char_u *r = NULL;
|
||||||
|
|
||||||
|
object_T *obj = tv->vval.v_object;
|
||||||
|
if (obj == NULL || obj->obj_class == NULL)
|
||||||
|
{
|
||||||
|
*tofree = NULL;
|
||||||
|
r = (char_u *)"object of [unknown]";
|
||||||
|
}
|
||||||
|
else if (copyID != 0 && obj->obj_copyID == copyID
|
||||||
|
&& obj->obj_class->class_obj_member_count != 0)
|
||||||
|
{
|
||||||
|
int n = 25 + strlen((char*)obj->obj_class->class_name);
|
||||||
|
r = alloc(n);
|
||||||
|
if (r != NULL)
|
||||||
|
(void)vim_snprintf((char*)r, n, "object of %s {...}",
|
||||||
|
obj->obj_class->class_name);
|
||||||
|
*tofree = r;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int old_copyID;
|
||||||
|
if (restore_copyID)
|
||||||
|
old_copyID = obj->obj_copyID;
|
||||||
|
|
||||||
|
obj->obj_copyID = copyID;
|
||||||
|
*tofree = object2string(obj, numbuf, copyID, echo_style,
|
||||||
|
restore_copyID, composite_val);
|
||||||
|
if (restore_copyID)
|
||||||
|
obj->obj_copyID = old_copyID;
|
||||||
|
r = *tofree;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return a string with the string representation of a variable.
|
* Return a string with the string representation of a variable.
|
||||||
* If the memory is allocated "tofree" is set to it, otherwise NULL.
|
* If the memory is allocated "tofree" is set to it, otherwise NULL.
|
||||||
@ -6169,7 +6221,7 @@ echo_string_core(
|
|||||||
{
|
{
|
||||||
// Only give this message once for a recursive call to avoid
|
// Only give this message once for a recursive call to avoid
|
||||||
// flooding the user with errors. And stop iterating over lists
|
// flooding the user with errors. And stop iterating over lists
|
||||||
// and dicts.
|
// and dicts and objects.
|
||||||
did_echo_string_emsg = TRUE;
|
did_echo_string_emsg = TRUE;
|
||||||
emsg(_(e_variable_nested_too_deep_for_displaying));
|
emsg(_(e_variable_nested_too_deep_for_displaying));
|
||||||
}
|
}
|
||||||
@ -6227,9 +6279,8 @@ echo_string_core(
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case VAR_OBJECT:
|
case VAR_OBJECT:
|
||||||
*tofree = r = object2string(tv->vval.v_object, numbuf, copyID,
|
r = object_tv2string(tv, tofree, copyID, restore_copyID,
|
||||||
echo_style, restore_copyID,
|
numbuf, echo_style, composite_val);
|
||||||
composite_val);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
|
@ -2246,6 +2246,47 @@ def Test_class_object_to_string()
|
|||||||
assert_equal("object of TextPosition {lnum: 1, col: 22}", string(pos))
|
assert_equal("object of TextPosition {lnum: 1, col: 22}", string(pos))
|
||||||
END
|
END
|
||||||
v9.CheckSourceSuccess(lines)
|
v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
# check string() with object nesting
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class C
|
||||||
|
var nest1: C
|
||||||
|
var nest2: C
|
||||||
|
def Init(n1: C, n2: C)
|
||||||
|
this.nest1 = n1
|
||||||
|
this.nest2 = n2
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
var o1 = C.new()
|
||||||
|
var o2 = C.new()
|
||||||
|
o1.Init(o1, o2)
|
||||||
|
o2.Init(o2, o1)
|
||||||
|
|
||||||
|
# The following previously put's vim into an infinite loop.
|
||||||
|
|
||||||
|
var expect = "object of C {nest1: object of C {...}, nest2: object of C {nest1: object of C {...}, nest2: object of C {...}}}"
|
||||||
|
assert_equal(expect, string(o1))
|
||||||
|
END
|
||||||
|
v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
class B
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class C
|
||||||
|
var b: B
|
||||||
|
var c: C
|
||||||
|
endclass
|
||||||
|
|
||||||
|
var o1 = C.new(B.new(), C.new(B.new()))
|
||||||
|
var expect = "object of C {b: object of B {}, c: object of C {b: object of B {}, c: object of [unknown]}}"
|
||||||
|
assert_equal(expect, string(o1))
|
||||||
|
END
|
||||||
|
v9.CheckSourceSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
def Test_interface_basics()
|
def Test_interface_basics()
|
||||||
@ -10516,6 +10557,27 @@ def Test_Object_Compare_With_Recursive_Class_Ref()
|
|||||||
assert_equal(false, result)
|
assert_equal(false, result)
|
||||||
END
|
END
|
||||||
v9.CheckScriptSuccess(lines)
|
v9.CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class C
|
||||||
|
var nest1: C
|
||||||
|
var nest2: C
|
||||||
|
def Init(n1: C, n2: C)
|
||||||
|
this.nest1 = n1
|
||||||
|
this.nest2 = n2
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
var o1 = C.new()
|
||||||
|
var o2 = C.new()
|
||||||
|
o1.Init(o1, o2)
|
||||||
|
o2.Init(o2, o1)
|
||||||
|
|
||||||
|
var result = o1 == o2
|
||||||
|
assert_equal(true, result)
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
" Test for using a compound operator from a lambda function in an object method
|
" Test for using a compound operator from a lambda function in an object method
|
||||||
|
@ -704,6 +704,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 */
|
||||||
|
/**/
|
||||||
|
522,
|
||||||
/**/
|
/**/
|
||||||
521,
|
521,
|
||||||
/**/
|
/**/
|
||||||
|
@ -3873,7 +3873,9 @@ object_equal(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return a textual representation of object "obj"
|
* Return a textual representation of object "obj".
|
||||||
|
* "obj" must not be NULL.
|
||||||
|
* May return NULL.
|
||||||
*/
|
*/
|
||||||
char_u *
|
char_u *
|
||||||
object2string(
|
object2string(
|
||||||
@ -3890,47 +3892,58 @@ object2string(
|
|||||||
== OK
|
== OK
|
||||||
&& rettv.vval.v_string != NULL)
|
&& rettv.vval.v_string != NULL)
|
||||||
return rettv.vval.v_string;
|
return rettv.vval.v_string;
|
||||||
|
|
||||||
|
int ok = OK;
|
||||||
|
class_T *cl = obj->obj_class;
|
||||||
|
garray_T ga;
|
||||||
|
ga_init2(&ga, 1, 50);
|
||||||
|
|
||||||
|
if (cl != NULL && IS_ENUM(cl))
|
||||||
|
{
|
||||||
|
ga_concat(&ga, (char_u *)"enum ");
|
||||||
|
ga_concat(&ga, cl->class_name);
|
||||||
|
char_u *enum_name = ((typval_T *)(obj + 1))->vval.v_string;
|
||||||
|
ga_concat(&ga, (char_u *)".");
|
||||||
|
ga_concat(&ga, enum_name);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
garray_T ga;
|
ga_concat(&ga, (char_u *)"object of ");
|
||||||
ga_init2(&ga, 1, 50);
|
ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]"
|
||||||
|
: cl->class_name);
|
||||||
class_T *cl = obj == NULL ? NULL : obj->obj_class;
|
|
||||||
if (cl != NULL && IS_ENUM(cl))
|
|
||||||
{
|
|
||||||
ga_concat(&ga, (char_u *)"enum ");
|
|
||||||
ga_concat(&ga, cl->class_name);
|
|
||||||
char_u *enum_name = ((typval_T *)(obj + 1))->vval.v_string;
|
|
||||||
ga_concat(&ga, (char_u *)".");
|
|
||||||
ga_concat(&ga, enum_name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ga_concat(&ga, (char_u *)"object of ");
|
|
||||||
ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]"
|
|
||||||
: cl->class_name);
|
|
||||||
}
|
|
||||||
if (cl != NULL)
|
|
||||||
{
|
|
||||||
ga_concat(&ga, (char_u *)" {");
|
|
||||||
for (int i = 0; i < cl->class_obj_member_count; ++i)
|
|
||||||
{
|
|
||||||
if (i > 0)
|
|
||||||
ga_concat(&ga, (char_u *)", ");
|
|
||||||
ocmember_T *m = &cl->class_obj_members[i];
|
|
||||||
ga_concat(&ga, m->ocm_name);
|
|
||||||
ga_concat(&ga, (char_u *)": ");
|
|
||||||
char_u *tf = NULL;
|
|
||||||
ga_concat(&ga, echo_string_core(
|
|
||||||
(typval_T *)(obj + 1) + i,
|
|
||||||
&tf, numbuf, copyID, echo_style,
|
|
||||||
restore_copyID, composite_val));
|
|
||||||
vim_free(tf);
|
|
||||||
}
|
|
||||||
ga_concat(&ga, (char_u *)"}");
|
|
||||||
}
|
|
||||||
return ga.ga_data;
|
|
||||||
}
|
}
|
||||||
|
if (cl != NULL)
|
||||||
|
{
|
||||||
|
ga_concat(&ga, (char_u *)" {");
|
||||||
|
for (int i = 0; i < cl->class_obj_member_count; ++i)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
ga_concat(&ga, (char_u *)", ");
|
||||||
|
ocmember_T *m = &cl->class_obj_members[i];
|
||||||
|
ga_concat(&ga, m->ocm_name);
|
||||||
|
ga_concat(&ga, (char_u *)": ");
|
||||||
|
char_u *tf = NULL;
|
||||||
|
char_u *s = echo_string_core((typval_T *)(obj + 1) + i,
|
||||||
|
&tf, numbuf, copyID, echo_style,
|
||||||
|
restore_copyID, composite_val);
|
||||||
|
if (s != NULL)
|
||||||
|
ga_concat(&ga, s);
|
||||||
|
vim_free(tf);
|
||||||
|
if (s == NULL || did_echo_string_emsg)
|
||||||
|
{
|
||||||
|
ok = FAIL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
line_breakcheck();
|
||||||
|
}
|
||||||
|
ga_concat(&ga, (char_u *)"}");
|
||||||
|
}
|
||||||
|
if (ok == FAIL)
|
||||||
|
{
|
||||||
|
vim_free(ga.ga_data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return (char_u *)ga.ga_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user