1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
3 ;;; cffi-gcl.lisp --- CFFI-SYS implementation for GNU Common Lisp.
5 ;;; Copyright (C) 2005-2006, 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.
28 ;;; GCL specific notes:
30 ;;; On ELF systems, a library can be loaded with the help of this:
31 ;;; http://www.copyleft.de/lisp/gcl-elf-loader.html
33 ;;; Another way is to link the library when creating a new image:
34 ;;; (compiler::link nil "new_image" "" "-lfoo")
36 ;;; As GCL's FFI is not dynamic, CFFI declarations will only work
37 ;;; after compiled and loaded.
39 ;;; *** this port is broken ***
40 ;;; gcl doesn't compile the rest of CFFI anyway..
44 (defpackage #:cffi-sys
45 (:use
#:common-lisp
#:alexandria
)
47 #:canonicalize-symbol-name-case
58 #:%foreign-type-alignment
60 #:%load-foreign-library
61 ;#:make-shareable-byte-vector
62 ;#:with-pointer-to-vector-data
66 (in-package #:cffi-sys
)
69 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
70 (pushnew :cffi
/no-foreign-funcall
*features
*))
74 (defun canonicalize-symbol-name-case (name)
75 (declare (string name
))
80 ;;; Functions and macros for allocating foreign memory on the stack
81 ;;; and on the heap. The main CFFI package defines macros that wrap
82 ;;; FOREIGN-ALLOC and FOREIGN-FREE in UNWIND-PROTECT for the common
83 ;;; usage when the memory has dynamic extent.
85 (defentry %foreign-alloc
(int) (int "malloc"))
87 ;(defun foreign-alloc (size)
88 ; "Allocate SIZE bytes on the heap and return a pointer."
89 ; (%foreign-alloc size))
91 (defentry foreign-free
(int) (void "free"))
93 ;(defun foreign-free (ptr)
94 ; "Free a PTR allocated by FOREIGN-ALLOC."
97 (defmacro with-foreign-ptr
((var size
&optional size-var
) &body body
)
98 "Bind VAR to SIZE bytes of foreign memory during BODY. The
99 pointer in VAR is invalid beyond the dynamic extent of BODY, and
100 may be stack-allocated if supported by the implementation. If
101 SIZE-VAR is supplied, it will be bound to SIZE during BODY."
103 (setf size-var
(gensym "SIZE")))
104 `(let* ((,size-var
,size
)
105 (,var
(foreign-alloc ,size-var
)))
108 (foreign-free ,var
))))
110 ;;;# Misc. Pointer Operations
112 (defun pointerp (ptr)
113 "Return true if PTR is a foreign pointer."
117 "Construct and return a null pointer."
120 (defun null-ptr-p (ptr)
121 "Return true if PTR is a null pointer."
124 (defun inc-ptr (ptr offset
)
125 "Return a pointer OFFSET bytes past PTR."
128 ;;;# Shareable Vectors
130 ;;; This interface is very experimental. WITH-POINTER-TO-VECTOR-DATA
131 ;;; should be defined to perform a copy-in/copy-out if the Lisp
132 ;;; implementation can't do this.
134 ;(defun make-shareable-byte-vector (size)
135 ; "Create a Lisp vector of SIZE bytes that can passed to
136 ;WITH-POINTER-TO-VECTOR-DATA."
137 ; (make-array size :element-type '(unsigned-byte 8)))
139 ;(defmacro with-pointer-to-vector-data ((ptr-var vector) &body body)
140 ; "Bind PTR-VAR to a foreign pointer to the data in VECTOR."
141 ; `(ccl:with-pointer-to-ivector (,ptr-var ,vector)
146 (defmacro define-mem-ref
/set
(type gcl-type
&optional c-name
)
148 (setq c-name
(substitute #\_
#\Space type
)))
149 (let ((ref-fn (concatenate 'string
"ref_" c-name
))
150 (set-fn (concatenate 'string
"set_" c-name
)))
153 (defcfun ,(format nil
"~A ~A(~A *ptr)" type ref-fn type
)
155 (defentry ,(intern (string-upcase (substitute #\-
#\_ ref-fn
)))
156 (int) (,gcl-type
,ref-fn
))
158 (defcfun ,(format nil
"void ~A(~A *ptr, ~A value)" set-fn type type
)
160 (defentry ,(intern (string-upcase (substitute #\-
#\_ set-fn
)))
161 (int ,gcl-type
) (void ,set-fn
)))))
163 (define-mem-ref/set
"char" char
)
164 (define-mem-ref/set
"unsigned char" char
)
165 (define-mem-ref/set
"short" int
)
166 (define-mem-ref/set
"unsigned short" int
)
167 (define-mem-ref/set
"int" int
)
168 (define-mem-ref/set
"unsigned int" int
)
169 (define-mem-ref/set
"long" int
)
170 (define-mem-ref/set
"unsigned long" int
)
171 (define-mem-ref/set
"float" float
)
172 (define-mem-ref/set
"double" double
)
173 (define-mem-ref/set
"void *" int
"ptr")
175 (defun %mem-ref
(ptr type
&optional
(offset 0))
176 "Dereference an object of TYPE at OFFSET bytes from PTR."
177 (unless (zerop offset
)
180 (:char
(ref-char ptr
))
181 (:unsigned-char
(ref-unsigned-char ptr
))
182 (:short
(ref-short ptr
))
183 (:unsigned-short
(ref-unsigned-short ptr
))
185 (:unsigned-int
(ref-unsigned-int ptr
))
186 (:long
(ref-long ptr
))
187 (:unsigned-long
(ref-unsigned-long ptr
))
188 (:float
(ref-float ptr
))
189 (:double
(ref-double ptr
))
190 (:pointer
(ref-ptr ptr
))))
192 (defun %mem-set
(value ptr type
&optional
(offset 0))
193 (unless (zerop offset
)
196 (:char
(set-char ptr value
))
197 (:unsigned-char
(set-unsigned-char ptr value
))
198 (:short
(set-short ptr value
))
199 (:unsigned-short
(set-unsigned-short ptr value
))
200 (:int
(set-int ptr value
))
201 (:unsigned-int
(set-unsigned-int ptr value
))
202 (:long
(set-long ptr value
))
203 (:unsigned-long
(set-unsigned-long ptr value
))
204 (:float
(set-float ptr value
))
205 (:double
(set-double ptr value
))
206 (:pointer
(set-ptr ptr value
)))
209 ;;;# Calling Foreign Functions
211 ;; TODO: figure out if these type conversions make any sense...
212 (defun convert-foreign-type (type-keyword)
213 "Convert a CFFI type keyword to a GCL type."
216 (:unsigned-char
'char
)
218 (:unsigned-short
'int
)
222 (:unsigned-long
'int
)
228 (defparameter +cffi-types
+
229 '(:char
:unsigned-char
:short
:unsigned-short
:int
:unsigned-int
230 :long
:unsigned-long
:float
:double
:pointer
))
232 (defcfun "int size_of(int type)" 0
234 case 0: return sizeof(char);
235 case 1: return sizeof(unsigned char);
236 case 2: return sizeof(short);
237 case 3: return sizeof(unsigned short);
238 case 4: return sizeof(int);
239 case 5: return sizeof(unsigned int);
240 case 6: return sizeof(long);
241 case 7: return sizeof(unsigned long);
242 case 8: return sizeof(float);
243 case 9: return sizeof(double);
244 case 10: return sizeof(void *);
248 (defentry size-of
(int) (int "size_of"))
250 ;; TODO: all this is doable inside the defcfun; figure that out..
251 (defun %foreign-type-size
(type-keyword)
252 "Return the size in bytes of a foreign type."
253 (size-of (position type-keyword
+cffi-types
+)))
255 (defcfun "int align_of(int type)" 0
257 case 0: return __alignof__(char);
258 case 1: return __alignof__(unsigned char);
259 case 2: return __alignof__(short);
260 case 3: return __alignof__(unsigned short);
261 case 4: return __alignof__(int);
262 case 5: return __alignof__(unsigned int);
263 case 6: return __alignof__(long);
264 case 7: return __alignof__(unsigned long);
265 case 8: return __alignof__(float);
266 case 9: return __alignof__(double);
267 case 10: return __alignof__(void *);
271 (defentry align-of
(int) (int "align_of"))
273 ;; TODO: like %foreign-type-size
274 (defun %foreign-type-alignment
(type-keyword)
275 "Return the alignment in bytes of a foreign type."
276 (align-of (position type-keyword
+cffi-types
+)))
279 (defun convert-external-name (name)
280 "Add an underscore to NAME if necessary for the ABI."
281 #+darwinppc-target
(concatenate 'string
"_" name
)
282 #-darwinppc-target name
)
284 (defmacro %foreign-funcall
(function-name &rest args
)
285 "Perform a foreign function all, document it more later."
286 `(format t
"~&;; Calling ~A with args ~S.~%" ,name
',args
))
288 (defun defcfun-helper-forms (name rettype args types
)
289 "Return 2 values for DEFCFUN. A prelude form and a caller form."
290 (let ((ff-name (intern (format nil
"%foreign-function/TildeA:~A" name
))))
292 `(defentry ,ff-name
,(mapcar #'convert-foreign-type types
)
293 (,(convert-foreign-type rettype
) ,name
))
294 `(,ff-name
,@args
))))
298 ;;; XXX unimplemented
299 (defmacro make-callback
(name rettype arg-names arg-types body-form
)
302 ;;;# Loading Foreign Libraries
304 (defun %load-foreign-library
(name)
305 "_Won't_ load the foreign library NAME."
306 (declare (ignore name
)))
310 ;;; XXX unimplemented
311 (defmacro foreign-var-ptr
(name)
312 "Return a pointer pointing to the foreign symbol NAME."