chiark / gitweb /
Static instance support.
[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
c6b4ed99
MW
118 ((class sod-class) nick name value pset
119 &key location inhibit-initargs (add-to-class t))
dea4d055
MW
120 (with-default-error-location (location)
121 (let* ((slot (find-instance-slot-by-name class nick name))
b2983f35 122 (initarg-name (get-property pset :initarg :id))
a888e3ac
MW
123 (initializer (and value
124 (make-sod-initializer-using-slot
125 class slot 'sod-instance-initializer
126 value pset (file-location location)))))
dea4d055 127 (with-slots (instance-initializers) class
b2983f35
MW
128 (unless (or initarg-name initializer)
129 (error "Slot initializer declaration with no effect"))
03570bbb 130 (when (and initarg-name (not inhibit-initargs))
81054f01
MW
131 (make-sod-slot-initarg-using-slot class initarg-name slot pset
132 :location location))
c6b4ed99 133 (when (and initializer add-to-class)
b2983f35
MW
134 (setf instance-initializers
135 (append instance-initializers (list initializer)))))
2e1a785d 136 initializer)))
dea4d055
MW
137
138(defmethod make-sod-class-initializer
c6b4ed99 139 ((class sod-class) nick name value pset &key location (add-to-class t))
dea4d055
MW
140 (with-default-error-location (location)
141 (let* ((slot (find-class-slot-by-name class nick name))
142 (initializer (make-sod-initializer-using-slot
143 class slot 'sod-class-initializer
a888e3ac 144 value pset (file-location location))))
c6b4ed99
MW
145 (when add-to-class
146 (with-slots (class-initializers) class
147 (setf class-initializers
148 (append class-initializers (list initializer)))))
2e1a785d 149 initializer)))
dea4d055
MW
150
151(defmethod make-sod-initializer-using-slot
a888e3ac 152 ((class sod-class) (slot sod-slot) init-class value pset location)
52a79ab8 153 (make-instance (get-property pset :initializer-class :symbol init-class)
dea4d055
MW
154 :class class
155 :slot slot
a888e3ac 156 :value value
29ad689c 157 :location (file-location location)
dea4d055
MW
158 :pset pset))
159
160(defmethod shared-initialize :after
161 ((init sod-initializer) slot-names &key pset)
162 "This method does nothing.
163
164 It only exists so that it isn't an error to provide a `:pset' initarg
165 to (make-instance 'sod-initializer ...)."
166 (declare (ignore slot-names pset))
167 nil)
168
b2983f35 169(defmethod make-sod-user-initarg
81054f01 170 ((class sod-class) name type pset &key default location)
b2983f35 171 (with-slots (initargs) class
0e5c0b9e
MW
172 (push (make-instance (get-property pset :initarg-class :symbol
173 'sod-user-initarg)
174 :location (file-location location)
b2983f35
MW
175 :class class :name name :type type :default default)
176 initargs)))
177
178(defmethod make-sod-slot-initarg
81054f01 179 ((class sod-class) name nick slot-name pset &key location)
b2983f35 180 (let ((slot (find-instance-slot-by-name class nick slot-name)))
81054f01
MW
181 (make-sod-slot-initarg-using-slot class name slot pset
182 :location location)))
b2983f35
MW
183
184(defmethod make-sod-slot-initarg-using-slot
81054f01 185 ((class sod-class) name (slot sod-slot) pset &key location)
b2983f35
MW
186 (with-slots (initargs) class
187 (with-slots ((type %type)) slot
8a7afc76
MW
188 (setf initargs
189 (append initargs
190 (cons (make-instance (get-property pset :initarg-class
191 :symbol
192 'sod-slot-initarg)
193 :location (file-location location)
194 :class class :name name
195 :type type :slot slot)
196 nil))))))
b2983f35
MW
197
198(defmethod sod-initarg-default ((initarg sod-initarg)) nil)
199
200(defmethod sod-initarg-argument ((initarg sod-initarg))
201 (make-argument (sod-initarg-name initarg)
202 (sod-initarg-type initarg)
203 (sod-initarg-default initarg)))
204
a42893dd
MW
205;;;--------------------------------------------------------------------------
206;;; Initialization and teardown fragments.
207
208(defmethod make-sod-class-initfrag
81054f01 209 ((class sod-class) frag pset &key location)
a42893dd
MW
210 (declare (ignore pset location))
211 (with-slots (initfrags) class
212 (setf initfrags (append initfrags (list frag)))))
213
214(defmethod make-sod-class-tearfrag
81054f01 215 ((class sod-class) frag pset &key location)
a42893dd
MW
216 (declare (ignore pset location))
217 (with-slots (tearfrags) class
218 (setf tearfrags (append tearfrags (list frag)))))
219
dea4d055
MW
220;;;--------------------------------------------------------------------------
221;;; Messages.
222
223(defmethod make-sod-message
81054f01 224 ((class sod-class) name type pset &key location)
dea4d055 225 (with-default-error-location (location)
d145f6df
MW
226 (let* ((msg-class (or (get-property pset :message-class :symbol)
227 (and (get-property pset :combination :keyword)
228 'aggregating-message)
229 'standard-message))
230 (message (make-instance msg-class
231 :class class
232 :name name
233 :type type
234 :location (file-location location)
235 :pset pset)))
dea4d055 236 (with-slots (messages) class
2e1a785d
MW
237 (setf messages (append messages (list message))))
238 message)))
dea4d055
MW
239
240(defmethod shared-initialize :after
241 ((message sod-message) slot-names &key pset)
4b8e5c03 242 (with-slots ((type %type)) message
e895be21
MW
243 (check-message-type message type))
244 (default-slot-from-property (message 'readonlyp slot-names)
245 (pset :readonly :boolean)
246 nil))
dea4d055
MW
247
248(defmethod check-message-type ((message sod-message) (type c-function-type))
249 nil)
250
251(defmethod check-message-type ((message sod-message) (type c-type))
252 (error "Messages must have function type, not ~A" type))
253
254;;;--------------------------------------------------------------------------
255;;; Methods.
256
257(defmethod make-sod-method
81054f01 258 ((class sod-class) nick name type body pset &key location)
dea4d055
MW
259 (with-default-error-location (location)
260 (let* ((message (find-message-by-name class nick name))
261 (method (make-sod-method-using-message message class
262 type body pset
263 (file-location location))))
264 (with-slots (methods) class
2e1a785d
MW
265 (setf methods (append methods (list method))))
266 method)))
dea4d055
MW
267
268(defmethod make-sod-method-using-message
269 ((message sod-message) (class sod-class) type body pset location)
52a79ab8 270 (make-instance (or (get-property pset :method-class :symbol)
dea4d055
MW
271 (sod-message-method-class message class pset))
272 :message message
273 :class class
274 :type type
275 :body body
29ad689c 276 :location (file-location location)
dea4d055
MW
277 :pset pset))
278
279(defmethod sod-message-method-class
280 ((message sod-message) (class sod-class) pset)
281 (declare (ignore pset))
282 'sod-method)
283
284(defmethod shared-initialize :after
285 ((method sod-method) slot-names &key pset)
286 (declare (ignore slot-names pset))
287
288 ;; Check that the arguments are named if we have a method body.
4b8e5c03 289 (with-slots (body (type %type)) method
dea4d055 290 (unless (or (not body)
9ec578d9 291 (every (lambda (arg)
c07860af
MW
292 (or (eq arg :ellipsis)
293 (argument-name arg)
e85df3ff
MW
294 (c-type-equal-p (argument-type arg)
295 c-type-void)))
9ec578d9 296 (c-function-arguments type)))
dea4d055
MW
297 (error "Abstract declarators not permitted in method definitions")))
298
299 ;; Check the method type.
4b8e5c03 300 (with-slots (message (type %type)) method
dea4d055
MW
301 (check-method-type method message type)))
302
303(defmethod check-method-type
304 ((method sod-method) (message sod-message) (type c-type))
305 (error "Methods must have function type, not ~A" type))
306
b70cb6d8
MW
307(export 'check-method-return-type)
308(defun check-method-return-type (method-type wanted-type)
309 "Signal an error unless METHOD-TYPE does not return the WANTED-TYPE."
310 (let ((method-returns (c-type-subtype method-type)))
311 (unless (c-type-equal-p method-returns wanted-type)
312 (error "Method return type ~A should be ~A"
313 method-returns wanted-type))))
314
315(export 'check-method-return-type-against-message)
316(defun check-method-return-type-against-message (method-type message-type)
317 "Signal an error unless METHOD-TYPE and MESSAGE-TYPE return the same type."
318 (let ((message-returns (c-type-subtype message-type))
319 (method-returns (c-type-subtype method-type)))
320 (unless (c-type-equal-p message-returns method-returns)
321 (error "Method return type ~A doesn't match message ~A"
322 method-returns message-returns))))
323
324(export 'check-method-argument-lists)
325(defun check-method-argument-lists (method-type message-type)
326 "Signal an error unless METHOD-TYPE and MESSAGE-TYPE have matching argument
327 lists.
328
2f8a99a8
MW
329 This checks (a) that the two types have matching lists of mandatory
330 arguments, and (b) that either both or neither types accept keyword
331 arguments."
43073476
MW
332 (let ((message-keywords-p (typep message-type 'c-keyword-function-type))
333 (method-keywords-p (typep method-type 'c-keyword-function-type)))
334 (cond (message-keywords-p
335 (unless method-keywords-p
336 (error "Method must declare a keyword argument list")))
337 (method-keywords-p
338 (error "Method must not declare a keyword argument list"))))
b70cb6d8
MW
339 (unless (argument-lists-compatible-p (c-function-arguments message-type)
340 (c-function-arguments method-type))
341 (error "Method arguments ~A don't match message ~A"
342 method-type message-type)))
343
dea4d055
MW
344(defmethod check-method-type
345 ((method sod-method) (message sod-message) (type c-function-type))
4b8e5c03 346 (with-slots ((msgtype %type)) message
b70cb6d8
MW
347 (check-method-return-type-against-message type msgtype)
348 (check-method-argument-lists type msgtype)))
dea4d055 349
00d59354
MW
350;;;--------------------------------------------------------------------------
351;;; Static instances.
352
353(defmethod shared-initialize :after
354 ((instance static-instance) slot-names &key pset)
355 "Initialize a static instance."
356 (default-slot-from-property (instance 'externp slot-names)
357 (pset :extern :boolean)
358 nil)
359 (default-slot-from-property (instance 'constp slot-names)
360 (pset :const :boolean)
361 t))
362
363(defmethod make-static-instance ((class sod-class) name initializers
364 pset location &key)
365
366 ;; Check that the initializers are all for distinct slots.
367 (find-duplicates (lambda (initializer previous)
368 (let ((slot (sod-initializer-slot initializer)))
369 (cerror*-with-location initializer
370 "Duplicate initializer for ~
371 instance slot `~A' in ~
372 static instance `~A'"
373 slot name)
374 (info-with-location previous
375 "Previous definition was here")))
376 initializers
377 :key #'sod-initializer-slot)
378
379 ;; Ensure that every slot will have an initializer, either defined directly
380 ;; on the instance or as part of some class definition.
381 (let ((have (make-hash-table)))
382
383 ;; First, populate the hash table with all of the slots for which we have
384 ;; initializers.
385 (flet ((seen-slot-initializer (init)
386 (setf (gethash (sod-initializer-slot init) have) t)))
387 (mapc #'seen-slot-initializer
388 initializers)
389 (dolist (super (sod-class-precedence-list class))
390 (mapc #'seen-slot-initializer
391 (sod-class-instance-initializers super))))
392
393 ;; Now go through all of the slots and check that they have initializers.
394 (dolist (super (sod-class-precedence-list class))
395 (dolist (slot (sod-class-slots super))
396 (unless (gethash slot have)
397 (cerror*-with-location location
398 "No initializer for instance slot `~A', ~
399 required by static instance `~A'"
400 slot name)
401 (info-with-location slot "Slot `~A' defined here" slot)))))
402
403 ;; Make the instance.
404 (make-instance 'static-instance
405 :class class
406 :name name
407 :initializers initializers
408 :location (file-location location)
409 :pset pset))
410
dea4d055 411;;;----- That's all, folks --------------------------------------------------