cffi-mcl.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP (HTM) git clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/ (DIR) Log (DIR) Files (DIR) Refs (DIR) Tags (DIR) README (DIR) LICENSE --- cffi-mcl.lisp (13656B) --- 1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*- 2 ;;; 3 ;;; cffi-mcl.lisp --- CFFI-SYS implementation for Digitool MCL. 4 ;;; 5 ;;; Copyright 2010 james.anderson@setf.de 6 ;;; Copyright 2005-2006, James Bielman <jamesjb@jamesjb.com> 7 ;;; 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: 15 ;;; 16 ;;; The above copyright notice and this permission notice shall be 17 ;;; included in all copies or substantial portions of the Software. 18 ;;; 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. 27 ;;; 28 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 33 ;;; 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;" ) 39 40 ;;;# Administrivia 41 42 (defpackage #:cffi-sys 43 (:use #:common-lisp #:ccl) 44 (:import-from #:alexandria #:once-only #:if-let) 45 (:export 46 #:canonicalize-symbol-name-case 47 #:foreign-pointer 48 #:pointerp ; ccl:pointerp 49 #:pointer-eq 50 #:%foreign-alloc 51 #:foreign-free 52 #:with-foreign-pointer 53 #:null-pointer 54 #:null-pointer-p 55 #:inc-pointer 56 #:make-pointer 57 #:pointer-address 58 #:%mem-ref 59 #:%mem-set 60 #:%foreign-funcall 61 #:%foreign-funcall-pointer 62 #:%foreign-type-alignment 63 #:%foreign-type-size 64 #:%load-foreign-library 65 #:%close-foreign-library 66 #:native-namestring 67 #:make-shareable-byte-vector 68 #:with-pointer-to-vector-data 69 #:%foreign-symbol-pointer 70 #:%defcallback 71 #:%callback)) 72 73 (in-package #:cffi-sys) 74 75 ;;;# Misfeatures 76 77 (pushnew 'flat-namespace *features*) 78 79 ;;;# Symbol Case 80 81 (defun canonicalize-symbol-name-case (name) 82 (declare (string name)) 83 (string-upcase name)) 84 85 ;;;# Allocation 86 ;;; 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. 91 92 (defun %foreign-alloc (size) 93 "Allocate SIZE bytes on the heap and return a pointer." 94 (#_newPtr size)) 95 96 (defun foreign-free (ptr) 97 "Free a PTR allocated by FOREIGN-ALLOC." 98 ;; TODO: Should we make this a dead macptr? 99 (#_disposePtr ptr)) 100 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." 106 (unless size-var 107 (setf size-var (gensym "SIZE"))) 108 `(let ((,size-var ,size)) 109 (ccl:%stack-block ((,var ,size-var)) 110 ,@body))) 111 112 ;;;# Misc. Pointer Operations 113 114 (deftype foreign-pointer () 115 'ccl:macptr) 116 117 (defun null-pointer () 118 "Construct and return a null pointer." 119 (ccl:%null-ptr)) 120 121 (defun null-pointer-p (ptr) 122 "Return true if PTR is a null pointer." 123 (ccl:%null-ptr-p ptr)) 124 125 (defun inc-pointer (ptr offset) 126 "Return a pointer OFFSET bytes past PTR." 127 (ccl:%inc-ptr ptr offset)) 128 129 (defun pointer-eq (ptr1 ptr2) 130 "Return true if PTR1 and PTR2 point to the same address." 131 (ccl:%ptr-eql ptr1 ptr2)) 132 133 (defun make-pointer (address) 134 "Return a pointer pointing to ADDRESS." 135 (ccl:%int-to-ptr address)) 136 137 (defun pointer-address (ptr) 138 "Return the address pointed to by PTR." 139 (ccl:%ptr-to-int ptr)) 140 141 ;;;# Shareable Vectors 142 ;;; 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. 146 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))) 151 152 ;;; from openmcl::macros.lisp 153 154 (defmacro with-pointer-to-vector-data ((ptr ivector) &body body) 155 "Bind PTR-VAR to a foreign pointer to the data in VECTOR." 156 (let* ((v (gensym)) 157 (l (gensym))) 158 `(let* ((,v ,ivector) 159 (,l (length ,v))) 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 165 (locally ,@body) 166 (ccl::%copy-ptr-to-ivector ,ptr 0 ,v 0 ,l))) 167 (#_disposePtr ,ptr)))))) 168 169 ;;;# Dereferencing 170 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 173 ;;; at compile-time. 174 (defmacro define-mem-accessors (&body pairs) 175 `(progn 176 (defun %mem-ref (ptr type &optional (offset 0)) 177 (ecase type 178 ,@(loop for (keyword fn) in pairs 179 collect `(,keyword (,fn ptr offset))))) 180 (defun %mem-set (value ptr type &optional (offset 0)) 181 (ecase type 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)) 186 (if (constantp type) 187 (ecase (eval type) 188 ,@(loop for (keyword fn) in pairs 189 collect `(,keyword `(,',fn ,ptr ,offset)))) 190 form)) 191 (define-compiler-macro %mem-set 192 (&whole form value ptr type &optional (offset 0)) 193 (if (constantp type) 194 (once-only (value) 195 (ecase (eval type) 196 ,@(loop for (keyword fn) in pairs 197 collect `(,keyword `(setf (,',fn ,ptr ,offset) 198 ,value))))) 199 form)))) 200 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) 214 (:pointer %get-ptr)) 215 216 217 (defun ccl::%get-unsigned-long-long (ptr offset) 218 (let ((value 0) (bit 0)) 219 (dotimes (i 8) 220 (setf (ldb (byte 8 (shiftf bit (+ bit 8))) value) 221 (ccl:%get-unsigned-byte ptr (+ offset i)))) 222 value)) 223 224 (setf (fdefinition 'ccl::%get-signed-long-long) 225 (fdefinition 'ccl::%get-unsigned-long-long)) 226 227 (defun (setf ccl::%get-unsigned-long-long) (value ptr offset) 228 (let ((bit 0)) 229 (dotimes (i 8) 230 (setf (ccl:%get-unsigned-byte ptr (+ offset i)) 231 (ldb (byte 8 (shiftf bit (+ bit 8))) value)))) 232 ptr) 233 234 (setf (fdefinition '(setf ccl::%get-signed-long-long)) 235 (fdefinition '(setf ccl::%get-unsigned-long-long))) 236 237 238 ;;;# Calling Foreign Functions 239 240 (defun convert-foreign-type (type-keyword) 241 "Convert a CFFI type keyword to a ppc-ff-call type." 242 (ecase type-keyword 243 (:char :signed-byte) 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) 255 (:pointer :address) 256 (:void :void))) 257 258 (defun ppc-ff-call-type=>mactype-name (type-keyword) 259 (ecase 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) 272 (:address :pointer) 273 (:void :void))) 274 275 276 277 (defun %foreign-type-size (type-keyword) 278 "Return the size in bytes of a foreign type." 279 (case type-keyword 280 ((:long-long :unsigned-long-long) 8) 281 (t (ccl::mactype-record-size 282 (ccl::find-mactype 283 (ppc-ff-call-type=>mactype-name (convert-foreign-type type-keyword))))))) 284 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." 289 (case type-keyword 290 ((:long-long :unsigned-long-long) 4) 291 (t (ccl::mactype-record-size 292 (ccl::find-mactype 293 (ppc-ff-call-type=>mactype-name (convert-foreign-type type-keyword))))))) 294 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) 299 if arg collect arg)) 300 301 (defun convert-external-name (name) 302 "no '_' is necessary here, the internal lookup operators handle it" 303 name) 304 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)) 308 `(ccl::ppc-ff-call 309 (ccl::macho-address ,(ccl::get-macho-entry-point (convert-external-name function-name))) 310 ,@(convert-foreign-funcall-types args))) 311 312 (defmacro %foreign-funcall-pointer (ptr args &key convention) 313 (declare (ignore convention)) 314 `(ccl::ppc-ff-call ,ptr ,@(convert-foreign-funcall-types args))) 315 316 ;;;# Callbacks 317 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)) 323 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 328 (:use)) 329 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) 336 "#") 337 (symbol-name name)) 338 '#:cffi-callbacks)) 339 340 (defmacro %defcallback (name rettype arg-names arg-types body 341 &key convention) 342 (declare (ignore convention)) 343 (let ((cb-name (intern-callback name))) 344 `(progn 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)) 349 arg-names arg-types) 350 ,(ppc-ff-call-type=>mactype-name (convert-foreign-type rettype))) 351 ,body) 352 (setf (gethash ',name *callbacks*) (symbol-value ',cb-name))))) 353 354 (defun %callback (name) 355 (or (gethash name *callbacks*) 356 (error "Undefined callback: ~S" name))) 357 358 ;;;# Loading Foreign Libraries 359 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)) 372 373 (defun %close-foreign-library (name) 374 "Close the foreign library NAME." 375 ;; for mcl do nothing 376 (declare (ignore name)) 377 nil) 378 379 (defun native-namestring (pathname) 380 (ccl::posix-namestring (ccl:full-pathname pathname))) 381 382 383 ;;;# Foreign Globals 384 385 (deftrap-inline "_findsymbol" 386 ((map :pointer) 387 (name :pointer)) 388 :pointer 389 ()) 390 391 392 (defun %foreign-symbol-pointer (name library) 393 "Returns a pointer to a foreign symbol NAME." 394 (declare (ignore library)) 395 (ccl::macho-address 396 (ccl::get-macho-entry-point (convert-external-name name))))