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;" )
40 (in-package #:cffi-sys
)
44 (pushnew 'flat-namespace
*features
*)
48 (defun canonicalize-symbol-name-case (name)
49 (declare (string name
))
54 ;;; Functions and macros for allocating foreign memory on the stack
55 ;;; and on the heap. The main CFFI package defines macros that wrap
56 ;;; FOREIGN-ALLOC and FOREIGN-FREE in UNWIND-PROTECT for the common
57 ;;; usage when the memory has dynamic extent.
59 (defun %foreign-alloc
(size)
60 "Allocate SIZE bytes on the heap and return a pointer."
63 (defun foreign-free (ptr)
64 "Free a PTR allocated by FOREIGN-ALLOC."
65 ;; TODO: Should we make this a dead macptr?
68 (defmacro with-foreign-pointer
((var size
&optional size-var
) &body body
)
69 "Bind VAR to SIZE bytes of foreign memory during BODY. The
70 pointer in VAR is invalid beyond the dynamic extent of BODY, and
71 may be stack-allocated if supported by the implementation. If
72 SIZE-VAR is supplied, it will be bound to SIZE during BODY."
74 (setf size-var
(gensym "SIZE")))
75 `(let ((,size-var
,size
))
76 (ccl:%stack-block
((,var
,size-var
))
79 ;;;# Misc. Pointer Operations
81 (deftype foreign-pointer
()
84 (defun null-pointer ()
85 "Construct and return a null pointer."
88 (defun null-pointer-p (ptr)
89 "Return true if PTR is a null pointer."
90 (ccl:%null-ptr-p ptr
))
92 (defun inc-pointer (ptr offset
)
93 "Return a pointer OFFSET bytes past PTR."
94 (ccl:%inc-ptr ptr offset
))
96 (defun pointer-eq (ptr1 ptr2
)
97 "Return true if PTR1 and PTR2 point to the same address."
98 (ccl:%ptr-eql ptr1 ptr2
))
100 (defun make-pointer (address)
101 "Return a pointer pointing to ADDRESS."
102 (ccl:%int-to-ptr address
))
104 (defun pointer-address (ptr)
105 "Return the address pointed to by PTR."
106 (ccl:%ptr-to-int ptr
))
108 ;;;# Shareable Vectors
110 ;;; This interface is very experimental. WITH-POINTER-TO-VECTOR-DATA
111 ;;; should be defined to perform a copy-in/copy-out if the Lisp
112 ;;; implementation can't do this.
114 (defun make-shareable-byte-vector (size)
115 "Create a Lisp vector of SIZE bytes that can passed to
116 WITH-POINTER-TO-VECTOR-DATA."
117 (make-array size
:element-type
'(unsigned-byte 8)))
119 ;;; from openmcl::macros.lisp
121 (defmacro with-pointer-to-vector-data
((ptr ivector
) &body body
)
122 "Bind PTR-VAR to a foreign pointer to the data in VECTOR."
125 `(let* ((,v
,ivector
)
127 (unless (typep ,v
'ccl
::ivector
) (ccl::report-bad-arg
,v
'ccl
::ivector
))
128 ;;;!!! this, unless it's possible to suppress gc
129 (let ((,ptr
(#_newPtr
,l
)))
130 (unwind-protect (progn (ccl::%copy-ivector-to-ptr
,v
0 ,ptr
0 ,l
)
131 (mutliple-value-prog1
133 (ccl::%copy-ptr-to-ivector
,ptr
0 ,v
0 ,l
)))
134 (#_disposePtr
,ptr
))))))
138 ;;; Define the %MEM-REF and %MEM-SET functions, as well as compiler
139 ;;; macros that optimize the case where the type keyword is constant
141 (defmacro define-mem-accessors
(&body pairs
)
143 (defun %mem-ref
(ptr type
&optional
(offset 0))
145 ,@(loop for
(keyword fn
) in pairs
146 collect
`(,keyword
(,fn ptr offset
)))))
147 (defun %mem-set
(value ptr type
&optional
(offset 0))
149 ,@(loop for
(keyword fn
) in pairs
150 collect
`(,keyword
(setf (,fn ptr offset
) value
)))))
151 (define-compiler-macro %mem-ref
152 (&whole form ptr type
&optional
(offset 0))
155 ,@(loop for
(keyword fn
) in pairs
156 collect
`(,keyword
`(,',fn
,ptr
,offset
))))
158 (define-compiler-macro %mem-set
159 (&whole form value ptr type
&optional
(offset 0))
163 ,@(loop for
(keyword fn
) in pairs
164 collect
`(,keyword
`(setf (,',fn
,ptr
,offset
)
168 (define-mem-accessors
169 (:char %get-signed-byte
)
170 (:unsigned-char %get-unsigned-byte
)
171 (:short %get-signed-word
)
172 (:unsigned-short %get-unsigned-word
)
173 (:int %get-signed-long
)
174 (:unsigned-int %get-unsigned-long
)
175 (:long %get-signed-long
)
176 (:unsigned-long %get-unsigned-long
)
177 (:long-long ccl
::%get-signed-long-long
)
178 (:unsigned-long-long ccl
::%get-unsigned-long-long
)
179 (:float %get-single-float
)
180 (:double %get-double-float
)
184 (defun ccl::%get-unsigned-long-long
(ptr offset
)
185 (let ((value 0) (bit 0))
187 (setf (ldb (byte 8 (shiftf bit
(+ bit
8))) value
)
188 (ccl:%get-unsigned-byte ptr
(+ offset i
))))
191 (setf (fdefinition 'ccl
::%get-signed-long-long
)
192 (fdefinition 'ccl
::%get-unsigned-long-long
))
194 (defun (setf ccl
::%get-unsigned-long-long
) (value ptr offset
)
197 (setf (ccl:%get-unsigned-byte ptr
(+ offset i
))
198 (ldb (byte 8 (shiftf bit
(+ bit
8))) value
))))
201 (setf (fdefinition '(setf ccl
::%get-signed-long-long
))
202 (fdefinition '(setf ccl
::%get-unsigned-long-long
)))
205 ;;;# Calling Foreign Functions
207 (defun convert-foreign-type (type-keyword)
208 "Convert a CFFI type keyword to a ppc-ff-call type."
211 (:unsigned-char
:unsigned-byte
)
212 (:short
:signed-short
)
213 (:unsigned-short
:unsigned-short
)
214 (:int
:signed-fullword
)
215 (:unsigned-int
:unsigned-fullword
)
216 (:long
:signed-fullword
)
217 (:unsigned-long
:unsigned-fullword
)
218 (:long-long
:signed-doubleword
)
219 (:unsigned-long-long
:unsigned-doubleword
)
220 (:float
:single-float
)
221 (:double
:double-float
)
225 (defun ppc-ff-call-type=>mactype-name
(type-keyword)
227 (:signed-byte
:sint8
)
228 (:unsigned-byte
:uint8
)
229 (:signed-short
:sint16
)
230 (:unsigned-short
:uint16
)
231 (:signed-halfword
:sint16
)
232 (:unsigned-halfword
:uint16
)
233 (:signed-fullword
:sint32
)
234 (:unsigned-fullword
:uint32
)
235 ;(:signed-doubleword :long-long)
236 ;(:unsigned-doubleword :unsigned-long-long)
237 (:single-float
:single-float
)
238 (:double-float
:double-float
)
244 (defun %foreign-type-size
(type-keyword)
245 "Return the size in bytes of a foreign type."
247 ((:long-long
:unsigned-long-long
) 8)
248 (t (ccl::mactype-record-size
250 (ppc-ff-call-type=>mactype-name
(convert-foreign-type type-keyword
)))))))
252 ;; There be dragons here. See the following thread for details:
253 ;; http://clozure.com/pipermail/openmcl-devel/2005-June/002777.html
254 (defun %foreign-type-alignment
(type-keyword)
255 "Return the alignment in bytes of a foreign type."
257 ((:long-long
:unsigned-long-long
) 4)
258 (t (ccl::mactype-record-size
260 (ppc-ff-call-type=>mactype-name
(convert-foreign-type type-keyword
)))))))
262 (defun convert-foreign-funcall-types (args)
263 "Convert foreign types for a call to FOREIGN-FUNCALL."
264 (loop for
(type arg
) on args by
#'cddr
265 collect
(convert-foreign-type type
)
268 (defun convert-external-name (name)
269 "no '_' is necessary here, the internal lookup operators handle it"
272 (defmacro %foreign-funcall
(function-name args
&key library convention
)
273 "Perform a foreign function call, document it more later."
274 (declare (ignore library convention
))
276 (ccl::macho-address
,(ccl::get-macho-entry-point
(convert-external-name function-name
)))
277 ,@(convert-foreign-funcall-types args
)))
279 (defmacro %foreign-funcall-pointer
(ptr args
&key convention
)
280 (declare (ignore convention
))
281 `(ccl::ppc-ff-call
,ptr
,@(convert-foreign-funcall-types args
)))
285 ;;; The *CALLBACKS* hash table maps CFFI callback names to OpenMCL "macptr"
286 ;;; entry points. It is safe to store the pointers directly because
287 ;;; OpenMCL will update the address of these pointers when a saved image
288 ;;; is loaded (see CCL::RESTORE-PASCAL-FUNCTIONS).
289 (defvar *callbacks
* (make-hash-table))
291 ;;; Intern a symbol in the CFFI-CALLBACKS package used to name the internal
292 ;;; callback for NAME.
293 (defun intern-callback (name)
294 (intern (format nil
"~A::~A"
295 (if-let (package (symbol-package name
))
296 (package-name package
)
301 (defmacro %defcallback
(name rettype arg-names arg-types body
303 (declare (ignore convention
))
304 (let ((cb-name (intern-callback name
)))
306 (ccl::ppc-defpascal
,cb-name
307 (;; ? ,@(when (eq convention :stdcall) '(:discard-stack-args))
308 ,@(mapcan (lambda (sym type
)
309 (list (ppc-ff-call-type=>mactype-name
(convert-foreign-type type
)) sym
))
311 ,(ppc-ff-call-type=>mactype-name
(convert-foreign-type rettype
)))
313 (setf (gethash ',name
*callbacks
*) (symbol-value ',cb-name
)))))
315 (defun %callback
(name)
316 (or (gethash name
*callbacks
*)
317 (error "Undefined callback: ~S" name
)))
319 ;;;# Loading Foreign Libraries
321 (defun %load-foreign-library
(name path
)
322 "Load the foreign library NAME."
323 (declare (ignore path
))
324 (setf name
(string name
))
325 ;; for mcl emulate this wrt frameworks
326 (unless (and (> (length name
) 10)
327 (string-equal name
".framework" :start1
(- (length name
) 10)))
328 (setf name
(concatenate 'string name
".framework")))
329 ;; if the framework was not registered, add it
330 (unless (gethash name ccl
::*framework-descriptors
*)
331 (ccl::add-framework-bundle name
:pathname
"ccl:frameworks;" ))
332 (ccl::load-framework-bundle name
))
334 (defun %close-foreign-library
(name)
335 "Close the foreign library NAME."
336 ;; for mcl do nothing
337 (declare (ignore name
))
340 (defun native-namestring (pathname)
341 (ccl::posix-namestring
(ccl:full-pathname pathname
)))
346 (deftrap-inline "_findsymbol"
353 (defun %foreign-symbol-pointer
(name library
)
354 "Returns a pointer to a foreign symbol NAME."
355 (declare (ignore library
))
357 (ccl::get-macho-entry-point
(convert-external-name name
))))