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.

930 lines
34 KiB

5 years ago
  1. ;;;; -*- indent-tabs-mode: nil -*-
  2. ;;;; SWANK support for CLISP.
  3. ;;;; Copyright (C) 2003, 2004 W. Jenkner, V. Sedach
  4. ;;;; This program is free software; you can redistribute it and/or
  5. ;;;; modify it under the terms of the GNU General Public License as
  6. ;;;; published by the Free Software Foundation; either version 2 of
  7. ;;;; the License, or (at your option) any later version.
  8. ;;;; This program is distributed in the hope that it will be useful,
  9. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. ;;;; GNU General Public License for more details.
  12. ;;;; You should have received a copy of the GNU General Public
  13. ;;;; License along with this program; if not, write to the Free
  14. ;;;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
  15. ;;;; MA 02111-1307, USA.
  16. ;;; This is work in progress, but it's already usable. Many things
  17. ;;; are adapted from other swank-*.lisp, in particular from
  18. ;;; swank-allegro (I don't use allegro at all, but it's the shortest
  19. ;;; one and I found Helmut Eller's code there enlightening).
  20. ;;; This code will work better with recent versions of CLISP (say, the
  21. ;;; last release or CVS HEAD) while it may not work at all with older
  22. ;;; versions. It is reasonable to expect it to work on platforms with
  23. ;;; a "SOCKET" package, in particular on GNU/Linux or Unix-like
  24. ;;; systems, but also on Win32. This backend uses the portable xref
  25. ;;; from the CMU AI repository and metering.lisp from CLOCC [1], which
  26. ;;; are conveniently included in SLIME.
  27. ;;; [1] http://cvs.sourceforge.net/viewcvs.py/clocc/clocc/src/tools/metering/
  28. (defpackage swank/clisp
  29. (:use cl swank/backend))
  30. (in-package swank/clisp)
  31. (eval-when (:compile-toplevel)
  32. (unless (string< "2.44" (lisp-implementation-version))
  33. (error "Need at least CLISP version 2.44")))
  34. (defimplementation gray-package-name ()
  35. "GRAY")
  36. ;;;; if this lisp has the complete CLOS then we use it, otherwise we
  37. ;;;; build up a "fake" swank-mop and then override the methods in the
  38. ;;;; inspector.
  39. (eval-when (:compile-toplevel :load-toplevel :execute)
  40. (defvar *have-mop*
  41. (and (find-package :clos)
  42. (eql :external
  43. (nth-value 1 (find-symbol (string ':standard-slot-definition)
  44. :clos))))
  45. "True in those CLISP images which have a complete MOP implementation."))
  46. #+#.(cl:if swank/clisp::*have-mop* '(cl:and) '(cl:or))
  47. (progn
  48. (import-swank-mop-symbols :clos '(:slot-definition-documentation))
  49. (defun swank-mop:slot-definition-documentation (slot)
  50. (clos::slot-definition-documentation slot)))
  51. #-#.(cl:if swank/clisp::*have-mop* '(and) '(or))
  52. (defclass swank-mop:standard-slot-definition ()
  53. ()
  54. (:documentation
  55. "Dummy class created so that swank.lisp will compile and load."))
  56. (let ((getpid (or (find-symbol "PROCESS-ID" :system)
  57. ;; old name prior to 2005-03-01, clisp <= 2.33.2
  58. (find-symbol "PROGRAM-ID" :system)
  59. #+win32 ; integrated into the above since 2005-02-24
  60. (and (find-package :win32) ; optional modules/win32
  61. (find-symbol "GetCurrentProcessId" :win32)))))
  62. (defimplementation getpid () ; a required interface
  63. (cond
  64. (getpid (funcall getpid))
  65. #+win32 ((ext:getenv "PID")) ; where does that come from?
  66. (t -1))))
  67. (defimplementation call-with-user-break-handler (handler function)
  68. (handler-bind ((system::simple-interrupt-condition
  69. (lambda (c)
  70. (declare (ignore c))
  71. (funcall handler)
  72. (when (find-restart 'socket-status)
  73. (invoke-restart (find-restart 'socket-status)))
  74. (continue))))
  75. (funcall function)))
  76. (defimplementation lisp-implementation-type-name ()
  77. "clisp")
  78. (defimplementation set-default-directory (directory)
  79. (setf (ext:default-directory) directory)
  80. (namestring (setf *default-pathname-defaults* (ext:default-directory))))
  81. (defimplementation filename-to-pathname (string)
  82. (cond ((member :cygwin *features*)
  83. (parse-cygwin-filename string))
  84. (t (parse-namestring string))))
  85. (defun parse-cygwin-filename (string)
  86. (multiple-value-bind (match _ drive absolute)
  87. (regexp:match "^(([a-zA-Z\\]+):)?([\\/])?" string :extended t)
  88. (declare (ignore _))
  89. (assert (and match (if drive absolute t)) ()
  90. "Invalid filename syntax: ~a" string)
  91. (let* ((sans-prefix (subseq string (regexp:match-end match)))
  92. (path (remove "" (regexp:regexp-split "[\\/]" sans-prefix)))
  93. (path (loop for name in path collect
  94. (cond ((equal name "..") ':back)
  95. (t name))))
  96. (directoryp (or (equal string "")
  97. (find (aref string (1- (length string))) "\\/"))))
  98. (multiple-value-bind (file type)
  99. (cond ((and (not directoryp) (last path))
  100. (let* ((file (car (last path)))
  101. (pos (position #\. file :from-end t)))
  102. (cond ((and pos (> pos 0))
  103. (values (subseq file 0 pos)
  104. (subseq file (1+ pos))))
  105. (t file)))))
  106. (make-pathname :host nil
  107. :device nil
  108. :directory (cons
  109. (if absolute :absolute :relative)
  110. (let ((path (if directoryp
  111. path
  112. (butlast path))))
  113. (if drive
  114. (cons
  115. (regexp:match-string string drive)
  116. path)
  117. path)))
  118. :name file
  119. :type type)))))
  120. ;;;; UTF
  121. (defimplementation string-to-utf8 (string)
  122. (let ((enc (load-time-value
  123. (ext:make-encoding :charset "utf-8" :line-terminator :unix)
  124. t)))
  125. (ext:convert-string-to-bytes string enc)))
  126. (defimplementation utf8-to-string (octets)
  127. (let ((enc (load-time-value
  128. (ext:make-encoding :charset "utf-8" :line-terminator :unix)
  129. t)))
  130. (ext:convert-string-from-bytes octets enc)))
  131. ;;;; TCP Server
  132. (defimplementation create-socket (host port &key backlog)
  133. (socket:socket-server port :interface host :backlog (or backlog 5)))
  134. (defimplementation local-port (socket)
  135. (socket:socket-server-port socket))
  136. (defimplementation close-socket (socket)
  137. (socket:socket-server-close socket))
  138. (defimplementation accept-connection (socket
  139. &key external-format buffering timeout)
  140. (declare (ignore buffering timeout))
  141. (socket:socket-accept socket
  142. :buffered buffering ;; XXX may not work if t
  143. :element-type (if external-format
  144. 'character
  145. '(unsigned-byte 8))
  146. :external-format (or external-format :default)))
  147. #-win32
  148. (defimplementation wait-for-input (streams &optional timeout)
  149. (assert (member timeout '(nil t)))
  150. (let ((streams (mapcar (lambda (s) (list* s :input nil)) streams)))
  151. (loop
  152. (cond ((check-slime-interrupts) (return :interrupt))
  153. (timeout
  154. (socket:socket-status streams 0 0)
  155. (return (loop for (s nil . x) in streams
  156. if x collect s)))
  157. (t
  158. (with-simple-restart (socket-status "Return from socket-status.")
  159. (socket:socket-status streams 0 500000))
  160. (let ((ready (loop for (s nil . x) in streams
  161. if x collect s)))
  162. (when ready (return ready))))))))
  163. #+win32
  164. (defimplementation wait-for-input (streams &optional timeout)
  165. (assert (member timeout '(nil t)))
  166. (loop
  167. (cond ((check-slime-interrupts) (return :interrupt))
  168. (t
  169. (let ((ready (remove-if-not #'input-available-p streams)))
  170. (when ready (return ready)))
  171. (when timeout (return nil))
  172. (sleep 0.1)))))
  173. #+win32
  174. ;; Some facts to remember (for the next time we need to debug this):
  175. ;; - interactive-sream-p returns t for socket-streams
  176. ;; - listen returns nil for socket-streams
  177. ;; - (type-of <socket-stream>) is 'stream
  178. ;; - (type-of *terminal-io*) is 'two-way-stream
  179. ;; - stream-element-type on our sockets is usually (UNSIGNED-BYTE 8)
  180. ;; - calling socket:socket-status on non sockets signals an error,
  181. ;; but seems to mess up something internally.
  182. ;; - calling read-char-no-hang on sockets does not signal an error,
  183. ;; but seems to mess up something internally.
  184. (defun input-available-p (stream)
  185. (case (stream-element-type stream)
  186. (character
  187. (let ((c (read-char-no-hang stream nil nil)))
  188. (cond ((not c)
  189. nil)
  190. (t
  191. (unread-char c stream)
  192. t))))
  193. (t
  194. (eq (socket:socket-status (cons stream :input) 0 0)
  195. :input))))
  196. ;;;; Coding systems
  197. (defvar *external-format-to-coding-system*
  198. '(((:charset "iso-8859-1" :line-terminator :unix)
  199. "latin-1-unix" "iso-latin-1-unix" "iso-8859-1-unix")
  200. ((:charset "iso-8859-1")
  201. "latin-1" "iso-latin-1" "iso-8859-1")
  202. ((:charset "utf-8") "utf-8")
  203. ((:charset "utf-8" :line-terminator :unix) "utf-8-unix")
  204. ((:charset "euc-jp") "euc-jp")
  205. ((:charset "euc-jp" :line-terminator :unix) "euc-jp-unix")
  206. ((:charset "us-ascii") "us-ascii")
  207. ((:charset "us-ascii" :line-terminator :unix) "us-ascii-unix")))
  208. (defimplementation find-external-format (coding-system)
  209. (let ((args (car (rassoc-if (lambda (x)
  210. (member coding-system x :test #'equal))
  211. *external-format-to-coding-system*))))
  212. (and args (apply #'ext:make-encoding args))))
  213. ;;;; Swank functions
  214. (defimplementation arglist (fname)
  215. (block nil
  216. (or (ignore-errors
  217. (let ((exp (function-lambda-expression fname)))
  218. (and exp (return (second exp)))))
  219. (ignore-errors
  220. (return (ext:arglist fname)))
  221. :not-available)))
  222. (defimplementation macroexpand-all (form &optional env)
  223. (declare (ignore env))
  224. (ext:expand-form form))
  225. (defimplementation collect-macro-forms (form &optional env)
  226. ;; Currently detects only normal macros, not compiler macros.
  227. (declare (ignore env))
  228. (with-collected-macro-forms (macro-forms)
  229. (handler-bind ((warning #'muffle-warning))
  230. (ignore-errors
  231. (compile nil `(lambda () ,form))))
  232. (values macro-forms nil)))
  233. (defimplementation describe-symbol-for-emacs (symbol)
  234. "Return a plist describing SYMBOL.
  235. Return NIL if the symbol is unbound."
  236. (let ((result ()))
  237. (flet ((doc (kind)
  238. (or (documentation symbol kind) :not-documented))
  239. (maybe-push (property value)
  240. (when value
  241. (setf result (list* property value result)))))
  242. (maybe-push :variable (when (boundp symbol) (doc 'variable)))
  243. (when (fboundp symbol)
  244. (maybe-push
  245. ;; Report WHEN etc. as macros, even though they may be
  246. ;; implemented as special operators.
  247. (if (macro-function symbol) :macro
  248. (typecase (fdefinition symbol)
  249. (generic-function :generic-function)
  250. (function :function)
  251. ;; (type-of 'progn) -> ext:special-operator
  252. (t :special-operator)))
  253. (doc 'function)))
  254. (when (or (get symbol 'system::setf-function) ; e.g. #'(setf elt)
  255. (get symbol 'system::setf-expander)); defsetf
  256. (maybe-push :setf (doc 'setf)))
  257. (when (or (get symbol 'system::type-symbol); cf. clisp/src/describe.lisp
  258. (get symbol 'system::defstruct-description)
  259. (get symbol 'system::deftype-expander))
  260. (maybe-push :type (doc 'type))) ; even for 'structure
  261. (when (find-class symbol nil)
  262. (maybe-push :class (doc 'type)))
  263. ;; Let this code work compiled in images without FFI
  264. (let ((types (load-time-value
  265. (and (find-package "FFI")
  266. (symbol-value
  267. (find-symbol "*C-TYPE-TABLE*" "FFI"))))))
  268. ;; Use ffi::*c-type-table* so as not to suffer the overhead of
  269. ;; (ignore-errors (ffi:parse-c-type symbol)) for 99.9% of symbols
  270. ;; which are not FFI type names.
  271. (when (and types (nth-value 1 (gethash symbol types)))
  272. ;; Maybe use (case (head (ffi:deparse-c-type)))
  273. ;; to distinguish struct and union types?
  274. (maybe-push :alien-type :not-documented)))
  275. result)))
  276. (defimplementation describe-definition (symbol namespace)
  277. (ecase namespace
  278. (:variable (describe symbol))
  279. (:macro (describe (macro-function symbol)))
  280. (:function (describe (symbol-function symbol)))
  281. (:class (describe (find-class symbol)))))
  282. (defimplementation type-specifier-p (symbol)
  283. (or (ignore-errors
  284. (subtypep nil symbol))
  285. (not (eq (type-specifier-arglist symbol) :not-available))))
  286. (defun fspec-pathname (spec)
  287. (let ((path spec)
  288. type
  289. lines)
  290. (when (consp path)
  291. (psetq type (car path)
  292. path (cadr path)
  293. lines (cddr path)))
  294. (when (and path
  295. (member (pathname-type path)
  296. custom:*compiled-file-types* :test #'equal))
  297. (setq path
  298. (loop for suffix in custom:*source-file-types*
  299. thereis (probe-file (make-pathname :defaults path
  300. :type suffix)))))
  301. (values path type lines)))
  302. (defun fspec-location (name fspec)
  303. (multiple-value-bind (file type lines)
  304. (fspec-pathname fspec)
  305. (list (if type (list name type) name)
  306. (cond (file
  307. (multiple-value-bind (truename c)
  308. (ignore-errors (truename file))
  309. (cond (truename
  310. (make-location
  311. (list :file (namestring truename))
  312. (if (consp lines)
  313. (list* :line lines)
  314. (list :function-name (string name)))
  315. (when (consp type)
  316. (list :snippet (format nil "~A" type)))))
  317. (t (list :error (princ-to-string c))))))
  318. (t (list :error
  319. (format nil "No source information available for: ~S"
  320. fspec)))))))
  321. (defimplementation find-definitions (name)
  322. (mapcar #'(lambda (e) (fspec-location name e))
  323. (documentation name 'sys::file)))
  324. (defun trim-whitespace (string)
  325. (string-trim #(#\newline #\space #\tab) string))
  326. (defvar *sldb-backtrace*)
  327. (defun sldb-backtrace ()
  328. "Return a list ((ADDRESS . DESCRIPTION) ...) of frames."
  329. (let* ((modes '((:all-stack-elements 1)
  330. (:all-frames 2)
  331. (:only-lexical-frames 3)
  332. (:only-eval-and-apply-frames 4)
  333. (:only-apply-frames 5)))
  334. (mode (cadr (assoc :all-stack-elements modes))))
  335. (do ((frames '())
  336. (last nil frame)
  337. (frame (sys::the-frame)
  338. (sys::frame-up 1 frame mode)))
  339. ((eq frame last) (nreverse frames))
  340. (unless (boring-frame-p frame)
  341. (push frame frames)))))
  342. (defimplementation call-with-debugging-environment (debugger-loop-fn)
  343. (let* (;;(sys::*break-count* (1+ sys::*break-count*))
  344. ;;(sys::*driver* debugger-loop-fn)
  345. ;;(sys::*fasoutput-stream* nil)
  346. (*sldb-backtrace*
  347. (let* ((f (sys::the-frame))
  348. (bt (sldb-backtrace))
  349. (rest (member f bt)))
  350. (if rest (nthcdr 8 rest) bt))))
  351. (funcall debugger-loop-fn)))
  352. (defun nth-frame (index)
  353. (nth index *sldb-backtrace*))
  354. (defun boring-frame-p (frame)
  355. (member (frame-type frame) '(stack-value bind-var bind-env
  356. compiled-tagbody compiled-block)))
  357. (defun frame-to-string (frame)
  358. (with-output-to-string (s)
  359. (sys::describe-frame s frame)))
  360. (defun frame-type (frame)
  361. ;; FIXME: should bind *print-length* etc. to small values.
  362. (frame-string-type (frame-to-string frame)))
  363. ;; FIXME: they changed the layout in 2.44 and not all patterns have
  364. ;; been updated.
  365. (defvar *frame-prefixes*
  366. '(("\\[[0-9]\\+\\] frame binding variables" bind-var)
  367. ("<1> #<compiled-function" compiled-fun)
  368. ("<1> #<system-function" sys-fun)
  369. ("<1> #<special-operator" special-op)
  370. ("EVAL frame" eval)
  371. ("APPLY frame" apply)
  372. ("\\[[0-9]\\+\\] compiled tagbody frame" compiled-tagbody)
  373. ("\\[[0-9]\\+\\] compiled block frame" compiled-block)
  374. ("block frame" block)
  375. ("nested block frame" block)
  376. ("tagbody frame" tagbody)
  377. ("nested tagbody frame" tagbody)
  378. ("catch frame" catch)
  379. ("handler frame" handler)
  380. ("unwind-protect frame" unwind-protect)
  381. ("driver frame" driver)
  382. ("\\[[0-9]\\+\\] frame binding environments" bind-env)
  383. ("CALLBACK frame" callback)
  384. ("- " stack-value)
  385. ("<1> " fun)
  386. ("<2> " 2nd-frame)
  387. ))
  388. (defun frame-string-type (string)
  389. (cadr (assoc-if (lambda (pattern) (is-prefix-p pattern string))
  390. *frame-prefixes*)))
  391. (defimplementation compute-backtrace (start end)
  392. (let* ((bt *sldb-backtrace*)
  393. (len (length bt)))
  394. (loop for f in (subseq bt start (min (or end len) len))
  395. collect f)))
  396. (defimplementation print-frame (frame stream)
  397. (let* ((str (frame-to-string frame)))
  398. (write-string (extract-frame-line str)
  399. stream)))
  400. (defun extract-frame-line (frame-string)
  401. (let ((s frame-string))
  402. (trim-whitespace
  403. (case (frame-string-type s)
  404. ((eval special-op)
  405. (string-match "EVAL frame .*for form \\(.*\\)" s 1))
  406. (apply
  407. (string-match "APPLY frame for call \\(.*\\)" s 1))
  408. ((compiled-fun sys-fun fun)
  409. (extract-function-name s))
  410. (t s)))))
  411. (defun extract-function-name (string)
  412. (let ((1st (car (split-frame-string string))))
  413. (or (string-match (format nil "^<1>[ ~%]*#<[-A-Za-z]* \\(.*\\)>")
  414. 1st
  415. 1)
  416. (string-match (format nil "^<1>[ ~%]*\\(.*\\)") 1st 1)
  417. 1st)))
  418. (defun split-frame-string (string)
  419. (let ((rx (format nil "~%\\(~{~A~^\\|~}\\)"
  420. (mapcar #'car *frame-prefixes*))))
  421. (loop for pos = 0 then (1+ (regexp:match-start match))
  422. for match = (regexp:match rx string :start pos)
  423. if match collect (subseq string pos (regexp:match-start match))
  424. else collect (subseq string pos)
  425. while match)))
  426. (defun string-match (pattern string n)
  427. (let* ((match (nth-value n (regexp:match pattern string))))
  428. (if match (regexp:match-string string match))))
  429. (defimplementation eval-in-frame (form frame-number)
  430. (sys::eval-at (nth-frame frame-number) form))
  431. (defimplementation frame-locals (frame-number)
  432. (let ((frame (nth-frame frame-number)))
  433. (loop for i below (%frame-count-vars frame)
  434. collect (list :name (%frame-var-name frame i)
  435. :value (%frame-var-value frame i)
  436. :id 0))))
  437. (defimplementation frame-var-value (frame var)
  438. (%frame-var-value (nth-frame frame) var))
  439. ;;; Interpreter-Variablen-Environment has the shape
  440. ;;; NIL or #(v1 val1 ... vn valn NEXT-ENV).
  441. (defun %frame-count-vars (frame)
  442. (cond ((sys::eval-frame-p frame)
  443. (do ((venv (frame-venv frame) (next-venv venv))
  444. (count 0 (+ count (/ (1- (length venv)) 2))))
  445. ((not venv) count)))
  446. ((member (frame-type frame) '(compiled-fun sys-fun fun special-op))
  447. (length (%parse-stack-values frame)))
  448. (t 0)))
  449. (defun %frame-var-name (frame i)
  450. (cond ((sys::eval-frame-p frame)
  451. (nth-value 0 (venv-ref (frame-venv frame) i)))
  452. (t (format nil "~D" i))))
  453. (defun %frame-var-value (frame i)
  454. (cond ((sys::eval-frame-p frame)
  455. (let ((name (venv-ref (frame-venv frame) i)))
  456. (multiple-value-bind (v c) (ignore-errors (sys::eval-at frame name))
  457. (if c
  458. (format-sldb-condition c)
  459. v))))
  460. ((member (frame-type frame) '(compiled-fun sys-fun fun special-op))
  461. (let ((str (nth i (%parse-stack-values frame))))
  462. (trim-whitespace (subseq str 2))))
  463. (t (break "Not implemented"))))
  464. (defun frame-venv (frame)
  465. (let ((env (sys::eval-at frame '(sys::the-environment))))
  466. (svref env 0)))
  467. (defun next-venv (venv) (svref venv (1- (length venv))))
  468. (defun venv-ref (env i)
  469. "Reference the Ith binding in ENV.
  470. Return two values: NAME and VALUE"
  471. (let ((idx (* i 2)))
  472. (if (< idx (1- (length env)))
  473. (values (svref env idx) (svref env (1+ idx)))
  474. (venv-ref (next-venv env) (- i (/ (1- (length env)) 2))))))
  475. (defun %parse-stack-values (frame)
  476. (labels ((next (fp) (sys::frame-down 1 fp 1))
  477. (parse (fp accu)
  478. (let ((str (frame-to-string fp)))
  479. (cond ((is-prefix-p "- " str)
  480. (parse (next fp) (cons str accu)))
  481. ((is-prefix-p "<1> " str)
  482. ;;(when (eq (frame-type frame) 'compiled-fun)
  483. ;; (pop accu))
  484. (dolist (str (cdr (split-frame-string str)))
  485. (when (is-prefix-p "- " str)
  486. (push str accu)))
  487. (nreverse accu))
  488. (t (parse (next fp) accu))))))
  489. (parse (next frame) '())))
  490. (defun is-prefix-p (regexp string)
  491. (if (regexp:match (concatenate 'string "^" regexp) string) t))
  492. (defimplementation return-from-frame (index form)
  493. (sys::return-from-eval-frame (nth-frame index) form))
  494. (defimplementation restart-frame (index)
  495. (sys::redo-eval-frame (nth-frame index)))
  496. (defimplementation frame-source-location (index)
  497. `(:error
  498. ,(format nil "frame-source-location not implemented. (frame: ~A)"
  499. (nth-frame index))))
  500. ;;;; Profiling
  501. (defimplementation profile (fname)
  502. (eval `(swank-monitor:monitor ,fname))) ;monitor is a macro
  503. (defimplementation profiled-functions ()
  504. swank-monitor:*monitored-functions*)
  505. (defimplementation unprofile (fname)
  506. (eval `(swank-monitor:unmonitor ,fname))) ;unmonitor is a macro
  507. (defimplementation unprofile-all ()
  508. (swank-monitor:unmonitor))
  509. (defimplementation profile-report ()
  510. (swank-monitor:report-monitoring))
  511. (defimplementation profile-reset ()
  512. (swank-monitor:reset-all-monitoring))
  513. (defimplementation profile-package (package callers-p methods)
  514. (declare (ignore callers-p methods))
  515. (swank-monitor:monitor-all package))
  516. ;;;; Handle compiler conditions (find out location of error etc.)
  517. (defmacro compile-file-frobbing-notes ((&rest args) &body body)
  518. "Pass ARGS to COMPILE-FILE, send the compiler notes to
  519. *STANDARD-INPUT* and frob them in BODY."
  520. `(let ((*error-output* (make-string-output-stream))
  521. (*compile-verbose* t))
  522. (multiple-value-prog1
  523. (compile-file ,@args)
  524. (handler-case
  525. (with-input-from-string
  526. (*standard-input* (get-output-stream-string *error-output*))
  527. ,@body)
  528. (sys::simple-end-of-file () nil)))))
  529. (defvar *orig-c-warn* (symbol-function 'system::c-warn))
  530. (defvar *orig-c-style-warn* (symbol-function 'system::c-style-warn))
  531. (defvar *orig-c-error* (symbol-function 'system::c-error))
  532. (defvar *orig-c-report-problems* (symbol-function 'system::c-report-problems))
  533. (defmacro dynamic-flet (names-functions &body body)
  534. "(dynamic-flet ((NAME FUNCTION) ...) BODY ...)
  535. Execute BODY with NAME's function slot set to FUNCTION."
  536. `(ext:letf* ,(loop for (name function) in names-functions
  537. collect `((symbol-function ',name) ,function))
  538. ,@body))
  539. (defvar *buffer-name* nil)
  540. (defvar *buffer-offset*)
  541. (defun compiler-note-location ()
  542. "Return the current compiler location."
  543. (let ((lineno1 sys::*compile-file-lineno1*)
  544. (lineno2 sys::*compile-file-lineno2*)
  545. (file sys::*compile-file-truename*))
  546. (cond ((and file lineno1 lineno2)
  547. (make-location (list ':file (namestring file))
  548. (list ':line lineno1)))
  549. (*buffer-name*
  550. (make-location (list ':buffer *buffer-name*)
  551. (list ':offset *buffer-offset* 0)))
  552. (t
  553. (list :error "No error location available")))))
  554. (defun signal-compiler-warning (cstring args severity orig-fn)
  555. (signal 'compiler-condition
  556. :severity severity
  557. :message (apply #'format nil cstring args)
  558. :location (compiler-note-location))
  559. (apply orig-fn cstring args))
  560. (defun c-warn (cstring &rest args)
  561. (signal-compiler-warning cstring args :warning *orig-c-warn*))
  562. (defun c-style-warn (cstring &rest args)
  563. (dynamic-flet ((sys::c-warn *orig-c-warn*))
  564. (signal-compiler-warning cstring args :style-warning *orig-c-style-warn*)))
  565. (defun c-error (&rest args)
  566. (signal 'compiler-condition
  567. :severity :error
  568. :message (apply #'format nil
  569. (if (= (length args) 3)
  570. (cdr args)
  571. args))
  572. :location (compiler-note-location))
  573. (apply *orig-c-error* args))
  574. (defimplementation call-with-compilation-hooks (function)
  575. (handler-bind ((warning #'handle-notification-condition))
  576. (dynamic-flet ((system::c-warn #'c-warn)
  577. (system::c-style-warn #'c-style-warn)
  578. (system::c-error #'c-error))
  579. (funcall function))))
  580. (defun handle-notification-condition (condition)
  581. "Handle a condition caused by a compiler warning."
  582. (signal 'compiler-condition
  583. :original-condition condition
  584. :severity :warning
  585. :message (princ-to-string condition)
  586. :location (compiler-note-location)))
  587. (defimplementation swank-compile-file (input-file output-file
  588. load-p external-format
  589. &key policy)
  590. (declare (ignore policy))
  591. (with-compilation-hooks ()
  592. (with-compilation-unit ()
  593. (multiple-value-bind (fasl-file warningsp failurep)
  594. (compile-file input-file
  595. :output-file output-file
  596. :external-format external-format)
  597. (values fasl-file warningsp
  598. (or failurep
  599. (and load-p
  600. (not (load fasl-file)))))))))
  601. (defimplementation swank-compile-string (string &key buffer position filename
  602. policy)
  603. (declare (ignore filename policy))
  604. (with-compilation-hooks ()
  605. (let ((*buffer-name* buffer)
  606. (*buffer-offset* position))
  607. (funcall (compile nil (read-from-string
  608. (format nil "(~S () ~A)" 'lambda string))))
  609. t)))
  610. ;;;; Portable XREF from the CMU AI repository.
  611. (setq pxref::*handle-package-forms* '(cl:in-package))
  612. (defmacro defxref (name function)
  613. `(defimplementation ,name (name)
  614. (xref-results (,function name))))
  615. (defxref who-calls pxref:list-callers)
  616. (defxref who-references pxref:list-readers)
  617. (defxref who-binds pxref:list-setters)
  618. (defxref who-sets pxref:list-setters)
  619. (defxref list-callers pxref:list-callers)
  620. (defxref list-callees pxref:list-callees)
  621. (defun xref-results (symbols)
  622. (let ((xrefs '()))
  623. (dolist (symbol symbols)
  624. (push (fspec-location symbol symbol) xrefs))
  625. xrefs))
  626. (when (find-package :swank-loader)
  627. (setf (symbol-function (intern "USER-INIT-FILE" :swank-loader))
  628. (lambda ()
  629. (let ((home (user-homedir-pathname)))
  630. (and (ext:probe-directory home)
  631. (probe-file (format nil "~A/.swank.lisp"
  632. (namestring (truename home)))))))))
  633. ;;; Don't set *debugger-hook* to nil on break.
  634. (ext:without-package-lock ()
  635. (defun break (&optional (format-string "Break") &rest args)
  636. (if (not sys::*use-clcs*)
  637. (progn
  638. (terpri *error-output*)
  639. (apply #'format *error-output*
  640. (concatenate 'string "*** - " format-string)
  641. args)
  642. (funcall ext:*break-driver* t))
  643. (let ((condition
  644. (make-condition 'simple-condition
  645. :format-control format-string
  646. :format-arguments args))
  647. ;;(*debugger-hook* nil)
  648. ;; Issue 91
  649. )
  650. (ext:with-restarts
  651. ((continue
  652. :report (lambda (stream)
  653. (format stream (sys::text "Return from ~S loop")
  654. 'break))
  655. ()))
  656. (with-condition-restarts condition (list (find-restart 'continue))
  657. (invoke-debugger condition)))))
  658. nil))
  659. ;;;; Inspecting
  660. (defmethod emacs-inspect ((o t))
  661. (let* ((*print-array* nil) (*print-pretty* t)
  662. (*print-circle* t) (*print-escape* t)
  663. (*print-lines* custom:*inspect-print-lines*)
  664. (*print-level* custom:*inspect-print-level*)
  665. (*print-length* custom:*inspect-print-length*)
  666. (sys::*inspect-all* (make-array 10 :fill-pointer 0 :adjustable t))
  667. (tmp-pack (make-package (gensym "INSPECT-TMP-PACKAGE-")))
  668. (*package* tmp-pack)
  669. (sys::*inspect-unbound-value* (intern "#<unbound>" tmp-pack)))
  670. (let ((inspection (sys::inspect-backend o)))
  671. (append (list
  672. (format nil "~S~% ~A~{~%~A~}~%" o
  673. (sys::insp-title inspection)
  674. (sys::insp-blurb inspection)))
  675. (loop with count = (sys::insp-num-slots inspection)
  676. for i below count
  677. append (multiple-value-bind (value name)
  678. (funcall (sys::insp-nth-slot inspection)
  679. i)
  680. `((:value ,name) " = " (:value ,value)
  681. (:newline))))))))
  682. (defimplementation quit-lisp ()
  683. #+lisp=cl (ext:quit)
  684. #-lisp=cl (lisp:quit))
  685. (defimplementation preferred-communication-style ()
  686. nil)
  687. ;;; FIXME
  688. ;;;
  689. ;;; Clisp 2.48 added experimental support for threads. Basically, you
  690. ;;; can use :SPAWN now, BUT:
  691. ;;;
  692. ;;; - there are problems with GC, and threads stuffed into weak
  693. ;;; hash-tables as is the case for *THREAD-PLIST-TABLE*.
  694. ;;;
  695. ;;; See test case at
  696. ;;; http://thread.gmane.org/gmane.lisp.clisp.devel/20429
  697. ;;;
  698. ;;; Even though said to be fixed, it's not:
  699. ;;;
  700. ;;; http://thread.gmane.org/gmane.lisp.clisp.devel/20429/focus=20443
  701. ;;;
  702. ;;; - The DYNAMIC-FLET above is an implementation technique that's
  703. ;;; probably not sustainable in light of threads. This got to be
  704. ;;; rewritten.
  705. ;;;
  706. ;;; TCR (2009-07-30)
  707. #+#.(cl:if (cl:find-package "MP") '(:and) '(:or))
  708. (progn
  709. (defimplementation spawn (fn &key name)
  710. (mp:make-thread fn :name name))
  711. (defvar *thread-plist-table-lock*
  712. (mp:make-mutex :name "THREAD-PLIST-TABLE-LOCK"))
  713. (defvar *thread-plist-table* (make-hash-table :weak :key)
  714. "A hashtable mapping threads to a plist.")
  715. (defvar *thread-id-counter* 0)
  716. (defimplementation thread-id (thread)
  717. (mp:with-mutex-lock (*thread-plist-table-lock*)
  718. (or (getf (gethash thread *thread-plist-table*) 'thread-id)
  719. (setf (getf (gethash thread *thread-plist-table*) 'thread-id)
  720. (incf *thread-id-counter*)))))
  721. (defimplementation find-thread (id)
  722. (find id (all-threads)
  723. :key (lambda (thread)
  724. (getf (gethash thread *thread-plist-table*) 'thread-id))))
  725. (defimplementation thread-name (thread)
  726. ;; To guard against returning #<UNBOUND>.
  727. (princ-to-string (mp:thread-name thread)))
  728. (defimplementation thread-status (thread)
  729. (if (thread-alive-p thread)
  730. "RUNNING"
  731. "STOPPED"))
  732. (defimplementation make-lock (&key name)
  733. (mp:make-mutex :name name :recursive-p t))
  734. (defimplementation call-with-lock-held (lock function)
  735. (mp:with-mutex-lock (lock)
  736. (funcall function)))
  737. (defimplementation current-thread ()
  738. (mp:current-thread))
  739. (defimplementation all-threads ()
  740. (mp:list-threads))
  741. (defimplementation interrupt-thread (thread fn)
  742. (mp:thread-interrupt thread :function fn))
  743. (defimplementation kill-thread (thread)
  744. (mp:thread-interrupt thread :function t))
  745. (defimplementation thread-alive-p (thread)
  746. (mp:thread-active-p thread))
  747. (defvar *mailboxes-lock* (make-lock :name "MAILBOXES-LOCK"))
  748. (defvar *mailboxes* (list))
  749. (defstruct (mailbox (:conc-name mailbox.))
  750. thread
  751. (lock (make-lock :name "MAILBOX.LOCK"))
  752. (waitqueue (mp:make-exemption :name "MAILBOX.WAITQUEUE"))
  753. (queue '() :type list))
  754. (defun mailbox (thread)
  755. "Return THREAD's mailbox."
  756. (mp:with-mutex-lock (*mailboxes-lock*)
  757. (or (find thread *mailboxes* :key #'mailbox.thread)
  758. (let ((mb (make-mailbox :thread thread)))
  759. (push mb *mailboxes*)
  760. mb))))
  761. (defimplementation send (thread message)
  762. (let* ((mbox (mailbox thread))
  763. (lock (mailbox.lock mbox)))
  764. (mp:with-mutex-lock (lock)
  765. (setf (mailbox.queue mbox)
  766. (nconc (mailbox.queue mbox) (list message)))
  767. (mp:exemption-broadcast (mailbox.waitqueue mbox)))))
  768. (defimplementation receive-if (test &optional timeout)
  769. (let* ((mbox (mailbox (current-thread)))
  770. (lock (mailbox.lock mbox)))
  771. (assert (or (not timeout) (eq timeout t)))
  772. (loop
  773. (check-slime-interrupts)
  774. (mp:with-mutex-lock (lock)
  775. (let* ((q (mailbox.queue mbox))
  776. (tail (member-if test q)))
  777. (when tail
  778. (setf (mailbox.queue mbox) (nconc (ldiff q tail) (cdr tail)))
  779. (return (car tail))))
  780. (when (eq timeout t) (return (values nil t)))
  781. (mp:exemption-wait (mailbox.waitqueue mbox) lock :timeout 0.2))))))
  782. ;;;; Weak hashtables
  783. (defimplementation make-weak-key-hash-table (&rest args)
  784. (apply #'make-hash-table :weak :key args))
  785. (defimplementation make-weak-value-hash-table (&rest args)
  786. (apply #'make-hash-table :weak :value args))
  787. (defimplementation save-image (filename &optional restart-function)
  788. (let ((args `(,filename
  789. ,@(if restart-function
  790. `((:init-function ,restart-function))))))
  791. (apply #'ext:saveinitmem args)))