sbcl.lisp (80172B)
1 ;;;;; -*- indent-tabs-mode: nil -*- 2 ;;; 3 ;;; slynk-sbcl.lisp --- SLY backend for SBCL. 4 ;;; 5 ;;; Created 2003, Daniel Barlow <dan@metacircles.com> 6 ;;; 7 ;;; This code has been placed in the Public Domain. All warranties are 8 ;;; disclaimed. 9 10 ;;; Requires the SB-INTROSPECT contrib. 11 12 ;;; Administrivia 13 14 (defpackage slynk-sbcl 15 (:use cl slynk-backend slynk-source-path-parser slynk-source-file-cache) 16 (:export 17 #:with-sbcl-version>=)) 18 19 (in-package slynk-sbcl) 20 21 (eval-when (:compile-toplevel :load-toplevel :execute) 22 (require 'sb-bsd-sockets) 23 (require 'sb-introspect) 24 (require 'sb-posix) 25 (require 'sb-cltl2)) 26 27 (declaim (optimize (debug 2) 28 (sb-c::insert-step-conditions 0) 29 (sb-c::insert-debug-catch 0))) 30 31 ;;; backwards compability tests 32 33 (eval-when (:compile-toplevel :load-toplevel :execute) 34 ;; Generate a form suitable for testing for stepper support (0.9.17) 35 ;; with #+. 36 (defun sbcl-with-new-stepper-p () 37 (with-symbol 'enable-stepping 'sb-impl)) 38 ;; Ditto for weak hash-tables 39 (defun sbcl-with-weak-hash-tables () 40 (with-symbol 'hash-table-weakness 'sb-ext)) 41 ;; And for xref support (1.0.1) 42 (defun sbcl-with-xref-p () 43 (with-symbol 'who-calls 'sb-introspect)) 44 ;; ... for restart-frame support (1.0.2) 45 (defun sbcl-with-restart-frame () 46 (with-symbol 'frame-has-debug-tag-p 'sb-debug)) 47 ;; ... for :setf :inverse info (1.1.17) 48 (defun sbcl-with-setf-inverse-meta-info () 49 (boolean-to-feature-expression 50 ;; going through FIND-SYMBOL since META-INFO was renamed from 51 ;; TYPE-INFO in 1.2.10. 52 (let ((sym (find-symbol "META-INFO" "SB-C"))) 53 (and sym 54 (fboundp sym) 55 (funcall sym :setf :inverse ())))))) 56 57 ;;; slynk-mop 58 59 (import-slynk-mop-symbols :sb-mop '(:slot-definition-documentation)) 60 61 (defun slynk-mop:slot-definition-documentation (slot) 62 (sb-pcl::documentation slot t)) 63 64 ;; stream support 65 66 (defimplementation gray-package-name () 67 "SB-GRAY") 68 69 ;; Pretty printer calls this, apparently 70 (defmethod sb-gray:stream-line-length 71 ((s sb-gray:fundamental-character-input-stream)) 72 nil) 73 74 ;;; Connection info 75 76 (defimplementation lisp-implementation-type-name () 77 "sbcl") 78 79 ;; Declare return type explicitly to shut up STYLE-WARNINGS about 80 ;; %SAP-ALIEN in ENABLE-SIGIO-ON-FD below. 81 (declaim (ftype (function () (values (signed-byte 32) &optional)) getpid)) 82 (defimplementation getpid () 83 (sb-posix:getpid)) 84 85 ;;; UTF8 86 87 (defimplementation string-to-utf8 (string) 88 (sb-ext:string-to-octets string :external-format '(:utf8 :replacement 89 #+sb-unicode #\Replacement_Character 90 #-sb-unicode #\? ))) 91 92 (defimplementation utf8-to-string (octets) 93 (sb-ext:octets-to-string octets :external-format '(:utf8 :replacement 94 #+sb-unicode #\Replacement_Character 95 #-sb-unicode #\? ))) 96 97 ;;; TCP Server 98 99 (defimplementation preferred-communication-style () 100 (cond 101 ;; fixme: when SBCL/win32 gains better select() support, remove 102 ;; this. 103 ((member :sb-thread *features*) :spawn) 104 ((member :win32 *features*) nil) 105 (t :fd-handler))) 106 107 108 (defun resolve-hostname (host) 109 "Returns valid IPv4 or IPv6 address for the host." 110 ;; get all IPv4 and IPv6 addresses as a list 111 (let* ((host-ents (multiple-value-list (sb-bsd-sockets:get-host-by-name host))) 112 ;; remove protocols for which we don't have an address 113 (addresses (remove-if-not #'sb-bsd-sockets:host-ent-address host-ents))) 114 ;; Return the first one or nil, 115 ;; but actually, it shouln't return nil, because 116 ;; get-host-by-name will signal NAME-SERVICE-ERROR condition 117 ;; if there isn't any address for the host. 118 (first addresses))) 119 120 121 (defimplementation create-socket (host port &key backlog) 122 (let* ((host-ent (resolve-hostname host)) 123 (socket (make-instance (cond #+#.(slynk-backend:with-symbol 'inet6-socket 'sb-bsd-sockets) 124 ((eql (sb-bsd-sockets:host-ent-address-type host-ent) 10) 125 'sb-bsd-sockets:inet6-socket) 126 (t 127 'sb-bsd-sockets:inet-socket)) 128 :type :stream 129 :protocol :tcp))) 130 (setf (sb-bsd-sockets:sockopt-reuse-address socket) t) 131 (sb-bsd-sockets:socket-bind socket (sb-bsd-sockets:host-ent-address host-ent) port) 132 133 (sb-bsd-sockets:socket-listen socket (or backlog 5)) 134 socket)) 135 136 (defimplementation local-port (socket) 137 (nth-value 1 (sb-bsd-sockets:socket-name socket))) 138 139 (defimplementation close-socket (socket) 140 (sb-sys:invalidate-descriptor (socket-fd socket)) 141 (sb-bsd-sockets:socket-close socket)) 142 143 (defimplementation accept-connection (socket &key 144 external-format 145 buffering timeout) 146 (declare (ignore timeout)) 147 (make-socket-io-stream (accept socket) external-format 148 (ecase buffering 149 ((t :full) :full) 150 ((nil :none) :none) 151 ((:line) :line)))) 152 153 154 ;; The SIGIO stuff should probably be removed as it's unlikey that 155 ;; anybody uses it. 156 #-win32 157 (progn 158 (defimplementation install-sigint-handler (function) 159 (sb-sys:enable-interrupt sb-unix:sigint 160 (lambda (&rest args) 161 (declare (ignore args)) 162 (sb-sys:invoke-interruption 163 (lambda () 164 (sb-sys:with-interrupts 165 (funcall function))))))) 166 167 (defvar *sigio-handlers* '() 168 "List of (key . fn) pairs to be called on SIGIO.") 169 170 (defun sigio-handler (signal code scp) 171 (declare (ignore signal code scp)) 172 (sb-sys:with-interrupts 173 (mapc (lambda (handler) 174 (funcall (the function (cdr handler)))) 175 *sigio-handlers*))) 176 177 (defun set-sigio-handler () 178 (sb-sys:enable-interrupt sb-unix:sigio #'sigio-handler)) 179 180 (defun enable-sigio-on-fd (fd) 181 (sb-posix::fcntl fd sb-posix::f-setfl sb-posix::o-async) 182 (sb-posix::fcntl fd sb-posix::f-setown (getpid)) 183 (values)) 184 185 (defimplementation add-sigio-handler (socket fn) 186 (set-sigio-handler) 187 (let ((fd (socket-fd socket))) 188 (enable-sigio-on-fd fd) 189 (push (cons fd fn) *sigio-handlers*))) 190 191 (defimplementation remove-sigio-handlers (socket) 192 (let ((fd (socket-fd socket))) 193 (setf *sigio-handlers* (delete fd *sigio-handlers* :key #'car)) 194 (sb-sys:invalidate-descriptor fd)) 195 (close socket))) 196 197 198 (defimplementation add-fd-handler (socket fun) 199 (let ((fd (socket-fd socket)) 200 (handler nil)) 201 (labels ((add () 202 (setq handler (sb-sys:add-fd-handler fd :input #'run))) 203 (run (fd) 204 (sb-sys:remove-fd-handler handler) ; prevent recursion 205 (unwind-protect 206 (funcall fun) 207 (when (sb-unix:unix-fstat fd) ; still open? 208 (add))))) 209 (add)))) 210 211 (defimplementation remove-fd-handlers (socket) 212 (sb-sys:invalidate-descriptor (socket-fd socket))) 213 214 (defimplementation socket-fd (socket) 215 (etypecase socket 216 (fixnum socket) 217 (sb-bsd-sockets:socket (sb-bsd-sockets:socket-file-descriptor socket)) 218 (file-stream (sb-sys:fd-stream-fd socket)))) 219 220 (defimplementation command-line-args () 221 sb-ext:*posix-argv*) 222 223 (defimplementation dup (fd) 224 (sb-posix:dup fd)) 225 226 (defvar *wait-for-input-called*) 227 228 (defimplementation wait-for-input (streams &optional timeout) 229 (assert (member timeout '(nil t))) 230 (when (boundp '*wait-for-input-called*) 231 (setq *wait-for-input-called* t)) 232 (let ((*wait-for-input-called* nil)) 233 (loop 234 (let ((ready (remove-if-not #'input-ready-p streams))) 235 (when ready (return ready))) 236 (when (check-sly-interrupts) 237 (return :interrupt)) 238 (when *wait-for-input-called* 239 (return :interrupt)) 240 (when timeout 241 (return nil)) 242 (sleep 0.1)))) 243 244 (defun fd-stream-input-buffer-empty-p (stream) 245 (let ((buffer (sb-impl::fd-stream-ibuf stream))) 246 (or (not buffer) 247 (= (sb-impl::buffer-head buffer) 248 (sb-impl::buffer-tail buffer))))) 249 250 #-win32 251 (defun input-ready-p (stream) 252 (or (not (fd-stream-input-buffer-empty-p stream)) 253 #+#.(slynk-backend:with-symbol 'fd-stream-fd-type 'sb-impl) 254 (eq :regular (sb-impl::fd-stream-fd-type stream)) 255 (not (sb-impl::sysread-may-block-p stream)))) 256 257 #+win32 258 (progn 259 (defun input-ready-p (stream) 260 (or (not (fd-stream-input-buffer-empty-p stream)) 261 (handle-listen (sockint::fd->handle (sb-impl::fd-stream-fd stream))))) 262 263 (sb-alien:define-alien-routine ("WSACreateEvent" wsa-create-event) 264 sb-win32:handle) 265 266 (sb-alien:define-alien-routine ("WSACloseEvent" wsa-close-event) 267 sb-alien:int 268 (event sb-win32:handle)) 269 270 (defconstant +fd-read+ #.(ash 1 0)) 271 (defconstant +fd-close+ #.(ash 1 5)) 272 273 (sb-alien:define-alien-routine ("WSAEventSelect" wsa-event-select) 274 sb-alien:int 275 (fd sb-alien:int) 276 (handle sb-win32:handle) 277 (mask sb-alien:long)) 278 279 (sb-alien:load-shared-object "kernel32.dll") 280 (sb-alien:define-alien-routine ("WaitForSingleObjectEx" 281 wait-for-single-object-ex) 282 sb-alien:int 283 (event sb-win32:handle) 284 (milliseconds sb-alien:long) 285 (alertable sb-alien:int)) 286 287 ;; see SB-WIN32:HANDLE-LISTEN 288 (defun handle-listen (handle) 289 (sb-alien:with-alien ((avail sb-win32:dword) 290 (buf (array char #.sb-win32::input-record-size))) 291 (unless (zerop (sb-win32:peek-named-pipe handle nil 0 nil 292 (sb-alien:alien-sap 293 (sb-alien:addr avail)) 294 nil)) 295 (return-from handle-listen (plusp avail))) 296 297 (unless (zerop (sb-win32:peek-console-input handle 298 (sb-alien:alien-sap buf) 299 sb-win32::input-record-size 300 (sb-alien:alien-sap 301 (sb-alien:addr avail)))) 302 (return-from handle-listen (plusp avail)))) 303 304 (let ((event (wsa-create-event))) 305 (wsa-event-select handle event (logior +fd-read+ +fd-close+)) 306 (let ((val (wait-for-single-object-ex event 0 0))) 307 (wsa-close-event event) 308 (unless (= val -1) 309 (return-from handle-listen (zerop val))))) 310 311 nil) 312 313 ) 314 315 (defvar *external-format-to-coding-system* 316 '((:iso-8859-1 317 "latin-1" "latin-1-unix" "iso-latin-1-unix" 318 "iso-8859-1" "iso-8859-1-unix") 319 (:utf-8 "utf-8" "utf-8-unix") 320 (:euc-jp "euc-jp" "euc-jp-unix") 321 (:us-ascii "us-ascii" "us-ascii-unix"))) 322 323 ;; C.f. R.M.Kreuter in <20536.1219412774@progn.net> on sbcl-general, 324 ;; 2008-08-22. 325 (defvar *physical-pathname-host* (pathname-host (user-homedir-pathname))) 326 327 (defimplementation filename-to-pathname (filename) 328 (sb-ext:parse-native-namestring filename *physical-pathname-host*)) 329 330 (defimplementation find-external-format (coding-system) 331 (car (rassoc-if (lambda (x) (member coding-system x :test #'equal)) 332 *external-format-to-coding-system*))) 333 334 (defimplementation set-default-directory (directory) 335 (let ((directory (truename (merge-pathnames directory)))) 336 (sb-posix:chdir directory) 337 (setf *default-pathname-defaults* directory) 338 (default-directory))) 339 340 (defun make-socket-io-stream (socket external-format buffering) 341 (let ((args `(:output t 342 :input t 343 :element-type ,(if external-format 344 'character 345 '(unsigned-byte 8)) 346 :buffering ,buffering 347 ,@(cond ((and external-format (sb-int:featurep :sb-unicode)) 348 `(:external-format ,external-format)) 349 (t '())) 350 :serve-events ,(eq :fd-handler 351 (slynk-value '*communication-style* t)) 352 ;; SBCL < 1.0.42.43 doesn't support :SERVE-EVENTS 353 ;; argument. 354 :allow-other-keys t))) 355 (apply #'sb-bsd-sockets:socket-make-stream socket args))) 356 357 (defun accept (socket) 358 "Like socket-accept, but retry on EAGAIN." 359 (loop (handler-case 360 (return (sb-bsd-sockets:socket-accept socket)) 361 (sb-bsd-sockets:interrupted-error ())))) 362 363 364 ;;;; Support for SBCL syntax 365 366 ;;; SBCL's source code is riddled with #! reader macros. Also symbols 367 ;;; containing `!' have special meaning. We have to work long and 368 ;;; hard to be able to read the source. To deal with #! reader 369 ;;; macros, we use a special readtable. The special symbols are 370 ;;; converted by a condition handler. 371 372 (defun feature-in-list-p (feature list) 373 (etypecase feature 374 (symbol (member feature list :test #'eq)) 375 (cons (flet ((subfeature-in-list-p (subfeature) 376 (feature-in-list-p subfeature list))) 377 ;; Don't use ECASE since SBCL also has :host-feature, 378 ;; don't need to handle it or anything else appearing in 379 ;; the future or in erronous code. 380 (case (first feature) 381 (:or (some #'subfeature-in-list-p (rest feature))) 382 (:and (every #'subfeature-in-list-p (rest feature))) 383 (:not (destructuring-bind (e) (cdr feature) 384 (not (subfeature-in-list-p e))))))))) 385 386 (defun shebang-reader (stream sub-character infix-parameter) 387 (declare (ignore sub-character)) 388 (when infix-parameter 389 (error "illegal read syntax: #~D!" infix-parameter)) 390 (let ((next-char (read-char stream))) 391 (unless (find next-char "+-") 392 (error "illegal read syntax: #!~C" next-char)) 393 ;; When test is not satisfied 394 ;; FIXME: clearer if order of NOT-P and (NOT NOT-P) were reversed? then 395 ;; would become "unless test is satisfied".. 396 (when (let* ((*package* (find-package "KEYWORD")) 397 (*read-suppress* nil) 398 (not-p (char= next-char #\-)) 399 (feature (read stream))) 400 (if (feature-in-list-p feature *features*) 401 not-p 402 (not not-p))) 403 ;; Read (and discard) a form from input. 404 (let ((*read-suppress* t)) 405 (read stream t nil t)))) 406 (values)) 407 408 (defvar *shebang-readtable* 409 (let ((*readtable* (copy-readtable nil))) 410 (set-dispatch-macro-character #\# #\! 411 (lambda (s c n) (shebang-reader s c n)) 412 *readtable*) 413 *readtable*)) 414 415 (defun shebang-readtable () 416 *shebang-readtable*) 417 418 (defun sbcl-package-p (package) 419 (let ((name (package-name package))) 420 (eql (mismatch "SB-" name) 3))) 421 422 (defun sbcl-source-file-p (filename) 423 (when filename 424 (loop for (nil pattern) in (logical-pathname-translations "SYS") 425 thereis (pathname-match-p filename pattern)))) 426 427 (defun guess-readtable-for-filename (filename) 428 (if (sbcl-source-file-p filename) 429 (shebang-readtable) 430 *readtable*)) 431 432 (defvar *debootstrap-packages* t) 433 434 (defun call-with-debootstrapping (fun) 435 (handler-bind ((sb-int:bootstrap-package-not-found 436 #'sb-int:debootstrap-package)) 437 (funcall fun))) 438 439 (defmacro with-debootstrapping (&body body) 440 `(call-with-debootstrapping (lambda () ,@body))) 441 442 (defimplementation call-with-syntax-hooks (fn) 443 (cond ((and *debootstrap-packages* 444 (sbcl-package-p *package*)) 445 (with-debootstrapping (funcall fn))) 446 (t 447 (funcall fn)))) 448 449 (defimplementation default-readtable-alist () 450 (let ((readtable (shebang-readtable))) 451 (loop for p in (remove-if-not #'sbcl-package-p (list-all-packages)) 452 collect (cons (package-name p) readtable)))) 453 454 ;;; Packages 455 456 #+#.(slynk-backend:with-symbol 'package-local-nicknames 'sb-ext) 457 (defimplementation package-local-nicknames (package) 458 (sb-ext:package-local-nicknames package)) 459 460 ;;; Utilities 461 462 (defun slynk-value (name &optional errorp) 463 ;; Easy way to refer to symbol values in SLYNK, which doesn't yet exist when 464 ;; this is file is loaded. 465 (let ((symbol (find-symbol (string name) :slynk))) 466 (if (and symbol (or errorp (boundp symbol))) 467 (symbol-value symbol) 468 (when errorp 469 (error "~S does not exist in SLYNK." name))))) 470 471 (defun sbcl-version>= (&rest subversions) 472 #+#.(slynk-backend:with-symbol 'assert-version->= 'sb-ext) 473 (values (ignore-errors (apply #'sb-ext:assert-version->= subversions) t)) 474 #-#.(slynk-backend:with-symbol 'assert-version->= 'sb-ext) 475 nil) 476 477 (defmacro with-sbcl-version>= (&rest subversions) 478 `(if (sbcl-version>= ,@subversions) 479 '(:and) '(:or))) 480 481 #+#.(slynk-backend:with-symbol 'function-lambda-list 'sb-introspect) 482 (defimplementation arglist (fname) 483 (sb-introspect:function-lambda-list fname)) 484 485 #-#.(slynk-backend:with-symbol 'function-lambda-list 'sb-introspect) 486 (defimplementation arglist (fname) 487 (sb-introspect:function-arglist fname)) 488 489 (defimplementation function-name (f) 490 (check-type f function) 491 (sb-impl::%fun-name f)) 492 493 (defmethod declaration-arglist ((decl-identifier (eql 'optimize))) 494 (flet ((ensure-list (thing) (if (listp thing) thing (list thing)))) 495 (let* ((flags (sb-cltl2:declaration-information decl-identifier))) 496 (if flags 497 ;; Symbols aren't printed with package qualifiers, but the 498 ;; FLAGS would have to be fully qualified when used inside a 499 ;; declaration. So we strip those as long as there's no 500 ;; better way. (FIXME) 501 `(&any ,@(remove-if-not 502 #'(lambda (qualifier) 503 (find-symbol (symbol-name (first qualifier)) :cl)) 504 flags :key #'ensure-list)) 505 (call-next-method))))) 506 507 #+#.(slynk-backend:with-symbol 'deftype-lambda-list 'sb-introspect) 508 (defmethod type-specifier-arglist :around (typespec-operator) 509 (multiple-value-bind (arglist foundp) 510 (sb-introspect:deftype-lambda-list typespec-operator) 511 (if foundp arglist (call-next-method)))) 512 513 (defimplementation type-specifier-p (symbol) 514 (or (sb-ext:valid-type-specifier-p symbol) 515 (not (eq (type-specifier-arglist symbol) :not-available)))) 516 517 (defvar *buffer-name* nil) 518 (defvar *buffer-tmpfile* nil) 519 (defvar *buffer-offset*) 520 (defvar *buffer-substring* nil) 521 522 (defvar *previous-compiler-condition* nil 523 "Used to detect duplicates.") 524 525 (defun handle-notification-condition (condition) 526 "Handle a condition caused by a compiler warning. 527 This traps all compiler conditions at a lower-level than using 528 C:*COMPILER-NOTIFICATION-FUNCTION*. The advantage is that we get to 529 craft our own error messages, which can omit a lot of redundant 530 information." 531 (unless (or (eq condition *previous-compiler-condition*)) 532 ;; First resignal warnings, so that outer handlers -- which may choose to 533 ;; muffle this -- get a chance to run. 534 (when (typep condition 'warning) 535 (signal condition)) 536 (setq *previous-compiler-condition* condition) 537 (signal-compiler-condition (real-condition condition) 538 (sb-c::find-error-context nil)))) 539 540 (defun signal-compiler-condition (condition context) 541 (signal 'compiler-condition 542 :original-condition condition 543 :severity (etypecase condition 544 (sb-ext:compiler-note :note) 545 (sb-c:compiler-error :error) 546 (reader-error :read-error) 547 (error :error) 548 #+#.(slynk-backend:with-symbol early-deprecation-warning sb-ext) 549 (sb-ext::early-deprecation-warning :early-deprecation-warning) 550 #+#.(slynk-backend:with-symbol late-deprecation-warning sb-ext) 551 (sb-ext::late-deprecation-warning :late-deprecation-warning) 552 #+#.(slynk-backend:with-symbol final-deprecation-warning sb-ext) 553 (sb-ext::final-deprecation-warning :final-deprecation-warning) 554 #+#.(slynk-backend:with-symbol redefinition-warning 555 sb-kernel) 556 (sb-kernel:redefinition-warning 557 :redefinition) 558 (style-warning :style-warning) 559 (warning :warning)) 560 :references (condition-references condition) 561 :message (brief-compiler-message-for-emacs condition) 562 :source-context (compiler-error-context context) 563 :location (compiler-note-location condition context))) 564 565 (defun real-condition (condition) 566 "Return the encapsulated condition or CONDITION itself." 567 (typecase condition 568 (sb-int:encapsulated-condition (sb-int:encapsulated-condition condition)) 569 (t condition))) 570 571 (defun condition-references (condition) 572 (if (typep condition 'sb-int:reference-condition) 573 (externalize-reference 574 (sb-int:reference-condition-references condition)))) 575 576 (defun compiler-note-location (condition context) 577 (flet ((bailout () 578 (return-from compiler-note-location 579 (make-error-location "No error location available")))) 580 (cond (context 581 (locate-compiler-note 582 (sb-c::compiler-error-context-file-name context) 583 (compiler-source-path context) 584 (sb-c::compiler-error-context-original-source context))) 585 ((typep condition 'reader-error) 586 (let* ((stream (stream-error-stream condition)) 587 ;; If STREAM is, for example, a STRING-INPUT-STREAM, 588 ;; an error will be signaled since PATHNAME only 589 ;; accepts a "stream associated with a file" which 590 ;; is a complicated predicate and hard to test 591 ;; portably. 592 (file (ignore-errors (pathname stream)))) 593 (unless (and file (open-stream-p stream)) 594 (bailout)) 595 (if (compiling-from-buffer-p file) 596 ;; The stream position for e.g. "comma not inside 597 ;; backquote" is at the character following the 598 ;; comma, :offset is 0-based, hence the 1-. 599 (make-location (list :buffer *buffer-name*) 600 (list :offset *buffer-offset* 601 (1- (file-position stream)))) 602 (progn 603 (assert (compiling-from-file-p file)) 604 ;; No 1- because :position is 1-based. 605 (make-location (list :file (namestring file)) 606 (list :position (file-position stream))))))) 607 (t (bailout))))) 608 609 (defun compiling-from-buffer-p (filename) 610 (and *buffer-name* 611 ;; The following is to trigger COMPILING-FROM-GENERATED-CODE-P 612 ;; in LOCATE-COMPILER-NOTE, and allows handling nested 613 ;; compilation from eg. hitting C-C on (eval-when ... (require ..))). 614 ;; 615 ;; PROBE-FILE to handle tempfile directory being a symlink. 616 (pathnamep filename) 617 (let ((true1 (probe-file filename)) 618 (true2 (probe-file *buffer-tmpfile*))) 619 (and true1 (equal true1 true2))))) 620 621 (defun compiling-from-file-p (filename) 622 (and (pathnamep filename) 623 (or (null *buffer-name*) 624 (null *buffer-tmpfile*) 625 (let ((true1 (probe-file filename)) 626 (true2 (probe-file *buffer-tmpfile*))) 627 (not (and true1 (equal true1 true2))))))) 628 629 (defun compiling-from-generated-code-p (filename source) 630 (and (eq filename :lisp) (stringp source))) 631 632 (defun locate-compiler-note (file source-path source) 633 (cond ((compiling-from-buffer-p file) 634 (make-location (list :buffer *buffer-name*) 635 (list :offset *buffer-offset* 636 (source-path-string-position 637 source-path *buffer-substring*)))) 638 ((compiling-from-file-p file) 639 (let ((position (source-path-file-position source-path file))) 640 (make-location (list :file (namestring file)) 641 (list :position (and position 642 (1+ position)))))) 643 ((compiling-from-generated-code-p file source) 644 (make-location (list :source-form source) 645 (list :position 1))) 646 (t 647 (error "unhandled case in compiler note ~S ~S ~S" 648 file source-path source)))) 649 650 (defun brief-compiler-message-for-emacs (condition) 651 "Briefly describe a compiler error for Emacs. 652 When Emacs presents the message it already has the source popped up 653 and the source form highlighted. This makes much of the information in 654 the error-context redundant." 655 (let ((sb-int:*print-condition-references* nil)) 656 (princ-to-string condition))) 657 658 (defun compiler-error-context (error-context) 659 "Describe a compiler error for Emacs including context information." 660 (declare (type (or sb-c::compiler-error-context null) error-context)) 661 (multiple-value-bind (enclosing source) 662 (if error-context 663 (values (sb-c::compiler-error-context-enclosing-source error-context) 664 (sb-c::compiler-error-context-source error-context))) 665 (and (or enclosing source) 666 (format nil "~@[--> ~{~<~%--> ~1:;~A~> ~}~%~]~@[~{==>~%~A~%~}~]" 667 enclosing source)))) 668 669 (defun compiler-source-path (context) 670 "Return the source-path for the current compiler error. 671 Returns NIL if this cannot be determined by examining internal 672 compiler state." 673 (cond ((sb-c::node-p context) 674 (reverse 675 (sb-c::source-path-original-source 676 (sb-c::node-source-path context)))) 677 ((sb-c::compiler-error-context-p context) 678 (reverse 679 (sb-c::compiler-error-context-original-source-path context))))) 680 681 (defimplementation call-with-compilation-hooks (function) 682 (declare (type function function)) 683 (handler-bind 684 ;; N.B. Even though these handlers are called HANDLE-FOO they 685 ;; actually decline, i.e. the signalling of the original 686 ;; condition continues upward. 687 ((sb-c:fatal-compiler-error #'handle-notification-condition) 688 (sb-c:compiler-error #'handle-notification-condition) 689 (sb-ext:compiler-note #'handle-notification-condition) 690 (error #'handle-notification-condition) 691 (warning #'handle-notification-condition)) 692 (funcall function))) 693 694 ;;; HACK: SBCL 1.2.12 shipped with a bug where 695 ;;; SB-EXT:RESTRICT-COMPILER-POLICY would signal an error when there 696 ;;; were no policy restrictions in place. This workaround ensures the 697 ;;; existence of at least one dummy restriction. 698 (handler-case (sb-ext:restrict-compiler-policy) 699 (error () (sb-ext:restrict-compiler-policy 'debug))) 700 701 (defun compiler-policy (qualities) 702 "Return compiler policy qualities present in the QUALITIES alist. 703 QUALITIES is an alist with (quality . value)" 704 #+#.(slynk-backend:with-symbol 'restrict-compiler-policy 'sb-ext) 705 (loop with policy = (sb-ext:restrict-compiler-policy) 706 for (quality) in qualities 707 collect (cons quality 708 (or (cdr (assoc quality policy)) 709 0)))) 710 711 (defun (setf compiler-policy) (policy) 712 (declare (ignorable policy)) 713 #+#.(slynk-backend:with-symbol 'restrict-compiler-policy 'sb-ext) 714 (loop for (qual . value) in policy 715 do (sb-ext:restrict-compiler-policy qual value))) 716 717 (defmacro with-compiler-policy (policy &body body) 718 (let ((current-policy (gensym))) 719 `(let ((,current-policy (compiler-policy ,policy))) 720 (setf (compiler-policy) ,policy) 721 (unwind-protect (progn ,@body) 722 (setf (compiler-policy) ,current-policy))))) 723 724 (defimplementation slynk-compile-file (input-file output-file 725 load-p external-format 726 &key policy) 727 (multiple-value-bind (output-file warnings-p failure-p) 728 (with-compiler-policy policy 729 (with-compilation-hooks () 730 (compile-file input-file :output-file output-file 731 :external-format external-format))) 732 (values output-file warnings-p 733 (or failure-p 734 (when load-p 735 ;; Cache the latest source file for definition-finding. 736 (source-cache-get input-file 737 (file-write-date input-file)) 738 (not (load output-file))))))) 739 740 ;;;; compile-string 741 742 ;;; We copy the string to a temporary file in order to get adequate 743 ;;; semantics for :COMPILE-TOPLEVEL and :LOAD-TOPLEVEL EVAL-WHEN forms 744 ;;; which the previous approach using 745 ;;; (compile nil `(lambda () ,(read-from-string string))) 746 ;;; did not provide. 747 748 (locally (declare (sb-ext:muffle-conditions sb-ext:compiler-note)) 749 750 (sb-alien:define-alien-routine (#-win32 "tempnam" #+win32 "_tempnam" tempnam) 751 sb-alien:c-string 752 (dir sb-alien:c-string) 753 (prefix sb-alien:c-string))) 754 755 (defun temp-file-name () 756 "Return a temporary file name to compile strings into." 757 (tempnam nil "slime")) 758 759 (defvar *trap-load-time-warnings* t) 760 761 (defimplementation slynk-compile-string (string &key buffer position filename 762 line column policy) 763 (declare (ignore line column)) 764 (let ((*buffer-name* buffer) 765 (*buffer-offset* position) 766 (*buffer-substring* string) 767 (*buffer-tmpfile* (temp-file-name))) 768 (labels ((load-it (filename) 769 (cond (*trap-load-time-warnings* 770 (with-compilation-hooks () (load filename))) 771 (t (load filename)))) 772 (cf () 773 (with-compiler-policy policy 774 (with-compilation-unit 775 (:source-plist (list :emacs-buffer buffer 776 :emacs-filename filename 777 :emacs-package (package-name *package*) 778 :emacs-position position 779 :emacs-string string) 780 :source-namestring filename 781 :allow-other-keys t) 782 (compile-file *buffer-tmpfile* :external-format :utf-8))))) 783 (with-open-file (s *buffer-tmpfile* :direction :output :if-exists :error 784 :external-format :utf-8) 785 (write-string string s)) 786 (unwind-protect 787 (multiple-value-bind (output-file warningsp failurep) 788 (with-compilation-hooks () (cf)) 789 (declare (ignore warningsp)) 790 (when output-file 791 (load-it output-file)) 792 (not failurep)) 793 (ignore-errors 794 (delete-file *buffer-tmpfile*) 795 (delete-file (compile-file-pathname *buffer-tmpfile*))))))) 796 797 ;;;; Definitions 798 799 (defparameter *definition-types* 800 '(:variable defvar 801 :constant defconstant 802 :type deftype 803 :symbol-macro define-symbol-macro 804 :macro defmacro 805 :compiler-macro define-compiler-macro 806 :function defun 807 :generic-function defgeneric 808 :method defmethod 809 :setf-expander define-setf-expander 810 :structure defstruct 811 :condition define-condition 812 :class defclass 813 :method-combination define-method-combination 814 :package defpackage 815 :transform :deftransform 816 :optimizer :defoptimizer 817 :vop :define-vop 818 :source-transform :define-source-transform 819 :ir1-convert :def-ir1-translator 820 :declaration declaim 821 :alien-type :define-alien-type) 822 "Map SB-INTROSPECT definition type names to SLY-friendly forms") 823 824 (defun definition-specifier (type) 825 "Return a pretty specifier for NAME representing a definition of type TYPE." 826 (getf *definition-types* type)) 827 828 (defun make-dspec (type name source-location) 829 (list* (definition-specifier type) 830 name 831 (sb-introspect::definition-source-description source-location))) 832 833 (defimplementation find-definitions (name) 834 (loop for type in *definition-types* by #'cddr 835 for defsrcs = (sb-introspect:find-definition-sources-by-name name type) 836 for filtered-defsrcs = (if (eq type :generic-function) 837 (remove :invalid defsrcs 838 :key #'categorize-definition-source) 839 defsrcs) 840 append (loop for defsrc in filtered-defsrcs collect 841 (list (make-dspec type name defsrc) 842 (converting-errors-to-error-location 843 (definition-source-for-emacs defsrc 844 type name)))))) 845 846 (defimplementation find-source-location (obj) 847 (flet ((general-type-of (obj) 848 (typecase obj 849 (method :method) 850 (generic-function :generic-function) 851 (function :function) 852 (structure-class :structure-class) 853 (class :class) 854 (method-combination :method-combination) 855 (package :package) 856 (condition :condition) 857 (structure-object :structure-object) 858 (standard-object :standard-object) 859 (t :thing))) 860 (to-string (obj) 861 (typecase obj 862 ;; Packages are possibly named entities. 863 (package (princ-to-string obj)) 864 ((or structure-object standard-object condition) 865 (with-output-to-string (s) 866 (print-unreadable-object (obj s :type t :identity t)))) 867 (t (princ-to-string obj))))) 868 (converting-errors-to-error-location 869 (let ((defsrc (sb-introspect:find-definition-source obj))) 870 (definition-source-for-emacs defsrc 871 (general-type-of obj) 872 (to-string obj)))))) 873 874 (defmacro with-definition-source ((&rest names) obj &body body) 875 "Like with-slots but works only for structs." 876 (flet ((reader (slot) 877 ;; Use read-from-string instead of intern so that 878 ;; conc-name can be a string such as ext:struct- and not 879 ;; cause errors and not force interning ext::struct- 880 (read-from-string 881 (concatenate 'string "sb-introspect:definition-source-" 882 (string slot))))) 883 (let ((tmp (gensym "OO-"))) 884 ` (let ((,tmp ,obj)) 885 (symbol-macrolet 886 ,(loop for name in names collect 887 (typecase name 888 (symbol `(,name (,(reader name) ,tmp))) 889 (cons `(,(first name) (,(reader (second name)) ,tmp))) 890 (t (error "Malformed syntax in WITH-STRUCT: ~A" name)))) 891 ,@body))))) 892 893 (defun categorize-definition-source (definition-source) 894 (with-definition-source (pathname form-path character-offset plist) 895 definition-source 896 (let ((file-p (and pathname (probe-file pathname) 897 (or form-path character-offset)))) 898 (cond ((and (getf plist :emacs-buffer) file-p) :buffer-and-file) 899 ((getf plist :emacs-buffer) :buffer) 900 (file-p :file) 901 (pathname :file-without-position) 902 (t :invalid))))) 903 904 #+#.(slynk-backend:with-symbol 'definition-source-form-number 'sb-introspect) 905 (defun form-number-position (definition-source stream) 906 (let* ((tlf-number (car (sb-introspect:definition-source-form-path definition-source))) 907 (form-number (sb-introspect:definition-source-form-number definition-source))) 908 (multiple-value-bind (tlf pos-map) (read-source-form tlf-number stream) 909 (let* ((path-table (sb-di::form-number-translations tlf 0)) 910 (path (cond ((<= (length path-table) form-number) 911 (warn "inconsistent form-number-translations") 912 (list 0)) 913 (t 914 (reverse (cdr (aref path-table form-number))))))) 915 (source-path-source-position path tlf pos-map))))) 916 917 #+#.(slynk-backend:with-symbol 'definition-source-form-number 'sb-introspect) 918 (defun file-form-number-position (definition-source) 919 (let* ((code-date (sb-introspect:definition-source-file-write-date definition-source)) 920 (filename (sb-introspect:definition-source-pathname definition-source)) 921 (*readtable* (guess-readtable-for-filename filename)) 922 (source-code (get-source-code filename code-date))) 923 (with-debootstrapping 924 (with-input-from-string (s source-code) 925 (form-number-position definition-source s))))) 926 927 #+#.(slynk-backend:with-symbol 'definition-source-form-number 'sb-introspect) 928 (defun string-form-number-position (definition-source string) 929 (with-input-from-string (s string) 930 (form-number-position definition-source s))) 931 932 (defun definition-source-buffer-location (definition-source) 933 (with-definition-source (form-path character-offset plist) definition-source 934 (destructuring-bind (&key emacs-buffer emacs-position emacs-directory 935 emacs-string &allow-other-keys) 936 plist 937 (let ((*readtable* (guess-readtable-for-filename emacs-directory)) 938 start 939 end) 940 (with-debootstrapping 941 (or 942 (and form-path 943 (or 944 #+#.(slynk-backend:with-symbol 'definition-source-form-number 'sb-introspect) 945 (setf (values start end) 946 (and (sb-introspect:definition-source-form-number definition-source) 947 (string-form-number-position definition-source emacs-string))) 948 (setf (values start end) 949 (source-path-string-position form-path emacs-string)))) 950 (setf start character-offset 951 end most-positive-fixnum))) 952 (make-location 953 `(:buffer ,emacs-buffer) 954 `(:offset ,emacs-position ,start) 955 `(:snippet 956 ,(subseq emacs-string 957 start 958 (min end (+ start *source-snippet-size*))))))))) 959 960 (defun definition-source-file-location (definition-source) 961 (with-definition-source (pathname form-path character-offset plist 962 file-write-date) definition-source 963 (let* ((namestring (namestring (translate-logical-pathname pathname))) 964 (pos (or (and form-path 965 (or 966 #+#.(slynk-backend:with-symbol 'definition-source-form-number 'sb-introspect) 967 (and (sb-introspect:definition-source-form-number definition-source) 968 (ignore-errors (file-form-number-position definition-source))) 969 (ignore-errors 970 (source-file-position namestring file-write-date 971 form-path)))) 972 character-offset)) 973 (snippet (source-hint-snippet namestring file-write-date pos))) 974 (make-location `(:file ,namestring) 975 ;; /file positions/ in Common Lisp start from 976 ;; 0, buffer positions in Emacs start from 1. 977 `(:position ,(1+ pos)) 978 `(:snippet ,snippet))))) 979 980 (defun definition-source-buffer-and-file-location (definition-source) 981 (let ((buffer (definition-source-buffer-location definition-source)) 982 (file (definition-source-file-location definition-source))) 983 (make-location (list :buffer-and-file 984 (cadr (location-buffer buffer)) 985 (cadr (location-buffer file))) 986 (location-position buffer) 987 (location-hints buffer)))) 988 989 (defun definition-source-for-emacs (definition-source type name) 990 (with-definition-source (pathname form-path character-offset plist 991 file-write-date) 992 definition-source 993 (ecase (categorize-definition-source definition-source) 994 (:buffer-and-file 995 (definition-source-buffer-and-file-location definition-source)) 996 (:buffer 997 (definition-source-buffer-location definition-source)) 998 (:file 999 (definition-source-file-location definition-source)) 1000 (:file-without-position 1001 (make-location `(:file ,(namestring 1002 (translate-logical-pathname pathname))) 1003 '(:position 1) 1004 (when (eql type :function) 1005 `(:snippet ,(format nil "(defun ~a " 1006 (symbol-name name)))))) 1007 (:invalid 1008 (error "DEFINITION-SOURCE of ~(~A~) ~A did not contain ~ 1009 meaningful information." 1010 type name))))) 1011 1012 (defun source-file-position (filename write-date form-path) 1013 (let ((source (get-source-code filename write-date)) 1014 (*readtable* (guess-readtable-for-filename filename))) 1015 (with-debootstrapping 1016 (source-path-string-position form-path source)))) 1017 1018 (defun source-hint-snippet (filename write-date position) 1019 (read-snippet-from-string (get-source-code filename write-date) position)) 1020 1021 (defun function-source-location (function &optional name) 1022 (declare (type function function)) 1023 (definition-source-for-emacs (sb-introspect:find-definition-source function) 1024 :function 1025 (or name (function-name function)))) 1026 1027 (defun setf-expander (symbol) 1028 (or 1029 #+#.(slynk-sbcl::sbcl-with-setf-inverse-meta-info) 1030 (sb-int:info :setf :inverse symbol) 1031 (sb-int:info :setf :expander symbol))) 1032 1033 (defimplementation describe-symbol-for-emacs (symbol) 1034 "Return a plist describing SYMBOL. 1035 Return NIL if the symbol is unbound." 1036 (let ((result '())) 1037 (flet ((doc (kind) 1038 (or (documentation symbol kind) :not-documented)) 1039 (maybe-push (property value) 1040 (when value 1041 (setf result (list* property value result))))) 1042 (maybe-push 1043 :variable (multiple-value-bind (kind recorded-p) 1044 (sb-int:info :variable :kind symbol) 1045 (declare (ignore kind)) 1046 (if (or (boundp symbol) recorded-p) 1047 (doc 'variable)))) 1048 (when (fboundp symbol) 1049 (maybe-push 1050 (cond ((macro-function symbol) :macro) 1051 ((special-operator-p symbol) :special-operator) 1052 ((typep (fdefinition symbol) 'generic-function) 1053 :generic-function) 1054 (t :function)) 1055 (doc 'function))) 1056 (maybe-push 1057 :setf (and (setf-expander symbol) 1058 (doc 'setf))) 1059 (maybe-push 1060 :type (if (sb-int:info :type :kind symbol) 1061 (doc 'type))) 1062 result))) 1063 1064 (defimplementation describe-definition (symbol type) 1065 (case type 1066 (:variable 1067 (describe symbol)) 1068 (:function 1069 (describe (symbol-function symbol))) 1070 (:setf 1071 (describe (setf-expander symbol))) 1072 (:class 1073 (describe (find-class symbol))) 1074 (:type 1075 (describe (sb-kernel:values-specifier-type symbol))))) 1076 1077 #+#.(slynk-sbcl::sbcl-with-xref-p) 1078 (progn 1079 (defmacro defxref (name &optional fn-name) 1080 `(defimplementation ,name (what) 1081 (sanitize-xrefs 1082 (mapcar #'source-location-for-xref-data 1083 (,(find-symbol (symbol-name (if fn-name 1084 fn-name 1085 name)) 1086 "SB-INTROSPECT") 1087 what))))) 1088 (defxref who-calls) 1089 (defxref who-binds) 1090 (defxref who-sets) 1091 (defxref who-references) 1092 (defxref who-macroexpands) 1093 #+#.(slynk-backend:with-symbol 'who-specializes-directly 'sb-introspect) 1094 (defxref who-specializes who-specializes-directly)) 1095 1096 (defun source-location-for-xref-data (xref-data) 1097 (destructuring-bind (name . defsrc) xref-data 1098 (list name (converting-errors-to-error-location 1099 (definition-source-for-emacs defsrc 'function name))))) 1100 1101 (defimplementation list-callers (symbol) 1102 (let ((fn (fdefinition symbol))) 1103 (sanitize-xrefs 1104 (mapcar #'function-dspec (sb-introspect:find-function-callers fn))))) 1105 1106 (defimplementation list-callees (symbol) 1107 (let ((fn (fdefinition symbol))) 1108 (sanitize-xrefs 1109 (mapcar #'function-dspec (sb-introspect:find-function-callees fn))))) 1110 1111 (defun sanitize-xrefs (xrefs) 1112 (remove-duplicates 1113 (remove-if (lambda (f) 1114 (member f (ignored-xref-function-names))) 1115 (loop for entry in xrefs 1116 for name = (car entry) 1117 collect (if (and (consp name) 1118 (member (car name) 1119 '(sb-pcl::fast-method 1120 sb-pcl::slow-method 1121 sb-pcl::method))) 1122 (cons (cons 'defmethod (cdr name)) 1123 (cdr entry)) 1124 entry)) 1125 :key #'car) 1126 :test (lambda (a b) 1127 (and (eq (first a) (first b)) 1128 (equal (second a) (second b)))))) 1129 1130 (defun ignored-xref-function-names () 1131 #-#.(slynk-sbcl::sbcl-with-new-stepper-p) 1132 '(nil sb-c::step-form sb-c::step-values) 1133 #+#.(slynk-sbcl::sbcl-with-new-stepper-p) 1134 '(nil)) 1135 1136 (defun function-dspec (fn) 1137 "Describe where the function FN was defined. 1138 Return a list of the form (NAME LOCATION)." 1139 (let ((name (function-name fn))) 1140 (list name (converting-errors-to-error-location 1141 (function-source-location fn name))))) 1142 1143 ;;; macroexpansion 1144 1145 (defimplementation macroexpand-all (form &optional env) 1146 (sb-cltl2:macroexpand-all form env)) 1147 1148 1149 ;;; Debugging 1150 1151 ;;; Notice that SB-EXT:*INVOKE-DEBUGGER-HOOK* is slightly stronger 1152 ;;; than just a hook into BREAK. In particular, it'll make 1153 ;;; (LET ((*DEBUGGER-HOOK* NIL)) ..error..) drop into SLY-DB rather 1154 ;;; than the native debugger. That should probably be considered a 1155 ;;; feature. 1156 1157 (defun make-invoke-debugger-hook (hook) 1158 (when hook 1159 #'(sb-int:named-lambda slynk-invoke-debugger-hook 1160 (condition old-hook) 1161 (if *debugger-hook* 1162 nil ; decline, *DEBUGGER-HOOK* will be tried next. 1163 (funcall hook condition old-hook))))) 1164 1165 (defun set-break-hook (hook) 1166 (setq sb-ext:*invoke-debugger-hook* (make-invoke-debugger-hook hook))) 1167 1168 (defun call-with-break-hook (hook continuation) 1169 (let ((sb-ext:*invoke-debugger-hook* (make-invoke-debugger-hook hook))) 1170 (funcall continuation))) 1171 1172 (defimplementation install-debugger-globally (function) 1173 (setq *debugger-hook* function) 1174 (set-break-hook function)) 1175 1176 (defimplementation condition-extras (condition) 1177 (cond #+#.(slynk-sbcl::sbcl-with-new-stepper-p) 1178 ((typep condition 'sb-impl::step-form-condition) 1179 `((:show-frame-source 0))) 1180 ((typep condition 'sb-int:reference-condition) 1181 (let ((refs (sb-int:reference-condition-references condition))) 1182 (if refs 1183 `((:references ,(externalize-reference refs)))))))) 1184 1185 (defun externalize-reference (ref) 1186 (etypecase ref 1187 (null nil) 1188 (cons (cons (externalize-reference (car ref)) 1189 (externalize-reference (cdr ref)))) 1190 ((or string number) ref) 1191 (symbol 1192 (cond ((eq (symbol-package ref) (symbol-package :test)) 1193 ref) 1194 (t (symbol-name ref)))))) 1195 1196 (defvar *sly-db-stack-top*) 1197 1198 (defimplementation call-with-debugging-environment (debugger-loop-fn) 1199 (declare (type function debugger-loop-fn)) 1200 (let ((*sly-db-stack-top* 1201 (if (and (not *debug-slynk-backend*) 1202 sb-debug:*stack-top-hint*) 1203 #+#.(slynk-backend:with-symbol 'resolve-stack-top-hint 'sb-debug) 1204 (sb-debug::resolve-stack-top-hint) 1205 #-#.(slynk-backend:with-symbol 'resolve-stack-top-hint 'sb-debug) 1206 sb-debug:*stack-top-hint* 1207 (sb-di:top-frame))) 1208 (sb-debug:*stack-top-hint* nil)) 1209 (handler-bind ((sb-di:debug-condition 1210 (lambda (condition) 1211 (signal 'sly-db-condition 1212 :original-condition condition)))) 1213 (funcall debugger-loop-fn)))) 1214 1215 #+#.(slynk-sbcl::sbcl-with-new-stepper-p) 1216 (progn 1217 (defimplementation activate-stepping (frame) 1218 (declare (ignore frame)) 1219 (sb-impl::enable-stepping)) 1220 (defimplementation sly-db-stepper-condition-p (condition) 1221 (typep condition 'sb-ext:step-form-condition)) 1222 (defimplementation sly-db-step-into () 1223 (invoke-restart 'sb-ext:step-into)) 1224 (defimplementation sly-db-step-next () 1225 (invoke-restart 'sb-ext:step-next)) 1226 (defimplementation sly-db-step-out () 1227 (invoke-restart 'sb-ext:step-out))) 1228 1229 (defimplementation call-with-debugger-hook (hook fun) 1230 (let ((*debugger-hook* hook) 1231 #+#.(slynk-sbcl::sbcl-with-new-stepper-p) 1232 (sb-ext:*stepper-hook* 1233 (lambda (condition) 1234 (typecase condition 1235 (sb-ext:step-form-condition 1236 (let ((sb-debug:*stack-top-hint* (sb-di::find-stepped-frame))) 1237 (sb-impl::invoke-debugger condition))))))) 1238 (handler-bind (#+#.(slynk-sbcl::sbcl-with-new-stepper-p) 1239 (sb-ext:step-condition #'sb-impl::invoke-stepper)) 1240 (call-with-break-hook hook fun)))) 1241 1242 (defun nth-frame (index) 1243 (do ((frame *sly-db-stack-top* (sb-di:frame-down frame)) 1244 (i index (1- i))) 1245 ((zerop i) frame))) 1246 1247 (defimplementation compute-backtrace (start end) 1248 "Return a list of frames starting with frame number START and 1249 continuing to frame number END or, if END is nil, the last frame on the 1250 stack." 1251 (let ((end (or end most-positive-fixnum))) 1252 (loop for f = (nth-frame start) then (sb-di:frame-down f) 1253 for i from start below end 1254 while f collect f))) 1255 1256 (defimplementation print-frame (frame stream) 1257 (sb-debug::print-frame-call frame stream 1258 :allow-other-keys t 1259 :emergency-best-effort t)) 1260 1261 (defimplementation frame-restartable-p (frame) 1262 #+#.(slynk-sbcl::sbcl-with-restart-frame) 1263 (not (null (sb-debug:frame-has-debug-tag-p frame)))) 1264 1265 (defimplementation frame-arguments (frame) 1266 (multiple-value-bind (name args) 1267 (sb-debug::frame-call (nth-frame frame)) 1268 (declare (ignore name)) 1269 (values-list args))) 1270 1271 ;;;; Code-location -> source-location translation 1272 1273 ;;; If debug-block info is avaibale, we determine the file position of 1274 ;;; the source-path for a code-location. If the code was compiled 1275 ;;; with C-c C-c, we have to search the position in the source string. 1276 ;;; If there's no debug-block info, we return the (less precise) 1277 ;;; source-location of the corresponding function. 1278 1279 (defun code-location-source-location (code-location) 1280 (let* ((dsource (sb-di:code-location-debug-source code-location)) 1281 (plist (sb-c::debug-source-plist dsource)) 1282 (package (getf plist :emacs-package)) 1283 (*package* (or (and package 1284 (find-package package)) 1285 *package*))) 1286 (if (getf plist :emacs-buffer) 1287 (emacs-buffer-source-location code-location plist) 1288 #+#.(slynk-backend:with-symbol 'debug-source-from 'sb-di) 1289 (ecase (sb-di:debug-source-from dsource) 1290 (:file (file-source-location code-location)) 1291 (:lisp (lisp-source-location code-location))) 1292 #-#.(slynk-backend:with-symbol 'debug-source-from 'sb-di) 1293 (if (sb-di:debug-source-namestring dsource) 1294 (file-source-location code-location) 1295 (lisp-source-location code-location))))) 1296 1297 ;;; FIXME: The naming policy of source-location functions is a bit 1298 ;;; fuzzy: we have FUNCTION-SOURCE-LOCATION which returns the 1299 ;;; source-location for a function, and we also have FILE-SOURCE-LOCATION &co 1300 ;;; which returns the source location for a _code-location_. 1301 ;;; 1302 ;;; Maybe these should be named code-location-file-source-location, 1303 ;;; etc, turned into generic functions, or something. In the very 1304 ;;; least the names should indicate the main entry point vs. helper 1305 ;;; status. 1306 1307 (defun file-source-location (code-location) 1308 (if (code-location-has-debug-block-info-p code-location) 1309 (source-file-source-location code-location) 1310 (fallback-source-location code-location))) 1311 1312 (defun fallback-source-location (code-location) 1313 (let ((fun (code-location-debug-fun-fun code-location))) 1314 (cond (fun (function-source-location fun)) 1315 (t (error "Cannot find source location for: ~A " code-location))))) 1316 1317 (defun lisp-source-location (code-location) 1318 (let ((source (prin1-to-string 1319 (sb-debug::code-location-source-form code-location 100))) 1320 (condition (slynk-value '*slynk-debugger-condition*))) 1321 (if (and (typep condition 'sb-impl::step-form-condition) 1322 (search "SB-IMPL::WITH-STEPPING-ENABLED" source 1323 :test #'char-equal) 1324 (search "SB-IMPL::STEP-FINISHED" source :test #'char-equal)) 1325 ;; The initial form is utterly uninteresting -- and almost 1326 ;; certainly right there in the REPL. 1327 (make-error-location "Stepping...") 1328 (make-location `(:source-form ,source) '(:position 1))))) 1329 1330 (defun emacs-buffer-source-location (code-location plist) 1331 (if (code-location-has-debug-block-info-p code-location) 1332 (destructuring-bind (&key emacs-buffer emacs-position emacs-string 1333 &allow-other-keys) 1334 plist 1335 (let* ((pos (string-source-position code-location emacs-string)) 1336 (snipped (read-snippet-from-string emacs-string pos))) 1337 (make-location `(:buffer ,emacs-buffer) 1338 `(:offset ,emacs-position ,pos) 1339 `(:snippet ,snipped)))) 1340 (fallback-source-location code-location))) 1341 1342 (defun source-file-source-location (code-location) 1343 (let* ((code-date (code-location-debug-source-created code-location)) 1344 (filename (code-location-debug-source-name code-location)) 1345 (*readtable* (guess-readtable-for-filename filename)) 1346 (source-code (get-source-code filename code-date))) 1347 (with-debootstrapping 1348 (with-input-from-string (s source-code) 1349 (let* ((pos (stream-source-position code-location s)) 1350 (snippet (read-snippet s pos))) 1351 (make-location `(:file ,filename) 1352 `(:position ,pos) 1353 `(:snippet ,snippet))))))) 1354 1355 (defun code-location-debug-source-name (code-location) 1356 (namestring (truename (#.(slynk-backend:choose-symbol 1357 'sb-c 'debug-source-name 1358 'sb-c 'debug-source-namestring) 1359 (sb-di::code-location-debug-source code-location))))) 1360 1361 (defun code-location-debug-source-created (code-location) 1362 (sb-c::debug-source-created 1363 (sb-di::code-location-debug-source code-location))) 1364 1365 (defun code-location-debug-fun-fun (code-location) 1366 (sb-di:debug-fun-fun (sb-di:code-location-debug-fun code-location))) 1367 1368 (defun code-location-has-debug-block-info-p (code-location) 1369 (handler-case 1370 (progn (sb-di:code-location-debug-block code-location) 1371 t) 1372 (sb-di:no-debug-blocks () nil))) 1373 1374 (defun stream-source-position (code-location stream) 1375 (let* ((cloc (sb-debug::maybe-block-start-location code-location)) 1376 (tlf-number (sb-di::code-location-toplevel-form-offset cloc)) 1377 (form-number (sb-di::code-location-form-number cloc))) 1378 (multiple-value-bind (tlf pos-map) (read-source-form tlf-number stream) 1379 (let* ((path-table (sb-di::form-number-translations tlf 0)) 1380 (path (cond ((<= (length path-table) form-number) 1381 (warn "inconsistent form-number-translations") 1382 (list 0)) 1383 (t 1384 (reverse (cdr (aref path-table form-number))))))) 1385 (source-path-source-position path tlf pos-map))))) 1386 1387 (defun string-source-position (code-location string) 1388 (with-input-from-string (s string) 1389 (stream-source-position code-location s))) 1390 1391 ;;; source-path-file-position and friends are in slynk-source-path-parser 1392 1393 (defimplementation frame-source-location (index) 1394 (converting-errors-to-error-location 1395 (code-location-source-location 1396 (sb-di:frame-code-location (nth-frame index))))) 1397 1398 (defvar *keep-non-valid-locals* nil) 1399 1400 (defun frame-debug-vars (frame) 1401 "Return a vector of debug-variables in frame." 1402 (let* ((all-vars (sb-di::debug-fun-debug-vars (sb-di:frame-debug-fun frame))) 1403 (loc (sb-di:frame-code-location frame)) 1404 (vars (if *keep-non-valid-locals* 1405 all-vars 1406 (remove-if (lambda (var) 1407 (ecase (sb-di:debug-var-validity var loc) 1408 (:valid nil) 1409 ((:invalid :unknown) t))) 1410 all-vars))) 1411 more-context 1412 more-count) 1413 (values (when vars 1414 (loop for v across vars 1415 unless 1416 (case (debug-var-info v) 1417 (:more-context 1418 (setf more-context (debug-var-value v frame loc)) 1419 t) 1420 (:more-count 1421 (setf more-count (debug-var-value v frame loc)) 1422 t)) 1423 collect v)) 1424 more-context more-count))) 1425 1426 (defun debug-var-value (var frame location) 1427 (ecase (sb-di:debug-var-validity var location) 1428 (:valid (sb-di:debug-var-value var frame)) 1429 ((:invalid :unknown) ':<not-available>))) 1430 1431 (defun debug-var-info (var) 1432 ;; Introduced by SBCL 1.0.49.76. 1433 (let ((s (find-symbol "DEBUG-VAR-INFO" :sb-di))) 1434 (when (and s (fboundp s)) 1435 (funcall s var)))) 1436 1437 (defimplementation frame-locals (index) 1438 (let* ((frame (nth-frame index)) 1439 (loc (sb-di:frame-code-location frame))) 1440 (multiple-value-bind (vars more-context more-count) 1441 (frame-debug-vars frame) 1442 (let ((locals 1443 (loop for v in vars 1444 collect 1445 (list :name (sb-di:debug-var-symbol v) 1446 :id (sb-di:debug-var-id v) 1447 :value (debug-var-value v frame loc))))) 1448 (if (and more-context more-count) 1449 (append locals 1450 (list 1451 (list :name 1452 ;; Since SBCL 1.0.49.76 PREPROCESS-FOR-EVAL understands SB-DEBUG::MORE 1453 ;; specially. 1454 (or (find-symbol "MORE" :sb-debug) 'more) 1455 :id 0 1456 :value (multiple-value-list 1457 (sb-c:%more-arg-values 1458 more-context 1459 0 more-count))))) 1460 locals))))) 1461 1462 (defimplementation frame-var-value (frame var) 1463 (let ((frame (nth-frame frame))) 1464 (multiple-value-bind (vars more-context more-count) 1465 (frame-debug-vars frame) 1466 (let* ((loc (sb-di:frame-code-location frame)) 1467 (dvar (if (= var (length vars)) 1468 ;; If VAR is out of bounds, it must be the fake var 1469 ;; we made up for &MORE. 1470 (return-from frame-var-value 1471 (multiple-value-list (sb-c:%more-arg-values 1472 more-context 1473 0 more-count))) 1474 (nth var vars)))) 1475 (debug-var-value dvar frame loc))))) 1476 1477 (defimplementation frame-catch-tags (index) 1478 (mapcar #'car (sb-di:frame-catches (nth-frame index)))) 1479 1480 (defimplementation eval-in-frame (form index) 1481 (let ((frame (nth-frame index))) 1482 (funcall (the function 1483 (sb-di:preprocess-for-eval form 1484 (sb-di:frame-code-location frame))) 1485 frame))) 1486 1487 (defimplementation frame-package (frame-number) 1488 (let* ((frame (nth-frame frame-number)) 1489 (fun (sb-di:debug-fun-fun (sb-di:frame-debug-fun frame)))) 1490 (when fun 1491 (let ((name (function-name fun))) 1492 (typecase name 1493 (null nil) 1494 (symbol (symbol-package name)) 1495 ((cons (eql setf) (cons symbol)) (symbol-package (cadr name)))))))) 1496 1497 #+#.(slynk-sbcl::sbcl-with-restart-frame) 1498 (progn 1499 (defimplementation return-from-frame (index form) 1500 (let* ((frame (nth-frame index))) 1501 (cond ((sb-debug:frame-has-debug-tag-p frame) 1502 (let ((values (multiple-value-list (eval-in-frame form index)))) 1503 (sb-debug:unwind-to-frame-and-call frame 1504 (lambda () 1505 (values-list values))))) 1506 (t (format nil "Cannot return from frame: ~S" frame))))) 1507 1508 (defimplementation restart-frame (index) 1509 (let ((frame (nth-frame index))) 1510 (when (sb-debug:frame-has-debug-tag-p frame) 1511 (multiple-value-bind (fname args) (sb-debug::frame-call frame) 1512 (multiple-value-bind (fun arglist) 1513 (if (and (sb-int:legal-fun-name-p fname) (fboundp fname)) 1514 (values (fdefinition fname) args) 1515 (values (sb-di:debug-fun-fun (sb-di:frame-debug-fun frame)) 1516 (sb-debug::frame-args-as-list frame))) 1517 (when (functionp fun) 1518 (sb-debug:unwind-to-frame-and-call 1519 frame 1520 (lambda () 1521 ;; Ensure TCO. 1522 (declare (optimize (debug 0))) 1523 (apply fun arglist))))))) 1524 (format nil "Cannot restart frame: ~S" frame)))) 1525 1526 ;; FIXME: this implementation doesn't unwind the stack before 1527 ;; re-invoking the function, but it's better than no implementation at 1528 ;; all. 1529 #-#.(slynk-sbcl::sbcl-with-restart-frame) 1530 (progn 1531 (defun sb-debug-catch-tag-p (tag) 1532 (and (symbolp tag) 1533 (not (symbol-package tag)) 1534 (string= tag :sb-debug-catch-tag))) 1535 1536 (defimplementation return-from-frame (index form) 1537 (let* ((frame (nth-frame index)) 1538 (probe (assoc-if #'sb-debug-catch-tag-p 1539 (sb-di::frame-catches frame)))) 1540 (cond (probe (throw (car probe) (eval-in-frame form index))) 1541 (t (format nil "Cannot return from frame: ~S" frame))))) 1542 1543 (defimplementation restart-frame (index) 1544 (let ((frame (nth-frame index))) 1545 (return-from-frame index (sb-debug::frame-call-as-list frame))))) 1546 1547 ;;;;; reference-conditions 1548 1549 (defimplementation print-condition (condition stream) 1550 (let ((sb-int:*print-condition-references* nil)) 1551 (princ condition stream))) 1552 1553 1554 ;;;; Profiling 1555 1556 (defimplementation profile (fname) 1557 (when fname (eval `(sb-profile:profile ,fname)))) 1558 1559 (defimplementation unprofile (fname) 1560 (when fname (eval `(sb-profile:unprofile ,fname)))) 1561 1562 (defimplementation unprofile-all () 1563 (sb-profile:unprofile) 1564 "All functions unprofiled.") 1565 1566 (defimplementation profile-report () 1567 (sb-profile:report)) 1568 1569 (defimplementation profile-reset () 1570 (sb-profile:reset) 1571 "Reset profiling counters.") 1572 1573 (defimplementation profiled-functions () 1574 (sb-profile:profile)) 1575 1576 (defimplementation profile-package (package callers methods) 1577 (declare (ignore callers methods)) 1578 (eval `(sb-profile:profile ,(package-name (find-package package))))) 1579 1580 1581 ;;;; Inspector 1582 1583 (defmethod emacs-inspect ((o t)) 1584 (cond ((sb-di::indirect-value-cell-p o) 1585 (label-value-line* (:value (sb-kernel:value-cell-ref o)))) 1586 (t 1587 (multiple-value-bind (text label parts) (sb-impl::inspected-parts o) 1588 (list* (string-right-trim '(#\Newline) text) 1589 '(:newline) 1590 (if label 1591 (loop for (l . v) in parts 1592 append (label-value-line l v)) 1593 (loop for value in parts 1594 for i from 0 1595 append (label-value-line i value)))))))) 1596 1597 (defmethod emacs-inspect ((o function)) 1598 (cond ((sb-kernel:simple-fun-p o) 1599 (label-value-line* 1600 (:name (sb-kernel:%simple-fun-name o)) 1601 (:arglist (sb-kernel:%simple-fun-arglist o)) 1602 (:type (sb-kernel:%simple-fun-type o)) 1603 (:code (sb-kernel:fun-code-header o)) 1604 (:documentation (documentation o t)))) 1605 ((sb-kernel:closurep o) 1606 (append 1607 (label-value-line :function (sb-kernel:%closure-fun o)) 1608 `("Closed over values:" (:newline)) 1609 (loop for i below (1- (sb-kernel:get-closure-length o)) 1610 append (label-value-line 1611 i (sb-kernel:%closure-index-ref o i))))) 1612 (t (call-next-method o)))) 1613 1614 (defmethod emacs-inspect ((o sb-kernel:code-component)) 1615 (append 1616 (label-value-line* 1617 (:code-size (sb-kernel:%code-code-size o)) 1618 (:debug-info (sb-kernel:%code-debug-info o))) 1619 `("Constants:" (:newline)) 1620 (loop for i from sb-vm:code-constants-offset 1621 below 1622 (#.(slynk-backend:choose-symbol 'sb-kernel 'code-header-words 1623 'sb-kernel 'get-header-data) 1624 o) 1625 append (label-value-line i (sb-kernel:code-header-ref o i))) 1626 `("Code:" (:newline) 1627 ,(with-output-to-string (s) 1628 (sb-disassem:disassemble-code-component o :stream s))))) 1629 1630 (defmethod emacs-inspect ((o sb-ext:weak-pointer)) 1631 (label-value-line* 1632 (:value (sb-ext:weak-pointer-value o)))) 1633 1634 (defmethod emacs-inspect ((o sb-kernel:fdefn)) 1635 (label-value-line* 1636 (:name (sb-kernel:fdefn-name o)) 1637 (:function (sb-kernel:fdefn-fun o)))) 1638 1639 (defmethod emacs-inspect :around ((o generic-function)) 1640 (append 1641 (call-next-method) 1642 (label-value-line* 1643 (:pretty-arglist (sb-pcl::generic-function-pretty-arglist o)) 1644 (:initial-methods (sb-pcl::generic-function-initial-methods o)) 1645 ))) 1646 1647 1648 ;;;; Multiprocessing 1649 1650 #+(and sb-thread 1651 #.(slynk-backend:with-symbol "THREAD-NAME" "SB-THREAD")) 1652 (progn 1653 (defvar *thread-id-counter* 0) 1654 1655 (defvar *thread-id-counter-lock* 1656 (sb-thread:make-mutex :name "thread id counter lock")) 1657 1658 (defun next-thread-id () 1659 (sb-thread:with-mutex (*thread-id-counter-lock*) 1660 (incf *thread-id-counter*))) 1661 1662 (defvar *thread-id-map* (make-hash-table)) 1663 1664 ;; This should be a thread -> id map but as weak keys are not 1665 ;; supported it is id -> map instead. 1666 (defvar *thread-id-map-lock* 1667 (sb-thread:make-mutex :name "thread id map lock")) 1668 1669 (defimplementation spawn (fn &key name) 1670 (sb-thread:make-thread fn :name name)) 1671 1672 (defimplementation thread-id (thread) 1673 (block thread-id 1674 (sb-thread:with-mutex (*thread-id-map-lock*) 1675 (loop for id being the hash-key in *thread-id-map* 1676 using (hash-value thread-pointer) 1677 do 1678 (let ((maybe-thread (sb-ext:weak-pointer-value thread-pointer))) 1679 (cond ((null maybe-thread) 1680 ;; the value is gc'd, remove it manually 1681 (remhash id *thread-id-map*)) 1682 ((eq thread maybe-thread) 1683 (return-from thread-id id))))) 1684 ;; lazy numbering 1685 (let ((id (next-thread-id))) 1686 (setf (gethash id *thread-id-map*) (sb-ext:make-weak-pointer thread)) 1687 id)))) 1688 1689 (defimplementation find-thread (id) 1690 (sb-thread:with-mutex (*thread-id-map-lock*) 1691 (let ((thread-pointer (gethash id *thread-id-map*))) 1692 (if thread-pointer 1693 (let ((maybe-thread (sb-ext:weak-pointer-value thread-pointer))) 1694 (if maybe-thread 1695 maybe-thread 1696 ;; the value is gc'd, remove it manually 1697 (progn 1698 (remhash id *thread-id-map*) 1699 nil))) 1700 nil)))) 1701 1702 (defimplementation thread-name (thread) 1703 ;; sometimes the name is not a string (e.g. NIL) 1704 (princ-to-string (sb-thread:thread-name thread))) 1705 1706 (defimplementation thread-status (thread) 1707 (if (sb-thread:thread-alive-p thread) 1708 "Running" 1709 "Stopped")) 1710 1711 (defimplementation make-lock (&key name) 1712 (sb-thread:make-mutex :name name)) 1713 1714 (defimplementation call-with-lock-held (lock function) 1715 (declare (type function function)) 1716 (sb-thread:with-recursive-lock (lock) (funcall function))) 1717 1718 (defimplementation current-thread () 1719 sb-thread:*current-thread*) 1720 1721 (defimplementation all-threads () 1722 (sb-thread:list-all-threads)) 1723 1724 (defimplementation interrupt-thread (thread fn) 1725 (sb-thread:interrupt-thread thread fn)) 1726 1727 (defimplementation kill-thread (thread) 1728 (sb-thread:terminate-thread thread)) 1729 1730 (defimplementation thread-alive-p (thread) 1731 (sb-thread:thread-alive-p thread)) 1732 1733 (defvar *mailbox-lock* (sb-thread:make-mutex :name "mailbox lock")) 1734 (defvar *mailboxes* (list)) 1735 (declaim (type list *mailboxes*)) 1736 1737 (defstruct (mailbox (:conc-name mailbox.)) 1738 thread 1739 (mutex (sb-thread:make-mutex)) 1740 (waitqueue (sb-thread:make-waitqueue)) 1741 (queue '() :type list)) 1742 1743 (defun mailbox (thread) 1744 "Return THREAD's mailbox." 1745 (sb-thread:with-mutex (*mailbox-lock*) 1746 (or (find thread *mailboxes* :key #'mailbox.thread) 1747 (let ((mb (make-mailbox :thread thread))) 1748 (push mb *mailboxes*) 1749 mb)))) 1750 1751 (defimplementation wake-thread (thread) 1752 (let* ((mbox (mailbox thread)) 1753 (mutex (mailbox.mutex mbox))) 1754 (sb-thread:with-recursive-lock (mutex) 1755 (sb-thread:condition-broadcast (mailbox.waitqueue mbox))))) 1756 1757 (defimplementation send (thread message) 1758 (let* ((mbox (mailbox thread)) 1759 (mutex (mailbox.mutex mbox))) 1760 (sb-thread:with-mutex (mutex) 1761 (setf (mailbox.queue mbox) 1762 (nconc (mailbox.queue mbox) (list message))) 1763 (sb-thread:condition-broadcast (mailbox.waitqueue mbox))))) 1764 1765 (defimplementation receive-if (test &optional timeout) 1766 (let* ((mbox (mailbox (current-thread))) 1767 (mutex (mailbox.mutex mbox)) 1768 (waitq (mailbox.waitqueue mbox))) 1769 (assert (or (not timeout) (eq timeout t))) 1770 (loop 1771 (check-sly-interrupts) 1772 (sb-thread:with-mutex (mutex) 1773 (let* ((q (mailbox.queue mbox)) 1774 (tail (member-if test q))) 1775 (when tail 1776 (setf (mailbox.queue mbox) (nconc (ldiff q tail) (cdr tail))) 1777 (return (car tail)))) 1778 (when (eq timeout t) (return (values nil t))) 1779 (sb-thread:condition-wait waitq mutex))))) 1780 1781 (let ((alist '()) 1782 (mutex (sb-thread:make-mutex :name "register-thread"))) 1783 1784 (defimplementation register-thread (name thread) 1785 (declare (type symbol name)) 1786 (sb-thread:with-mutex (mutex) 1787 (etypecase thread 1788 (null 1789 (setf alist (delete name alist :key #'car))) 1790 (sb-thread:thread 1791 (let ((probe (assoc name alist))) 1792 (cond (probe (setf (cdr probe) thread)) 1793 (t (setf alist (acons name thread alist)))))))) 1794 nil) 1795 1796 (defimplementation find-registered (name) 1797 (sb-thread:with-mutex (mutex) 1798 (cdr (assoc name alist)))))) 1799 1800 (defimplementation quit-lisp () 1801 #+#.(slynk-backend:with-symbol 'exit 'sb-ext) 1802 (sb-ext:exit) 1803 #-#.(slynk-backend:with-symbol 'exit 'sb-ext) 1804 (progn 1805 #+sb-thread 1806 (dolist (thread (remove (current-thread) (all-threads))) 1807 (ignore-errors (sb-thread:terminate-thread thread))) 1808 (sb-ext:quit))) 1809 1810 1811 1812 ;;Trace implementations 1813 ;;In SBCL, we have: 1814 ;; (trace <name>) 1815 ;; (trace :methods '<name>) ;to trace all methods of the gf <name> 1816 ;; (trace (method <name> <qualifier>? (<specializer>+))) 1817 ;; <name> can be a normal name or a (setf name) 1818 1819 (defun toggle-trace-aux (fspec &rest args) 1820 (cond ((member fspec (eval '(trace)) :test #'equal) 1821 (eval `(untrace ,fspec)) 1822 (format nil "~S is now untraced." fspec)) 1823 (t 1824 (eval `(trace ,@(if args `(:encapsulate nil) (list)) ,fspec ,@args)) 1825 (format nil "~S is now traced." fspec)))) 1826 1827 (defun process-fspec (fspec) 1828 (cond ((consp fspec) 1829 (ecase (first fspec) 1830 ((:defun :defgeneric) (second fspec)) 1831 ((:defmethod) `(method ,@(rest fspec))) 1832 ((:labels) `(labels ,(process-fspec (second fspec)) ,(third fspec))) 1833 ((:flet) `(flet ,(process-fspec (second fspec)) ,(third fspec))))) 1834 (t 1835 fspec))) 1836 1837 (defimplementation toggle-trace (spec) 1838 (ecase (car spec) 1839 ((setf) 1840 (toggle-trace-aux spec)) 1841 ((:defmethod) 1842 (toggle-trace-aux `(sb-pcl::fast-method ,@(rest (process-fspec spec))))) 1843 ((:defgeneric) 1844 (toggle-trace-aux (second spec) :methods t)) 1845 ((:call) 1846 (destructuring-bind (caller callee) (cdr spec) 1847 (toggle-trace-aux callee :wherein (list (process-fspec caller))))))) 1848 1849 ;;; Weak datastructures 1850 1851 (defimplementation make-weak-key-hash-table (&rest args) 1852 #+#.(slynk-sbcl::sbcl-with-weak-hash-tables) 1853 (apply #'make-hash-table :weakness :key args) 1854 #-#.(slynk-sbcl::sbcl-with-weak-hash-tables) 1855 (apply #'make-hash-table args)) 1856 1857 (defimplementation make-weak-value-hash-table (&rest args) 1858 #+#.(slynk-sbcl::sbcl-with-weak-hash-tables) 1859 (apply #'make-hash-table :weakness :value args) 1860 #-#.(slynk-sbcl::sbcl-with-weak-hash-tables) 1861 (apply #'make-hash-table args)) 1862 1863 (defimplementation hash-table-weakness (hashtable) 1864 #+#.(slynk-sbcl::sbcl-with-weak-hash-tables) 1865 (sb-ext:hash-table-weakness hashtable)) 1866 1867 ;;; Floating point 1868 1869 (defimplementation float-nan-p (float) 1870 (sb-ext:float-nan-p float)) 1871 1872 (defimplementation float-infinity-p (float) 1873 (sb-ext:float-infinity-p float)) 1874 1875 #-win32 1876 (defimplementation save-image (filename &optional restart-function) 1877 (flet ((restart-sbcl () 1878 (sb-debug::enable-debugger) 1879 (setf sb-impl::*descriptor-handlers* nil) 1880 (funcall restart-function))) 1881 (let ((pid (sb-posix:fork))) 1882 (cond ((= pid 0) 1883 (sb-debug::disable-debugger) 1884 (apply #'sb-ext:save-lisp-and-die filename 1885 (when restart-function 1886 (list :toplevel #'restart-sbcl)))) 1887 (t 1888 (multiple-value-bind (rpid status) (sb-posix:waitpid pid 0) 1889 (assert (= pid rpid)) 1890 (assert (and (sb-posix:wifexited status) 1891 (zerop (sb-posix:wexitstatus status)))))))))) 1892 1893 #+unix 1894 (progn 1895 (sb-alien:define-alien-routine ("execv" sys-execv) sb-alien:int 1896 (program sb-alien:c-string) 1897 (argv (* sb-alien:c-string))) 1898 1899 (defun execv (program args) 1900 "Replace current executable with another one." 1901 (let ((a-args (sb-alien:make-alien sb-alien:c-string 1902 (+ 1 (length args))))) 1903 (unwind-protect 1904 (progn 1905 (loop for index from 0 by 1 1906 and item in (append args '(nil)) 1907 do (setf (sb-alien:deref a-args index) 1908 item)) 1909 (when (minusp 1910 (sys-execv program a-args)) 1911 (error "execv(3) returned."))) 1912 (sb-alien:free-alien a-args)))) 1913 1914 (defun runtime-pathname () 1915 #+#.(slynk-backend:with-symbol 1916 '*runtime-pathname* 'sb-ext) 1917 sb-ext:*runtime-pathname* 1918 #-#.(slynk-backend:with-symbol 1919 '*runtime-pathname* 'sb-ext) 1920 (car sb-ext:*posix-argv*)) 1921 1922 (defimplementation exec-image (image-file args) 1923 (loop with fd-arg = 1924 (loop for arg in args 1925 and key = "" then arg 1926 when (string-equal key "--slynk-fd") 1927 return (parse-integer arg)) 1928 for my-fd from 3 to 1024 1929 when (/= my-fd fd-arg) 1930 do (ignore-errors (sb-posix:fcntl my-fd sb-posix:f-setfd 1))) 1931 (let* ((self-string (pathname-to-filename (runtime-pathname)))) 1932 (execv 1933 self-string 1934 (apply 'list self-string "--core" image-file args))))) 1935 1936 (defimplementation make-fd-stream (fd external-format) 1937 (sb-sys:make-fd-stream fd :input t :output t 1938 :element-type 'character 1939 :buffering :full 1940 :dual-channel-p t 1941 :external-format external-format)) 1942 1943 #-win32 1944 (defimplementation background-save-image (filename &key restart-function 1945 completion-function) 1946 (flet ((restart-sbcl () 1947 (sb-debug::enable-debugger) 1948 (setf sb-impl::*descriptor-handlers* nil) 1949 (funcall restart-function))) 1950 (multiple-value-bind (pipe-in pipe-out) (sb-posix:pipe) 1951 (let ((pid (sb-posix:fork))) 1952 (cond ((= pid 0) 1953 (sb-posix:close pipe-in) 1954 (sb-debug::disable-debugger) 1955 (apply #'sb-ext:save-lisp-and-die filename 1956 (when restart-function 1957 (list :toplevel #'restart-sbcl)))) 1958 (t 1959 (sb-posix:close pipe-out) 1960 (sb-sys:add-fd-handler 1961 pipe-in :input 1962 (lambda (fd) 1963 (sb-sys:invalidate-descriptor fd) 1964 (sb-posix:close fd) 1965 (multiple-value-bind (rpid status) (sb-posix:waitpid pid 0) 1966 (assert (= pid rpid)) 1967 (assert (sb-posix:wifexited status)) 1968 (funcall completion-function 1969 (zerop (sb-posix:wexitstatus status)))))))))))) 1970 1971 (pushnew 'deinit-log-output sb-ext:*save-hooks*) 1972 1973 1974 ;;;; wrap interface implementation 1975 1976 (defimplementation wrap (spec indicator &key before after replace) 1977 (when (wrapped-p spec indicator) 1978 (warn "~a already wrapped with indicator ~a, unwrapping first" 1979 spec indicator) 1980 (sb-int:unencapsulate spec indicator)) 1981 (sb-int:encapsulate spec indicator 1982 #-#.(slynk-backend:with-symbol 'arg-list 'sb-int) 1983 (lambda (function &rest args) 1984 (sbcl-wrap spec before after replace function args)) 1985 #+#.(slynk-backend:with-symbol 'arg-list 'sb-int) 1986 (if (sbcl-version>= 1 1 16) 1987 (lambda () 1988 (sbcl-wrap spec before after replace 1989 (symbol-value 'sb-int:basic-definition) 1990 (symbol-value 'sb-int:arg-list))) 1991 `(sbcl-wrap ',spec ,before ,after ,replace 1992 (symbol-value 'sb-int:basic-definition) 1993 (symbol-value 'sb-int:arg-list)))) 1994 (symbol-function spec)) 1995 1996 (defimplementation unwrap (spec indicator) 1997 (sb-int:unencapsulate spec indicator)) 1998 1999 (defimplementation wrapped-p (spec indicator) 2000 (sb-int:encapsulated-p spec indicator)) 2001 2002 (defun sbcl-wrap (spec before after replace function args) 2003 (declare (ignore spec)) 2004 (let (retlist completed) 2005 (unwind-protect 2006 (progn 2007 (when before 2008 (funcall before args)) 2009 (setq retlist (multiple-value-list (if replace 2010 (funcall replace 2011 args) 2012 (apply function args)))) 2013 (setq completed t) 2014 (values-list retlist)) 2015 (when after 2016 (funcall after (if completed retlist :exited-non-locally)))))) 2017 2018 #+#.(slynk-backend:with-symbol 'comma-expr 'sb-impl) 2019 (progn 2020 (defmethod sexp-in-bounds-p ((s sb-impl::comma) i) 2021 (sexp-in-bounds-p (sb-impl::comma-expr s) i)) 2022 2023 (defmethod sexp-ref ((s sb-impl::comma) i) 2024 (sexp-ref (sb-impl::comma-expr s) i)))