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>
diff --git a/src/eval.c b/src/eval.c
index 7848fea..66c9562 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -6139,6 +6139,58 @@
 }
 
 /*
+ * 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.
  * If the memory is allocated "tofree" is set to it, otherwise NULL.
  * "numbuf" is used for a number.
@@ -6169,7 +6221,7 @@
 	{
 	    // Only give this message once for a recursive call to avoid
 	    // flooding the user with errors.  And stop iterating over lists
-	    // and dicts.
+	    // and dicts and objects.
 	    did_echo_string_emsg = TRUE;
 	    emsg(_(e_variable_nested_too_deep_for_displaying));
 	}
@@ -6227,9 +6279,8 @@
 	    break;
 
 	case VAR_OBJECT:
-	    *tofree = r = object2string(tv->vval.v_object, numbuf, copyID,
-					echo_style, restore_copyID,
-					composite_val);
+	    r = object_tv2string(tv, tofree, copyID, restore_copyID,
+					 numbuf, echo_style, composite_val);
 	    break;
 
 	case VAR_FLOAT: