+;;;; Conditions signalled on invalid fasls (wrong fasl version, etc),
+;;;; so that user code (esp. ASDF) can reasonably handle attempts to
+;;;; load such fasls by recompiling them, etc. For simplicity's sake
+;;;; make only condition INVALID-FASL part of the public interface,
+;;;; and keep the guts internal.
+
+(define-condition invalid-fasl (error)
+ ((stream :reader invalid-fasl-stream :initarg :stream)
+ (expected :reader invalid-fasl-expected :initarg :expected))
+ (:report
+ (lambda (condition stream)
+ (format stream "~S is an invalid fasl file."
+ (invalid-fasl-stream condition)))))
+
+(define-condition invalid-fasl-header (invalid-fasl)
+ ((byte :reader invalid-fasl-byte :initarg :byte)
+ (byte-nr :reader invalid-fasl-byte-nr :initarg :byte-nr))
+ (:report
+ (lambda (condition stream)
+ (format stream "~@<~S contains an illegal byte in the FASL header at ~
+ position ~A: Expected ~A, got ~A.~:@>"
+ (invalid-fasl-stream condition)
+ (invalid-fasl-byte-nr condition)
+ (invalid-fasl-expected condition)
+ (invalid-fasl-byte condition)))))
+
+(define-condition invalid-fasl-version (invalid-fasl)
+ ((version :reader invalid-fasl-version :initarg :version))
+ (:report
+ (lambda (condition stream)
+ (format stream "~@<~S is a fasl file compiled with SBCL ~W, and ~
+ can't be loaded into SBCL ~W.~:@>"
+ (invalid-fasl-stream condition)
+ (invalid-fasl-version condition)
+ (invalid-fasl-expected condition)))))
+
+(define-condition invalid-fasl-implementation (invalid-fasl)
+ ((implementation :reader invalid-fasl-implementation
+ :initarg :implementation))
+ (:report
+ (lambda (condition stream)
+ (format stream "~S was compiled for implementation ~A, but this is a ~A."
+ (invalid-fasl-stream condition)
+ (invalid-fasl-implementation condition)
+ (invalid-fasl-expected condition)))))
+
+(define-condition invalid-fasl-features (invalid-fasl)
+ ((potential-features :reader invalid-fasl-potential-features
+ :initarg :potential-features)
+ (features :reader invalid-fasl-features :initarg :features))
+ (:report
+ (lambda (condition stream)
+ (format stream "~@<incompatible ~S in fasl file ~S: ~2I~_~
+ Of features affecting binary compatibility, ~4I~_~S~2I~_~
+ the fasl has ~4I~_~A,~2I~_~
+ while the runtime expects ~4I~_~A.~:>"
+ '*features*
+ (invalid-fasl-stream condition)
+ (invalid-fasl-potential-features condition)
+ (invalid-fasl-features condition)
+ (invalid-fasl-expected condition)))))
+
+;;; Skips past the shebang line on stream, if any.
+(defun maybe-skip-shebang-line (stream)
+ (let ((p (file-position stream)))
+ (flet ((next () (read-byte stream nil)))
+ (unwind-protect
+ (when (and (eq (next) (char-code #\#))
+ (eq (next) (char-code #\!)))
+ (setf p nil)
+ (loop for x = (next)
+ until (or (not x) (eq x (char-code #\newline)))))
+ (when p
+ (file-position stream p))))
+ t))
+
+;;; Returns T if the stream is a binary input stream with a FASL header.
+(defun fasl-header-p (stream &key errorp)
+ (unless (and (member (stream-element-type stream) '(character base-char))
+ ;; give up if it's not a file stream, or it's an
+ ;; fd-stream but it's either not bivalent or not
+ ;; seekable (doesn't really have a file)
+ (or (not (typep stream 'file-stream))
+ (and (typep stream 'fd-stream)
+ (or (not (sb!impl::fd-stream-bivalent-p stream))
+ (not (sb!impl::fd-stream-file stream))))))
+ (let ((p (file-position stream)))
+ (unwind-protect
+ (let* ((header *fasl-header-string-start-string*)
+ (buffer (make-array (length header) :element-type '(unsigned-byte 8)))
+ (n 0))
+ (flet ((scan ()
+ (maybe-skip-shebang-line stream)
+ (setf n (read-sequence buffer stream))))
+ (if errorp
+ (scan)
+ (or (ignore-errors (scan))
+ ;; no a binary input stream
+ (return-from fasl-header-p nil))))
+ (if (mismatch buffer header
+ :test #'(lambda (code char) (= code (char-code char))))
+ ;; Immediate EOF is valid -- we want to match what
+ ;; CHECK-FASL-HEADER does...
+ (or (zerop n)
+ (when errorp
+ (error 'fasl-header-missing
+ :stream stream
+ :fhsss buffer
+ :expected header)))
+ t))
+ (file-position stream p)))))
+
+