|
|
- ;;;; -*- indent-tabs-mode: nil; outline-regexp: ";;;;;* "; -*-
- ;;;
- ;;; swank-allegro.lisp --- Allegro CL specific code for SLIME.
- ;;;
- ;;; Created 2003
- ;;;
- ;;; This code has been placed in the Public Domain. All warranties
- ;;; are disclaimed.
- ;;;
-
- (defpackage swank/allegro
- (:use cl swank/backend))
-
- (in-package swank/allegro)
-
- (eval-when (:compile-toplevel :load-toplevel :execute)
- (require :sock)
- (require :process)
- #+(version>= 8 2)
- (require 'lldb))
-
- (defimplementation gray-package-name ()
- '#:excl)
-
- ;;; swank-mop
-
- (import-swank-mop-symbols :clos '(:slot-definition-documentation))
-
- (defun swank-mop:slot-definition-documentation (slot)
- (documentation slot t))
-
- ;;;; UTF8
-
- (define-symbol-macro utf8-ef
- (load-time-value
- (excl:crlf-base-ef (excl:find-external-format :utf-8))
- t))
-
- (defimplementation string-to-utf8 (s)
- (excl:string-to-octets s :external-format utf8-ef
- :null-terminate nil))
-
- (defimplementation utf8-to-string (u)
- (excl:octets-to-string u :external-format utf8-ef))
-
- ;;;; TCP Server
-
- (defimplementation preferred-communication-style ()
- :spawn)
-
- (defimplementation create-socket (host port &key backlog)
- (socket:make-socket :connect :passive :local-port port
- :local-host host :reuse-address t
- :backlog (or backlog 5)))
-
- (defimplementation local-port (socket)
- (socket:local-port socket))
-
- (defimplementation close-socket (socket)
- (close socket))
-
- (defimplementation accept-connection (socket &key external-format buffering
- timeout)
- (declare (ignore buffering timeout))
- (let ((s (socket:accept-connection socket :wait t)))
- (when external-format
- (setf (stream-external-format s) external-format))
- s))
-
- (defimplementation socket-fd (stream)
- (excl::stream-input-handle stream))
-
- (defvar *external-format-to-coding-system*
- '((:iso-8859-1
- "latin-1" "latin-1-unix" "iso-latin-1-unix"
- "iso-8859-1" "iso-8859-1-unix")
- (:utf-8 "utf-8" "utf-8-unix")
- (:euc-jp "euc-jp" "euc-jp-unix")
- (:us-ascii "us-ascii" "us-ascii-unix")
- (:emacs-mule "emacs-mule" "emacs-mule-unix")))
-
- (defimplementation find-external-format (coding-system)
- (let ((e (rassoc-if (lambda (x) (member coding-system x :test #'equal))
- *external-format-to-coding-system*)))
- (and e (excl:crlf-base-ef
- (excl:find-external-format (car e)
- :try-variant t)))))
-
- ;;;; Unix signals
-
- (defimplementation getpid ()
- (excl::getpid))
-
- (defimplementation lisp-implementation-type-name ()
- "allegro")
-
- (defimplementation set-default-directory (directory)
- (let* ((dir (namestring (truename (merge-pathnames directory)))))
- (setf *default-pathname-defaults* (pathname (excl:chdir dir)))
- dir))
-
- (defimplementation default-directory ()
- (namestring (excl:current-directory)))
-
- ;;;; Misc
-
- (defimplementation arglist (symbol)
- (handler-case (excl:arglist symbol)
- (simple-error () :not-available)))
-
- (defimplementation macroexpand-all (form &optional env)
- (declare (ignore env))
- #+(version>= 8 0)
- (excl::walk-form form)
- #-(version>= 8 0)
- (excl::walk form))
-
- (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)))))
-
- (defimplementation type-specifier-p (symbol)
- (or (ignore-errors
- (subtypep nil symbol))
- (not (eq (type-specifier-arglist symbol) :not-available))))
-
- (defimplementation function-name (f)
- (check-type f function)
- (cross-reference::object-to-function-name f))
-
- ;;;; Debugger
-
- (defvar *sldb-topframe*)
-
- (defimplementation call-with-debugging-environment (debugger-loop-fn)
- (let ((*sldb-topframe* (find-topframe))
- (excl::*break-hook* nil))
- (funcall debugger-loop-fn)))
-
- (defimplementation sldb-break-at-start (fname)
- ;; :print-before is kind of mis-used but we just want to stuff our
- ;; break form somewhere. This does not work for setf, :before and
- ;; :after methods, which need special syntax in the trace call, see
- ;; ACL's doc/debugging.htm chapter 10.
- (eval `(trace (,fname
- :print-before
- ((break "Function start breakpoint of ~A" ',fname)))))
- `(:ok ,(format nil "Set breakpoint at start of ~S" fname)))
-
- (defun find-topframe ()
- (let ((magic-symbol (intern (symbol-name :swank-debugger-hook)
- (find-package :swank)))
- (top-frame (excl::int-newest-frame (excl::current-thread))))
- (loop for frame = top-frame then (next-frame frame)
- for i from 0
- while (and frame (< i 30))
- when (eq (debugger:frame-name frame) magic-symbol)
- return (next-frame frame)
- finally (return top-frame))))
-
- (defun next-frame (frame)
- (let ((next (excl::int-next-older-frame frame)))
- (cond ((not next) nil)
- ((debugger:frame-visible-p next) next)
- (t (next-frame next)))))
-
- (defun nth-frame (index)
- (do ((frame *sldb-topframe* (next-frame frame))
- (i index (1- i)))
- ((zerop i) frame)))
-
- (defimplementation compute-backtrace (start end)
- (let ((end (or end most-positive-fixnum)))
- (loop for f = (nth-frame start) then (next-frame f)
- for i from start below end
- while f collect f)))
-
- (defimplementation print-frame (frame stream)
- (debugger:output-frame stream frame :moderate))
-
- (defimplementation frame-locals (index)
- (let ((frame (nth-frame index)))
- (loop for i from 0 below (debugger:frame-number-vars frame)
- collect (list :name (debugger:frame-var-name frame i)
- :id 0
- :value (debugger:frame-var-value frame i)))))
-
- (defimplementation frame-var-value (frame var)
- (let ((frame (nth-frame frame)))
- (debugger:frame-var-value frame var)))
-
- (defimplementation disassemble-frame (index)
- (let ((frame (nth-frame index)))
- (multiple-value-bind (x fun xx xxx pc) (debugger::dyn-fd-analyze frame)
- (format t "pc: ~d (~s ~s ~s)~%fun: ~a~%" pc x xx xxx fun)
- (disassemble (debugger:frame-function frame)))))
-
- (defimplementation frame-source-location (index)
- (let* ((frame (nth-frame index)))
- (multiple-value-bind (x fun xx xxx pc) (debugger::dyn-fd-analyze frame)
- (declare (ignore x xx xxx))
- (cond ((and pc
- #+(version>= 8 2)
- (pc-source-location fun pc)
- #-(version>= 8 2)
- (function-source-location fun)))
- (t ; frames for unbound functions etc end up here
- (cadr (car (fspec-definition-locations
- (car (debugger:frame-expression frame))))))))))
-
- (defun function-source-location (fun)
- (cadr (car (fspec-definition-locations
- (xref::object-to-function-name fun)))))
-
- #+(version>= 8 2)
- (defun pc-source-location (fun pc)
- (let* ((debug-info (excl::function-source-debug-info fun)))
- (cond ((not debug-info)
- (function-source-location fun))
- (t
- (let* ((code-loc (find-if (lambda (c)
- (<= (- pc (sys::natural-width))
- (let ((x (excl::ldb-code-pc c)))
- (or x -1))
- pc))
- debug-info)))
- (cond ((not code-loc)
- (ldb-code-to-src-loc (aref debug-info 0)))
- (t
- (ldb-code-to-src-loc code-loc))))))))
-
- #+(version>= 8 2)
- (defun ldb-code-to-src-loc (code)
- (declare (optimize debug))
- (let* ((func (excl::ldb-code-func code))
- (debug-info (excl::function-source-debug-info func))
- (start (loop for i from (excl::ldb-code-index code) downto 0
- for bpt = (aref debug-info i)
- for start = (excl::ldb-code-start-char bpt)
- when start
- return (if (listp start)
- (first start)
- start)))
- (src-file (excl:source-file func)))
- (cond (start
- (buffer-or-file-location src-file start))
- (func
- (let* ((debug-info (excl::function-source-debug-info func))
- (whole (aref debug-info 0))
- (paths (source-paths-of (excl::ldb-code-source whole)
- (excl::ldb-code-source code)))
- (path (if paths (longest-common-prefix paths) '()))
- (start 0))
- (buffer-or-file
- src-file
- (lambda (file)
- (make-location `(:file ,file)
- `(:source-path (0 . ,path) ,start)))
- (lambda (buffer bstart)
- (make-location `(:buffer ,buffer)
- `(:source-path (0 . ,path)
- ,(+ bstart start)))))))
- (t
- nil))))
-
- (defun longest-common-prefix (sequences)
- (assert sequences)
- (flet ((common-prefix (s1 s2)
- (let ((diff-pos (mismatch s1 s2)))
- (if diff-pos (subseq s1 0 diff-pos) s1))))
- (reduce #'common-prefix sequences)))
-
- (defun source-paths-of (whole part)
- (let ((result '()))
- (labels ((walk (form path)
- (cond ((eq form part)
- (push (reverse path) result))
- ((consp form)
- (loop for i from 0 while (consp form) do
- (walk (pop form) (cons i path)))))))
- (walk whole '())
- (reverse result))))
-
- (defimplementation eval-in-frame (form frame-number)
- (let ((frame (nth-frame frame-number)))
- ;; let-bind lexical variables
- (let ((vars (loop for i below (debugger:frame-number-vars frame)
- for name = (debugger:frame-var-name frame i)
- if (typep name '(and symbol (not null) (not keyword)))
- collect `(,name ',(debugger:frame-var-value frame i)))))
- (debugger:eval-form-in-context
- `(let* ,vars ,form)
- (debugger:environment-of-frame frame)))))
-
- (defimplementation frame-package (frame-number)
- (let* ((frame (nth-frame frame-number))
- (exp (debugger:frame-expression frame)))
- (typecase exp
- ((cons symbol) (symbol-package (car exp)))
- ((cons (cons (eql :internal) (cons symbol)))
- (symbol-package (cadar exp))))))
-
- (defimplementation return-from-frame (frame-number form)
- (let ((frame (nth-frame frame-number)))
- (multiple-value-call #'debugger:frame-return
- frame (debugger:eval-form-in-context
- form
- (debugger:environment-of-frame frame)))))
-
- (defimplementation frame-restartable-p (frame)
- (handler-case (debugger:frame-retryable-p frame)
- (serious-condition (c)
- (funcall (read-from-string "swank::background-message")
- "~a ~a" frame (princ-to-string c))
- nil)))
-
- (defimplementation restart-frame (frame-number)
- (let ((frame (nth-frame frame-number)))
- (cond ((debugger:frame-retryable-p frame)
- (apply #'debugger:frame-retry frame (debugger:frame-function frame)
- (cdr (debugger:frame-expression frame))))
- (t "Frame is not retryable"))))
-
- ;;;; Compiler hooks
-
- (defvar *buffer-name* nil)
- (defvar *buffer-start-position*)
- (defvar *buffer-string*)
- (defvar *compile-filename* nil)
-
- (defun compiler-note-p (object)
- (member (type-of object) '(excl::compiler-note compiler::compiler-note)))
-
- (defun redefinition-p (condition)
- (and (typep condition 'style-warning)
- (every #'char-equal "redefin" (princ-to-string condition))))
-
- (defun compiler-undefined-functions-called-warning-p (object)
- (typep object 'excl:compiler-undefined-functions-called-warning))
-
- (deftype compiler-note ()
- `(satisfies compiler-note-p))
-
- (deftype redefinition ()
- `(satisfies redefinition-p))
-
- (defun signal-compiler-condition (&rest args)
- (apply #'signal 'compiler-condition args))
-
- (defun handle-compiler-warning (condition)
- (declare (optimize (debug 3) (speed 0) (space 0)))
- (cond ((and #-(version>= 10 0) (not *buffer-name*)
- (compiler-undefined-functions-called-warning-p condition))
- (handle-undefined-functions-warning condition))
- ((and (typep condition 'excl::compiler-note)
- (let ((format (slot-value condition 'excl::format-control)))
- (and (search "Closure" format)
- (search "will be stack allocated" format))))
- ;; Ignore "Closure <foo> will be stack allocated" notes.
- ;; That occurs often but is usually uninteresting.
- )
- (t
- (signal-compiler-condition
- :original-condition condition
- :severity (etypecase condition
- (redefinition :redefinition)
- (style-warning :style-warning)
- (warning :warning)
- (compiler-note :note)
- (reader-error :read-error)
- (error :error))
- :message (format nil "~A" condition)
- :location (compiler-warning-location condition)))))
-
- (defun condition-pathname-and-position (condition)
- (let* ((context #+(version>= 10 0)
- (getf (slot-value condition 'excl::plist)
- :source-context))
- (location-available (and context
- (excl::source-context-start-char context))))
- (cond (location-available
- (values (excl::source-context-pathname context)
- (when-let (start-char (excl::source-context-start-char context))
- (let ((position (if (listp start-char) ; HACK
- (first start-char)
- start-char)))
- (if (typep condition 'excl::compiler-free-reference-warning)
- position
- (1+ position))))))
- ((typep condition 'reader-error)
- (let ((pos (car (last (slot-value condition 'excl::format-arguments))))
- (file (pathname (stream-error-stream condition))))
- (when (integerp pos)
- (values file pos))))
- (t
- (let ((loc (getf (slot-value condition 'excl::plist) :loc)))
- (when loc
- (destructuring-bind (file . pos) loc
- (let ((start (if (consp pos) ; 8.2 and newer
- #+(version>= 10 1)
- (if (typep condition 'excl::compiler-inconsistent-name-usage-warning)
- (second pos)
- (first pos))
- #-(version>= 10 1)
- (first pos)
- pos)))
- (values file start)))))))))
-
- (defun compiler-warning-location (condition)
- (multiple-value-bind (pathname position)
- (condition-pathname-and-position condition)
- (cond (*buffer-name*
- (make-location
- (list :buffer *buffer-name*)
- (if position
- (list :offset 1 (1- position))
- (list :offset *buffer-start-position* 0))))
- (pathname
- (make-location
- (list :file (namestring (truename pathname)))
- #+(version>= 10 1)
- (list :offset 1 position)
- #-(version>= 10 1)
- (list :position (1+ position))))
- (t
- (make-error-location "No error location available.")))))
-
- ;; TODO: report it as a bug to Franz that the condition's plist
- ;; slot contains (:loc nil).
- (defun handle-undefined-functions-warning (condition)
- (let ((fargs (slot-value condition 'excl::format-arguments)))
- (loop for (fname . locs) in (car fargs) do
- (dolist (loc locs)
- (multiple-value-bind (pos file) (ecase (length loc)
- (2 (values-list loc))
- (3 (destructuring-bind
- (start end file) loc
- (declare (ignore end))
- (values start file))))
- (signal-compiler-condition
- :original-condition condition
- :severity :warning
- :message (format nil "Undefined function referenced: ~S"
- fname)
- :location (make-location (list :file file)
- #+(version>= 9 0)
- (list :offset 1 pos)
- #-(version>= 9 0)
- (list :position (1+ pos)))))))))
-
- (defimplementation call-with-compilation-hooks (function)
- (handler-bind ((warning #'handle-compiler-warning)
- (compiler-note #'handle-compiler-warning)
- (reader-error #'handle-compiler-warning))
- (funcall function)))
-
- (defimplementation swank-compile-file (input-file output-file
- load-p external-format
- &key policy)
- (declare (ignore policy))
- (handler-case
- (with-compilation-hooks ()
- (let ((*buffer-name* nil)
- (*compile-filename* input-file)
- #+(version>= 8 2)
- (compiler:save-source-level-debug-info-switch t)
- (excl:*load-source-file-info* t)
- #+(version>= 8 2)
- (excl:*load-source-debug-info* t))
- (compile-file *compile-filename*
- :output-file output-file
- :load-after-compile load-p
- :external-format external-format)))
- (reader-error () (values nil nil t))))
-
- (defun call-with-temp-file (fn)
- (let ((tmpname (system:make-temp-file-name)))
- (unwind-protect
- (with-open-file (file tmpname :direction :output :if-exists :error)
- (funcall fn file tmpname))
- (delete-file tmpname))))
-
- (defvar *temp-file-map* (make-hash-table :test #'equal)
- "A mapping from tempfile names to Emacs buffer names.")
-
- (defun write-tracking-preamble (stream file file-offset)
- "Instrument the top of the temporary file to be compiled.
-
- The header tells allegro that any definitions compiled in the temp
- file should be found in FILE exactly at FILE-OFFSET. To get Allegro
- to do this, this factors in the length of the inserted header itself."
- (with-standard-io-syntax
- (let* ((*package* (find-package :keyword))
- (source-pathname-form
- `(cl:eval-when (:compile-toplevel :load-toplevel :execute)
- (cl:setq excl::*source-pathname*
- (pathname ,(sys::frob-source-file file)))))
- (source-pathname-string (write-to-string source-pathname-form))
- (position-form-length-bound 160) ; should be enough for everyone
- (header-length (+ (length source-pathname-string)
- position-form-length-bound))
- (position-form
- `(cl:eval-when (:compile-toplevel :load-toplevel :execute)
- (cl:setq excl::*partial-source-file-p* ,(- file-offset
- header-length
- 1 ; for the newline
- ))))
- (position-form-string (write-to-string position-form))
- (padding-string (make-string (- position-form-length-bound
- (length position-form-string))
- :initial-element #\;)))
- (write-string source-pathname-string stream)
- (write-string position-form-string stream)
- (write-string padding-string stream)
- (write-char #\newline stream))))
-
- (defun compile-from-temp-file (string buffer offset file)
- (call-with-temp-file
- (lambda (stream filename)
- (when (and file offset (probe-file file))
- (write-tracking-preamble stream file offset))
- (write-string string stream)
- (finish-output stream)
- (multiple-value-bind (binary-filename warnings? failure?)
- (let ((sys:*source-file-types* '(nil)) ; suppress .lisp extension
- #+(version>= 8 2)
- (compiler:save-source-level-debug-info-switch t)
- (excl:*redefinition-warnings* nil))
- (compile-file filename))
- (declare (ignore warnings?))
- (when binary-filename
- (let ((excl:*load-source-file-info* t)
- #+(version>= 8 2)
- (excl:*load-source-debug-info* t))
- excl::*source-pathname*
- (load binary-filename))
- (when (and buffer offset (or (not file)
- (not (probe-file file))))
- (setf (gethash (pathname stream) *temp-file-map*)
- (list buffer offset)))
- (delete-file binary-filename))
- (not failure?)))))
-
- (defimplementation swank-compile-string (string &key buffer position filename
- policy)
- (declare (ignore policy))
- (handler-case
- (with-compilation-hooks ()
- (let ((*buffer-name* buffer)
- (*buffer-start-position* position)
- (*buffer-string* string))
- (compile-from-temp-file string buffer position filename)))
- (reader-error () nil)))
-
- ;;;; Definition Finding
-
- (defun buffer-or-file (file file-fun buffer-fun)
- (let* ((probe (gethash file *temp-file-map*)))
- (cond (probe
- (destructuring-bind (buffer start) probe
- (funcall buffer-fun buffer start)))
- (t (funcall file-fun (namestring (truename file)))))))
-
- (defun buffer-or-file-location (file offset)
- (buffer-or-file file
- (lambda (filename)
- (make-location `(:file ,filename)
- `(:position ,(1+ offset))))
- (lambda (buffer start)
- (make-location `(:buffer ,buffer)
- `(:offset ,start ,offset)))))
-
- (defun fspec-primary-name (fspec)
- (etypecase fspec
- (symbol fspec)
- (list (fspec-primary-name (second fspec)))))
-
- (defun find-definition-in-file (fspec type file top-level)
- (let* ((part
- (or (scm::find-definition-in-definition-group
- fspec type (scm:section-file :file file)
- :top-level top-level)
- (scm::find-definition-in-definition-group
- (fspec-primary-name fspec)
- type (scm:section-file :file file)
- :top-level top-level)))
- (start (and part
- (scm::source-part-start part)))
- (pos (if start
- (list :offset 1 start)
- (list :function-name (string (fspec-primary-name fspec))))))
- (make-location (list :file (namestring (truename file)))
- pos)))
-
- (defun find-fspec-location (fspec type file top-level)
- (handler-case
- (etypecase file
- (pathname
- (let ((probe (gethash file *temp-file-map*)))
- (cond (probe
- (destructuring-bind (buffer offset) probe
- (make-location `(:buffer ,buffer)
- `(:offset ,offset 0))))
- (t
- (find-definition-in-file fspec type file top-level)))))
- ((member :top-level)
- (make-error-location "Defined at toplevel: ~A"
- (fspec->string fspec))))
- (error (e)
- (make-error-location "Error: ~A" e))))
-
- (defun fspec->string (fspec)
- (typecase fspec
- (symbol (let ((*package* (find-package :keyword)))
- (prin1-to-string fspec)))
- (list (format nil "(~A ~A)"
- (prin1-to-string (first fspec))
- (let ((*package* (find-package :keyword)))
- (prin1-to-string (second fspec)))))
- (t (princ-to-string fspec))))
-
- (defun fspec-definition-locations (fspec)
- (cond
- ((and (listp fspec) (eq (car fspec) :internal))
- (destructuring-bind (_internal next _n) fspec
- (declare (ignore _internal _n))
- (fspec-definition-locations next)))
- (t
- (let ((defs (excl::find-source-file fspec)))
- (when (and (null defs)
- (listp fspec)
- (string= (car fspec) '#:method))
- ;; If methods are defined in a defgeneric form, the source location is
- ;; recorded for the gf but not for the methods. Therefore fall back to
- ;; the gf as the likely place of definition.
- (setq defs (excl::find-source-file (second fspec))))
- (if (null defs)
- (list
- (list fspec
- (make-error-location "Unknown source location for ~A"
- (fspec->string fspec))))
- (loop for (fspec type file top-level) in defs collect
- (list (list type fspec)
- (find-fspec-location fspec type file top-level))))))))
-
- (defimplementation find-definitions (symbol)
- (fspec-definition-locations symbol))
-
- (defimplementation find-source-location (obj)
- (first (rest (first (fspec-definition-locations obj)))))
-
- ;;;; XREF
-
- (defmacro defxref (name relation name1 name2)
- `(defimplementation ,name (x)
- (xref-result (xref:get-relation ,relation ,name1 ,name2))))
-
- (defxref who-calls :calls :wild x)
- (defxref calls-who :calls x :wild)
- (defxref who-references :uses :wild x)
- (defxref who-binds :binds :wild x)
- (defxref who-macroexpands :macro-calls :wild x)
- (defxref who-sets :sets :wild x)
-
- (defun xref-result (fspecs)
- (loop for fspec in fspecs
- append (fspec-definition-locations fspec)))
-
- ;; list-callers implemented by groveling through all fbound symbols.
- ;; Only symbols are considered. Functions in the constant pool are
- ;; searched recursively. Closure environments are ignored at the
- ;; moment (constants in methods are therefore not found).
-
- (defun map-function-constants (function fn depth)
- "Call FN with the elements of FUNCTION's constant pool."
- (do ((i 0 (1+ i))
- (max (excl::function-constant-count function)))
- ((= i max))
- (let ((c (excl::function-constant function i)))
- (cond ((and (functionp c)
- (not (eq c function))
- (plusp depth))
- (map-function-constants c fn (1- depth)))
- (t
- (funcall fn c))))))
-
- (defun in-constants-p (fun symbol)
- (map-function-constants fun
- (lambda (c)
- (when (eq c symbol)
- (return-from in-constants-p t)))
- 3))
-
- (defun function-callers (name)
- (let ((callers '()))
- (do-all-symbols (sym)
- (when (fboundp sym)
- (let ((fn (fdefinition sym)))
- (when (in-constants-p fn name)
- (push sym callers)))))
- callers))
-
- (defimplementation list-callers (name)
- (xref-result (function-callers name)))
-
- (defimplementation list-callees (name)
- (let ((result '()))
- (map-function-constants (fdefinition name)
- (lambda (c)
- (when (fboundp c)
- (push c result)))
- 2)
- (xref-result result)))
-
- ;;;; Profiling
-
- ;; Per-function profiling based on description in
- ;; http://www.franz.com/support/documentation/8.0/\
- ;; doc/runtime-analyzer.htm#data-collection-control-2
-
- (defvar *profiled-functions* ())
- (defvar *profile-depth* 0)
-
- (defmacro with-redirected-y-or-n-p (&body body)
- ;; If the profiler is restarted when the data from the previous
- ;; session is not reported yet, the user is warned via Y-OR-N-P.
- ;; As the CL:Y-OR-N-P question is (for some reason) not directly
- ;; sent to the Slime user, the function CL:Y-OR-N-P is temporarily
- ;; overruled.
- `(let* ((pkg (find-package :common-lisp))
- (saved-pdl (excl::package-definition-lock pkg))
- (saved-ynp (symbol-function 'cl:y-or-n-p)))
- (setf (excl::package-definition-lock pkg) nil
- (symbol-function 'cl:y-or-n-p)
- (symbol-function (read-from-string "swank:y-or-n-p-in-emacs")))
- (unwind-protect
- (progn ,@body)
- (setf (symbol-function 'cl:y-or-n-p) saved-ynp
- (excl::package-definition-lock pkg) saved-pdl))))
-
- (defun start-acl-profiler ()
- (with-redirected-y-or-n-p
- (prof:start-profiler :type :time :count t
- :start-sampling-p nil :verbose nil)))
- (defun acl-profiler-active-p ()
- (not (eq (prof:profiler-status :verbose nil) :inactive)))
-
- (defun stop-acl-profiler ()
- (prof:stop-profiler :verbose nil))
-
- (excl:def-fwrapper profile-fwrapper (&rest args)
- ;; Ensures sampling is done during the execution of the function,
- ;; taking into account recursion.
- (declare (ignore args))
- (cond ((zerop *profile-depth*)
- (let ((*profile-depth* (1+ *profile-depth*)))
- (prof:start-sampling)
- (unwind-protect (excl:call-next-fwrapper)
- (prof:stop-sampling))))
- (t
- (excl:call-next-fwrapper))))
-
- (defimplementation profile (fname)
- (unless (acl-profiler-active-p)
- (start-acl-profiler))
- (excl:fwrap fname 'profile-fwrapper 'profile-fwrapper)
- (push fname *profiled-functions*))
-
- (defimplementation profiled-functions ()
- *profiled-functions*)
-
- (defimplementation unprofile (fname)
- (excl:funwrap fname 'profile-fwrapper)
- (setq *profiled-functions* (remove fname *profiled-functions*)))
-
- (defimplementation profile-report ()
- (prof:show-flat-profile :verbose nil)
- (when *profiled-functions*
- (start-acl-profiler)))
-
- (defimplementation profile-reset ()
- (when (acl-profiler-active-p)
- (stop-acl-profiler)
- (start-acl-profiler))
- "Reset profiling counters.")
-
- ;;;; Inspecting
-
- (excl:without-redefinition-warnings
- (defmethod emacs-inspect ((o t))
- (allegro-inspect o)))
-
- (defmethod emacs-inspect ((o function))
- (allegro-inspect o))
-
- (defmethod emacs-inspect ((o standard-object))
- (allegro-inspect o))
-
- (defun allegro-inspect (o)
- (loop for (d dd) on (inspect::inspect-ctl o)
- append (frob-allegro-field-def o d)
- until (eq d dd)))
-
- (defun frob-allegro-field-def (object def)
- (with-struct (inspect::field-def- name type access) def
- (ecase type
- ((:unsigned-word :unsigned-byte :unsigned-natural
- :unsigned-long :unsigned-half-long
- :unsigned-3byte :unsigned-long32)
- (label-value-line name (inspect::component-ref-v object access type)))
- ((:lisp :value :func)
- (label-value-line name (inspect::component-ref object access)))
- (:indirect
- (destructuring-bind (prefix count ref set) access
- (declare (ignore set prefix))
- (loop for i below (funcall count object)
- append (label-value-line (format nil "~A-~D" name i)
- (funcall ref object i))))))))
-
- ;;;; Multithreading
-
- (defimplementation initialize-multiprocessing (continuation)
- (mp:start-scheduler)
- (funcall continuation))
-
- (defimplementation spawn (fn &key name)
- (mp:process-run-function name fn))
-
- (defvar *id-lock* (mp:make-process-lock :name "id lock"))
- (defvar *thread-id-counter* 0)
-
- (defimplementation thread-id (thread)
- (mp:with-process-lock (*id-lock*)
- (or (getf (mp:process-property-list thread) 'id)
- (setf (getf (mp:process-property-list thread) 'id)
- (incf *thread-id-counter*)))))
-
- (defimplementation find-thread (id)
- (find id mp:*all-processes*
- :key (lambda (p) (getf (mp:process-property-list p) 'id))))
-
- (defimplementation thread-name (thread)
- (mp:process-name thread))
-
- (defimplementation thread-status (thread)
- (princ-to-string (mp:process-whostate thread)))
-
- (defimplementation thread-attributes (thread)
- (list :priority (mp:process-priority thread)
- :times-resumed (mp:process-times-resumed thread)))
-
- (defimplementation make-lock (&key name)
- (mp:make-process-lock :name name))
-
- (defimplementation call-with-lock-held (lock function)
- (mp:with-process-lock (lock) (funcall function)))
-
- (defimplementation current-thread ()
- mp:*current-process*)
-
- (defimplementation all-threads ()
- (copy-list mp:*all-processes*))
-
- (defimplementation interrupt-thread (thread fn)
- (mp:process-interrupt thread fn))
-
- (defimplementation kill-thread (thread)
- (mp:process-kill thread))
-
- (defvar *mailbox-lock* (mp:make-process-lock :name "mailbox lock"))
-
- (defstruct (mailbox (:conc-name mailbox.))
- (lock (mp:make-process-lock :name "process mailbox"))
- (queue '() :type list)
- (gate (mp:make-gate nil)))
-
- (defun mailbox (thread)
- "Return THREAD's mailbox."
- (mp:with-process-lock (*mailbox-lock*)
- (or (getf (mp:process-property-list thread) 'mailbox)
- (setf (getf (mp:process-property-list thread) 'mailbox)
- (make-mailbox)))))
-
- (defimplementation send (thread message)
- (let* ((mbox (mailbox thread)))
- (mp:with-process-lock ((mailbox.lock mbox))
- (setf (mailbox.queue mbox)
- (nconc (mailbox.queue mbox) (list message)))
- (mp:open-gate (mailbox.gate mbox)))))
-
- (defimplementation receive-if (test &optional timeout)
- (let ((mbox (mailbox mp:*current-process*)))
- (assert (or (not timeout) (eq timeout t)))
- (loop
- (check-slime-interrupts)
- (mp:with-process-lock ((mailbox.lock mbox))
- (let* ((q (mailbox.queue mbox))
- (tail (member-if test q)))
- (when tail
- (setf (mailbox.queue mbox) (nconc (ldiff q tail) (cdr tail)))
- (return (car tail)))
- (mp:close-gate (mailbox.gate mbox))))
- (when (eq timeout t) (return (values nil t)))
- (mp:process-wait-with-timeout "receive-if" 0.5
- #'mp:gate-open-p (mailbox.gate mbox)))))
-
- (let ((alist '())
- (lock (mp:make-process-lock :name "register-thread")))
-
- (defimplementation register-thread (name thread)
- (declare (type symbol name))
- (mp:with-process-lock (lock)
- (etypecase thread
- (null
- (setf alist (delete name alist :key #'car)))
- (mp:process
- (let ((probe (assoc name alist)))
- (cond (probe (setf (cdr probe) thread))
- (t (setf alist (acons name thread alist))))))))
- nil)
-
- (defimplementation find-registered (name)
- (mp:with-process-lock (lock)
- (cdr (assoc name alist)))))
-
- (defimplementation set-default-initial-binding (var form)
- (push (cons var form)
- #+(version>= 9 0)
- excl:*required-thread-bindings*
- #-(version>= 9 0)
- excl::required-thread-bindings))
-
- (defimplementation quit-lisp ()
- (excl:exit 0 :quiet t))
-
- ;;Trace implementations
- ;;In Allegro 7.0, we have:
- ;; (trace <name>)
- ;; (trace ((method <name> <qualifier>? (<specializer>+))))
- ;; (trace ((labels <name> <label-name>)))
- ;; (trace ((labels (method <name> (<specializer>+)) <label-name>)))
- ;; <name> can be a normal name or a (setf name)
-
- (defimplementation toggle-trace (spec)
- (ecase (car spec)
- ((setf)
- (toggle-trace-aux spec))
- (:defgeneric (toggle-trace-generic-function-methods (second spec)))
- ((setf :defmethod :labels :flet)
- (toggle-trace-aux (process-fspec-for-allegro spec)))
- (:call
- (destructuring-bind (caller callee) (cdr spec)
- (toggle-trace-aux callee
- :inside (list (process-fspec-for-allegro caller)))))))
-
- (defun tracedp (fspec)
- (member fspec (eval '(trace)) :test #'equal))
-
- (defun toggle-trace-aux (fspec &rest args)
- (cond ((tracedp fspec)
- (eval `(untrace ,fspec))
- (format nil "~S is now untraced." fspec))
- (t
- (eval `(trace (,fspec ,@args)))
- (format nil "~S is now traced." fspec))))
-
- (defun toggle-trace-generic-function-methods (name)
- (let ((methods (mop:generic-function-methods (fdefinition name))))
- (cond ((tracedp name)
- (eval `(untrace ,name))
- (dolist (method methods (format nil "~S is now untraced." name))
- (excl:funtrace (mop:method-function method))))
- (t
- (eval `(trace (,name)))
- (dolist (method methods (format nil "~S is now traced." name))
- (excl:ftrace (mop:method-function method)))))))
-
- (defun process-fspec-for-allegro (fspec)
- (cond ((consp fspec)
- (ecase (first fspec)
- ((setf) fspec)
- ((:defun :defgeneric) (second fspec))
- ((:defmethod) `(method ,@(rest fspec)))
- ((:labels) `(labels ,(process-fspec-for-allegro (second fspec))
- ,(third fspec)))
- ((:flet) `(flet ,(process-fspec-for-allegro (second fspec))
- ,(third fspec)))))
- (t
- fspec)))
-
- ;;;; Weak hashtables
-
- (defimplementation make-weak-key-hash-table (&rest args)
- (apply #'make-hash-table :weak-keys t args))
-
- (defimplementation make-weak-value-hash-table (&rest args)
- (apply #'make-hash-table :values :weak args))
-
- (defimplementation hash-table-weakness (hashtable)
- (cond ((excl:hash-table-weak-keys hashtable) :key)
- ((eq (excl:hash-table-values hashtable) :weak) :value)))
-
-
- ;;;; Character names
-
- (defimplementation character-completion-set (prefix matchp)
- (loop for name being the hash-keys of excl::*name-to-char-table*
- when (funcall matchp prefix name)
- collect (string-capitalize name)))
-
- ;;;; wrap interface implementation
-
- (defimplementation wrap (spec indicator &key before after replace)
- (let ((allegro-spec (process-fspec-for-allegro spec)))
- (excl:fwrap allegro-spec
- indicator
- (excl:def-fwrapper allegro-wrapper (&rest args)
- (let (retlist completed)
- (unwind-protect
- (progn
- (when before
- (funcall before args))
- (setq retlist (multiple-value-list
- (if replace
- (funcall replace args)
- (excl:call-next-fwrapper))))
- (setq completed t)
- (values-list retlist))
- (when after
- (funcall after (if completed
- retlist
- :exited-non-locally)))))))
- allegro-spec))
-
- (defimplementation unwrap (spec indicator)
- (let ((allegro-spec (process-fspec-for-allegro spec)))
- (excl:funwrap allegro-spec indicator)
- allegro-spec))
-
- (defimplementation wrapped-p (spec indicator)
- (getf (excl:fwrap-order (process-fspec-for-allegro spec)) indicator))
|