Coverage report: /home/ellis/.stash/quicklisp/dists/quicklisp/software/cffi-20250622-git/src/functions.lisp
Kind | Covered | All | % |
expression | 0 | 615 | 0.0 |
branch | 0 | 44 | 0.0 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
3
;;; functions.lisp --- High-level interface to foreign functions.
5
;;; Copyright (C) 2005-2006, James Bielman <jamesjb@jamesjb.com>
6
;;; Copyright (C) 2005-2007, Luis Oliveira <loliveira@common-lisp.net>
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:
16
;;; The above copyright notice and this permission notice shall be
17
;;; included in all copies or substantial portions of the Software.
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.
31
;;;# Calling Foreign Functions
33
;;; FOREIGN-FUNCALL is the main primitive for calling foreign
34
;;; functions. It converts each argument based on the installed
35
;;; translators for its type, then passes the resulting list to
36
;;; CFFI-SYS:%FOREIGN-FUNCALL.
38
;;; For implementation-specific reasons, DEFCFUN doesn't use
39
;;; FOREIGN-FUNCALL directly and might use something else (passed to
40
;;; TRANSLATE-OBJECTS as the CALL-FORM argument) instead of
41
;;; CFFI-SYS:%FOREIGN-FUNCALL to call the foreign-function.
43
(defun translate-objects (syms args types rettype call-form &optional indirect)
44
"Helper function for FOREIGN-FUNCALL and DEFCFUN. If 'indirect is T, all arguments are represented by foreign pointers, even those that can be represented by CL objects."
46
(expand-from-foreign call-form (parse-type rettype))
49
#'expand-to-foreign-dyn-indirect
50
#'expand-to-foreign-dyn)
52
(list (translate-objects (cdr syms) (cdr args)
53
(cdr types) rettype call-form indirect))
54
(parse-type (car types)))))
56
(defun parse-args-and-types (args)
57
"Returns 4 values: types, canonicalized types, args and return type."
58
(let* ((len (length args))
59
(return-type (if (oddp len) (lastcar args) :void)))
60
(loop repeat (floor len 2)
61
for (type arg) on args by #'cddr
62
collect type into types
63
collect (canonicalize-foreign-type type) into ctypes
64
collect arg into fargs
65
finally (return (values types ctypes fargs return-type)))))
67
;;; While the options passed directly to DEFCFUN/FOREIGN-FUNCALL have
68
;;; precedence, we also grab its library's options, if possible.
69
(defun parse-function-options (options &key pointer)
70
(destructuring-bind (&key (library :default libraryp)
72
(calling-convention cconv calling-convention-p)
73
(convention calling-convention))
76
(warn-obsolete-argument :cconv :convention))
77
(when calling-convention-p
78
(warn-obsolete-argument :calling-convention :convention))
82
(let ((lib-options (foreign-library-options
83
(get-foreign-library library))))
84
(getf lib-options :convention)))
86
;; Don't pass the library option if we're dealing with
87
;; FOREIGN-FUNCALL-POINTER.
89
(list :library library)))))
91
(defun structure-by-value-p (ctype)
92
"A structure or union is to be called or returned by value."
93
(let ((actual-type (ensure-parsed-base-type ctype)))
94
(or (and (typep actual-type 'foreign-struct-type)
95
(not (bare-struct-type-p actual-type)))
96
#+cffi::no-long-long (typep actual-type 'emulated-llong-type))))
98
(defun fn-call-by-value-p (argument-types return-type)
99
"One or more structures in the arguments or return from the function are called by value."
100
(or (some 'structure-by-value-p argument-types)
101
(structure-by-value-p return-type)))
103
(defvar *foreign-structures-by-value*
105
(declare (ignore args))
107
(error "Unable to call structures by value without cffi-libffi loaded.")
108
(load-cffi-libffi () :report "Load cffi-libffi."
109
(asdf:operate 'asdf:load-op 'cffi-libffi))))
110
"A function that produces a form suitable for calling structures by value.")
112
(defun foreign-funcall-form (thing options args pointerp)
113
(multiple-value-bind (types ctypes fargs rettype)
114
(parse-args-and-types args)
115
(let ((syms (make-gensym-list (length fargs)))
116
(fsbvp (fn-call-by-value-p ctypes rettype)))
118
;; Structures by value call through *foreign-structures-by-value*
119
(funcall *foreign-structures-by-value*
128
syms fargs types rettype
129
`(,(if pointerp '%foreign-funcall-pointer '%foreign-funcall)
130
;; No structures by value, direct call
132
(,@(mapcan #'list ctypes syms)
133
,(canonicalize-foreign-type rettype))
134
,@(parse-function-options options :pointer pointerp)))))))
136
(defmacro foreign-funcall (name-and-options &rest args)
137
"Wrapper around %FOREIGN-FUNCALL that translates its arguments."
138
(let ((name (car (ensure-list name-and-options)))
139
(options (cdr (ensure-list name-and-options))))
140
(foreign-funcall-form name options args nil)))
142
(defmacro foreign-funcall-pointer (pointer options &rest args)
143
(foreign-funcall-form pointer options args t))
145
(defun promote-varargs-type (builtin-type)
146
"Default argument promotions."
149
((:char :short) :int)
150
((:unsigned-char :unsigned-short) :unsigned-int)
153
;; If cffi-sys doesn't provide a %foreign-funcall-varargs macros we
154
;; define one that use %foreign-funcall.
155
(eval-when (:compile-toplevel :load-toplevel :execute)
156
(unless (fboundp '%foreign-funcall-varargs)
157
(defmacro %foreign-funcall-varargs (name fixed-args varargs
158
&rest args &key convention library)
159
(declare (ignore convention library))
160
`(%foreign-funcall ,name ,(append fixed-args varargs) ,@args)))
161
(unless (fboundp '%foreign-funcall-pointer-varargs)
162
(defmacro %foreign-funcall-pointer-varargs (pointer fixed-args varargs
163
&rest args &key convention)
164
(declare (ignore convention))
165
`(%foreign-funcall-pointer ,pointer ,(append fixed-args varargs) ,@args))))
167
(defun foreign-funcall-varargs-form (thing options fixed-args varargs pointerp)
168
(multiple-value-bind (fixed-types fixed-ctypes fixed-fargs)
169
(parse-args-and-types fixed-args)
170
(multiple-value-bind (varargs-types varargs-ctypes varargs-fargs rettype)
171
(parse-args-and-types varargs)
172
(let ((fixed-syms (make-gensym-list (length fixed-fargs)))
173
(varargs-syms (make-gensym-list (length varargs-fargs))))
175
(append fixed-syms varargs-syms)
176
(append fixed-fargs varargs-fargs)
177
(append fixed-types varargs-types)
179
`(,(if pointerp '%foreign-funcall-pointer-varargs '%foreign-funcall-varargs)
181
,(mapcan #'list fixed-ctypes fixed-syms)
184
(mapcar #'promote-varargs-type varargs-ctypes)
185
(loop for sym in varargs-syms
186
and type in varargs-ctypes
188
collect `(float ,sym 1.0d0)
190
(list (canonicalize-foreign-type rettype)))
193
(defmacro foreign-funcall-varargs (name-and-options fixed-args
195
"Wrapper around %FOREIGN-FUNCALL that translates its arguments
196
and does type promotion for the variadic arguments."
197
(let ((name (car (ensure-list name-and-options)))
198
(options (cdr (ensure-list name-and-options))))
199
(foreign-funcall-varargs-form name options fixed-args varargs nil)))
201
(defmacro foreign-funcall-pointer-varargs (pointer options fixed-args
203
"Wrapper around %FOREIGN-FUNCALL-POINTER that translates its
204
arguments and does type promotion for the variadic arguments."
205
(foreign-funcall-varargs-form pointer options fixed-args varargs t))
207
;;;# Defining Foreign Functions
209
;;; The DEFCFUN macro provides a declarative interface for defining
210
;;; Lisp functions that call foreign functions.
212
;; If cffi-sys doesn't provide a defcfun-helper-forms,
213
;; we define one that uses %foreign-funcall.
214
(eval-when (:compile-toplevel :load-toplevel :execute)
215
(unless (fboundp 'defcfun-helper-forms)
216
(defun defcfun-helper-forms (name lisp-name rettype args types options)
217
(declare (ignore lisp-name))
220
`(%foreign-funcall ,name ,(append (mapcan #'list types args)
224
(defun %defcfun (lisp-name foreign-name return-type args options docstring)
225
(let* ((arg-names (mapcar #'first args))
226
(arg-types (mapcar #'second args))
227
(syms (make-gensym-list (length args)))
228
(call-by-value (fn-call-by-value-p arg-types return-type)))
229
(multiple-value-bind (prelude caller)
232
(defcfun-helper-forms
233
foreign-name lisp-name (canonicalize-foreign-type return-type)
234
syms (mapcar #'canonicalize-foreign-type arg-types) options))
237
(defun ,lisp-name ,arg-names
238
#+cmucl (declare (notinline alien::%heap-alien))
239
,@(ensure-list docstring)
242
,(cons foreign-name options)
243
,@(append (mapcan #'list arg-types arg-names)
246
syms arg-names arg-types return-type caller)))))))
248
(defun %defcfun-varargs (lisp-name foreign-name return-type args options doc)
249
(with-unique-names (varargs)
250
(let ((arg-names (mapcar #'car args)))
251
`(defmacro ,lisp-name (,@arg-names &rest ,varargs)
253
`(foreign-funcall-varargs
254
,'(,foreign-name ,@options)
255
,,`(list ,@(loop for (name type) in args
256
collect `',type collect name))
260
(defgeneric translate-underscore-separated-name (name)
261
(:method ((name string))
262
(values (intern (canonicalize-symbol-name-case (substitute #\- #\_ name)))))
263
(:method ((name symbol))
264
(substitute #\_ #\- (string-downcase (symbol-name name)))))
266
(defun collapse-prefix (l special-words)
268
(multiple-value-bind (newpre skip) (check-prefix l special-words)
269
(cons newpre (collapse-prefix (nthcdr skip l) special-words)))))
271
(defun check-prefix (l special-words)
272
(let ((pl (loop for i from (1- (length l)) downto 0
273
collect (apply #'concatenate 'simple-string (butlast l i)))))
274
(loop for w in special-words
275
for p = (position-if #'(lambda (s) (string= s w)) pl)
276
when p do (return-from check-prefix (values (nth p pl) (1+ p))))
277
(values (first l) 1)))
279
(defgeneric translate-camelcase-name (name &key upper-initial-p special-words)
280
(:method ((name string) &key upper-initial-p special-words)
281
(declare (ignore upper-initial-p))
282
(values (intern (reduce #'(lambda (s1 s2)
283
(concatenate 'simple-string s1 "-" s2))
284
(mapcar #'string-upcase
286
(split-if #'(lambda (ch)
287
(or (upper-case-p ch)
291
(:method ((name symbol) &key upper-initial-p special-words)
294
(loop for str in (split-if #'(lambda (ch) (eq ch #\-))
297
for first-word-p = t then nil
298
for e = (member str special-words
299
:test #'equal :key #'string-upcase)
301
((and first-word-p (not upper-initial-p))
302
(string-downcase str))
304
(t (string-capitalize str)))))))
306
(defgeneric translate-name-from-foreign (foreign-name package &optional varp)
307
(:method (foreign-name package &optional varp)
308
(declare (ignore package))
309
(let ((sym (translate-underscore-separated-name foreign-name)))
311
(values (intern (format nil "*~A*"
312
(canonicalize-symbol-name-case
313
(symbol-name sym)))))
316
(defgeneric translate-name-to-foreign (lisp-name package &optional varp)
317
(:method (lisp-name package &optional varp)
318
(declare (ignore package))
319
(let ((name (translate-underscore-separated-name lisp-name)))
321
(string-trim '(#\*) name)
324
(defun lisp-name (spec varp)
325
(check-type spec string)
326
(translate-name-from-foreign spec *package* varp))
328
(defun foreign-name (spec varp)
329
(check-type spec (and symbol (not null)))
330
(translate-name-to-foreign spec *package* varp))
332
(defun foreign-options (opts varp)
334
(funcall 'parse-defcvar-options opts)
335
(parse-function-options opts)))
337
(defun lisp-name-p (name)
338
(and name (symbolp name) (not (keywordp name))))
340
(defun %parse-name-and-options (spec varp)
343
(values (lisp-name spec varp) spec nil))
345
(assert (not (null spec)))
346
(values spec (foreign-name spec varp) nil))
347
((and (consp spec) (stringp (first spec)))
348
(destructuring-bind (foreign-name &rest options)
352
(keywordp (first options)))
353
(values (lisp-name foreign-name varp) foreign-name options))
355
(assert (lisp-name-p (first options)))
356
(values (first options) foreign-name (rest options))))))
357
((and (consp spec) (lisp-name-p (first spec)))
358
(destructuring-bind (lisp-name &rest options)
362
(keywordp (first options)))
363
(values lisp-name (foreign-name spec varp) options))
365
(assert (stringp (first options)))
366
(values lisp-name (first options) (rest options))))))
368
(error "Not a valid foreign function specifier: ~A" spec))))
370
;;; DEFCFUN's first argument has can have the following syntax:
374
;;; 3. \( string [symbol] options* )
375
;;; 4. \( symbol [string] options* )
377
;;; The string argument denotes the foreign function's name. The
378
;;; symbol argument is used to name the Lisp function. If one isn't
379
;;; present, its name is derived from the other. See the user
380
;;; documentation for an explanation of the derivation rules.
381
(defun parse-name-and-options (spec &optional varp)
382
(multiple-value-bind (lisp-name foreign-name options)
383
(%parse-name-and-options spec varp)
384
(values lisp-name foreign-name (foreign-options options varp))))
386
;;; If we find a &REST token at the end of ARGS, it means this is a
387
;;; varargs foreign function therefore we define a lisp macro using
388
;;; %DEFCFUN-VARARGS. Otherwise, a lisp function is defined with
390
(defmacro defcfun (name-and-options return-type &body args)
391
"Defines a Lisp function that calls a foreign function."
392
(let ((docstring (when (stringp (car args)) (pop args))))
393
(multiple-value-bind (lisp-name foreign-name options)
394
(parse-name-and-options name-and-options)
395
(if (eq (lastcar args) '&rest)
396
(%defcfun-varargs lisp-name foreign-name return-type
397
(butlast args) options docstring)
398
(%defcfun lisp-name foreign-name return-type args options
401
;;;# Defining Callbacks
403
(defun inverse-translate-objects (args types declarations rettype call)
404
`(let (,@(loop for arg in args and type in types
405
collect (list arg (expand-from-foreign
406
arg (parse-type type)))))
408
,(expand-to-foreign call (parse-type rettype))))
410
(defun parse-defcallback-options (options)
411
(destructuring-bind (&key (cconv :cdecl cconv-p)
412
(calling-convention cconv calling-convention-p)
413
(convention calling-convention))
416
(warn-obsolete-argument :cconv :convention))
417
(when calling-convention-p
418
(warn-obsolete-argument :calling-convention :convention))
419
(list :convention convention)))
421
(defmacro defcallback (name-and-options return-type args &body body)
422
(multiple-value-bind (body declarations)
423
(parse-body body :documentation t)
424
(let ((arg-names (mapcar #'car args))
425
(arg-types (mapcar #'cadr args))
426
(name (car (ensure-list name-and-options)))
427
(options (cdr (ensure-list name-and-options))))
429
(%defcallback ,name ,(canonicalize-foreign-type return-type)
430
,arg-names ,(mapcar #'canonicalize-foreign-type arg-types)
431
,(inverse-translate-objects
432
arg-names arg-types declarations return-type
433
`(block ,name ,@body))
434
,@(parse-defcallback-options options))
437
(declaim (inline get-callback))
438
(defun get-callback (symbol)
441
(defmacro callback (name)