funcall.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 --- funcall.lisp (6272B) --- 1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*- 2 ;;; 3 ;;; funcall.lisp -- FOREIGN-FUNCALL implementation using libffi 4 ;;; 5 ;;; Copyright (C) 2009, 2010, 2011 Liam M. Healy <lhealy@common-lisp.net> 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 (in-package #:cffi) 29 30 (define-condition libffi-error (cffi-error) 31 ((function-name 32 :initarg :function-name :reader function-name))) 33 34 (define-condition simple-libffi-error (simple-error libffi-error) 35 ()) 36 37 (defun libffi-error (function-name format-control &rest format-arguments) 38 (error 'simple-libffi-error 39 :function-name function-name 40 :format-control format-control 41 :format-arguments format-arguments)) 42 43 (defun make-libffi-cif (function-name return-type argument-types 44 &optional (abi :default-abi)) 45 "Generate or retrieve the Call InterFace needed to call the function through libffi." 46 (let* ((argument-count (length argument-types)) 47 (cif (foreign-alloc '(:struct ffi-cif))) 48 (ffi-argtypes (foreign-alloc :pointer :count argument-count))) 49 (loop 50 :for type :in argument-types 51 :for index :from 0 52 :do (setf (mem-aref ffi-argtypes :pointer index) 53 (make-libffi-type-descriptor (parse-type type)))) 54 (unless (eql :ok (libffi/prep-cif cif abi argument-count 55 (make-libffi-type-descriptor (parse-type return-type)) 56 ffi-argtypes)) 57 (libffi-error function-name 58 "The 'ffi_prep_cif' libffi call failed for function ~S." 59 function-name)) 60 cif)) 61 62 (defun free-libffi-cif (ptr) 63 (foreign-free (foreign-slot-value ptr '(:struct ffi-cif) 'argument-types)) 64 (foreign-free ptr)) 65 66 (defun translate-objects-ret (symbols function-arguments types return-type call-form) 67 (translate-objects 68 symbols 69 function-arguments 70 types 71 return-type 72 (if (or (eql return-type :void) 73 (typep (parse-type return-type) 'translatable-foreign-type)) 74 call-form 75 ;; built-in types won't be translated by 76 ;; expand-from-foreign, we have to do it here 77 `(mem-ref 78 ,call-form 79 ',(canonicalize-foreign-type return-type))) 80 t)) 81 82 (defun foreign-funcall-form/fsbv-with-libffi (function function-arguments symbols types 83 return-type argument-types 84 &optional pointerp (abi :default-abi)) 85 "A body of foreign-funcall calling the libffi function #'call (ffi_call)." 86 (let ((argument-count (length argument-types))) 87 `(with-foreign-objects ((argument-values :pointer ,argument-count) 88 ,@(unless (eql return-type :void) 89 `((result ',return-type)))) 90 ,(translate-objects-ret 91 symbols function-arguments types return-type 92 ;; NOTE: We must delay the cif creation until the first call 93 ;; because it's FOREIGN-ALLOC'd, i.e. it gets corrupted by an 94 ;; image save/restore cycle. This way a lib will remain usable 95 ;; through a save/restore cycle if the save happens before any 96 ;; FFI calls will have been made, i.e. nothing is malloc'd yet. 97 `(progn 98 (loop 99 :for arg :in (list ,@symbols) 100 :for count :from 0 101 :do (setf (mem-aref argument-values :pointer count) arg)) 102 (let* ((libffi-cif-cache (load-time-value (cons 'libffi-cif-cache nil))) 103 (libffi-cif (or (cdr libffi-cif-cache) 104 (setf (cdr libffi-cif-cache) 105 ;; FIXME ideally we should install a finalizer on the cons 106 ;; that calls FREE-LIBFFI-CIF on the cif (when the function 107 ;; gets redefined, and the cif becomes unreachable). but a 108 ;; finite world is full of compromises... - attila 109 (make-libffi-cif ,function ',return-type 110 ',argument-types ',abi))))) 111 (libffi/call libffi-cif 112 ,(if pointerp 113 function 114 `(foreign-symbol-pointer ,function)) 115 ,(if (eql return-type :void) '(null-pointer) 'result) 116 argument-values) 117 ,(if (eql return-type :void) 118 '(values) 119 'result))))))) 120 121 (setf *foreign-structures-by-value* 'foreign-funcall-form/fsbv-with-libffi) 122 123 ;; DEPRECATED Its presence encourages the use of #+fsbv which may lead to the 124 ;; situation where a fasl was produced by an image that has fsbv feature 125 ;; and then ends up being loaded into an image later that has no fsbv support 126 ;; loaded. Use explicit ASDF dependencies instead and assume the presence 127 ;; of the feature accordingly. 128 (pushnew :fsbv *features*) 129 130 ;; DEPRECATED This is here only for backwards compatibility until its fate is 131 ;; decided. See the mailing list discussion for details. 132 (defctype :sizet size-t)