123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- ;;; Catching errors.
- ;;; Copyright (C) 2024 Igalia, S.L.
- ;;;
- ;;; Licensed under the Apache License, Version 2.0 (the "License");
- ;;; you may not use this file except in compliance with the License.
- ;;; You may obtain a copy of the License at
- ;;;
- ;;; http://www.apache.org/licenses/LICENSE-2.0
- ;;;
- ;;; Unless required by applicable law or agreed to in writing, software
- ;;; distributed under the License is distributed on an "AS IS" BASIS,
- ;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ;;; See the License for the specific language governing permissions and
- ;;; limitations under the License.
- ;;; Commentary:
- ;;;
- ;;; with-exception-handler, guard, and all that.
- ;;;
- ;;; Code:
- (library (hoot error-handling)
- (export guard format-exception)
- (import (hoot primitives)
- (hoot cond-expand)
- (hoot pairs)
- (hoot errors)
- (hoot exceptions)
- (hoot fluids)
- (only (hoot control)
- make-prompt-tag call-with-prompt abort-to-prompt)
- (hoot match)
- (hoot numbers)
- (hoot ports)
- (hoot write))
- ;; Snarfed from Guile's (ice-9 exceptions). Deviates a bit from R7RS.
- (define-syntax guard
- (lambda (stx)
- (define (dispatch tag exn clauses)
- (define (build-clause test handler clauses)
- #`(let ((t #,test))
- (if t
- (abort-to-prompt #,tag #,handler t)
- #,(dispatch tag exn clauses))))
- (syntax-case clauses (=> else)
- (() #`(raise-continuable #,exn))
- (((test => f) . clauses)
- (build-clause #'test #'(lambda (res) (f res)) #'clauses))
- (((else e e* ...) . clauses)
- (build-clause #'#t #'(lambda (res) e e* ...) #'clauses))
- (((test) . clauses)
- (build-clause #'test #'(lambda (res) res) #'clauses))
- (((test e* ...) . clauses)
- (build-clause #'test #'(lambda (res) e* ...) #'clauses))))
- (syntax-case stx ()
- ((guard (exn clause clause* ...) body body* ...)
- (identifier? #'exn)
- #`(let ((tag (make-prompt-tag)))
- (call-with-prompt
- tag
- (lambda ()
- (with-exception-handler
- (lambda (exn)
- #,(dispatch #'tag #'exn #'(clause clause* ...)))
- (lambda () body body* ...)))
- (lambda (_ h v)
- (h v))))))))
- (define (format-exception exception port)
- (display "Scheme error:\n")
- (match (simple-exceptions exception)
- (() (display "Empty exception object" port))
- (components
- (let loop ((i 1) (components components))
- (define (format-numbered-exception exception)
- (display " " port)
- (display i port)
- (display ". " port)
- (write exception port))
- (match components
- ((component)
- (format-numbered-exception component))
- ((component . rest)
- (format-numbered-exception component)
- (newline port)
- (loop (+ i 1) rest)))))))
- (cond-expand
- (guile-vm)
- (hoot-main
- (let ()
- (define %exception-handler (make-fluid #f))
- (define (fluid-ref* fluid depth)
- (%inline-wasm
- '(func (param $fluid (ref $fluid)) (param $depth i32)
- (result (ref eq))
- (call $fluid-ref* (local.get $fluid) (local.get $depth)))
- fluid depth))
- ;; FIXME: Use #:key instead
- (define* (with-exception-handler handler thunk
- #:optional keyword (unwind? #f))
- #;
- (unless (procedure? handler) ; ; ; ;
- (error "not a procedure" handler))
- (cond
- (unwind?
- (let ((tag (make-prompt-tag "exception handler")))
- (call-with-prompt
- tag
- (lambda ()
- (with-fluids ((%exception-handler (cons #t tag)))
- (thunk)))
- (lambda (k exn)
- (handler exn)))))
- (else
- (let ((running? (make-fluid #f)))
- (with-fluids ((%exception-handler (cons running? handler)))
- (thunk))))))
- ;; FIXME: Use #:key instead
- (define* (raise-exception exn #:optional keyword continuable?)
- (let lp ((depth 0))
- ;; FIXME: fluid-ref* takes time proportional to depth, which
- ;; makes this loop quadratic.
- (match (fluid-ref* %exception-handler depth)
- (#f
- ;; No exception handlers bound; fall back.
- (let ((port (current-error-port)))
- (format-exception exn port)
- (newline port)
- (flush-output-port port))
- (%inline-wasm
- '(func (param $exn (ref eq))
- (call $die (string.const "uncaught exception")
- (local.get $exn))
- (unreachable))
- exn))
- ((#t . prompt-tag)
- (abort-to-prompt prompt-tag exn)
- (raise (make-non-continuable-violation)))
- ((running? . handler)
- (if (fluid-ref running?)
- (begin
- (lp (1+ depth)))
- (with-fluids ((running? #t))
- (cond
- (continuable?
- (handler exn))
- (else
- (handler exn)
- (raise (make-non-continuable-violation))))))))))
- (define-syntax-rule (initialize-globals (global type proc) ...)
- (%inline-wasm
- '(func (param global type) ...
- (global.set global (local.get global)) ...)
- proc ...))
- (define-syntax-rule (initialize-proc-globals (global proc) ...)
- (initialize-globals (global (ref $proc) proc) ...))
- (initialize-proc-globals
- ($with-exception-handler with-exception-handler)
- ($raise-exception raise-exception))))
- (hoot-aux)))
|