1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
3 ;;; cffi-allegro.lisp --- CFFI-SYS implementation for Allegro CL.
5 ;;; Copyright (C) 2005-2009, Luis Oliveira <loliveira(@)common-lisp.net>
7 ;;; Permission is hereby granted, free of charge, to any person
8 ;;; obtaining a copy of this software and associated documentation
9 ;;; files (the "Software"), to deal in the Software without
10 ;;; restriction, including without limitation the rights to use, copy,
11 ;;; modify, merge, publish, distribute, sublicense, and/or sell copies
12 ;;; of the Software, and to permit persons to whom the Software is
13 ;;; furnished to do so, subject to the following conditions:
15 ;;; The above copyright notice and this permission notice shall be
16 ;;; included in all copies or substantial portions of the Software.
18 ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 ;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 ;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 ;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 ;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 ;;; DEALINGS IN THE SOFTWARE.
30 (defpackage #:cffi-sys
32 (:import-from
#:alexandria
#:if-let
#:with-unique-names
#:once-only
)
34 #:canonicalize-symbol-name-case
45 #:with-foreign-pointer
47 #:%foreign-funcall-pointer
48 #:%foreign-type-alignment
50 #:%load-foreign-library
51 #:%close-foreign-library
55 #:make-shareable-byte-vector
56 #:with-pointer-to-vector-data
57 #:%foreign-symbol-pointer
58 #:defcfun-helper-forms
62 (in-package #:cffi-sys
)
66 #-
64bit
(pushnew 'no-long-long
*features
*)
67 (pushnew 'flat-namespace
*features
*)
71 (defun canonicalize-symbol-name-case (name)
72 (declare (string name
))
73 (if (eq excl
:*current-case-mode
* :case-sensitive-lower
)
74 (string-downcase name
)
75 (string-upcase name
)))
77 ;;;# Basic Pointer Operations
79 (deftype foreign-pointer
()
83 "Return true if PTR is a foreign pointer."
84 (ff:foreign-address-p ptr
))
86 (defun pointer-eq (ptr1 ptr2
)
87 "Return true if PTR1 and PTR2 point to the same address."
90 (defun null-pointer ()
91 "Return a null pointer."
94 (defun null-pointer-p (ptr)
95 "Return true if PTR is a null pointer."
98 (defun inc-pointer (ptr offset
)
99 "Return a pointer pointing OFFSET bytes past PTR."
102 (defun make-pointer (address)
103 "Return a pointer pointing to ADDRESS."
104 (check-type address ff
:foreign-address
)
107 (defun pointer-address (ptr)
108 "Return the address pointed to by PTR."
109 (check-type ptr ff
:foreign-address
)
114 ;;; Functions and macros for allocating foreign memory on the stack
115 ;;; and on the heap. The main CFFI package defines macros that wrap
116 ;;; FOREIGN-ALLOC and FOREIGN-FREE in UNWIND-PROTECT for the common usage
117 ;;; when the memory has dynamic extent.
119 (defun %foreign-alloc
(size)
120 "Allocate SIZE bytes on the heap and return a pointer."
121 (ff:allocate-fobject
:char
:c size
))
123 (defun foreign-free (ptr)
124 "Free a PTR allocated by FOREIGN-ALLOC."
125 (ff:free-fobject ptr
))
127 (defmacro with-foreign-pointer
((var size
&optional size-var
) &body body
)
128 "Bind VAR to SIZE bytes of foreign memory during BODY. The
129 pointer in VAR is invalid beyond the dynamic extent of BODY, and
130 may be stack-allocated if supported by the implementation. If
131 SIZE-VAR is supplied, it will be bound to SIZE during BODY."
133 (setf size-var
(gensym "SIZE")))
135 (when (and (constantp size
) (<= (eval size
) ff
:*max-stack-fobject-bytes
*))
136 (return-from with-foreign-pointer
137 `(let ((,size-var
,(eval size
)))
138 (declare (ignorable ,size-var
))
139 (ff:with-static-fobject
(,var
'(:array
:char
,(eval size
))
140 :allocation
:foreign-static-gc
)
141 ;; (excl::stack-allocated-p var) => T
142 (let ((,var
(ff:fslot-address
,var
)))
144 `(let* ((,size-var
,size
)
145 (,var
(ff:allocate-fobject
:char
:c
,size-var
)))
148 (ff:free-fobject
,var
))))
150 ;;;# Shareable Vectors
152 ;;; This interface is very experimental. WITH-POINTER-TO-VECTOR-DATA
153 ;;; should be defined to perform a copy-in/copy-out if the Lisp
154 ;;; implementation can't do this.
156 (defun make-shareable-byte-vector (size)
157 "Create a Lisp vector of SIZE bytes can passed to
158 WITH-POINTER-TO-VECTOR-DATA."
159 (make-array size
:element-type
'(unsigned-byte 8)
160 :allocation
:static-reclaimable
))
162 (defmacro with-pointer-to-vector-data
((ptr-var vector
) &body body
)
163 "Bind PTR-VAR to a foreign pointer to the data in VECTOR."
164 ;; An array allocated in static-reclamable is a non-simple array in
165 ;; the normal Lisp allocation area, pointing to a simple array in
166 ;; the static-reclaimable allocation area. Therefore we have to get
167 ;; out the simple-array to find the pointer to the actual contents.
168 (with-unique-names (simple-vec)
169 `(excl:with-underlying-simple-vector
(,vector
,simple-vec
)
170 (let ((,ptr-var
(ff:fslot-address-typed
:unsigned-char
:lisp
176 (defun convert-foreign-type (type-keyword)
177 "Convert a CFFI type keyword to an Allegro type."
180 (:unsigned-char
:unsigned-char
)
182 (:unsigned-short
:unsigned-short
)
184 (:unsigned-int
:unsigned-int
)
186 (:unsigned-long
:unsigned-long
)
189 #-
64bit
(error "this platform does not support :long-long."))
191 #+64bit
:unsigned-nat
192 #-
64bit
(error "this platform does not support :unsigned-long-long"))
195 (:pointer
:unsigned-nat
)
198 (defun %mem-ref
(ptr type
&optional
(offset 0))
199 "Dereference an object of TYPE at OFFSET bytes from PTR."
200 (unless (zerop offset
)
201 (setf ptr
(inc-pointer ptr offset
)))
202 (ff:fslot-value-typed
(convert-foreign-type type
) :c ptr
))
204 ;;; Compiler macro to open-code the call to FSLOT-VALUE-TYPED when the
205 ;;; CFFI type is constant. Allegro does its own transformation on the
206 ;;; call that results in efficient code.
207 (define-compiler-macro %mem-ref
(&whole form ptr type
&optional
(off 0))
209 (let ((ptr-form (if (eql off
0) ptr
`(+ ,ptr
,off
))))
210 `(ff:fslot-value-typed
',(convert-foreign-type (eval type
))
214 (defun %mem-set
(value ptr type
&optional
(offset 0))
215 "Set the object of TYPE at OFFSET bytes from PTR."
216 (unless (zerop offset
)
217 (setf ptr
(inc-pointer ptr offset
)))
218 (setf (ff:fslot-value-typed
(convert-foreign-type type
) :c ptr
) value
))
220 ;;; Compiler macro to open-code the call to (SETF FSLOT-VALUE-TYPED)
221 ;;; when the CFFI type is constant. Allegro does its own
222 ;;; transformation on the call that results in efficient code.
223 (define-compiler-macro %mem-set
(&whole form val ptr type
&optional
(off 0))
226 (let ((ptr-form (if (eql off
0) ptr
`(+ ,ptr
,off
))))
227 `(setf (ff:fslot-value-typed
',(convert-foreign-type (eval type
))
228 :c
,ptr-form
) ,val
)))
231 ;;;# Calling Foreign Functions
233 (defun %foreign-type-size
(type-keyword)
234 "Return the size in bytes of a foreign type."
235 (ff:sizeof-fobject
(convert-foreign-type type-keyword
)))
237 (defun %foreign-type-alignment
(type-keyword)
238 "Returns the alignment in bytes of a foreign type."
239 #+(and powerpc macosx32
)
240 (when (eq type-keyword
:double
)
241 (return-from %foreign-type-alignment
8))
242 ;; No override necessary for the remaining types....
243 (ff::sized-ftype-prim-align
244 (ff::iforeign-type-sftype
246 (convert-foreign-type type-keyword
)))))
248 (defun foreign-funcall-type-and-args (args)
249 "Returns a list of types, list of args and return type."
250 (let ((return-type :void
))
251 (loop for
(type arg
) on args by
#'cddr
252 if arg collect type into types
253 and collect arg into fargs
254 else do
(setf return-type type
)
255 finally
(return (values types fargs return-type
)))))
257 (defun convert-to-lisp-type (type)
259 ((:char
:short
:int
:long
:nat
)
260 `(signed-byte ,(* 8 (ff:sizeof-fobject type
))))
261 ((:unsigned-char
:unsigned-short
:unsigned-int
:unsigned-long
:unsigned-nat
)
262 `(unsigned-byte ,(* 8 (ff:sizeof-fobject type
))))
263 (:float
'single-float
)
264 (:double
'double-float
)
267 (defun allegro-type-pair (cffi-type)
268 ;; the :FOREIGN-ADDRESS pseudo-type accepts both pointers and
269 ;; arrays. We need the latter for shareable byte vector support.
270 (if (eq cffi-type
:pointer
)
271 (list :foreign-address
)
272 (let ((ftype (convert-foreign-type cffi-type
)))
273 (list ftype
(convert-to-lisp-type ftype
)))))
276 (defun note-named-foreign-function (symbol name types rettype
)
277 "Give Allegro's compiler a hint to perform a direct call."
278 `(eval-when (:compile-toplevel
:load-toplevel
:execute
)
279 (setf (get ',symbol
'system
::direct-ff-call
)
280 (list '(,name
:language
:c
)
283 ;; return type '(:c-type lisp-type)
284 ',(allegro-type-pair rettype
)
285 ;; arg types '({(:c-type lisp-type)}*)
286 '(,@(mapcar #'allegro-type-pair types
))
288 ff
::ep-flag-never-release
))))
290 (defmacro %foreign-funcall
(name args
&key convention library
)
291 (declare (ignore convention library
))
292 (multiple-value-bind (types fargs rettype
)
293 (foreign-funcall-type-and-args args
)
295 (load-time-value (excl::determine-foreign-address
296 '(,name
:language
:c
)
297 #-
(version>= 8 1) ff
::ep-flag-never-release
298 #+(version>= 8 1) ff
::ep-flag-always-release
301 ;; arg types {'(:c-type lisp-type) argN}*
302 ,@(mapcan (lambda (type arg
)
303 `(',(allegro-type-pair type
) ,arg
))
305 ;; return type '(:c-type lisp-type)
306 ',(allegro-type-pair rettype
))))
308 (defun defcfun-helper-forms (name lisp-name rettype args types options
)
309 "Return 2 values for DEFCFUN. A prelude form and a caller form."
310 (declare (ignore options
))
311 (let ((ff-name (intern (format nil
"%cffi-foreign-function/~A" lisp-name
))))
313 `(ff:def-foreign-call
(,ff-name
,name
)
314 ,(loop for type in types
315 collect
(list* (gensym) (allegro-type-pair type
)))
316 :returning
,(allegro-type-pair rettype
)
317 ;; Don't use call-direct when there are no arguments.
318 ,@(unless (null args
) '(:call-direct t
))
321 #+(version>= 8 1) ,@'(:release-heap
:when-ok
322 :release-heap-ignorable t
)
323 #+smp
,@'(:release-heap-implies-allow-gc t
))
324 `(,ff-name
,@args
))))
326 ;;; See doc/allegro-internals.txt for a clue about entry-vec.
327 (defmacro %foreign-funcall-pointer
(ptr args
&key convention
)
328 (declare (ignore convention
))
329 (multiple-value-bind (types fargs rettype
)
330 (foreign-funcall-type-and-args args
)
331 (with-unique-names (entry-vec)
332 `(let ((,entry-vec
(excl::make-entry-vec-boa
)))
333 (setf (aref ,entry-vec
1) ,ptr
) ; set jump address
336 ;; arg types {'(:c-type lisp-type) argN}*
337 ,@(mapcan (lambda (type arg
)
338 `(',(allegro-type-pair type
) ,arg
))
340 ;; return type '(:c-type lisp-type)
341 ',(allegro-type-pair rettype
))))))
345 ;;; The *CALLBACKS* hash table contains information about a callback
346 ;;; for the Allegro FFI. The key is the name of the CFFI callback,
347 ;;; and the value is a cons, the car containing the symbol the
348 ;;; callback was defined on in the CFFI-CALLBACKS package, the cdr
349 ;;; being an Allegro FFI pointer (a fixnum) that can be passed to C
352 ;;; These pointers must be restored when a saved Lisp image is loaded.
353 ;;; The RESTORE-CALLBACKS function is added to *RESTART-ACTIONS* to
354 ;;; re-register the callbacks during Lisp startup.
355 (defvar *callbacks
* (make-hash-table))
357 ;;; Register a callback in the *CALLBACKS* hash table.
358 (defun register-callback (cffi-name callback-name
)
359 (setf (gethash cffi-name
*callbacks
*)
360 (cons callback-name
(ff:register-foreign-callable
361 callback-name
:reuse t
))))
363 ;;; Restore the saved pointers in *CALLBACKS* when loading an image.
364 (defun restore-callbacks ()
365 (maphash (lambda (key value
)
366 (register-callback key
(car value
)))
369 ;;; Arrange for RESTORE-CALLBACKS to run when a saved image containing
370 ;;; CFFI is restarted.
371 (eval-when (:load-toplevel
:execute
)
372 (pushnew 'restore-callbacks excl
:*restart-actions
*))
374 ;;; Create a package to contain the symbols for callback functions.
375 (defpackage #:cffi-callbacks
378 (defun intern-callback (name)
379 (intern (format nil
"~A::~A"
380 (if-let (package (symbol-package name
))
381 (package-name package
)
386 (defun convert-calling-convention (convention)
389 (:stdcall
:stdcall
)))
391 (defmacro %defcallback
(name rettype arg-names arg-types body
393 (declare (ignore rettype
))
394 (let ((cb-name (intern-callback name
)))
396 (ff:defun-foreign-callable
,cb-name
397 ,(mapcar (lambda (sym type
) (list sym
(convert-foreign-type type
)))
399 (declare (:convention
,(convert-calling-convention convention
)))
401 (register-callback ',name
',cb-name
))))
403 ;;; Return the saved Lisp callback pointer from *CALLBACKS* for the
404 ;;; CFFI callback named NAME.
405 (defun %callback
(name)
406 (or (cdr (gethash name
*callbacks
*))
407 (error "Undefined callback: ~S" name
)))
409 ;;;# Loading and Closing Foreign Libraries
411 (defun %load-foreign-library
(name path
)
412 "Load a foreign library."
413 ;; ACL 8.0 honors the :FOREIGN option and always tries to foreign load
414 ;; the argument. However, previous versions do not and will only
415 ;; foreign load the argument if its type is a member of the
416 ;; EXCL::*LOAD-FOREIGN-TYPES* list. Therefore, we bind that special
417 ;; to a list containing whatever type NAME has.
418 (declare (ignore name
))
419 (let ((excl::*load-foreign-types
*
420 (list (pathname-type (parse-namestring path
)))))
423 #+(version>= 7) (load path
:foreign t
)
424 #-
(version>= 7) (load path
))
426 (error (change-class fe
'simple-error
))))
429 (defun %close-foreign-library
(name)
430 "Close the foreign library NAME."
431 (ff:unload-foreign-library name
))
433 (defun native-namestring (pathname)
434 (namestring pathname
))
438 (defun convert-external-name (name)
439 "Add an underscore to NAME if necessary for the ABI."
440 #+macosx
(concatenate 'string
"_" name
)
443 (defun %foreign-symbol-pointer
(name library
)
444 "Returns a pointer to a foreign symbol NAME."
445 (declare (ignore library
))
446 (prog1 (ff:get-entry-point
(convert-external-name name
))))