chiark / gitweb /
Initial checkin
[clg] / glib / proxy.lisp
CommitLineData
94f15c3c 1;; Common Lisp bindings for GTK+ v2.0
2;; Copyright (C) 2000 Espen S. Johnsen <esj@stud.cs.uit.no>
3;;
4;; This library is free software; you can redistribute it and/or
5;; modify it under the terms of the GNU Lesser General Public
6;; License as published by the Free Software Foundation; either
7;; version 2 of the License, or (at your option) any later version.
8;;
9;; This library is distributed in the hope that it will be useful,
10;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12;; Lesser General Public License for more details.
13;;
14;; You should have received a copy of the GNU Lesser General Public
15;; License along with this library; if not, write to the Free Software
16;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18;; $Id: proxy.lisp,v 1.1 2001-04-29 20:19:25 espen Exp $
19
20(in-package "GLIB")
21
22
23;;;; Superclass for all metaclasses implementing some sort of virtual slots
24
25(eval-when (:compile-toplevel :load-toplevel :execute)
26 (defclass virtual-class (pcl::standard-class))
27
28 (defclass direct-virtual-slot-definition (standard-direct-slot-definition)
29 ((location
30 :reader slot-definition-location
31 :initarg :location)))
32
33 (defclass effective-virtual-slot-definition
34 (standard-effective-slot-definition)))
35
36
37(defmethod direct-slot-definition-class ((class virtual-class) initargs)
38 (if (eq (getf initargs :allocation) :virtual)
39 (find-class 'direct-virtual-slot-definition)
40 (call-next-method)))
41
42
43(defmethod effective-slot-definition-class ((class virtual-class) initargs)
44 (if (eq (getf initargs :allocation) :virtual)
45 (find-class 'effective-virtual-slot-definition)
46 (call-next-method)))
47
48
49(defun %direct-slot-definitions-slot-value (slotds slot &optional default)
50 (let ((slotd
51 (find-if
52 #'(lambda (slotd)
53 (and
54 (slot-exists-p slotd slot)
55 (slot-boundp slotd slot)))
56 slotds)))
57 (if slotd
58 (slot-value slotd slot)
59 default)))
60
61
62(defgeneric compute-virtual-slot-location (class slotd direct-slotds))
63
64(defmethod compute-virtual-slot-location
65 ((class virtual-class)
66 (slotd effective-virtual-slot-definition)
67 direct-slotds)
68 (let ((location
69 (%direct-slot-definitions-slot-value direct-slotds 'location)))
70 (if (and location (symbolp location))
71 (list location `(setf ,location))
72 location)))
73
74
75(defmethod compute-effective-slot-definition
76 ((class virtual-class) direct-slotds)
77 (let ((slotd (call-next-method)))
78 (when (typep slotd 'effective-virtual-slot-definition)
79 (setf
80 (slot-value slotd 'pcl::location)
81 (compute-virtual-slot-location class slotd direct-slotds)))
82 slotd))
83
84
85(defmethod slot-value-using-class
86 ((class virtual-class) (object standard-object)
87 (slotd effective-virtual-slot-definition))
88 (let ((reader (first (slot-definition-location slotd))))
89 (if reader
90 (funcall reader object)
91 (slot-unbound class object (slot-definition-name slotd)))))
92
93
94(defmethod slot-boundp-using-class
95 ((class virtual-class) (object standard-object)
96 (slotd effective-virtual-slot-definition))
97 (and (first (slot-definition-location slotd)) t))
98
99
100
101(defmethod (setf slot-value-using-class)
102 (value (class virtual-class) (object standard-object)
103 (slotd effective-virtual-slot-definition))
104 (let ((writer (second (slot-definition-location slotd))))
105 (cond
106 ((null writer)
107 (error
108 "Can't set read-only slot ~A in ~A"
109 (slot-definition-name slotd)
110 object))
111 ((or (functionp writer) (symbolp writer))
112 (funcall writer value object)
113 value)
114 (t
115 (funcall (fdefinition writer) value object)
116 value))))
117
118
119(defmethod validate-superclass
120 ((class virtual-class) (super pcl::standard-class))
121 t)
122
123
124;;;; Proxy cache
125
126(internal *instance-cache*)
127(defvar *instance-cache* (make-hash-table :test #'eql))
128
129(defun cache-instance (instance)
130 (setf
131 (gethash (system:sap-int (proxy-location instance)) *instance-cache*)
132 (ext:make-weak-pointer instance)))
133
134(defun find-cached-instance (location)
135 (let ((ref (gethash (system:sap-int location) *instance-cache*)))
136 (when ref
137 (ext:weak-pointer-value ref))))
138
139(defun remove-cached-instance (location)
140 (remhash (system:sap-int location) *instance-cache*))
141
142
143
144;;;; Proxy for alien instances
145
146(eval-when (:compile-toplevel :load-toplevel :execute)
147 (defclass proxy ()
148 ((location
149 :reader proxy-location
150 :type system-area-pointer)))
151
152 (defgeneric initialize-proxy (object &rest initargs))
153 (defgeneric instance-finalizer (object)))
154
155
156(defmethod initialize-instance :after ((instance proxy)
157 &rest initargs &key)
158 (declare (ignore initargs))
159 (cache-instance instance)
160 (ext:finalize instance (instance-finalizer instance)))
161
162
163(defmethod initialize-proxy ((instance proxy)
164 &rest initargs)
165 (declare (ignore initargs))
166 (cache-instance instance))
167
168
169(defmethod instance-finalizer ((instance proxy))
170 (let ((location (proxy-location instance)))
171 #'(lambda ()
172 (remove-cached-instance location))))
173
174
175(deftype-method translate-type-spec proxy (type-spec)
176 (declare (ignore type-spec))
177 (translate-type-spec 'pointer))
178
179(deftype-method size-of proxy (type-spec)
180 (declare (ignore type-spec))
181 (size-of 'pointer))
182
183(deftype-method translate-from-alien
184 proxy (type-spec location &optional weak-ref)
185 `(let ((location ,location))
186 (unless (null-pointer-p location)
187 (ensure-proxy-instance ',type-spec location ,weak-ref))))
188
189
190
191;;;; Metaclass used for subclasses of proxy
192
193(eval-when (:compile-toplevel :load-toplevel :execute)
194 (defclass proxy-class (virtual-class)
195 ((size :reader proxy-class-instance-size)))
196
197 (defclass direct-alien-slot-definition (direct-virtual-slot-definition)
198 ((allocation
199 :initform :alien)
200 (offset
201 :reader slot-definition-offset
202 :initarg :offset
203 :initform 0)))
204
205 (defclass effective-alien-slot-definition (effective-virtual-slot-definition)
206 ((offset :reader slot-definition-offset)))
207
208 (defclass effective-virtual-alien-slot-definition
209 (effective-virtual-slot-definition))
210
211
212 (defmethod most-specific-proxy-superclass ((class proxy-class))
213 (find-if
214 #'(lambda (class)
215 (subtypep (class-name class) 'proxy))
216 (cdr (pcl::compute-class-precedence-list class))))
217
218
219 (defmethod shared-initialize ((class proxy-class) names
220 &rest initargs &key size name)
221 (declare (ignore initargs))
222 (call-next-method)
223 (when size
224 (setf (slot-value class 'size) (first size))))
225
226
227 (defmethod shared-initialize :after ((class proxy-class) names
228 &rest initargs &key)
229 (declare (ignore initargs names))
230 (let* ((super (most-specific-proxy-superclass class))
231 (actual-size
232 (if (eq (class-name super) 'proxy)
233 0
234 (proxy-class-instance-size super))))
235 (dolist (slotd (class-slots class))
236 (when (eq (slot-definition-allocation slotd) :alien)
237 (with-slots (offset type) slotd
238 (setq actual-size (max actual-size (+ offset (size-of type)))))))
239 (cond
240 ((not (slot-boundp class 'size))
241 (setf (slot-value class 'size) actual-size))
242 ((> actual-size (slot-value class 'size))
243 (warn "The actual size of class ~A is lager than specified" class)))))
244
245
246 (defmethod direct-slot-definition-class ((class proxy-class) initargs)
247 (case (getf initargs :allocation)
248 ((nil :alien) (find-class 'direct-alien-slot-definition))
249; (:instance (error "Allocation :instance not allowed in class ~A" class))
250 (t (call-next-method))))
251
252
253 (defmethod effective-slot-definition-class ((class proxy-class) initargs)
254 (case (getf initargs :allocation)
255 (:alien (find-class 'effective-alien-slot-definition))
256 (:virtual (find-class 'effective-virtual-alien-slot-definition))
257 (t (call-next-method))))
258
259
260 (defmethod compute-virtual-slot-location
261 ((class proxy-class) (slotd effective-alien-slot-definition)
262 direct-slotds)
263 (with-slots (offset type) slotd
264 (setf offset (%direct-slot-definitions-slot-value direct-slotds 'offset))
265 (let ((reader (intern-reader-function type))
266 (writer (intern-writer-function type))
267 (destroy (intern-destroy-function type)))
268 (list
269 #'(lambda (object)
270 (funcall reader (proxy-location object) offset))
271 #'(lambda (value object)
272 (let ((location (proxy-location object)))
273 (funcall destroy location offset)
274 (funcall writer value location offset)))))))
275
276
277 (defmethod compute-virtual-slot-location
278 ((class proxy-class)
279 (slotd effective-virtual-alien-slot-definition)
280 direct-slotds)
281 (let ((location (call-next-method)))
282 (if (or (stringp location) (consp location))
283 (destructuring-bind (reader &optional writer) (mklist location)
284 (with-slots (type) slotd
285 (list
286 (if (stringp reader)
287 (let* ((alien-type (translate-type-spec type))
288 (alien
289 (alien::%heap-alien
290 (alien::make-heap-alien-info
291 :type (alien::parse-alien-type
292 `(function ,alien-type system-area-pointer))
293 :sap-form (system:foreign-symbol-address reader))))
294 (translate-return-value
295 (intern-return-value-translator type)))
296 #'(lambda (object)
297 (funcall
298 translate-return-value
299 (alien-funcall
300 alien (proxy-location object)))))
301 reader)
302 (if (stringp writer)
303 (let* ((alien-type (translate-type-spec type))
304 (alien
305 (alien::%heap-alien
306 (alien::make-heap-alien-info
307 :type (alien::parse-alien-type
308 `(function
309 void system-area-pointer ,alien-type))
310 :sap-form (system:foreign-symbol-address writer))))
311 (translate-argument (intern-argument-translator type))
312 (cleanup (intern-cleanup-function type)))
313 #'(lambda (value object)
314 (let ((tmp (funcall translate-argument value))
315 (location (proxy-location object)))
316 (alien-funcall alien location tmp)
317 (funcall cleanup tmp))))
318 writer))))
319 location)))
320
321
322 (defmethod compute-slots ((class proxy-class))
323 ;; Translating the user supplied relative (to previous slot) offsets
324 ;; to absolute offsets.
325 ;; This code is broken and have to be fixed.
326 (with-slots (direct-slots) class
327 (let* ((super (most-specific-proxy-superclass class))
328 (slot-offset
329 (if (eq (class-name super) 'proxy)
330 0
331 (proxy-class-instance-size super))))
332 (dolist (slotd direct-slots)
333 (when (eq (slot-definition-allocation slotd) :alien)
334 (with-slots (offset type) slotd
335 (setf
336 offset (+ slot-offset offset)
337 slot-offset (+ offset (size-of type)))))))
338
339 ;; Reverse the direct slot definitions so the effective slots
340 ;; will be in correct order.
341 (setf direct-slots (reverse direct-slots))
342 ;; This nreverse caused me so much frustration that I leave it
343 ;; here just as a reminder of what not to do.
344; (setf direct-slots (nreverse direct-slots))
345 )
346 (call-next-method))
347
348
349 (defmethod validate-superclass ((class proxy-class)
350 (super pcl::standard-class))
351 (subtypep (class-name super) 'proxy))
352
353 (defgeneric make-proxy-instance (class location weak-ref &rest initargs &key)))
354
355
356(defmethod make-proxy-instance ((class symbol) location weak-ref
357 &rest initargs &key)
358 (apply #'make-proxy-instance (find-class class) location weak-ref initargs))
359
360(defmethod make-proxy-instance ((class proxy-class) location weak-ref
361 &rest initargs &key)
362 (let ((instance (allocate-instance class)))
363 (apply
364 #'initialize-proxy
365 instance :location location :weak-ref weak-ref initargs)
366 instance))
367
368(defun ensure-proxy-instance (class location weak-ref &rest initargs)
369 (or
370 (find-cached-instance location)
371 (apply #'make-proxy-instance class location weak-ref initargs)))
372
373
374;;;; Superclass for wrapping of C structures
375
376(eval-when (:compile-toplevel :load-toplevel :execute)
377 (defclass alien-structure (proxy)
378 ()
379 (:metaclass proxy-class)
380 (:size 0)))
381
382
383(defmethod initialize-instance ((structure alien-structure)
384 &rest initargs)
385 (declare (ignore initargs))
386 (setf
387 (slot-value structure 'location)
388 (allocate-memory (proxy-class-instance-size (class-of structure))))
389 (call-next-method))
390
391
392(defmethod initialize-proxy ((structure alien-structure)
393 &rest initargs &key location weak-ref)
394 (declare (ignore initargs))
395 (setf
396 (slot-value structure 'location)
397 (if weak-ref
398 (copy-memory location (proxy-class-instance-size (class-of structure)))
399 location))
400 (call-next-method))
401
402
403(defmethod instance-finalizer ((structure alien-structure))
404 (let ((location (proxy-location structure)))
405 (declare (type system-area-pointer location))
406 #'(lambda ()
407 (deallocate-memory location)
408 (remove-cached-instance location))))
409
410
411(deftype-method translate-to-alien
412 alien-structure (type-spec object &optional weak-ref)
413 (if weak-ref
414 `(proxy-location ,object)
415 `(copy-memory
416 (proxy-location ,object)
417 ,(proxy-class-instance-size (find-class type-spec)))))
418
419
420(deftype-method unreference-alien alien-structure (type-spec c-struct)
421 (declare (ignore type-spec))
422 `(deallocate-memory ,c-struct))