~ chicken-core (chicken-5) a94b69e52433708179d34098cae12f352a01d33b
commit a94b69e52433708179d34098cae12f352a01d33b Author: Peter Bex <peter@more-magic.net> AuthorDate: Sat Jan 23 15:10:26 2016 +0100 Commit: Evan Hanson <evhan@foldling.org> CommitDate: Tue Feb 9 19:02:11 2016 +1300 Fix nonlocal exit handling in parameterize When a parameter's "guard" procedure raises an exception, the values should be consistent. Before, the values were assigned one by one, and if the exception got raised halfway through, the already-assigned parameters would not be restored to their original values. Add a clarifying comment as to why we must alias all the parameters: they don't need to be identifiers but can be complex expressions evaluating to parameters. This is not immediately obvious from the code. It's only clear when you remember that parameterize can be used like this when reading the code. Signed-off-by: Evan Hanson <evhan@foldling.org> diff --git a/NEWS b/NEWS index 777a6be8..fcdbe1c9 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,9 @@ - Core libraries - SRFI-18: thread-join! no longer gives an error when passed a thread in the "sleeping" state (thanks to Joerg Wittenberger) + - SRFI-39: When a parameter's "guard" procedure raises an exception, + "parameterize" now correctly resets the original values of all + parameters (fixes #1227, thanks to Joo ChurlSoo). - Irregex has been updated to 0.9.4, which fixes severe performance problems with {n,m} repeating patterns (thanks to Caolan McMahon). - Unit "posix": The following posix procedures now work on port diff --git a/chicken-syntax.scm b/chicken-syntax.scm index 11ecda23..f3ee9c81 100644 --- a/chicken-syntax.scm +++ b/chicken-syntax.scm @@ -280,25 +280,36 @@ (let* ((bindings (cadr form)) (body (cddr form)) (swap (r 'swap)) - (mode (r 'mode)) + (convert? (r 'convert?)) (params (##sys#map car bindings)) (vals (##sys#map cadr bindings)) - (aliases (##sys#map (lambda (z) (r (pname z))) params)) - (aliases2 (##sys#map (lambda (z) (r (gensym))) params)) ) + (param-aliases (##sys#map (lambda (z) (r (pname z))) params)) + (saveds (##sys#map (lambda (z) (r (gensym 'saved))) params)) + (temps (##sys#map (lambda (z) (r (gensym 'tmp))) params)) ) `(##core#let - ,(map ##sys#list aliases params) + ,(map ##sys#list param-aliases params) ; These may be expressions (##core#let - ,(map ##sys#list aliases2 vals) + ,(map ##sys#list saveds vals) (##core#let - ((,mode #f)) + ((,convert? #t)) (##core#let ((,swap (##core#lambda () - ,@(map (lambda (a a2) - `(##core#let ((t (,a))) (,a ,a2 ,mode) - (##core#set! ,a2 t))) - aliases aliases2) - (##core#set! ,mode #t)))) + (##core#let + ;; First, convert all (converters may throw exns) + (,@(map (lambda (p s t) + `(,t (##core#if ,convert? + (,p ,s #t #f) + ,s))) + param-aliases saveds temps)) + ;; Save current values so we can restore them + ,@(map (lambda (p s) `(##core#set! ,s (,p))) + param-aliases saveds) + ;; Now set params to their new values (can't fail) + ,@(map (lambda (p t) `(,p ,t #f #t)) + param-aliases temps) + ;; And toggle conversion + (##core#set! ,convert? #f))))) (##sys#dynamic-wind ,swap (##core#lambda () ,@body) diff --git a/library.scm b/library.scm index 9fa47610..141bffe0 100644 --- a/library.scm +++ b/library.scm @@ -2247,42 +2247,46 @@ EOF (define make-parameter (let ((count 0)) (lambda (init #!optional (guard (lambda (x) x))) - (let ((val (guard init)) - (i count)) + (let* ((val (guard init)) + (i count) + (assign (lambda (val n convert? set?) + (when (fx>= i n) + (set! ##sys#current-parameter-vector + (##sys#vector-resize + ##sys#current-parameter-vector + (fx+ i 1) + ##sys#snafu) ) ) + (let ((val (if convert? (guard val) val))) + (when set? + (##sys#setslot ##sys#current-parameter-vector i val)) + val)))) + (set! count (fx+ count 1)) (when (fx>= i (##sys#size ##sys#default-parameter-vector)) - (set! ##sys#default-parameter-vector + (set! ##sys#default-parameter-vector (##sys#vector-resize ##sys#default-parameter-vector (fx+ i 1) (##core#undefined)) ) ) (##sys#setslot ##sys#default-parameter-vector i val) - (let ((assign - (lambda (val n mode) - (when (fx>= i n) - (set! ##sys#current-parameter-vector - (##sys#vector-resize - ##sys#current-parameter-vector - (fx+ i 1) - ##sys#snafu) ) ) - (let ((val (if mode val (guard val)))) - (##sys#setslot ##sys#current-parameter-vector i val) - val)))) - (getter-with-setter - (lambda args - (let ((n (##sys#size ##sys#current-parameter-vector))) - (cond ((pair? args) - (assign (car args) n (optional (cdr args) #f))) - ((fx>= i n) - (##sys#slot ##sys#default-parameter-vector i) ) - (else - (let ((val (##sys#slot ##sys#current-parameter-vector i))) - (if (eq? val ##sys#snafu) - (##sys#slot ##sys#default-parameter-vector i) - val) ) ) ) ) ) - (lambda (val) - (let ((n (##sys#size ##sys#current-parameter-vector))) - (assign val n #f))))))))) + + (getter-with-setter + (lambda args + (let ((n (##sys#size ##sys#current-parameter-vector))) + (cond ((pair? args) + (let-optionals (cdr args) ((convert? #t) + (set? #t)) + (assign (car args) n convert? set?))) + ((fx>= i n) + (##sys#slot ##sys#default-parameter-vector i) ) + (else + (let ((val (##sys#slot ##sys#current-parameter-vector i))) + (if (eq? val ##sys#snafu) + (##sys#slot ##sys#default-parameter-vector i) + val) ) ) ) ) ) + (lambda (val) + (let ((n (##sys#size ##sys#current-parameter-vector))) + (assign val n #f #t)))))))) ;;; Input: diff --git a/tests/library-tests.scm b/tests/library-tests.scm index bbe27191..b37cfce1 100644 --- a/tests/library-tests.scm +++ b/tests/library-tests.scm @@ -508,6 +508,27 @@ A (assert (= 2 guard-called)) +;; Parameters are reset correctly (#1227, pointed out by Joo ChurlSoo) + +(let ((a (make-parameter 1 number->string)) + (b (make-parameter 2 number->string))) + (assert (equal? (list "1" "2") (list (a) (b)))) + + (assert (equal? (list "10" "20") + (parameterize ((a 10) (b 20)) (list (a) (b))))) + + (assert (equal? (list "1" "2") (list (a) (b)))) + + (handle-exceptions exn #f (parameterize ((a 10) (b 'x)) (void))) + + (assert (equal? (list "1" "2") (list (a) (b)))) + + (parameterize ((a 10) (b 30) (a 20)) + (assert (equal? (list "20" "30") (list (a) (b))))) + + (assert (equal? (list "1" "2") (list (a) (b)))) ) + + ;;; vector and blob limits (assert-fail (make-blob -1))Trap