From 0dafb8b8b7030dc63dce6c5bfb12dac526205696 Mon Sep 17 00:00:00 2001
From: Kaz Kylheku <kaz@kylheku.com>
Date: Fri, 21 Oct 2016 06:22:09 -0700
Subject: Another fix to print method circular printing.

Continuing on the theme of the previous patch, we now properly
detect the situation when the recursive call is re-introducing
duplicate references to objects that have already been sent to
the stream without at #<num>= label.  It's too late to print
those objects, so we throw an exception.

* lib.c (obj_print_impl): When we print an object that doesn't
have duplicates (its hash entry has a nil value), we replace
that value with the : symbol to indicate that the object has
already been printed.  (obj_hash_merge): New function,
factoring out the hash merging logic from obj_print,
introduced in parent commit. Here, we detect the case that the
recursive print call has submitted to us an object which was
already printed without a label: because it is associated with
the : symbol in the parent hash. This situation is a
show-stopper so we throw. We cannot attempt to print the
object in any manner because we can get into runaway
recursion.
(obj_print): Hash merging logic replaced with call to new
static function.
---
 lib.c | 29 +++++++++++++++++++++--------
 1 file changed, 21 insertions(+), 8 deletions(-)

(limited to 'lib.c')

diff --git a/lib.c b/lib.c
index 0028f543..7fcfb875 100644
--- a/lib.c
+++ b/lib.c
@@ -9071,9 +9071,11 @@ val obj_print_impl(val obj, val out, val pretty, struct strm_ctx *ctx)
       ctx->counter = counter;
       rplacd(cell, counter);
       format(out, lit("#~s="), counter, nao);
-    } else if (label) {
+    } else if (integerp(label)) {
       format(out, lit("#~s#"), label, nao);
       return ret;
+    } else if (!label) {
+      rplacd(cell, colon_k);
     }
   }
 
@@ -9425,6 +9427,23 @@ tail:
   }
 }
 
+static void obj_hash_merge(val parent_hash, val child_hash)
+{
+  val iter, cell;
+
+  for (iter = hash_begin(child_hash); (cell = hash_next(iter));) {
+    val new_p;
+    val pcell = gethash_c(parent_hash, car(cell), mkcloc(new_p));
+    if (new_p)
+      rplacd(pcell, cdr(cell));
+    else if (cdr(pcell) == colon_k)
+      uw_throwf(error_s, lit("print: unexpected duplicate object "
+                             "(misbehaving print method?)"), nao);
+    else if (!cdr(pcell))
+      rplacd(pcell, t);
+  }
+}
+
 val obj_print(val obj, val out, val pretty)
 {
   val ret = nil;
@@ -9436,16 +9455,10 @@ val obj_print(val obj, val out, val pretty)
   uw_simple_catch_begin;
 
   if (ctx) {
-    val cell, iter;
     val prev_hash = ctx->obj_hash;
     ctx->obj_hash = make_hash(nil, nil, nil);
     populate_obj_hash(obj, ctx);
-    for (iter = hash_begin(ctx->obj_hash); (cell = hash_next(iter));) {
-      val new_p;
-      val pcell = gethash_c(prev_hash, car(cell), mkcloc(new_p));
-      if (new_p)
-        rplacd(pcell, cdr(cell));
-    }
+    obj_hash_merge(prev_hash, ctx->obj_hash);
     ctx->obj_hash = prev_hash;
   } else {
     if (cdr(lookup_var(nil, print_circle_s))) {
-- 
cgit v1.2.3