123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- ; Part of Scheme 48 1.9. See file COPYING for notices and license.
- ; Authors: Richard Kelsey, Jonathan Rees, Mike Sperber
- ; Package / structure / interface mutation operations.
- ; None of these is essential for the static semantics of packages.
- ; They only come into play in order to implement dynamic package
- ; mutation, which is solely a debugging operation.
- ; This is work in progress. There are surely numerous bugs.
- ; (define (package-system-sentinel) #f)
- (define (package-open! p struct-thunk)
- (if (not (memq (struct-thunk) (package-opens p)))
- (let ((thunk (package-opens-thunk p)))
- (set-package-opens-thunk! p (lambda ()
- (cons (struct-thunk) (thunk))))
- (reinitialize-package! p)
- (verify-package p)
- )))
- (define (reinitialize-package! p)
- (if *debug?*
- (begin (write `(reinitialize ,p)) (newline)))
- ;; (set-package-opens! p #f)
- (if (package-opens-really p)
- (initialize-package! p)))
- (define (reinitialize-structure! s)
- (if *debug?*
- (begin (write `(reinitialize ,s)) (newline)))
- ;; (set-structure-interface! s #f)
- (if (structure-interface-really s)
- (initialize-structure! s)))
- ; GET-NEW-LOCATION-CAREFULLY is called to obtain a location when
- ; a variable becomes newly defined.
- ; If the variable has already been referenced in any code that ought
- ; to see the new binding, we need to alter all such references to
- ; access the new location instead of whatever location they saw
- ; before.
- (define (get-new-location-carefully p name)
- (let* ((prev (package-lookup p name))
- (new (if (binding? prev)
- (let ((new (make-new-location p name))
- (cached (table-ref (package-cached p) name)))
- (copy-shadowed-contents! (binding-place prev) new)
- (cond (cached
- (if (eq? (binding-place prev) cached)
- (cope-with-mutation p name new cached)
- (assertion-violation
- 'get-new-location-carefully
- "binding cache inconsistency"
- p name new cached))))
- new)
- (get-new-location-non-shadowing p name)))
- (aloc (table-ref (package-undefined-but-assigneds p)
- name)))
- (if aloc ; Assigned?
- (begin (if *debug?*
- (note "assigned -> defined" name))
- (table-set! (package-undefined-but-assigneds p) name #f)
- (set-location-forward! aloc new name p)))
- new))
- (define (get-new-location-non-shadowing p name)
- (let* ((uloc (table-ref (package-undefineds p) name))
- (loc
- (if uloc
- (begin (if *debug?* (note "undefined -> defined" name uloc))
- (table-set! (package-undefineds p) name #f)
- uloc)
- (make-new-location p name))))
- (if (and (not uloc)
- (symbol? name)) ; interfaces can't handle generated names
- (let recur ((q p))
- (let loop ((opens (package-opens q)))
- (if (not (null? opens))
- (call-with-values
- (lambda ()
- (interface-ref (structure-interface (car opens))
- name))
- (lambda (base-name type)
- (if base-name
- ;; Shadowing
- (let* ((q (structure-package (car opens)))
- (probe (table-ref (package-undefineds q)
- base-name)))
- (if probe
- (begin (if *debug?*
- (note "undefined -> shadowed"
- name loc probe))
- (cope-with-mutation p name loc probe))
- (recur q)))
- (loop (cdr opens)))))))))
- loc))
- ; COPE-WITH-MUTATION:
- ; A package system mutation has newly caused NAME to be bound in
- ; package P to NEW, but prior references assumed that its binding was
- ; PREV (perhaps inherited from another package). If PREV is a
- ; location, then each occurrence of it stowed away in templates and
- ; packages must eventually be replaced by either location NEW, if P's
- ; binding is the one that's visible at that occurrence, or to a fresh
- ; location to replace PREV, if not.
- (define (cope-with-mutation p name new prev)
- (if (eq? new prev)
- (assertion-violation 'cope-with-mutation
- "lossage in cope-with-mutation" p name new prev))
- (let ((replacement (make-new-location p name)))
- (copy-location-info! prev replacement)
- (table-set! (package-cached p) name new)
- (if *debug?*
- (begin (write `(mutation ,prev ,new ,replacement)) (newline)))
- (shadow-location! prev
- (map package-uid
- (packages-seeing-location p name prev))
- new
- replacement)))
- ;(define (set-binding-place! b foo) (vector-set! b 1 foo))
- ; Hmm. It ought to be possible to turn this into an RPC.
- (define (copy-shadowed-contents! import loc)
- (if (location-defined? import)
- (set-contents! loc (contents import))))
- ; Return a list of all packages that might have cached a particular
- ; inherited binding.
- (define (packages-seeing-location package name loc)
- (if (not (symbol? name)) ; interfaces cannot handle generated names
- (list package)
- (let ((losers '())) ; was (list package) but that disables the
- ; entire procedure
- (let recur ((package-or-structure package))
- (let ((package (if (package? package-or-structure)
- package-or-structure
- (structure-package package-or-structure))))
- (if (and (not (memq package losers))
- (not (table-ref (package-definitions package) name)))
- (begin (set! losers (cons package losers))
- (walk-population
- (lambda (struct)
- (if (interface-member? (structure-interface struct)
- name)
- (walk-population recur
- (structure-clients struct))))
- (package-clients package))))))
- losers)))
- (define (set-location-forward! loser new name p)
- (if *debug?*
- (begin (write `(forward ,loser ,new)) (newline)))
- (for-each (lambda (q)
- (package-note-caching! q name new))
- (packages-seeing-location p name loser))
- (shadow-location! loser '() #f new))
- (fluid-cell-set! $get-location get-new-location-carefully) ;foo
- (define (copy-location-info! from to)
- (let ((probe (location-info from)))
- (if probe
- (table-set! location-info-table (location-id to) probe))))
- ;; Deal with editing operations
- (define (really-verify-later! thunk) ;cf. define-structure macro
- (let ((premature (ignore-errors thunk))) ; old value if we're redefining
- (let ((cell (fluid $losers))
- (loser (if (or (structure? premature)
- (interface? premature))
- (cons premature thunk)
- (cons #f thunk))))
- (cell-set! cell
- (cons loser
- (cell-ref cell))))))
-
- ; each element is a a pair (thing, thunk)
- (define $losers (make-fluid (make-cell '())))
- (define $package-losers (make-fluid (make-cell '())))
- (define (package-system-sentinel)
- (drain $losers verify-loser)
- (drain $package-losers verify-package))
- (define (verify-loser loser)
- (really-verify-loser (car loser)
- (ignore-errors (cdr loser))))
- (define (really-verify-loser premature thing)
- (if *debug?*
- (begin (write `(verify-loser ,premature ,thing)) (newline)))
- (cond
- ((structure? thing) (verify-structure premature thing))
- ((interface? thing) (verify-interface premature thing))))
-
- (define (verify-structure premature struct)
- (reinitialize-structure! struct)
- (if *debug?*
- (begin
- (write `(verify-structure ,struct ,(population->list (structure-clients struct))))
- (newline)))
- (walk-population (lambda (thing)
- (cond
- ((package? thing) (note-verify-package thing))
- ((structure? thing) (verify-structure #f thing))))
- (structure-clients struct)))
- (define (verify-interface premature int)
- ;; add clients of old interface to new one
- (if (interface? premature)
- (walk-population
- (lambda (client)
- (note-reference-to-interface! int client))
- (interface-clients premature)))
- (if *debug?*
- (begin
- (write `(verify-interface ,(population->list (interface-clients int))))
- (newline)))
- (walk-population (lambda (thing) (really-verify-loser #f thing))
- (interface-clients int)))
- (define (note-verify-package pack)
- (reinitialize-package! pack)
- (let* ((cell (fluid $package-losers))
- (losers (cell-ref cell)))
- (if (not (memq pack losers))
- (cell-set! cell
- (cons pack losers)))))
- (define (drain flu check)
- (let ((cell (fluid flu)))
- (let loop ()
- (let ((losers (cell-ref cell)))
- (if (not (null? losers))
- (let ((loser (car losers)))
- (cell-set! cell (cdr losers))
- (check loser)
- (loop)))))))
- (define *debug?* #f)
- ; --------------------
- ; If any looked-up location is now invalid, do something about it. We
- ; might have a previously unbound variable that is now exported by
- ; some structure; or, we might have a variable that was bound in some
- ; structure before, but now either that structure doesn't export it at
- ; all, or we're now getting that binding from some new structure.
- (define (verify-package p)
- (verify-package-cached p)
- (verify-package-undefineds p))
- ; unbound -> bound
- (define (verify-package-undefineds p)
- (if *debug?*
- (begin (write `(verify undefineds ,p)) (newline)))
- (let ((newly-defined '())
- (defs (package-definitions p))
- (undefs (package-undefineds p)))
- (table-walk (lambda (name prev)
- (if (table-ref defs name)
- (assertion-violation 'verify-package-undefineds
- "lossage" p name))
- (let ((binding (package-lookup p name)))
- (if (binding? binding)
- (let ((place (binding-place binding)))
- (if (eq? place prev)
- (assertion-violation 'verify-package-undefineds
- "lossage"
- p name binding))
- (set-location-forward! prev place name p)
- (set! newly-defined
- (cons name newly-defined)))
- (let ((loc (location-for-reference p name)))
- (if (not (eq? loc prev))
- ;; Newly imported
- (begin (set-location-forward! prev loc name p)
- (set! newly-defined
- (cons name newly-defined))))))))
- undefs)
- (if (not (null? newly-defined))
- (begin (display "Newly accessible in ")
- (write (package-name p))
- (display ": ")
- (write newly-defined)
- (newline)))
- (for-each (lambda (winner)
- (table-set! undefs winner #f))
- newly-defined)))
- (define (verify-package-cached p)
- ;; (write `(verify ,p cached)) (newline)
- (let ((newly-moved '())
- (newly-undefined '())
- (cached (package-cached p)))
- (table-walk (lambda (name prev)
- (let ((binding (package-lookup p name)))
- (if (binding? binding)
- (if (not (eq? (binding-place binding) prev))
- (set! newly-moved
- (cons name newly-moved)))
- ;; Otherwise either (a) it was previously
- ;; defined but has become newly undefined, or
- ;; (b) it was undefined before and is still, but
- ;; is inherited (or not) from a different place.
- (let ((u (location-for-reference p name)))
- (if (not (eq? u prev))
- (set! newly-undefined
- (cons name newly-undefined)))))))
- cached)
- (if (not (null? newly-moved))
- (begin (display "Definitions visible in ")
- (write (package-name p))
- (display " have moved: ")
- (write newly-moved)
- (newline)))
- (for-each (lambda (name)
- (let ((new (binding-place (package-lookup p name))))
- (cope-with-mutation p name new
- (table-ref cached name))
- (table-set! cached name new)))
- newly-moved)
- (if (and *debug?* (not (null? newly-undefined)))
- (begin (display "Newly undefined in ")
- (write (package-name p))
- (display ": ")
- (write newly-undefined)
- (newline)))
- (for-each (lambda (name)
- (let ((new (location-for-reference p name)))
- (cope-with-mutation p name new
- (table-ref cached name))
- (table-set! cached name new)))
- newly-undefined)))
- (set-verify-later! really-verify-later!)
- ;(define (maybe-note-redefinition p name seen new)
- ; (if (and seen (not (compatible? new seen)))
- ; (let ((old-description (binding-description-string seen))
- ; (new-description (binding-description-string new))
- ; (doing (if (package-definition p name)
- ; "redefining"
- ; "shadowing")))
- ; (warning 'maybe-note-redefinition
- ; (if (equal? old-description new-description)
- ; doing
- ; (string-append doing
- ; " as "
- ; new-description
- ; " (prior references assumed "
- ; old-description
- ; ")"))
- ; name))))
- ;
- ;; Foo. This should really set SEEN for all the packages up the
- ;; inheritance chain.
- ;
- ;(define (assume-denotation p name info)
- ; (if (package-unstable? p) ;***** save space? and time?
- ; (table-set! (package-seen p) name info))
- ; info)
- ;
- ;; Little utility for warning messages.
- ;
- ;(define (binding-description-string d)
- ; (cond ((transform? d)
- ; (if (syntax? d) "macro" "integrated procedure"))
- ; ((operator? d)
- ; (if (syntax? d) "special operator" "primitive procedure"))
- ; (else "variable")))
- (define (package-undefine! p name)
- (let ((probe (table-ref (package-definitions p) name)))
- (if probe
- (begin (table-set! (package-definitions p) name #f)
- (set-location-defined?! (binding-place probe) #f)
- (set-location-forward!
- (binding-place probe)
- (let ((new (package-lookup p name)))
- (if (binding? new)
- (binding-place new)
- (location-for-reference p name)))
- name p))
- (warning 'package-undefine!
- "can't undefine - binding is inherited"
- p name))))
|