scsh-0.6/scheme/rts/exception.scm

179 lines
5.2 KiB
Scheme

; -*- Mode: Scheme; Syntax: Scheme; Package: Scheme; -*-
; Copyright (c) 1993-1999 by Richard Kelsey and Jonathan Rees. See file COPYING.
;;;; Raising and handling conditions
; An exception is an unusual situation detected by the virtual machine.
; A condition is a run-time system structure describing unusual situations,
; including exceptions.
; Usual exception handler vector.
(define (define-exception-handler opcode proc)
(vector-set! exception-handlers opcode proc))
(define (signal-exception opcode reason . args)
(really-signal-condition (make-exception opcode
(if reason
(enumerand->name reason exception)
#f)
args)))
(define exception-handlers
(make-vector op-count signal-exception))
; TRAP is the same as SIGNAL-CONDITION.
(define-exception-handler (enum op trap)
(lambda (opcode reason arg)
(really-signal-condition arg)))
; The time opcode sometimes needs a little help.
(define-exception-handler (enum op time)
(lambda (opcode reason option arg0 . maybe-arg1)
(if (= reason (enum exception arithmetic-overflow))
(+ (* arg0 1000) ; seconds
(car maybe-arg1)) ; milliseconds
(apply signal-exception opcode reason option arg0 mayge-arg1))))
; This is for generic arithmetic, mostly
(define (extend-opcode! opcode make-handler)
(let* ((except (lambda args
(apply signal-exception
opcode
#f ; lost our reason
args)))
(handler (make-handler except)))
(define-exception-handler opcode
(lambda (opcode reason . args)
(apply handler args)))))
; Raising and handling conditions.
; (fluid $condition-handlers) is a list of handler procedures.
; Each handler takes two arguments: the condition to be handled, and
; a thunk that can be called if the handler decides to decline handling
; the condition. The continuation to a call to a handler is that
; of the call to signal-condition.
(define (really-signal-condition condition)
(let loop ((hs (fluid $condition-handlers)))
((car hs) condition (lambda () (loop (cdr hs))))))
(define (with-handler h thunk)
(let-fluid $condition-handlers
(cons h (fluid $condition-handlers))
thunk))
(define $condition-handlers
(make-fluid #f))
; CURRENT-ERROR-PORT and WRITE-STRING are passed in to avoid circular
; module dependencies.
(define (initialize-exceptions! current-error-port write-string thunk)
(call-with-current-continuation
(lambda (k)
(set-fluid! $condition-handlers
(list (last-resort-condition-handler k
current-error-port
write-string)))
(set-exception-handlers! exception-handlers)
(thunk))))
(define (last-resort-condition-handler halt current-error-port write-string)
(let ((interrupt/keyboard (enum interrupt keyboard))
(losing? #f))
(lambda (condition punt)
(cond ((error? condition)
(primitive-catch
(lambda (c)
(if (not losing?)
(begin (set! losing? #t)
(report-utter-lossage condition
c
current-error-port
write-string)))
(halt 123))))
((and (interrupt? condition)
(= (cadr condition) interrupt/keyboard))
(halt 2))
(else
(unspecific)))))) ;proceed
; This will print a list of template id's, which you can look up in
; initial.debug to get some idea of what was going on.
(define (report-utter-lossage condition c current-error-port write-string)
(let ((out (current-error-port)))
(if out
(begin
(cond ((exception? condition)
(write-string (number->string (exception-opcode condition))
out)
(write-string " / " out))
((or (error? condition)
(warning? condition))
(write-string (car (condition-stuff condition)) out)
(write-string " / " out)))
(for-each (lambda (id+pc)
(if (number? (car id+pc))
(write-string (number->string
(car id+pc))
out))
(write-string " <- " out))
(continuation-preview c))
(write-char #\newline out)))))
(define (continuation-preview c)
(if (continuation? c)
(cons (cons (template-info (continuation-template c))
(continuation-pc c))
(continuation-preview (continuation-parent c)))
'()))
; ERROR is a compiler primitive, but if it weren't, it could be
; defined as follows:
;(define (error message . irritants)
; (signal-condition (make-condition 'error (cons message irritants))))
; Run THUNK, returning either the value returned by THUNK or any error
; that occurs.
(define (ignore-errors thunk)
(call-with-current-continuation
(lambda (k)
(with-handler (lambda (c next)
(if (error? c)
(k c)
(next)))
thunk))))
; Downgrade errors to warnings while executing THUNK. Returns #T if an
; error occured.
(define (report-errors-as-warnings thunk message . irritants)
(let ((condition (ignore-errors
(lambda ()
(thunk)
#f))))
(if condition
(begin
(apply warn message (append irritants (list condition)))
#t)
#f)))
; Define disclosers that are most important for error messages.
(define-method &disclose ((obj :closure))
(list 'procedure (template-ref (closure-template obj) 1)))
(define-method &disclose ((obj :location))
(list 'location (location-id obj)))
; (put 'with-handler 'scheme-indent-hook 1)