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.

1098 lines
34 KiB

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