cffi-mkcl.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-mkcl.lisp (12048B)
       ---
            1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
            2 ;;;
            3 ;;; cffi-mkcl.lisp --- MKCL backend for CFFI.
            4 ;;;
            5 ;;; Copyright (C) 2010-2012, Jean-Claude Beaudoin
            6 ;;; Copyright (C) 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 ;;;# Administrivia
           30 
           31 (defpackage #:cffi-sys
           32   (:use #:common-lisp #:alexandria)
           33   (:export
           34    #:canonicalize-symbol-name-case
           35    #:foreign-pointer
           36    #:pointerp
           37    #:pointer-eq
           38    #:null-pointer
           39    #:null-pointer-p
           40    #:inc-pointer
           41    #:make-pointer
           42    #:pointer-address
           43    #:%foreign-alloc
           44    #:foreign-free
           45    #:with-foreign-pointer
           46    #:%foreign-funcall
           47    #:%foreign-funcall-pointer
           48    #:%foreign-type-alignment
           49    #:%foreign-type-size
           50    #:%load-foreign-library
           51    #:%close-foreign-library
           52    #:native-namestring
           53    #:%mem-ref
           54    #:%mem-set
           55    #:make-shareable-byte-vector
           56    #:with-pointer-to-vector-data
           57    #:%foreign-symbol-pointer
           58    #:%defcallback
           59    #:%callback))
           60 
           61 (in-package #:cffi-sys)
           62 
           63 ;;;# Mis-features
           64 
           65 (pushnew 'flat-namespace *features*)
           66 
           67 ;;;# Symbol Case
           68 
           69 (defun canonicalize-symbol-name-case (name)
           70   (declare (string name))
           71   (string-upcase name))
           72 
           73 ;;;# Allocation
           74 
           75 (defun %foreign-alloc (size)
           76   "Allocate SIZE bytes of foreign-addressable memory."
           77   (si:allocate-foreign-data :void size))
           78 
           79 (defun foreign-free (ptr)
           80   "Free a pointer PTR allocated by FOREIGN-ALLOC."
           81   (si:free-foreign-data ptr)
           82   nil)
           83 
           84 (defmacro with-foreign-pointer ((var size &optional size-var) &body body)
           85   "Bind VAR to SIZE bytes of foreign memory during BODY.  The
           86 pointer in VAR is invalid beyond the dynamic extent of BODY, and
           87 may be stack-allocated if supported by the implementation.  If
           88 SIZE-VAR is supplied, it will be bound to SIZE during BODY."
           89   (unless size-var
           90     (setf size-var (gensym "SIZE")))
           91   `(let* ((,size-var ,size)
           92           (,var (%foreign-alloc ,size-var)))
           93      (unwind-protect
           94           (progn ,@body)
           95        (foreign-free ,var))))
           96 
           97 ;;;# Misc. Pointer Operations
           98 
           99 (deftype foreign-pointer ()
          100   'si:foreign)
          101 
          102 (defun null-pointer ()
          103   "Construct and return a null pointer."
          104   (si:make-foreign-null-pointer))
          105 
          106 (defun null-pointer-p (ptr)
          107   "Return true if PTR is a null pointer."
          108   (si:null-pointer-p ptr))
          109 
          110 (defun inc-pointer (ptr offset)
          111   "Return a pointer OFFSET bytes past PTR."
          112   (ffi:make-pointer (+ (ffi:pointer-address ptr) offset) :void))
          113 
          114 (defun pointerp (ptr)
          115   "Return true if PTR is a foreign pointer."
          116   ;;(typep ptr 'si:foreign)
          117   (si:foreignp ptr))
          118 
          119 (defun pointer-eq (ptr1 ptr2)
          120   "Return true if PTR1 and PTR2 point to the same address."
          121   (= (ffi:pointer-address ptr1) (ffi:pointer-address ptr2)))
          122 
          123 (defun make-pointer (address)
          124   "Return a pointer pointing to ADDRESS."
          125   (ffi:make-pointer address :void))
          126 
          127 (defun pointer-address (ptr)
          128   "Return the address pointed to by PTR."
          129   (ffi:pointer-address ptr))
          130 
          131 ;;;# Shareable Vectors
          132 ;;;
          133 ;;; This interface is very experimental.  WITH-POINTER-TO-VECTOR-DATA
          134 ;;; should be defined to perform a copy-in/copy-out if the Lisp
          135 ;;; implementation can't do this.
          136 
          137 (defun make-shareable-byte-vector (size)
          138   "Create a Lisp vector of SIZE bytes that can passed to
          139 WITH-POINTER-TO-VECTOR-DATA."
          140   (make-array size :element-type '(unsigned-byte 8)))
          141 
          142 ;;; MKCL, built with the Boehm GC never moves allocated data, so this
          143 ;;; isn't nearly as hard to do.
          144 (defun %vector-address (vector)
          145   "Return the address of VECTOR's data."
          146   (check-type vector (vector (unsigned-byte 8)))
          147   #-mingw64
          148   (ffi:c-inline (vector) (object) 
          149                 :unsigned-long
          150                 "(uintptr_t) #0->vector.self.b8"
          151                 :side-effects nil
          152                 :one-liner t)
          153   #+mingw64
          154   (ffi:c-inline (vector) (object) 
          155                 :unsigned-long-long
          156                 "(uintptr_t) #0->vector.self.b8"
          157                 :side-effects nil
          158                 :one-liner t))
          159 
          160 (defmacro with-pointer-to-vector-data ((ptr-var vector) &body body)
          161   "Bind PTR-VAR to a foreign pointer to the data in VECTOR."
          162   `(let ((,ptr-var (make-pointer (%vector-address ,vector))))
          163      ,@body))
          164 
          165 ;;;# Dereferencing
          166 
          167 (defun %mem-ref (ptr type &optional (offset 0))
          168   "Dereference an object of TYPE at OFFSET bytes from PTR."
          169   (let* ((type (cffi-type->mkcl-type type))
          170          (type-size (ffi:size-of-foreign-type type)))
          171     (si:foreign-ref-elt
          172      (si:foreign-recast ptr (+ offset type-size) :void) offset type)))
          173 
          174 (defun %mem-set (value ptr type &optional (offset 0))
          175   "Set an object of TYPE at OFFSET bytes from PTR."
          176   (let* ((type (cffi-type->mkcl-type type))
          177          (type-size (ffi:size-of-foreign-type type)))
          178     (si:foreign-set-elt
          179      (si:foreign-recast ptr (+ offset type-size) :void)
          180      offset type value)))
          181 
          182 ;;;# Type Operations
          183 
          184 (defconstant +translation-table+
          185   '((:char               :byte               "char")
          186     (:unsigned-char      :unsigned-byte      "unsigned char")
          187     (:short              :short              "short")
          188     (:unsigned-short     :unsigned-short     "unsigned short")
          189     (:int                :int                "int")
          190     (:unsigned-int       :unsigned-int       "unsigned int")
          191     (:long               :long               "long")
          192     (:unsigned-long      :unsigned-long      "unsigned long")
          193     (:long-long          :long-long          "long long")
          194     (:unsigned-long-long :unsigned-long-long "unsigned long long")
          195     (:float              :float              "float")
          196     (:double             :double             "double")
          197     (:pointer            :pointer-void       "void*")
          198     (:void               :void               "void")))
          199 
          200 (defun cffi-type->mkcl-type (type-keyword)
          201   "Convert a CFFI type keyword to an MKCL type keyword."
          202   (or (second (find type-keyword +translation-table+ :key #'first))
          203       (error "~S is not a valid CFFI type" type-keyword)))
          204 
          205 (defun mkcl-type->c-type (type-keyword)
          206   "Convert a CFFI type keyword to an valid C type keyword."
          207   (or (third (find type-keyword +translation-table+ :key #'second))
          208       (error "~S is not a valid CFFI type" type-keyword)))
          209 
          210 (defun %foreign-type-size (type-keyword)
          211   "Return the size in bytes of a foreign type."
          212   (nth-value 0 (ffi:size-of-foreign-type
          213                 (cffi-type->mkcl-type type-keyword))))
          214 
          215 (defun %foreign-type-alignment (type-keyword)
          216   "Return the alignment in bytes of a foreign type."
          217   (nth-value 1 (ffi:size-of-foreign-type
          218                 (cffi-type->mkcl-type type-keyword))))
          219 
          220 ;;;# Calling Foreign Functions
          221 
          222 #|
          223 (defconstant +mkcl-inline-codes+ "#0,#1,#2,#3,#4,#5,#6,#7,#8,#9,#a,#b,#c,#d,#e,#f,#g,#h,#i,#j,#k,#l,#m,#n,#o,#p,#q,#r,#s,#t,#u,#v,#w,#x,#y,#z")
          224 |#
          225 
          226 (defun produce-function-pointer-call (pointer types values return-type)
          227 #|
          228   (if (stringp pointer)
          229       (produce-function-pointer-call
          230        `(%foreign-symbol-pointer ,pointer nil) types values return-type)
          231       `(ffi:c-inline
          232         ,(list* pointer values)
          233         ,(list* :pointer-void types) ,return-type
          234         ,(with-output-to-string (s)
          235            (let ((types (mapcar #'mkcl-type->c-type types)))
          236              ;; On AMD64, the following code only works with the extra
          237              ;; argument ",...". If this is not present, functions
          238              ;; like sprintf do not work
          239              (format s "((~A (*)(~@[~{~A,~}...~]))(#0))(~A)"
          240                      (mkcl-type->c-type return-type) types
          241                      (subseq +mkcl-inline-codes+ 3
          242                              (max 3 (+ 2 (* (length values) 3)))))))
          243         :one-liner t :side-effects t))
          244 |#
          245   ;; The version here below is definitely not as efficient as the one above
          246   ;; but it has the great vertue of working in all cases, (contrary to the
          247   ;; silent and unsafe limitations of the one above). JCB
          248   ;; I should re-optimize this one day, when I get time... JCB
          249   (progn
          250     (when (stringp pointer)
          251       (setf pointer `(%foreign-symbol-pointer ,pointer nil)))
          252     `(si:call-cfun ,pointer ,return-type (list ,@types) (list ,@values))))
          253 
          254 
          255 (defun foreign-funcall-parse-args (args)
          256   "Return three values, lists of arg types, values, and result type."
          257   (let ((return-type :void))
          258     (loop for (type arg) on args by #'cddr
          259           if arg collect (cffi-type->mkcl-type type) into types
          260           and collect arg into values
          261           else do (setf return-type (cffi-type->mkcl-type type))
          262           finally (return (values types values return-type)))))
          263 
          264 (defmacro %foreign-funcall (name args &key library convention)
          265   "Call a foreign function."
          266   (declare (ignore library convention))
          267   (multiple-value-bind (types values return-type)
          268       (foreign-funcall-parse-args args)
          269     (produce-function-pointer-call name types values return-type)))
          270 
          271 (defmacro %foreign-funcall-pointer (ptr args &key convention)
          272   "Funcall a pointer to a foreign function."
          273   (declare (ignore convention))
          274   (multiple-value-bind (types values return-type)
          275       (foreign-funcall-parse-args args)
          276     (produce-function-pointer-call ptr types values return-type)))
          277 
          278 ;;;# Foreign Libraries
          279 
          280 (defun %load-foreign-library (name path)
          281   "Load a foreign library."
          282   (declare (ignore name))
          283   (handler-case (si:load-foreign-module path)
          284     (file-error ()
          285       (error "file error while trying to load `~A'" path))))
          286 
          287 (defun %close-foreign-library (handle)
          288   ;;(declare (ignore handle))
          289   ;;(error "%CLOSE-FOREIGN-LIBRARY unimplemented.")
          290   (si:unload-foreign-module handle))
          291 
          292 (defun native-namestring (pathname)
          293   (namestring pathname))
          294 
          295 ;;;# Callbacks
          296 
          297 ;;; Create a package to contain the symbols for callback functions.
          298 ;;; We want to redefine callbacks with the same symbol so the internal
          299 ;;; data structures are reused.
          300 (defpackage #:cffi-callbacks
          301   (:use))
          302 
          303 (defvar *callbacks* (make-hash-table))
          304 
          305 ;;; Intern a symbol in the CFFI-CALLBACKS package used to name the
          306 ;;; internal callback for NAME.
          307 (eval-when (:compile-toplevel :load-toplevel :execute)
          308   (defun intern-callback (name)
          309     (intern (format nil "~A::~A"
          310                     (if-let (package (symbol-package name))
          311                       (package-name package)
          312                       "#")
          313                     (symbol-name name))
          314             '#:cffi-callbacks)))
          315 
          316 (defmacro %defcallback (name rettype arg-names arg-types body
          317                         &key convention)
          318   (declare (ignore convention))
          319   (let ((cb-name (intern-callback name)))
          320     `(progn
          321        (ffi:defcallback (,cb-name :cdecl)
          322                         ,(cffi-type->mkcl-type rettype)
          323                         ,(mapcar #'list arg-names
          324                                  (mapcar #'cffi-type->mkcl-type arg-types))
          325                         ;;(block ,cb-name ,@body)
          326                         (block ,cb-name ,body))
          327        (setf (gethash ',name *callbacks*) ',cb-name))))
          328 
          329 (defun %callback (name)
          330   (multiple-value-bind (symbol winp)
          331       (gethash name *callbacks*)
          332     (unless winp
          333       (error "Undefined callback: ~S" name))
          334     (ffi:callback symbol)))
          335 
          336 ;;;# Foreign Globals
          337 
          338 (defun %foreign-symbol-pointer (name library)
          339   "Returns a pointer to a foreign symbol NAME."
          340   (declare (ignore library))
          341   (values (ignore-errors (si:find-foreign-symbol name :default :pointer-void 0))))
          342