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/vim9class.c b/src/vim9class.c
index cf2af61..78f5ab8 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -3873,7 +3873,9 @@
 }
 
 /*
- * Return a textual representation of object "obj"
+ * Return a textual representation of object "obj".
+ * "obj" must not be NULL.
+ * May return NULL.
  */
     char_u *
 object2string(
@@ -3890,47 +3892,58 @@
 								== OK
 					&& rettv.vval.v_string != NULL)
 	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
     {
-	garray_T ga;
-	ga_init2(&ga, 1, 50);
-
-	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;
+	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;
+	    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;
 }
 
 /*