1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
3 ;;; cffi-mcl.lisp --- CFFI-SYS implementation for Digitool MCL.
5 ;;; Copyright 2010 james.anderson@setf.de
6 ;;; Copyright 2005-2006, James Bielman <jamesjb@jamesjb.com>
8 ;;; Permission is hereby granted, free of charge, to any person
9 ;;; obtaining a copy of this software and associated documentation
10 ;;; files (the "Software"), to deal in the Software without
11 ;;; restriction, including without limitation the rights to use, copy,
12 ;;; modify, merge, publish, distribute, sublicense, and/or sell copies
13 ;;; of the Software, and to permit persons to whom the Software is
14 ;;; furnished to do so, subject to the following conditions:
16 ;;; The above copyright notice and this permission notice shall be
17 ;;; included in all copies or substantial portions of the Software.
19 ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 ;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23 ;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 ;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 ;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 ;;; DEALINGS IN THE SOFTWARE.
29 ;;; this is a stop-gap emulation. (at least) three things are not right
30 ;;; - integer vector arguments are copied
31 ;;; - return values are not typed
32 ;;; - a shared library must be packaged as a framework and statically loaded
34 ;;; on the topic of shared libraries, see
35 ;;; http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/MachOTopics/1-Articles/loading_code.html
36 ;;; which describes how to package a shared library as a framework.
37 ;;; once a framework exists, load it as, eg.
38 ;;; (ccl::add-framework-bundle "fftw.framework" :pathname "ccl:frameworks;" )
42 (defpackage #:cffi-sys
43 (:use
#:common-lisp
#:ccl
)
44 (:import-from
#:alexandria
#:once-only
#:if-let
)
46 #:canonicalize-symbol-name-case
48 #:pointerp
; ccl:pointerp
52 #:with-foreign-pointer
61 #:%foreign-funcall-pointer
62 #:%foreign-type-alignment
64 #:%load-foreign-library
65 #:%close-foreign-library
67 #:make-shareable-byte-vector
68 #:with-pointer-to-vector-data
69 #:%foreign-symbol-pointer
73 (in-package #:cffi-sys
)
77 (pushnew 'flat-namespace
*features
*)
81 (defun canonicalize-symbol-name-case (name)
82 (declare (string name
))
87 ;;; Functions and macros for allocating foreign memory on the stack
88 ;;; and on the heap. The main CFFI package defines macros that wrap
89 ;;; FOREIGN-ALLOC and FOREIGN-FREE in UNWIND-PROTECT for the common
90 ;;; usage when the memory has dynamic extent.
92 (defun %foreign-alloc
(size)
93 "Allocate SIZE bytes on the heap and return a pointer."
96 (defun foreign-free (ptr)
97 "Free a PTR allocated by FOREIGN-ALLOC."
98 ;; TODO: Should we make this a dead macptr?
101 (defmacro with-foreign-pointer
((var size
&optional size-var
) &body body
)
102 "Bind VAR to SIZE bytes of foreign memory during BODY. The
103 pointer in VAR is invalid beyond the dynamic extent of BODY, and
104 may be stack-allocated if supported by the implementation. If
105 SIZE-VAR is supplied, it will be bound to SIZE during BODY."
107 (setf size-var
(gensym "SIZE")))
108 `(let ((,size-var
,size
))
109 (ccl:%stack-block
((,var
,size-var
))
112 ;;;# Misc. Pointer Operations
114 (deftype foreign-pointer
()
117 (defun null-pointer ()
118 "Construct and return a null pointer."
121 (defun null-pointer-p (ptr)
122 "Return true if PTR is a null pointer."
123 (ccl:%null-ptr-p ptr
))
125 (defun inc-pointer (ptr offset
)
126 "Return a pointer OFFSET bytes past PTR."
127 (ccl:%inc-ptr ptr offset
))
129 (defun pointer-eq (ptr1 ptr2
)
130 "Return true if PTR1 and PTR2 point to the same address."
131 (ccl:%ptr-eql ptr1 ptr2
))
133 (defun make-pointer (address)
134 "Return a pointer pointing to ADDRESS."
135 (ccl:%int-to-ptr address
))
137 (defun pointer-address (ptr)
138 "Return the address pointed to by PTR."
139 (ccl:%ptr-to-int ptr
))
141 ;;;# Shareable Vectors
143 ;;; This interface is very experimental. WITH-POINTER-TO-VECTOR-DATA
144 ;;; should be defined to perform a copy-in/copy-out if the Lisp
145 ;;; implementation can't do this.
147 (defun make-shareable-byte-vector (size)
148 "Create a Lisp vector of SIZE bytes that can passed to
149 WITH-POINTER-TO-VECTOR-DATA."
150 (make-array size
:element-type
'(unsigned-byte 8)))
152 ;;; from openmcl::macros.lisp
154 (defmacro with-pointer-to-vector-data
((ptr ivector
) &body body
)
155 "Bind PTR-VAR to a foreign pointer to the data in VECTOR."
158 `(let* ((,v
,ivector
)
160 (unless (typep ,v
'ccl
::ivector
) (ccl::report-bad-arg
,v
'ccl
::ivector
))
161 ;;;!!! this, unless it's possible to suppress gc
162 (let ((,ptr
(#_newPtr
,l
)))
163 (unwind-protect (progn (ccl::%copy-ivector-to-ptr
,v
0 ,ptr
0 ,l
)
164 (mutliple-value-prog1
166 (ccl::%copy-ptr-to-ivector
,ptr
0 ,v
0 ,l
)))
167 (#_disposePtr
,ptr
))))))
171 ;;; Define the %MEM-REF and %MEM-SET functions, as well as compiler
172 ;;; macros that optimize the case where the type keyword is constant
174 (defmacro define-mem-accessors
(&body pairs
)
176 (defun %mem-ref
(ptr type
&optional
(offset 0))
178 ,@(loop for
(keyword fn
) in pairs
179 collect
`(,keyword
(,fn ptr offset
)))))
180 (defun %mem-set
(value ptr type
&optional
(offset 0))
182 ,@(loop for
(keyword fn
) in pairs
183 collect
`(,keyword
(setf (,fn ptr offset
) value
)))))
184 (define-compiler-macro %mem-ref
185 (&whole form ptr type
&optional
(offset 0))
188 ,@(loop for
(keyword fn
) in pairs
189 collect
`(,keyword
`(,',fn
,ptr
,offset
))))
191 (define-compiler-macro %mem-set
192 (&whole form value ptr type
&optional
(offset 0))
196 ,@(loop for
(keyword fn
) in pairs
197 collect
`(,keyword
`(setf (,',fn
,ptr
,offset
)
201 (define-mem-accessors
202 (:char %get-signed-byte
)
203 (:unsigned-char %get-unsigned-byte
)
204 (:short %get-signed-word
)
205 (:unsigned-short %get-unsigned-word
)
206 (:int %get-signed-long
)
207 (:unsigned-int %get-unsigned-long
)
208 (:long %get-signed-long
)
209 (:unsigned-long %get-unsigned-long
)
210 (:long-long ccl
::%get-signed-long-long
)
211 (:unsigned-long-long ccl
::%get-unsigned-long-long
)
212 (:float %get-single-float
)
213 (:double %get-double-float
)
217 (defun ccl::%get-unsigned-long-long
(ptr offset
)
218 (let ((value 0) (bit 0))
220 (setf (ldb (byte 8 (shiftf bit
(+ bit
8))) value
)
221 (ccl:%get-unsigned-byte ptr
(+ offset i
))))
224 (setf (fdefinition 'ccl
::%get-signed-long-long
)
225 (fdefinition 'ccl
::%get-unsigned-long-long
))
227 (defun (setf ccl
::%get-unsigned-long-long
) (value ptr offset
)
230 (setf (ccl:%get-unsigned-byte ptr
(+ offset i
))
231 (ldb (byte 8 (shiftf bit
(+ bit
8))) value
))))
234 (setf (fdefinition '(setf ccl
::%get-signed-long-long
))
235 (fdefinition '(setf ccl
::%get-unsigned-long-long
)))
238 ;;;# Calling Foreign Functions
240 (defun convert-foreign-type (type-keyword)
241 "Convert a CFFI type keyword to a ppc-ff-call type."
244 (:unsigned-char
:unsigned-byte
)
245 (:short
:signed-short
)
246 (:unsigned-short
:unsigned-short
)
247 (:int
:signed-fullword
)
248 (:unsigned-int
:unsigned-fullword
)
249 (:long
:signed-fullword
)
250 (:unsigned-long
:unsigned-fullword
)
251 (:long-long
:signed-doubleword
)
252 (:unsigned-long-long
:unsigned-doubleword
)
253 (:float
:single-float
)
254 (:double
:double-float
)
258 (defun ppc-ff-call-type=>mactype-name
(type-keyword)
260 (:signed-byte
:sint8
)
261 (:unsigned-byte
:uint8
)
262 (:signed-short
:sint16
)
263 (:unsigned-short
:uint16
)
264 (:signed-halfword
:sint16
)
265 (:unsigned-halfword
:uint16
)
266 (:signed-fullword
:sint32
)
267 (:unsigned-fullword
:uint32
)
268 ;(:signed-doubleword :long-long)
269 ;(:unsigned-doubleword :unsigned-long-long)
270 (:single-float
:single-float
)
271 (:double-float
:double-float
)
277 (defun %foreign-type-size
(type-keyword)
278 "Return the size in bytes of a foreign type."
280 ((:long-long
:unsigned-long-long
) 8)
281 (t (ccl::mactype-record-size
283 (ppc-ff-call-type=>mactype-name
(convert-foreign-type type-keyword
)))))))
285 ;; There be dragons here. See the following thread for details:
286 ;; http://clozure.com/pipermail/openmcl-devel/2005-June/002777.html
287 (defun %foreign-type-alignment
(type-keyword)
288 "Return the alignment in bytes of a foreign type."
290 ((:long-long
:unsigned-long-long
) 4)
291 (t (ccl::mactype-record-size
293 (ppc-ff-call-type=>mactype-name
(convert-foreign-type type-keyword
)))))))
295 (defun convert-foreign-funcall-types (args)
296 "Convert foreign types for a call to FOREIGN-FUNCALL."
297 (loop for
(type arg
) on args by
#'cddr
298 collect
(convert-foreign-type type
)
301 (defun convert-external-name (name)
302 "no '_' is necessary here, the internal lookup operators handle it"
305 (defmacro %foreign-funcall
(function-name args
&key library convention
)
306 "Perform a foreign function call, document it more later."
307 (declare (ignore library convention
))
309 (ccl::macho-address
,(ccl::get-macho-entry-point
(convert-external-name function-name
)))
310 ,@(convert-foreign-funcall-types args
)))
312 (defmacro %foreign-funcall-pointer
(ptr args
&key convention
)
313 (declare (ignore convention
))
314 `(ccl::ppc-ff-call
,ptr
,@(convert-foreign-funcall-types args
)))
318 ;;; The *CALLBACKS* hash table maps CFFI callback names to OpenMCL "macptr"
319 ;;; entry points. It is safe to store the pointers directly because
320 ;;; OpenMCL will update the address of these pointers when a saved image
321 ;;; is loaded (see CCL::RESTORE-PASCAL-FUNCTIONS).
322 (defvar *callbacks
* (make-hash-table))
324 ;;; Create a package to contain the symbols for callback functions. We
325 ;;; want to redefine callbacks with the same symbol so the internal data
326 ;;; structures are reused.
327 (defpackage #:cffi-callbacks
330 ;;; Intern a symbol in the CFFI-CALLBACKS package used to name the internal
331 ;;; callback for NAME.
332 (defun intern-callback (name)
333 (intern (format nil
"~A::~A"
334 (if-let (package (symbol-package name
))
335 (package-name package
)
340 (defmacro %defcallback
(name rettype arg-names arg-types body
342 (declare (ignore convention
))
343 (let ((cb-name (intern-callback name
)))
345 (ccl::ppc-defpascal
,cb-name
346 (;; ? ,@(when (eq convention :stdcall) '(:discard-stack-args))
347 ,@(mapcan (lambda (sym type
)
348 (list (ppc-ff-call-type=>mactype-name
(convert-foreign-type type
)) sym
))
350 ,(ppc-ff-call-type=>mactype-name
(convert-foreign-type rettype
)))
352 (setf (gethash ',name
*callbacks
*) (symbol-value ',cb-name
)))))
354 (defun %callback
(name)
355 (or (gethash name
*callbacks
*)
356 (error "Undefined callback: ~S" name
)))
358 ;;;# Loading Foreign Libraries
360 (defun %load-foreign-library
(name path
)
361 "Load the foreign library NAME."
362 (declare (ignore path
))
363 (setf name
(string name
))
364 ;; for mcl emulate this wrt frameworks
365 (unless (and (> (length name
) 10)
366 (string-equal name
".framework" :start1
(- (length name
) 10)))
367 (setf name
(concatenate 'string name
".framework")))
368 ;; if the framework was not registered, add it
369 (unless (gethash name ccl
::*framework-descriptors
*)
370 (ccl::add-framework-bundle name
:pathname
"ccl:frameworks;" ))
371 (ccl::load-framework-bundle name
))
373 (defun %close-foreign-library
(name)
374 "Close the foreign library NAME."
375 ;; for mcl do nothing
376 (declare (ignore name
))
379 (defun native-namestring (pathname)
380 (ccl::posix-namestring
(ccl:full-pathname pathname
)))
385 (deftrap-inline "_findsymbol"
392 (defun %foreign-symbol-pointer
(name library
)
393 "Returns a pointer to a foreign symbol NAME."
394 (declare (ignore library
))
396 (ccl::get-macho-entry-point
(convert-external-name name
))))