cffi-sbcl.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-sbcl.lisp (14835B)
       ---
            1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
            2 ;;;
            3 ;;; cffi-sbcl.lisp --- CFFI-SYS implementation for SBCL.
            4 ;;;
            5 ;;; Copyright (C) 2005-2006, James Bielman  <jamesjb@jamesjb.com>
            6 ;;;
            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:
           14 ;;;
           15 ;;; The above copyright notice and this permission notice shall be
           16 ;;; included in all copies or substantial portions of the Software.
           17 ;;;
           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.
           26 ;;;
           27 
           28 ;;;# Administrivia
           29 
           30 (defpackage #:cffi-sys
           31   (:use #:common-lisp #:sb-alien)
           32   (:import-from #:alexandria
           33                 #:once-only #:with-unique-names #:when-let #:removef)
           34   (:export
           35    #:canonicalize-symbol-name-case
           36    #:foreign-pointer
           37    #:pointerp
           38    #:pointer-eq
           39    #:null-pointer
           40    #:null-pointer-p
           41    #:inc-pointer
           42    #:make-pointer
           43    #:pointer-address
           44    #:%foreign-alloc
           45    #:foreign-free
           46    #:with-foreign-pointer
           47    #:%foreign-funcall
           48    #:%foreign-funcall-pointer
           49    #:%foreign-type-alignment
           50    #:%foreign-type-size
           51    #:%load-foreign-library
           52    #:%close-foreign-library
           53    #:native-namestring
           54    #:%mem-ref
           55    #:%mem-set
           56    #:make-shareable-byte-vector
           57    #:with-pointer-to-vector-data
           58    #:%foreign-symbol-pointer
           59    #:%defcallback
           60    #:%callback))
           61 
           62 (in-package #:cffi-sys)
           63 
           64 ;;;# Misfeatures
           65 
           66 (pushnew 'flat-namespace *features*)
           67 
           68 ;;;# Symbol Case
           69 
           70 (declaim (inline canonicalize-symbol-name-case))
           71 (defun canonicalize-symbol-name-case (name)
           72   (declare (string name))
           73   (string-upcase name))
           74 
           75 ;;;# Basic Pointer Operations
           76 
           77 (deftype foreign-pointer ()
           78   'sb-sys:system-area-pointer)
           79 
           80 (declaim (inline pointerp))
           81 (defun pointerp (ptr)
           82   "Return true if PTR is a foreign pointer."
           83   (sb-sys:system-area-pointer-p ptr))
           84 
           85 (declaim (inline pointer-eq))
           86 (defun pointer-eq (ptr1 ptr2)
           87   "Return true if PTR1 and PTR2 point to the same address."
           88   (declare (type system-area-pointer ptr1 ptr2))
           89   (sb-sys:sap= ptr1 ptr2))
           90 
           91 (declaim (inline null-pointer))
           92 (defun null-pointer ()
           93   "Construct and return a null pointer."
           94   (sb-sys:int-sap 0))
           95 
           96 (declaim (inline null-pointer-p))
           97 (defun null-pointer-p (ptr)
           98   "Return true if PTR is a null pointer."
           99   (declare (type system-area-pointer ptr))
          100   (zerop (sb-sys:sap-int ptr)))
          101 
          102 (declaim (inline inc-pointer))
          103 (defun inc-pointer (ptr offset)
          104   "Return a pointer pointing OFFSET bytes past PTR."
          105   (declare (type system-area-pointer ptr)
          106            (type integer offset))
          107   (sb-sys:sap+ ptr offset))
          108 
          109 (declaim (inline make-pointer))
          110 (defun make-pointer (address)
          111   "Return a pointer pointing to ADDRESS."
          112   ;; (declare (type (unsigned-byte 32) address))
          113   (sb-sys:int-sap address))
          114 
          115 (declaim (inline pointer-address))
          116 (defun pointer-address (ptr)
          117   "Return the address pointed to by PTR."
          118   (declare (type system-area-pointer ptr))
          119   (sb-sys:sap-int ptr))
          120 
          121 ;;;# Allocation
          122 ;;;
          123 ;;; Functions and macros for allocating foreign memory on the stack
          124 ;;; and on the heap.  The main CFFI package defines macros that wrap
          125 ;;; FOREIGN-ALLOC and FOREIGN-FREE in UNWIND-PROTECT for the common usage
          126 ;;; when the memory has dynamic extent.
          127 
          128 (declaim (inline %foreign-alloc))
          129 (defun %foreign-alloc (size)
          130   "Allocate SIZE bytes on the heap and return a pointer."
          131   ;; (declare (type (unsigned-byte 32) size))
          132   (alien-sap (make-alien (unsigned 8) size)))
          133 
          134 (declaim (inline foreign-free))
          135 (defun foreign-free (ptr)
          136   "Free a PTR allocated by FOREIGN-ALLOC."
          137   (declare (type system-area-pointer ptr)
          138            (optimize speed))
          139   (free-alien (sap-alien ptr (* (unsigned 8)))))
          140 
          141 (defmacro with-foreign-pointer ((var size &optional size-var) &body body)
          142   "Bind VAR to SIZE bytes of foreign memory during BODY.  The
          143 pointer in VAR is invalid beyond the dynamic extent of BODY, and
          144 may be stack-allocated if supported by the implementation.  If
          145 SIZE-VAR is supplied, it will be bound to SIZE during BODY."
          146   (unless size-var
          147     (setf size-var (gensym "SIZE")))
          148   ;; If the size is constant we can stack-allocate.
          149   (if (constantp size)
          150       (let ((alien-var (gensym "ALIEN")))
          151         `(with-alien ((,alien-var (array (unsigned 8) ,(eval size))))
          152            (let ((,size-var ,(eval size))
          153                  (,var (alien-sap ,alien-var)))
          154              (declare (ignorable ,size-var))
          155              ,@body)))
          156       `(let* ((,size-var ,size)
          157               (,var (%foreign-alloc ,size-var)))
          158          (unwind-protect
          159               (progn ,@body)
          160            (foreign-free ,var)))))
          161 
          162 ;;;# Shareable Vectors
          163 ;;;
          164 ;;; This interface is very experimental.  WITH-POINTER-TO-VECTOR-DATA
          165 ;;; should be defined to perform a copy-in/copy-out if the Lisp
          166 ;;; implementation can't do this.
          167 
          168 (declaim (inline make-shareable-byte-vector))
          169 (defun make-shareable-byte-vector (size)
          170   "Create a Lisp vector of SIZE bytes that can be passed to
          171 WITH-POINTER-TO-VECTOR-DATA."
          172   ; (declare (type sb-int:index size))
          173   (make-array size :element-type '(unsigned-byte 8)))
          174 
          175 (defmacro with-pointer-to-vector-data ((ptr-var vector) &body body)
          176   "Bind PTR-VAR to a foreign pointer to the data in VECTOR."
          177   (let ((vector-var (gensym "VECTOR")))
          178     `(let ((,vector-var ,vector))
          179        (declare (type (sb-kernel:simple-unboxed-array (*)) ,vector-var))
          180        (sb-sys:with-pinned-objects (,vector-var)
          181          (let ((,ptr-var (sb-sys:vector-sap ,vector-var)))
          182            ,@body)))))
          183 
          184 ;;;# Dereferencing
          185 
          186 ;;; Define the %MEM-REF and %MEM-SET functions, as well as compiler
          187 ;;; macros that optimize the case where the type keyword is constant
          188 ;;; at compile-time.
          189 (defmacro define-mem-accessors (&body pairs)
          190   `(progn
          191      (defun %mem-ref (ptr type &optional (offset 0))
          192        (ecase type
          193          ,@(loop for (keyword fn) in pairs
          194                  collect `(,keyword (,fn ptr offset)))))
          195      (defun %mem-set (value ptr type &optional (offset 0))
          196        (ecase type
          197          ,@(loop for (keyword fn) in pairs
          198                  collect `(,keyword (setf (,fn ptr offset) value)))))
          199      (define-compiler-macro %mem-ref
          200          (&whole form ptr type &optional (offset 0))
          201        (if (constantp type)
          202            (ecase (eval type)
          203              ,@(loop for (keyword fn) in pairs
          204                      collect `(,keyword `(,',fn ,ptr ,offset))))
          205            form))
          206      (define-compiler-macro %mem-set
          207          (&whole form value ptr type &optional (offset 0))
          208        (if (constantp type)
          209            (once-only (value)
          210              (ecase (eval type)
          211                ,@(loop for (keyword fn) in pairs
          212                        collect `(,keyword `(setf (,',fn ,ptr ,offset)
          213                                                  ,value)))))
          214            form))))
          215 
          216 ;;; Look up alien type information and build both define-mem-accessors form
          217 ;;; and convert-foreign-type function definition.
          218 (defmacro define-type-mapping (accessor-table alien-table)
          219   (let* ((accessible-types
          220            (remove 'void alien-table :key #'second))
          221          (size-and-signedp-forms
          222            (mapcar (lambda (name)
          223                      (list (eval `(alien-size ,(second name)))
          224                            (typep -1 `(alien ,(second name)))))
          225                    accessible-types)))
          226     `(progn
          227        (define-mem-accessors
          228          ,@(loop for (cffi-keyword alien-type fixed-accessor)
          229                    in accessible-types
          230                  and (alien-size signedp)
          231                    in size-and-signedp-forms
          232                  for (signed-ref unsigned-ref)
          233                    = (cdr (assoc alien-size accessor-table))
          234                  collect
          235                  `(,cffi-keyword
          236                    ,(or fixed-accessor
          237                         (if signedp signed-ref unsigned-ref)
          238                         (error "No accessor found for ~S"
          239                                alien-type)))))
          240        (defun convert-foreign-type (type-keyword)
          241          (ecase type-keyword
          242            ,@(loop for (cffi-keyword alien-type) in alien-table
          243                    collect `(,cffi-keyword (quote ,alien-type))))))))
          244 
          245 (define-type-mapping
          246     ((8  sb-sys:signed-sap-ref-8  sb-sys:sap-ref-8)
          247      (16 sb-sys:signed-sap-ref-16 sb-sys:sap-ref-16)
          248      (32 sb-sys:signed-sap-ref-32 sb-sys:sap-ref-32)
          249      (64 sb-sys:signed-sap-ref-64 sb-sys:sap-ref-64))
          250     ((:char               char)
          251      (:unsigned-char      unsigned-char)
          252      (:short              short)
          253      (:unsigned-short     unsigned-short)
          254      (:int                int)
          255      (:unsigned-int       unsigned-int)
          256      (:long               long)
          257      (:unsigned-long      unsigned-long)
          258      (:long-long          long-long)
          259      (:unsigned-long-long unsigned-long-long)
          260      (:float              single-float
          261                           sb-sys:sap-ref-single)
          262      (:double             double-float
          263                           sb-sys:sap-ref-double)
          264      (:pointer            system-area-pointer
          265                           sb-sys:sap-ref-sap)
          266      (:void               void)))
          267 
          268 ;;;# Calling Foreign Functions
          269 
          270 (defun %foreign-type-size (type-keyword)
          271   "Return the size in bytes of a foreign type."
          272   (/ (sb-alien-internals:alien-type-bits
          273       (sb-alien-internals:parse-alien-type
          274        (convert-foreign-type type-keyword) nil)) 8))
          275 
          276 (defun %foreign-type-alignment (type-keyword)
          277   "Return the alignment in bytes of a foreign type."
          278   #+(and darwin ppc (not ppc64))
          279   (case type-keyword
          280     ((:double :long-long :unsigned-long-long)
          281      (return-from %foreign-type-alignment 8)))
          282   ;; No override necessary for other types...
          283   (/ (sb-alien-internals:alien-type-alignment
          284       (sb-alien-internals:parse-alien-type
          285        (convert-foreign-type type-keyword) nil)) 8))
          286 
          287 (defun foreign-funcall-type-and-args (args)
          288   "Return an SB-ALIEN function type for ARGS."
          289   (let ((return-type 'void))
          290     (loop for (type arg) on args by #'cddr
          291           if arg collect (convert-foreign-type type) into types
          292           and collect arg into fargs
          293           else do (setf return-type (convert-foreign-type type))
          294           finally (return (values types fargs return-type)))))
          295 
          296 (defmacro %%foreign-funcall (name types fargs rettype)
          297   "Internal guts of %FOREIGN-FUNCALL."
          298   `(alien-funcall
          299     (extern-alien ,name (function ,rettype ,@types))
          300     ,@fargs))
          301 
          302 (defmacro %foreign-funcall (name args &key library convention)
          303   "Perform a foreign function call, document it more later."
          304   (declare (ignore library convention))
          305   (multiple-value-bind (types fargs rettype)
          306       (foreign-funcall-type-and-args args)
          307     `(%%foreign-funcall ,name ,types ,fargs ,rettype)))
          308 
          309 (defmacro %foreign-funcall-pointer (ptr args &key convention)
          310   "Funcall a pointer to a foreign function."
          311   (declare (ignore convention))
          312   (multiple-value-bind (types fargs rettype)
          313       (foreign-funcall-type-and-args args)
          314     (with-unique-names (function)
          315       `(with-alien ((,function (* (function ,rettype ,@types)) ,ptr))
          316          (alien-funcall ,function ,@fargs)))))
          317 
          318 ;;;# Callbacks
          319 
          320 ;;; The *CALLBACKS* hash table contains a direct mapping of CFFI
          321 ;;; callback names to SYSTEM-AREA-POINTERs obtained by ALIEN-LAMBDA.
          322 ;;; SBCL will maintain the addresses of the callbacks across saved
          323 ;;; images, so it is safe to store the pointers directly.
          324 (defvar *callbacks* (make-hash-table))
          325 
          326 (defmacro %defcallback (name rettype arg-names arg-types body
          327                         &key convention)
          328   (check-type convention (member :stdcall :cdecl))
          329   `(setf (gethash ',name *callbacks*)
          330          (alien-sap
          331           (sb-alien::alien-lambda
          332             #+alien-callback-conventions
          333             (,convention ,(convert-foreign-type rettype))
          334             #-alien-callback-conventions
          335             ,(convert-foreign-type rettype)
          336             ,(mapcar (lambda (sym type)
          337                        (list sym (convert-foreign-type type)))
          338                arg-names arg-types)
          339             ,body))))
          340 
          341 (defun %callback (name)
          342   (or (gethash name *callbacks*)
          343       (error "Undefined callback: ~S" name)))
          344 
          345 ;;;# Loading and Closing Foreign Libraries
          346 
          347 #+darwin
          348 (defun call-within-initial-thread (fn &rest args)
          349   (let (result
          350         error
          351         (sem (sb-thread:make-semaphore)))
          352     (sb-thread:interrupt-thread
          353      ;; KLUDGE: find a better way to get the initial thread.
          354      (car (last (sb-thread:list-all-threads)))
          355      (lambda ()
          356        (multiple-value-setq (result error)
          357          (ignore-errors (apply fn args)))
          358        (sb-thread:signal-semaphore sem)))
          359     (sb-thread:wait-on-semaphore sem)
          360     (if error
          361         (signal error)
          362         result)))
          363 
          364 (declaim (inline %load-foreign-library))
          365 (defun %load-foreign-library (name path)
          366   "Load a foreign library."
          367   (declare (ignore name))
          368   ;; As of MacOS X 10.6.6, loading things like CoreFoundation from a
          369   ;; thread other than the initial one results in a crash.
          370   #+(and darwin sb-thread) (call-within-initial-thread 'load-shared-object path)
          371   #-(and darwin sb-thread) (load-shared-object path))
          372 
          373 ;;; SBCL 1.0.21.15 renamed SB-ALIEN::SHARED-OBJECT-FILE but introduced
          374 ;;; SB-ALIEN:UNLOAD-SHARED-OBJECT which we can use instead.
          375 (eval-when (:compile-toplevel :load-toplevel :execute)
          376   (defun unload-shared-object-present-p ()
          377     (multiple-value-bind (foundp kind)
          378         (find-symbol "UNLOAD-SHARED-OBJECT" "SB-ALIEN")
          379       (if (and foundp (eq kind :external))
          380           '(:and)
          381           '(:or)))))
          382 
          383 (defun %close-foreign-library (handle)
          384   "Closes a foreign library."
          385   #+#.(cffi-sys::unload-shared-object-present-p)
          386   (sb-alien:unload-shared-object handle)
          387   #-#.(cffi-sys::unload-shared-object-present-p)
          388   (sb-thread:with-mutex (sb-alien::*shared-objects-lock*)
          389     (let ((obj (find (sb-ext:native-namestring handle)
          390                      sb-alien::*shared-objects*
          391                      :key #'sb-alien::shared-object-file
          392                      :test #'string=)))
          393       (when obj
          394         (sb-alien::dlclose-or-lose obj)
          395         (removef sb-alien::*shared-objects* obj)
          396         #-win32
          397         (sb-alien::update-linkage-table)))))
          398 
          399 (defun native-namestring (pathname)
          400   (sb-ext:native-namestring pathname))
          401 
          402 ;;;# Foreign Globals
          403 
          404 (defun %foreign-symbol-pointer (name library)
          405   "Returns a pointer to a foreign symbol NAME."
          406   (declare (ignore library))
          407   (when-let (address (sb-sys:find-foreign-symbol-address name))
          408     (sb-sys:int-sap address)))