funcall.lisp 6.1 KB

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