Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

583 lines
19 KiB

4 years ago
  1. ;;;
  2. ;;; swank-corman.lisp --- Corman Lisp specific code for SLIME.
  3. ;;;
  4. ;;; Copyright (C) 2004, 2005 Espen Wiborg (espenhw@grumblesmurf.org)
  5. ;;;
  6. ;;; License
  7. ;;; =======
  8. ;;; This software is provided 'as-is', without any express or implied
  9. ;;; warranty. In no event will the author be held liable for any damages
  10. ;;; arising from the use of this software.
  11. ;;;
  12. ;;; Permission is granted to anyone to use this software for any purpose,
  13. ;;; including commercial applications, and to alter it and redistribute
  14. ;;; it freely, subject to the following restrictions:
  15. ;;;
  16. ;;; 1. The origin of this software must not be misrepresented; you must
  17. ;;; not claim that you wrote the original software. If you use this
  18. ;;; software in a product, an acknowledgment in the product documentation
  19. ;;; would be appreciated but is not required.
  20. ;;;
  21. ;;; 2. Altered source versions must be plainly marked as such, and must
  22. ;;; not be misrepresented as being the original software.
  23. ;;;
  24. ;;; 3. This notice may not be removed or altered from any source
  25. ;;; distribution.
  26. ;;;
  27. ;;; Notes
  28. ;;; =====
  29. ;;; You will need CCL 2.51, and you will *definitely* need to patch
  30. ;;; CCL with the patches at
  31. ;;; http://www.grumblesmurf.org/lisp/corman-patches, otherwise SLIME
  32. ;;; will blow up in your face. You should also follow the
  33. ;;; instructions on http://www.grumblesmurf.org/lisp/corman-slime.
  34. ;;;
  35. ;;; The only communication style currently supported is NIL.
  36. ;;;
  37. ;;; Starting CCL inside emacs (with M-x slime) seems to work for me
  38. ;;; with Corman Lisp 2.51, but I have seen random failures with 2.5
  39. ;;; (sometimes it works, other times it hangs on start or hangs when
  40. ;;; initializing WinSock) - starting CCL externally and using M-x
  41. ;;; slime-connect always works fine.
  42. ;;;
  43. ;;; Sometimes CCL gets confused and starts giving you random memory
  44. ;;; access violation errors on startup; if this happens, try redumping
  45. ;;; your image.
  46. ;;;
  47. ;;; What works
  48. ;;; ==========
  49. ;;; * Basic editing and evaluation
  50. ;;; * Arglist display
  51. ;;; * Compilation
  52. ;;; * Loading files
  53. ;;; * apropos/describe
  54. ;;; * Debugger
  55. ;;; * Inspector
  56. ;;;
  57. ;;; TODO
  58. ;;; ====
  59. ;;; * More debugger functionality (missing bits: restart-frame,
  60. ;;; return-from-frame, disassemble-frame, activate-stepping,
  61. ;;; toggle-trace)
  62. ;;; * XREF
  63. ;;; * Profiling
  64. ;;; * More sophisticated communication styles than NIL
  65. ;;;
  66. (in-package :swank/backend)
  67. ;;; Pull in various needed bits
  68. (require :composite-streams)
  69. (require :sockets)
  70. (require :winbase)
  71. (require :lp)
  72. (use-package :gs)
  73. ;; MOP stuff
  74. (defclass swank-mop:standard-slot-definition ()
  75. ()
  76. (:documentation
  77. "Dummy class created so that swank.lisp will compile and load."))
  78. (defun named-by-gensym-p (c)
  79. (null (symbol-package (class-name c))))
  80. (deftype swank-mop:eql-specializer ()
  81. '(satisfies named-by-gensym-p))
  82. (defun swank-mop:eql-specializer-object (specializer)
  83. (with-hash-table-iterator (next-entry cl::*clos-singleton-specializers*)
  84. (loop (multiple-value-bind (more key value)
  85. (next-entry)
  86. (unless more (return nil))
  87. (when (eq specializer value)
  88. (return key))))))
  89. (defun swank-mop:class-finalized-p (class)
  90. (declare (ignore class))
  91. t)
  92. (defun swank-mop:class-prototype (class)
  93. (make-instance class))
  94. (defun swank-mop:specializer-direct-methods (obj)
  95. (declare (ignore obj))
  96. nil)
  97. (defun swank-mop:generic-function-argument-precedence-order (gf)
  98. (generic-function-lambda-list gf))
  99. (defun swank-mop:generic-function-method-combination (gf)
  100. (declare (ignore gf))
  101. :standard)
  102. (defun swank-mop:generic-function-declarations (gf)
  103. (declare (ignore gf))
  104. nil)
  105. (defun swank-mop:slot-definition-documentation (slot)
  106. (declare (ignore slot))
  107. (getf slot :documentation nil))
  108. (defun swank-mop:slot-definition-type (slot)
  109. (declare (ignore slot))
  110. t)
  111. (import-swank-mop-symbols :cl '(;; classes
  112. :standard-slot-definition
  113. :eql-specializer
  114. :eql-specializer-object
  115. ;; standard class readers
  116. :class-default-initargs
  117. :class-direct-default-initargs
  118. :class-finalized-p
  119. :class-prototype
  120. :specializer-direct-methods
  121. ;; gf readers
  122. :generic-function-argument-precedence-order
  123. :generic-function-declarations
  124. :generic-function-method-combination
  125. ;; method readers
  126. ;; slot readers
  127. :slot-definition-documentation
  128. :slot-definition-type))
  129. ;;;; swank implementations
  130. ;;; Debugger
  131. (defvar *stack-trace* nil)
  132. (defvar *frame-trace* nil)
  133. (defstruct frame
  134. name function address debug-info variables)
  135. (defimplementation call-with-debugging-environment (fn)
  136. (let* ((real-stack-trace (cl::stack-trace))
  137. (*stack-trace* (cdr (member 'cl:invoke-debugger real-stack-trace
  138. :key #'car)))
  139. (*frame-trace*
  140. (let* ((db::*debug-level* (1+ db::*debug-level*))
  141. (db::*debug-frame-pointer* (db::stash-ebp
  142. (ct:create-foreign-ptr)))
  143. (db::*debug-max-level* (length real-stack-trace))
  144. (db::*debug-min-level* 1))
  145. (cdr (member #'cl:invoke-debugger
  146. (cons
  147. (make-frame :function nil)
  148. (loop for i from db::*debug-min-level*
  149. upto db::*debug-max-level*
  150. until (eq (db::get-frame-function i)
  151. cl::*top-level*)
  152. collect
  153. (make-frame
  154. :function (db::get-frame-function i)
  155. :address (db::get-frame-address i))))
  156. :key #'frame-function)))))
  157. (funcall fn)))
  158. (defimplementation compute-backtrace (start end)
  159. (loop for f in (subseq *stack-trace* start (min end (length *stack-trace*)))
  160. collect f))
  161. (defimplementation print-frame (frame stream)
  162. (format stream "~S" frame))
  163. (defun get-frame-debug-info (frame)
  164. (or (frame-debug-info frame)
  165. (setf (frame-debug-info frame)
  166. (db::prepare-frame-debug-info (frame-function frame)
  167. (frame-address frame)))))
  168. (defimplementation frame-locals (frame-number)
  169. (let* ((frame (elt *frame-trace* frame-number))
  170. (info (get-frame-debug-info frame)))
  171. (let ((var-list
  172. (loop for i from 4 below (length info) by 2
  173. collect `(list :name ',(svref info i) :id 0
  174. :value (db::debug-filter ,(svref info i))))))
  175. (let ((vars (eval-in-frame `(list ,@var-list) frame-number)))
  176. (setf (frame-variables frame) vars)))))
  177. (defimplementation eval-in-frame (form frame-number)
  178. (let ((frame (elt *frame-trace* frame-number)))
  179. (let ((cl::*compiler-environment* (get-frame-debug-info frame)))
  180. (eval form))))
  181. (defimplementation frame-var-value (frame-number var)
  182. (let ((vars (frame-variables (elt *frame-trace* frame-number))))
  183. (when vars
  184. (second (elt vars var)))))
  185. (defimplementation frame-source-location (frame-number)
  186. (fspec-location (frame-function (elt *frame-trace* frame-number))))
  187. (defun break (&optional (format-control "Break") &rest format-arguments)
  188. (with-simple-restart (continue "Return from BREAK.")
  189. (let ();(*debugger-hook* nil))
  190. (let ((condition
  191. (make-condition 'simple-condition
  192. :format-control format-control
  193. :format-arguments format-arguments)))
  194. ;;(format *debug-io* ";;; User break: ~A~%" condition)
  195. (invoke-debugger condition))))
  196. nil)
  197. ;;; Socket communication
  198. (defimplementation create-socket (host port &key backlog)
  199. (sockets:start-sockets)
  200. (sockets:make-server-socket :host host :port port))
  201. (defimplementation local-port (socket)
  202. (sockets:socket-port socket))
  203. (defimplementation close-socket (socket)
  204. (close socket))
  205. (defimplementation accept-connection (socket
  206. &key external-format buffering timeout)
  207. (declare (ignore buffering timeout external-format))
  208. (sockets:make-socket-stream (sockets:accept-socket socket)))
  209. ;;; Misc
  210. (defimplementation preferred-communication-style ()
  211. nil)
  212. (defimplementation getpid ()
  213. ccl:*current-process-id*)
  214. (defimplementation lisp-implementation-type-name ()
  215. "cormanlisp")
  216. (defimplementation quit-lisp ()
  217. (sockets:stop-sockets)
  218. (win32:exitprocess 0))
  219. (defimplementation set-default-directory (directory)
  220. (setf (ccl:current-directory) directory)
  221. (directory-namestring (setf *default-pathname-defaults*
  222. (truename (merge-pathnames directory)))))
  223. (defimplementation default-directory ()
  224. (directory-namestring (ccl:current-directory)))
  225. (defimplementation macroexpand-all (form &optional env)
  226. (declare (ignore env))
  227. (ccl:macroexpand-all form))
  228. ;;; Documentation
  229. (defun fspec-location (fspec)
  230. (when (symbolp fspec)
  231. (setq fspec (symbol-function fspec)))
  232. (let ((file (ccl::function-source-file fspec)))
  233. (if file
  234. (handler-case
  235. (let ((truename (truename
  236. (merge-pathnames file
  237. ccl:*cormanlisp-directory*))))
  238. (make-location (list :file (namestring truename))
  239. (if (ccl::function-source-line fspec)
  240. (list :line
  241. (1+ (ccl::function-source-line fspec)))
  242. (list :function-name
  243. (princ-to-string
  244. (function-name fspec))))))
  245. (error (c) (list :error (princ-to-string c))))
  246. (list :error (format nil "No source information available for ~S"
  247. fspec)))))
  248. (defimplementation find-definitions (name)
  249. (list (list name (fspec-location name))))
  250. (defimplementation arglist (name)
  251. (handler-case
  252. (cond ((and (symbolp name)
  253. (macro-function name))
  254. (ccl::macro-lambda-list (symbol-function name)))
  255. (t
  256. (when (symbolp name)
  257. (setq name (symbol-function name)))
  258. (if (eq (class-of name) cl::the-class-standard-gf)
  259. (generic-function-lambda-list name)
  260. (ccl:function-lambda-list name))))
  261. (error () :not-available)))
  262. (defimplementation function-name (fn)
  263. (handler-case (getf (cl::function-info-list fn) 'cl::function-name)
  264. (error () nil)))
  265. (defimplementation describe-symbol-for-emacs (symbol)
  266. (let ((result '()))
  267. (flet ((doc (kind &optional (sym symbol))
  268. (or (documentation sym kind) :not-documented))
  269. (maybe-push (property value)
  270. (when value
  271. (setf result (list* property value result)))))
  272. (maybe-push
  273. :variable (when (boundp symbol)
  274. (doc 'variable)))
  275. (maybe-push
  276. :function (if (fboundp symbol)
  277. (doc 'function)))
  278. (maybe-push
  279. :class (if (find-class symbol nil)
  280. (doc 'class)))
  281. result)))
  282. (defimplementation describe-definition (symbol namespace)
  283. (ecase namespace
  284. (:variable
  285. (describe symbol))
  286. ((:function :generic-function)
  287. (describe (symbol-function symbol)))
  288. (:class
  289. (describe (find-class symbol)))))
  290. ;;; Compiler
  291. (defvar *buffer-name* nil)
  292. (defvar *buffer-position*)
  293. (defvar *buffer-string*)
  294. (defvar *compile-filename* nil)
  295. ;; FIXME
  296. (defimplementation call-with-compilation-hooks (FN)
  297. (handler-bind ((error (lambda (c)
  298. (signal 'compiler-condition
  299. :original-condition c
  300. :severity :warning
  301. :message (format nil "~A" c)
  302. :location
  303. (cond (*buffer-name*
  304. (make-location
  305. (list :buffer *buffer-name*)
  306. (list :offset *buffer-position* 0)))
  307. (*compile-filename*
  308. (make-location
  309. (list :file *compile-filename*)
  310. (list :position 1)))
  311. (t
  312. (list :error "No location")))))))
  313. (funcall fn)))
  314. (defimplementation swank-compile-file (input-file output-file
  315. load-p external-format
  316. &key policy)
  317. (declare (ignore external-format policy))
  318. (with-compilation-hooks ()
  319. (let ((*buffer-name* nil)
  320. (*compile-filename* input-file))
  321. (multiple-value-bind (output-file warnings? failure?)
  322. (compile-file input-file :output-file output-file)
  323. (values output-file warnings?
  324. (or failure? (and load-p (load output-file))))))))
  325. (defimplementation swank-compile-string (string &key buffer position filename
  326. policy)
  327. (declare (ignore filename policy))
  328. (with-compilation-hooks ()
  329. (let ((*buffer-name* buffer)
  330. (*buffer-position* position)
  331. (*buffer-string* string))
  332. (funcall (compile nil (read-from-string
  333. (format nil "(~S () ~A)" 'lambda string))))
  334. t)))
  335. ;;;; Inspecting
  336. ;; Hack to make swank.lisp load, at least
  337. (defclass file-stream ())
  338. (defun comma-separated (list &optional (callback (lambda (v)
  339. `(:value ,v))))
  340. (butlast (loop for e in list
  341. collect (funcall callback e)
  342. collect ", ")))
  343. (defmethod emacs-inspect ((class standard-class))
  344. `("Name: "
  345. (:value ,(class-name class))
  346. (:newline)
  347. "Super classes: "
  348. ,@(comma-separated (swank-mop:class-direct-superclasses class))
  349. (:newline)
  350. "Direct Slots: "
  351. ,@(comma-separated
  352. (swank-mop:class-direct-slots class)
  353. (lambda (slot)
  354. `(:value ,slot
  355. ,(princ-to-string
  356. (swank-mop:slot-definition-name slot)))))
  357. (:newline)
  358. "Effective Slots: "
  359. ,@(if (swank-mop:class-finalized-p class)
  360. (comma-separated
  361. (swank-mop:class-slots class)
  362. (lambda (slot)
  363. `(:value ,slot ,(princ-to-string
  364. (swank-mop:slot-definition-name slot)))))
  365. '("#<N/A (class not finalized)>"))
  366. (:newline)
  367. ,@(when (documentation class t)
  368. `("Documentation:" (:newline) ,(documentation class t) (:newline)))
  369. "Sub classes: "
  370. ,@(comma-separated (swank-mop:class-direct-subclasses class)
  371. (lambda (sub)
  372. `(:value ,sub ,(princ-to-string (class-name sub)))))
  373. (:newline)
  374. "Precedence List: "
  375. ,@(if (swank-mop:class-finalized-p class)
  376. (comma-separated
  377. (swank-mop:class-precedence-list class)
  378. (lambda (class)
  379. `(:value ,class
  380. ,(princ-to-string (class-name class)))))
  381. '("#<N/A (class not finalized)>"))
  382. (:newline)))
  383. (defmethod emacs-inspect ((slot cons))
  384. ;; Inspects slot definitions
  385. (if (eq (car slot) :name)
  386. `("Name: " (:value ,(swank-mop:slot-definition-name slot))
  387. (:newline)
  388. ,@(when (swank-mop:slot-definition-documentation slot)
  389. `("Documentation:"
  390. (:newline)
  391. (:value
  392. ,(swank-mop:slot-definition-documentation slot))
  393. (:newline)))
  394. "Init args: " (:value
  395. ,(swank-mop:slot-definition-initargs slot))
  396. (:newline)
  397. "Init form: "
  398. ,(if (swank-mop:slot-definition-initfunction slot)
  399. `(:value ,(swank-mop:slot-definition-initform slot))
  400. "#<unspecified>") (:newline)
  401. "Init function: "
  402. (:value ,(swank-mop:slot-definition-initfunction slot))
  403. (:newline))
  404. (call-next-method)))
  405. (defmethod emacs-inspect ((pathname pathnames::pathname-internal))
  406. (list* (if (wild-pathname-p pathname)
  407. "A wild pathname."
  408. "A pathname.")
  409. '(:newline)
  410. (append (label-value-line*
  411. ("Namestring" (namestring pathname))
  412. ("Host" (pathname-host pathname))
  413. ("Device" (pathname-device pathname))
  414. ("Directory" (pathname-directory pathname))
  415. ("Name" (pathname-name pathname))
  416. ("Type" (pathname-type pathname))
  417. ("Version" (pathname-version pathname)))
  418. (unless (or (wild-pathname-p pathname)
  419. (not (probe-file pathname)))
  420. (label-value-line "Truename" (truename pathname))))))
  421. (defmethod emacs-inspect ((o t))
  422. (cond ((cl::structurep o) (inspect-structure o))
  423. (t (call-next-method))))
  424. (defun inspect-structure (o)
  425. (let* ((template (cl::uref o 1))
  426. (num-slots (cl::struct-template-num-slots template)))
  427. (cond ((symbolp template)
  428. (loop for i below num-slots
  429. append (label-value-line i (cl::uref o (+ 2 i)))))
  430. (t
  431. (loop for i below num-slots
  432. append (label-value-line (elt template (+ 6 (* i 5)))
  433. (cl::uref o (+ 2 i))))))))
  434. ;;; Threads
  435. (require 'threads)
  436. (defstruct (mailbox (:conc-name mailbox.))
  437. thread
  438. (lock (make-instance 'threads:critical-section))
  439. (queue '() :type list))
  440. (defvar *mailbox-lock* (make-instance 'threads:critical-section))
  441. (defvar *mailboxes* (list))
  442. (defmacro with-lock (lock &body body)
  443. `(threads:with-synchronization (threads:cs ,lock)
  444. ,@body))
  445. (defimplementation spawn (fun &key name)
  446. (declare (ignore name))
  447. (th:create-thread
  448. (lambda ()
  449. (handler-bind ((serious-condition #'invoke-debugger))
  450. (unwind-protect (funcall fun)
  451. (with-lock *mailbox-lock*
  452. (setq *mailboxes* (remove cormanlisp:*current-thread-id*
  453. *mailboxes* :key #'mailbox.thread))))))))
  454. (defimplementation thread-id (thread)
  455. thread)
  456. (defimplementation find-thread (thread)
  457. (if (thread-alive-p thread)
  458. thread))
  459. (defimplementation thread-alive-p (thread)
  460. (if (threads:thread-handle thread) t nil))
  461. (defimplementation current-thread ()
  462. cormanlisp:*current-thread-id*)
  463. ;; XXX implement it
  464. (defimplementation all-threads ()
  465. '())
  466. ;; XXX something here is broken
  467. (defimplementation kill-thread (thread)
  468. (threads:terminate-thread thread 'killed))
  469. (defun mailbox (thread)
  470. (with-lock *mailbox-lock*
  471. (or (find thread *mailboxes* :key #'mailbox.thread)
  472. (let ((mb (make-mailbox :thread thread)))
  473. (push mb *mailboxes*)
  474. mb))))
  475. (defimplementation send (thread message)
  476. (let ((mbox (mailbox thread)))
  477. (with-lock (mailbox.lock mbox)
  478. (setf (mailbox.queue mbox)
  479. (nconc (mailbox.queue mbox) (list message))))))
  480. (defimplementation receive ()
  481. (let ((mbox (mailbox cormanlisp:*current-thread-id*)))
  482. (loop
  483. (with-lock (mailbox.lock mbox)
  484. (when (mailbox.queue mbox)
  485. (return (pop (mailbox.queue mbox)))))
  486. (sleep 0.1))))
  487. ;;; This is probably not good, but it WFM
  488. (in-package :common-lisp)
  489. (defvar *old-documentation* #'documentation)
  490. (defun documentation (thing &optional (type 'function))
  491. (if (symbolp thing)
  492. (funcall *old-documentation* thing type)
  493. (values)))
  494. (defmethod print-object ((restart restart) stream)
  495. (if (or *print-escape*
  496. *print-readably*)
  497. (print-unreadable-object (restart stream :type t :identity t)
  498. (princ (restart-name restart) stream))
  499. (when (functionp (restart-report-function restart))
  500. (funcall (restart-report-function restart) stream))))