|
|
- ;;;
- ;;; swank-corman.lisp --- Corman Lisp specific code for SLIME.
- ;;;
- ;;; Copyright (C) 2004, 2005 Espen Wiborg (espenhw@grumblesmurf.org)
- ;;;
- ;;; License
- ;;; =======
- ;;; This software is provided 'as-is', without any express or implied
- ;;; warranty. In no event will the author be held liable for any damages
- ;;; arising from the use of this software.
- ;;;
- ;;; Permission is granted to anyone to use this software for any purpose,
- ;;; including commercial applications, and to alter it and redistribute
- ;;; it freely, subject to the following restrictions:
- ;;;
- ;;; 1. The origin of this software must not be misrepresented; you must
- ;;; not claim that you wrote the original software. If you use this
- ;;; software in a product, an acknowledgment in the product documentation
- ;;; would be appreciated but is not required.
- ;;;
- ;;; 2. Altered source versions must be plainly marked as such, and must
- ;;; not be misrepresented as being the original software.
- ;;;
- ;;; 3. This notice may not be removed or altered from any source
- ;;; distribution.
- ;;;
- ;;; Notes
- ;;; =====
- ;;; You will need CCL 2.51, and you will *definitely* need to patch
- ;;; CCL with the patches at
- ;;; http://www.grumblesmurf.org/lisp/corman-patches, otherwise SLIME
- ;;; will blow up in your face. You should also follow the
- ;;; instructions on http://www.grumblesmurf.org/lisp/corman-slime.
- ;;;
- ;;; The only communication style currently supported is NIL.
- ;;;
- ;;; Starting CCL inside emacs (with M-x slime) seems to work for me
- ;;; with Corman Lisp 2.51, but I have seen random failures with 2.5
- ;;; (sometimes it works, other times it hangs on start or hangs when
- ;;; initializing WinSock) - starting CCL externally and using M-x
- ;;; slime-connect always works fine.
- ;;;
- ;;; Sometimes CCL gets confused and starts giving you random memory
- ;;; access violation errors on startup; if this happens, try redumping
- ;;; your image.
- ;;;
- ;;; What works
- ;;; ==========
- ;;; * Basic editing and evaluation
- ;;; * Arglist display
- ;;; * Compilation
- ;;; * Loading files
- ;;; * apropos/describe
- ;;; * Debugger
- ;;; * Inspector
- ;;;
- ;;; TODO
- ;;; ====
- ;;; * More debugger functionality (missing bits: restart-frame,
- ;;; return-from-frame, disassemble-frame, activate-stepping,
- ;;; toggle-trace)
- ;;; * XREF
- ;;; * Profiling
- ;;; * More sophisticated communication styles than NIL
- ;;;
-
- (in-package :swank/backend)
-
- ;;; Pull in various needed bits
- (require :composite-streams)
- (require :sockets)
- (require :winbase)
- (require :lp)
-
- (use-package :gs)
-
- ;; MOP stuff
-
- (defclass swank-mop:standard-slot-definition ()
- ()
- (:documentation
- "Dummy class created so that swank.lisp will compile and load."))
-
- (defun named-by-gensym-p (c)
- (null (symbol-package (class-name c))))
-
- (deftype swank-mop:eql-specializer ()
- '(satisfies named-by-gensym-p))
-
- (defun swank-mop:eql-specializer-object (specializer)
- (with-hash-table-iterator (next-entry cl::*clos-singleton-specializers*)
- (loop (multiple-value-bind (more key value)
- (next-entry)
- (unless more (return nil))
- (when (eq specializer value)
- (return key))))))
-
- (defun swank-mop:class-finalized-p (class)
- (declare (ignore class))
- t)
-
- (defun swank-mop:class-prototype (class)
- (make-instance class))
-
- (defun swank-mop:specializer-direct-methods (obj)
- (declare (ignore obj))
- nil)
-
- (defun swank-mop:generic-function-argument-precedence-order (gf)
- (generic-function-lambda-list gf))
-
- (defun swank-mop:generic-function-method-combination (gf)
- (declare (ignore gf))
- :standard)
-
- (defun swank-mop:generic-function-declarations (gf)
- (declare (ignore gf))
- nil)
-
- (defun swank-mop:slot-definition-documentation (slot)
- (declare (ignore slot))
- (getf slot :documentation nil))
-
- (defun swank-mop:slot-definition-type (slot)
- (declare (ignore slot))
- t)
-
- (import-swank-mop-symbols :cl '(;; classes
- :standard-slot-definition
- :eql-specializer
- :eql-specializer-object
- ;; standard class readers
- :class-default-initargs
- :class-direct-default-initargs
- :class-finalized-p
- :class-prototype
- :specializer-direct-methods
- ;; gf readers
- :generic-function-argument-precedence-order
- :generic-function-declarations
- :generic-function-method-combination
- ;; method readers
- ;; slot readers
- :slot-definition-documentation
- :slot-definition-type))
-
- ;;;; swank implementations
-
- ;;; Debugger
-
- (defvar *stack-trace* nil)
- (defvar *frame-trace* nil)
-
- (defstruct frame
- name function address debug-info variables)
-
- (defimplementation call-with-debugging-environment (fn)
- (let* ((real-stack-trace (cl::stack-trace))
- (*stack-trace* (cdr (member 'cl:invoke-debugger real-stack-trace
- :key #'car)))
- (*frame-trace*
- (let* ((db::*debug-level* (1+ db::*debug-level*))
- (db::*debug-frame-pointer* (db::stash-ebp
- (ct:create-foreign-ptr)))
- (db::*debug-max-level* (length real-stack-trace))
- (db::*debug-min-level* 1))
- (cdr (member #'cl:invoke-debugger
- (cons
- (make-frame :function nil)
- (loop for i from db::*debug-min-level*
- upto db::*debug-max-level*
- until (eq (db::get-frame-function i)
- cl::*top-level*)
- collect
- (make-frame
- :function (db::get-frame-function i)
- :address (db::get-frame-address i))))
- :key #'frame-function)))))
- (funcall fn)))
-
- (defimplementation compute-backtrace (start end)
- (loop for f in (subseq *stack-trace* start (min end (length *stack-trace*)))
- collect f))
-
- (defimplementation print-frame (frame stream)
- (format stream "~S" frame))
-
- (defun get-frame-debug-info (frame)
- (or (frame-debug-info frame)
- (setf (frame-debug-info frame)
- (db::prepare-frame-debug-info (frame-function frame)
- (frame-address frame)))))
-
- (defimplementation frame-locals (frame-number)
- (let* ((frame (elt *frame-trace* frame-number))
- (info (get-frame-debug-info frame)))
- (let ((var-list
- (loop for i from 4 below (length info) by 2
- collect `(list :name ',(svref info i) :id 0
- :value (db::debug-filter ,(svref info i))))))
- (let ((vars (eval-in-frame `(list ,@var-list) frame-number)))
- (setf (frame-variables frame) vars)))))
-
- (defimplementation eval-in-frame (form frame-number)
- (let ((frame (elt *frame-trace* frame-number)))
- (let ((cl::*compiler-environment* (get-frame-debug-info frame)))
- (eval form))))
-
- (defimplementation frame-var-value (frame-number var)
- (let ((vars (frame-variables (elt *frame-trace* frame-number))))
- (when vars
- (second (elt vars var)))))
-
- (defimplementation frame-source-location (frame-number)
- (fspec-location (frame-function (elt *frame-trace* frame-number))))
-
- (defun break (&optional (format-control "Break") &rest format-arguments)
- (with-simple-restart (continue "Return from BREAK.")
- (let ();(*debugger-hook* nil))
- (let ((condition
- (make-condition 'simple-condition
- :format-control format-control
- :format-arguments format-arguments)))
- ;;(format *debug-io* ";;; User break: ~A~%" condition)
- (invoke-debugger condition))))
- nil)
-
- ;;; Socket communication
-
- (defimplementation create-socket (host port &key backlog)
- (sockets:start-sockets)
- (sockets:make-server-socket :host host :port port))
-
- (defimplementation local-port (socket)
- (sockets:socket-port socket))
-
- (defimplementation close-socket (socket)
- (close socket))
-
- (defimplementation accept-connection (socket
- &key external-format buffering timeout)
- (declare (ignore buffering timeout external-format))
- (sockets:make-socket-stream (sockets:accept-socket socket)))
-
- ;;; Misc
-
- (defimplementation preferred-communication-style ()
- nil)
-
- (defimplementation getpid ()
- ccl:*current-process-id*)
-
- (defimplementation lisp-implementation-type-name ()
- "cormanlisp")
-
- (defimplementation quit-lisp ()
- (sockets:stop-sockets)
- (win32:exitprocess 0))
-
- (defimplementation set-default-directory (directory)
- (setf (ccl:current-directory) directory)
- (directory-namestring (setf *default-pathname-defaults*
- (truename (merge-pathnames directory)))))
-
- (defimplementation default-directory ()
- (directory-namestring (ccl:current-directory)))
-
- (defimplementation macroexpand-all (form &optional env)
- (declare (ignore env))
- (ccl:macroexpand-all form))
-
- ;;; Documentation
-
- (defun fspec-location (fspec)
- (when (symbolp fspec)
- (setq fspec (symbol-function fspec)))
- (let ((file (ccl::function-source-file fspec)))
- (if file
- (handler-case
- (let ((truename (truename
- (merge-pathnames file
- ccl:*cormanlisp-directory*))))
- (make-location (list :file (namestring truename))
- (if (ccl::function-source-line fspec)
- (list :line
- (1+ (ccl::function-source-line fspec)))
- (list :function-name
- (princ-to-string
- (function-name fspec))))))
- (error (c) (list :error (princ-to-string c))))
- (list :error (format nil "No source information available for ~S"
- fspec)))))
-
- (defimplementation find-definitions (name)
- (list (list name (fspec-location name))))
-
- (defimplementation arglist (name)
- (handler-case
- (cond ((and (symbolp name)
- (macro-function name))
- (ccl::macro-lambda-list (symbol-function name)))
- (t
- (when (symbolp name)
- (setq name (symbol-function name)))
- (if (eq (class-of name) cl::the-class-standard-gf)
- (generic-function-lambda-list name)
- (ccl:function-lambda-list name))))
- (error () :not-available)))
-
- (defimplementation function-name (fn)
- (handler-case (getf (cl::function-info-list fn) 'cl::function-name)
- (error () nil)))
-
- (defimplementation describe-symbol-for-emacs (symbol)
- (let ((result '()))
- (flet ((doc (kind &optional (sym symbol))
- (or (documentation sym kind) :not-documented))
- (maybe-push (property value)
- (when value
- (setf result (list* property value result)))))
- (maybe-push
- :variable (when (boundp symbol)
- (doc 'variable)))
- (maybe-push
- :function (if (fboundp symbol)
- (doc 'function)))
- (maybe-push
- :class (if (find-class symbol nil)
- (doc 'class)))
- result)))
-
- (defimplementation describe-definition (symbol namespace)
- (ecase namespace
- (:variable
- (describe symbol))
- ((:function :generic-function)
- (describe (symbol-function symbol)))
- (:class
- (describe (find-class symbol)))))
-
- ;;; Compiler
-
- (defvar *buffer-name* nil)
- (defvar *buffer-position*)
- (defvar *buffer-string*)
- (defvar *compile-filename* nil)
-
- ;; FIXME
- (defimplementation call-with-compilation-hooks (FN)
- (handler-bind ((error (lambda (c)
- (signal 'compiler-condition
- :original-condition c
- :severity :warning
- :message (format nil "~A" c)
- :location
- (cond (*buffer-name*
- (make-location
- (list :buffer *buffer-name*)
- (list :offset *buffer-position* 0)))
- (*compile-filename*
- (make-location
- (list :file *compile-filename*)
- (list :position 1)))
- (t
- (list :error "No location")))))))
- (funcall fn)))
-
- (defimplementation swank-compile-file (input-file output-file
- load-p external-format
- &key policy)
- (declare (ignore external-format policy))
- (with-compilation-hooks ()
- (let ((*buffer-name* nil)
- (*compile-filename* input-file))
- (multiple-value-bind (output-file warnings? failure?)
- (compile-file input-file :output-file output-file)
- (values output-file warnings?
- (or failure? (and load-p (load output-file))))))))
-
- (defimplementation swank-compile-string (string &key buffer position filename
- policy)
- (declare (ignore filename policy))
- (with-compilation-hooks ()
- (let ((*buffer-name* buffer)
- (*buffer-position* position)
- (*buffer-string* string))
- (funcall (compile nil (read-from-string
- (format nil "(~S () ~A)" 'lambda string))))
- t)))
-
- ;;;; Inspecting
-
- ;; Hack to make swank.lisp load, at least
- (defclass file-stream ())
-
- (defun comma-separated (list &optional (callback (lambda (v)
- `(:value ,v))))
- (butlast (loop for e in list
- collect (funcall callback e)
- collect ", ")))
-
- (defmethod emacs-inspect ((class standard-class))
- `("Name: "
- (:value ,(class-name class))
- (:newline)
- "Super classes: "
- ,@(comma-separated (swank-mop:class-direct-superclasses class))
- (:newline)
- "Direct Slots: "
- ,@(comma-separated
- (swank-mop:class-direct-slots class)
- (lambda (slot)
- `(:value ,slot
- ,(princ-to-string
- (swank-mop:slot-definition-name slot)))))
- (:newline)
- "Effective Slots: "
- ,@(if (swank-mop:class-finalized-p class)
- (comma-separated
- (swank-mop:class-slots class)
- (lambda (slot)
- `(:value ,slot ,(princ-to-string
- (swank-mop:slot-definition-name slot)))))
- '("#<N/A (class not finalized)>"))
- (:newline)
- ,@(when (documentation class t)
- `("Documentation:" (:newline) ,(documentation class t) (:newline)))
- "Sub classes: "
- ,@(comma-separated (swank-mop:class-direct-subclasses class)
- (lambda (sub)
- `(:value ,sub ,(princ-to-string (class-name sub)))))
- (:newline)
- "Precedence List: "
- ,@(if (swank-mop:class-finalized-p class)
- (comma-separated
- (swank-mop:class-precedence-list class)
- (lambda (class)
- `(:value ,class
- ,(princ-to-string (class-name class)))))
- '("#<N/A (class not finalized)>"))
- (:newline)))
-
- (defmethod emacs-inspect ((slot cons))
- ;; Inspects slot definitions
- (if (eq (car slot) :name)
- `("Name: " (:value ,(swank-mop:slot-definition-name slot))
- (:newline)
- ,@(when (swank-mop:slot-definition-documentation slot)
- `("Documentation:"
- (:newline)
- (:value
- ,(swank-mop:slot-definition-documentation slot))
- (:newline)))
- "Init args: " (:value
- ,(swank-mop:slot-definition-initargs slot))
- (:newline)
- "Init form: "
- ,(if (swank-mop:slot-definition-initfunction slot)
- `(:value ,(swank-mop:slot-definition-initform slot))
- "#<unspecified>") (:newline)
- "Init function: "
- (:value ,(swank-mop:slot-definition-initfunction slot))
- (:newline))
- (call-next-method)))
-
- (defmethod emacs-inspect ((pathname pathnames::pathname-internal))
- (list* (if (wild-pathname-p pathname)
- "A wild pathname."
- "A pathname.")
- '(:newline)
- (append (label-value-line*
- ("Namestring" (namestring pathname))
- ("Host" (pathname-host pathname))
- ("Device" (pathname-device pathname))
- ("Directory" (pathname-directory pathname))
- ("Name" (pathname-name pathname))
- ("Type" (pathname-type pathname))
- ("Version" (pathname-version pathname)))
- (unless (or (wild-pathname-p pathname)
- (not (probe-file pathname)))
- (label-value-line "Truename" (truename pathname))))))
-
- (defmethod emacs-inspect ((o t))
- (cond ((cl::structurep o) (inspect-structure o))
- (t (call-next-method))))
-
- (defun inspect-structure (o)
- (let* ((template (cl::uref o 1))
- (num-slots (cl::struct-template-num-slots template)))
- (cond ((symbolp template)
- (loop for i below num-slots
- append (label-value-line i (cl::uref o (+ 2 i)))))
- (t
- (loop for i below num-slots
- append (label-value-line (elt template (+ 6 (* i 5)))
- (cl::uref o (+ 2 i))))))))
-
- ;;; Threads
-
- (require 'threads)
-
- (defstruct (mailbox (:conc-name mailbox.))
- thread
- (lock (make-instance 'threads:critical-section))
- (queue '() :type list))
-
- (defvar *mailbox-lock* (make-instance 'threads:critical-section))
- (defvar *mailboxes* (list))
-
- (defmacro with-lock (lock &body body)
- `(threads:with-synchronization (threads:cs ,lock)
- ,@body))
-
- (defimplementation spawn (fun &key name)
- (declare (ignore name))
- (th:create-thread
- (lambda ()
- (handler-bind ((serious-condition #'invoke-debugger))
- (unwind-protect (funcall fun)
- (with-lock *mailbox-lock*
- (setq *mailboxes* (remove cormanlisp:*current-thread-id*
- *mailboxes* :key #'mailbox.thread))))))))
-
- (defimplementation thread-id (thread)
- thread)
-
- (defimplementation find-thread (thread)
- (if (thread-alive-p thread)
- thread))
-
- (defimplementation thread-alive-p (thread)
- (if (threads:thread-handle thread) t nil))
-
- (defimplementation current-thread ()
- cormanlisp:*current-thread-id*)
-
- ;; XXX implement it
- (defimplementation all-threads ()
- '())
-
- ;; XXX something here is broken
- (defimplementation kill-thread (thread)
- (threads:terminate-thread thread 'killed))
-
- (defun mailbox (thread)
- (with-lock *mailbox-lock*
- (or (find thread *mailboxes* :key #'mailbox.thread)
- (let ((mb (make-mailbox :thread thread)))
- (push mb *mailboxes*)
- mb))))
-
- (defimplementation send (thread message)
- (let ((mbox (mailbox thread)))
- (with-lock (mailbox.lock mbox)
- (setf (mailbox.queue mbox)
- (nconc (mailbox.queue mbox) (list message))))))
-
- (defimplementation receive ()
- (let ((mbox (mailbox cormanlisp:*current-thread-id*)))
- (loop
- (with-lock (mailbox.lock mbox)
- (when (mailbox.queue mbox)
- (return (pop (mailbox.queue mbox)))))
- (sleep 0.1))))
-
- ;;; This is probably not good, but it WFM
- (in-package :common-lisp)
-
- (defvar *old-documentation* #'documentation)
- (defun documentation (thing &optional (type 'function))
- (if (symbolp thing)
- (funcall *old-documentation* thing type)
- (values)))
-
- (defmethod print-object ((restart restart) stream)
- (if (or *print-escape*
- *print-readably*)
- (print-unreadable-object (restart stream :type t :identity t)
- (princ (restart-name restart) stream))
- (when (functionp (restart-report-function restart))
- (funcall (restart-report-function restart) stream))))
|