chiark / gitweb /
Added ALLOCATE-FOREIGN method for gobject. Construct slot renamed construct-only
[clg] / glib / proxy.lisp
CommitLineData
112ac1d3 1;; Common Lisp bindings for GTK+ v2.x
2;; Copyright 2000-2005 Espen S. Johnsen <espen@users.sf.net>
94f15c3c 3;;
112ac1d3 4;; Permission is hereby granted, free of charge, to any person obtaining
5;; a copy of this software and associated documentation files (the
6;; "Software"), to deal in the Software without restriction, including
7;; without limitation the rights to use, copy, modify, merge, publish,
8;; distribute, sublicense, and/or sell copies of the Software, and to
9;; permit persons to whom the Software is furnished to do so, subject to
10;; the following conditions:
94f15c3c 11;;
112ac1d3 12;; The above copyright notice and this permission notice shall be
13;; included in all copies or substantial portions of the Software.
94f15c3c 14;;
112ac1d3 15;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18;; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19;; CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20;; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
94f15c3c 22
308bfcab 23;; $Id: proxy.lisp,v 1.32 2006-02-09 22:26:38 espen Exp $
94f15c3c 24
25(in-package "GLIB")
26
94f15c3c 27;;;; Superclass for all metaclasses implementing some sort of virtual slots
28
29(eval-when (:compile-toplevel :load-toplevel :execute)
9adccb27 30 (defclass virtual-slots-class (standard-class)
4d83a8a6 31 ())
94f15c3c 32
33 (defclass direct-virtual-slot-definition (standard-direct-slot-definition)
12d0437e 34 ((setter :reader slot-definition-setter :initarg :setter)
4d83a8a6 35 (getter :reader slot-definition-getter :initarg :getter)
eeda1c2d 36 (unbound :reader slot-definition-unbound :initarg :unbound)
4d83a8a6 37 (boundp :reader slot-definition-boundp :initarg :boundp)))
94f15c3c 38
4d83a8a6 39 (defclass effective-virtual-slot-definition (standard-effective-slot-definition)
40 ((setter :reader slot-definition-setter :initarg :setter)
41 (getter :reader slot-definition-getter :initarg :getter)
eeda1c2d 42 (unbound :reader slot-definition-unbound :initarg :unbound)
e2ebafb1 43 (boundp :reader slot-definition-boundp :initarg :boundp)))
4d83a8a6 44
e2ebafb1 45 (defclass direct-special-slot-definition (standard-direct-slot-definition)
46 ())
94f15c3c 47
e2ebafb1 48 (defclass effective-special-slot-definition (standard-effective-slot-definition)
49 ()))
50
51(defvar *unbound-marker* (gensym "UNBOUND-MARKER-"))
52
53(defun most-specific-slot-value (instances slot &optional (default *unbound-marker*))
54 (let ((object (find-if
55 #'(lambda (ob)
56 (and (slot-exists-p ob slot) (slot-boundp ob slot)))
57 instances)))
58 (if object
59 (slot-value object slot)
60 default)))
61
62(defmethod initialize-instance ((slotd effective-special-slot-definition) &rest initargs)
63 (declare (ignore initargs))
64 (call-next-method)
65 (setf (slot-value slotd 'allocation) :instance))
66
94f15c3c 67
9adccb27 68(defmethod direct-slot-definition-class ((class virtual-slots-class) &rest initargs)
e2ebafb1 69 (case (getf initargs :allocation)
70 (:virtual (find-class 'direct-virtual-slot-definition))
71 (:special (find-class 'direct-special-slot-definition))
72 (t (call-next-method))))
94f15c3c 73
9adccb27 74(defmethod effective-slot-definition-class ((class virtual-slots-class) &rest initargs)
e2ebafb1 75 (case (getf initargs :allocation)
76 (:virtual (find-class 'effective-virtual-slot-definition))
77 (:special (find-class 'effective-special-slot-definition))
78 (t (call-next-method))))
94f15c3c 79
4d83a8a6 80
81(defmethod initialize-internal-slot-functions ((slotd effective-virtual-slot-definition))
eeda1c2d 82 (if (not (slot-boundp slotd 'getter))
83 (setf
4d83a8a6 84 (slot-value slotd 'reader-function)
eeda1c2d 85 #'(lambda (object)
86 (declare (ignore object))
308bfcab 87 (error "Slot is not readable: ~A" (slot-definition-name slotd)))
eeda1c2d 88 (slot-value slotd 'boundp-function)
89 #'(lambda (object) (declare (ignore object)) nil))
90
91 (let ((getter-function
92 (let ((getter (slot-value slotd 'getter)))
93 (etypecase getter
94 (function getter)
95 (symbol
96 #'(lambda (object)
97 (funcall getter object)))
98 (string
99 (let ((reader nil))
100 (setf (slot-value slotd 'reader-function)
101 #'(lambda (object)
102 (unless reader
12b7df04 103 (setq reader
104 (mkbinding getter
105 (slot-definition-type slotd) 'pointer)))
09f6e237 106 (funcall reader (foreign-location object))))))))))
eeda1c2d 107
4d83a8a6 108 (setf
eeda1c2d 109 (slot-value slotd 'boundp-function)
110 (cond
eeda1c2d 111 ((slot-boundp slotd 'unbound)
112 (let ((unbound-value (slot-value slotd 'unbound)))
12b7df04 113 #'(lambda (object)
114 (not (eq (funcall getter-function object) unbound-value)))))
115 ((slot-boundp slotd 'boundp)
116 (let ((boundp (slot-value slotd 'boundp)))
eeda1c2d 117 (etypecase boundp
118 (function boundp)
119 (symbol #'(lambda (object)
120 (funcall boundp object)))
121 (string (let ((reader ()))
122 #'(lambda (object)
123 (unless reader
124 (setq reader
125 (mkbinding boundp
126 (slot-definition-type slotd) 'pointer)))
09f6e237 127 (funcall reader (foreign-location object))))))))
12b7df04 128 ((multiple-value-bind (unbound-p unbound-value)
129 (unbound-value (slot-definition-type slotd))
130 (when unbound-p
131 #'(lambda (object)
132 (not (eq (funcall getter-function object) unbound-value))))))
133 (#'(lambda (object) (declare (ignore object)) t))))
eeda1c2d 134
135 (setf
136 (slot-value slotd 'reader-function)
137 (cond
138 ((slot-boundp slotd 'unbound)
139 (let ((unbound (slot-value slotd 'unbound))
140 (slot-name (slot-definition-name slotd)))
12b7df04 141 #'(lambda (object)
142 (let ((value (funcall getter-function object)))
143 (if (eq value unbound)
144 (slot-unbound (class-of object) object slot-name)
145 value)))))
eeda1c2d 146 ((slot-boundp slotd 'boundp)
147 (let ((boundp-function (slot-value slotd 'boundp-function)))
12b7df04 148 #'(lambda (object)
149 (and
150 (funcall boundp-function object)
151 (funcall getter-function object)))))
152 ((multiple-value-bind (unbound-p unbound-value)
153 (unbound-value (slot-definition-type slotd))
154 (let ((slot-name (slot-definition-name slotd)))
155 (when unbound-p
156 #'(lambda (object)
157 (let ((value (funcall getter-function object)))
158 (if (eq value unbound-value)
159 (slot-unbound (class-of object) object slot-name)
160 value)))))))
eeda1c2d 161 (getter-function)))))
162
163 (setf
164 (slot-value slotd 'writer-function)
165 (if (not (slot-boundp slotd 'setter))
308bfcab 166 #'(lambda (value object)
167 (declare (ignore value object))
168 (error "Slot is not writable: ~A" (slot-definition-name slotd)))
eeda1c2d 169 (with-slots (setter) slotd
4d83a8a6 170 (etypecase setter
171 (function setter)
eeda1c2d 172 ((or symbol cons)
173 #'(lambda (value object)
174 (funcall (fdefinition setter) value object)))
7d1ddc9e 175 (string
eeda1c2d 176 (let ((writer ()))
177 (setf
178 (slot-value slotd 'writer-function)
179 #'(lambda (value object)
180 (unless writer
181 (setq writer
182 (mkbinding setter 'nil 'pointer
183 (slot-definition-type slotd))))
09f6e237 184 (funcall writer (foreign-location object) value)))))))))
eeda1c2d 185
65466e9c 186 #-sbcl>=0.9.8(initialize-internal-slot-gfs (slot-definition-name slotd)))
4d83a8a6 187
188
189
eeda1c2d 190(defmethod compute-slot-accessor-info ((slotd effective-virtual-slot-definition) type gf)
4d83a8a6 191 nil)
192
9adccb27 193(defmethod compute-effective-slot-definition-initargs ((class virtual-slots-class) direct-slotds)
eeda1c2d 194 (if (typep (first direct-slotds) 'direct-virtual-slot-definition)
195 (let ((initargs ()))
196 (let ((getter (most-specific-slot-value direct-slotds 'getter)))
197 (unless (eq getter *unbound-marker*)
198 (setf (getf initargs :getter) getter)))
199 (let ((setter (most-specific-slot-value direct-slotds 'setter)))
200 (unless (eq setter *unbound-marker*)
201 (setf (getf initargs :setter) setter)))
202 (let ((unbound (most-specific-slot-value direct-slotds 'unbound)))
203 (unless (eq unbound *unbound-marker*)
204 (setf (getf initargs :unbound) unbound)))
205 (let ((boundp (most-specific-slot-value direct-slotds 'boundp)))
206 (unless (eq boundp *unbound-marker*)
207 (setf (getf initargs :boundp) boundp)))
208 (nconc initargs (call-next-method)))
4d83a8a6 209 (call-next-method)))
210
94f15c3c 211
94f15c3c 212(defmethod slot-value-using-class
9adccb27 213 ((class virtual-slots-class) (object standard-object)
94f15c3c 214 (slotd effective-virtual-slot-definition))
4d83a8a6 215 (if (funcall (slot-value slotd 'boundp-function) object)
216 (funcall (slot-value slotd 'reader-function) object)
217 (slot-unbound class object (slot-definition-name slotd))))
94f15c3c 218
94f15c3c 219(defmethod slot-boundp-using-class
9adccb27 220 ((class virtual-slots-class) (object standard-object)
94f15c3c 221 (slotd effective-virtual-slot-definition))
4d83a8a6 222 (funcall (slot-value slotd 'boundp-function) object))
223
224(defmethod (setf slot-value-using-class)
9adccb27 225 (value (class virtual-slots-class) (object standard-object)
94f15c3c 226 (slotd effective-virtual-slot-definition))
4d83a8a6 227 (funcall (slot-value slotd 'writer-function) value object))
228
229
94f15c3c 230(defmethod validate-superclass
9adccb27 231 ((class virtual-slots-class) (super standard-class))
94f15c3c 232 t)
233
234
235;;;; Proxy cache
236
94f15c3c 237(defvar *instance-cache* (make-hash-table :test #'eql))
238
982a215a 239(defun cache-instance (instance &optional (weak-ref t))
94f15c3c 240 (setf
09f6e237 241 (gethash (sap-int (foreign-location instance)) *instance-cache*)
982a215a 242 (if weak-ref
243 (make-weak-pointer instance)
244 instance)))
94f15c3c 245
246(defun find-cached-instance (location)
73572c12 247 (let ((ref (gethash (sap-int location) *instance-cache*)))
94f15c3c 248 (when ref
982a215a 249 (if (weak-pointer-p ref)
250 (weak-pointer-value ref)
251 ref))))
94f15c3c 252
0f134a29 253(defun instance-cached-p (location)
73572c12 254 (gethash (sap-int location) *instance-cache*))
0f134a29 255
94f15c3c 256(defun remove-cached-instance (location)
73572c12 257 (remhash (sap-int location) *instance-cache*))
94f15c3c 258
9adccb27 259;; For debuging
982a215a 260(defun list-cached-instances ()
9adccb27 261 (let ((instances ()))
262 (maphash #'(lambda (location ref)
263 (declare (ignore location))
982a215a 264 (push ref instances))
9adccb27 265 *instance-cache*)
266 instances))
267
ca01de1b 268;; Instances that gets invalidated tend to be short lived, but created
269;; in large numbers. So we're keeping them in a hash table to be able
270;; to reuse them (and thus reduce consing)
271(defvar *invalidated-instance-cache* (make-hash-table :test #'eql))
272
273(defun cache-invalidated-instance (instance)
274 (push instance
275 (gethash (class-of instance) *invalidated-instance-cache*)))
276
277(defun find-invalidated-instance (class)
278 (when (gethash class *invalidated-instance-cache*)
279 (pop (gethash class *invalidated-instance-cache*))))
280
281(defun list-invalidated-instances ()
282 (let ((instances ()))
283 (maphash #'(lambda (location ref)
284 (declare (ignore location))
285 (push ref instances))
286 *invalidated-instance-cache*)
287 instances))
288
94f15c3c 289
290
291;;;; Proxy for alien instances
292
4a64c16d 293;; TODO: add a ref-counted-proxy subclass
9adccb27 294(defclass proxy ()
c0e19882 295 ((location :allocation :special :type pointer))
e2ebafb1 296 (:metaclass virtual-slots-class))
94f15c3c 297
9adccb27 298(defgeneric instance-finalizer (object))
299(defgeneric reference-foreign (class location))
300(defgeneric unreference-foreign (class location))
4a64c16d 301(defgeneric invalidate-instance (object))
308bfcab 302(defgeneric allocate-foreign (object &key &allow-other-keys))
9adccb27 303
c0e19882 304(defun foreign-location (instance)
305 (slot-value instance 'location))
306
307(defun (setf foreign-location) (location instance)
308 (setf (slot-value instance 'location) location))
309
310(defun proxy-valid-p (instance)
311 (slot-boundp instance 'location))
312
3b167652 313(defmethod reference-foreign ((name symbol) location)
314 (reference-foreign (find-class name) location))
315
316(defmethod unreference-foreign ((name symbol) location)
317 (unreference-foreign (find-class name) location))
318
9adccb27 319(defmethod unreference-foreign :around ((class class) location)
320 (unless (null-pointer-p location)
09f6e237 321 (call-next-method)))
94f15c3c 322
0f134a29 323(defmethod print-object ((instance proxy) stream)
324 (print-unreadable-object (instance stream :type t :identity nil)
09f6e237 325 (if (slot-boundp instance 'location)
326 (format stream "at 0x~X" (sap-int (foreign-location instance)))
327 (write-string "at \"unbound\"" stream))))
94f15c3c 328
308bfcab 329(defmethod initialize-instance :around ((instance proxy) &rest initargs &key &allow-other-keys)
330 (setf
331 (foreign-location instance)
332 (apply #'allocate-foreign instance initargs))
8958fa4a 333 (prog1
334 (call-next-method)
335 (cache-instance instance)
336 (finalize instance (instance-finalizer instance))))
94f15c3c 337
338(defmethod instance-finalizer ((instance proxy))
09f6e237 339 (let ((location (foreign-location instance))
9adccb27 340 (class (class-of instance)))
341;; (unless (find-method #'unreference-foreign nil (list (class-of class) t) nil)
342;; (error "No matching method for UNREFERENCE-INSTANCE when called with class ~A" class))
343 #'(lambda ()
1dbf4216 344 (remove-cached-instance location)
9adccb27 345 (unreference-foreign class location))))
12d0437e 346
ca01de1b 347;; Any reference to the foreign object the instance may have held
348;; should be released before this method is invoked
4a64c16d 349(defmethod invalidate-instance ((instance proxy))
350 (remove-cached-instance (foreign-location instance))
ca01de1b 351 (slot-makunbound instance 'location)
352 (cancel-finalization instance)
353 (cache-invalidated-instance instance))
4a64c16d 354
94f15c3c 355
356;;;; Metaclass used for subclasses of proxy
357
73572c12 358(defgeneric most-specific-proxy-superclass (class))
359(defgeneric direct-proxy-superclass (class))
360
361
94f15c3c 362(eval-when (:compile-toplevel :load-toplevel :execute)
9adccb27 363 (defclass proxy-class (virtual-slots-class)
09f6e237 364 ((size :reader foreign-size)))
94f15c3c 365
366 (defclass direct-alien-slot-definition (direct-virtual-slot-definition)
12d0437e 367 ((allocation :initform :alien)
368 (offset :reader slot-definition-offset :initarg :offset)))
94f15c3c 369
370 (defclass effective-alien-slot-definition (effective-virtual-slot-definition)
4d83a8a6 371 ((offset :reader slot-definition-offset :initarg :offset)))
12d0437e 372
94f15c3c 373 (defmethod most-specific-proxy-superclass ((class proxy-class))
374 (find-if
375 #'(lambda (class)
376 (subtypep (class-name class) 'proxy))
4d83a8a6 377 (cdr (compute-class-precedence-list class))))
73572c12 378
12d0437e 379 (defmethod direct-proxy-superclass ((class proxy-class))
380 (find-if
381 #'(lambda (class)
382 (subtypep (class-name class) 'proxy))
4d83a8a6 383 (class-direct-superclasses class)))
384
eeda1c2d 385 (defmethod shared-initialize ((class proxy-class) names &key size)
94f15c3c 386 (call-next-method)
12d0437e 387 (cond
4d83a8a6 388 (size (setf (slot-value class 'size) (first size)))
9adccb27 389 ((slot-boundp class 'size) (slot-makunbound class 'size))))
09f6e237 390
4d83a8a6 391 (defmethod direct-slot-definition-class ((class proxy-class) &rest initargs)
94f15c3c 392 (case (getf initargs :allocation)
e2ebafb1 393 (:alien (find-class 'direct-alien-slot-definition))
94f15c3c 394 (t (call-next-method))))
4d83a8a6 395
396 (defmethod effective-slot-definition-class ((class proxy-class) &rest initargs)
94f15c3c 397 (case (getf initargs :allocation)
398 (:alien (find-class 'effective-alien-slot-definition))
94f15c3c 399 (t (call-next-method))))
400
4d83a8a6 401
402 (defmethod compute-effective-slot-definition-initargs ((class proxy-class) direct-slotds)
403 (if (eq (most-specific-slot-value direct-slotds 'allocation) :alien)
404 (nconc
405 (list :offset (most-specific-slot-value direct-slotds 'offset))
406 (call-next-method))
407 (call-next-method)))
408
409
410 (defmethod initialize-internal-slot-functions ((slotd effective-alien-slot-definition))
411 (with-slots (offset) slotd
9adccb27 412 (let ((type (slot-definition-type slotd)))
eeda1c2d 413 (unless (slot-boundp slotd 'getter)
9adccb27 414 (let ((reader (reader-function type)))
415 (setf
eeda1c2d 416 (slot-value slotd 'getter)
9adccb27 417 #'(lambda (object)
09f6e237 418 (funcall reader (foreign-location object) offset)))))
4d83a8a6 419
eeda1c2d 420 (unless (slot-boundp slotd 'setter)
9adccb27 421 (let ((writer (writer-function type))
422 (destroy (destroy-function type)))
423 (setf
eeda1c2d 424 (slot-value slotd 'setter)
9adccb27 425 #'(lambda (value object)
09f6e237 426 (let ((location (foreign-location object)))
9adccb27 427 (funcall destroy location offset) ; destroy old value
eeda1c2d 428 (funcall writer value location offset))))))))
429
4d83a8a6 430 (call-next-method))
431
4d83a8a6 432 ;; TODO: call some C code to detect this a compile time
433 (defconstant +struct-alignmen+ 4)
94f15c3c 434
09f6e237 435 (defun align-offset (size)
436 (if (zerop (mod size +struct-alignmen+))
437 size
438 (+ size (- +struct-alignmen+ (mod size +struct-alignmen+)))))
439
94f15c3c 440 (defmethod compute-slots ((class proxy-class))
09f6e237 441 (let ((alien-slots
442 (remove-if-not
443 #'(lambda (slotd)
444 (eq (slot-definition-allocation slotd) :alien))
445 (class-direct-slots class))))
446 (when alien-slots
447 (loop
448 as offset = (align-offset (foreign-size
449 (most-specific-proxy-superclass class)))
450 then (align-offset
451 (+
452 (slot-definition-offset slotd)
453 (size-of (slot-definition-type slotd))))
454 for slotd in alien-slots
455 unless (slot-boundp slotd 'offset)
456 do (setf (slot-value slotd 'offset) offset))))
94f15c3c 457 (call-next-method))
3e15002d 458
4d83a8a6 459 (defmethod validate-superclass ((class proxy-class) (super standard-class))
460 (subtypep (class-name super) 'proxy))
461
09f6e237 462 (defmethod foreign-size ((class-name symbol))
463 (foreign-size (find-class class-name))))
47a11c16 464
09f6e237 465(defmethod foreign-size ((object proxy))
466 (foreign-size (class-of object)))
4d83a8a6 467
09f6e237 468
9adccb27 469(defmethod alien-type ((class proxy-class) &rest args)
470 (declare (ignore class args))
471 (alien-type 'pointer))
472
473(defmethod size-of ((class proxy-class) &rest args)
474 (declare (ignore class args))
475 (size-of 'pointer))
476
477(defmethod from-alien-form (location (class proxy-class) &rest args)
478 (declare (ignore args))
479 `(ensure-proxy-instance ',(class-name class) ,location))
480
481(defmethod from-alien-function ((class proxy-class) &rest args)
482 (declare (ignore args))
483 #'(lambda (location)
484 (ensure-proxy-instance class location)))
94f15c3c 485
9adccb27 486(defmethod to-alien-form (instance (class proxy-class) &rest args)
487 (declare (ignore class args))
09f6e237 488 `(foreign-location ,instance))
94f15c3c 489
9adccb27 490(defmethod to-alien-function ((class proxy-class) &rest args)
491 (declare (ignore class args))
09f6e237 492 #'foreign-location)
9adccb27 493
9ca5565a 494(defmethod copy-from-alien-form (location (class proxy-class) &rest args)
495 (declare (ignore args))
496 (let ((class-name (class-name class)))
497 `(ensure-proxy-instance ',class-name
498 (reference-foreign ',class-name ,location))))
499
500(defmethod copy-from-alien-function ((class proxy-class) &rest args)
501 (declare (ignore args))
502 #'(lambda (location)
503 (ensure-proxy-instance class (reference-foreign class location))))
504
505(defmethod copy-to-alien-form (instance (class proxy-class) &rest args)
506 (declare (ignore args))
09f6e237 507 `(reference-foreign ',(class-name class) (foreign-location ,instance)))
9ca5565a 508
509(defmethod copy-to-alien-function ((class proxy-class) &rest args)
73572c12 510 (declare (ignore args))
9ca5565a 511 #'(lambda (instance)
09f6e237 512 (reference-foreign class (foreign-location instance))))
9ca5565a 513
9adccb27 514(defmethod writer-function ((class proxy-class) &rest args)
515 (declare (ignore args))
516 #'(lambda (instance location &optional (offset 0))
517 (assert (null-pointer-p (sap-ref-sap location offset)))
518 (setf
519 (sap-ref-sap location offset)
09f6e237 520 (reference-foreign class (foreign-location instance)))))
94f15c3c 521
9adccb27 522(defmethod reader-function ((class proxy-class) &rest args)
523 (declare (ignore args))
3005806e 524 #'(lambda (location &optional (offset 0) weak-p)
525 (declare (ignore weak-p))
9ca5565a 526 (let ((instance (sap-ref-sap location offset)))
527 (unless (null-pointer-p instance)
528 (ensure-proxy-instance class (reference-foreign class instance))))))
94f15c3c 529
9adccb27 530(defmethod destroy-function ((class proxy-class) &rest args)
531 (declare (ignore args))
532 #'(lambda (location &optional (offset 0))
533 (unreference-foreign class (sap-ref-sap location offset))))
534
12b7df04 535(defmethod unbound-value ((class proxy-class) &rest args)
47a11c16 536 (declare (ignore args))
12b7df04 537 (values t nil))
9adccb27 538
8958fa4a 539(defun ensure-proxy-instance (class location &rest initargs)
540 "Returns a proxy object representing the foreign object at the give
541location. If an existing object is not found in the cache
542MAKE-PROXY-INSTANCE is called to create one."
9adccb27 543 (unless (null-pointer-p location)
544 (or
aaced14e 545 #-debug-ref-counting(find-cached-instance location)
546 #+debug-ref-counting
4a64c16d 547 (let ((instance (find-cached-instance location)))
548 (when instance
549 (format t "Object found in cache: ~A~%" instance)
550 instance))
8958fa4a 551 (let ((instance (apply #'make-proxy-instance class location initargs)))
552 (cache-instance instance)
553 instance))))
554
555(defgeneric make-proxy-instance (class location &key weak)
556 (:documentation "Creates a new proxy object representing the foreign
557object at the give location. If WEAK is non NIL the foreign memory
558will not be released when the proxy is garbage collected."))
559
4a64c16d 560(defmethod make-proxy-instance ((class symbol) location &rest initargs)
561 (apply #'make-proxy-instance (find-class class) location initargs))
8958fa4a 562
563(defmethod make-proxy-instance ((class proxy-class) location &key weak)
ca01de1b 564 (let ((instance
565 (or
566 (find-invalidated-instance class)
567 (allocate-instance class))))
c0e19882 568 (setf (foreign-location instance) location)
8958fa4a 569 (unless weak
570 (finalize instance (instance-finalizer instance)))
571 instance))
94f15c3c 572
12d0437e 573
574;;;; Superclasses for wrapping of C structures
94f15c3c 575
9adccb27 576(defclass struct (proxy)
577 ()
09f6e237 578 (:metaclass proxy-class)
579 (:size 0))
94f15c3c 580
308bfcab 581(defmethod allocate-foreign ((struct struct) &rest initargs)
94f15c3c 582 (declare (ignore initargs))
308bfcab 583 (let ((size (foreign-size (class-of struct))))
584 (if (zerop size)
585 (error "~A has zero size" (class-of struct))
586 (allocate-memory size))))
94f15c3c 587
588
9adccb27 589;;;; Metaclasses used for subclasses of struct
590
591(defclass struct-class (proxy-class)
592 ())
94f15c3c 593
e2ebafb1 594(defmethod direct-slot-definition-class ((class struct-class) &rest initargs)
595 (if (not (getf initargs :allocation))
596 (find-class 'direct-alien-slot-definition)
597 (call-next-method)))
598
9adccb27 599(defmethod reference-foreign ((class struct-class) location)
09f6e237 600 (copy-memory location (foreign-size class)))
9adccb27 601
602(defmethod unreference-foreign ((class struct-class) location)
12d0437e 603 (deallocate-memory location))
94f15c3c 604
65466e9c 605(defmethod compute-slots :around ((class struct-class))
606 (let ((slots (call-next-method)))
607 (when (and
608 #-sbcl>=0.9.8(class-finalized-p class) #+sbc098 t
609 (not (slot-boundp class 'size)))
610 (let ((size (loop
611 for slotd in slots
612 when (eq (slot-definition-allocation slotd) :alien)
613 maximize (+
614 (slot-definition-offset slotd)
615 (size-of (slot-definition-type slotd))))))
616 (setf (slot-value class 'size) (+ size (mod size +struct-alignmen+)))))
617 slots))
09f6e237 618
3005806e 619(defmethod reader-function ((class struct-class) &rest args)
4a64c16d 620 (declare (ignore args))
3005806e 621 #'(lambda (location &optional (offset 0) weak-p)
4a64c16d 622 (let ((instance (sap-ref-sap location offset)))
623 (unless (null-pointer-p instance)
3005806e 624 (if weak-p
625 (ensure-proxy-instance class instance :weak t)
626 (ensure-proxy-instance class (reference-foreign class instance)))))))
4a64c16d 627
94f15c3c 628
9adccb27 629(defclass static-struct-class (struct-class)
630 ())
94f15c3c 631
9adccb27 632(defmethod reference-foreign ((class static-struct-class) location)
633 (declare (ignore class))
12d0437e 634 location)
94f15c3c 635
9adccb27 636(defmethod unreference-foreign ((class static-struct-class) location)
637 (declare (ignore class location))
12d0437e 638 nil)
bde0b906 639
3005806e 640(defmethod reader-function ((class struct-class) &rest args)
641 (declare (ignore args))
642 #'(lambda (location &optional (offset 0) weak-p)
643 (declare (ignore weak-p))
644 (let ((instance (sap-ref-sap location offset)))
645 (unless (null-pointer-p instance)
646 (ensure-proxy-instance class instance :weak t)))))
647
bde0b906 648
649;;; Pseudo type for structs which are inlined in other objects
650
651(defmethod size-of ((type (eql 'inlined)) &rest args)
652 (declare (ignore type))
09f6e237 653 (foreign-size (first args)))
bde0b906 654
655(defmethod reader-function ((type (eql 'inlined)) &rest args)
656 (declare (ignore type))
657 (destructuring-bind (class) args
3005806e 658 #'(lambda (location &optional (offset 0) weak-p)
659 (declare (ignore weak-p))
bde0b906 660 (ensure-proxy-instance class
661 (reference-foreign class (sap+ location offset))))))
662
4a64c16d 663(defmethod writer-function ((type (eql 'inlined)) &rest args)
664 (declare (ignore type))
665 (destructuring-bind (class) args
666 #'(lambda (instance location &optional (offset 0))
667 (copy-memory (foreign-location instance) (foreign-size class) (sap+ location offset)))))
668
bde0b906 669(defmethod destroy-function ((type (eql 'inlined)) &rest args)
670 (declare (ignore args))
671 #'(lambda (location &optional (offset 0))
672 (declare (ignore location offset))))
673
674(export 'inlined)