chiark / gitweb /
src/method-impl.lisp, etc.: Add a `readonly' message property.
[sod] / src / class-make-impl.lisp
CommitLineData
dea4d055
MW
1;;; -*-lisp-*-
2;;;
3;;; Class construction protocol implementation
4;;;
5;;; (c) 2009 Straylight/Edgeware
6;;;
7
8;;;----- Licensing notice ---------------------------------------------------
9;;;
e0808c47 10;;; This file is part of the Sensible Object Design, an object system for C.
dea4d055
MW
11;;;
12;;; SOD is free software; you can redistribute it and/or modify
13;;; it under the terms of the GNU General Public License as published by
14;;; the Free Software Foundation; either version 2 of the License, or
15;;; (at your option) any later version.
16;;;
17;;; SOD is distributed in the hope that it will be useful,
18;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;;; GNU General Public License for more details.
21;;;
22;;; You should have received a copy of the GNU General Public License
23;;; along with SOD; if not, write to the Free Software Foundation,
24;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26(cl:in-package #:sod)
27
28;;;--------------------------------------------------------------------------
29;;; Classes.
30
73eceea6
MW
31(defmethod guess-metaclass ((class sod-class))
32 "Default metaclass-guessing function for classes.
33
34 Return the most specific metaclass of any of the CLASS's direct
35 superclasses."
36
37 (select-minimal-class-property (sod-class-direct-superclasses class)
38 #'sod-class-metaclass
39 #'sod-subclass-p class "metaclass"))
40
dea4d055
MW
41(defmethod shared-initialize :after ((class sod-class) slot-names &key pset)
42 "Specific behaviour for SOD class initialization.
43
44 Properties inspected are as follows:
45
f960a07b
MW
46 * `:metaclass' names the metaclass to use. If unspecified, this will be
47 left unbound, and (unless you intervene later) `guess-metaclass' will
48 be called by `finalize-sod-class' to find a suitable default.
dea4d055
MW
49
50 * `:nick' provides a nickname for the class. If unspecified, a default
51 (the class's name, forced to lowercase) will be chosen in
52 `finalize-sod-class'.
53
54 * `:link' names the chained superclass. If unspecified, this class will
73eceea6
MW
55 be left at the head of its chain.
56
57 Usually, the class's metaclass is determined here, either direcly from the
58 `:metaclass' property or by calling `guess-metaclass'. Guessing is
59 inhibited if the `:%bootstrapping' property is non-nil."
dea4d055
MW
60
61 ;; If no nickname, copy the class name. It won't be pretty, though.
62 (default-slot-from-property (class 'nickname slot-names)
63 (pset :nick :id)
64 (string-downcase (slot-value class 'name)))
65
73eceea6
MW
66 ;; Set the metaclass if the appropriate property has been provided or we're
67 ;; not bootstreapping; otherwise leave it unbound for now, and trust the
68 ;; caller to sort out the mess.
69 (multiple-value-bind (meta floc) (get-property pset :metaclass :id)
70 (cond (floc
71 (setf (slot-value class 'metaclass)
72 (with-default-error-location (floc)
73 (find-sod-class meta))))
74 ((not (get-property pset :%bootstrapping :boolean))
75 (default-slot (class 'metaclass slot-names)
76 (guess-metaclass class)))))
dea4d055
MW
77
78 ;; If no chain-link, then start a new chain here.
79 (default-slot-from-property (class 'chain-link slot-names)
80 (pset :link :id link (find-sod-class link))
81 nil))
82
83;;;--------------------------------------------------------------------------
84;;; Slots.
85
86(defmethod make-sod-slot
81054f01 87 ((class sod-class) name type pset &key location)
dea4d055 88 (with-default-error-location (location)
eeb8cc3f
MW
89 (when (typep type 'c-function-type)
90 (error "Slot declarations cannot have function type"))
52a79ab8 91 (let ((slot (make-instance (get-property pset :slot-class :symbol
dea4d055
MW
92 'sod-slot)
93 :class class
94 :name name
95 :type type
96 :location (file-location location)
b2983f35
MW
97 :pset pset))
98 (initarg-name (get-property pset :initarg :id)))
dea4d055 99 (with-slots (slots) class
2e1a785d 100 (setf slots (append slots (list slot))))
b2983f35 101 (when initarg-name
81054f01
MW
102 (make-sod-slot-initarg-using-slot class initarg-name slot pset
103 :location location))
2e1a785d 104 slot)))
dea4d055
MW
105
106(defmethod shared-initialize :after ((slot sod-slot) slot-names &key pset)
107 "This method does nothing.
108
109 It only exists so that it isn't an error to provide a `:pset' initarg
110 to (make-instance 'sod-slot ...)."
111
112 (declare (ignore slot-names pset)))
113
114;;;--------------------------------------------------------------------------
115;;; Slot initializers.
116
117(defmethod make-sod-instance-initializer
03570bbb 118 ((class sod-class) nick name value pset &key location inhibit-initargs)
dea4d055
MW
119 (with-default-error-location (location)
120 (let* ((slot (find-instance-slot-by-name class nick name))
b2983f35 121 (initarg-name (get-property pset :initarg :id))
a888e3ac
MW
122 (initializer (and value
123 (make-sod-initializer-using-slot
124 class slot 'sod-instance-initializer
125 value pset (file-location location)))))
dea4d055 126 (with-slots (instance-initializers) class
b2983f35
MW
127 (unless (or initarg-name initializer)
128 (error "Slot initializer declaration with no effect"))
03570bbb 129 (when (and initarg-name (not inhibit-initargs))
81054f01
MW
130 (make-sod-slot-initarg-using-slot class initarg-name slot pset
131 :location location))
b2983f35
MW
132 (when initializer
133 (setf instance-initializers
134 (append instance-initializers (list initializer)))))
2e1a785d 135 initializer)))
dea4d055
MW
136
137(defmethod make-sod-class-initializer
81054f01 138 ((class sod-class) nick name value pset &key location)
dea4d055
MW
139 (with-default-error-location (location)
140 (let* ((slot (find-class-slot-by-name class nick name))
141 (initializer (make-sod-initializer-using-slot
142 class slot 'sod-class-initializer
a888e3ac 143 value pset (file-location location))))
dea4d055
MW
144 (with-slots (class-initializers) class
145 (setf class-initializers
2e1a785d
MW
146 (append class-initializers (list initializer))))
147 initializer)))
dea4d055
MW
148
149(defmethod make-sod-initializer-using-slot
a888e3ac 150 ((class sod-class) (slot sod-slot) init-class value pset location)
52a79ab8 151 (make-instance (get-property pset :initializer-class :symbol init-class)
dea4d055
MW
152 :class class
153 :slot slot
a888e3ac 154 :value value
29ad689c 155 :location (file-location location)
dea4d055
MW
156 :pset pset))
157
158(defmethod shared-initialize :after
159 ((init sod-initializer) slot-names &key pset)
160 "This method does nothing.
161
162 It only exists so that it isn't an error to provide a `:pset' initarg
163 to (make-instance 'sod-initializer ...)."
164 (declare (ignore slot-names pset))
165 nil)
166
b2983f35 167(defmethod make-sod-user-initarg
81054f01 168 ((class sod-class) name type pset &key default location)
b2983f35 169 (with-slots (initargs) class
0e5c0b9e
MW
170 (push (make-instance (get-property pset :initarg-class :symbol
171 'sod-user-initarg)
172 :location (file-location location)
b2983f35
MW
173 :class class :name name :type type :default default)
174 initargs)))
175
176(defmethod make-sod-slot-initarg
81054f01 177 ((class sod-class) name nick slot-name pset &key location)
b2983f35 178 (let ((slot (find-instance-slot-by-name class nick slot-name)))
81054f01
MW
179 (make-sod-slot-initarg-using-slot class name slot pset
180 :location location)))
b2983f35
MW
181
182(defmethod make-sod-slot-initarg-using-slot
81054f01 183 ((class sod-class) name (slot sod-slot) pset &key location)
b2983f35
MW
184 (with-slots (initargs) class
185 (with-slots ((type %type)) slot
8a7afc76
MW
186 (setf initargs
187 (append initargs
188 (cons (make-instance (get-property pset :initarg-class
189 :symbol
190 'sod-slot-initarg)
191 :location (file-location location)
192 :class class :name name
193 :type type :slot slot)
194 nil))))))
b2983f35
MW
195
196(defmethod sod-initarg-default ((initarg sod-initarg)) nil)
197
198(defmethod sod-initarg-argument ((initarg sod-initarg))
199 (make-argument (sod-initarg-name initarg)
200 (sod-initarg-type initarg)
201 (sod-initarg-default initarg)))
202
a42893dd
MW
203;;;--------------------------------------------------------------------------
204;;; Initialization and teardown fragments.
205
206(defmethod make-sod-class-initfrag
81054f01 207 ((class sod-class) frag pset &key location)
a42893dd
MW
208 (declare (ignore pset location))
209 (with-slots (initfrags) class
210 (setf initfrags (append initfrags (list frag)))))
211
212(defmethod make-sod-class-tearfrag
81054f01 213 ((class sod-class) frag pset &key location)
a42893dd
MW
214 (declare (ignore pset location))
215 (with-slots (tearfrags) class
216 (setf tearfrags (append tearfrags (list frag)))))
217
dea4d055
MW
218;;;--------------------------------------------------------------------------
219;;; Messages.
220
221(defmethod make-sod-message
81054f01 222 ((class sod-class) name type pset &key location)
dea4d055 223 (with-default-error-location (location)
d145f6df
MW
224 (let* ((msg-class (or (get-property pset :message-class :symbol)
225 (and (get-property pset :combination :keyword)
226 'aggregating-message)
227 'standard-message))
228 (message (make-instance msg-class
229 :class class
230 :name name
231 :type type
232 :location (file-location location)
233 :pset pset)))
dea4d055 234 (with-slots (messages) class
2e1a785d
MW
235 (setf messages (append messages (list message))))
236 message)))
dea4d055
MW
237
238(defmethod shared-initialize :after
239 ((message sod-message) slot-names &key pset)
4b8e5c03 240 (with-slots ((type %type)) message
e895be21
MW
241 (check-message-type message type))
242 (default-slot-from-property (message 'readonlyp slot-names)
243 (pset :readonly :boolean)
244 nil))
dea4d055
MW
245
246(defmethod check-message-type ((message sod-message) (type c-function-type))
247 nil)
248
249(defmethod check-message-type ((message sod-message) (type c-type))
250 (error "Messages must have function type, not ~A" type))
251
252;;;--------------------------------------------------------------------------
253;;; Methods.
254
255(defmethod make-sod-method
81054f01 256 ((class sod-class) nick name type body pset &key location)
dea4d055
MW
257 (with-default-error-location (location)
258 (let* ((message (find-message-by-name class nick name))
259 (method (make-sod-method-using-message message class
260 type body pset
261 (file-location location))))
262 (with-slots (methods) class
2e1a785d
MW
263 (setf methods (append methods (list method))))
264 method)))
dea4d055
MW
265
266(defmethod make-sod-method-using-message
267 ((message sod-message) (class sod-class) type body pset location)
52a79ab8 268 (make-instance (or (get-property pset :method-class :symbol)
dea4d055
MW
269 (sod-message-method-class message class pset))
270 :message message
271 :class class
272 :type type
273 :body body
29ad689c 274 :location (file-location location)
dea4d055
MW
275 :pset pset))
276
277(defmethod sod-message-method-class
278 ((message sod-message) (class sod-class) pset)
279 (declare (ignore pset))
280 'sod-method)
281
282(defmethod shared-initialize :after
283 ((method sod-method) slot-names &key pset)
284 (declare (ignore slot-names pset))
285
286 ;; Check that the arguments are named if we have a method body.
4b8e5c03 287 (with-slots (body (type %type)) method
dea4d055 288 (unless (or (not body)
9ec578d9 289 (every (lambda (arg)
c07860af
MW
290 (or (eq arg :ellipsis)
291 (argument-name arg)
e85df3ff
MW
292 (c-type-equal-p (argument-type arg)
293 c-type-void)))
9ec578d9 294 (c-function-arguments type)))
dea4d055
MW
295 (error "Abstract declarators not permitted in method definitions")))
296
297 ;; Check the method type.
4b8e5c03 298 (with-slots (message (type %type)) method
dea4d055
MW
299 (check-method-type method message type)))
300
301(defmethod check-method-type
302 ((method sod-method) (message sod-message) (type c-type))
303 (error "Methods must have function type, not ~A" type))
304
b70cb6d8
MW
305(export 'check-method-return-type)
306(defun check-method-return-type (method-type wanted-type)
307 "Signal an error unless METHOD-TYPE does not return the WANTED-TYPE."
308 (let ((method-returns (c-type-subtype method-type)))
309 (unless (c-type-equal-p method-returns wanted-type)
310 (error "Method return type ~A should be ~A"
311 method-returns wanted-type))))
312
313(export 'check-method-return-type-against-message)
314(defun check-method-return-type-against-message (method-type message-type)
315 "Signal an error unless METHOD-TYPE and MESSAGE-TYPE return the same type."
316 (let ((message-returns (c-type-subtype message-type))
317 (method-returns (c-type-subtype method-type)))
318 (unless (c-type-equal-p message-returns method-returns)
319 (error "Method return type ~A doesn't match message ~A"
320 method-returns message-returns))))
321
322(export 'check-method-argument-lists)
323(defun check-method-argument-lists (method-type message-type)
324 "Signal an error unless METHOD-TYPE and MESSAGE-TYPE have matching argument
325 lists.
326
2f8a99a8
MW
327 This checks (a) that the two types have matching lists of mandatory
328 arguments, and (b) that either both or neither types accept keyword
329 arguments."
43073476
MW
330 (let ((message-keywords-p (typep message-type 'c-keyword-function-type))
331 (method-keywords-p (typep method-type 'c-keyword-function-type)))
332 (cond (message-keywords-p
333 (unless method-keywords-p
334 (error "Method must declare a keyword argument list")))
335 (method-keywords-p
336 (error "Method must not declare a keyword argument list"))))
b70cb6d8
MW
337 (unless (argument-lists-compatible-p (c-function-arguments message-type)
338 (c-function-arguments method-type))
339 (error "Method arguments ~A don't match message ~A"
340 method-type message-type)))
341
dea4d055
MW
342(defmethod check-method-type
343 ((method sod-method) (message sod-message) (type c-function-type))
4b8e5c03 344 (with-slots ((msgtype %type)) message
b70cb6d8
MW
345 (check-method-return-type-against-message type msgtype)
346 (check-method-argument-lists type msgtype)))
dea4d055
MW
347
348;;;----- That's all, folks --------------------------------------------------