#lang racket/base (require compiler/zo-structs unstable/struct racket/port racket/vector racket/match racket/contract racket/local racket/list racket/dict racket/function racket/pretty racket/path) (provide/contract [zo-marshal (compilation-top? . -> . bytes?)] [zo-marshal-to (compilation-top? output-port? . -> . void?)]) (define (zo-marshal top) (define bs (open-output-bytes)) (zo-marshal-to top bs) (get-output-bytes bs)) (define (zo-marshal-to top outp) (match top [(struct compilation-top (max-let-depth prefix form)) (define shared (make-hasheq)) (define wrapped (make-hasheq)) (define (shared-obj-pos v) (hash-ref shared v #f)) (define (share! v) (hash-set! shared v (add1 (hash-count shared)))) (define ct (list* max-let-depth prefix (protect-quote form))) ; Compute what objects are in ct multiple times (by equal?) (local [(define encountered (make-hasheq)) (define (encountered? v) (hash-ref encountered v #f)) (define (encounter! v) (hash-set! encountered v #t)) (define (visit! v) (cond [(shared-obj-pos v) #f] [(encountered? v) (share! v) #f] [else (encounter! v) ; All closures MUST be in the symbol table (when (closure? v) (share! v)) #t]))] (traverse wrapped visit! ct)) ; Hash tables aren't sorted, so we need to order them (define in-order-shareds (sort (hash-map shared (lambda (k v) (cons v k))) < #:key car)) (define (write-all outp) ; As we are writing the symbol table entry for v, ; the writing code will attempt to see if v is shared and ; insert a symtable reference, which would be wrong. ; So, the first time it is encountered while writing, ; we should pretend it ISN'T shared, so it is actually written. ; However, subsequent times (or for other shared values) ; we defer to the normal 'shared-obj-pos' (define (shared-obj-pos/modulo-v v) (define skip? #t) (lambda (v2) (if (and skip? (eq? v v2)) (begin (set! skip? #f) #f) (shared-obj-pos v2)))) ; Write the symbol table, computing offsets as we go (define offsets (for/list ([k*v (in-list in-order-shareds)]) (define v (cdr k*v)) (begin0 (file-position outp) (out-anything v (make-out outp (shared-obj-pos/modulo-v v) wrapped))))) ; Compute where we ended (define post-shared (file-position outp)) ; Write the entire ctop (out-data ct (make-out outp shared-obj-pos wrapped)) (values offsets post-shared (file-position outp))) ; Compute where the symbol table ends (define counting-p (open-output-nowhere)) (define-values (offsets post-shared all-forms-length) (write-all counting-p)) ; Write the compiled form header (write-bytes #"#~" outp) ; Write the version (notice that it isn't the same as out-string) (define version-bs (string->bytes/latin-1 (version))) (write-bytes (bytes (bytes-length version-bs)) outp) (write-bytes version-bs outp) ; Write the symbol table information (size, offsets) (define symtabsize (add1 (hash-count shared))) (write-bytes (int->bytes symtabsize) outp) (define all-short? (post-shared . < . #xFFFF)) (write-bytes (bytes (if all-short? 1 0)) outp) (for ([o (in-list offsets)]) (write-bytes (integer->integer-bytes o (if all-short? 2 4) #f #f) outp)) ; Post-shared is where the ctop actually starts (write-bytes (int->bytes post-shared) outp) ; This is where the file should end (write-bytes (int->bytes all-forms-length) outp) ; Write the symbol table then the ctop (write-all outp) (void)])) (define (traverse wrapped-ht visit! expr) (when (visit! expr) (match expr [(? wrapped? w) (define encoded-w (hash-ref! wrapped-ht w (lambda () (encode-wrapped w)))) (traverse wrapped-ht visit! encoded-w)] [(? prefab-struct-key) (map (curry traverse wrapped-ht visit!) (struct->list expr))] [(cons l r) (traverse wrapped-ht visit! l) (traverse wrapped-ht visit! r)] [(? vector?) (for ([v (in-vector expr)]) (traverse wrapped-ht visit! v))] [(? hash?) (for ([(k v) (in-hash expr)]) (traverse wrapped-ht visit! k) (traverse wrapped-ht visit! v))] [(? module-path-index?) (define-values (name base) (module-path-index-split expr)) (traverse wrapped-ht visit! name) (traverse wrapped-ht visit! base)] [(box v) (traverse wrapped-ht visit! v)] [(protected-symref v) (traverse wrapped-ht visit! v)] [(quoted v) (traverse wrapped-ht visit! v)] [else (void)]))) ;; ---------------------------------------- (define toplevel-type-num 0) (define syntax-type-num 3) (define sequence-type-num 7) (define unclosed-procedure-type-num 9) (define let-value-type-num 10) (define let-void-type-num 11) (define letrec-type-num 12) (define wcm-type-num 14) (define quote-syntax-type-num 15) (define variable-type-num 24) (define top-type-num 89) (define case-lambda-sequence-type-num 99) (define begin0-sequence-type-num 100) (define module-type-num 103) (define prefix-type-num 105) (define free-id-info-type-num 154) (define-syntax define-enum (syntax-rules () [(_ n) (begin)] [(_ n id . rest) (begin (define id n) (define-enum (add1 n) . rest))])) (define-enum 0 CPT_ESCAPE CPT_SYMBOL CPT_SYMREF CPT_WEIRD_SYMBOL CPT_KEYWORD CPT_BYTE_STRING CPT_CHAR_STRING CPT_CHAR CPT_INT CPT_NULL CPT_TRUE CPT_FALSE CPT_VOID CPT_BOX CPT_PAIR CPT_LIST CPT_VECTOR CPT_HASH_TABLE CPT_STX CPT_LET_ONE_FLONUM CPT_MARSHALLED CPT_QUOTE CPT_REFERENCE CPT_LOCAL CPT_LOCAL_UNBOX CPT_SVECTOR CPT_APPLICATION CPT_LET_ONE CPT_BRANCH CPT_MODULE_INDEX CPT_MODULE_VAR CPT_PATH CPT_CLOSURE CPT_DELAY_REF ; XXX unused, but appears to be same as CPT_SYMREF CPT_PREFAB CPT_LET_ONE_UNUSED) (define-enum 0 DEFINE_VALUES_EXPD DEFINE_SYNTAX_EXPD SET_EXPD CASE_LAMBDA_EXPD BEGIN0_EXPD BOXENV_EXPD MODULE_EXPD REQUIRE_EXPD DEFINE_FOR_SYNTAX_EXPD REF_EXPD APPVALS_EXPD SPLICE_EXPD) (define CPT_SMALL_NUMBER_START 36) (define CPT_SMALL_NUMBER_END 60) (define CPT_SMALL_SYMBOL_START 60) (define CPT_SMALL_SYMBOL_END 80) (define CPT_SMALL_MARSHALLED_START 80) (define CPT_SMALL_MARSHALLED_END 92) (define CPT_SMALL_LIST_MAX 65) (define CPT_SMALL_PROPER_LIST_START 92) (define CPT_SMALL_PROPER_LIST_END (+ CPT_SMALL_PROPER_LIST_START CPT_SMALL_LIST_MAX)) (define CPT_SMALL_LIST_START CPT_SMALL_PROPER_LIST_END) (define CPT_SMALL_LIST_END 192) (define CPT_SMALL_LOCAL_START 192) (define CPT_SMALL_LOCAL_END 207) (define CPT_SMALL_LOCAL_UNBOX_START 207) (define CPT_SMALL_LOCAL_UNBOX_END 222) (define CPT_SMALL_SVECTOR_START 222) (define CPT_SMALL_SVECTOR_END 247) (define CPT_SMALL_APPLICATION_START 247) (define CPT_SMALL_APPLICATION_END 255) (define CLOS_HAS_REST 1) (define CLOS_HAS_REF_ARGS 2) (define CLOS_PRESERVES_MARKS 4) (define CLOS_IS_METHOD 16) (define CLOS_SINGLE_RESULT 32) (define BITS_PER_MZSHORT 32) (define *dummy* #f) (define (int->bytes x) (integer->integer-bytes x 4 #f #f)) (define-struct case-seq (name lams)) (define-struct (seq0 seq) ()) (define-struct out (s shared-index encoded-wraps)) (define (out-shared v out k) (let ([v ((out-shared-index out) v)]) (if v (begin (out-byte CPT_SYMREF out) (out-number v out)) (k)))) (define (display-byte b) (if (b . <= . #xf) (printf "0~x" b) (printf "~x" b))) (define (out-byte v out) (write-byte v (out-s out))) (define (out-bytes b out) (write-bytes b (out-s out))) (define (out-number n out) (cond [(n . < . 0) (if (n . > . -32) (out-byte (bitwise-ior #xC0 (- n)) out) (begin (out-byte #xE0 out) (out-bytes (int->bytes (- n)) out)))] [(n . < . 128) (out-byte n out)] [(n . < . #x4000) (out-byte (bitwise-ior #x80 (bitwise-and n #x3F)) out) (out-byte (bitwise-and #xFF (arithmetic-shift n -6)) out)] [else (out-byte #xF0 out) (out-bytes (int->bytes n) out)])) (define (out-syntax key val out) (out-marshaled syntax-type-num (list* key val) out)) (define (out-marshaled type-num val out) (if (type-num . < . (- CPT_SMALL_MARSHALLED_END CPT_SMALL_MARSHALLED_START)) (out-byte (+ CPT_SMALL_MARSHALLED_START type-num) out) (begin (out-byte CPT_MARSHALLED out) (out-number type-num out))) (out-data val out)) (define (out-anything v out) (cond [(module-variable? v) (out-toplevel v out)] [(closure? v) (out-expr v out)] [else (out-data v out)])) (define (out-prefix a-prefix out) (match a-prefix [(struct prefix (num-lifts toplevels stxs)) (out-marshaled prefix-type-num (cons num-lifts (cons (list->vector toplevels) (list->vector stxs))) out)])) (define (out-free-id-info a-free-id-info out) (match a-free-id-info [(struct free-id-info (mpi0 s0 mpi1 s1 p0 p1 p2 insp?)) (out-marshaled free-id-info-type-num (vector mpi0 s0 mpi1 s1 p0 p1 p2 insp?) out)])) (define-struct module-decl (content)) (define (out-module mod-form out) (match mod-form [(struct mod (name srcname self-modidx prefix provides requires body syntax-body unexported max-let-depth dummy lang-info internal-context)) (out-syntax MODULE_EXPD (let* ([lookup-req (lambda (phase) (let ([a (assq phase requires)]) (if a (cdr a) null)))] [other-requires (filter (lambda (l) (not (memq (car l) '(#f -1 0 1)))) requires)] [extract-protects (lambda (phase) (let ([a (assq phase provides)]) (and a (let ([p (map provided-protected? (append (cadr a) (caddr a)))]) (if (ormap values p) (list->vector p) #f)))))] [list->vector/#f (lambda (default l) (if (andmap (lambda (x) (equal? x default)) l) #f (list->vector l)))] [l (let loop ([l other-requires]) (match l [(list) empty] [(list-rest (cons phase reqs) rst) (list* phase reqs (loop rst))]))] [l (cons (length other-requires) l)] [l (cons (lookup-req #f) l)] ; dt-requires [l (cons (lookup-req -1) l)] ; tt-requires [l (cons (lookup-req 1) l)] ; et-requires [l (cons (lookup-req 0) l)] ; requires [l (cons (list->vector body) l)] [l (cons (list->vector (for/list ([i (in-list syntax-body)]) (define (maybe-one l) ;; a single symbol is ok (if (and (pair? l) (null? (cdr l))) (car l) l)) (match i [(struct def-syntaxes (ids rhs prefix max-let-depth)) (vector (maybe-one ids) rhs max-let-depth prefix #f)] [(struct def-for-syntax (ids rhs prefix max-let-depth)) (vector (maybe-one ids) rhs max-let-depth prefix #t)]))) l)] [l (append (apply append (map (lambda (l) (let ([phase (car l)] [all (append (cadr l) (caddr l))]) (list phase (list->vector/#f #f (map provided-insp all)) (list->vector/#f 0 (map (lambda (p) (= 1 (provided-src-phase p))) all)) (list->vector/#f #f (map (lambda (p) (if (eq? (provided-nom-src p) (provided-src p)) #f ; #f means "same as src" (provided-nom-src p))) all)) (list->vector (map provided-src-name all)) (list->vector (map provided-src all)) (list->vector (map provided-name all)) (length (cadr l)) (length all)))) provides)) l)] [l (cons (length provides) l)] ; number of provide sets [l (cons (extract-protects 0) l)] ; protects [l (cons (extract-protects 1) l)] ; et protects [l (list* (list->vector (car unexported)) (length (car unexported)) l)] ; indirect-provides [l (list* (list->vector (cadr unexported)) (length (cadr unexported)) l)] ; indirect-syntax-provides [l (list* (list->vector (caddr unexported)) (length (caddr unexported)) l)] ; indirect-et-provides [l (cons prefix l)] [l (cons dummy l)] [l (cons max-let-depth l)] [l (cons internal-context l)] ; module->namespace syntax [l (list* #f #f l)] ; obsolete `functional?' info [l (cons lang-info l)] ; lang-info [l (cons self-modidx l)] [l (cons srcname l)] [l (cons name l)]) (make-module-decl l)) out)])) (define (out-toplevel tl out) (match tl [#f (out-data tl out)] [(? symbol?) (out-data tl out)] [(struct global-bucket (name)) (out-marshaled variable-type-num name out)] [(struct module-variable (modidx sym pos phase)) (out-shared tl out (lambda () (out-byte CPT_MODULE_VAR out) (out-data modidx out) (out-data sym out) (unless (zero? phase) (out-number -2 out)) (out-number pos out)))])) (define (encode-module-bindings module-bindings) (define encode-nominal-path (match-lambda [(struct simple-nominal-path (value)) value] [(struct imported-nominal-path (value import-phase)) (cons value import-phase)] [(struct phased-nominal-path (value import-phase phase)) (cons value (cons import-phase phase))])) (define encoded-bindings (make-vector (* (length module-bindings) 2))) (for ([i (in-naturals)] [(k v) (in-dict module-bindings)]) (vector-set! encoded-bindings (* i 2) k) (vector-set! encoded-bindings (add1 (* i 2)) (match v [(struct simple-module-binding (path)) path] [(struct exported-module-binding (path export-name)) (cons path export-name)] [(struct nominal-module-binding (path nominal-path)) (cons path (encode-nominal-path nominal-path))] [(struct exported-nominal-module-binding (path export-name nominal-path nominal-export-name)) (list* path export-name (encode-nominal-path nominal-path) nominal-export-name)] [(struct phased-module-binding (path phase export-name nominal-path nominal-export-name)) (list* path phase export-name (encode-nominal-path nominal-path) nominal-export-name)]))) encoded-bindings) (define encode-all-from-module (match-lambda [(struct all-from-module (path phase src-phase #f #f)) (list* path phase src-phase)] [(struct all-from-module (path phase src-phase exns #f)) (list* path phase exns src-phase)] [(struct all-from-module (path phase src-phase exns (vector prefix))) (list* path phase src-phase exns prefix)])) (define (encode-wraps wraps) (for/list ([wrap (in-list wraps)]) (match wrap [(struct phase-shift (amt src dest)) (box (vector amt src dest #f))] [(struct module-rename (phase kind set-id unmarshals renames mark-renames plus-kern?)) (define encoded-kind (eq? kind 'marked)) (define encoded-unmarshals (map encode-all-from-module unmarshals)) (define encoded-renames (encode-module-bindings renames)) (define-values (maybe-unmarshals maybe-renames) (if (null? encoded-unmarshals) (values encoded-renames mark-renames) (values encoded-unmarshals (cons encoded-renames mark-renames)))) (define mod-rename (list* phase encoded-kind set-id maybe-unmarshals maybe-renames)) (if plus-kern? (cons #t mod-rename) mod-rename)] [(struct lexical-rename (bool1 bool2 alist)) (define len (length alist)) (define vec (make-vector (+ (* 2 len) 2))) ; + 2 for booleans at the beginning (vector-set! vec 0 bool1) (vector-set! vec 1 bool2) (for ([(k v) (in-dict alist)] [i (in-naturals)]) (vector-set! vec (+ 2 i) k) (vector-set! vec (+ 2 i len) v)) vec] [(struct top-level-rename (flag)) flag] [(struct mark-barrier (value)) value] [(struct prune (syms)) (box syms)] [(struct wrap-mark (val)) (list val)]))) (define (encode-mark-map mm) mm #;(for/fold ([l empty]) ([(k v) (in-hash ht)]) (list* k v l))) (define-struct protected-symref (val)) (define encode-certs (match-lambda [(struct certificate:nest (m1 m2)) (list* (encode-mark-map m1) (encode-mark-map m2))] [(struct certificate:ref (val m)) (list* #f (make-protected-symref val) (encode-mark-map m))])) (define (encode-wrapped w) (match w [(struct wrapped (datum wraps certs)) (let* ([enc-datum (match datum [(cons a b) (let ([p (cons (encode-wrapped a) (let bloop ([b b]) (match b ['() null] [(cons b1 b2) (cons (encode-wrapped b1) (bloop b2))] [else (encode-wrapped b)])))] ; XXX Cylic list error possible [len (let loop ([datum datum][len 0]) (cond [(null? datum) #f] [(pair? datum) (loop (cdr datum) (add1 len))] [else len]))]) ;; for improper lists, we need to include the length so the ;; parser knows where the end of the improper list is (if len (cons len p) p))] [(box x) (box (encode-wrapped x))] [(? vector? v) (vector-map encode-wrapped v)] [(? prefab-struct-key) (define l (vector->list (struct->vector datum))) (apply make-prefab-struct (car l) (map encode-wrapped (cdr l)))] [_ datum])] [p (cons enc-datum (encode-wraps wraps))]) (if certs (vector p (encode-certs certs)) p))])) (define (lookup-encoded-wrapped w out) (hash-ref (out-encoded-wraps out) w (lambda () (error 'lookup-encoded-wrapped "Cannot find encoded version of wrap: ~e" w)))) (define (out-wrapped w out) (out-data (lookup-encoded-wrapped w out) out)) (define (out-stx s out) (out-shared s out (lambda () (match s [(struct stx (encoded)) (out-byte CPT_STX out) (out-wrapped encoded out)])))) (define (out-form form out) (match form [(? mod?) (out-module form out)] [(struct def-values (ids rhs)) (out-syntax DEFINE_VALUES_EXPD (list->vector (cons (protect-quote rhs) ids)) out)] [(struct def-syntaxes (ids rhs prefix max-let-depth)) (out-syntax DEFINE_SYNTAX_EXPD (list->vector (list* (protect-quote rhs) prefix max-let-depth *dummy* ids)) out)] [(struct def-for-syntax (ids rhs prefix max-let-depth)) (out-syntax DEFINE_FOR_SYNTAX_EXPD (list->vector (list* (protect-quote rhs) prefix max-let-depth *dummy* ids)) out)] [(struct seq0 (forms)) (out-marshaled begin0-sequence-type-num (map protect-quote forms) out)] [(struct seq (forms)) (out-marshaled sequence-type-num (map protect-quote forms) out)] [(struct splice (forms)) (out-syntax SPLICE_EXPD (make-seq forms) out)] [(struct req (reqs dummy)) (error "cannot handle top-level `require', yet") (out-syntax REQUIRE_EXPD (cons dummy reqs) out)] [else (out-expr form out)])) (define (out-expr expr out) (match expr [(struct toplevel (depth pos const? ready?)) (out-marshaled toplevel-type-num (cons depth (if (or const? ready?) (cons pos (bitwise-ior (if const? #x1 0) (if ready? #x2 0))) pos)) out)] [(struct topsyntax (depth pos midpt)) (out-marshaled quote-syntax-type-num (cons depth (cons pos midpt)) out)] [(struct primval (id)) (out-byte CPT_REFERENCE out) (out-number id out)] [(struct assign (id rhs undef-ok?)) (out-syntax SET_EXPD (cons undef-ok? (cons id rhs)) out)] [(struct localref (unbox? offset clear? other-clears? flonum?)) (if (and (not clear?) (not other-clears?) (not flonum?) (offset . < . (- CPT_SMALL_LOCAL_END CPT_SMALL_LOCAL_START))) (out-byte (+ (if unbox? CPT_SMALL_LOCAL_UNBOX_START CPT_SMALL_LOCAL_START) offset) out) (begin (out-byte (if unbox? CPT_LOCAL_UNBOX CPT_LOCAL) out) (if (not (or clear? other-clears? flonum?)) (out-number offset out) (begin (out-number (- (add1 offset)) out) (out-number (if clear? #x1 (if other-clears? #x2 (if flonum? #x3 0))) out)))))] [(? lam?) (out-lam expr out)] [(struct case-lam (name lams)) (let ([seq (make-case-seq name lams)]) ;; If all closures are empy, generate a case sequence directly (if (andmap (lambda (lam) (or (closure? lam) (and (lam? lam) (equal? (lam-closure-map lam) #())))) lams) (out-data seq out) (out-syntax CASE_LAMBDA_EXPD seq out)))] [(struct case-seq (name lams)) (out-marshaled case-lambda-sequence-type-num (cons (or name null) lams) out)] [(struct let-one (rhs body flonum? unused?)) (out-byte (cond [flonum? CPT_LET_ONE_FLONUM] [unused? CPT_LET_ONE_UNUSED] [else CPT_LET_ONE]) out) (out-expr (protect-quote rhs) out) (out-expr (protect-quote body) out)] [(struct let-void (count boxes? body)) (out-marshaled let-void-type-num (list* count boxes? (protect-quote body)) out)] [(struct let-rec (procs body)) (out-marshaled letrec-type-num (list* (length procs) (protect-quote body) procs) out)] [(struct install-value (count pos boxes? rhs body)) (out-marshaled let-value-type-num (list* count pos boxes? (protect-quote rhs) (protect-quote body)) out)] [(struct boxenv (pos body)) (out-syntax BOXENV_EXPD (cons pos (protect-quote body)) out)] [(struct branch (test then else)) (out-byte CPT_BRANCH out) (out-expr (protect-quote test) out) (out-expr (protect-quote then) out) (out-expr (protect-quote else) out)] [(struct application (rator rands)) (let ([len (length rands)]) (if (len . < . (- CPT_SMALL_APPLICATION_END CPT_SMALL_APPLICATION_START)) (out-byte (+ CPT_SMALL_APPLICATION_START (length rands)) out) (begin (out-byte CPT_APPLICATION out) (out-number len out))) (for-each (lambda (e) (out-expr (protect-quote e) out)) (cons rator rands)))] [(struct apply-values (proc args-expr)) (out-syntax APPVALS_EXPD (cons (protect-quote proc) (protect-quote args-expr)) out)] [(struct seq (exprs)) (out-form expr out)] [(struct beg0 (exprs)) (out-syntax BEGIN0_EXPD (make-seq0 exprs) out)] [(struct with-cont-mark (key val body)) (out-marshaled wcm-type-num (list* (protect-quote key) (protect-quote val) (protect-quote body)) out)] [(struct closure (lam gen-id)) (out-lam expr out)] [(struct indirect (val)) (out-expr val out)] [(struct varref (expr)) (out-syntax REF_EXPD expr out)] [else (out-value expr out)])) (define (out-lam expr out) (match expr [(struct indirect (val)) (out-lam val out)] [(struct closure (lam gen-id)) (out-shared expr out (lambda () (out-byte CPT_CLOSURE out) (out-number ((out-shared-index out) expr) out) (out-lam lam out)))] [(struct lam (name flags num-params param-types rest? closure-map closure-types max-let-depth body)) (let* ([l (protect-quote body)] [any-refs? (or (ormap (lambda (t) (memq t '(ref flonum))) param-types) (ormap (lambda (t) (memq t '(flonum))) closure-types))] [num-all-params ((if rest? add1 values) num-params)] [l (cons (make-svector (if any-refs? (list->vector (append (vector->list closure-map) (let* ([v (make-vector (ceiling (/ (* 2 (+ num-params (vector-length closure-map))) BITS_PER_MZSHORT)))] [set-bit! (lambda (i bit) (let ([pos (quotient (* 2 i) BITS_PER_MZSHORT)]) (vector-set! v pos (bitwise-ior (vector-ref v pos) (arithmetic-shift bit (modulo (* 2 i) BITS_PER_MZSHORT))))))]) (for ([t (in-list param-types)] [i (in-naturals)]) (when (eq? t 'ref) (set-bit! i 1)) (when (eq? t 'flonum) (set-bit! i 2))) (for ([t (in-list closure-types)] [i (in-naturals num-all-params)]) (when (eq? t 'flonum) (set-bit! i 2))) (vector->list v)))) closure-map)) l)] [l (if any-refs? (cons (vector-length closure-map) l) l)]) (out-marshaled unclosed-procedure-type-num (list* (+ (if rest? CLOS_HAS_REST 0) (if any-refs? CLOS_HAS_REF_ARGS 0) (if (memq 'preserves-marks flags) CLOS_PRESERVES_MARKS 0) (if (memq 'is-method flags) CLOS_IS_METHOD 0) (if (memq 'single-result flags) CLOS_SINGLE_RESULT 0)) num-all-params max-let-depth name l) out))])) (define (out-as-bytes expr ->bytes CPT len2 out #:before-length [before-length #f]) (out-shared expr out (lambda () (let ([s (->bytes expr)]) (out-byte CPT out) (when before-length (out-number before-length out)) (out-number (bytes-length s) out) (when len2 (out-number len2 out)) (out-bytes s out))))) (define (out-data expr out) (cond [(prefix? expr) (out-prefix expr out)] [(global-bucket? expr) (out-toplevel expr out)] [(module-variable? expr) (out-toplevel expr out)] [(free-id-info? expr) (out-free-id-info expr out)] [else (out-form expr out)])) (define (out-value expr out) (cond [(protected-symref? expr) (let* ([val (protected-symref-val expr)] [val-ref ((out-shared-index out) val)]) (out-value val-ref out))] [(and (symbol? expr) (not (symbol-interned? expr))) (out-as-bytes expr #:before-length (if (symbol-unreadable? expr) 0 1) (compose string->bytes/utf-8 symbol->string) CPT_WEIRD_SYMBOL #f out)] [(symbol? expr) (out-shared expr out (lambda () (define bs (string->bytes/utf-8 (symbol->string expr))) (define len (bytes-length bs)) (if (len . < . (- CPT_SMALL_SYMBOL_END CPT_SMALL_SYMBOL_START)) (out-byte (+ CPT_SMALL_SYMBOL_START len) out) (begin (out-byte CPT_SYMBOL out) (out-number len out))) (out-bytes bs out)))] [(keyword? expr) (out-as-bytes expr (compose string->bytes/utf-8 keyword->string) CPT_KEYWORD #f out)] [(string? expr) (out-as-bytes expr string->bytes/utf-8 CPT_CHAR_STRING (string-length expr) out)] [(bytes? expr) (out-as-bytes expr values CPT_BYTE_STRING #f out)] #; [(path? expr) (out-as-bytes expr path->bytes CPT_PATH #f out)] [(char? expr) (out-byte CPT_CHAR out) (out-number (char->integer expr) out)] [(and (exact-integer? expr) (and (expr . >= . -1073741824) (expr . <= . 1073741823))) (if (and (expr . >= . 0) (expr . < . (- CPT_SMALL_NUMBER_END CPT_SMALL_NUMBER_START))) (out-byte (+ CPT_SMALL_NUMBER_START expr) out) (begin (out-byte CPT_INT out) (out-number expr out)))] [(null? expr) (out-byte CPT_NULL out)] [(eq? expr #t) (out-byte CPT_TRUE out)] [(eq? expr #f) (out-byte CPT_FALSE out)] [(void? expr) (out-byte CPT_VOID out)] [(box? expr) (out-byte CPT_BOX out) (out-data (unbox expr) out)] [(pair? expr) (local [(define seen? (make-hasheq)) ; XXX Maybe this should be global? (define (list-length-before-cycle/improper-end l) (if (hash-has-key? seen? l) (begin (values 0 #f)) (begin (hash-set! seen? l #t) (cond [(null? l) (values 0 #t)] [(pair? l) (let-values ([(len proper?) (list-length-before-cycle/improper-end (cdr l))]) (values (add1 len) proper?))] [else (values 0 #f)])))) (define-values (len proper?) (list-length-before-cycle/improper-end expr)) (define (print-contents-as-proper) (for ([e (in-list expr)]) (out-data e out))) (define (print-contents-as-improper) (let loop ([l expr] [i len]) (cond [(zero? i) (out-data l out)] [else (out-data (car l) out) (loop (cdr l) (sub1 i))])))] (if proper? (if (len . < . (- CPT_SMALL_PROPER_LIST_END CPT_SMALL_PROPER_LIST_START)) (begin (out-byte (+ CPT_SMALL_PROPER_LIST_START len) out) (print-contents-as-proper)) (begin (out-byte CPT_LIST out) (out-number len out) (print-contents-as-proper) (out-data null out))) (if (len . < . (- CPT_SMALL_LIST_END CPT_SMALL_LIST_START)) ; XXX If len = 1 (or maybe = 2?) then this could by CPT_PAIR (begin (out-byte (+ CPT_SMALL_LIST_START len) out) (print-contents-as-improper)) (begin (out-byte CPT_LIST out) (out-number len out) (print-contents-as-improper)))))] [(vector? expr) (out-byte CPT_VECTOR out) (out-number (vector-length expr) out) (for ([v (in-vector expr)]) (out-data v out))] [(hash? expr) (out-shared expr out (lambda () (out-byte CPT_HASH_TABLE out) (out-number (cond [(hash-eqv? expr) 2] [(hash-eq? expr) 0] [else 1]) out) (out-number (hash-count expr) out) (for ([(k v) (in-hash expr)]) (out-data k out) (out-data v out))))] [(svector? expr) (let* ([vec (svector-vec expr)] [len (vector-length vec)]) (if (len . < . (- CPT_SMALL_SVECTOR_END CPT_SMALL_SVECTOR_START)) (out-byte (+ CPT_SMALL_SVECTOR_START len) out) (begin (out-byte CPT_SVECTOR out) (out-number len out))) (for ([n (in-range (sub1 len) -1 -1)]) (out-number (vector-ref vec n) out)))] [(module-path-index? expr) (out-shared expr out (lambda () (out-byte CPT_MODULE_INDEX out) (let-values ([(name base) (module-path-index-split expr)]) (out-data name out) (out-data base out))))] [(module-decl? expr) (out-marshaled module-type-num (module-decl-content expr) out)] [(stx? expr) (out-stx expr out)] [(wrapped? expr) (out-wrapped expr out)] [(prefab-struct-key expr) => (lambda (key) (define pre-v (struct->vector expr)) (vector-set! pre-v 0 key) (out-byte CPT_PREFAB out) (out-data pre-v out))] [else (out-byte CPT_QUOTE out) (if (quoted? expr) (out-data (quoted-v expr) out) (let ([s (open-output-bytes)]) ;; print `expr' to a string, but print paths ;; in a special way (parameterize ([pretty-print-size-hook (lambda (v mode port) (and (path? v) (let ([v (make-relative v)]) (+ 2 (let ([p (open-output-bytes)]) (write (path->bytes v) p) (bytes-length (get-output-bytes p)))))))] [pretty-print-print-hook (lambda (v mode port) (display "#^" port) (write (path->bytes (make-relative v)) port))]) (pretty-write expr s)) (out-byte CPT_ESCAPE out) (let ([bstr (get-output-bytes s)]) (out-number (bytes-length bstr) out) (out-bytes bstr out))))])) (define-struct quoted (v)) (define (protect-quote v) (if (or (pair? v) (vector? v) (prefab-struct-key v) (box? v) (hash? v) (svector? v)) (make-quoted v) v)) (define-struct svector (vec)) (define (make-relative v) (let ([r (current-write-relative-directory)]) (if r (find-relative-path r v) v))) ;; ----------------------------------------