dotemacs

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

ecl.lisp (35049B)


      1 ;;;; -*- indent-tabs-mode: nil -*-
      2 ;;;
      3 ;;; slynk-ecl.lisp --- SLY backend for ECL.
      4 ;;;
      5 ;;; This code has been placed in the Public Domain.  All warranties
      6 ;;; are disclaimed.
      7 ;;;
      8 
      9 ;;; Administrivia
     10 
     11 (defpackage slynk-ecl
     12   (:use cl slynk-backend))
     13 
     14 (in-package slynk-ecl)
     15 
     16 (eval-when (:compile-toplevel :load-toplevel :execute)
     17   (defun ecl-version ()
     18     (let ((version (find-symbol "+ECL-VERSION-NUMBER+" :EXT)))
     19       (if version
     20           (symbol-value version)
     21           0)))
     22   (when (< (ecl-version) 100301)
     23     (error "~&IMPORTANT:~%  ~
     24               The version of ECL you're using (~A) is too old.~%  ~
     25               Please upgrade to at least 10.3.1.~%  ~
     26               Sorry for the inconvenience.~%~%"
     27            (lisp-implementation-version))))
     28 
     29 ;; Hard dependencies.
     30 (eval-when (:compile-toplevel :load-toplevel :execute)
     31   (require 'sockets))
     32 
     33 ;; Soft dependencies.
     34 (eval-when (:compile-toplevel :load-toplevel :execute)
     35   (when (probe-file "sys:profile.fas")
     36     (require :profile)
     37     (pushnew :profile *features*))
     38   (when (probe-file "sys:serve-event.fas")
     39     (require :serve-event)
     40     (pushnew :serve-event *features*)))
     41 
     42 (declaim (optimize (debug 3)))
     43 
     44 ;;; Slynk-mop
     45 
     46 (eval-when (:compile-toplevel :load-toplevel :execute)
     47   (import-slynk-mop-symbols
     48    :clos
     49    (and (< (ecl-version) 121201)
     50         `(:eql-specializer
     51           :eql-specializer-object
     52           :generic-function-declarations
     53           :specializer-direct-methods
     54           ,@(unless (fboundp 'clos:compute-applicable-methods-using-classes)
     55               '(:compute-applicable-methods-using-classes))))))
     56 
     57 (defimplementation gray-package-name ()
     58   "GRAY")
     59 
     60 
     61 ;;;; UTF8
     62 
     63 ;;; Convert the string STRING to a (simple-array (unsigned-byte 8)).
     64 ;;;
     65 ;;;   string-to-utf8 (string)
     66 
     67 ;;; Convert the (simple-array (unsigned-byte 8)) OCTETS to a string.
     68 ;;;
     69 ;;;   utf8-to-string (octets)
     70 
     71 
     72 ;;;; TCP Server
     73 
     74 (defun resolve-hostname (name)
     75   (car (sb-bsd-sockets:host-ent-addresses
     76         (sb-bsd-sockets:get-host-by-name name))))
     77 
     78 (defimplementation create-socket (host port &key backlog)
     79   (let ((socket (make-instance 'sb-bsd-sockets:inet-socket
     80 			       :type :stream
     81 			       :protocol :tcp)))
     82     (setf (sb-bsd-sockets:sockopt-reuse-address socket) t)
     83     (sb-bsd-sockets:socket-bind socket (resolve-hostname host) port)
     84     (sb-bsd-sockets:socket-listen socket (or backlog 5))
     85     socket))
     86 
     87 (defimplementation local-port (socket)
     88   (nth-value 1 (sb-bsd-sockets:socket-name socket)))
     89 
     90 (defimplementation close-socket (socket)
     91   (sb-bsd-sockets:socket-close socket))
     92 
     93 (defun accept (socket)
     94   "Like socket-accept, but retry on EAGAIN."
     95   (loop (handler-case
     96             (return (sb-bsd-sockets:socket-accept socket))
     97           (sb-bsd-sockets:interrupted-error ()))))
     98 
     99 (defimplementation accept-connection (socket
    100                                       &key external-format
    101                                       buffering timeout)
    102   (declare (ignore timeout))
    103   (sb-bsd-sockets:socket-make-stream (accept socket)
    104                                      :output t
    105                                      :input t
    106                                      :buffering (ecase buffering
    107                                                   ((t) :full)
    108                                                   ((nil) :none)
    109                                                   (:line :line))
    110                                      :element-type (if external-format
    111                                                        'character 
    112                                                        '(unsigned-byte 8))
    113                                      :external-format external-format))
    114 
    115 ;;; Call FN whenever SOCKET is readable.
    116 ;;;
    117 ;;;   add-sigio-handler (socket fn)
    118 
    119 ;;; Remove all sigio handlers for SOCKET.
    120 ;;;
    121 ;;;   remove-sigio-handlers (socket)
    122 
    123 ;;; Call FN when Lisp is waiting for input and SOCKET is readable.
    124 ;;;
    125 ;;;   add-fd-handler (socket fn)
    126 
    127 ;;; Remove all fd-handlers for SOCKET.
    128 ;;;
    129 ;;;   remove-fd-handlers (socket)
    130 
    131 (defimplementation preferred-communication-style ()
    132   (cond
    133     ((member :threads *features*) :spawn)
    134     ((member :windows *features*) nil)
    135     (t #|:fd-handler|# nil)))
    136 
    137 ;;; Set the 'stream 'timeout.  The timeout is either the real number
    138 ;;; specifying the timeout in seconds or 'nil for no timeout.
    139 ;;;
    140 ;;;   set-stream-timeout (stream timeout)
    141 
    142 
    143 ;;; Hook called when the first connection from Emacs is established.
    144 ;;; Called from the INIT-FN of the socket server that accepts the
    145 ;;; connection.
    146 ;;;
    147 ;;; This is intended for setting up extra context, e.g. to discover
    148 ;;; that the calling thread is the one that interacts with Emacs.
    149 ;;;
    150 ;;;   emacs-connected ()
    151 
    152 
    153 ;;;; Unix Integration
    154 
    155 (defimplementation getpid ()
    156   (si:getpid))
    157 
    158 ;;; Call FUNCTION on SIGINT (instead of invoking the debugger).
    159 ;;; Return old signal handler.
    160 ;;;
    161 ;;;   install-sigint-handler (function)
    162 
    163 ;;; XXX!
    164 ;;; If ECL is built with thread support, it'll spawn a helper thread
    165 ;;; executing the SIGINT handler. We do not want to BREAK into that
    166 ;;; helper but into the main thread, though. This is coupled with the
    167 ;;; current choice of NIL as communication-style in so far as ECL's
    168 ;;; main-thread is also the Sly's REPL thread.
    169 
    170 (defun make-interrupt-handler (real-handler)
    171   #+threads
    172   (let ((main-thread (find 'si:top-level (mp:all-processes)
    173                            :key #'mp:process-name)))
    174     #'(lambda (&rest args)
    175         (declare (ignore args))
    176         (mp:interrupt-process main-thread real-handler)))
    177   #-threads
    178   #'(lambda (&rest args)
    179       (declare (ignore args))
    180       (funcall real-handler)))
    181 
    182 (defimplementation call-with-user-break-handler (real-handler function)
    183   (let ((old-handler #'si:terminal-interrupt))
    184     (setf (symbol-function 'si:terminal-interrupt)
    185           (make-interrupt-handler real-handler))
    186     (unwind-protect (funcall function)
    187       (setf (symbol-function 'si:terminal-interrupt) old-handler))))
    188 
    189 (defimplementation quit-lisp ()
    190   (ext:quit))
    191 
    192 ;;; Default implementation is fine.
    193 ;;;
    194 ;;;   lisp-implementation-type-name
    195 ;;;   lisp-implementation-program
    196 
    197 (defimplementation socket-fd (socket)
    198   (etypecase socket
    199     (fixnum socket)
    200     (two-way-stream (socket-fd (two-way-stream-input-stream socket)))
    201     (sb-bsd-sockets:socket (sb-bsd-sockets:socket-file-descriptor socket))
    202     (file-stream (si:file-stream-fd socket))))
    203 
    204 ;;; Create a character stream for the file descriptor FD. This
    205 ;;; interface implementation requires either `ffi:c-inline' or has to
    206 ;;; wait for the exported interface.
    207 ;;;
    208 ;;;   make-fd-stream (socket-stream)
    209 
    210 ;;; Duplicate a file descriptor. If the syscall fails, signal a
    211 ;;; condition. See dup(2). This interface requiers `ffi:c-inline' or
    212 ;;; has to wait for the exported interface.
    213 ;;;
    214 ;;;   dup (fd)
    215 
    216 ;;; Does not apply to ECL which doesn't dump images.
    217 ;;;
    218 ;;;   exec-image (image-file args)
    219 
    220 (defimplementation command-line-args ()
    221   (ext:command-args))
    222 
    223 
    224 ;;;; pathnames
    225 
    226 ;;; Return a pathname for FILENAME.
    227 ;;; A filename in Emacs may for example contain asterisks which should not
    228 ;;; be translated to wildcards.
    229 ;;;
    230 ;;;   filename-to-pathname (filename)
    231 
    232 ;;; Return the filename for PATHNAME.
    233 ;;;
    234 ;;;   pathname-to-filename (pathname)
    235 
    236 (defimplementation default-directory ()
    237   (namestring (ext:getcwd)))
    238 
    239 (defimplementation set-default-directory (directory)
    240   (ext:chdir (namestring directory)) ; adapts *DEFAULT-PATHNAME-DEFAULTS*.
    241   (default-directory))
    242 
    243 
    244 ;;; Call FN with hooks to handle special syntax. Can we use it for
    245 ;;; `ffi:c-inline' to be handled as C/C++ code?
    246 ;;;
    247 ;;;   call-with-syntax-hooks
    248 
    249 ;;; Return a suitable initial value for SLYNK:*READTABLE-ALIST*.
    250 ;;;
    251 ;;;   default-readtable-alist
    252 
    253 
    254 ;;;; Packages
    255 
    256 #+package-local-nicknames
    257 (defimplementation package-local-nicknames (package)
    258   (ext:package-local-nicknames package))
    259 
    260 
    261 ;;;; Compilation
    262 
    263 (defvar *buffer-name* nil)
    264 (defvar *buffer-start-position*)
    265 
    266 (defun signal-compiler-condition (&rest args)
    267   (apply #'signal 'compiler-condition args))
    268 
    269 #-ecl-bytecmp
    270 (defun handle-compiler-message (condition)
    271   ;; ECL emits lots of noise in compiler-notes, like "Invoking
    272   ;; external command".
    273   (unless (typep condition 'c::compiler-note)
    274     (signal-compiler-condition
    275      :original-condition condition
    276      :message (princ-to-string condition)
    277      :severity (etypecase condition
    278                  (c:compiler-fatal-error :error)
    279                  (c:compiler-error       :error)
    280                  (error                  :error)
    281                  (style-warning          :style-warning)
    282                  (warning                :warning))
    283      :location (condition-location condition))))
    284 
    285 #-ecl-bytecmp
    286 (defun condition-location (condition)
    287   (let ((file     (c:compiler-message-file condition))
    288         (position (c:compiler-message-file-position condition)))
    289     (if (and position (not (minusp position)))
    290         (if *buffer-name*
    291             (make-buffer-location *buffer-name*
    292                                   *buffer-start-position*
    293                                   position)
    294             (make-file-location file position))
    295         (make-error-location "No location found."))))
    296 
    297 (defimplementation call-with-compilation-hooks (function)
    298   #+ecl-bytecmp
    299   (funcall function)
    300   #-ecl-bytecmp
    301   (handler-bind ((c:compiler-message #'handle-compiler-message))
    302     (funcall function)))
    303 
    304 (defvar *tmpfile-map* (make-hash-table :test #'equal))
    305 
    306 (defun note-buffer-tmpfile (tmp-file buffer-name)
    307   ;; EXT:COMPILED-FUNCTION-FILE below will return a namestring.
    308   (let ((tmp-namestring (namestring (truename tmp-file))))
    309     (setf (gethash tmp-namestring *tmpfile-map*) buffer-name)
    310     tmp-namestring))
    311 
    312 (defun tmpfile-to-buffer (tmp-file)
    313   (gethash tmp-file *tmpfile-map*))
    314 
    315 (defimplementation slynk-compile-string
    316     (string &key buffer position filename line column policy)
    317   (declare (ignore line column policy))
    318   (with-compilation-hooks ()
    319     (let ((*buffer-name* buffer)        ; for compilation hooks
    320           (*buffer-start-position* position))
    321       (let ((tmp-file (si:mkstemp "TMP:ecl-slynk-tmpfile-"))
    322             (fasl-file)
    323             (warnings-p)
    324             (failure-p))
    325         (unwind-protect
    326              (with-open-file (tmp-stream tmp-file :direction :output
    327                                          :if-exists :supersede)
    328                (write-string string tmp-stream)
    329                (finish-output tmp-stream)
    330                (multiple-value-setq (fasl-file warnings-p failure-p)
    331                  (compile-file tmp-file
    332                                :load t
    333                                :source-truename (or filename
    334                                                     (note-buffer-tmpfile tmp-file buffer))
    335                                :source-offset (1- position))))
    336           (when (probe-file tmp-file)
    337             (delete-file tmp-file))
    338           (when fasl-file
    339             (delete-file fasl-file)))
    340         (not failure-p)))))
    341 
    342 (defimplementation slynk-compile-file (input-file output-file
    343                                        load-p external-format
    344                                        &key policy)
    345   (declare (ignore policy))
    346   (with-compilation-hooks ()
    347     (compile-file input-file :output-file output-file
    348                   :load load-p
    349                   :external-format external-format)))
    350 
    351 (defvar *external-format-to-coding-system*
    352   '((:latin-1
    353      "latin-1" "latin-1-unix" "iso-latin-1-unix"
    354      "iso-8859-1" "iso-8859-1-unix")
    355     (:utf-8 "utf-8" "utf-8-unix")))
    356 
    357 (defun external-format (coding-system)
    358   (or (car (rassoc-if (lambda (x) (member coding-system x :test #'equal))
    359                       *external-format-to-coding-system*))
    360       (find coding-system (ext:all-encodings) :test #'string-equal)))
    361 
    362 (defimplementation find-external-format (coding-system)
    363   #+unicode (external-format coding-system)
    364   ;; Without unicode support, ECL uses the one-byte encoding of the
    365   ;; underlying OS, and will barf on anything except :DEFAULT.  We
    366   ;; return NIL here for known multibyte encodings, so
    367   ;; SLYNK:CREATE-SERVER will barf.
    368   #-unicode (let ((xf (external-format coding-system)))
    369               (if (member xf '(:utf-8))
    370                   nil
    371                   :default)))
    372 
    373 
    374 ;;; Default implementation is fine
    375 ;;;
    376 ;;;   guess-external-format
    377 
    378 
    379 ;;;; Streams
    380 
    381 ;;; Implemented in `gray'
    382 ;;;
    383 ;;;   make-output-stream
    384 ;;;   make-input-stream
    385 
    386 
    387 ;;;; Documentation
    388 
    389 (defimplementation arglist (name)
    390   (multiple-value-bind (arglist foundp)
    391       (ext:function-lambda-list name)
    392     (if foundp arglist :not-available)))
    393 
    394 (defimplementation type-specifier-p (symbol)
    395   (or (subtypep nil symbol)
    396       (not (eq (type-specifier-arglist symbol) :not-available))))
    397 
    398 (defimplementation function-name (f)
    399   (typecase f
    400     (generic-function (clos:generic-function-name f))
    401     (function (si:compiled-function-name f))))
    402 
    403 ;;; Default implementation is fine (CL).
    404 ;;; 
    405 ;;; valid-function-name-p (form)
    406 
    407 #+walker
    408 (defimplementation macroexpand-all (form &optional env)
    409   (walker:macroexpand-all form env))
    410 
    411 ;;; Default implementation is fine.
    412 ;;;
    413 ;;;   compiler-macroexpand-1
    414 ;;;   compiler-macroexpand
    415 
    416 (defimplementation describe-symbol-for-emacs (symbol)
    417   (let ((result '()))
    418     (flet ((frob (type boundp)
    419              (when (funcall boundp symbol)
    420                (let ((doc (describe-definition symbol type)))
    421                  (setf result (list* type doc result))))))
    422       (frob :VARIABLE #'boundp)
    423       (frob :FUNCTION #'fboundp)
    424       (frob :CLASS (lambda (x) (find-class x nil))))
    425     result))
    426 
    427 (defimplementation describe-definition (name type)
    428   (case type
    429     (:variable (documentation name 'variable))
    430     (:function (documentation name 'function))
    431     (:class (documentation name 'class))
    432     (t nil)))
    433 
    434 
    435 ;;;; Debugging
    436 
    437 (eval-when (:compile-toplevel :load-toplevel :execute)
    438   (import
    439    '(si::*break-env*
    440      si::*ihs-top*
    441      si::*ihs-current*
    442      si::*ihs-base*
    443      si::*frs-base*
    444      si::*frs-top*
    445      si::*tpl-commands*
    446      si::*tpl-level*
    447      si::frs-top
    448      si::ihs-top
    449      si::ihs-fun
    450      si::ihs-env
    451      si::sch-frs-base
    452      si::set-break-env
    453      si::set-current-ihs
    454      si::tpl-commands)))
    455 
    456 (defun make-invoke-debugger-hook (hook)
    457   (when hook
    458     #'(lambda (condition old-hook)
    459         ;; Regard *debugger-hook* if set by user.
    460         (if *debugger-hook*
    461             nil         ; decline, *DEBUGGER-HOOK* will be tried next.
    462             (funcall hook condition old-hook)))))
    463 
    464 (defimplementation install-debugger-globally (function)
    465   (setq *debugger-hook* function)
    466   (setq ext:*invoke-debugger-hook* (make-invoke-debugger-hook function)))
    467 
    468 (defimplementation call-with-debugger-hook (hook fun)
    469   (let ((*debugger-hook* hook)
    470         (ext:*invoke-debugger-hook* (make-invoke-debugger-hook hook)))
    471     (funcall fun)))
    472 
    473 (defvar *backtrace* '())
    474 
    475 ;;; Commented out; it's not clear this is a good way of doing it. In
    476 ;;; particular because it makes errors stemming from this file harder
    477 ;;; to debug, and given the "young" age of ECL's slynk backend, that's
    478 ;;; a bad idea.
    479 ;;;
    480 ;;; Also before thinking whether to uncomment this consider that SLY
    481 ;;; might not be loaded with slynk-loader.lisp at all.
    482 
    483 ;; (defun in-slynk-package-p (x)
    484 ;;   (and
    485 ;;    (symbolp x)
    486 ;;    (member (symbol-package x)
    487 ;;            (list #.(find-package :slynk)
    488 ;;                  #.(find-package :slynk-backend)
    489 ;;                  #.(ignore-errors (find-package :slynk-mop))
    490 ;;                  #.(ignore-errors (find-package :slynk-loader))))
    491 ;;    t))
    492 
    493 ;; (defun is-slynk-source-p (name)
    494 ;;   (setf name (pathname name))
    495 ;;   (pathname-match-p
    496 ;;    name
    497 ;;    (make-pathname :defaults slynk-loader::*source-directory*
    498 ;;                   :name (pathname-name name)
    499 ;;                   :type (pathname-type name)
    500 ;;                   :version (pathname-version name))))
    501 
    502 ;; (defun is-ignorable-fun-p (x)
    503 ;;   (or
    504 ;;    (in-slynk-package-p (frame-name x))
    505 ;;    (multiple-value-bind (file position)
    506 ;;        (ignore-errors (si::bc-file (car x)))
    507 ;;      (declare (ignore position))
    508 ;;      (if file (is-slynk-source-p file)))))
    509 
    510 (defimplementation call-with-debugging-environment (debugger-loop-fn)
    511   (declare (type function debugger-loop-fn))
    512   (let* ((*ihs-top* (ihs-top))
    513          (*ihs-current* *ihs-top*)
    514          (*frs-base* (or (sch-frs-base *frs-top* *ihs-base*) (1+ (frs-top))))
    515          (*frs-top* (frs-top))
    516          (*tpl-level* (1+ *tpl-level*))
    517          (*backtrace* (loop for ihs from 0 below *ihs-top*
    518                             collect (list (si::ihs-fun ihs)
    519                                           (si::ihs-env ihs)
    520                                           nil))))
    521     (declare (special *ihs-current*))
    522     (loop for f from *frs-base* until *frs-top*
    523           do (let ((i (- (si::frs-ihs f) *ihs-base* 1)))
    524                (when (plusp i)
    525                  (let* ((x (elt *backtrace* i))
    526                         (name (si::frs-tag f)))
    527                    (unless (si::fixnump name)
    528                      (push name (third x)))))))
    529     ;; (setf *backtrace* (remove-if #'is-ignorable-fun-p (nreverse *backtrace*)))
    530     (setf *backtrace* (nreverse *backtrace*))
    531     (set-break-env)
    532     (set-current-ihs)
    533     (let ((*ihs-base* *ihs-top*))
    534       (funcall debugger-loop-fn))))
    535 
    536 (defimplementation compute-backtrace (start end)
    537   (subseq *backtrace* start
    538           (and (numberp end)
    539                (min end (length *backtrace*)))))
    540 
    541 (defun frame-name (frame)
    542   (let ((x (first frame)))
    543     (if (symbolp x)
    544         x
    545         (function-name x))))
    546 
    547 (defun function-position (fun)
    548   (multiple-value-bind (file position)
    549       (si::bc-file fun)
    550     (when file
    551       (make-file-location file position))))
    552 
    553 (defun frame-function (frame)
    554   (let* ((x (first frame))
    555          fun position)
    556     (etypecase x
    557       (symbol (and (fboundp x)
    558                    (setf fun (fdefinition x)
    559                          position (function-position fun))))
    560       (function (setf fun x position (function-position x))))
    561     (values fun position)))
    562 
    563 (defun frame-decode-env (frame)
    564   (let ((functions '())
    565         (blocks '())
    566         (variables '()))
    567     (setf frame (si::decode-ihs-env (second frame)))
    568     (dolist (record (remove-if-not #'consp frame))
    569       (let* ((record0 (car record))
    570 	     (record1 (cdr record)))
    571 	(cond ((or (symbolp record0) (stringp record0))
    572 	       (setq variables (acons record0 record1 variables)))
    573 	      ((not (si::fixnump record0))
    574 	       (push record1 functions))
    575 	      ((symbolp record1)
    576 	       (push record1 blocks))
    577 	      (t
    578 	       ))))
    579     (values functions blocks variables)))
    580 
    581 (defimplementation print-frame (frame stream)
    582   (format stream "~A" (first frame)))
    583 
    584 ;;; Is the frame FRAME restartable?.
    585 ;;; Return T if `restart-frame' can safely be called on the frame.
    586 ;;;
    587 ;;; frame-restartable-p (frame)
    588 
    589 (defimplementation frame-source-location (frame-number)
    590   (let ((frame (elt *backtrace* frame-number)))
    591     (or (nth-value 1 (frame-function frame))
    592         (make-error-location "Unknown source location for ~A." (car frame)))))
    593 
    594 (defimplementation frame-catch-tags (frame-number)
    595   (third (elt *backtrace* frame-number)))
    596 
    597 (defimplementation frame-locals (frame-number)
    598   (loop for (name . value) in (nth-value 2 (frame-decode-env
    599                                             (elt *backtrace* frame-number)))
    600         collect (list :name name :id 0 :value value)))
    601 
    602 (defimplementation frame-var-value (frame-number var-number)
    603   (destructuring-bind (name . value)
    604       (elt
    605        (nth-value 2 (frame-decode-env (elt *backtrace* frame-number)))
    606        var-number)
    607     (declare (ignore name))
    608     value))
    609 
    610 (defimplementation disassemble-frame (frame-number)
    611   (let ((fun (frame-function (elt *backtrace* frame-number))))
    612     (disassemble fun)))
    613 
    614 (defimplementation eval-in-frame (form frame-number)
    615   (let ((env (second (elt *backtrace* frame-number))))
    616     (si:eval-with-env form env)))
    617 
    618 ;;; frame-package
    619 ;;; frame-call
    620 ;;; return-from-frame
    621 ;;; restart-frame
    622 ;;; print-condition
    623 ;;; condition-extras
    624 
    625 (defimplementation gdb-initial-commands ()
    626   ;; These signals are used by the GC.
    627   #+linux '("handle SIGPWR  noprint nostop"
    628             "handle SIGXCPU noprint nostop"))
    629 
    630 ;;; active-stepping
    631 ;;; sldb-break-on-return
    632 ;;; sldb-break-at-start
    633 ;;; sldb-stepper-condition-p
    634 ;;; sldb-setp-into
    635 ;;; sldb-step-next
    636 ;;; sldb-step-out
    637 
    638 
    639 ;;;; Definition finding
    640 
    641 (defvar +TAGS+ (namestring
    642                 (merge-pathnames "TAGS" (translate-logical-pathname "SYS:"))))
    643 
    644 (defun make-file-location (file file-position)
    645   ;; File positions in CL start at 0, but Emacs' buffer positions
    646   ;; start at 1. We specify (:ALIGN T) because the positions comming
    647   ;; from ECL point at right after the toplevel form appearing before
    648   ;; the actual target toplevel form; (:ALIGN T) will DTRT in that case.
    649   (make-location `(:file ,(namestring (translate-logical-pathname file)))
    650                  `(:position ,(1+ file-position))
    651                  `(:align t)))
    652 
    653 (defun make-buffer-location (buffer-name start-position &optional (offset 0))
    654   (make-location `(:buffer ,buffer-name)
    655                  `(:offset ,start-position ,offset)
    656                  `(:align t)))
    657 
    658 (defun make-TAGS-location (&rest tags)
    659   (make-location `(:etags-file ,+TAGS+)
    660                  `(:tag ,@tags)))
    661 
    662 (defimplementation find-definitions (name)
    663   (let ((annotations (ext:get-annotation name 'si::location :all)))
    664     (cond (annotations
    665            (loop for annotation in annotations
    666                  collect (destructuring-bind (dspec file . pos) annotation
    667                            `(,dspec ,(make-file-location file pos)))))
    668           (t
    669            (mapcan #'(lambda (type) (find-definitions-by-type name type))
    670                    (classify-definition-name name))))))
    671 
    672 (defun classify-definition-name (name)
    673   (let ((types '()))
    674     (when (fboundp name)
    675       (cond ((special-operator-p name)
    676              (push :special-operator types))
    677             ((macro-function name)
    678              (push :macro types))
    679             ((typep (fdefinition name) 'generic-function)
    680              (push :generic-function types))
    681             ((si:mangle-name name t)
    682              (push :c-function types))
    683             (t
    684              (push :lisp-function types))))
    685     (when (boundp name)
    686       (cond ((constantp name)
    687              (push :constant types))
    688             (t
    689              (push :global-variable types))))
    690     types))
    691 
    692 (defun find-definitions-by-type (name type)
    693   (ecase type
    694     (:lisp-function
    695      (when-let (loc (source-location (fdefinition name)))
    696        (list `((defun ,name) ,loc))))
    697     (:c-function
    698      (when-let (loc (source-location (fdefinition name)))
    699        (list `((c-source ,name) ,loc))))
    700     (:generic-function
    701      (loop for method in (clos:generic-function-methods (fdefinition name))
    702            for specs = (clos:method-specializers method)
    703            for loc   = (source-location method)
    704            when loc
    705              collect `((defmethod ,name ,specs) ,loc)))
    706     (:macro
    707      (when-let (loc (source-location (macro-function name)))
    708        (list `((defmacro ,name) ,loc))))
    709     (:constant
    710      (when-let (loc (source-location name))
    711        (list `((defconstant ,name) ,loc))))
    712     (:global-variable
    713      (when-let (loc (source-location name))
    714        (list `((defvar ,name) ,loc))))
    715     (:special-operator)))
    716 
    717 ;;; FIXME: There ought to be a better way.
    718 (eval-when (:compile-toplevel :load-toplevel :execute)
    719   (defun c-function-name-p (name)
    720     (and (symbolp name) (si:mangle-name name t) t))
    721   (defun c-function-p (object)
    722     (and (functionp object)
    723          (let ((fn-name (function-name object)))
    724            (and fn-name (c-function-name-p fn-name))))))
    725 
    726 (deftype c-function ()
    727   `(satisfies c-function-p))
    728 
    729 (defun assert-source-directory ()
    730   (unless (probe-file #P"SRC:")
    731     (error "ECL's source directory ~A does not exist. ~
    732             You can specify a different location via the environment ~
    733             variable `ECLSRCDIR'."
    734            (namestring (translate-logical-pathname #P"SYS:"))))) 
    735 
    736 (defun assert-TAGS-file ()
    737   (unless (probe-file +TAGS+)
    738     (error "No TAGS file ~A found. It should have been installed with ECL."
    739            +TAGS+)))
    740 
    741 (defun package-names (package)
    742   (cons (package-name package) (package-nicknames package)))
    743 
    744 (defun source-location (object)
    745   (converting-errors-to-error-location
    746    (typecase object
    747      (c-function
    748       (assert-source-directory)
    749       (assert-TAGS-file)
    750       (let ((lisp-name (function-name object)))
    751         (assert lisp-name)
    752         (multiple-value-bind (flag c-name) (si:mangle-name lisp-name t)
    753           (assert flag)
    754           ;; In ECL's code base sometimes the mangled name is used
    755           ;; directly, sometimes ECL's DPP magic of @SI::SYMBOL or
    756           ;; @EXT::SYMBOL is used. We cannot predict here, so we just
    757           ;; provide several candidates.
    758           (apply #'make-TAGS-location
    759                  c-name
    760                  (loop with s = (symbol-name lisp-name)
    761                        for p in (package-names (symbol-package lisp-name))
    762                        collect (format nil "~A::~A" p s)
    763                        collect (format nil "~(~A::~A~)" p s))))))
    764      (function
    765       (multiple-value-bind (file pos) (ext:compiled-function-file object)
    766         (cond ((not file)
    767                (return-from source-location nil))
    768               ((tmpfile-to-buffer file)
    769                (make-buffer-location (tmpfile-to-buffer file) pos))
    770               (t
    771                (assert (probe-file file))
    772                (assert (not (minusp pos)))
    773                (make-file-location file pos)))))
    774      (method
    775       ;; FIXME: This will always return NIL at the moment; ECL does not
    776       ;; store debug information for methods yet.
    777       (source-location (clos:method-function object)))
    778      ((member nil t)
    779       (multiple-value-bind (flag c-name) (si:mangle-name object)
    780         (assert flag)
    781         (make-TAGS-location c-name))))))
    782 
    783 (defimplementation find-source-location (object)
    784   (or (source-location object)
    785       (make-error-location "Source definition of ~S not found." object)))
    786 
    787 ;;; buffer-first-change
    788 
    789 
    790 ;;;; XREF
    791 
    792 ;;; who-calls
    793 ;;; calls-who
    794 ;;; who-references
    795 ;;; who-binds
    796 ;;; who-sets
    797 ;;; who-macroexpands
    798 ;;; who-specializes
    799 ;;; list-callers
    800 ;;; list-callees
    801 
    802 
    803 ;;;; Profiling
    804 
    805 ;;; XXX: use monitor.lisp (ccl,clisp)
    806 
    807 #+profile
    808 (progn
    809 
    810 (defimplementation profile (fname)
    811   (when fname (eval `(profile:profile ,fname))))
    812 
    813 (defimplementation unprofile (fname)
    814   (when fname (eval `(profile:unprofile ,fname))))
    815 
    816 (defimplementation unprofile-all ()
    817   (profile:unprofile-all)
    818   "All functions unprofiled.")
    819 
    820 (defimplementation profile-report ()
    821   (profile:report))
    822 
    823 (defimplementation profile-reset ()
    824   (profile:reset)
    825   "Reset profiling counters.")
    826 
    827 (defimplementation profiled-functions ()
    828   (profile:profile))
    829 
    830 (defimplementation profile-package (package callers methods)
    831   (declare (ignore callers methods))
    832   (eval `(profile:profile ,(package-name (find-package package)))))
    833 ) ; #+profile (progn ...
    834 
    835 
    836 ;;;; Trace
    837 
    838 ;;; Toggle tracing of the function(s) given with SPEC.
    839 ;;; SPEC can be:
    840 ;;;  (setf NAME)                            ; a setf function
    841 ;;;  (:defmethod NAME QUALIFIER... (SPECIALIZER...)) ; a specific method
    842 ;;;  (:defgeneric NAME)                     ; a generic function with all methods
    843 ;;;  (:call CALLER CALLEE)                  ; trace calls from CALLER to CALLEE.
    844 ;;;  (:labels TOPLEVEL LOCAL)
    845 ;;;  (:flet TOPLEVEL LOCAL) 
    846 ;;;
    847 ;;;   toggle-trace (spec)
    848 
    849 
    850 ;;;; Inspector
    851 
    852 ;;; FIXME: Would be nice if it was possible to inspect objects
    853 ;;; implemented in C.
    854 
    855 ;;; Return a list of bindings corresponding to OBJECT's slots.
    856 ;;;   eval-context (object)
    857 
    858 ;;; Return a string describing the primitive type of object.
    859 ;;;   describe-primitive-type (object)
    860 
    861 
    862 ;;;; Multithreading
    863 
    864 ;;; Not needed in ECL
    865 ;;;
    866 ;;;   initialize-multiprocessing
    867 
    868 #+threads
    869 (progn
    870   (defvar *thread-id-counter* 0)
    871 
    872   (defparameter *thread-id-map* (make-hash-table))
    873 
    874   (defvar *thread-id-map-lock*
    875     (mp:make-lock :name "thread id map lock"))
    876 
    877   (defimplementation spawn (fn &key name)
    878     (mp:process-run-function name fn))
    879 
    880   (defimplementation thread-id (target-thread)
    881     (block thread-id
    882       (mp:with-lock (*thread-id-map-lock*)
    883         ;; Does TARGET-THREAD have an id already?
    884         (maphash (lambda (id thread-pointer)
    885                    (let ((thread (si:weak-pointer-value thread-pointer)))
    886                      (cond ((not thread)
    887                             (remhash id *thread-id-map*))
    888                            ((eq thread target-thread)
    889                             (return-from thread-id id)))))
    890                  *thread-id-map*)
    891         ;; TARGET-THREAD not found in *THREAD-ID-MAP*
    892         (let ((id (incf *thread-id-counter*))
    893               (thread-pointer (si:make-weak-pointer target-thread)))
    894           (setf (gethash id *thread-id-map*) thread-pointer)
    895           id))))
    896 
    897   (defimplementation find-thread (id)
    898     (mp:with-lock (*thread-id-map-lock*)
    899       (let* ((thread-ptr (gethash id *thread-id-map*))
    900              (thread (and thread-ptr (si:weak-pointer-value thread-ptr))))
    901         (unless thread
    902           (remhash id *thread-id-map*))
    903         thread)))
    904 
    905   (defimplementation thread-name (thread)
    906     (mp:process-name thread))
    907 
    908   (defimplementation thread-status (thread)
    909     (if (mp:process-active-p thread)
    910         "RUNNING"
    911         "STOPPED"))
    912 
    913   ;; thread-attributes
    914 
    915   (defimplementation current-thread ()
    916     mp:*current-process*)
    917 
    918   (defimplementation all-threads ()
    919     (mp:all-processes))
    920 
    921   (defimplementation thread-alive-p (thread)
    922     (mp:process-active-p thread))
    923 
    924   (defimplementation interrupt-thread (thread fn)
    925     (mp:interrupt-process thread fn))
    926 
    927   (defimplementation kill-thread (thread)
    928     (mp:process-kill thread))
    929 
    930   (defvar *mailbox-lock* (mp:make-lock :name "mailbox lock"))
    931   (defvar *mailboxes* (list))
    932   (declaim (type list *mailboxes*))
    933 
    934   (defstruct (mailbox (:conc-name mailbox.))
    935     thread
    936     (mutex (mp:make-lock))
    937     (cvar  (mp:make-condition-variable))
    938     (queue '() :type list))
    939 
    940   (defun mailbox (thread)
    941     "Return THREAD's mailbox."
    942     (mp:with-lock (*mailbox-lock*)
    943       (or (find thread *mailboxes* :key #'mailbox.thread)
    944           (let ((mb (make-mailbox :thread thread)))
    945             (push mb *mailboxes*)
    946             mb))))
    947 
    948   (defimplementation send (thread message)
    949     (let* ((mbox (mailbox thread))
    950            (mutex (mailbox.mutex mbox)))
    951       (mp:with-lock (mutex)
    952         (setf (mailbox.queue mbox)
    953               (nconc (mailbox.queue mbox) (list message)))
    954         (mp:condition-variable-broadcast (mailbox.cvar mbox)))))
    955 
    956   ;; receive
    957 
    958   (defimplementation receive-if (test &optional timeout)
    959     (let* ((mbox (mailbox (current-thread)))
    960            (mutex (mailbox.mutex mbox)))
    961       (assert (or (not timeout) (eq timeout t)))
    962       (loop
    963          (check-sly-interrupts)
    964          (mp:with-lock (mutex)
    965            (let* ((q (mailbox.queue mbox))
    966                   (tail (member-if test q)))
    967              (when tail
    968                (setf (mailbox.queue mbox) (nconc (ldiff q tail) (cdr tail)))
    969                (return (car tail))))
    970            (when (eq timeout t) (return (values nil t)))
    971            (mp:condition-variable-wait (mailbox.cvar mbox) mutex)))))
    972 
    973   ;; Trigger a call to CHECK-SLIME-INTERRUPTS in THREAD without using
    974   ;; asynchronous interrupts.
    975   ;;
    976   ;; Doesn't have to implement this if RECEIVE-IF periodically calls
    977   ;; CHECK-SLIME-INTERRUPTS, but that's energy inefficient.
    978   ;;
    979   ;;   wake-thread (thread)
    980 
    981   ;; Copied from sbcl.lisp and adjusted to ECL.
    982   (let ((alist '())
    983         (mutex (mp:make-lock :name "register-thread")))
    984 
    985     (defimplementation register-thread (name thread)
    986       (declare (type symbol name))
    987       (mp:with-lock (mutex)
    988         (etypecase thread
    989           (null
    990            (setf alist (delete name alist :key #'car)))
    991           (mp:process
    992            (let ((probe (assoc name alist)))
    993              (cond (probe (setf (cdr probe) thread))
    994                    (t (setf alist (acons name thread alist))))))))
    995       nil)
    996 
    997     (defimplementation find-registered (name)
    998       (mp:with-lock (mutex)
    999         (cdr (assoc name alist)))))
   1000 
   1001   ;; Not needed in ECL (?).
   1002   ;;
   1003   ;;   set-default-initial-binding (var form)
   1004 
   1005   ) ; #+threads
   1006 
   1007 ;;; Instead of busy waiting with communication-style NIL, use select()
   1008 ;;; on the sockets' streams.
   1009 #+serve-event
   1010 (defimplementation wait-for-input (streams &optional timeout)
   1011   (assert (member timeout '(nil t)))
   1012   (flet ((poll-streams (streams timeout)
   1013            (let* ((serve-event::*descriptor-handlers*
   1014                    (copy-list serve-event::*descriptor-handlers*))
   1015                   (active-fds '())
   1016                   (fd-stream-alist
   1017                    (loop for s in streams
   1018                       for fd = (socket-fd s)
   1019                       collect (cons fd s)
   1020                       do (serve-event:add-fd-handler fd :input
   1021                                                      #'(lambda (fd)
   1022                                                          (push fd active-fds))))))
   1023              (serve-event:serve-event timeout)
   1024              (loop for fd in active-fds collect (cdr (assoc fd fd-stream-alist))))))
   1025     (loop
   1026        (cond ((check-slime-interrupts) (return :interrupt))
   1027              (timeout (return (poll-streams streams 0)))
   1028              (t
   1029               (when-let (ready (poll-streams streams 0.2))
   1030                 (return ready)))))))
   1031 
   1032 #-serve-event
   1033 (defimplementation wait-for-input (streams &optional timeout)
   1034   (assert (member timeout '(nil t)))
   1035   (loop
   1036    (cond ((check-slime-interrupts) (return :interrupt))
   1037          (timeout (return (remove-if-not #'listen streams)))
   1038          (t
   1039           (let ((ready (remove-if-not #'listen streams)))
   1040             (if ready (return ready))
   1041             (sleep 0.1))))))
   1042 
   1043 
   1044 ;;;; Locks
   1045 
   1046 #+threads
   1047 (defimplementation make-lock (&key name)
   1048   (mp:make-lock :name name :recursive t))
   1049 
   1050 (defimplementation call-with-lock-held (lock function)
   1051   (declare (type function function))
   1052   (mp:with-lock (lock) (funcall function)))
   1053 
   1054 
   1055 ;;;; Weak datastructures
   1056 
   1057 ;;; XXX: this should work but causes SLIME REPL hang at some point of time. May
   1058 ;;; be ECL or SLIME bug - disabling for now.
   1059 #+(and ecl-weak-hash (or))
   1060 (progn
   1061   (defimplementation make-weak-key-hash-table (&rest args)
   1062     (apply #'make-hash-table :weakness :key args))
   1063 
   1064   (defimplementation make-weak-value-hash-table (&rest args)
   1065     (apply #'make-hash-table :weakness :value args))
   1066 
   1067   (defimplementation hash-table-weakness (hashtable)
   1068     (ext:hash-table-weakness hashtable)))
   1069 
   1070 
   1071 ;;;; Character names
   1072 
   1073 ;;; Default implementation is fine.
   1074 ;;;
   1075 ;;;   character-completion-set (prefix matchp)
   1076 
   1077 
   1078 ;;;; Heap dumps
   1079 
   1080 ;;; Doesn't apply to ECL.
   1081 ;;;
   1082 ;;;   save-image (filename &optional restart-function)
   1083 ;;;   background-save-image (filename &key restart-function completion-function)
   1084 
   1085 
   1086 ;;;; Wrapping
   1087 
   1088 ;;; Intercept future calls to SPEC and surround them in callbacks.
   1089 ;;; Very much similar to so-called advices for normal functions.
   1090 ;;;
   1091 ;;;   wrap (spec indicator &key before after replace)
   1092 ;;;   unwrap (spec indicator)
   1093 ;;;   wrapped-p (spec indicator)