1 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; This software is derived from software originally released by Xerox
5 ;;;; Corporation. Copyright and release statements follow. Later modifications
6 ;;;; to the software are in the public domain and are provided with
7 ;;;; absolutely no warranty. See the COPYING and CREDITS files for more
10 ;;;; copyright information from original PCL sources:
12 ;;;; Copyright (c) 1985, 1986, 1987, 1988, 1989, 1990 Xerox Corporation.
13 ;;;; All rights reserved.
15 ;;;; Use and copying of this software and preparation of derivative works based
16 ;;;; upon this software are permitted. Any distribution of this software or
17 ;;;; derivative works must comply with all applicable United States export
20 ;;;; This software is made available AS IS, and Xerox Corporation makes no
21 ;;;; warranty about the software, its performance or its conformity to any
26 (defun get-method-function (method &optional method-alist wrappers
)
27 (let ((fn (cadr (assoc method method-alist
))))
29 (values fn nil nil nil
)
30 (multiple-value-bind (mf fmf
)
32 (early-method-function method
)
33 (values nil
(safe-method-fast-function method
)))
34 (let* ((pv-table (and fmf
(method-plist-value method
:pv-table
))))
35 (if (and fmf
(or (null pv-table
) wrappers
))
36 (let* ((pv-wrappers (when pv-table
37 (pv-wrappers-from-all-wrappers
39 (pv (when (and pv-table pv-wrappers
)
40 (pv-table-lookup pv-table pv-wrappers
))))
43 (or mf
(if (listp method
)
44 (bug "early method with no method-function")
45 (method-function method
)))
48 (defun make-effective-method-function (generic-function form
&optional
49 method-alist wrappers
)
50 (funcall (make-effective-method-function1 generic-function form
51 (not (null method-alist
))
52 (not (null wrappers
)))
53 method-alist wrappers
))
55 (defun make-effective-method-function1 (generic-function form
56 method-alist-p wrappers-p
)
58 (eq (car form
) 'call-method
)
59 (not (gf-requires-emf-keyword-checks generic-function
)))
60 (make-effective-method-function-simple generic-function form
)
61 ;; We have some sort of `real' effective method. Go off and get a
62 ;; compiled function for it. Most of the real hair here is done by
63 ;; the GET-FUN mechanism.
64 (make-effective-method-function-internal generic-function form
65 method-alist-p wrappers-p
)))
67 (defun make-effective-method-fun-type (generic-function
72 (eq (car form
) 'call-method
))
73 (let* ((cm-args (cdr form
))
74 (method (car cm-args
)))
76 (if (if (listp method
)
77 (eq (car method
) :early-method
)
81 (multiple-value-bind (mf fmf
)
83 (early-method-function method
)
84 (values nil
(safe-method-fast-function method
)))
86 (let* ((pv-table (and fmf
(method-plist-value method
:pv-table
))))
87 (if (and fmf
(or (null pv-table
) wrappers-p
))
90 (if (and (consp method
) (eq (car method
) 'make-method
))
91 (make-effective-method-fun-type
92 generic-function
(cadr method
) method-alist-p wrappers-p
)
96 (defun make-effective-method-function-simple
97 (generic-function form
&optional no-fmf-p
)
98 ;; The effective method is just a call to CALL-METHOD. This opens up
99 ;; the possibility of just using the method function of the method as
100 ;; the effective method function.
102 ;; But we have to be careful. If that method function will ask for
103 ;; the next methods we have to provide them. We do not look to see
104 ;; if there are next methods, we look at whether the method function
105 ;; asks about them. If it does, we must tell it whether there are
106 ;; or aren't to prevent the leaky next methods bug.
107 (let* ((cm-args (cdr form
))
108 (fmf-p (and (null no-fmf-p
)
109 (or (not (eq **boot-state
** 'complete
))
110 (gf-fast-method-function-p generic-function
))
111 (null (cddr cm-args
))))
112 (method (car cm-args
))
113 (cm-args1 (cdr cm-args
)))
114 (lambda (method-alist wrappers
)
115 (make-effective-method-function-simple1 generic-function
122 ;;; methods-tracing TODO:
124 ;;; 2. tracing method calls for non-fast-method-function calls
125 ;;; - [DONE] the calls themselves
126 ;;; - calls to the METHOD-FUNCTION of methods with fast functions
127 ;;; (e.g. from something implementing CALL-NEXT-METHOD; handle this with
128 ;;; some more smarts in %METHOD-FUNCTION objects?)
129 ;;; - calls to the METHOD-FUNCTION of methods without fast functions
130 ;;; (TRACE :METHODS T /could/ modify the METHOD-FUNCTION slot)
131 ;;; 4. tracing particular methods
132 ;;; - need an interface.
133 ;;; * (trace (method foo :around (t)))? [ how to trace the method and not
134 ;;; the generic function as a whole?]
135 ;;; * (trace :methods '((:around (t))) foo)? [probably not, interacts
136 ;;; poorly with TRACE arg handling]
137 ;;; 5. supporting non-munged arguments as an option
139 (defun method-trace-name (gf method
)
140 ;; KLUDGE: we abuse NIL as second argument to mean that this is a
141 ;; combined method (i.e. something resulting from MAKE-METHOD in a
142 ;; method combination, rather than CALL-METHOD on a method object).
144 `(method ,(generic-function-name gf
)
145 ,@(method-qualifiers method
)
146 ,(unparse-specializers gf
(method-specializers method
)))
147 `(combined-method ,(generic-function-name gf
))))
149 (defun maybe-trace-method (gf method fun fmf-p
)
150 (let ((m-name (when (plusp (hash-table-count sb-debug
::*traced-funs
*))
151 ;; KLUDGE: testing if *TRACE-FUNS* has anything anything to
152 ;; avoid calling METHOD-TRACE-NAME during PCL bootstrapping
153 ;; when the generic-function type is not yet defined.)
154 (method-trace-name gf method
))))
156 (sb-debug::retrace-local-funs m-name
))
157 (let ((info (when m-name
158 (or (gethash m-name sb-debug
::*traced-funs
*)
159 (let ((gf-info (gethash (or (generic-function-name gf
) gf
)
160 sb-debug
::*traced-funs
*)))
161 (when (and gf-info
(sb-debug::trace-info-methods gf-info
))
162 (let ((copy (copy-structure gf-info
)))
163 (setf (sb-debug::trace-info-what copy
) m-name
)
167 (apply #'sb-debug
::trace-method-call info fun fmf-p args
))
170 (defun make-emf-from-method
171 (gf method cm-args fmf-p
&optional method-alist wrappers
)
172 ;; Avoid style-warning about compiler-macro being unavailable.
173 (declare (notinline make-instance
))
174 (multiple-value-bind (mf real-mf-p fmf pv
)
175 (get-method-function method method-alist wrappers
)
177 (let* ((next-methods (car cm-args
))
178 (next (make-effective-method-function-simple1
179 gf
(car next-methods
)
180 (list* (cdr next-methods
) (cdr cm-args
))
181 fmf-p method-alist wrappers
))
182 (arg-info (method-plist-value method
:arg-info
))
183 (default (cons nil nil
))
184 (value (method-plist-value method
:constant-value default
))
185 (fun (maybe-trace-method gf method fmf t
)))
186 (if (eq value default
)
187 (make-fast-method-call
188 :function fun
:pv pv
:next-method-call next
:arg-info arg-info
)
189 (make-constant-fast-method-call
190 :function fun
:pv pv
:next-method-call next
191 :arg-info arg-info
:value value
)))
193 (flet ((frob-cm-arg (arg)
195 (eq (car arg
) :early-method
)
198 (if (and (consp arg
) (eq (car arg
) 'make-method
))
199 (let ((emf (make-effective-method-function
200 gf
(cadr arg
) method-alist wrappers
)))
203 (make-instance 'standard-method
204 :specializers nil
; XXX
205 :qualifiers nil
; XXX
206 :function
(method-call-function emf
)))
208 (let* ((fmf (fast-method-call-function emf
))
209 (fun (method-function-from-fast-method-call emf
))
210 (mf (%make-method-function fmf
)))
211 (setf (%funcallable-instance-fun mf
) fun
)
212 (make-instance 'standard-method
213 :specializers nil
; XXX
217 (let* ((default (cons nil nil
))
219 (method-plist-value method
:constant-value default
))
220 ;; FIXME: this is wrong. Very wrong. It assumes
221 ;; that the only place that can have make-method
222 ;; calls is in the list structure of the second
223 ;; argument to CALL-METHOD, but AMOP says that
224 ;; CALL-METHOD can be more complicated if
225 ;; COMPUTE-EFFECTIVE-METHOD (and presumably
226 ;; MAKE-METHOD-LAMBDA) is adjusted to match.
228 ;; On the other hand, it's a start, because
229 ;; without this calls to MAKE-METHOD in method
230 ;; combination where one of the methods is of a
231 ;; user-defined class don't work at all. -- CSR,
233 (args (cons (mapcar #'frob-cm-arg
(car cm-args
))
235 (fun (maybe-trace-method gf method mf nil
)))
236 (if (eq value default
)
237 (make-method-call :function fun
:call-method-args args
)
238 (make-constant-method-call
239 :function fun
:value value
:call-method-args args
))))
242 (defun make-effective-method-function-simple1
243 (gf method cm-args fmf-p
&optional method-alist wrappers
)
245 (if (if (listp method
)
246 (eq (car method
) :early-method
)
248 (make-emf-from-method gf method cm-args fmf-p method-alist wrappers
)
249 (if (and (consp method
) (eq (car method
) 'make-method
))
250 (make-effective-method-function gf
252 method-alist wrappers
)
255 (defvar *global-effective-method-gensyms
* ())
256 (defvar *rebound-effective-method-gensyms
*)
258 (defun get-effective-method-gensym ()
259 (or (pop *rebound-effective-method-gensyms
*)
260 (let ((new (pcl-symbolicate "EFFECTIVE-METHOD-GENSYM-"
261 (length *global-effective-method-gensyms
*))))
262 (setq *global-effective-method-gensyms
*
263 (append *global-effective-method-gensyms
* (list new
)))
266 (let ((*rebound-effective-method-gensyms
* ()))
267 (dotimes-fixnum (i 10) (get-effective-method-gensym)))
269 (defun expand-effective-method-function (gf effective-method
&optional env
)
270 (declare (ignore env
))
271 (declare (muffle-conditions code-deletion-note
))
272 (multiple-value-bind (nreq applyp
)
273 (get-generic-fun-info gf
)
274 (let ((ll (make-fast-method-call-lambda-list nreq applyp
))
276 (when (eq **boot-state
** 'complete
)
277 ;; Otherwise the METHOD-COMBINATION slot is not bound.
278 (let ((combin (generic-function-method-combination gf
)))
279 (and (long-method-combination-p combin
)
280 (long-method-combination-args-lambda-list combin
)))))
281 (name `(emf ,(generic-function-name gf
))))
284 (let* ((required (make-dfun-required-args nreq
))
287 (sb-c::%listify-rest-args
289 (the (and unsigned-byte fixnum
)
291 `(list ,@required
))))
292 `(named-lambda ,name
,ll
293 (declare (ignore .pv. .next-method-call.
))
294 (let ((.gf-args.
,gf-args
))
295 (declare (ignorable .gf-args.
))
296 ,effective-method
))))
298 `(named-lambda ,name
,ll
299 (declare (ignore .pv. .next-method-call.
))
300 (declare (ignorable ,@(make-dfun-required-args nreq
)
301 ,@(when applyp
'(.dfun-more-context. .dfun-more-count.
))))
302 ,effective-method
))))))
304 (defun expand-emf-call-method (gf form metatypes applyp env
)
305 (declare (ignore gf metatypes applyp env
))
306 `(call-method ,(cdr form
)))
308 (defmacro call-method
(&rest args
)
309 (declare (ignore args
))
310 ;; the PROGN is here to defend against premature macroexpansion by
312 `(progn (error "~S outside of a effective method form" 'call-method
)))
314 (defun make-effective-method-list-fun-type
315 (generic-function form method-alist-p wrappers-p
)
316 (if (every (lambda (form)
317 (eq 'fast-method-call
318 (make-effective-method-fun-type
319 generic-function form method-alist-p wrappers-p
)))
324 (defun memf-test-converter (form generic-function method-alist-p wrappers-p
)
325 (case (and (consp form
) (car form
))
327 (case (make-effective-method-fun-type
328 generic-function form method-alist-p wrappers-p
)
329 (fast-method-call '.fast-call-method.
)
332 (case (make-effective-method-list-fun-type
333 generic-function form method-alist-p wrappers-p
)
334 (fast-method-call '.fast-call-method-list.
)
335 (t '.call-method-list.
)))
336 (t (default-test-converter form
))))
338 ;;; CMUCL comment (2003-10-15):
340 ;;; This function is called via the GET-FUNCTION mechanism on forms
341 ;;; of an emf lambda. First value returned replaces FORM in the emf
342 ;;; lambda. Second value is a list of variable names that become
343 ;;; closure variables.
344 (defun memf-code-converter
345 (form generic-function metatypes applyp method-alist-p wrappers-p
)
346 (case (and (consp form
) (car form
))
348 (let ((gensym (get-effective-method-gensym)))
349 (values (make-emf-call
350 (length metatypes
) applyp gensym
351 (make-effective-method-fun-type
352 generic-function form method-alist-p wrappers-p
))
355 (let ((gensym (get-effective-method-gensym))
356 (type (make-effective-method-list-fun-type
357 generic-function form method-alist-p wrappers-p
)))
358 (values `(dolist (emf ,gensym nil
)
359 ,(make-emf-call (length metatypes
) applyp
'emf type
))
362 (default-code-converter form
))))
364 (defun memf-constant-converter (form generic-function
)
365 (case (and (consp form
) (car form
))
368 (make-effective-method-function-simple
369 generic-function form
))))
371 (list (cons '.meth-list.
372 (mapcar (lambda (form)
373 (make-effective-method-function-simple
374 generic-function form
))
377 (default-constant-converter form
))))
379 (defun make-effective-method-function-internal
380 (generic-function effective-method method-alist-p wrappers-p
)
381 (multiple-value-bind (nreq applyp metatypes nkeys arg-info
)
382 (get-generic-fun-info generic-function
)
383 (declare (ignore nkeys arg-info
))
384 (let* ((*rebound-effective-method-gensyms
*
385 *global-effective-method-gensyms
*)
386 (name (if (early-gf-p generic-function
)
387 (!early-gf-name generic-function
)
388 (generic-function-name generic-function
)))
389 (arg-info (cons nreq applyp
))
390 (effective-method-lambda (expand-effective-method-function
391 generic-function effective-method
)))
392 (multiple-value-bind (cfunction constants
)
393 (get-fun effective-method-lambda
395 (memf-test-converter form generic-function
396 method-alist-p wrappers-p
))
398 (memf-code-converter form generic-function
400 method-alist-p wrappers-p
))
402 (memf-constant-converter form generic-function
)))
403 (lambda (method-alist wrappers
)
404 (flet ((compute-constant (constant)
408 (funcall (cdr constant
) method-alist wrappers
))
411 (funcall fn method-alist wrappers
))
416 (let ((fun (apply cfunction
417 (mapcar #'compute-constant constants
))))
418 (set-fun-name fun
`(combined-method ,name
))
419 (make-fast-method-call :function
(maybe-trace-method generic-function nil fun t
)
420 :arg-info arg-info
))))))))
422 (defmacro call-method-list
(&rest calls
)
425 (defun make-call-methods (methods)
427 ,@(mapcar (lambda (method) `(call-method ,method
())) methods
)))
429 (defun gf-requires-emf-keyword-checks (generic-function)
430 (member '&key
(gf-lambda-list generic-function
)))
432 (defconstant-eqx +standard-method-combination-qualifiers
+
433 '(:around
:before
:after
) #'equal
)
435 (defun standard-method-combination-qualifier-p (qualifier)
436 (member qualifier
+standard-method-combination-qualifiers
+))
438 (defun standard-compute-effective-method
439 (generic-function combin applicable-methods
)
440 (collect ((before) (primary) (after) (around))
441 (flet ((invalid (method)
442 (return-from standard-compute-effective-method
443 `(invalid-qualifiers ',generic-function
',combin
',method
))))
444 (dolist (m applicable-methods
)
445 (let ((qualifiers (if (listp m
)
446 (early-method-qualifiers m
)
447 (safe-method-qualifiers m
))))
449 ((null qualifiers
) (primary m
))
450 ((cdr qualifiers
) (invalid m
))
451 ((eq (car qualifiers
) :around
) (around m
))
452 ((eq (car qualifiers
) :before
) (before m
))
453 ((eq (car qualifiers
) :after
) (after m
))
455 (cond ((null applicable-methods
)
456 ;; APPLICABLE-METHODS is normally non-null in effective
457 ;; method computation, but COMPUTE-APPLICABLE-METHODS can
458 ;; in principle be called by MetaObject Protocol programmers.
459 `(method-combination-error
460 "No applicable method found for ~S"
463 ;; PCL checks for no primary method before method
464 ;; combination, but a MetaObject Protocol programmer could
465 ;; call COMPUTE-EFFECTIVE-METHOD themselves and end up
467 `(method-combination-error
468 "No primary method found for ~S among applicable methods: ~S"
469 ',generic-function
(list ,@(mapcar (lambda (m) `(quote ,m
)) applicable-methods
))))
470 ((and (null (before)) (null (after)) (null (around)))
471 ;; By returning a single call-method `form' here we enable
472 ;; an important implementation-specific optimization; that
473 ;; is, we can use the fast method function directly as the
474 ;; effective method function.
476 ;; However, the requirement by ANSI (CLHS 7.6.5) on generic
477 ;; function argument checking inhibits this, as we don't
478 ;; perform this checking in fast-method-functions given
479 ;; that they are not solely used for effective method
480 ;; functions, but also in combination, when they should not
481 ;; perform argument checks. We still return the bare
482 ;; CALL-METHOD, but the caller is responsible for ensuring
483 ;; that keyword applicability is checked if this is a fast
484 ;; method function used in an effective method. (See
485 ;; WRAP-WITH-APPLICABLE-KEYWORD-CHECK below).
486 `(call-method ,(first (primary)) ,(rest (primary))))
488 (let ((main-effective-method
489 (if (or (before) (after))
490 `(multiple-value-prog1
492 ,(make-call-methods (before))
493 (call-method ,(first (primary))
495 ,(make-call-methods (reverse (after))))
496 `(call-method ,(first (primary)) ,(rest (primary))))))
498 `(call-method ,(first (around))
500 (make-method ,main-effective-method
)))
501 main-effective-method
))))))
503 (defun short-method-combination-qualifiers (type-name)
504 (list type-name
:around
))
506 (defun short-method-combination-qualifier-p (type-name qualifier
)
507 (or (eq qualifier type-name
) (eq qualifier
:around
)))
509 (defun short-compute-effective-method
510 (generic-function combin applicable-methods
)
511 (let ((type-name (method-combination-type-name combin
))
512 (operator (short-combination-operator combin
))
513 (ioa (short-combination-identity-with-one-argument combin
))
514 (order (car (method-combination-options combin
)))
517 (flet ((invalid (method)
518 (return-from short-compute-effective-method
519 `(invalid-qualifiers ',generic-function
',combin
',method
))))
520 (dolist (m applicable-methods
)
521 (let ((qualifiers (method-qualifiers m
)))
522 (cond ((null qualifiers
) (invalid m
))
523 ((cdr qualifiers
) (invalid m
))
524 ((eq (car qualifiers
) :around
)
526 ((eq (car qualifiers
) type-name
)
529 (setq around
(nreverse around
))
531 (:most-specific-last
) ; nothing to be done, already in correct order
532 (:most-specific-first
533 (setq primary
(nreverse primary
))))
535 (if (and (null (cdr primary
))
537 `(call-method ,(car primary
) ())
538 `(,operator
,@(mapcar (lambda (m) `(call-method ,m
()))
540 (cond ((null applicable-methods
)
541 ;; APPLICABLE-METHODS is normally non-null in effective
542 ;; method computation, but COMPUTE-APPLICABLE-METHODS can
543 ;; in principle be called by MetaObject Protocol programmers.
544 `(method-combination-error
545 "No applicable method found for ~S"
548 ;; PCL checks for no primary method before method
549 ;; combination, but a MetaObject Protocol programmer could
550 ;; call COMPUTE-EFFECTIVE-METHOD themselves and end up
552 `(method-combination-error
553 "No primary method found for ~S among applicable methods: ~S"
554 ',generic-function
(list ,@(mapcar (lambda (m) `(quote ,m
)) applicable-methods
))))
555 ((null around
) main-method
)
557 `(call-method ,(car around
)
558 (,@(cdr around
) (make-method ,main-method
))))))))
560 ;;; helper code for checking keywords in generic function calls.
561 (defun compute-applicable-keywords (gf methods
)
562 (let ((any-keyp nil
))
563 (flet ((analyze (lambda-list)
564 (multiple-value-bind (llks nreq nopt keys
)
565 (analyze-lambda-list lambda-list
)
566 (declare (ignore nreq
))
567 (when (ll-kwds-keyp llks
)
569 (values nopt
(ll-kwds-allowp llks
) keys
))))
570 (multiple-value-bind (nopt allowp keys
)
571 (analyze (gf-lambda-list gf
))
572 (dolist (method methods
)
573 (let ((ll (if (consp method
)
574 (early-method-lambda-list method
)
575 (method-lambda-list method
))))
576 (multiple-value-bind (n allowp method-keys
)
580 (return-from compute-applicable-keywords
(values t nopt
)))
581 (setq keys
(union method-keys keys
)))))
583 (values (if allowp t keys
) nopt
)))))
585 (defun check-applicable-keywords (start valid-keys more-context more-count
)
586 (let ((allow-other-keys-seen nil
)
587 (allow-other-keys nil
)
589 (declare (type index i more-count
)
591 (flet ((current-value ()
592 (sb-c::%more-arg more-context i
)))
593 (declare (inline current-value
))
596 (when (>= i more-count
)
597 (when (and (invalid) (not allow-other-keys
))
598 (%program-error
"~@<invalid keyword argument~P: ~
599 ~{~S~^, ~} (valid keys are ~{~S~^, ~}).~@:>"
600 (length (invalid)) (invalid) valid-keys
))
602 (let ((key (current-value)))
606 (%program-error
"~@<keyword argument not a symbol: ~S.~@:>"
609 (sb-c::%odd-key-args-error
))
610 ((eq key
:allow-other-keys
)
611 ;; only the leftmost :ALLOW-OTHER-KEYS has any effect
612 (unless allow-other-keys-seen
613 (setq allow-other-keys-seen t
614 allow-other-keys
(current-value))))
616 ((not (memq key valid-keys
)) (invalid key
))))
619 (defun wrap-with-applicable-keyword-check (effective valid-keys keyargs-start
)
620 `(let ((.valid-keys.
',valid-keys
)
621 (.keyargs-start.
',keyargs-start
))
622 (check-applicable-keywords
623 .keyargs-start. .valid-keys. .dfun-more-context. .dfun-more-count.
)
626 ;;;; the STANDARD method combination type. This is coded by hand
627 ;;;; (rather than with DEFINE-METHOD-COMBINATION) for bootstrapping
628 ;;;; and efficiency reasons. Note that the definition of the
629 ;;;; FIND-METHOD-COMBINATION-METHOD appears in the file
630 ;;;; defcombin.lisp. This is because EQL methods can't appear in the
633 ;;;; The DEFCLASS for the METHOD-COMBINATION and
634 ;;;; STANDARD-METHOD-COMBINATION classes has to appear here for this
635 ;;;; reason. This code must conform to the code in the file
636 ;;;; defcombin.lisp, look there for more details.
638 (defun compute-effective-method (generic-function combin applicable-methods
)
639 (standard-compute-effective-method generic-function
643 ;;; not INVALID-METHOD-ERROR as that would violate CLHS 11.1.2.1.1
644 (define-condition invalid-method-program-error
(program-error simple-condition
)
646 (defun invalid-method-error (method format-control
&rest format-arguments
)
647 (let ((sb-debug:*stack-top-hint
* (find-caller-frame)))
648 (error 'invalid-method-program-error
649 :format-control
"~@<invalid method error for ~2I~_~S ~I~_method: ~2I~_~?~:>"
650 :format-arguments
(list method format-control format-arguments
))))
652 ;;; not METHOD-COMBINATION-ERROR as that would violate CLHS 11.1.2.1.1
653 (define-condition method-combination-program-error
(program-error simple-condition
)
655 (defun method-combination-error (format-control &rest format-arguments
)
656 (let ((sb-debug:*stack-top-hint
* (find-caller-frame)))
657 (error 'method-combination-program-error
658 :format-control
"~@<method combination error in CLOS dispatch: ~2I~_~?~:>"
659 :format-arguments
(list format-control format-arguments
))))