dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

sbcl.lisp (80172B)


      1 ;;;;; -*- indent-tabs-mode: nil -*-
      2 ;;;
      3 ;;; slynk-sbcl.lisp --- SLY backend for SBCL.
      4 ;;;
      5 ;;; Created 2003, Daniel Barlow <dan@metacircles.com>
      6 ;;;
      7 ;;; This code has been placed in the Public Domain.  All warranties are
      8 ;;; disclaimed.
      9 
     10 ;;; Requires the SB-INTROSPECT contrib.
     11 
     12 ;;; Administrivia
     13 
     14 (defpackage slynk-sbcl
     15   (:use cl slynk-backend slynk-source-path-parser slynk-source-file-cache)
     16   (:export
     17    #:with-sbcl-version>=))
     18 
     19 (in-package slynk-sbcl)
     20 
     21 (eval-when (:compile-toplevel :load-toplevel :execute)
     22   (require 'sb-bsd-sockets)
     23   (require 'sb-introspect)
     24   (require 'sb-posix)
     25   (require 'sb-cltl2))
     26 
     27 (declaim (optimize (debug 2)
     28                    (sb-c::insert-step-conditions 0)
     29                    (sb-c::insert-debug-catch 0)))
     30 
     31 ;;; backwards compability tests
     32 
     33 (eval-when (:compile-toplevel :load-toplevel :execute)
     34   ;; Generate a form suitable for testing for stepper support (0.9.17)
     35   ;; with #+.
     36   (defun sbcl-with-new-stepper-p ()
     37     (with-symbol 'enable-stepping 'sb-impl))
     38   ;; Ditto for weak hash-tables
     39   (defun sbcl-with-weak-hash-tables ()
     40     (with-symbol 'hash-table-weakness 'sb-ext))
     41   ;; And for xref support (1.0.1)
     42   (defun sbcl-with-xref-p ()
     43     (with-symbol 'who-calls 'sb-introspect))
     44   ;; ... for restart-frame support (1.0.2)
     45   (defun sbcl-with-restart-frame ()
     46     (with-symbol 'frame-has-debug-tag-p 'sb-debug))
     47   ;; ... for :setf :inverse info (1.1.17)
     48   (defun sbcl-with-setf-inverse-meta-info ()
     49     (boolean-to-feature-expression
     50      ;; going through FIND-SYMBOL since META-INFO was renamed from
     51      ;; TYPE-INFO in 1.2.10.
     52      (let ((sym (find-symbol "META-INFO" "SB-C")))
     53        (and sym
     54             (fboundp sym)
     55             (funcall sym :setf :inverse ()))))))
     56 
     57 ;;; slynk-mop
     58 
     59 (import-slynk-mop-symbols :sb-mop '(:slot-definition-documentation))
     60 
     61 (defun slynk-mop:slot-definition-documentation (slot)
     62   (sb-pcl::documentation slot t))
     63 
     64 ;; stream support
     65 
     66 (defimplementation gray-package-name ()
     67   "SB-GRAY")
     68 
     69 ;; Pretty printer calls this, apparently
     70 (defmethod sb-gray:stream-line-length
     71     ((s sb-gray:fundamental-character-input-stream))
     72   nil)
     73 
     74 ;;; Connection info
     75 
     76 (defimplementation lisp-implementation-type-name ()
     77   "sbcl")
     78 
     79 ;; Declare return type explicitly to shut up STYLE-WARNINGS about
     80 ;; %SAP-ALIEN in ENABLE-SIGIO-ON-FD below.
     81 (declaim (ftype (function () (values (signed-byte 32) &optional)) getpid))
     82 (defimplementation getpid ()
     83   (sb-posix:getpid))
     84 
     85 ;;; UTF8
     86 
     87 (defimplementation string-to-utf8 (string)
     88   (sb-ext:string-to-octets string :external-format '(:utf8 :replacement
     89                                                      #+sb-unicode #\Replacement_Character
     90                                                      #-sb-unicode #\? )))
     91 
     92 (defimplementation utf8-to-string (octets)
     93   (sb-ext:octets-to-string octets :external-format '(:utf8 :replacement
     94                                                      #+sb-unicode #\Replacement_Character
     95                                                      #-sb-unicode #\? )))
     96 
     97 ;;; TCP Server
     98 
     99 (defimplementation preferred-communication-style ()
    100   (cond
    101     ;; fixme: when SBCL/win32 gains better select() support, remove
    102     ;; this.
    103     ((member :sb-thread *features*) :spawn)
    104     ((member :win32 *features*) nil)
    105     (t :fd-handler)))
    106 
    107 
    108 (defun resolve-hostname (host)
    109   "Returns valid IPv4 or IPv6 address for the host."
    110   ;; get all IPv4 and IPv6 addresses as a list
    111   (let* ((host-ents (multiple-value-list (sb-bsd-sockets:get-host-by-name host)))
    112          ;; remove protocols for which we don't have an address
    113          (addresses (remove-if-not #'sb-bsd-sockets:host-ent-address host-ents)))
    114     ;; Return the first one or nil,
    115     ;; but actually, it shouln't return nil, because
    116     ;; get-host-by-name will signal NAME-SERVICE-ERROR condition
    117     ;; if there isn't any address for the host.
    118     (first addresses)))
    119 
    120 
    121 (defimplementation create-socket (host port &key backlog)
    122   (let* ((host-ent (resolve-hostname host))
    123          (socket (make-instance (cond #+#.(slynk-backend:with-symbol 'inet6-socket 'sb-bsd-sockets)
    124                                       ((eql (sb-bsd-sockets:host-ent-address-type host-ent) 10)
    125                                        'sb-bsd-sockets:inet6-socket)
    126                                       (t
    127                                        'sb-bsd-sockets:inet-socket))
    128                                 :type :stream
    129                                 :protocol :tcp)))
    130     (setf (sb-bsd-sockets:sockopt-reuse-address socket) t)
    131     (sb-bsd-sockets:socket-bind socket (sb-bsd-sockets:host-ent-address host-ent) port)
    132 
    133     (sb-bsd-sockets:socket-listen socket (or backlog 5))
    134     socket))
    135 
    136 (defimplementation local-port (socket)
    137   (nth-value 1 (sb-bsd-sockets:socket-name socket)))
    138 
    139 (defimplementation close-socket (socket)
    140   (sb-sys:invalidate-descriptor (socket-fd socket))
    141   (sb-bsd-sockets:socket-close socket))
    142 
    143 (defimplementation accept-connection (socket &key
    144                                       external-format
    145                                       buffering timeout)
    146   (declare (ignore timeout))
    147   (make-socket-io-stream (accept socket) external-format
    148                          (ecase buffering
    149                            ((t :full) :full)
    150                            ((nil :none) :none)
    151                            ((:line) :line))))
    152 
    153 
    154 ;; The SIGIO stuff should probably be removed as it's unlikey that
    155 ;; anybody uses it.
    156 #-win32
    157 (progn
    158   (defimplementation install-sigint-handler (function)
    159     (sb-sys:enable-interrupt sb-unix:sigint
    160                              (lambda (&rest args)
    161                                (declare (ignore args))
    162                                (sb-sys:invoke-interruption
    163                                 (lambda ()
    164                                   (sb-sys:with-interrupts
    165                                     (funcall function)))))))
    166 
    167   (defvar *sigio-handlers* '()
    168     "List of (key . fn) pairs to be called on SIGIO.")
    169 
    170   (defun sigio-handler (signal code scp)
    171     (declare (ignore signal code scp))
    172     (sb-sys:with-interrupts
    173       (mapc (lambda (handler)
    174               (funcall (the function (cdr handler))))
    175             *sigio-handlers*)))
    176 
    177   (defun set-sigio-handler ()
    178     (sb-sys:enable-interrupt sb-unix:sigio #'sigio-handler))
    179 
    180   (defun enable-sigio-on-fd (fd)
    181     (sb-posix::fcntl fd sb-posix::f-setfl sb-posix::o-async)
    182     (sb-posix::fcntl fd sb-posix::f-setown (getpid))
    183     (values))
    184 
    185   (defimplementation add-sigio-handler (socket fn)
    186     (set-sigio-handler)
    187     (let ((fd (socket-fd socket)))
    188       (enable-sigio-on-fd fd)
    189       (push (cons fd fn) *sigio-handlers*)))
    190 
    191   (defimplementation remove-sigio-handlers (socket)
    192     (let ((fd (socket-fd socket)))
    193       (setf *sigio-handlers* (delete fd *sigio-handlers* :key #'car))
    194       (sb-sys:invalidate-descriptor fd))
    195     (close socket)))
    196 
    197 
    198 (defimplementation add-fd-handler (socket fun)
    199   (let ((fd (socket-fd socket))
    200         (handler nil))
    201     (labels ((add ()
    202                (setq handler (sb-sys:add-fd-handler fd :input #'run)))
    203              (run (fd)
    204                (sb-sys:remove-fd-handler handler) ; prevent recursion
    205                (unwind-protect
    206                     (funcall fun)
    207                  (when (sb-unix:unix-fstat fd) ; still open?
    208                    (add)))))
    209       (add))))
    210 
    211 (defimplementation remove-fd-handlers (socket)
    212   (sb-sys:invalidate-descriptor (socket-fd socket)))
    213 
    214 (defimplementation socket-fd (socket)
    215   (etypecase socket
    216     (fixnum socket)
    217     (sb-bsd-sockets:socket (sb-bsd-sockets:socket-file-descriptor socket))
    218     (file-stream (sb-sys:fd-stream-fd socket))))
    219 
    220 (defimplementation command-line-args ()
    221   sb-ext:*posix-argv*)
    222 
    223 (defimplementation dup (fd)
    224   (sb-posix:dup fd))
    225 
    226 (defvar *wait-for-input-called*)
    227 
    228 (defimplementation wait-for-input (streams &optional timeout)
    229   (assert (member timeout '(nil t)))
    230   (when (boundp '*wait-for-input-called*)
    231     (setq *wait-for-input-called* t))
    232   (let ((*wait-for-input-called* nil))
    233     (loop
    234       (let ((ready (remove-if-not #'input-ready-p streams)))
    235         (when ready (return ready)))
    236       (when (check-sly-interrupts)
    237         (return :interrupt))
    238       (when *wait-for-input-called*
    239         (return :interrupt))
    240       (when timeout
    241         (return nil))
    242       (sleep 0.1))))
    243 
    244 (defun fd-stream-input-buffer-empty-p (stream)
    245   (let ((buffer (sb-impl::fd-stream-ibuf stream)))
    246     (or (not buffer)
    247         (= (sb-impl::buffer-head buffer)
    248            (sb-impl::buffer-tail buffer)))))
    249 
    250 #-win32
    251 (defun input-ready-p (stream)
    252   (or (not (fd-stream-input-buffer-empty-p stream))
    253       #+#.(slynk-backend:with-symbol 'fd-stream-fd-type 'sb-impl)
    254       (eq :regular (sb-impl::fd-stream-fd-type stream))
    255       (not (sb-impl::sysread-may-block-p stream))))
    256 
    257 #+win32
    258 (progn
    259   (defun input-ready-p (stream)
    260     (or (not (fd-stream-input-buffer-empty-p stream))
    261         (handle-listen (sockint::fd->handle (sb-impl::fd-stream-fd stream)))))
    262 
    263   (sb-alien:define-alien-routine ("WSACreateEvent" wsa-create-event)
    264       sb-win32:handle)
    265 
    266   (sb-alien:define-alien-routine ("WSACloseEvent" wsa-close-event)
    267       sb-alien:int
    268     (event sb-win32:handle))
    269 
    270   (defconstant +fd-read+ #.(ash 1 0))
    271   (defconstant +fd-close+ #.(ash 1 5))
    272 
    273   (sb-alien:define-alien-routine ("WSAEventSelect" wsa-event-select)
    274       sb-alien:int
    275     (fd sb-alien:int)
    276     (handle sb-win32:handle)
    277     (mask sb-alien:long))
    278 
    279   (sb-alien:load-shared-object "kernel32.dll")
    280   (sb-alien:define-alien-routine ("WaitForSingleObjectEx"
    281                                   wait-for-single-object-ex)
    282       sb-alien:int
    283     (event sb-win32:handle)
    284     (milliseconds sb-alien:long)
    285     (alertable sb-alien:int))
    286 
    287   ;; see SB-WIN32:HANDLE-LISTEN
    288   (defun handle-listen (handle)
    289     (sb-alien:with-alien ((avail sb-win32:dword)
    290                           (buf (array char #.sb-win32::input-record-size)))
    291       (unless (zerop (sb-win32:peek-named-pipe handle nil 0 nil
    292                                                (sb-alien:alien-sap
    293                                                 (sb-alien:addr avail))
    294                                                nil))
    295         (return-from handle-listen (plusp avail)))
    296 
    297       (unless (zerop (sb-win32:peek-console-input handle
    298                                                   (sb-alien:alien-sap buf)
    299                                                   sb-win32::input-record-size
    300                                                   (sb-alien:alien-sap
    301                                                    (sb-alien:addr avail))))
    302         (return-from handle-listen (plusp avail))))
    303 
    304     (let ((event (wsa-create-event)))
    305       (wsa-event-select handle event (logior +fd-read+ +fd-close+))
    306       (let ((val (wait-for-single-object-ex event 0 0)))
    307         (wsa-close-event event)
    308         (unless (= val -1)
    309           (return-from handle-listen (zerop val)))))
    310 
    311     nil)
    312 
    313   )
    314 
    315 (defvar *external-format-to-coding-system*
    316   '((:iso-8859-1
    317      "latin-1" "latin-1-unix" "iso-latin-1-unix"
    318      "iso-8859-1" "iso-8859-1-unix")
    319     (:utf-8 "utf-8" "utf-8-unix")
    320     (:euc-jp "euc-jp" "euc-jp-unix")
    321     (:us-ascii "us-ascii" "us-ascii-unix")))
    322 
    323 ;; C.f. R.M.Kreuter in <20536.1219412774@progn.net> on sbcl-general,
    324 ;; 2008-08-22.
    325 (defvar *physical-pathname-host* (pathname-host (user-homedir-pathname)))
    326 
    327 (defimplementation filename-to-pathname (filename)
    328   (sb-ext:parse-native-namestring filename *physical-pathname-host*))
    329 
    330 (defimplementation find-external-format (coding-system)
    331   (car (rassoc-if (lambda (x) (member coding-system x :test #'equal))
    332                   *external-format-to-coding-system*)))
    333 
    334 (defimplementation set-default-directory (directory)
    335   (let ((directory (truename (merge-pathnames directory))))
    336     (sb-posix:chdir directory)
    337     (setf *default-pathname-defaults* directory)
    338     (default-directory)))
    339 
    340 (defun make-socket-io-stream (socket external-format buffering)
    341   (let ((args `(:output t
    342                 :input t
    343                 :element-type ,(if external-format
    344                                    'character
    345                                    '(unsigned-byte 8))
    346                 :buffering ,buffering
    347                 ,@(cond ((and external-format (sb-int:featurep :sb-unicode))
    348                          `(:external-format ,external-format))
    349                         (t '()))
    350                 :serve-events ,(eq :fd-handler
    351                                    (slynk-value '*communication-style* t))
    352                   ;; SBCL < 1.0.42.43 doesn't support :SERVE-EVENTS
    353                   ;; argument.
    354                 :allow-other-keys t)))
    355   (apply #'sb-bsd-sockets:socket-make-stream socket args)))
    356 
    357 (defun accept (socket)
    358   "Like socket-accept, but retry on EAGAIN."
    359   (loop (handler-case
    360             (return (sb-bsd-sockets:socket-accept socket))
    361           (sb-bsd-sockets:interrupted-error ()))))
    362 
    363 
    364 ;;;; Support for SBCL syntax
    365 
    366 ;;; SBCL's source code is riddled with #! reader macros.  Also symbols
    367 ;;; containing `!' have special meaning.  We have to work long and
    368 ;;; hard to be able to read the source.  To deal with #! reader
    369 ;;; macros, we use a special readtable.  The special symbols are
    370 ;;; converted by a condition handler.
    371 
    372 (defun feature-in-list-p (feature list)
    373   (etypecase feature
    374     (symbol (member feature list :test #'eq))
    375     (cons (flet ((subfeature-in-list-p (subfeature)
    376                    (feature-in-list-p subfeature list)))
    377             ;; Don't use ECASE since SBCL also has :host-feature,
    378             ;; don't need to handle it or anything else appearing in
    379             ;; the future or in erronous code.
    380             (case (first feature)
    381               (:or  (some  #'subfeature-in-list-p (rest feature)))
    382               (:and (every #'subfeature-in-list-p (rest feature)))
    383               (:not (destructuring-bind (e) (cdr feature)
    384                       (not (subfeature-in-list-p e)))))))))
    385 
    386 (defun shebang-reader (stream sub-character infix-parameter)
    387   (declare (ignore sub-character))
    388   (when infix-parameter
    389     (error "illegal read syntax: #~D!" infix-parameter))
    390   (let ((next-char (read-char stream)))
    391     (unless (find next-char "+-")
    392       (error "illegal read syntax: #!~C" next-char))
    393     ;; When test is not satisfied
    394     ;; FIXME: clearer if order of NOT-P and (NOT NOT-P) were reversed? then
    395     ;; would become "unless test is satisfied"..
    396     (when (let* ((*package* (find-package "KEYWORD"))
    397                  (*read-suppress* nil)
    398                  (not-p (char= next-char #\-))
    399                  (feature (read stream)))
    400             (if (feature-in-list-p feature *features*)
    401 		not-p
    402 		(not not-p)))
    403       ;; Read (and discard) a form from input.
    404       (let ((*read-suppress* t))
    405 	(read stream t nil t))))
    406  (values))
    407 
    408 (defvar *shebang-readtable*
    409   (let ((*readtable* (copy-readtable nil)))
    410     (set-dispatch-macro-character #\# #\!
    411                                   (lambda (s c n) (shebang-reader s c n))
    412                                   *readtable*)
    413     *readtable*))
    414 
    415 (defun shebang-readtable ()
    416   *shebang-readtable*)
    417 
    418 (defun sbcl-package-p (package)
    419   (let ((name (package-name package)))
    420     (eql (mismatch "SB-" name) 3)))
    421 
    422 (defun sbcl-source-file-p (filename)
    423   (when filename
    424     (loop for (nil pattern) in (logical-pathname-translations "SYS")
    425           thereis (pathname-match-p filename pattern))))
    426 
    427 (defun guess-readtable-for-filename (filename)
    428   (if (sbcl-source-file-p filename)
    429       (shebang-readtable)
    430       *readtable*))
    431 
    432 (defvar *debootstrap-packages* t)
    433 
    434 (defun call-with-debootstrapping (fun)
    435   (handler-bind ((sb-int:bootstrap-package-not-found
    436                   #'sb-int:debootstrap-package))
    437     (funcall fun)))
    438 
    439 (defmacro with-debootstrapping (&body body)
    440   `(call-with-debootstrapping (lambda () ,@body)))
    441 
    442 (defimplementation call-with-syntax-hooks (fn)
    443   (cond ((and *debootstrap-packages*
    444               (sbcl-package-p *package*))
    445          (with-debootstrapping (funcall fn)))
    446         (t
    447          (funcall fn))))
    448 
    449 (defimplementation default-readtable-alist ()
    450   (let ((readtable (shebang-readtable)))
    451     (loop for p in (remove-if-not #'sbcl-package-p (list-all-packages))
    452           collect (cons (package-name p) readtable))))
    453 
    454 ;;; Packages
    455 
    456 #+#.(slynk-backend:with-symbol 'package-local-nicknames 'sb-ext)
    457 (defimplementation package-local-nicknames (package)
    458   (sb-ext:package-local-nicknames package))
    459 
    460 ;;; Utilities
    461 
    462 (defun slynk-value (name &optional errorp)
    463   ;; Easy way to refer to symbol values in SLYNK, which doesn't yet exist when
    464   ;; this is file is loaded.
    465   (let ((symbol (find-symbol (string name) :slynk)))
    466     (if (and symbol (or errorp (boundp symbol)))
    467         (symbol-value symbol)
    468         (when errorp
    469           (error "~S does not exist in SLYNK." name)))))
    470 
    471 (defun sbcl-version>= (&rest subversions)
    472   #+#.(slynk-backend:with-symbol 'assert-version->= 'sb-ext)
    473   (values (ignore-errors (apply #'sb-ext:assert-version->= subversions) t))
    474   #-#.(slynk-backend:with-symbol 'assert-version->= 'sb-ext)
    475   nil)
    476 
    477 (defmacro with-sbcl-version>= (&rest subversions)
    478   `(if (sbcl-version>= ,@subversions)
    479        '(:and) '(:or)))
    480 
    481 #+#.(slynk-backend:with-symbol 'function-lambda-list 'sb-introspect)
    482 (defimplementation arglist (fname)
    483   (sb-introspect:function-lambda-list fname))
    484 
    485 #-#.(slynk-backend:with-symbol 'function-lambda-list 'sb-introspect)
    486 (defimplementation arglist (fname)
    487   (sb-introspect:function-arglist fname))
    488 
    489 (defimplementation function-name (f)
    490   (check-type f function)
    491   (sb-impl::%fun-name f))
    492 
    493 (defmethod declaration-arglist ((decl-identifier (eql 'optimize)))
    494   (flet ((ensure-list (thing) (if (listp thing) thing (list thing))))
    495     (let* ((flags (sb-cltl2:declaration-information decl-identifier)))
    496       (if flags
    497           ;; Symbols aren't printed with package qualifiers, but the
    498           ;; FLAGS would have to be fully qualified when used inside a
    499           ;; declaration. So we strip those as long as there's no
    500           ;; better way. (FIXME)
    501           `(&any ,@(remove-if-not
    502                     #'(lambda (qualifier)
    503                         (find-symbol (symbol-name (first qualifier)) :cl))
    504                     flags :key #'ensure-list))
    505           (call-next-method)))))
    506 
    507 #+#.(slynk-backend:with-symbol 'deftype-lambda-list 'sb-introspect)
    508 (defmethod type-specifier-arglist :around (typespec-operator)
    509   (multiple-value-bind (arglist foundp)
    510       (sb-introspect:deftype-lambda-list typespec-operator)
    511     (if foundp arglist (call-next-method))))
    512 
    513 (defimplementation type-specifier-p (symbol)
    514   (or (sb-ext:valid-type-specifier-p symbol)
    515       (not (eq (type-specifier-arglist symbol) :not-available))))
    516 
    517 (defvar *buffer-name* nil)
    518 (defvar *buffer-tmpfile* nil)
    519 (defvar *buffer-offset*)
    520 (defvar *buffer-substring* nil)
    521 
    522 (defvar *previous-compiler-condition* nil
    523   "Used to detect duplicates.")
    524 
    525 (defun handle-notification-condition (condition)
    526   "Handle a condition caused by a compiler warning.
    527 This traps all compiler conditions at a lower-level than using
    528 C:*COMPILER-NOTIFICATION-FUNCTION*. The advantage is that we get to
    529 craft our own error messages, which can omit a lot of redundant
    530 information."
    531   (unless (or (eq condition *previous-compiler-condition*))
    532     ;; First resignal warnings, so that outer handlers -- which may choose to
    533     ;; muffle this -- get a chance to run.
    534     (when (typep condition 'warning)
    535       (signal condition))
    536     (setq *previous-compiler-condition* condition)
    537     (signal-compiler-condition (real-condition condition)
    538                                (sb-c::find-error-context nil))))
    539 
    540 (defun signal-compiler-condition (condition context)
    541   (signal 'compiler-condition
    542           :original-condition condition
    543           :severity (etypecase condition
    544                       (sb-ext:compiler-note :note)
    545                       (sb-c:compiler-error  :error)
    546                       (reader-error         :read-error)
    547                       (error                :error)
    548                       #+#.(slynk-backend:with-symbol early-deprecation-warning sb-ext)
    549                       (sb-ext::early-deprecation-warning :early-deprecation-warning)
    550                       #+#.(slynk-backend:with-symbol late-deprecation-warning sb-ext)
    551                       (sb-ext::late-deprecation-warning :late-deprecation-warning)
    552                       #+#.(slynk-backend:with-symbol final-deprecation-warning sb-ext)
    553                       (sb-ext::final-deprecation-warning :final-deprecation-warning)
    554                       #+#.(slynk-backend:with-symbol redefinition-warning
    555                             sb-kernel)
    556                       (sb-kernel:redefinition-warning
    557                        :redefinition)
    558                       (style-warning        :style-warning)
    559                       (warning              :warning))
    560           :references (condition-references condition)
    561           :message (brief-compiler-message-for-emacs condition)
    562           :source-context (compiler-error-context context)
    563           :location (compiler-note-location condition context)))
    564 
    565 (defun real-condition (condition)
    566   "Return the encapsulated condition or CONDITION itself."
    567   (typecase condition
    568     (sb-int:encapsulated-condition (sb-int:encapsulated-condition condition))
    569     (t condition)))
    570 
    571 (defun condition-references (condition)
    572   (if (typep condition 'sb-int:reference-condition)
    573       (externalize-reference
    574        (sb-int:reference-condition-references condition))))
    575 
    576 (defun compiler-note-location (condition context)
    577   (flet ((bailout ()
    578            (return-from compiler-note-location
    579              (make-error-location "No error location available"))))
    580     (cond (context
    581            (locate-compiler-note
    582             (sb-c::compiler-error-context-file-name context)
    583             (compiler-source-path context)
    584             (sb-c::compiler-error-context-original-source context)))
    585           ((typep condition 'reader-error)
    586            (let* ((stream (stream-error-stream condition))
    587                   ;; If STREAM is, for example, a STRING-INPUT-STREAM,
    588                   ;; an error will be signaled since PATHNAME only
    589                   ;; accepts a "stream associated with a file" which
    590                   ;; is a complicated predicate and hard to test
    591                   ;; portably.
    592                   (file   (ignore-errors (pathname stream))))
    593              (unless (and file (open-stream-p stream))
    594                (bailout))
    595              (if (compiling-from-buffer-p file)
    596                  ;; The stream position for e.g. "comma not inside
    597                  ;; backquote" is at the character following the
    598                  ;; comma, :offset is 0-based, hence the 1-.
    599                  (make-location (list :buffer *buffer-name*)
    600                                 (list :offset *buffer-offset*
    601                                       (1- (file-position stream))))
    602                  (progn
    603                    (assert (compiling-from-file-p file))
    604                    ;; No 1- because :position is 1-based.
    605                    (make-location (list :file (namestring file))
    606                                   (list :position (file-position stream)))))))
    607           (t (bailout)))))
    608 
    609 (defun compiling-from-buffer-p (filename)
    610   (and *buffer-name*
    611        ;; The following is to trigger COMPILING-FROM-GENERATED-CODE-P
    612        ;; in LOCATE-COMPILER-NOTE, and allows handling nested
    613        ;; compilation from eg. hitting C-C on (eval-when ... (require ..))).
    614        ;;
    615        ;; PROBE-FILE to handle tempfile directory being a symlink.
    616        (pathnamep filename)
    617        (let ((true1 (probe-file filename))
    618              (true2 (probe-file *buffer-tmpfile*)))
    619          (and true1 (equal true1 true2)))))
    620 
    621 (defun compiling-from-file-p (filename)
    622   (and (pathnamep filename)
    623        (or (null *buffer-name*)
    624            (null *buffer-tmpfile*)
    625            (let ((true1 (probe-file filename))
    626                  (true2 (probe-file *buffer-tmpfile*)))
    627              (not (and true1 (equal true1 true2)))))))
    628 
    629 (defun compiling-from-generated-code-p (filename source)
    630   (and (eq filename :lisp) (stringp source)))
    631 
    632 (defun locate-compiler-note (file source-path source)
    633   (cond ((compiling-from-buffer-p file)
    634          (make-location (list :buffer *buffer-name*)
    635                         (list :offset  *buffer-offset*
    636                               (source-path-string-position
    637                                source-path *buffer-substring*))))
    638         ((compiling-from-file-p file)
    639          (let ((position (source-path-file-position source-path file)))
    640            (make-location (list :file (namestring file))
    641                           (list :position (and position
    642                                                (1+ position))))))
    643         ((compiling-from-generated-code-p file source)
    644          (make-location (list :source-form source)
    645                         (list :position 1)))
    646         (t
    647          (error "unhandled case in compiler note ~S ~S ~S"
    648                 file source-path source))))
    649 
    650 (defun brief-compiler-message-for-emacs (condition)
    651   "Briefly describe a compiler error for Emacs.
    652 When Emacs presents the message it already has the source popped up
    653 and the source form highlighted. This makes much of the information in
    654 the error-context redundant."
    655   (let ((sb-int:*print-condition-references* nil))
    656     (princ-to-string condition)))
    657 
    658 (defun compiler-error-context (error-context)
    659   "Describe a compiler error for Emacs including context information."
    660   (declare (type (or sb-c::compiler-error-context null) error-context))
    661   (multiple-value-bind (enclosing source)
    662       (if error-context
    663           (values (sb-c::compiler-error-context-enclosing-source error-context)
    664                   (sb-c::compiler-error-context-source error-context)))
    665     (and (or enclosing source)
    666          (format nil "~@[--> ~{~<~%--> ~1:;~A~> ~}~%~]~@[~{==>~%~A~%~}~]"
    667                  enclosing source))))
    668 
    669 (defun compiler-source-path (context)
    670   "Return the source-path for the current compiler error.
    671 Returns NIL if this cannot be determined by examining internal
    672 compiler state."
    673   (cond ((sb-c::node-p context)
    674          (reverse
    675           (sb-c::source-path-original-source
    676            (sb-c::node-source-path context))))
    677         ((sb-c::compiler-error-context-p context)
    678          (reverse
    679           (sb-c::compiler-error-context-original-source-path context)))))
    680 
    681 (defimplementation call-with-compilation-hooks (function)
    682   (declare (type function function))
    683   (handler-bind
    684       ;; N.B. Even though these handlers are called HANDLE-FOO they
    685       ;; actually decline, i.e. the signalling of the original
    686       ;; condition continues upward.
    687       ((sb-c:fatal-compiler-error #'handle-notification-condition)
    688        (sb-c:compiler-error       #'handle-notification-condition)
    689        (sb-ext:compiler-note      #'handle-notification-condition)
    690        (error                     #'handle-notification-condition)
    691        (warning                   #'handle-notification-condition))
    692     (funcall function)))
    693 
    694 ;;; HACK: SBCL 1.2.12 shipped with a bug where
    695 ;;; SB-EXT:RESTRICT-COMPILER-POLICY would signal an error when there
    696 ;;; were no policy restrictions in place. This workaround ensures the
    697 ;;; existence of at least one dummy restriction.
    698 (handler-case (sb-ext:restrict-compiler-policy)
    699   (error () (sb-ext:restrict-compiler-policy 'debug)))
    700 
    701 (defun compiler-policy (qualities)
    702   "Return compiler policy qualities present in the QUALITIES alist.
    703 QUALITIES is an alist with (quality . value)"
    704   #+#.(slynk-backend:with-symbol 'restrict-compiler-policy 'sb-ext)
    705   (loop with policy = (sb-ext:restrict-compiler-policy)
    706         for (quality) in qualities
    707         collect (cons quality
    708                       (or (cdr (assoc quality policy))
    709                           0))))
    710 
    711 (defun (setf compiler-policy) (policy)
    712   (declare (ignorable policy))
    713   #+#.(slynk-backend:with-symbol 'restrict-compiler-policy 'sb-ext)
    714   (loop for (qual . value) in policy
    715         do (sb-ext:restrict-compiler-policy qual value)))
    716 
    717 (defmacro with-compiler-policy (policy &body body)
    718   (let ((current-policy (gensym)))
    719     `(let ((,current-policy (compiler-policy ,policy)))
    720        (setf (compiler-policy) ,policy)
    721        (unwind-protect (progn ,@body)
    722          (setf (compiler-policy) ,current-policy)))))
    723 
    724 (defimplementation slynk-compile-file (input-file output-file
    725                                        load-p external-format
    726                                        &key policy)
    727   (multiple-value-bind (output-file warnings-p failure-p)
    728       (with-compiler-policy policy
    729         (with-compilation-hooks ()
    730           (compile-file input-file :output-file output-file
    731                         :external-format external-format)))
    732     (values output-file warnings-p
    733             (or failure-p
    734                 (when load-p
    735                   ;; Cache the latest source file for definition-finding.
    736                   (source-cache-get input-file
    737                                     (file-write-date input-file))
    738                   (not (load output-file)))))))
    739 
    740 ;;;; compile-string
    741 
    742 ;;; We copy the string to a temporary file in order to get adequate
    743 ;;; semantics for :COMPILE-TOPLEVEL and :LOAD-TOPLEVEL EVAL-WHEN forms
    744 ;;; which the previous approach using
    745 ;;;     (compile nil `(lambda () ,(read-from-string string)))
    746 ;;; did not provide.
    747 
    748 (locally (declare (sb-ext:muffle-conditions sb-ext:compiler-note))
    749 
    750 (sb-alien:define-alien-routine (#-win32 "tempnam" #+win32 "_tempnam" tempnam)
    751     sb-alien:c-string
    752   (dir sb-alien:c-string)
    753   (prefix sb-alien:c-string)))
    754 
    755 (defun temp-file-name ()
    756   "Return a temporary file name to compile strings into."
    757   (tempnam nil "slime"))
    758 
    759 (defvar *trap-load-time-warnings* t)
    760 
    761 (defimplementation slynk-compile-string (string &key buffer position filename
    762                                                 line column policy)
    763   (declare (ignore line column))
    764   (let ((*buffer-name* buffer)
    765         (*buffer-offset* position)
    766         (*buffer-substring* string)
    767         (*buffer-tmpfile* (temp-file-name)))
    768     (labels ((load-it (filename)
    769                (cond (*trap-load-time-warnings*
    770                       (with-compilation-hooks () (load filename)))
    771                      (t (load filename))))
    772              (cf ()
    773                (with-compiler-policy policy
    774                  (with-compilation-unit
    775                      (:source-plist (list :emacs-buffer buffer
    776                                           :emacs-filename filename
    777                                           :emacs-package (package-name *package*)
    778                                           :emacs-position position
    779                                           :emacs-string string)
    780                       :source-namestring filename
    781                       :allow-other-keys t)
    782                    (compile-file *buffer-tmpfile* :external-format :utf-8)))))
    783       (with-open-file (s *buffer-tmpfile* :direction :output :if-exists :error
    784                          :external-format :utf-8)
    785         (write-string string s))
    786       (unwind-protect
    787            (multiple-value-bind (output-file warningsp failurep)
    788                (with-compilation-hooks () (cf))
    789              (declare (ignore warningsp))
    790              (when output-file
    791                (load-it output-file))
    792              (not failurep))
    793         (ignore-errors
    794           (delete-file *buffer-tmpfile*)
    795           (delete-file (compile-file-pathname *buffer-tmpfile*)))))))
    796 
    797 ;;;; Definitions
    798 
    799 (defparameter *definition-types*
    800   '(:variable defvar
    801     :constant defconstant
    802     :type deftype
    803     :symbol-macro define-symbol-macro
    804     :macro defmacro
    805     :compiler-macro define-compiler-macro
    806     :function defun
    807     :generic-function defgeneric
    808     :method defmethod
    809     :setf-expander define-setf-expander
    810     :structure defstruct
    811     :condition define-condition
    812     :class defclass
    813     :method-combination define-method-combination
    814     :package defpackage
    815     :transform :deftransform
    816     :optimizer :defoptimizer
    817     :vop :define-vop
    818     :source-transform :define-source-transform
    819     :ir1-convert :def-ir1-translator
    820     :declaration declaim
    821     :alien-type :define-alien-type)
    822   "Map SB-INTROSPECT definition type names to SLY-friendly forms")
    823 
    824 (defun definition-specifier (type)
    825   "Return a pretty specifier for NAME representing a definition of type TYPE."
    826   (getf *definition-types* type))
    827 
    828 (defun make-dspec (type name source-location)
    829   (list* (definition-specifier type)
    830          name
    831          (sb-introspect::definition-source-description source-location)))
    832 
    833 (defimplementation find-definitions (name)
    834   (loop for type in *definition-types* by #'cddr
    835         for defsrcs = (sb-introspect:find-definition-sources-by-name name type)
    836         for filtered-defsrcs = (if (eq type :generic-function)
    837                                    (remove :invalid defsrcs
    838                                            :key #'categorize-definition-source)
    839                                    defsrcs)
    840         append (loop for defsrc in filtered-defsrcs collect
    841                      (list (make-dspec type name defsrc)
    842                            (converting-errors-to-error-location
    843                              (definition-source-for-emacs defsrc
    844                                  type name))))))
    845 
    846 (defimplementation find-source-location (obj)
    847   (flet ((general-type-of (obj)
    848            (typecase obj
    849              (method             :method)
    850              (generic-function   :generic-function)
    851              (function           :function)
    852              (structure-class    :structure-class)
    853              (class              :class)
    854              (method-combination :method-combination)
    855              (package            :package)
    856              (condition          :condition)
    857              (structure-object   :structure-object)
    858              (standard-object    :standard-object)
    859              (t                  :thing)))
    860          (to-string (obj)
    861            (typecase obj
    862              ;; Packages are possibly named entities.
    863              (package (princ-to-string obj))
    864              ((or structure-object standard-object condition)
    865               (with-output-to-string (s)
    866                 (print-unreadable-object (obj s :type t :identity t))))
    867              (t (princ-to-string obj)))))
    868     (converting-errors-to-error-location
    869       (let ((defsrc (sb-introspect:find-definition-source obj)))
    870         (definition-source-for-emacs defsrc
    871                                      (general-type-of obj)
    872                                      (to-string obj))))))
    873 
    874 (defmacro with-definition-source ((&rest names) obj &body body)
    875   "Like with-slots but works only for structs."
    876   (flet ((reader (slot)
    877            ;; Use read-from-string instead of intern so that
    878            ;; conc-name can be a string such as ext:struct- and not
    879            ;; cause errors and not force interning ext::struct-
    880            (read-from-string
    881             (concatenate 'string "sb-introspect:definition-source-"
    882                          (string slot)))))
    883     (let ((tmp (gensym "OO-")))
    884       ` (let ((,tmp ,obj))
    885           (symbol-macrolet
    886               ,(loop for name in names collect
    887                      (typecase name
    888                        (symbol `(,name (,(reader name) ,tmp)))
    889                        (cons `(,(first name) (,(reader (second name)) ,tmp)))
    890                        (t (error "Malformed syntax in WITH-STRUCT: ~A" name))))
    891             ,@body)))))
    892 
    893 (defun categorize-definition-source (definition-source)
    894   (with-definition-source (pathname form-path character-offset plist)
    895                           definition-source
    896     (let ((file-p (and pathname (probe-file pathname)
    897                        (or form-path character-offset))))
    898       (cond ((and (getf plist :emacs-buffer) file-p) :buffer-and-file)
    899             ((getf plist :emacs-buffer) :buffer)
    900             (file-p :file)
    901             (pathname :file-without-position)
    902             (t :invalid)))))
    903 
    904 #+#.(slynk-backend:with-symbol 'definition-source-form-number 'sb-introspect)
    905 (defun form-number-position (definition-source stream)
    906   (let* ((tlf-number (car (sb-introspect:definition-source-form-path definition-source)))
    907          (form-number (sb-introspect:definition-source-form-number definition-source)))
    908     (multiple-value-bind (tlf pos-map) (read-source-form tlf-number stream)
    909       (let* ((path-table (sb-di::form-number-translations tlf 0))
    910              (path (cond ((<= (length path-table) form-number)
    911                           (warn "inconsistent form-number-translations")
    912                           (list 0))
    913                          (t
    914                           (reverse (cdr (aref path-table form-number)))))))
    915         (source-path-source-position path tlf pos-map)))))
    916 
    917 #+#.(slynk-backend:with-symbol 'definition-source-form-number 'sb-introspect)
    918 (defun file-form-number-position (definition-source)
    919   (let* ((code-date (sb-introspect:definition-source-file-write-date definition-source))
    920          (filename (sb-introspect:definition-source-pathname definition-source))
    921          (*readtable* (guess-readtable-for-filename filename))
    922          (source-code (get-source-code filename code-date)))
    923     (with-debootstrapping
    924       (with-input-from-string (s source-code)
    925         (form-number-position definition-source s)))))
    926 
    927 #+#.(slynk-backend:with-symbol 'definition-source-form-number 'sb-introspect)
    928 (defun string-form-number-position (definition-source string)
    929   (with-input-from-string (s string)
    930     (form-number-position definition-source s)))
    931 
    932 (defun definition-source-buffer-location (definition-source)
    933   (with-definition-source (form-path character-offset plist) definition-source
    934     (destructuring-bind (&key emacs-buffer emacs-position emacs-directory
    935                               emacs-string &allow-other-keys)
    936         plist
    937       (let ((*readtable* (guess-readtable-for-filename emacs-directory))
    938             start
    939             end)
    940         (with-debootstrapping
    941           (or
    942            (and form-path
    943                 (or
    944                  #+#.(slynk-backend:with-symbol 'definition-source-form-number 'sb-introspect)
    945                  (setf (values start end)
    946                        (and (sb-introspect:definition-source-form-number definition-source)
    947                             (string-form-number-position definition-source emacs-string)))
    948                  (setf (values start end)
    949                        (source-path-string-position form-path emacs-string))))
    950            (setf start character-offset
    951                  end most-positive-fixnum)))
    952         (make-location
    953          `(:buffer ,emacs-buffer)
    954          `(:offset ,emacs-position ,start)
    955          `(:snippet
    956            ,(subseq emacs-string
    957                     start
    958                     (min end (+ start *source-snippet-size*)))))))))
    959 
    960 (defun definition-source-file-location (definition-source)
    961   (with-definition-source (pathname form-path character-offset plist
    962                            file-write-date) definition-source
    963     (let* ((namestring (namestring (translate-logical-pathname pathname)))
    964            (pos (or (and form-path
    965                          (or
    966                           #+#.(slynk-backend:with-symbol 'definition-source-form-number 'sb-introspect)
    967                           (and (sb-introspect:definition-source-form-number definition-source)
    968                                (ignore-errors (file-form-number-position definition-source)))
    969                           (ignore-errors
    970                            (source-file-position namestring file-write-date
    971                                                  form-path))))
    972                     character-offset))
    973            (snippet (source-hint-snippet namestring file-write-date pos)))
    974       (make-location `(:file ,namestring)
    975                      ;; /file positions/ in Common Lisp start from
    976                      ;; 0, buffer positions in Emacs start from 1.
    977                      `(:position ,(1+ pos))
    978                      `(:snippet ,snippet)))))
    979 
    980 (defun definition-source-buffer-and-file-location (definition-source)
    981   (let ((buffer (definition-source-buffer-location definition-source))
    982         (file (definition-source-file-location definition-source)))
    983     (make-location (list :buffer-and-file
    984                          (cadr (location-buffer buffer))
    985                          (cadr (location-buffer file)))
    986                    (location-position buffer)
    987                    (location-hints buffer))))
    988 
    989 (defun definition-source-for-emacs (definition-source type name)
    990   (with-definition-source (pathname form-path character-offset plist
    991                                     file-write-date)
    992       definition-source
    993     (ecase (categorize-definition-source definition-source)
    994       (:buffer-and-file
    995        (definition-source-buffer-and-file-location definition-source))
    996       (:buffer
    997        (definition-source-buffer-location definition-source))
    998       (:file
    999        (definition-source-file-location definition-source))
   1000       (:file-without-position
   1001        (make-location `(:file ,(namestring
   1002                                 (translate-logical-pathname pathname)))
   1003                       '(:position 1)
   1004                       (when (eql type :function)
   1005                         `(:snippet ,(format nil "(defun ~a "
   1006                                             (symbol-name name))))))
   1007       (:invalid
   1008        (error "DEFINITION-SOURCE of ~(~A~) ~A did not contain ~
   1009                meaningful information."
   1010               type name)))))
   1011 
   1012 (defun source-file-position (filename write-date form-path)
   1013   (let ((source (get-source-code filename write-date))
   1014         (*readtable* (guess-readtable-for-filename filename)))
   1015     (with-debootstrapping
   1016       (source-path-string-position form-path source))))
   1017 
   1018 (defun source-hint-snippet (filename write-date position)
   1019   (read-snippet-from-string (get-source-code filename write-date) position))
   1020 
   1021 (defun function-source-location (function &optional name)
   1022   (declare (type function function))
   1023   (definition-source-for-emacs (sb-introspect:find-definition-source function)
   1024                                :function
   1025                                (or name (function-name function))))
   1026 
   1027 (defun setf-expander (symbol)
   1028   (or
   1029    #+#.(slynk-sbcl::sbcl-with-setf-inverse-meta-info)
   1030    (sb-int:info :setf :inverse symbol)
   1031    (sb-int:info :setf :expander symbol)))
   1032 
   1033 (defimplementation describe-symbol-for-emacs (symbol)
   1034   "Return a plist describing SYMBOL.
   1035 Return NIL if the symbol is unbound."
   1036   (let ((result '()))
   1037     (flet ((doc (kind)
   1038              (or (documentation symbol kind) :not-documented))
   1039            (maybe-push (property value)
   1040              (when value
   1041                (setf result (list* property value result)))))
   1042       (maybe-push
   1043        :variable (multiple-value-bind (kind recorded-p)
   1044                      (sb-int:info :variable :kind symbol)
   1045                    (declare (ignore kind))
   1046                    (if (or (boundp symbol) recorded-p)
   1047                        (doc 'variable))))
   1048       (when (fboundp symbol)
   1049 	(maybe-push
   1050          (cond ((macro-function symbol)     :macro)
   1051                ((special-operator-p symbol) :special-operator)
   1052                ((typep (fdefinition symbol) 'generic-function)
   1053                 :generic-function)
   1054                (t :function))
   1055          (doc 'function)))
   1056       (maybe-push
   1057        :setf (and (setf-expander symbol)
   1058                   (doc 'setf)))
   1059       (maybe-push
   1060        :type (if (sb-int:info :type :kind symbol)
   1061                  (doc 'type)))
   1062       result)))
   1063 
   1064 (defimplementation describe-definition (symbol type)
   1065   (case type
   1066     (:variable
   1067      (describe symbol))
   1068     (:function
   1069      (describe (symbol-function symbol)))
   1070     (:setf
   1071      (describe (setf-expander symbol)))
   1072     (:class
   1073      (describe (find-class symbol)))
   1074     (:type
   1075      (describe (sb-kernel:values-specifier-type symbol)))))
   1076   
   1077 #+#.(slynk-sbcl::sbcl-with-xref-p)
   1078 (progn
   1079   (defmacro defxref (name &optional fn-name)
   1080     `(defimplementation ,name (what)
   1081        (sanitize-xrefs
   1082         (mapcar #'source-location-for-xref-data
   1083                 (,(find-symbol (symbol-name (if fn-name
   1084                                                 fn-name
   1085                                                 name))
   1086                                "SB-INTROSPECT")
   1087                   what)))))
   1088   (defxref who-calls)
   1089   (defxref who-binds)
   1090   (defxref who-sets)
   1091   (defxref who-references)
   1092   (defxref who-macroexpands)
   1093   #+#.(slynk-backend:with-symbol 'who-specializes-directly 'sb-introspect)
   1094   (defxref who-specializes who-specializes-directly))
   1095 
   1096 (defun source-location-for-xref-data (xref-data)
   1097   (destructuring-bind (name . defsrc) xref-data
   1098     (list name (converting-errors-to-error-location
   1099                  (definition-source-for-emacs defsrc 'function name)))))
   1100 
   1101 (defimplementation list-callers (symbol)
   1102   (let ((fn (fdefinition symbol)))
   1103     (sanitize-xrefs
   1104      (mapcar #'function-dspec (sb-introspect:find-function-callers fn)))))
   1105 
   1106 (defimplementation list-callees (symbol)
   1107   (let ((fn (fdefinition symbol)))
   1108     (sanitize-xrefs
   1109      (mapcar #'function-dspec (sb-introspect:find-function-callees fn)))))
   1110 
   1111 (defun sanitize-xrefs (xrefs)
   1112   (remove-duplicates
   1113    (remove-if (lambda (f)
   1114                 (member f (ignored-xref-function-names)))
   1115               (loop for entry in xrefs
   1116                     for name = (car entry)
   1117                     collect (if (and (consp name)
   1118                                      (member (car name)
   1119                                              '(sb-pcl::fast-method
   1120                                                sb-pcl::slow-method
   1121                                                sb-pcl::method)))
   1122                                 (cons (cons 'defmethod (cdr name))
   1123                                       (cdr entry))
   1124                                 entry))
   1125               :key #'car)
   1126    :test (lambda (a b)
   1127            (and (eq (first a) (first b))
   1128                 (equal (second a) (second b))))))
   1129 
   1130 (defun ignored-xref-function-names ()
   1131   #-#.(slynk-sbcl::sbcl-with-new-stepper-p)
   1132   '(nil sb-c::step-form sb-c::step-values)
   1133   #+#.(slynk-sbcl::sbcl-with-new-stepper-p)
   1134   '(nil))
   1135 
   1136 (defun function-dspec (fn)
   1137   "Describe where the function FN was defined.
   1138 Return a list of the form (NAME LOCATION)."
   1139   (let ((name (function-name fn)))
   1140     (list name (converting-errors-to-error-location
   1141                  (function-source-location fn name)))))
   1142 
   1143 ;;; macroexpansion
   1144 
   1145 (defimplementation macroexpand-all (form &optional env)
   1146   (sb-cltl2:macroexpand-all form env))
   1147 
   1148 
   1149 ;;; Debugging
   1150 
   1151 ;;; Notice that SB-EXT:*INVOKE-DEBUGGER-HOOK* is slightly stronger
   1152 ;;; than just a hook into BREAK. In particular, it'll make
   1153 ;;; (LET ((*DEBUGGER-HOOK* NIL)) ..error..) drop into SLY-DB rather
   1154 ;;; than the native debugger. That should probably be considered a
   1155 ;;; feature.
   1156 
   1157 (defun make-invoke-debugger-hook (hook)
   1158   (when hook
   1159     #'(sb-int:named-lambda slynk-invoke-debugger-hook
   1160           (condition old-hook)
   1161         (if *debugger-hook*
   1162             nil         ; decline, *DEBUGGER-HOOK* will be tried next.
   1163             (funcall hook condition old-hook)))))
   1164 
   1165 (defun set-break-hook (hook)
   1166   (setq sb-ext:*invoke-debugger-hook* (make-invoke-debugger-hook hook)))
   1167 
   1168 (defun call-with-break-hook (hook continuation)
   1169   (let ((sb-ext:*invoke-debugger-hook* (make-invoke-debugger-hook hook)))
   1170     (funcall continuation)))
   1171 
   1172 (defimplementation install-debugger-globally (function)
   1173   (setq *debugger-hook* function)
   1174   (set-break-hook function))
   1175 
   1176 (defimplementation condition-extras (condition)
   1177   (cond #+#.(slynk-sbcl::sbcl-with-new-stepper-p)
   1178         ((typep condition 'sb-impl::step-form-condition)
   1179          `((:show-frame-source 0)))
   1180         ((typep condition 'sb-int:reference-condition)
   1181          (let ((refs (sb-int:reference-condition-references condition)))
   1182            (if refs
   1183                `((:references ,(externalize-reference refs))))))))
   1184 
   1185 (defun externalize-reference (ref)
   1186   (etypecase ref
   1187     (null nil)
   1188     (cons (cons (externalize-reference (car ref))
   1189                 (externalize-reference (cdr ref))))
   1190     ((or string number) ref)
   1191     (symbol
   1192      (cond ((eq (symbol-package ref) (symbol-package :test))
   1193             ref)
   1194            (t (symbol-name ref))))))
   1195 
   1196 (defvar *sly-db-stack-top*)
   1197 
   1198 (defimplementation call-with-debugging-environment (debugger-loop-fn)
   1199   (declare (type function debugger-loop-fn))
   1200   (let ((*sly-db-stack-top*
   1201           (if (and (not *debug-slynk-backend*)
   1202                    sb-debug:*stack-top-hint*)
   1203               #+#.(slynk-backend:with-symbol 'resolve-stack-top-hint 'sb-debug)
   1204               (sb-debug::resolve-stack-top-hint)
   1205               #-#.(slynk-backend:with-symbol 'resolve-stack-top-hint 'sb-debug)
   1206               sb-debug:*stack-top-hint*
   1207               (sb-di:top-frame)))
   1208         (sb-debug:*stack-top-hint* nil))
   1209     (handler-bind ((sb-di:debug-condition
   1210                      (lambda (condition)
   1211                        (signal 'sly-db-condition
   1212                                :original-condition condition))))
   1213       (funcall debugger-loop-fn))))
   1214 
   1215 #+#.(slynk-sbcl::sbcl-with-new-stepper-p)
   1216 (progn
   1217   (defimplementation activate-stepping (frame)
   1218     (declare (ignore frame))
   1219     (sb-impl::enable-stepping))
   1220   (defimplementation sly-db-stepper-condition-p (condition)
   1221     (typep condition 'sb-ext:step-form-condition))
   1222   (defimplementation sly-db-step-into ()
   1223     (invoke-restart 'sb-ext:step-into))
   1224   (defimplementation sly-db-step-next ()
   1225     (invoke-restart 'sb-ext:step-next))
   1226   (defimplementation sly-db-step-out ()
   1227     (invoke-restart 'sb-ext:step-out)))
   1228 
   1229 (defimplementation call-with-debugger-hook (hook fun)
   1230   (let ((*debugger-hook* hook)
   1231         #+#.(slynk-sbcl::sbcl-with-new-stepper-p)
   1232         (sb-ext:*stepper-hook*
   1233          (lambda (condition)
   1234            (typecase condition
   1235              (sb-ext:step-form-condition
   1236               (let ((sb-debug:*stack-top-hint* (sb-di::find-stepped-frame)))
   1237                 (sb-impl::invoke-debugger condition)))))))
   1238     (handler-bind (#+#.(slynk-sbcl::sbcl-with-new-stepper-p)
   1239                    (sb-ext:step-condition #'sb-impl::invoke-stepper))
   1240       (call-with-break-hook hook fun))))
   1241 
   1242 (defun nth-frame (index)
   1243   (do ((frame *sly-db-stack-top* (sb-di:frame-down frame))
   1244        (i index (1- i)))
   1245       ((zerop i) frame)))
   1246 
   1247 (defimplementation compute-backtrace (start end)
   1248   "Return a list of frames starting with frame number START and
   1249 continuing to frame number END or, if END is nil, the last frame on the
   1250 stack."
   1251   (let ((end (or end most-positive-fixnum)))
   1252     (loop for f = (nth-frame start) then (sb-di:frame-down f)
   1253           for i from start below end
   1254           while f collect f)))
   1255 
   1256 (defimplementation print-frame (frame stream)
   1257   (sb-debug::print-frame-call frame stream
   1258                               :allow-other-keys t
   1259                               :emergency-best-effort t))
   1260 
   1261 (defimplementation frame-restartable-p (frame)
   1262   #+#.(slynk-sbcl::sbcl-with-restart-frame)
   1263   (not (null (sb-debug:frame-has-debug-tag-p frame))))
   1264 
   1265 (defimplementation frame-arguments (frame)
   1266   (multiple-value-bind (name args)
   1267       (sb-debug::frame-call (nth-frame frame))
   1268     (declare (ignore name))
   1269     (values-list args)))
   1270 
   1271 ;;;; Code-location -> source-location translation
   1272 
   1273 ;;; If debug-block info is avaibale, we determine the file position of
   1274 ;;; the source-path for a code-location.  If the code was compiled
   1275 ;;; with C-c C-c, we have to search the position in the source string.
   1276 ;;; If there's no debug-block info, we return the (less precise)
   1277 ;;; source-location of the corresponding function.
   1278 
   1279 (defun code-location-source-location (code-location)
   1280   (let* ((dsource (sb-di:code-location-debug-source code-location))
   1281          (plist (sb-c::debug-source-plist dsource))
   1282          (package (getf plist :emacs-package))
   1283          (*package* (or (and package
   1284                              (find-package package))
   1285                         *package*)))
   1286     (if (getf plist :emacs-buffer)
   1287         (emacs-buffer-source-location code-location plist)
   1288         #+#.(slynk-backend:with-symbol 'debug-source-from 'sb-di)
   1289         (ecase (sb-di:debug-source-from dsource)
   1290           (:file (file-source-location code-location))
   1291           (:lisp (lisp-source-location code-location)))
   1292         #-#.(slynk-backend:with-symbol 'debug-source-from 'sb-di)
   1293         (if (sb-di:debug-source-namestring dsource)
   1294             (file-source-location code-location)
   1295             (lisp-source-location code-location)))))
   1296 
   1297 ;;; FIXME: The naming policy of source-location functions is a bit
   1298 ;;; fuzzy: we have FUNCTION-SOURCE-LOCATION which returns the
   1299 ;;; source-location for a function, and we also have FILE-SOURCE-LOCATION &co
   1300 ;;; which returns the source location for a _code-location_.
   1301 ;;;
   1302 ;;; Maybe these should be named code-location-file-source-location,
   1303 ;;; etc, turned into generic functions, or something. In the very
   1304 ;;; least the names should indicate the main entry point vs. helper
   1305 ;;; status.
   1306 
   1307 (defun file-source-location (code-location)
   1308   (if (code-location-has-debug-block-info-p code-location)
   1309       (source-file-source-location code-location)
   1310       (fallback-source-location code-location)))
   1311 
   1312 (defun fallback-source-location (code-location)
   1313   (let ((fun (code-location-debug-fun-fun code-location)))
   1314     (cond (fun (function-source-location fun))
   1315           (t (error "Cannot find source location for: ~A " code-location)))))
   1316 
   1317 (defun lisp-source-location (code-location)
   1318   (let ((source (prin1-to-string
   1319                  (sb-debug::code-location-source-form code-location 100)))
   1320         (condition (slynk-value '*slynk-debugger-condition*)))
   1321     (if (and (typep condition 'sb-impl::step-form-condition)
   1322              (search "SB-IMPL::WITH-STEPPING-ENABLED" source
   1323                      :test #'char-equal)
   1324              (search "SB-IMPL::STEP-FINISHED" source :test #'char-equal))
   1325         ;; The initial form is utterly uninteresting -- and almost
   1326         ;; certainly right there in the REPL.
   1327         (make-error-location "Stepping...")
   1328         (make-location `(:source-form ,source) '(:position 1)))))
   1329 
   1330 (defun emacs-buffer-source-location (code-location plist)
   1331   (if (code-location-has-debug-block-info-p code-location)
   1332       (destructuring-bind (&key emacs-buffer emacs-position emacs-string
   1333                                 &allow-other-keys)
   1334           plist
   1335         (let* ((pos (string-source-position code-location emacs-string))
   1336                (snipped (read-snippet-from-string emacs-string pos)))
   1337           (make-location `(:buffer ,emacs-buffer)
   1338                          `(:offset ,emacs-position ,pos)
   1339                          `(:snippet ,snipped))))
   1340       (fallback-source-location code-location)))
   1341 
   1342 (defun source-file-source-location (code-location)
   1343   (let* ((code-date (code-location-debug-source-created code-location))
   1344          (filename (code-location-debug-source-name code-location))
   1345          (*readtable* (guess-readtable-for-filename filename))
   1346          (source-code (get-source-code filename code-date)))
   1347     (with-debootstrapping
   1348       (with-input-from-string (s source-code)
   1349         (let* ((pos (stream-source-position code-location s))
   1350                (snippet (read-snippet s pos)))
   1351           (make-location `(:file ,filename)
   1352                          `(:position ,pos)
   1353                          `(:snippet ,snippet)))))))
   1354 
   1355 (defun code-location-debug-source-name (code-location)
   1356   (namestring (truename (#.(slynk-backend:choose-symbol
   1357                             'sb-c 'debug-source-name
   1358                             'sb-c 'debug-source-namestring)
   1359                            (sb-di::code-location-debug-source code-location)))))
   1360 
   1361 (defun code-location-debug-source-created (code-location)
   1362   (sb-c::debug-source-created
   1363    (sb-di::code-location-debug-source code-location)))
   1364 
   1365 (defun code-location-debug-fun-fun (code-location)
   1366   (sb-di:debug-fun-fun (sb-di:code-location-debug-fun code-location)))
   1367 
   1368 (defun code-location-has-debug-block-info-p (code-location)
   1369   (handler-case
   1370       (progn (sb-di:code-location-debug-block code-location)
   1371              t)
   1372     (sb-di:no-debug-blocks  () nil)))
   1373 
   1374 (defun stream-source-position (code-location stream)
   1375   (let* ((cloc (sb-debug::maybe-block-start-location code-location))
   1376          (tlf-number (sb-di::code-location-toplevel-form-offset cloc))
   1377          (form-number (sb-di::code-location-form-number cloc)))
   1378     (multiple-value-bind (tlf pos-map) (read-source-form tlf-number stream)
   1379       (let* ((path-table (sb-di::form-number-translations tlf 0))
   1380              (path (cond ((<= (length path-table) form-number)
   1381                           (warn "inconsistent form-number-translations")
   1382                           (list 0))
   1383                          (t
   1384                           (reverse (cdr (aref path-table form-number)))))))
   1385         (source-path-source-position path tlf pos-map)))))
   1386 
   1387 (defun string-source-position (code-location string)
   1388   (with-input-from-string (s string)
   1389     (stream-source-position code-location s)))
   1390 
   1391 ;;; source-path-file-position and friends are in slynk-source-path-parser
   1392 
   1393 (defimplementation frame-source-location (index)
   1394   (converting-errors-to-error-location
   1395     (code-location-source-location
   1396      (sb-di:frame-code-location (nth-frame index)))))
   1397 
   1398 (defvar *keep-non-valid-locals* nil)
   1399 
   1400 (defun frame-debug-vars (frame)
   1401   "Return a vector of debug-variables in frame."
   1402   (let* ((all-vars (sb-di::debug-fun-debug-vars (sb-di:frame-debug-fun frame)))
   1403          (loc (sb-di:frame-code-location frame))
   1404          (vars (if *keep-non-valid-locals*
   1405                    all-vars
   1406                    (remove-if (lambda (var)
   1407                                 (ecase (sb-di:debug-var-validity var loc)
   1408                                   (:valid nil)
   1409                                   ((:invalid :unknown) t)))
   1410                               all-vars)))
   1411          more-context
   1412          more-count)
   1413     (values (when vars
   1414               (loop for v across vars
   1415                     unless
   1416                     (case (debug-var-info v)
   1417                       (:more-context
   1418                        (setf more-context (debug-var-value v frame loc))
   1419                        t)
   1420                       (:more-count
   1421                        (setf more-count (debug-var-value v frame loc))
   1422                        t))
   1423                     collect v))
   1424             more-context more-count)))
   1425 
   1426 (defun debug-var-value (var frame location)
   1427   (ecase (sb-di:debug-var-validity var location)
   1428     (:valid (sb-di:debug-var-value var frame))
   1429     ((:invalid :unknown) ':<not-available>)))
   1430 
   1431 (defun debug-var-info (var)
   1432   ;; Introduced by SBCL 1.0.49.76.
   1433   (let ((s (find-symbol "DEBUG-VAR-INFO" :sb-di)))
   1434     (when (and s (fboundp s))
   1435       (funcall s var))))
   1436 
   1437 (defimplementation frame-locals (index)
   1438   (let* ((frame (nth-frame index))
   1439          (loc (sb-di:frame-code-location frame)))
   1440     (multiple-value-bind (vars more-context more-count)
   1441         (frame-debug-vars frame)
   1442       (let ((locals
   1443               (loop for v in vars
   1444                     collect
   1445                     (list :name (sb-di:debug-var-symbol v)
   1446                           :id (sb-di:debug-var-id v)
   1447                           :value (debug-var-value v frame loc)))))
   1448         (if (and more-context more-count)
   1449             (append locals
   1450                     (list
   1451                      (list :name
   1452                            ;; Since SBCL 1.0.49.76 PREPROCESS-FOR-EVAL understands SB-DEBUG::MORE
   1453                            ;; specially.
   1454                            (or (find-symbol "MORE" :sb-debug) 'more)
   1455                            :id 0
   1456                            :value (multiple-value-list
   1457                                    (sb-c:%more-arg-values
   1458                                     more-context
   1459                                     0 more-count)))))
   1460             locals)))))
   1461 
   1462 (defimplementation frame-var-value (frame var)
   1463   (let ((frame (nth-frame frame)))
   1464     (multiple-value-bind (vars more-context more-count)
   1465         (frame-debug-vars frame)
   1466       (let* ((loc (sb-di:frame-code-location frame))
   1467              (dvar (if (= var (length vars))
   1468                        ;; If VAR is out of bounds, it must be the fake var
   1469                        ;; we made up for &MORE.
   1470                        (return-from frame-var-value
   1471                          (multiple-value-list (sb-c:%more-arg-values
   1472                                                more-context
   1473                                                0 more-count)))
   1474                        (nth var vars))))
   1475         (debug-var-value dvar frame loc)))))
   1476 
   1477 (defimplementation frame-catch-tags (index)
   1478   (mapcar #'car (sb-di:frame-catches (nth-frame index))))
   1479 
   1480 (defimplementation eval-in-frame (form index)
   1481   (let ((frame (nth-frame index)))
   1482     (funcall (the function
   1483                (sb-di:preprocess-for-eval form
   1484                                           (sb-di:frame-code-location frame)))
   1485              frame)))
   1486 
   1487 (defimplementation frame-package (frame-number)
   1488   (let* ((frame (nth-frame frame-number))
   1489          (fun (sb-di:debug-fun-fun (sb-di:frame-debug-fun frame))))
   1490     (when fun
   1491       (let ((name (function-name fun)))
   1492         (typecase name
   1493           (null nil)
   1494           (symbol (symbol-package name))
   1495           ((cons (eql setf) (cons symbol)) (symbol-package (cadr name))))))))
   1496 
   1497 #+#.(slynk-sbcl::sbcl-with-restart-frame)
   1498 (progn
   1499   (defimplementation return-from-frame (index form)
   1500     (let* ((frame (nth-frame index)))
   1501       (cond ((sb-debug:frame-has-debug-tag-p frame)
   1502              (let ((values (multiple-value-list (eval-in-frame form index))))
   1503                (sb-debug:unwind-to-frame-and-call frame
   1504                                                    (lambda ()
   1505                                                      (values-list values)))))
   1506             (t (format nil "Cannot return from frame: ~S" frame)))))
   1507 
   1508   (defimplementation restart-frame (index)
   1509     (let ((frame (nth-frame index)))
   1510       (when (sb-debug:frame-has-debug-tag-p frame)
   1511         (multiple-value-bind (fname args) (sb-debug::frame-call frame)
   1512           (multiple-value-bind (fun arglist)
   1513               (if (and (sb-int:legal-fun-name-p fname) (fboundp fname))
   1514                   (values (fdefinition fname) args)
   1515                   (values (sb-di:debug-fun-fun (sb-di:frame-debug-fun frame))
   1516                           (sb-debug::frame-args-as-list frame)))
   1517             (when (functionp fun)
   1518               (sb-debug:unwind-to-frame-and-call
   1519                frame
   1520                (lambda ()
   1521                  ;; Ensure TCO.
   1522                  (declare (optimize (debug 0)))
   1523                  (apply fun arglist)))))))
   1524       (format nil "Cannot restart frame: ~S" frame))))
   1525 
   1526 ;; FIXME: this implementation doesn't unwind the stack before
   1527 ;; re-invoking the function, but it's better than no implementation at
   1528 ;; all.
   1529 #-#.(slynk-sbcl::sbcl-with-restart-frame)
   1530 (progn
   1531   (defun sb-debug-catch-tag-p (tag)
   1532     (and (symbolp tag)
   1533          (not (symbol-package tag))
   1534          (string= tag :sb-debug-catch-tag)))
   1535 
   1536   (defimplementation return-from-frame (index form)
   1537     (let* ((frame (nth-frame index))
   1538            (probe (assoc-if #'sb-debug-catch-tag-p
   1539                             (sb-di::frame-catches frame))))
   1540       (cond (probe (throw (car probe) (eval-in-frame form index)))
   1541             (t (format nil "Cannot return from frame: ~S" frame)))))
   1542 
   1543   (defimplementation restart-frame (index)
   1544     (let ((frame (nth-frame index)))
   1545       (return-from-frame index (sb-debug::frame-call-as-list frame)))))
   1546 
   1547 ;;;;; reference-conditions
   1548 
   1549 (defimplementation print-condition (condition stream)
   1550   (let ((sb-int:*print-condition-references* nil))
   1551     (princ condition stream)))
   1552 
   1553 
   1554 ;;;; Profiling
   1555 
   1556 (defimplementation profile (fname)
   1557   (when fname (eval `(sb-profile:profile ,fname))))
   1558 
   1559 (defimplementation unprofile (fname)
   1560   (when fname (eval `(sb-profile:unprofile ,fname))))
   1561 
   1562 (defimplementation unprofile-all ()
   1563   (sb-profile:unprofile)
   1564   "All functions unprofiled.")
   1565 
   1566 (defimplementation profile-report ()
   1567   (sb-profile:report))
   1568 
   1569 (defimplementation profile-reset ()
   1570   (sb-profile:reset)
   1571   "Reset profiling counters.")
   1572 
   1573 (defimplementation profiled-functions ()
   1574   (sb-profile:profile))
   1575 
   1576 (defimplementation profile-package (package callers methods)
   1577   (declare (ignore callers methods))
   1578   (eval `(sb-profile:profile ,(package-name (find-package package)))))
   1579 
   1580 
   1581 ;;;; Inspector
   1582 
   1583 (defmethod emacs-inspect ((o t))
   1584   (cond ((sb-di::indirect-value-cell-p o)
   1585          (label-value-line* (:value (sb-kernel:value-cell-ref o))))
   1586 	(t
   1587          (multiple-value-bind (text label parts) (sb-impl::inspected-parts o)
   1588            (list* (string-right-trim '(#\Newline) text)
   1589                   '(:newline)
   1590                   (if label
   1591                       (loop for (l . v) in parts
   1592                             append (label-value-line l v))
   1593                       (loop for value in parts
   1594                             for i from 0
   1595                             append (label-value-line i value))))))))
   1596 
   1597 (defmethod emacs-inspect ((o function))
   1598     (cond ((sb-kernel:simple-fun-p o)
   1599                    (label-value-line*
   1600                     (:name (sb-kernel:%simple-fun-name o))
   1601                     (:arglist (sb-kernel:%simple-fun-arglist o))
   1602                     (:type (sb-kernel:%simple-fun-type o))
   1603                     (:code (sb-kernel:fun-code-header o))
   1604                     (:documentation (documentation o t))))
   1605           ((sb-kernel:closurep o)
   1606                    (append
   1607                     (label-value-line :function (sb-kernel:%closure-fun o))
   1608                     `("Closed over values:" (:newline))
   1609                     (loop for i below (1- (sb-kernel:get-closure-length o))
   1610                           append (label-value-line
   1611                                   i (sb-kernel:%closure-index-ref o i)))))
   1612           (t (call-next-method o))))
   1613 
   1614 (defmethod emacs-inspect ((o sb-kernel:code-component))
   1615   (append
   1616    (label-value-line*
   1617     (:code-size (sb-kernel:%code-code-size o))
   1618     (:debug-info (sb-kernel:%code-debug-info o)))
   1619    `("Constants:" (:newline))
   1620    (loop for i from sb-vm:code-constants-offset
   1621          below
   1622          (#.(slynk-backend:choose-symbol 'sb-kernel 'code-header-words
   1623                                          'sb-kernel 'get-header-data)
   1624             o)
   1625          append (label-value-line i (sb-kernel:code-header-ref o i)))
   1626    `("Code:" (:newline)
   1627              ,(with-output-to-string (s)
   1628                 (sb-disassem:disassemble-code-component o :stream s)))))
   1629 
   1630 (defmethod emacs-inspect ((o sb-ext:weak-pointer))
   1631           (label-value-line*
   1632            (:value (sb-ext:weak-pointer-value o))))
   1633 
   1634 (defmethod emacs-inspect ((o sb-kernel:fdefn))
   1635           (label-value-line*
   1636            (:name (sb-kernel:fdefn-name o))
   1637            (:function (sb-kernel:fdefn-fun o))))
   1638 
   1639 (defmethod emacs-inspect :around ((o generic-function))
   1640             (append
   1641              (call-next-method)
   1642              (label-value-line*
   1643               (:pretty-arglist (sb-pcl::generic-function-pretty-arglist o))
   1644               (:initial-methods (sb-pcl::generic-function-initial-methods o))
   1645               )))
   1646 
   1647 
   1648 ;;;; Multiprocessing
   1649 
   1650 #+(and sb-thread
   1651        #.(slynk-backend:with-symbol "THREAD-NAME" "SB-THREAD"))
   1652 (progn
   1653   (defvar *thread-id-counter* 0)
   1654 
   1655   (defvar *thread-id-counter-lock*
   1656     (sb-thread:make-mutex :name "thread id counter lock"))
   1657 
   1658   (defun next-thread-id ()
   1659     (sb-thread:with-mutex (*thread-id-counter-lock*)
   1660       (incf *thread-id-counter*)))
   1661 
   1662   (defvar *thread-id-map* (make-hash-table))
   1663 
   1664   ;; This should be a thread -> id map but as weak keys are not
   1665   ;; supported it is id -> map instead.
   1666   (defvar *thread-id-map-lock*
   1667     (sb-thread:make-mutex :name "thread id map lock"))
   1668 
   1669   (defimplementation spawn (fn &key name)
   1670     (sb-thread:make-thread fn :name name))
   1671 
   1672   (defimplementation thread-id (thread)
   1673     (block thread-id
   1674       (sb-thread:with-mutex (*thread-id-map-lock*)
   1675         (loop for id being the hash-key in *thread-id-map*
   1676               using (hash-value thread-pointer)
   1677               do
   1678               (let ((maybe-thread (sb-ext:weak-pointer-value thread-pointer)))
   1679                 (cond ((null maybe-thread)
   1680                        ;; the value is gc'd, remove it manually
   1681                        (remhash id *thread-id-map*))
   1682                       ((eq thread maybe-thread)
   1683                        (return-from thread-id id)))))
   1684         ;; lazy numbering
   1685         (let ((id (next-thread-id)))
   1686           (setf (gethash id *thread-id-map*) (sb-ext:make-weak-pointer thread))
   1687           id))))
   1688 
   1689   (defimplementation find-thread (id)
   1690     (sb-thread:with-mutex (*thread-id-map-lock*)
   1691       (let ((thread-pointer (gethash id *thread-id-map*)))
   1692         (if thread-pointer
   1693             (let ((maybe-thread (sb-ext:weak-pointer-value thread-pointer)))
   1694               (if maybe-thread
   1695                   maybe-thread
   1696                   ;; the value is gc'd, remove it manually
   1697                   (progn
   1698                     (remhash id *thread-id-map*)
   1699                     nil)))
   1700             nil))))
   1701 
   1702   (defimplementation thread-name (thread)
   1703     ;; sometimes the name is not a string (e.g. NIL)
   1704     (princ-to-string (sb-thread:thread-name thread)))
   1705 
   1706   (defimplementation thread-status (thread)
   1707     (if (sb-thread:thread-alive-p thread)
   1708         "Running"
   1709         "Stopped"))
   1710 
   1711   (defimplementation make-lock (&key name)
   1712     (sb-thread:make-mutex :name name))
   1713 
   1714   (defimplementation call-with-lock-held (lock function)
   1715     (declare (type function function))
   1716     (sb-thread:with-recursive-lock (lock) (funcall function)))
   1717 
   1718   (defimplementation current-thread ()
   1719     sb-thread:*current-thread*)
   1720 
   1721   (defimplementation all-threads ()
   1722     (sb-thread:list-all-threads))
   1723 
   1724   (defimplementation interrupt-thread (thread fn)
   1725     (sb-thread:interrupt-thread thread fn))
   1726 
   1727   (defimplementation kill-thread (thread)
   1728     (sb-thread:terminate-thread thread))
   1729 
   1730   (defimplementation thread-alive-p (thread)
   1731     (sb-thread:thread-alive-p thread))
   1732 
   1733   (defvar *mailbox-lock* (sb-thread:make-mutex :name "mailbox lock"))
   1734   (defvar *mailboxes* (list))
   1735   (declaim (type list *mailboxes*))
   1736 
   1737   (defstruct (mailbox (:conc-name mailbox.))
   1738     thread
   1739     (mutex (sb-thread:make-mutex))
   1740     (waitqueue  (sb-thread:make-waitqueue))
   1741     (queue '() :type list))
   1742 
   1743   (defun mailbox (thread)
   1744     "Return THREAD's mailbox."
   1745     (sb-thread:with-mutex (*mailbox-lock*)
   1746       (or (find thread *mailboxes* :key #'mailbox.thread)
   1747           (let ((mb (make-mailbox :thread thread)))
   1748             (push mb *mailboxes*)
   1749             mb))))
   1750 
   1751   (defimplementation wake-thread (thread)
   1752     (let* ((mbox (mailbox thread))
   1753            (mutex (mailbox.mutex mbox)))
   1754       (sb-thread:with-recursive-lock (mutex)
   1755         (sb-thread:condition-broadcast (mailbox.waitqueue mbox)))))
   1756 
   1757   (defimplementation send (thread message)
   1758     (let* ((mbox (mailbox thread))
   1759            (mutex (mailbox.mutex mbox)))
   1760       (sb-thread:with-mutex (mutex)
   1761         (setf (mailbox.queue mbox)
   1762               (nconc (mailbox.queue mbox) (list message)))
   1763         (sb-thread:condition-broadcast (mailbox.waitqueue mbox)))))
   1764 
   1765   (defimplementation receive-if (test &optional timeout)
   1766     (let* ((mbox (mailbox (current-thread)))
   1767            (mutex (mailbox.mutex mbox))
   1768            (waitq (mailbox.waitqueue mbox)))
   1769       (assert (or (not timeout) (eq timeout t)))
   1770       (loop
   1771        (check-sly-interrupts)
   1772        (sb-thread:with-mutex (mutex)
   1773          (let* ((q (mailbox.queue mbox))
   1774                 (tail (member-if test q)))
   1775            (when tail
   1776              (setf (mailbox.queue mbox) (nconc (ldiff q tail) (cdr tail)))
   1777              (return (car tail))))
   1778          (when (eq timeout t) (return (values nil t)))
   1779          (sb-thread:condition-wait waitq mutex)))))
   1780 
   1781   (let ((alist '())
   1782         (mutex (sb-thread:make-mutex :name "register-thread")))
   1783 
   1784     (defimplementation register-thread (name thread)
   1785       (declare (type symbol name))
   1786       (sb-thread:with-mutex (mutex)
   1787         (etypecase thread
   1788           (null
   1789            (setf alist (delete name alist :key #'car)))
   1790           (sb-thread:thread
   1791            (let ((probe (assoc name alist)))
   1792              (cond (probe (setf (cdr probe) thread))
   1793                    (t (setf alist (acons name thread alist))))))))
   1794       nil)
   1795 
   1796     (defimplementation find-registered (name)
   1797       (sb-thread:with-mutex (mutex)
   1798         (cdr (assoc name alist))))))
   1799 
   1800 (defimplementation quit-lisp ()
   1801   #+#.(slynk-backend:with-symbol 'exit 'sb-ext)
   1802   (sb-ext:exit)
   1803   #-#.(slynk-backend:with-symbol 'exit 'sb-ext)
   1804   (progn
   1805     #+sb-thread
   1806     (dolist (thread (remove (current-thread) (all-threads)))
   1807       (ignore-errors (sb-thread:terminate-thread thread)))
   1808     (sb-ext:quit)))
   1809 
   1810 
   1811 
   1812 ;;Trace implementations
   1813 ;;In SBCL, we have:
   1814 ;; (trace <name>)
   1815 ;; (trace :methods '<name>) ;to trace all methods of the gf <name>
   1816 ;; (trace (method <name> <qualifier>? (<specializer>+)))
   1817 ;; <name> can be a normal name or a (setf name)
   1818 
   1819 (defun toggle-trace-aux (fspec &rest args)
   1820   (cond ((member fspec (eval '(trace)) :test #'equal)
   1821          (eval `(untrace ,fspec))
   1822          (format nil "~S is now untraced." fspec))
   1823         (t
   1824          (eval `(trace ,@(if args `(:encapsulate nil) (list)) ,fspec ,@args))
   1825          (format nil "~S is now traced." fspec))))
   1826 
   1827 (defun process-fspec (fspec)
   1828   (cond ((consp fspec)
   1829          (ecase (first fspec)
   1830            ((:defun :defgeneric) (second fspec))
   1831            ((:defmethod) `(method ,@(rest fspec)))
   1832            ((:labels) `(labels ,(process-fspec (second fspec)) ,(third fspec)))
   1833            ((:flet) `(flet ,(process-fspec (second fspec)) ,(third fspec)))))
   1834         (t
   1835          fspec)))
   1836 
   1837 (defimplementation toggle-trace (spec)
   1838   (ecase (car spec)
   1839     ((setf)
   1840      (toggle-trace-aux spec))
   1841     ((:defmethod)
   1842      (toggle-trace-aux `(sb-pcl::fast-method ,@(rest (process-fspec spec)))))
   1843     ((:defgeneric)
   1844      (toggle-trace-aux (second spec) :methods t))
   1845     ((:call)
   1846      (destructuring-bind (caller callee) (cdr spec)
   1847        (toggle-trace-aux callee :wherein (list (process-fspec caller)))))))
   1848 
   1849 ;;; Weak datastructures
   1850 
   1851 (defimplementation make-weak-key-hash-table (&rest args)  
   1852   #+#.(slynk-sbcl::sbcl-with-weak-hash-tables)
   1853   (apply #'make-hash-table :weakness :key args)
   1854   #-#.(slynk-sbcl::sbcl-with-weak-hash-tables)
   1855   (apply #'make-hash-table args))
   1856 
   1857 (defimplementation make-weak-value-hash-table (&rest args)
   1858   #+#.(slynk-sbcl::sbcl-with-weak-hash-tables)
   1859   (apply #'make-hash-table :weakness :value args)
   1860   #-#.(slynk-sbcl::sbcl-with-weak-hash-tables)
   1861   (apply #'make-hash-table args))
   1862 
   1863 (defimplementation hash-table-weakness (hashtable)
   1864   #+#.(slynk-sbcl::sbcl-with-weak-hash-tables)
   1865   (sb-ext:hash-table-weakness hashtable))
   1866 
   1867 ;;; Floating point
   1868 
   1869 (defimplementation float-nan-p (float)
   1870   (sb-ext:float-nan-p float))
   1871 
   1872 (defimplementation float-infinity-p (float)
   1873   (sb-ext:float-infinity-p float))
   1874 
   1875 #-win32
   1876 (defimplementation save-image (filename &optional restart-function)
   1877   (flet ((restart-sbcl ()
   1878            (sb-debug::enable-debugger)
   1879            (setf sb-impl::*descriptor-handlers* nil)
   1880            (funcall restart-function)))
   1881     (let ((pid (sb-posix:fork)))
   1882       (cond ((= pid 0)
   1883              (sb-debug::disable-debugger)
   1884              (apply #'sb-ext:save-lisp-and-die filename
   1885                     (when restart-function
   1886                       (list :toplevel #'restart-sbcl))))
   1887             (t
   1888              (multiple-value-bind (rpid status) (sb-posix:waitpid pid 0)
   1889                (assert (= pid rpid))
   1890                (assert (and (sb-posix:wifexited status)
   1891                             (zerop (sb-posix:wexitstatus status))))))))))
   1892 
   1893 #+unix
   1894 (progn
   1895   (sb-alien:define-alien-routine ("execv" sys-execv) sb-alien:int
   1896     (program sb-alien:c-string)
   1897     (argv (* sb-alien:c-string)))
   1898 
   1899   (defun execv (program args)
   1900     "Replace current executable with another one."
   1901     (let ((a-args (sb-alien:make-alien sb-alien:c-string
   1902                                        (+ 1 (length args)))))
   1903       (unwind-protect
   1904            (progn
   1905              (loop for index from 0 by 1
   1906                    and item in (append args '(nil))
   1907                    do (setf (sb-alien:deref a-args index)
   1908                             item))
   1909              (when (minusp
   1910                     (sys-execv program a-args))
   1911                (error "execv(3) returned.")))
   1912         (sb-alien:free-alien a-args))))
   1913 
   1914   (defun runtime-pathname ()
   1915     #+#.(slynk-backend:with-symbol
   1916             '*runtime-pathname* 'sb-ext)
   1917     sb-ext:*runtime-pathname*
   1918     #-#.(slynk-backend:with-symbol
   1919             '*runtime-pathname* 'sb-ext)
   1920     (car sb-ext:*posix-argv*))
   1921 
   1922   (defimplementation exec-image (image-file args)
   1923     (loop with fd-arg =
   1924           (loop for arg in args
   1925                 and key = "" then arg
   1926                 when (string-equal key "--slynk-fd")
   1927                 return (parse-integer arg))
   1928           for my-fd from 3 to 1024
   1929           when (/= my-fd fd-arg)
   1930           do (ignore-errors (sb-posix:fcntl my-fd sb-posix:f-setfd 1)))
   1931     (let* ((self-string (pathname-to-filename (runtime-pathname))))
   1932       (execv
   1933        self-string
   1934        (apply 'list self-string "--core" image-file args)))))
   1935 
   1936 (defimplementation make-fd-stream (fd external-format)
   1937   (sb-sys:make-fd-stream fd :input t :output t
   1938                          :element-type 'character
   1939                          :buffering :full
   1940                          :dual-channel-p t
   1941                          :external-format external-format))
   1942 
   1943 #-win32
   1944 (defimplementation background-save-image (filename &key restart-function
   1945                                                    completion-function)
   1946   (flet ((restart-sbcl ()
   1947            (sb-debug::enable-debugger)
   1948            (setf sb-impl::*descriptor-handlers* nil)
   1949            (funcall restart-function)))
   1950     (multiple-value-bind (pipe-in pipe-out) (sb-posix:pipe)
   1951       (let ((pid (sb-posix:fork)))
   1952         (cond ((= pid 0)
   1953                (sb-posix:close pipe-in)
   1954                (sb-debug::disable-debugger)
   1955                (apply #'sb-ext:save-lisp-and-die filename
   1956                       (when restart-function
   1957                         (list :toplevel #'restart-sbcl))))
   1958               (t
   1959                (sb-posix:close pipe-out)
   1960                (sb-sys:add-fd-handler
   1961                 pipe-in :input
   1962                 (lambda (fd)
   1963                   (sb-sys:invalidate-descriptor fd)
   1964                   (sb-posix:close fd)
   1965                   (multiple-value-bind (rpid status) (sb-posix:waitpid pid 0)
   1966                     (assert (= pid rpid))
   1967                     (assert (sb-posix:wifexited status))
   1968                     (funcall completion-function
   1969                              (zerop (sb-posix:wexitstatus status))))))))))))
   1970 
   1971 (pushnew 'deinit-log-output sb-ext:*save-hooks*)
   1972 
   1973 
   1974 ;;;; wrap interface implementation
   1975 
   1976 (defimplementation wrap (spec indicator &key before after replace)
   1977   (when (wrapped-p spec indicator)
   1978     (warn "~a already wrapped with indicator ~a, unwrapping first"
   1979           spec indicator)
   1980     (sb-int:unencapsulate spec indicator))
   1981   (sb-int:encapsulate spec indicator
   1982                       #-#.(slynk-backend:with-symbol 'arg-list 'sb-int)
   1983                       (lambda (function &rest args)
   1984                         (sbcl-wrap spec before after replace function args))
   1985                       #+#.(slynk-backend:with-symbol 'arg-list 'sb-int)
   1986                       (if (sbcl-version>= 1 1 16)
   1987                           (lambda ()
   1988                             (sbcl-wrap spec before after replace
   1989                                        (symbol-value 'sb-int:basic-definition)
   1990                                        (symbol-value 'sb-int:arg-list)))
   1991                           `(sbcl-wrap ',spec ,before ,after ,replace
   1992                                       (symbol-value 'sb-int:basic-definition)
   1993                                       (symbol-value 'sb-int:arg-list))))
   1994   (symbol-function spec))
   1995 
   1996 (defimplementation unwrap (spec indicator)
   1997   (sb-int:unencapsulate spec indicator))
   1998 
   1999 (defimplementation wrapped-p (spec indicator)
   2000   (sb-int:encapsulated-p spec indicator))
   2001 
   2002 (defun sbcl-wrap (spec before after replace function args)
   2003   (declare (ignore spec))
   2004   (let (retlist completed)
   2005     (unwind-protect
   2006          (progn
   2007            (when before
   2008              (funcall before args))
   2009            (setq retlist (multiple-value-list (if replace
   2010                                                   (funcall replace
   2011                                                            args)
   2012                                                   (apply function args))))
   2013            (setq completed t)
   2014            (values-list retlist))
   2015       (when after
   2016         (funcall after (if completed retlist :exited-non-locally))))))
   2017 
   2018 #+#.(slynk-backend:with-symbol 'comma-expr 'sb-impl)
   2019 (progn
   2020   (defmethod sexp-in-bounds-p ((s sb-impl::comma) i)
   2021     (sexp-in-bounds-p (sb-impl::comma-expr s) i))
   2022 
   2023   (defmethod sexp-ref ((s sb-impl::comma) i)
   2024     (sexp-ref (sb-impl::comma-expr s) i)))