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