racket/collects/drracket/private/expanding-place.rkt
Robby Findler 13e16d2b12 add preferences for displaying error messages from online expansion
also improved the highlighting in the margin by invalidating only the region
of the editor that actually needs redrawing
2011-09-20 17:15:04 -05:00

237 lines
9.0 KiB
Racket

#lang racket/base
(require racket/place
"eval-helpers.rkt"
compiler/cm)
(provide start)
(struct job (cust response-pc working-thd))
;; key : any (used by equal? for comparision, but back in the main place)
(struct handler (key proc))
(define handlers '())
(define module-language-parallel-lock-client
'uninitialized-module-language-parallel-lock-client)
(define old-registry-chan (make-channel))
(define (start p)
;; get the module-language-compile-lock in the initial message
(set! module-language-parallel-lock-client
(compile-lock->parallel-lock-client
(place-channel-get p)
(current-custodian)))
;; get the handlers in a second message
(set! handlers (for/list ([lst (place-channel-get p)])
(define file (list-ref lst 0))
(define id (list-ref lst 1))
(handler lst (dynamic-require file id))))
(let loop ([current-job #f]
;; the old-registry argument holds on to the namespace-module-registry
;; from a previous run in order to keep entries in the bytecode cache
[old-registry #f])
(sync
(handle-evt
old-registry-chan
(λ (reg) (loop current-job reg)))
(handle-evt
p
(λ (message)
(cond
[(eq? message 'abort)
(when current-job (abort-job current-job))
(loop #f old-registry)]
[(vector? message)
(when current-job (abort-job current-job))
(define program-as-string (vector-ref message 0))
(define path (vector-ref message 1))
(define response-pc (vector-ref message 2))
(define settings (vector-ref message 3))
(loop (new-job program-as-string path response-pc settings)
old-registry)]))))))
(define (abort-job job)
(when (log-level? (current-logger) 'info)
(define stack (continuation-mark-set->context
(continuation-marks
(job-working-thd job))))
(log-info (format "expanding-place.rkt: kill; worker-thd stack (size ~a) dead? ~a:"
(length stack)
(thread-dead? (job-working-thd job))))
(for ([x (in-list stack)])
(log-info (format " ~s" x))))
(custodian-shutdown-all (job-cust job))
(place-channel-put (job-response-pc job) #f))
(struct exn:access exn:fail ())
(define (new-job program-as-string path response-pc settings)
(define cust (make-custodian))
(define exn-chan (make-channel))
(define result-chan (make-channel))
(define normal-termination (make-channel))
(define abnormal-termination (make-channel))
(define the-source (or path "unsaved editor"))
(define orig-cust (current-custodian))
(define working-thd
(parameterize ([current-custodian cust])
(thread
(λ ()
(log-info "expanding-place.rkt: 01 starting thread")
(define sema (make-semaphore 0))
(log-info "expanding-place.rkt: 02 setting basic parameters")
(set-basic-parameters/no-gui)
(log-info "expanding-place.rkt: 03 setting module language parameters")
(set-module-language-parameters settings
module-language-parallel-lock-client
#:use-use-current-security-guard? #t)
(log-info "expanding-place.rkt: 04 setting directories")
(when path
(let-values ([(base name dir?) (split-path path)])
(current-directory base)
(current-load-relative-directory base)))
(define sp (open-input-string program-as-string))
(port-count-lines! sp)
(log-info "expanding-place.rkt: 05 installing security guard")
(install-security-guard) ;; must come after the call to set-module-language-parameters
(log-info "expanding-place.rkt: 06 setting uncaught-exception-handler")
(uncaught-exception-handler
(λ (exn)
(parameterize ([current-custodian orig-cust])
(thread
(λ ()
(channel-put normal-termination #t)
(semaphore-post sema)
(channel-put exn-chan exn))))
(semaphore-wait sema)
((error-escape-handler))))
(log-info "expanding-place.rkt: 07 starting read-syntax")
(define stx
(parameterize ([read-accept-reader #t])
(read-syntax the-source sp)))
(log-info "expanding-place.rkt: 08 read")
(when (syntax? stx) ;; could be eof
(define-values (name lang transformed-stx)
(transform-module path
(namespace-syntax-introduce stx)
raise-hopeless-syntax-error))
(log-info "expanding-place.rkt: 09 starting expansion")
(define expanded (expand transformed-stx))
(channel-put old-registry-chan
(namespace-module-registry (current-namespace)))
(log-info "expanding-place.rkt: 10 expanded")
(define handler-results
(for/list ([handler (in-list handlers)])
(list (handler-key handler)
((handler-proc handler) expanded
path
the-source))))
(log-info "expanding-place.rkt: 11 handlers finished")
(parameterize ([current-custodian orig-cust])
(thread
(λ ()
(channel-put normal-termination #t)
(semaphore-post sema)
(channel-put result-chan handler-results))))
(semaphore-wait sema)
(log-info "expanding-place.rkt: 12 finished"))))))
(thread
(λ ()
(sync
(handle-evt
normal-termination
(λ (x) (void)))
(handle-evt
(thread-dead-evt working-thd)
(λ (x) (channel-put abnormal-termination #t))))))
(thread
(λ ()
(sync
(handle-evt
abnormal-termination
(λ (val)
(place-channel-put
response-pc
(vector 'abnormal-termination))))
(handle-evt
result-chan
(λ (val)
(place-channel-put response-pc (vector 'handler-results val))))
(handle-evt
exn-chan
(λ (exn)
(place-channel-put
response-pc
(cond
[(exn:access? exn)
(vector 'access-violation (exn-message exn))]
[else
(vector
(cond
[(and (exn:fail:read? exn)
(andmap (λ (srcloc) (equal? (srcloc-source srcloc) the-source))
(exn:fail:read-srclocs exn)))
'reader-in-defs-error]
[(regexp-match #rx"expand: unbound identifier" (exn-message exn))
'exn:variable]
[else 'exn])
(trim-message
(if (exn? exn)
(regexp-replace* #rx"[ \t]*\n[ \t]*" (exn-message exn) " ")
(format "uncaught exn: ~s" exn)))
(if (exn:srclocs? exn)
(sort
(filter
values
(for/list ([srcloc ((exn:srclocs-accessor exn) exn)])
(and (srcloc? srcloc)
(equal? the-source (srcloc-source srcloc))
(srcloc-position srcloc)
(srcloc-span srcloc)
(vector (srcloc-position srcloc)
(srcloc-span srcloc)))))
<
#:key (λ (x) (vector-ref x 0)))
'()))])))))))
(job cust response-pc working-thd))
(define (raise-hopeless-syntax-error . args)
(apply raise-syntax-error '|Module Language| args))
(define (install-security-guard)
(current-security-guard
(make-security-guard
(current-security-guard)
(λ (prim path whats)
(when (or (member 'write whats)
(member 'execute whats)
(member 'delete whats))
(raise (exn:access (format "~a: forbidden ~a access to ~a" prim whats path)
(current-continuation-marks)))))
(λ (prim target port what)
(raise (exn:access (format "~a: forbidden ~a access to ~a:~a" prim what target port)
(current-continuation-marks))))
(λ (prim path1 path2)
(raise (exn:access (format "~a: forbidden to link ~a to ~a" prim path1 path2)
(current-continuation-marks)))))))
;; trim-message : string -> string[200 chars max]
(define (trim-message str)
(cond
[(<= (string-length str) 200)
str]
[else
(define prefix-len 99)
(define suffix-len 98)
(define middle "...")
;; (+ prefix-len suffix-len (string-length middle)) must be 200 (or less)
(string-append (substring str 0 prefix-len)
middle
(substring str (- (string-length str) suffix-len) (string-length str)))]))