dotemacs

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

mkcl.lisp (31123B)


      1 ;;;; -*- indent-tabs-mode: nil -*-
      2 ;;;
      3 ;;; slynk-mkcl.lisp --- SLIME backend for MKCL.
      4 ;;;
      5 ;;; This code has been placed in the Public Domain.  All warranties
      6 ;;; are disclaimed.
      7 ;;;
      8 
      9 ;;; Administrivia
     10 
     11 (defpackage slynk-mkcl
     12   (:use cl slynk-backend))
     13 
     14 (in-package slynk-mkcl)
     15 
     16 ;;(declaim (optimize (debug 3)))
     17 
     18 (defvar *tmp*)
     19 
     20 (defimplementation gray-package-name ()
     21   '#:gray)
     22 
     23 (eval-when (:compile-toplevel :load-toplevel)
     24 
     25   (slynk-backend::import-slynk-mop-symbols :clos
     26     ;;  '(:eql-specializer
     27     ;;    :eql-specializer-object
     28     ;;    :generic-function-declarations
     29     ;;    :specializer-direct-methods
     30     ;;    :compute-applicable-methods-using-classes)
     31     nil
     32     ))
     33 
     34 
     35 ;;; UTF8
     36 
     37 (defimplementation string-to-utf8 (string)
     38   (mkcl:octets (si:utf-8 string)))
     39 
     40 (defimplementation utf8-to-string (octets)
     41   (string (si:utf-8 octets)))
     42 
     43 
     44 ;;;; TCP Server
     45 
     46 (eval-when (:compile-toplevel :load-toplevel)
     47   ;; At compile-time we need access to the sb-bsd-sockets package for the
     48   ;; the following code to be read properly.
     49   ;; It is a bit a shame we have to load the entire module to get that.
     50   (require 'sockets))
     51 
     52 
     53 (defun resolve-hostname (name)
     54   (car (sb-bsd-sockets:host-ent-addresses
     55         (sb-bsd-sockets:get-host-by-name name))))
     56 
     57 (defimplementation create-socket (host port &key backlog)
     58   (let ((socket (make-instance 'sb-bsd-sockets:inet-socket
     59 			       :type :stream
     60 			       :protocol :tcp)))
     61     (setf (sb-bsd-sockets:sockopt-reuse-address socket) t)
     62     (sb-bsd-sockets:socket-bind socket (resolve-hostname host) port)
     63     (sb-bsd-sockets:socket-listen socket (or backlog 5))
     64     socket))
     65 
     66 (defimplementation local-port (socket)
     67   (nth-value 1 (sb-bsd-sockets:socket-name socket)))
     68 
     69 (defimplementation close-socket (socket)
     70   (sb-bsd-sockets:socket-close socket))
     71 
     72 (defun accept (socket)
     73   "Like socket-accept, but retry on EINTR."
     74   (loop (handler-case
     75             (return (sb-bsd-sockets:socket-accept socket))
     76           (sb-bsd-sockets:interrupted-error ()))))
     77 
     78 (defimplementation accept-connection (socket
     79                                       &key external-format
     80                                       buffering timeout)
     81   (declare (ignore timeout))
     82   (sb-bsd-sockets:socket-make-stream (accept socket)
     83                                      :output t ;; bogus
     84                                      :input t ;; bogus
     85                                      :buffering buffering ;; bogus
     86                                      :element-type (if external-format
     87                                                        'character 
     88                                                      '(unsigned-byte 8))
     89                                      :external-format external-format
     90                                      ))
     91 
     92 (defimplementation preferred-communication-style ()
     93   :spawn
     94   )
     95 
     96 (defvar *external-format-to-coding-system*
     97   '((:iso-8859-1
     98      "latin-1" "latin-1-unix" "iso-latin-1-unix" 
     99      "iso-8859-1" "iso-8859-1-unix")
    100     (:utf-8 "utf-8" "utf-8-unix")))
    101 
    102 (defun external-format (coding-system)
    103   (or (car (rassoc-if (lambda (x) (member coding-system x :test #'equal))
    104                       *external-format-to-coding-system*))
    105       (find coding-system (si:all-encodings) :test #'string-equal)))
    106 
    107 (defimplementation find-external-format (coding-system)
    108   #+unicode (external-format coding-system)
    109   ;; Without unicode support, MKCL uses the one-byte encoding of the
    110   ;; underlying OS, and will barf on anything except :DEFAULT.  We
    111   ;; return NIL here for known multibyte encodings, so
    112   ;; SLYNK:CREATE-SERVER will barf.
    113   #-unicode (let ((xf (external-format coding-system)))
    114               (if (member xf '(:utf-8))
    115                   nil
    116                 :default)))
    117 
    118 
    119 
    120 ;;;; Unix signals
    121 
    122 (defimplementation install-sigint-handler (handler)
    123   (let ((old-handler (symbol-function 'si:terminal-interrupt)))
    124     (setf (symbol-function 'si:terminal-interrupt)
    125           (if (consp handler)
    126               (car handler)
    127               (lambda (&rest args)
    128                 (declare (ignore args))
    129                 (funcall handler)
    130                 (continue))))
    131     (list old-handler)))
    132 
    133 
    134 (defimplementation getpid ()
    135   (mkcl:getpid))
    136 
    137 (defimplementation set-default-directory (directory)
    138   (mk-ext::chdir (namestring directory))
    139   (default-directory))
    140 
    141 (defimplementation default-directory ()
    142   (namestring (mk-ext:getcwd)))
    143 
    144 (defmacro progf (plist &rest forms)
    145   `(let (_vars _vals)
    146      (do ((p ,plist (cddr p)))
    147          ((endp p))
    148          (push (car p) _vars)
    149          (push (cadr p) _vals))
    150      (progv _vars _vals ,@forms)
    151      )
    152   )
    153 
    154 (defvar *inferior-lisp-sleeping-post* nil)
    155 
    156 (defimplementation quit-lisp ()
    157   ;; restore original IO streams.
    158   (progf (ignore-errors (eval
    159                          (slynk-backend:find-symbol2 "slynk::*saved-global-streams*"))) 
    160          (when *inferior-lisp-sleeping-post* (mt:semaphore-signal *inferior-lisp-sleeping-post*))
    161          ;;(mk-ext:quit :verbose t)
    162          ))
    163 
    164 
    165 ;;;; Compilation
    166 
    167 (defvar *buffer-name* nil)
    168 (defvar *buffer-start-position*)
    169 (defvar *buffer-string*)
    170 (defvar *compile-filename*)
    171 
    172 (defun signal-compiler-condition (&rest args)
    173   (signal (apply #'make-condition 'compiler-condition args)))
    174 
    175 #|
    176 (defun handle-compiler-warning (condition)
    177   (signal-compiler-condition
    178    :original-condition condition
    179    :message (format nil "~A" condition)
    180    :severity :warning
    181    :location
    182    (if *buffer-name*
    183        (make-location (list :buffer *buffer-name*)
    184                       (list :offset *buffer-start-position* 0))
    185        ;; ;; compiler::*current-form*
    186        ;; (if compiler::*current-function*
    187        ;;     (make-location (list :file *compile-filename*)
    188        ;;                    (list :function-name   
    189        ;;                          (symbol-name
    190        ;;                           (slot-value compiler::*current-function*
    191        ;;                                       'compiler::name))))
    192        (list :error "No location found.")
    193            ;; )
    194        )))
    195 |#
    196 
    197 #|
    198 (defun condition-location (condition)
    199   (let ((file     (compiler:compiler-message-file condition))
    200         (position (compiler:compiler-message-file-position condition)))
    201     (if (and position (not (minusp position)))
    202         (if *buffer-name*
    203             (make-buffer-location *buffer-name*
    204                                   *buffer-start-position*
    205                                   position)
    206             (make-file-location file position))
    207         (make-error-location "No location found."))))
    208 |#
    209 
    210 (defun condition-location (condition)
    211   (if *buffer-name*
    212       (make-location (list :buffer *buffer-name*)
    213                      (list :offset *buffer-start-position* 0))
    214        ;; ;; compiler::*current-form*   ;
    215        ;; (if compiler::*current-function* ;
    216        ;;     (make-location (list :file *compile-filename*) ;
    217        ;;                    (list :function-name ;
    218        ;;                          (symbol-name ;
    219        ;;                           (slot-value compiler::*current-function* ;
    220        ;;                                       'compiler::name)))) ;
    221     (if (typep condition 'compiler::compiler-message)
    222         (make-location (list :file (namestring (compiler:compiler-message-file condition)))
    223                        (list :end-position (compiler:compiler-message-file-end-position condition)))
    224       (list :error "No location found."))
    225     )
    226   )
    227 
    228 (defun handle-compiler-message (condition)
    229   (unless (typep condition 'compiler::compiler-note)
    230     (signal-compiler-condition
    231      :original-condition condition
    232      :message (princ-to-string condition)
    233      :severity (etypecase condition
    234                  (compiler:compiler-fatal-error :error)
    235                  (compiler:compiler-error       :error)
    236                  (error                  :error)
    237                  (style-warning          :style-warning)
    238                  (warning                :warning))
    239      :location (condition-location condition))))
    240 
    241 (defimplementation call-with-compilation-hooks (function)
    242   (handler-bind ((compiler:compiler-message #'handle-compiler-message))
    243     (funcall function)))
    244 
    245 (defimplementation slynk-compile-file (input-file output-file
    246                                                   load-p external-format
    247                                                   &key policy)
    248   (declare (ignore policy))
    249   (with-compilation-hooks ()
    250     (let ((*buffer-name* nil)
    251           (*compile-filename* input-file))
    252       (handler-bind (#|
    253                      (compiler::compiler-note
    254                       #'(lambda (n)
    255                           (format t "~%slynk saw a compiler note: ~A~%" n) (finish-output) nil))
    256                      (compiler::compiler-warning
    257                       #'(lambda (w)
    258                           (format t "~%slynk saw a compiler warning: ~A~%" w) (finish-output) nil))
    259                      (compiler::compiler-error
    260                       #'(lambda (e)
    261                           (format t "~%slynk saw a compiler error: ~A~%" e) (finish-output) nil))
    262                      |#
    263                      )
    264         (multiple-value-bind (output-truename warnings-p failure-p)
    265              (compile-file input-file :output-file output-file :external-format external-format)
    266            (values output-truename warnings-p
    267                    (or failure-p
    268                        (and load-p (not (load output-truename))))))))))
    269 
    270 (defimplementation slynk-compile-string (string &key buffer position filename line column policy)
    271   (declare (ignore filename line column policy))
    272   (with-compilation-hooks ()
    273     (let ((*buffer-name* buffer)
    274           (*buffer-start-position* position)
    275           (*buffer-string* string))
    276       (with-input-from-string (s string)
    277         (when position (file-position position))
    278         (compile-from-stream s)))))
    279 
    280 (defun compile-from-stream (stream)
    281   (let ((file (mkcl:mkstemp "TMP:MKCL-SLYNK-TMPXXXXXX"))
    282         output-truename
    283         warnings-p
    284         failure-p
    285         )
    286     (with-open-file (s file :direction :output :if-exists :overwrite)
    287       (do ((line (read-line stream nil) (read-line stream nil)))
    288 	  ((not line))
    289 	(write-line line s)))
    290     (unwind-protect
    291         (progn
    292           (multiple-value-setq (output-truename warnings-p failure-p)
    293                (compile-file file))
    294           (and (not failure-p) (load output-truename)))
    295       (when (probe-file file) (delete-file file))
    296       (when (probe-file output-truename) (delete-file output-truename)))))
    297 
    298 
    299 ;;;; Documentation
    300 
    301 (defun grovel-docstring-for-arglist (name type)
    302   (flet ((compute-arglist-offset (docstring)
    303            (when docstring
    304              (let ((pos1 (search "Args: " docstring)))
    305                (if pos1
    306                    (+ pos1 6)
    307                    (let ((pos2 (search "Syntax: " docstring)))
    308                      (when pos2
    309                        (+ pos2 8))))))))
    310     (let* ((docstring (si::get-documentation name type))
    311            (pos (compute-arglist-offset docstring)))
    312       (if pos
    313           (multiple-value-bind (arglist errorp)
    314               (ignore-errors
    315                 (values (read-from-string docstring t nil :start pos)))
    316             (if (or errorp (not (listp arglist)))
    317                 :not-available
    318                 arglist
    319                 ))
    320           :not-available ))))
    321 
    322 (defimplementation arglist (name)
    323   (cond ((and (symbolp name) (special-operator-p name))
    324          (let ((arglist (grovel-docstring-for-arglist name 'function)))
    325            (if (consp arglist) (cdr arglist) arglist)))
    326         ((and (symbolp name) (macro-function name))
    327          (let ((arglist (grovel-docstring-for-arglist name 'function)))
    328            (if (consp arglist) (cdr arglist) arglist)))
    329         ((or (functionp name) (fboundp name))
    330          (multiple-value-bind (name fndef)
    331            (if (functionp name)
    332                (values (function-name name) name)
    333              (values name (fdefinition name)))
    334            (let ((fle (function-lambda-expression fndef)))
    335              (case (car fle)
    336                    (si:lambda-block (caddr fle))
    337                    (t (typecase fndef
    338                         (generic-function (clos::generic-function-lambda-list fndef))
    339                         (compiled-function (grovel-docstring-for-arglist name 'function))
    340                         (function :not-available)))))))
    341         (t :not-available)))
    342 
    343 (defimplementation function-name (f)
    344   (si:compiled-function-name f)
    345   )
    346 
    347 (eval-when (:compile-toplevel :load-toplevel)
    348   ;; At compile-time we need access to the walker package for the
    349   ;; the following code to be read properly.
    350   ;; It is a bit a shame we have to load the entire module to get that.
    351   (require 'walker))
    352 
    353 (defimplementation macroexpand-all (form &optional env)
    354   (declare (ignore env))
    355   (walker:macroexpand-all form))
    356 
    357 (defimplementation describe-symbol-for-emacs (symbol)
    358   (let ((result '()))
    359     (dolist (type '(:VARIABLE :FUNCTION :CLASS))
    360       (let ((doc (describe-definition symbol type)))
    361         (when doc
    362           (setf result (list* type doc result)))))
    363     result))
    364 
    365 (defimplementation describe-definition (name type)
    366   (case type
    367     (:variable (documentation name 'variable))
    368     (:function (documentation name 'function))
    369     (:class (documentation name 'class))
    370     (t nil)))
    371 
    372 ;;; Debugging
    373 
    374 (eval-when (:compile-toplevel :load-toplevel)
    375   (import
    376    '(si::*break-env*
    377      si::*ihs-top*
    378      si::*ihs-current*
    379      si::*ihs-base*
    380      si::*frs-base*
    381      si::*frs-top*
    382      si::*tpl-commands*
    383      si::*tpl-level*
    384      si::frs-top
    385      si::ihs-top
    386      si::ihs-fun
    387      si::ihs-env
    388      si::sch-frs-base
    389      si::set-break-env
    390      si::set-current-ihs
    391      si::tpl-commands)))
    392 
    393 (defvar *backtrace* '())
    394 
    395 (defun in-slynk-package-p (x)
    396   (and
    397    (symbolp x)
    398    (member (symbol-package x)
    399            (list #.(find-package :slynk)
    400                  #.(find-package :slynk-backend)
    401                  #.(ignore-errors (find-package :slynk-mop))
    402                  #.(ignore-errors (find-package :slynk-loader))))
    403    t))
    404 
    405 (defun is-slynk-source-p (name)
    406   (setf name (pathname name))
    407   #+(or)
    408   (pathname-match-p
    409    name
    410    (make-pathname :defaults slynk-loader::*source-directory*
    411                   :name (pathname-name name)
    412                   :type (pathname-type name)
    413                   :version (pathname-version name)))
    414   nil)
    415 
    416 (defun is-ignorable-fun-p (x)
    417   (or
    418    (in-slynk-package-p (frame-name x))
    419    (multiple-value-bind (file position)
    420        (ignore-errors (si::compiled-function-file (car x)))
    421      (declare (ignore position))
    422      (if file (is-slynk-source-p file)))))
    423 
    424 (defmacro find-ihs-top (x)
    425   (declare (ignore x))
    426   '(si::ihs-top))
    427 
    428 (defimplementation call-with-debugging-environment (debugger-loop-fn)
    429   (declare (type function debugger-loop-fn))
    430   (let* (;;(*tpl-commands* si::tpl-commands)
    431          (*ihs-base* 0)
    432          (*ihs-top* (find-ihs-top 'call-with-debugging-environment))
    433          (*ihs-current* *ihs-top*)
    434          (*frs-base* (or (sch-frs-base 0 #|*frs-top*|# *ihs-base*) (1+ (frs-top))))
    435          (*frs-top* (frs-top))
    436          (*read-suppress* nil)
    437          ;;(*tpl-level* (1+ *tpl-level*))
    438          (*backtrace* (loop for ihs from 0 below *ihs-top*
    439                             collect (list (si::ihs-fun ihs)
    440                                           (si::ihs-env ihs)
    441                                           nil))))
    442     (declare (special *ihs-current*))
    443     (loop for f from *frs-base* to *frs-top*
    444           do (let ((i (- (si::frs-ihs f) *ihs-base* 1)))
    445                (when (plusp i)
    446                  (let* ((x (elt *backtrace* i))
    447                         (name (si::frs-tag f)))
    448                    (unless (mkcl:fixnump name)
    449                      (push name (third x)))))))
    450     (setf *backtrace* (remove-if #'is-ignorable-fun-p (nreverse *backtrace*)))
    451     (setf *tmp* *backtrace*)
    452     (set-break-env)
    453     (set-current-ihs)
    454     (let ((*ihs-base* *ihs-top*))
    455       (funcall debugger-loop-fn))))
    456 
    457 (defimplementation call-with-debugger-hook (hook fun)
    458   (let ((*debugger-hook* hook)
    459         (*ihs-base* (find-ihs-top 'call-with-debugger-hook)))
    460     (funcall fun)))
    461 
    462 (defimplementation compute-backtrace (start end)
    463   (when (numberp end)
    464     (setf end (min end (length *backtrace*))))
    465   (loop for f in (subseq *backtrace* start end)
    466         collect f))
    467 
    468 (defimplementation format-sldb-condition (condition)
    469   "Format a condition for display in SLDB."
    470   ;;(princ-to-string condition)
    471   (format nil "~A~%In thread: ~S" condition mt:*thread*)
    472   )
    473 
    474 (defun frame-name (frame)
    475   (let ((x (first frame)))
    476     (if (symbolp x)
    477       x
    478       (function-name x))))
    479 
    480 (defun function-position (fun)
    481   (multiple-value-bind (file position)
    482       (si::compiled-function-file fun)
    483     (and file (make-location
    484                `(:file ,(if (stringp file) file (namestring file)))
    485                ;;`(:position ,position)
    486                `(:end-position , position)))))
    487 
    488 (defun frame-function (frame)
    489   (let* ((x (first frame))
    490          fun position)
    491     (etypecase x
    492       (symbol (and (fboundp x)
    493                    (setf fun (fdefinition x)
    494                          position (function-position fun))))
    495       (function (setf fun x position (function-position x))))
    496     (values fun position)))
    497 
    498 (defun frame-decode-env (frame)
    499   (let ((functions '())
    500         (blocks '())
    501         (variables '()))
    502     (setf frame (si::decode-ihs-env (second frame)))
    503     (dolist (record frame)
    504       (let* ((record0 (car record))
    505 	     (record1 (cdr record)))
    506 	(cond ((or (symbolp record0) (stringp record0))
    507 	       (setq variables (acons record0 record1 variables)))
    508 	      ((not (mkcl:fixnump record0))
    509 	       (push record1 functions))
    510 	      ((symbolp record1)
    511 	       (push record1 blocks))
    512 	      (t
    513 	       ))))
    514     (values functions blocks variables)))
    515 
    516 (defimplementation print-frame (frame stream)
    517   (let ((function (first frame)))
    518     (let ((fname
    519 ;;;           (cond ((symbolp function) function)
    520 ;;;                 ((si:instancep function) (slot-value function 'name))
    521 ;;;                 ((compiled-function-p function)
    522 ;;;                  (or (si::compiled-function-name function) 'lambda))
    523 ;;;                 (t :zombi))
    524            (si::get-fname function)
    525            ))
    526       (if (eq fname 'si::bytecode)
    527           (format stream "~A [Evaluation of: ~S]"
    528                   fname (function-lambda-expression function))
    529         (format stream "~A" fname)
    530         )
    531       (when (si::closurep function)
    532         (format stream
    533                 ", closure generated from ~A"
    534                 (si::get-fname (si:closure-producer function)))
    535         )
    536       )
    537     )
    538   )
    539 
    540 (defimplementation frame-source-location (frame-number)
    541   (nth-value 1 (frame-function (elt *backtrace* frame-number))))
    542 
    543 (defimplementation frame-catch-tags (frame-number)
    544   (third (elt *backtrace* frame-number)))
    545 
    546 (defimplementation frame-locals (frame-number)
    547   (loop for (name . value) in (nth-value 2 (frame-decode-env (elt *backtrace* frame-number)))
    548         with i = 0
    549         collect (list :name name :id (prog1 i (incf i)) :value value)))
    550 
    551 (defimplementation frame-var-value (frame-number var-id)
    552   (cdr (elt (nth-value 2 (frame-decode-env (elt *backtrace* frame-number))) var-id)))
    553 
    554 (defimplementation disassemble-frame (frame-number)
    555   (let ((fun (frame-fun (elt *backtrace* frame-number))))
    556     (disassemble fun)))
    557 
    558 (defimplementation eval-in-frame (form frame-number)
    559   (let ((env (second (elt *backtrace* frame-number))))
    560     (si:eval-in-env form env)))
    561 
    562 #|
    563 (defimplementation gdb-initial-commands ()
    564   ;; These signals are used by the GC.
    565   #+linux '("handle SIGPWR  noprint nostop"
    566             "handle SIGXCPU noprint nostop"))
    567 
    568 (defimplementation command-line-args ()
    569   (loop for n from 0 below (si:argc) collect (si:argv n)))
    570 |#
    571 
    572 ;;;; Inspector
    573 
    574 (defmethod emacs-inspect ((o t))
    575   ; ecl clos support leaves some to be desired
    576   (cond
    577     ((streamp o)
    578      (list*
    579       (format nil "~S is an ordinary stream~%" o)
    580       (append
    581        (list
    582         "Open for "
    583         (cond
    584           ((ignore-errors (interactive-stream-p o)) "Interactive")
    585           ((and (input-stream-p o) (output-stream-p o)) "Input and output")
    586           ((input-stream-p o) "Input")
    587           ((output-stream-p o) "Output"))
    588         `(:newline) `(:newline))
    589        (label-value-line*
    590         ("Element type" (stream-element-type o))
    591         ("External format" (stream-external-format o)))
    592        (ignore-errors (label-value-line*
    593                        ("Broadcast streams" (broadcast-stream-streams o))))
    594        (ignore-errors (label-value-line*
    595                        ("Concatenated streams" (concatenated-stream-streams o))))
    596        (ignore-errors (label-value-line*
    597                        ("Echo input stream" (echo-stream-input-stream o))))
    598        (ignore-errors (label-value-line*
    599                        ("Echo output stream" (echo-stream-output-stream o))))
    600        (ignore-errors (label-value-line*
    601                        ("Output String" (get-output-stream-string o))))
    602        (ignore-errors (label-value-line*
    603                        ("Synonym symbol" (synonym-stream-symbol o))))
    604        (ignore-errors (label-value-line*
    605                        ("Input stream" (two-way-stream-input-stream o))))
    606        (ignore-errors (label-value-line*
    607                        ("Output stream" (two-way-stream-output-stream o)))))))
    608     ((si:instancep o) ;;t
    609      (let* ((cl (si:instance-class o))
    610             (slots (clos::class-slots cl)))
    611        (list* (format nil "~S is an instance of class ~A~%"
    612                        o (clos::class-name cl))
    613                (loop for x in slots append
    614                     (let* ((name (clos::slot-definition-name x))
    615                            (value (if (slot-boundp o name)
    616                                       (clos::slot-value o name)
    617                                       "Unbound"
    618                                       )))
    619                       (list
    620                        (format nil "~S: " name)
    621                        `(:value ,value)
    622                        `(:newline)))))))
    623     (t (list (format nil "~A" o)))))
    624 
    625 ;;;; Definitions
    626 
    627 (defimplementation find-definitions (name)
    628   (if (fboundp name)
    629       (let ((tmp (find-source-location (symbol-function name))))
    630         `(((defun ,name) ,tmp)))))
    631 
    632 (defimplementation find-source-location (obj)
    633   (setf *tmp* obj)
    634   (or
    635    (typecase obj
    636      (function
    637       (multiple-value-bind (file pos) (ignore-errors (si::compiled-function-file obj))
    638         (if (and file pos) 
    639             (make-location
    640               `(:file ,(if (stringp file) file (namestring file)))
    641               `(:end-position ,pos) ;; `(:position ,pos)
    642               `(:snippet
    643                 ,(with-open-file (s file)
    644                                  (file-position s pos)
    645                                  (skip-comments-and-whitespace s)
    646                                  (read-snippet s))))))))
    647    `(:error (format nil "Source definition of ~S not found" obj))))
    648 
    649 ;;;; Profiling
    650 
    651 
    652 (eval-when (:compile-toplevel :load-toplevel)
    653   ;; At compile-time we need access to the profile package for the
    654   ;; the following code to be read properly.
    655   ;; It is a bit a shame we have to load the entire module to get that.
    656   (require 'profile))
    657 
    658 
    659 (defimplementation profile (fname)
    660   (when fname (eval `(profile:profile ,fname))))
    661 
    662 (defimplementation unprofile (fname)
    663   (when fname (eval `(profile:unprofile ,fname))))
    664 
    665 (defimplementation unprofile-all ()
    666   (profile:unprofile-all)
    667   "All functions unprofiled.")
    668 
    669 (defimplementation profile-report ()
    670   (profile:report))
    671 
    672 (defimplementation profile-reset ()
    673   (profile:reset)
    674   "Reset profiling counters.")
    675 
    676 (defimplementation profiled-functions ()
    677   (profile:profile))
    678 
    679 (defimplementation profile-package (package callers methods)
    680   (declare (ignore callers methods))
    681   (eval `(profile:profile ,(package-name (find-package package)))))
    682 
    683 
    684 ;;;; Threads
    685 
    686 (defvar *thread-id-counter* 0)
    687 
    688 (defvar *thread-id-counter-lock*
    689   (mt:make-lock :name "thread id counter lock"))
    690 
    691 (defun next-thread-id ()
    692   (mt:with-lock (*thread-id-counter-lock*)
    693     (incf *thread-id-counter*))
    694   )
    695 
    696 (defparameter *thread-id-map* (make-hash-table))
    697 (defparameter *id-thread-map* (make-hash-table))
    698 
    699 (defvar *thread-id-map-lock*
    700   (mt:make-lock :name "thread id map lock"))
    701 
    702 (defparameter +default-thread-local-variables+
    703   '(*macroexpand-hook*
    704     *default-pathname-defaults*
    705     *readtable*
    706     *random-state*
    707     *compile-print*
    708     *compile-verbose*
    709     *load-print*
    710     *load-verbose*
    711     *print-array*
    712     *print-base*
    713     *print-case*
    714     *print-circle*
    715     *print-escape*
    716     *print-gensym*
    717     *print-length*
    718     *print-level*
    719     *print-lines*
    720     *print-miser-width*
    721     *print-pprint-dispatch*
    722     *print-pretty*
    723     *print-radix*
    724     *print-readably*
    725     *print-right-margin*
    726     *read-base*
    727     *read-default-float-format*
    728     *read-eval*
    729     *read-suppress*
    730     ))
    731   
    732 (defun thread-local-default-bindings ()
    733   (let (local)
    734     (dolist (var +default-thread-local-variables+ local)
    735       (setq local (acons var (symbol-value var) local))
    736       )))
    737 
    738 ;; mkcl doesn't have weak pointers
    739 (defimplementation spawn (fn &key name initial-bindings)
    740   (let* ((local-defaults (thread-local-default-bindings))
    741          (thread 
    742           ;;(mt:make-thread :name name)
    743           (mt:make-thread :name name
    744                           :initial-bindings (nconc initial-bindings 
    745                                                    local-defaults))
    746            )
    747          (id (next-thread-id)))
    748     (mt:with-lock (*thread-id-map-lock*)
    749       (setf (gethash id *thread-id-map*) thread)
    750       (setf (gethash thread *id-thread-map*) id))
    751     (mt:thread-preset
    752      thread
    753      #'(lambda ()
    754          (unwind-protect
    755               (progn
    756                 ;;(format t "~&Starting thread: ~S.~%" name) (finish-output)
    757                 (mt:thread-detach nil)
    758                 (funcall fn))
    759            (progn
    760              ;;(format t "~&Wrapping up thread: ~S.~%" name) (finish-output)
    761              (mt:with-lock (*thread-id-map-lock*)
    762                (remhash thread *id-thread-map*)
    763                (remhash id *thread-id-map*))
    764              ;;(format t "~&Finished thread: ~S~%" name) (finish-output)
    765              ))))
    766     (mt:thread-enable thread)
    767     (mt:thread-yield)
    768     thread
    769     ))
    770 
    771 (defimplementation thread-id (thread)
    772   (block thread-id
    773     (mt:with-lock (*thread-id-map-lock*)
    774       (or (gethash thread *id-thread-map*)
    775           (let ((id (next-thread-id)))
    776             (setf (gethash id *thread-id-map*) thread)
    777             (setf (gethash thread *id-thread-map*) id)
    778             id)))))
    779 
    780 (defimplementation find-thread (id)
    781   (mt:with-lock (*thread-id-map-lock*)
    782     (gethash id *thread-id-map*)))
    783 
    784 (defimplementation thread-name (thread)
    785   (mt:thread-name thread))
    786 
    787 (defimplementation thread-status (thread)
    788   (if (mt:thread-active-p thread)
    789       "RUNNING"
    790       "STOPPED"))
    791 
    792 (defimplementation make-lock (&key name)
    793   (mt:make-lock :name name :recursive t))
    794 
    795 (defimplementation call-with-lock-held (lock function)
    796   (declare (type function function))
    797   (mt:with-lock (lock) (funcall function)))
    798 
    799 (defimplementation current-thread ()
    800   mt:*thread*)
    801 
    802 (defimplementation all-threads ()
    803   (mt:all-threads))
    804 
    805 (defimplementation interrupt-thread (thread fn)
    806   (mt:interrupt-thread thread fn))
    807 
    808 (defimplementation kill-thread (thread)
    809   (mt:interrupt-thread thread #'mt:terminate-thread)
    810   )
    811 
    812 (defimplementation thread-alive-p (thread)
    813   (mt:thread-active-p thread))
    814 
    815 (defvar *mailbox-lock* (mt:make-lock :name "mailbox lock"))
    816 (defvar *mailboxes* (list))
    817 (declaim (type list *mailboxes*))
    818 
    819 (defstruct (mailbox (:conc-name mailbox.))
    820   thread
    821   locked-by
    822   (mutex (mt:make-lock :name "thread mailbox"))
    823   (semaphore (mt:make-semaphore))
    824   (queue '() :type list))
    825 
    826 (defun mailbox (thread)
    827   "Return THREAD's mailbox."
    828   (mt:with-lock (*mailbox-lock*)
    829     (or (find thread *mailboxes* :key #'mailbox.thread)
    830         (let ((mb (make-mailbox :thread thread)))
    831           (push mb *mailboxes*)
    832           mb))))
    833 
    834 (defimplementation send (thread message)
    835   (handler-case
    836       (let* ((mbox (mailbox thread))
    837          (mutex (mailbox.mutex mbox)))
    838 ;;     (mt:interrupt-thread
    839 ;;      thread
    840 ;;      (lambda ()
    841 ;;        (mt:with-lock (mutex)
    842 ;;          (setf (mailbox.queue mbox)
    843 ;;                (nconc (mailbox.queue mbox) (list message))))))
    844 
    845 ;;     (format t "~&! thread = ~S~% thread = ~S~% message = ~S~%"
    846 ;;             mt:*thread* thread message) (finish-output)
    847     (mt:with-lock (mutex)
    848       (setf (mailbox.locked-by mbox) mt:*thread*)
    849       (setf (mailbox.queue mbox)
    850             (nconc (mailbox.queue mbox) (list message)))
    851       ;;(format t "*") (finish-output)
    852       (handler-case
    853           (mt:semaphore-signal (mailbox.semaphore mbox))
    854         (condition (condition)
    855           (format t "Something went bad with semaphore-signal ~A" condition) (finish-output)
    856           ;;(break)
    857           ))
    858       (setf (mailbox.locked-by mbox) nil)
    859       )
    860     ;;(format t "+") (finish-output)
    861     )
    862     (condition (condition)
    863       (format t "~&Error in send: ~S~%" condition) (finish-output))
    864     )
    865   )
    866 
    867 ;; (defimplementation receive ()
    868 ;;   (block got-mail
    869 ;;     (let* ((mbox (mailbox mt:*thread*))
    870 ;;            (mutex (mailbox.mutex mbox)))
    871 ;;       (loop
    872 ;;          (mt:with-lock (mutex)
    873 ;;            (if (mailbox.queue mbox)
    874 ;;                (return-from got-mail (pop (mailbox.queue mbox)))))
    875 ;;          ;;interrupt-thread will halt this if it takes longer than 1sec
    876 ;;          (sleep 1)))))
    877 
    878 
    879 (defimplementation receive-if (test &optional timeout)
    880   (handler-case
    881   (let* ((mbox (mailbox (current-thread)))
    882          (mutex (mailbox.mutex mbox))
    883          got-one)
    884     (assert (or (not timeout) (eq timeout t)))
    885     (loop
    886        (check-slime-interrupts)
    887        ;;(format t "~&: ~S~%" mt:*thread*) (finish-output)
    888        (handler-case
    889         (setq got-one (mt:semaphore-wait (mailbox.semaphore mbox) 2))
    890         (condition (condition)
    891            (format t "~&In (slynk-mkcl) receive-if: Something went bad with semaphore-wait ~A~%" condition)
    892            (finish-output)
    893            nil
    894            )
    895         )
    896        (mt:with-lock (mutex)
    897          (setf (mailbox.locked-by mbox) mt:*thread*)
    898          (let* ((q (mailbox.queue mbox))
    899                 (tail (member-if test q)))
    900            (when tail 
    901              (setf (mailbox.queue mbox) (nconc (ldiff q tail) (cdr tail)))
    902              (setf (mailbox.locked-by mbox) nil)
    903              ;;(format t "~&thread ~S received: ~S~%" mt:*thread* (car tail))
    904              (return (car tail))))
    905          (setf (mailbox.locked-by mbox) nil)
    906          )
    907 
    908        ;;(format t "/ ~S~%" mt:*thread*) (finish-output)
    909        (when (eq timeout t) (return (values nil t)))
    910 ;;        (unless got-one
    911 ;;          (format t "~&In (slynk-mkcl) receive-if: semaphore-wait timed out!~%"))
    912        )
    913     )
    914     (condition (condition)
    915       (format t "~&Error in (slynk-mkcl) receive-if: ~S, ~A~%" condition condition) (finish-output)
    916       nil
    917       )
    918     )
    919   )
    920 
    921 
    922 (defmethod stream-finish-output ((stream stream))
    923   (finish-output stream))
    924 
    925 
    926 ;;
    927 
    928 ;;#+windows
    929 (defimplementation doze-in-repl ()
    930   (setq *inferior-lisp-sleeping-post* (mt:make-semaphore))
    931   ;;(loop (sleep 1))
    932   (mt:semaphore-wait *inferior-lisp-sleeping-post*)
    933   (mk-ext:quit :verbose t)
    934   )
    935