File: clos-specializer1.lisp

package info (click to toggle)
clisp 1%3A2.49-8.1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 45,160 kB
  • sloc: lisp: 79,960; ansic: 48,257; xml: 26,814; sh: 12,846; fortran: 7,286; makefile: 1,456; perl: 164
file content (192 lines) | stat: -rw-r--r-- 8,225 bytes parent folder | download | duplicates (9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
;;;; Common Lisp Object System for CLISP
;;;; Specializers
;;;; Part 1: Class definitions, preliminary accessors, utility functions.
;;;; Bruno Haible 2004-05-15

(in-package "CLOS")

;;; ===========================================================================

;;; The abstract class <specializer> allows specializers for methods in
;;; generic functions (i.e. classes and EQL-specializers) to be treated in a
;;; homogenous way.

(defvar *<specializer>-defclass*
  '(defclass specializer (standard-stablehash metaobject)
     (($direct-methods          ; weak-list or weak-hash-table of methods that
                                ; use this specializer
       :initform nil))
    (:fixed-slot-locations t)))

;; Fixed slot locations.
(defconstant *<specializer>-direct-methods-location* 2)

;; Preliminary accessors.
(predefun specializer-direct-methods-table (object)
  (sys::%record-ref object *<specializer>-direct-methods-location*))
(predefun (setf specializer-direct-methods-table) (new-value object)
  (setf (sys::%record-ref object *<specializer>-direct-methods-location*) new-value))

;; Initialization of a <specializer> instance.
(defun shared-initialize-<specializer> (specializer situation &rest args
                                        &key &allow-other-keys)
  (apply #'shared-initialize-<standard-stablehash> specializer situation args)
  (unless *classes-finished*
    ; Bootstrapping: Simulate the effect of #'%shared-initialize.
    (when (eq situation 't) ; called from initialize-instance?
      (setf (specializer-direct-methods-table specializer) nil)))
  specializer)

;;; ===========================================================================

;;; The class <eql-specializer> represents an EQL-specializer.

(defvar <eql-specializer> 'eql-specializer)
(defvar *<eql-specializer>-defclass*
  '(defclass eql-specializer (specializer)
     (($singleton :initarg singleton))
     (:fixed-slot-locations t)))
(defvar *<eql-specializer>-class-version* (make-class-version))

;; Fixed slot locations.
(defconstant *<eql-specializer>-singleton-location* 3)

;; Preliminary accessors.
(predefun eql-specializer-singleton (object)
  (sys::%record-ref object *<eql-specializer>-singleton-location*))
(predefun (setf eql-specializer-singleton) (new-value object)
  (setf (sys::%record-ref object *<eql-specializer>-singleton-location*) new-value))

(defconstant *<eql-specializer>-instance-size* 4)

;; Initialization of an <eql-specializer> instance.
(defun shared-initialize-<eql-specializer> (specializer situation &rest args
                                            &key ((singleton singleton) nil singleton-p)
                                            &allow-other-keys)
  (apply #'shared-initialize-<specializer> specializer situation args)
  (unless *classes-finished*
    ; Bootstrapping: Simulate the effect of #'%shared-initialize.
    (when singleton-p
      (setf (eql-specializer-singleton specializer) singleton)))
  specializer)

(defun initialize-instance-<eql-specializer> (specializer &rest args
                                              &key &allow-other-keys)
  ;; Don't add functionality here! This is a preliminary definition that is
  ;; replaced with #'initialize-instance later.
  (apply #'shared-initialize-<eql-specializer> specializer 't args))

(defun make-instance-<eql-specializer> (class &rest args
                                        &key &allow-other-keys)
  ;; class = <eql-specializer>
  ;; Don't add functionality here! This is a preliminary definition that is
  ;; replaced with #'make-instance later.
  (declare (ignore class))
  (let ((specializer (allocate-metaobject-instance *<eql-specializer>-class-version* *<eql-specializer>-instance-size*)))
    (apply #'initialize-instance-<eql-specializer> specializer args)))

;; Type test.
(defun eql-specializer-p (object)
  (and (std-instance-p object)
       (let ((cv (sys::%record-ref object 0)))
         ; Treat the most frequent case first, for bootstrapping.
         (or (eq cv *<eql-specializer>-class-version*)
             (gethash <eql-specializer>
                      (class-all-superclasses (class-of object)))))))

;;; ===========================================================================

;; We don't store the list of generic functions that use a given specializer
;; in the specializer, but instead compute it on the fly, because
;; 1. For good asymptotic performance the generic-functions list would have to
;;    be stored as a weak set or a weak multiset, thus requiring that
;;    <generic-function> inherits from <standard-stable-hash> - but this gives
;;    a collision with <funcallable-instance>.
;; 2. The generic-functions list of a specializer is generally not much
;;    shorter than the methods list of the specializer, and is redundant.

(defun compute-direct-generic-functions (specializer)
  (let* ((methods (specializer-direct-methods specializer))
         (gfs (delete-duplicates (mapcar #'method-generic-function methods) :test #'eq)))
    (when (memq nil gfs)
      (error (TEXT "~S: Some methods have been removed from their generic function, but the list in the ~S specializer was not updated.")
             'specializer-direct-generic-functions specializer))
    gfs))

;; MOP p. 103
(predefun specializer-direct-generic-functions (specializer)
  (compute-direct-generic-functions specializer))

#|
;; Adds a method to the list of direct methods.
(defun add-direct-method (specializer method) ...)
;; Removes a method from the list of direct methods.
(defun remove-direct-method (specializer method) ...)
;; Returns the currently existing direct methods, as a freshly consed list.
(defun list-direct-methods (specializer) ...)
|#
(def-weak-set-accessors specializer-direct-methods-table method
  add-direct-method-internal
  remove-direct-method-internal
  list-direct-methods)

(defun add-direct-method-<specializer>-<method> (specializer method)
  (add-direct-method-internal specializer method)
  (when (eql-specializer-p specializer)
    (let ((its-class (class-of (eql-specializer-singleton specializer))))
      (when (semi-standard-class-p its-class)
        (add-direct-instance-specializer its-class specializer)))))

;; Preliminary.
(predefun add-direct-method (specializer method)
  (add-direct-method-<specializer>-<method> specializer method))

(predefun remove-direct-method (specializer method)
  (remove-direct-method-internal specializer method))

;; MOP p. 103
(predefun specializer-direct-methods (specializer)
  (list-direct-methods specializer))

;;; ===========================================================================

;; EQL-specializers for numbers.
(defvar *eql-specializer-table*
  (make-hash-table :key-type 'number :value-type 'eql-specializer
                   :test 'ext:fasthash-eql :warn-if-needs-rehash-after-gc t))

;; EQL-specializers for other kinds of objects.
(defvar *eq-specializer-table*
  (make-hash-table :key-type '(not number) :value-type 'eql-specializer
                   :test 'ext:stablehash-eq
                   :weak :key))

;; MOP p. 70
(defun intern-eql-specializer (object)
  (let ((table (if (numberp object) *eql-specializer-table* *eq-specializer-table*)))
    (or (gethash object table)
        (setf (gethash object table)
              (make-instance-<eql-specializer> <eql-specializer>
                                               'singleton object)))))

;; Returns the eql-specializer for the given object only if it already exists,
;; otherwise nil.
(defun existing-eql-specializer (object)
  (let ((table (if (numberp object) *eql-specializer-table* *eq-specializer-table*)))
    (gethash object table)))

;; MOP p. 52
(defun eql-specializer-object (specializer)
  (eql-specializer-singleton specializer))

(defun print-object-<eql-specializer> (specializer stream)
  (print-unreadable-object (specializer stream :type t)
    (write (eql-specializer-object specializer) :stream stream)))

;;; ===========================================================================

;; Converts a specializer to a pretty printing type.
(defun specializer-pretty (specializer)
  (if (eql-specializer-p specializer)
    `(EQL ,(eql-specializer-object specializer))
    specializer))