chiark / gitweb /
Fix for CLisp 2.40
[clg] / gffi / interface.lisp
CommitLineData
beae6579 1;; Common Lisp bindings for GTK+ v2.x
2;; Copyright 1999-2006 Espen S. Johnsen <espen@users.sf.net>
3;;
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:
11;;
12;; The above copyright notice and this permission notice shall be
13;; included in all copies or substantial portions of the Software.
14;;
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.
22
4f2a8644 23;; $Id: interface.lisp,v 1.4 2006-12-12 14:32:06 espen Exp $
beae6579 24
25(in-package "GFFI")
26
27
28;;;; Foreign function call interface
29
30(defvar *package-prefix* nil)
31
32(defun set-package-prefix (prefix &optional (package *package*))
33 (let ((package (find-package package)))
34 (setq *package-prefix* (delete package *package-prefix* :key #'car))
35 (push (cons package prefix) *package-prefix*))
36 prefix)
37
38(defun package-prefix (&optional (package *package*))
39 (let ((package (find-package package)))
40 (or
41 (cdr (assoc package *package-prefix*))
42 (substitute #\_ #\- (string-downcase (package-name package))))))
43
44(defun find-prefix-package (prefix)
45 (or
46 (car (rassoc (string-downcase prefix) *package-prefix* :test #'string=))
47 (find-package (string-upcase prefix))))
48
49(defmacro use-prefix (prefix &optional (package *package*))
50 `(eval-when (:compile-toplevel :load-toplevel :execute)
51 (set-package-prefix ,prefix ,package)))
52
53
54(defun default-alien-fname (lisp-name)
55 (let* ((name (substitute #\_ #\- (string-downcase lisp-name)))
49ef0cdc 56 (start (position-if-not #'(lambda (char) (char= char #\%)) name))
57 (end (if (string= "_p" name :start2 (- (length name) 2))
58 (- (length name) 2)
59 (length name)))
60 (stripped-name (subseq name start end))
beae6579 61 (prefix (package-prefix *package*)))
62 (if (or (not prefix) (string= prefix ""))
63 stripped-name
64 (format nil "~A_~A" prefix stripped-name))))
65
66(defun default-alien-type-name (type-name)
67 (let ((prefix (package-prefix *package*)))
68 (apply
69 #'concatenate
70 'string
71 (mapcar
72 #'string-capitalize
73 (cons prefix (split-string (symbol-name type-name) :delimiter #\-))))))
74
75(defun default-type-name (alien-name)
76 (let ((parts
77 (mapcar
78 #'string-upcase
79 (split-string-if alien-name #'upper-case-p))))
80 (intern
81 (concatenate-strings (rest parts) #\-)
82 (find-prefix-package (first parts)))))
83
84
85(defun in-arg-p (style)
86 (find style '(:in :in/out :in/return :in-out :return)))
87
88(defun out-arg-p (style)
89 (find style '(:out :in/out :in-out)))
90
91(defun return-arg-p (style)
92 (find style '(:in/return :return)))
93
94(defmacro defbinding (name lambda-list return-type &rest args)
95 (multiple-value-bind (lisp-name c-name)
96 (if (atom name)
97 (values name (default-alien-fname name))
98 (values-list name))
99
100 (let* ((lambda-list-supplied-p lambda-list)
101 (lambda-list (unless (equal lambda-list '(nil)) lambda-list))
102 (aux-vars ())
103 (doc-string (when (stringp (first args)) (pop args)))
104 (parsed-args
105 (mapcar
106 #'(lambda (arg)
107 (destructuring-bind
108 (expr type &optional (style :in) (out-type type)) arg
109 (cond
110 ((find style '(:in-out :return))
111 (warn "Deprecated argument style: ~S" style))
112 ((not (find style '(:in :out :in/out :in/return)))
113 (error "Bogus argument style: ~S" style)))
114 (when (and
115 (not lambda-list-supplied-p)
116 (namep expr) (in-arg-p style))
117 (push expr lambda-list))
118 (let ((aux (unless (or (not (in-arg-p style)) (namep expr))
119 (gensym))))
120 (when aux
121 (push `(,aux ,expr) aux-vars))
122 (list
123 (cond
124 ((and (namep expr) (not (in-arg-p style))) expr)
125 ((namep expr) (make-symbol (string expr)))
126 ((gensym)))
127 (or aux expr) type style out-type))))
128 args)))
129
130 (%defbinding c-name lisp-name
131 (if lambda-list-supplied-p lambda-list (nreverse lambda-list))
132 aux-vars return-type doc-string parsed-args))))
133
134
135#+(or cmu sbcl)
136(defun foreign-funcall (cname args return-type)
137 (let ((fparams (loop
138 for (var expr type style out-type) in args
139 collect (if (out-arg-p style)
140 `(addr ,var)
141 var)))
142 (ftypes (loop
143 for (var expr type style out-type) in args
144 collect (if (out-arg-p style)
145 `(* ,(alien-type out-type))
146 (alien-type out-type))))
147 (fname (make-symbol cname)))
148 `(with-alien ((,fname (function ,(alien-type return-type) ,@ftypes) :extern ,cname))
149 (alien-funcall ,fname ,@fparams))))
150
151#+clisp
152(defun foreign-funcall (cname args return-type)
153 (let* ((fparams (loop
154 for (var expr type style out-type) in args
155 collect (if (out-arg-p style)
156 `(ffi:c-var-address ,var)
157 var)))
158 (fargs (loop
159 for (var expr type style out-type) in args
160 collect (list var (if (out-arg-p style)
161 'ffi:c-pointer
162 (alien-type out-type)))))
163 (c-function `(ffi:c-function
164 (:arguments ,@fargs)
165 (:return-type ,(alien-type return-type))
166 (:language :stdc))))
167 `(funcall
168 (load-time-value
4f2a8644 169 (ffi::foreign-library-function
170 ,cname (ffi::foreign-library :default) #?(clisp>= 2 40)nil
beae6579 171 nil (ffi:parse-c-type ',c-function)))
172 ,@fparams)))
173
174
175;; TODO: check if in and out types (if different) translates to same
176;; alien type
177(defun %defbinding (cname lisp-name lambda-list aux-vars return-type doc args)
178 (let ((out (loop
179 for (var expr type style out-type) in args
180 when (or (out-arg-p style) (return-arg-p style))
181 collect (from-alien-form out-type var)))
182 (fcall (from-alien-form return-type
183 (foreign-funcall cname args return-type))))
184
185 (labels ((create-wrapper (args body)
186 (if args
187 (destructuring-bind (var expr type style out-type) (first args)
188 (declare (ignore out-type))
189 (alien-arg-wrapper type var expr style
190 (create-wrapper (rest args) body)))
191 body)))
192 `(defun ,lisp-name ,lambda-list
193 ,doc
194 (let ,aux-vars
195 ,(if return-type
196 (create-wrapper args `(values ,fcall ,@out))
197 (create-wrapper args `(progn ,fcall (values ,@out)))))))))
198
199
200
201;;;; Dynamic (runtime) bindings
202
203(defun mkbinding (name return-type &rest arg-types)
204 #+cmu(declare (optimize (inhibit-warnings 3)))
205 #+sbcl(declare (muffle-conditions compiler-note))
206 (let* ((c-function
207 #+(or cmu sbcl)
208 `(function ,@(mapcar #'alien-type (cons return-type arg-types)))
209 #+clisp
210 `(ffi:c-function
211 (:arguments ,@(mapcar
212 #'(lambda (type)
213 (list (gensym) (alien-type type)))
214 arg-types))
215 (:return-type ,(alien-type return-type))
216 (:language :stdc)))
217 (foreign
218 #+(or cmu sbcl)
219 (handler-bind (#+sbcl(compiler-note #'(lambda (condition)
220 (declare (ignore condition))
221 (muffle-warning))))
222 (%heap-alien
223 (make-heap-alien-info
224 :type (parse-alien-type c-function #+sbcl nil)
225 :sap-form (let ((address (foreign-symbol-address name)))
226 (etypecase address
227 (integer (int-sap address))
228 (system-area-pointer address))))))
229 #+clisp
230 (ffi::foreign-library-function name
4f2a8644 231 (ffi::foreign-library :default) #?(clisp>= 2 40)nil
beae6579 232 nil (ffi:parse-c-type c-function)))
233 (return-value-translator (from-alien-function return-type)))
234 (multiple-value-bind (arg-translators cleanup-funcs)
235 (let ((translator/cleanup-pairs
236 (mapcar
237 #'(lambda (type)
238 (multiple-value-list (to-alien-function type)))
239 arg-types)))
240 (values
241 (mapcar #'first translator/cleanup-pairs)
242 (mapcar #'second translator/cleanup-pairs)))
243 #'(lambda (&rest args)
244 (let ((translated-args (mapcar #'funcall arg-translators args)))
245 (prog1
246 (funcall return-value-translator
247 #+(or cmu sbcl)(apply #'alien-funcall foreign translated-args)
248 #+clisp(apply foreign translated-args))
249 (mapc
250 #'(lambda (cleanup arg translated-arg)
251 (when cleanup
252 (funcall cleanup arg translated-arg)))
253 cleanup-funcs args translated-args)))))))
254
255
256
257;;;; C Callbacks
258
259(defun callback-body (args return-type body)
260 (labels ((create-wrappers (args body)
261 (if args
262 (destructuring-bind (var type) (first args)
263 (callback-wrapper type var var
264 (create-wrappers (rest args) body)))
265 body))
266 (create-body (args body)
267 (to-alien-form return-type
268 (create-wrappers args `(progn ,@body)))))
269 (if (and (consp (first body)) (eq (caar body) 'declare))
270 (let ((ignored (loop
271 for declaration in (cdar body)
272 when (eq (first declaration) 'ignore)
273 nconc (rest declaration))))
274 `(,(first body)
275 ,(create-body
276 (remove-if #'(lambda (arg)
277 (find (first arg) ignored))
278 args)
279 (rest body))))
280 (list (create-body args body)))))
281
282
283#+(or cmu sbcl)
284(defmacro define-callback (name return-type args &body body)
285 (let ((define-callback
286 #+cmu'alien:def-callback
287 #+(and sbcl alien-callbacks)'sb-alien::define-alien-callback
288 #+(and sbcl (not alien-callbacks))'sb-alien:define-alien-function))
289 `(progn
290 #+cmu(defparameter ,name nil)
291 (,define-callback ,name
292 #+(and sbcl alien-callbacks) ,(alien-type return-type)
293 (#+(or cmu (and sbcl (not alien-callbacks))),(alien-type return-type)
294 ,@(loop
295 for (name type) in args
296 collect `(,name ,(alien-type type))))
297 ,@(callback-body args return-type body)))))
298
299#+(or cmu sbcl)
300(defun callback-address (callback)
301 #+cmu(alien::callback-trampoline callback)
302 #+(and sbcl (not alien-callbacks))(sb-alien:alien-function-sap callback)
303 #+(and sbcl alien-callbacks)(sb-alien:alien-sap callback))
304
305#+sbcl
306(deftype callback ()
307 #-alien-callbacks'sb-alien:alien-function
308 #+alien-callbacks'sb-alien:alien)
309
310
311;;; The callback code for CLISP is based on code from CFFI
312;;; Copyright (C) 2005, James Bielman <jamesjb@jamesjb.com>
313;;; (C) 2005, Joerg Hoehle <hoehle@users.sourceforge.net>
314
315
316;;; *CALLBACKS* contains the callbacks defined by the %DEFCALLBACK
317;;; macro. The symbol naming the callback is the key, and the value
318;;; is a list containing a Lisp function, the parsed CLISP FFI type of
319;;; the callback, and a saved pointer that should not persist across
320;;; saved images.
321#+clisp
322(progn
323 (defvar *callbacks* (make-hash-table))
324
325 ;;; Return a CLISP FFI function type for a CFFI callback function
326 ;;; given a return type and list of argument names and types.
327 (eval-when (:compile-toplevel :load-toplevel :execute)
328 (defun callback-type (return-type arg-names arg-types)
329 (ffi:parse-c-type
330 `(ffi:c-function
331 (:arguments ,@(mapcar (lambda (sym type)
332 (list sym (alien-type type)))
333 arg-names arg-types))
334 (:return-type ,(alien-type return-type))
335 (:language :stdc)))))
336
337 ;;; Register and create a callback function.
338 (defun register-callback (name function parsed-type)
339 (setf (gethash name *callbacks*)
340 (list function parsed-type
341 (ffi:with-foreign-object (ptr 'ffi:c-pointer)
342 ;; Create callback by converting Lisp function to foreign
343 (setf (ffi:memory-as ptr parsed-type) function)
344 (ffi:foreign-value ptr)))))
345
346 ;;; Restore all saved callback pointers when restarting the Lisp
347 ;;; image. This is pushed onto CUSTOM:*INIT-HOOKS*.
348 ;;; Needs clisp > 2.35, bugfix 2005-09-29
349 (defun restore-callback-pointers ()
350 (maphash
351 (lambda (name list)
352 (register-callback name (first list) (second list)))
353 *callbacks*))
354
355 ;;; Add RESTORE-CALLBACK-POINTERS to the lists of functions to run
356 ;;; when an image is restarted.
357 (eval-when (:load-toplevel :execute)
358 (pushnew 'restore-callback-pointers custom:*init-hooks*))
359
360 ;;; Define a callback function NAME to run BODY with arguments
361 ;;; ARG-NAMES translated according to ARG-TYPES and the return type
362 ;;; translated according to RETTYPE. Obtain a pointer that can be
363 ;;; passed to C code for this callback by calling %CALLBACK.
364 (defmacro define-callback (name return-type args &body body)
365 (let ((arg-names (mapcar #'first args))
366 (arg-types (mapcar #'second args)))
367 `(progn
368 (defvar ,name ',name)
369 (register-callback ',name
370 (lambda ,arg-names ,@(callback-body args return-type body))
371 ,(callback-type return-type arg-names arg-types)))))
372
373 ;;; Look up the name of a callback and return a pointer that can be
374 ;;; passed to a C function. Signals an error if no callback is
375 ;;; defined called NAME.
376 (defun callback-address (name)
377 (multiple-value-bind (list winp) (gethash name *callbacks*)
378 (unless winp
379 (error "Undefined callback: ~S" name))
380 (third list)))
381
382 (deftype callback () 'symbol))
383
384
385
386;;;; Type expansion
387
388(defun type-expand-1 (form)
389 #+(or cmu sbcl)
390 (let ((def (cond ((symbolp form)
391 #+cmu(kernel::info type expander form)
392 #+sbcl(sb-impl::info :type :expander form))
393 ((and (consp form) (symbolp (car form)))
394 #+cmu(kernel::info type expander (car form))
395 #+sbcl(sb-impl::info :type :expander (car form)))
396 (t nil))))
397 (if def
398 (values (funcall def (if (consp form) form (list form))) t)
399 (values form nil)))
400 #+clisp(ext:type-expand form t))
401
402(defun type-expand-to (type form)
403 (labels ((expand (form0)
404 (if (eq (first (mklist form0)) type)
405 form0
406 (multiple-value-bind (expanded-form expanded-p)
407 (type-expand-1 form0)
408 (if expanded-p
409 (expand expanded-form)
410 (error "~A can not be expanded to ~A" form type))))))
411 (expand form)))
412
413
414
415;;;; Type methods
416
417(defun find-next-type-method (name type-spec &optional (error-p t))
418 (let ((type-methods (get name 'type-methods)))
419 (labels ((search-method-in-cpl-order (classes)
420 (when classes
421 (or
422 (gethash (class-name (first classes)) type-methods)
423 (search-method-in-cpl-order (rest classes)))))
424 (lookup-method (type-spec)
425 (if (and (symbolp type-spec) (find-class type-spec nil))
426 (let ((class (find-class type-spec)))
584285fb 427 #?(or (sbcl>= 0 9 15) (featurep :clisp))
beae6579 428 (unless (class-finalized-p class)
429 (finalize-inheritance class))
430 (search-method-in-cpl-order
431 (rest (class-precedence-list class))))
432 (multiple-value-bind (expanded-type expanded-p)
433 (type-expand-1 type-spec)
434 (when expanded-p
435 (or
436 (let ((specifier (etypecase expanded-type
437 (symbol expanded-type)
438 (list (first expanded-type)))))
439 (gethash specifier type-methods))
440 (lookup-method expanded-type))))))
441 (search-built-in-type-hierarchy (sub-tree)
442 (when (subtypep type-spec (first sub-tree))
443 (or
444 (search-nodes (cddr sub-tree))
445 (second sub-tree))))
446 (search-nodes (nodes)
447 (loop
448 for node in nodes
449 as method = (search-built-in-type-hierarchy node)
450 until method
451 finally (return method))))
452 (or
453 (lookup-method type-spec)
454 ;; This is to handle unexpandable types whichs doesn't name a
455 ;; class. It may cause infinite loops with illegal
456 ;; call-next-method calls
457 (unless (and (symbolp type-spec) (find-class type-spec nil))
458 (search-nodes (get name 'built-in-type-hierarchy)))
459 (when error-p
460 (error "No next type method ~A for type specifier ~A"
461 name type-spec))))))
462
463(defun find-applicable-type-method (name type-spec &optional (error-p t))
464 (let ((type-methods (get name 'type-methods))
465 (specifier (if (atom type-spec)
466 type-spec
467 (first type-spec))))
468 (or
469 (gethash specifier type-methods)
470 (find-next-type-method name type-spec nil)
471 (when error-p
472 (error
473 "No applicable type method for ~A when call width type specifier ~A"
474 name type-spec)))))
475
476(defun insert-type-in-hierarchy (specifier function nodes)
477 (cond
478 ((let ((node (find specifier nodes :key #'first)))
479 (when node
480 (setf (second node) function)
481 nodes)))
482 ((let ((node
483 (find-if
484 #'(lambda (node)
485 (subtypep specifier (first node)))
486 nodes)))
487 (when node
488 (setf (cddr node)
489 (insert-type-in-hierarchy specifier function (cddr node)))
490 nodes)))
491 ((let ((sub-nodes (remove-if-not
492 #'(lambda (node)
493 (subtypep (first node) specifier))
494 nodes)))
495 (cons
496 (list* specifier function sub-nodes)
497 (nset-difference nodes sub-nodes))))))
498
499(defun add-type-method (name specifier function)
500 (setf (gethash specifier (get name 'type-methods)) function)
501 (when (typep (find-class specifier nil) 'built-in-class)
502 (setf (get name 'built-in-type-hierarchy)
503 (insert-type-in-hierarchy specifier function
504 (get name 'built-in-type-hierarchy)))))
505
506
507(defmacro define-type-generic (name lambda-list &optional documentation)
508 (let ((type-spec (first lambda-list)))
509 (if (or
510 (not lambda-list)
511 (find type-spec '(&optional &key &rest &allow-other-keys)))
512 (error "A type generic needs at least one required argument")
513 `(progn
514 (unless (get ',name 'type-methods)
515 (setf (get ',name 'type-methods) (make-hash-table))
516 (setf (get ',name 'built-in-type-hierarchy) ()))
517 ,(if (intersection '(&optional &key &rest &allow-other-keys) lambda-list)
518 (let ((args (make-symbol "ARGS")))
519 `(defun ,name (,type-spec &rest ,args)
520 ,documentation
521 (apply
522 (find-applicable-type-method ',name ,type-spec)
523 ,type-spec ,args)))
524 `(defun ,name ,lambda-list
525 ,documentation
526 (funcall
527 (find-applicable-type-method ',name ,type-spec)
528 ,@lambda-list)))))))
529
530
531(defmacro define-type-method (name lambda-list &body body)
532 (let ((specifier (cadar lambda-list))
533 (args (make-symbol "ARGS")))
534 `(progn
535 (add-type-method ',name ',specifier
536 #'(lambda (&rest ,args)
537 (flet ((call-next-method (&rest args)
538 (let ((next-method (find-next-type-method ',name ',specifier)))
539 (apply next-method (or args ,args)))))
540 (destructuring-bind (,(caar lambda-list) ,@(rest lambda-list)) ,args
541 ,@body))))
542 ',name)))
543
544
545;;; Rules for auto-exporting symbols
546
547(defexport defbinding (name &rest args)
548 (declare (ignore args))
549 (if (symbolp name)
550 name
551 (first name)))
552
553(defexport define-type-generic (name &rest args)
554 (declare (ignore args))
555 name)