216 lines
7.1 KiB
Scheme
216 lines
7.1 KiB
Scheme
; -*- Mode: Scheme; Syntax: Scheme; Package: Scheme; -*-
|
|
; Copyright (c) 1993-1999 by Richard Kelsey and Jonathan Rees. See file COPYING.
|
|
|
|
; Calling C procedures.
|
|
|
|
; The arguments have been pushed on the stack after the procedure.
|
|
; *stack* = procedure name arg1 ... argN rest-list N+1 total-nargs
|
|
;
|
|
; The procedure must be an external binding whose value is a pointer-sized
|
|
; code-vector. If it is, we actually do the call.
|
|
|
|
(define-primitive call-external-value ()
|
|
(lambda ()
|
|
(let* ((nargs (extract-fixnum (pop)))
|
|
(stack-nargs (extract-fixnum (pop)))
|
|
(rest-list (pop)))
|
|
(if (< maximum-external-call-args
|
|
nargs)
|
|
(raise-exception too-many-arguments-to-external-procedure
|
|
(stack-ref (- stack-nargs 1))
|
|
nargs)
|
|
(begin
|
|
(do ((rest-list rest-list (vm-cdr rest-list)))
|
|
((vm-eq? rest-list null))
|
|
(push (vm-car rest-list)))
|
|
(let ((proc (stack-ref (- nargs 1)))
|
|
(name (stack-ref (- nargs 2)))
|
|
(args (pointer-to-stack-arguments)))
|
|
; Push a header to keep the arguments from being scanned.
|
|
(push (make-header (enum stob vector)
|
|
(cells->bytes nargs)))
|
|
(if (and (vm-string? name)
|
|
(code-vector? proc)
|
|
(= (code-vector-length proc)
|
|
(cells->bytes 1)))
|
|
(let ((result (external-call proc name (- nargs 2) args)))
|
|
(cond (*external-exception?*
|
|
(set! *external-exception?* #f)
|
|
(goto raise *external-exception-nargs*))
|
|
(else
|
|
(goto continue-with-value result 0))))
|
|
(raise-exception wrong-type-argument 0 proc name))))))))
|
|
|
|
;----------------
|
|
; Raising exceptions from C.
|
|
|
|
; True if the C procedure is raising an exception instead of doing a normal
|
|
; return.
|
|
|
|
(define *external-exception?* #f)
|
|
|
|
; The number of arguments being passed to the exception handler.
|
|
|
|
(define *external-exception-nargs*)
|
|
|
|
; These are for exceptions raised by external code. They work pretty
|
|
; much in the same way as other VM instructions raise exceptions.
|
|
; Note that this doesn't actually perform the raise; it just sets
|
|
; *EXTERNAL-EXCEPTION?* to be true, so that the EXTERNAL-CALL procedure
|
|
; will do the raise.
|
|
;
|
|
; If you extend the maximum number of arguments, be sure to adjust the
|
|
; definition of STACK-SLACK in stack.scm. It needs to know the maximum
|
|
; number of values pushed by an exception handler, which is the number
|
|
; of arguments pushed here, plus the procedure.
|
|
|
|
(define (s48-setup-external-exception why nargs)
|
|
(push-exception-continuation! why 1) ; 1 = one-byte instruction
|
|
(if (< 10 nargs)
|
|
(error "too many arguments from external exception"))
|
|
(set! *external-exception-nargs* nargs)
|
|
(set! *external-exception?* #t))
|
|
|
|
; The external code needs to piggyback an exception on top of one already
|
|
; being raised. We increase the argument count and return the old exception.
|
|
|
|
(define (s48-resetup-external-exception new-why additional-nargs)
|
|
(let* ((old-nargs *external-exception-nargs*)
|
|
(old-why (stack-ref old-nargs)))
|
|
(stack-set! old-nargs (enter-fixnum new-why))
|
|
(set! *external-exception-nargs* (+ old-nargs additional-nargs))
|
|
old-why))
|
|
|
|
;----------------
|
|
; Two tables of shared bindings: those we import from the outside and those
|
|
; that we provide to the outside.
|
|
|
|
(define *imported-bindings*)
|
|
(define *exported-bindings*)
|
|
|
|
; When resuming a statically-linked image these tables are FALSE.
|
|
|
|
(define (install-shared-bindings!+gc imported-bindings exported-bindings)
|
|
(receive (key imported-bindings exported-bindings)
|
|
(ensure-space-saving-temps (* hash-table-size 2)
|
|
imported-bindings
|
|
exported-bindings)
|
|
(set! *imported-bindings*
|
|
(if (vm-eq? imported-bindings false)
|
|
(make-hash-table key)
|
|
imported-bindings))
|
|
(set! *exported-bindings*
|
|
(if (vm-eq? exported-bindings false)
|
|
(make-hash-table key)
|
|
exported-bindings))))
|
|
|
|
(define shared-binding-table-size
|
|
(* hash-table-size 2))
|
|
|
|
(add-gc-root!
|
|
(lambda ()
|
|
(set! *imported-bindings* (s48-trace-value *imported-bindings*))
|
|
(set! *exported-bindings* (s48-trace-value *exported-bindings*))))
|
|
|
|
; These next two procedure are used to write the bindings tables out in images.
|
|
|
|
(define (s48-exported-bindings)
|
|
*exported-bindings*)
|
|
|
|
; This returns a version of the import table that contains only those bindings
|
|
; that have been traced, and in which the values of the bindings are all set to
|
|
; UNASSIGNED-VALUE.
|
|
|
|
(define s48-cleaned-imported-bindings
|
|
(let ((cleaner (table-cleaner shared-binding-next set-shared-binding-next!))
|
|
(walker (table-walker shared-binding-next)))
|
|
(lambda ()
|
|
(let ((table (s48-trace-value *imported-bindings*)))
|
|
(cleaner table)
|
|
(walker (lambda (binding)
|
|
(shared-binding-set! binding unassigned-marker))
|
|
table)
|
|
table))))
|
|
|
|
; Imported bindings.
|
|
|
|
(define lookup-imported-binding
|
|
(let* ((maker (lambda (string next key)
|
|
(make-shared-binding string true unspecific-value next key)))
|
|
(lookup (table-searcher shared-binding-name
|
|
shared-binding-next
|
|
maker)))
|
|
(lambda (name key)
|
|
(lookup *imported-bindings* name key))))
|
|
|
|
; Exporting bindings.
|
|
|
|
(define lookup-exported-binding
|
|
(let* ((maker (lambda (string next key)
|
|
(make-shared-binding string false unspecific-value next key)))
|
|
(lookup (table-searcher shared-binding-name
|
|
shared-binding-next
|
|
maker)))
|
|
(lambda (name key)
|
|
(lookup *exported-bindings* name key))))
|
|
|
|
; Print warnings about all imported bindings which the external code
|
|
; has not yet defined.
|
|
|
|
(define s48-warn-about-undefined-imported-bindings
|
|
(let ((walker (table-walker shared-binding-next)))
|
|
(lambda ()
|
|
(let ((out (current-error-port)))
|
|
(walker (lambda (binding)
|
|
(if (undefined? (shared-binding-ref binding))
|
|
(let ((name (shared-binding-name binding)))
|
|
(write-string "undefined imported name " out)
|
|
(if (vm-string? name)
|
|
(write-string (extract-string name) out)
|
|
(write-string "<invalid name>" out))
|
|
(write-char #\newline out))))
|
|
*imported-bindings*)))))
|
|
|
|
;----------------
|
|
; Four primitives for adding and removing imports and exports.
|
|
|
|
(define-consing-primitive lookup-shared-binding (string-> boolean->)
|
|
(lambda (ignore)
|
|
shared-binding-size)
|
|
(lambda (name is-import? key)
|
|
(goto return
|
|
(if is-import?
|
|
(lookup-imported-binding name key)
|
|
(lookup-exported-binding name key)))))
|
|
|
|
(define undefine-shared-binding!
|
|
(table-remover shared-binding-name
|
|
shared-binding-next
|
|
set-shared-binding-next!))
|
|
|
|
(define-primitive undefine-shared-binding (string-> boolean->)
|
|
(lambda (name is-import?)
|
|
(undefine-shared-binding! (if is-import?
|
|
*imported-bindings*
|
|
*exported-bindings*)
|
|
name)
|
|
(goto continue 0)))
|
|
|
|
;----------------
|
|
; The following two functions are exported to C, hence the reversal of the
|
|
; export/import terminology.
|
|
|
|
(define (s48-define-exported-binding name value)
|
|
(receive (key value)
|
|
(ensure-space-saving-temp (+ shared-binding-size
|
|
(vm-string-size (string-length name)))
|
|
value)
|
|
(shared-binding-set! (lookup-imported-binding (enter-string name key) key)
|
|
value)))
|
|
|
|
(define (s48-get-imported-binding name)
|
|
(let ((key (ensure-space (+ shared-binding-size
|
|
(vm-string-size (string-length name))))))
|
|
(lookup-exported-binding (enter-string name key) key)))
|
|
|