~ chicken-r7rs (master) 64ed36736e514c9a7b81856bbb60669cf95b8ba0


commit 64ed36736e514c9a7b81856bbb60669cf95b8ba0
Author:     Peter Bex <peter@more-magic.net>
AuthorDate: Sat Jun 8 15:10:30 2013 +0000
Commit:     Evan Hanson <evhan@foldling.org>
CommitDate: Sat Jun 8 15:10:30 2013 +0000

    Fix call-with-port, it should only close the port on normal return.  Add a test for this.

diff --git a/scheme.base.scm b/scheme.base.scm
index 8037b83..65d1ca6 100644
--- a/scheme.base.scm
+++ b/scheme.base.scm
@@ -161,7 +161,10 @@
 ;;;
 
 (define (call-with-port port proc)
-  (dynamic-wind void (lambda () (proc port)) (lambda () (close-port port))))
+  (receive ret
+      (proc port)
+    (close-port port)
+    (apply values ret)))
 
 (define (close-port port)
   (cond ((input-port? port)
diff --git a/tests/run.scm b/tests/run.scm
index e33838a..2c33573 100644
--- a/tests/run.scm
+++ b/tests/run.scm
@@ -15,45 +15,69 @@
     ((_ . body) (handle-exceptions e e . body))))
 
 (test-group "exceptions"
- (test "with-exception-handler (escape)"
-  'exception
-  (call-with-current-continuation
-   (lambda (k)
-    (with-exception-handler
-     (lambda (e) (k 'exception))
-     (lambda () (+ 1 (raise 'an-error)))))))
- (test-error "with-exception-handler (return)"
-  (with-exception-handler
-   (lambda (e) 'ignore)
-   (lambda () (+ 1 (raise 'an-error)))))
- (test-error "with-exception-handler (raise)"
-  (with-exception-handler
-   (lambda (e) 'ignore)
-   (lambda () (+ 1 (raise 'an-error)))))
- (test "with-exception-handler (raise-continuable)"
-  65
-  (with-exception-handler
-   (lambda (e) 42)
-   (lambda () (+ (raise-continuable "should be a number") 23))))
- (test "error-object? (#f)" #f (error-object? 'no))
- (test "error-object? (#t)" #t (error-object? (catch (car '()))))
- (test "error-object-message" "fubar" (error-object-message (catch (error "fubar"))))
- (test "error-object-irritants" '(42) (error-object-irritants (catch (error "fubar" 42))))
- (test "read-error? (#f)" #f (read-error? (catch (car '()))))
- (test "read-error? (#t)" #t (read-error? (catch (read-from-string ")"))))
- (test "file-error? (#f)" #f (file-error? (catch (car '()))))
- (test "file-error? (#t)" #t (file-error? (catch (open-input-file "foo"))))
- (test-error "guard (no match)"
-  (guard (condition ((assq 'c condition))) (raise '((a . 42)))))
- (test "guard (match)"
-  '(b . 23)
-  (guard (condition ((assq 'b condition))) (raise '((b . 23)))))
- (test "guard (=>)"
-  42
-  (guard (condition ((assq 'a condition) => cdr)) (raise '((a . 42)))))
- (test "guard (multiple)"
-  '(b . 23)
-  (guard (condition
-          ((assq 'a condition) => cdr)
-          ((assq 'b condition)))
-    (raise '((b . 23))))))
+  (test "with-exception-handler (escape)"
+        'exception
+        (call-with-current-continuation
+         (lambda (k)
+           (with-exception-handler
+            (lambda (e) (k 'exception))
+            (lambda () (+ 1 (raise 'an-error)))))))
+  (test-error "with-exception-handler (return)"
+              (with-exception-handler
+               (lambda (e) 'ignore)
+               (lambda () (+ 1 (raise 'an-error)))))
+  (test-error "with-exception-handler (raise)"
+              (with-exception-handler
+               (lambda (e) 'ignore)
+               (lambda () (+ 1 (raise 'an-error)))))
+  (test "with-exception-handler (raise-continuable)"
+        65
+        (with-exception-handler
+         (lambda (e) 42)
+         (lambda () (+ (raise-continuable "should be a number") 23))))
+  (test "error-object? (#f)" #f (error-object? 'no))
+  (test "error-object? (#t)" #t (error-object? (catch (car '()))))
+  (test "error-object-message" "fubar" (error-object-message (catch (error "fubar"))))
+  (test "error-object-irritants" '(42) (error-object-irritants (catch (error "fubar" 42))))
+  (test "read-error? (#f)" #f (read-error? (catch (car '()))))
+  (test "read-error? (#t)" #t (read-error? (catch (read-from-string ")"))))
+  (test "file-error? (#f)" #f (file-error? (catch (car '()))))
+  (test "file-error? (#t)" #t (file-error? (catch (open-input-file "foo"))))
+  (test-error "guard (no match)"
+              (guard (condition ((assq 'c condition))) (raise '((a . 42)))))
+  (test "guard (match)"
+        '(b . 23)
+        (guard (condition ((assq 'b condition))) (raise '((b . 23)))))
+  (test "guard (=>)"
+        42
+        (guard (condition ((assq 'a condition) => cdr)) (raise '((a . 42)))))
+  (test "guard (multiple)"
+        '(b . 23)
+        (guard (condition
+                ((assq 'a condition) => cdr)
+                ((assq 'b condition)))
+               (raise '((b . 23))))))
+
+;; call-with-port is not supposed to close its port when leaving the
+;; dynamic extent, only on normal return.
+(test-group "ports"
+  (let*-values (((jump-back? jump!) (call/cc (lambda (k) (values #f k))))
+                (_ (and jump-back? (jump! (void))))
+                ((string)
+                 (call-with-output-string
+                  (lambda (the-string-port)
+                    (test "call-with-port returns all values yielded by proc"
+                          '(1 2 3)
+                          (receive (call-with-port
+                                    the-string-port
+                                    (lambda (p)
+                                      (display "foo" p)
+                                      ;; Leave the dynamic extent momentarily;
+                                      ;; jump! will immediately return with #t.
+                                      (call/cc (lambda (k) (jump! #t k)))
+                                      (display "bar" p)
+                                      (values 1 2 3)))))
+                    (test-assert "call-with-port closes the port on normal return"
+                                 (port-closed? the-string-port))))))
+    (test "call-with-port passes the port correctly and allows temporary escapes"
+          "foobar" string)))
Trap