SXEmacs, meet Dbus. Dbus, meet SXEmacs
[sxemacs] / src / effi.c
1 /*
2  * effi.c --- Foreign Function Interface for SXEmacs.
3  *
4  * Copyright (C) 2004-2008 Zajcev Evgeny
5  *
6 This file is part of SXEmacs
7
8 SXEmacs is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 SXEmacs is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
20
21
22 #include <config.h>
23 #include "lisp.h"
24
25 #include <dlfcn.h>
26 #include <math.h>
27 #include "sysdep.h"
28 #include "ent/ent.h"
29 #include "effi.h"
30
31 #include "buffer.h"
32 #ifdef FILE_CODING
33 #  include "mule/file-coding.h"
34 #endif
35
36 #ifdef HAVE_LIBFFI
37 #  undef ALIGN
38 #  include <ffi.h>
39 #endif  /* HAVE_LIBFFI */
40
41 #ifdef EF_USE_ASYNEQ
42 #  include "events/workers.h"
43 #  include "events/worker-asyneq.h"
44 #endif  /* EF_USE_ASYNEQ */
45
46 /* For `x-device-display' */
47 #include "ui/X11/console-x.h"
48 #include "ui/device.h"
49
50 #define EFFI_CODING     Qnative
51
52 /*
53  * Some compatibility for XEmacs
54  */
55 #ifdef SXEMACS
56 #  define SIGNAL_ERROR signal_error
57 #  define FFIBYTE Bufbyte
58 #  define WRITE_C_STRING(x,y) write_c_string((x),(y))
59 #  define WRITE_FMT_STRING(x,y,...) write_fmt_string((x),(y),__VA_ARGS__)
60 #  define LRECORD_DESCRIPTION lrecord_description
61 #else
62 #  define SIGNAL_ERROR Fsignal
63 #  define FFIBYTE Ibyte
64 #  define WRITE_C_STRING(x,y) write_c_string((y),(x))
65 #  define WRITE_FMT_STRING(x,y,...)                     \
66         do {                                            \
67                 char wcsb[128];                         \
68                 int wcss = snprintf(wcsb, sizeof(wcsb), \
69                                     (y),__VA_ARGS__);   \
70                 write_c_string((y),wcsb);               \
71         } while(0)
72 #  define LRECORD_DESCRIPTION memory_description
73 #endif  /* SXEMACS */
74
75 /*
76  * Built-in types:
77  *   byte, ubyte, char, uchar,
78  *   short, ushort, int, uint,
79  *   long, ulong,
80  *   float, double,
81  *   void, pointer, c-string
82  *
83  * Function type:
84  *
85  *   (function RET-TYPE IN-TYPE .. IN-TYPE)
86  *
87  * Array types:
88  *
89  *   (array TYPE SIZE)
90  *
91  * Structures and unions types:
92  *
93  *   (struct|union NAME
94  *     (SLOT-NAME TYPE)
95  *     (SLOT-NAME TYPE)
96  *     ...
97  *     (SLOT-NAME TYPE))
98  *
99  * Pointers:
100  *
101  *   pointer or (pointer TYPE)
102  */
103
104 /* Foreign types, not defined as symbols elsewhere. */
105 Lisp_Object Qarray, Qbyte, Qc_data, Qc_string, Qdouble, Qlong, Qstruct;
106 Lisp_Object Qunion, Qunsigned_byte, Qunsigned_char, Qunsigned_int;
107 Lisp_Object Qunsigned_long, Qunsigned_short;
108
109 #define FFI_POINTERP(type) (EQ(type, Qpointer)                                \
110                             || (CONSP(type) && EQ(XCAR(type), Qpointer)))
111
112 #define FFI_TPTR(type) (EQ(type, Qc_string)                                   \
113                         || EQ(type, Qc_data)                                  \
114                         || FFI_POINTERP(type)                                  \
115                         || (CONSP(type) && ((EQ(XCAR(type), Qc_data))         \
116                                             || EQ(XCAR(type), Qarray))))
117 Lisp_Object Qffiobjectp;
118 Lisp_Object Qffi_translate_to_foreign;
119 Lisp_Object Qffi_translate_from_foreign;
120
121 /* Alist with elements in form (NAME . TYPE) */
122 Lisp_Object Vffi_loaded_libraries;
123 Lisp_Object Vffi_named_types;
124
125 Lisp_Object Vffi_type_checker;
126
127 static Lisp_Object Vffi_all_objects;
128
129 Lisp_Object Qffi_callback;
130
131 static Lisp_Object
132 mark_ffiobject(Lisp_Object obj)
133 {
134         Lisp_EffiObject *ffio = XEFFIO(obj);
135         mark_object(ffio->type);
136         mark_object(ffio->size);
137         mark_object(ffio->plist);
138         return (ffio->plist);
139 }
140
141 static void
142 print_ffiobject(Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
143 {
144         /* This function can GC */
145         Lisp_EffiObject *ffio = XEFFIO(obj);
146         escapeflag = escapeflag;        /* shutup compiler */
147         if (print_readably) {
148 #ifdef SXEMACS
149                 error("printing unreadable object #<ffiobject 0x%x>",
150                       ffio->header.uid);
151 #else
152                 signal_ferror(Qinternal_error,
153                               "printing unreadable object #<ffiobject 0x%x>",
154                               ffio->header.uid);
155 #endif  /* SXEMACS */
156         }
157         WRITE_C_STRING("#<ffiobject ", printcharfun);
158         /* Print FFIO type */
159         if (!NILP(ffio->type)) {
160                 WRITE_C_STRING("type=", printcharfun);
161                 print_internal(ffio->type, printcharfun, 1);
162                 WRITE_C_STRING(" ", printcharfun);
163         }
164         WRITE_FMT_STRING(printcharfun,"size=%ld fotype=%d foptr=%p>",
165                          (long)XINT(ffio->size), ffio->fotype, ffio->fop.generic);
166 }
167
168 static const struct LRECORD_DESCRIPTION ffiobject_description[] = {
169         {XD_LISP_OBJECT, offsetof(Lisp_EffiObject, type)},
170         {XD_LISP_OBJECT, offsetof(Lisp_EffiObject, size)},
171         {XD_LISP_OBJECT, offsetof(Lisp_EffiObject, plist)},
172         {XD_INT, offsetof(Lisp_EffiObject, fotype)},
173         {XD_OPAQUE_PTR, offsetof(Lisp_EffiObject, fop)},
174 #ifdef SXEMACS
175         {XD_SIZE_T, offsetof(Lisp_EffiObject, storage_size)},
176 #else
177         {XD_ELEMCOUNT, offsetof(Lisp_EffiObject, storage_size)},
178 #endif  /* SXEMACS */
179         {XD_END}
180 };
181
182 static Lisp_Object
183 ffi_getprop(Lisp_Object fo, Lisp_Object property)
184 {
185         return external_plist_get(&XEFFIO(fo)->plist, property, 0, ERROR_ME);
186 }
187
188 static int
189 ffi_putprop(Lisp_Object fo, Lisp_Object property, Lisp_Object value)
190 {
191         external_plist_put(&XEFFIO(fo)->plist, property, value, 0, ERROR_ME);
192         return 1;
193 }
194
195 static int
196 ffi_remprop(Lisp_Object fo, Lisp_Object property)
197 {
198         return external_remprop(&XEFFIO(fo)->plist, property, 0, ERROR_ME);
199 }
200
201 #ifdef SXEMACS
202 static size_t
203 sizeof_ffiobject(const void *header)
204 {
205         const Lisp_EffiObject *effio = (const Lisp_EffiObject *)header;
206         return (sizeof(Lisp_EffiObject) + effio->storage_size);
207 }
208 #else
209 static Bytecount
210 sizeof_ffiobject(const void *header)
211 {
212         const Lisp_EffiObject *effio = (const Lisp_EffiObject *)header;
213         return (sizeof(Lisp_EffiObject) + effio->storage_size);
214 }
215 #endif  /* SXEMACS */
216
217 /* Define ffiobject implementation */
218 const struct lrecord_implementation lrecord_ffiobject = {
219         .name = "ffiobject",
220         .marker = mark_ffiobject,
221         .printer = print_ffiobject,
222         .finalizer = 0,
223         .equal = 0,
224         .hash = 0,
225         .description = ffiobject_description,
226         .getprop = ffi_getprop,
227         .putprop = ffi_putprop,
228         .remprop = ffi_remprop,
229         .plist = Fffi_plist,
230         .static_size = 0,
231         .size_in_bytes_method = sizeof_ffiobject,
232         .lrecord_type_index = lrecord_type_ffiobject,
233         .basic_p = 0
234 };
235
236 \f
237 /** alignment in union and structures **/
238 /*
239  * x86:
240  *
241  *   - An entire structure or union is aligned on the same boundary as
242  *     its most strictly aligned member.
243  *
244  *   - Each member is assigned to the lowest available offset with the
245  *     appropriate alignment.  This may require /internal padding/,
246  *     depending on the previous member.
247  *
248  *   - A structure's size is increased, if necessary, to make it a
249  *     multiple of the alignment.  This may require /tail padding/,
250  *     depending on the last member.
251  *
252  *  Internal padding:
253  *
254  *    struct {
255  *     char c;            .-------2+---1+---0.
256  *     short s;           |  s     |pad |  c |
257  *    }                   `--------+----+----'
258  *
259  *  Internal and Tail padding:
260  *
261  *    struct {            .------------1+---0.
262  *     char c;            |     pad     |  c |
263  *     double d;          |-------------+---4|
264  *     short s;           |         d        |
265  *    }                   |-----------------8|
266  *                        |         d        |
267  *                        |------14+-------12|
268  *                        |   pad  |    s    |
269  *                        `--------+---------'
270  *
271  *  Union allocation:
272  *
273  *    union {             .------------1+---0.
274  *     char c;            |     pad     |  c |
275  *     short s;           |-------2+----+---0|
276  *     int j;             |  pad   |    s    |
277  *    }                   |--------+--------0|
278  *                        |        j         |
279  *                        `------------------'
280  */
281 static Lisp_Object
282 ffi_check_type(Lisp_Object type)
283 {
284         return apply1(Vffi_type_checker, Fcons(type, Fcons(Qt, Qnil)));
285 }
286
287 DEFUN("ffi-basic-type-p", Fffi_basic_type_p, 1, 1, 0, /*
288 Return non-nil if TYPE is a basic FFI type.
289
290 A type is said to be basic, if it is neither a pointer nor a
291 function, and there is a corresponding built-in type in C.
292 */
293       (type))
294 {
295         if (EQ(type, Qbyte) || EQ(type, Qunsigned_byte) || EQ(type, Qchar)
296             || EQ(type, Qunsigned_char) || EQ(type, Qshort)
297             || EQ(type, Qunsigned_short) || EQ(type, Qint)
298             || EQ(type, Qunsigned_int) || EQ(type, Qlong)
299             || EQ(type, Qunsigned_long) || EQ(type, Qfloat)
300             || EQ(type, Qdouble) || EQ(type, Qvoid)
301             || EQ(type, Qc_string) || EQ(type, Qc_data)
302             || (CONSP(type) && EQ(XCAR(type), Qc_data)))
303                 return Qt;
304         else
305                 return Qnil;
306 }
307
308
309 static Lisp_Object
310 ffi_canonicalise_type(Lisp_Object type)
311 {
312 /* this function canNOT GC */
313
314         while (!NILP(type) && NILP(Fffi_basic_type_p(type)) && SYMBOLP(type)) {
315                 if EQ(type, Qpointer)
316                         break;
317                 type = Fcdr(Fassq(type, Vffi_named_types));
318         }
319
320         return type;
321 }
322
323 DEFUN("ffi-canonicalise-type", Fffi_canonicalise_type, 1, 1, 0, /*
324 Return FFI type TYPE in a canonical form.
325 */
326       (type))
327 {
328         Lisp_Object canon_type = ffi_canonicalise_type(type);
329         if (NILP(canon_type)) {
330 #ifdef SXEMACS
331                 signal_simple_error("No such FFI type", type);
332 #else
333                 signal_error(Qinternal_error, "No such FFI type", type);
334 #endif  /* SXEMACS */
335         }
336         return canon_type;
337 }
338
339 DEFUN("ffi-size-of-type", Fffi_size_of_type, 1, 1, 0,   /*
340 Return the size of the foreign type TYPE.
341
342 Valid foreign types are: `byte', `unsigned-byte', `char',
343 `unsigned-char', `short', `unsigned-short', `int', `unsigned-int',
344 `long', `unsigned-long', `pointer', `float', `double',
345 `object', and `c-string'.
346 */
347       (type))
348 {
349         int tsize;
350
351         type = ffi_canonicalise_type(type);
352         if (EQ(type, Qvoid))
353                 tsize = 0;
354         else if (EQ(type, Qbyte))
355                 tsize = sizeof(int8_t);
356         else if (EQ(type, Qunsigned_byte))
357                 tsize = sizeof(uint8_t);
358         else if (EQ(type, Qchar))
359                 tsize = sizeof(char);
360         else if (EQ(type, Qunsigned_char))
361                 tsize = sizeof(unsigned char);
362         else if (EQ(type, Qshort))
363                 tsize = sizeof(short);
364         else if (EQ(type, Qunsigned_short))
365                 tsize = sizeof(unsigned short);
366         else if (EQ(type, Qint))
367                 tsize = sizeof(int);
368         else if (EQ(type, Qunsigned_int))
369                 tsize = sizeof(unsigned int);
370         else if (EQ(type, Qlong))
371                 tsize = sizeof(long);
372         else if (EQ(type, Qunsigned_long))
373                 tsize = sizeof(unsigned long);
374         else if (EQ(type, Qfloat))
375                 tsize = sizeof(float);
376         else if (EQ(type, Qdouble))
377                 tsize = sizeof(double);
378         else if (EQ(type, Qc_string))
379                 tsize = sizeof(char *);
380         else if (FFI_POINTERP(type))
381                 tsize = sizeof(void *);
382         else if (EQ(type, Qc_data))
383                 tsize = sizeof(void *);
384         else if (CONSP(type) && EQ(XCAR(type), Qc_data)) {
385                 Lisp_Object cdsize = XCDR(type);
386                 CHECK_INT(cdsize);
387                 tsize = XINT(cdsize);
388         } else if (CONSP(type) && EQ(XCAR(type), Qfunction))
389                 tsize = sizeof(void(*));
390         else if (CONSP(type) && EQ(XCAR(type), Qarray)) {
391                 Lisp_Object atype = Fcar(XCDR(type));
392                 Lisp_Object asize = Fcar(Fcdr(XCDR(type)));
393
394                 CHECK_INT(asize);
395                 tsize = XINT(asize) * XINT(Fffi_size_of_type(atype));
396         } else if (CONSP(type) && EQ(XCAR(type), Qstruct)) {
397                 return Fffi_slot_offset(type, Qnil);
398         } else if (CONSP(type) && EQ(XCAR(type), Qunion)) {
399                 Lisp_Object slots = Fcdr(XCDR(type));
400
401                 CHECK_CONS(slots);
402
403                 tsize = 0;
404                 while (!NILP(slots)) {
405                         Lisp_Object slot_type = Fcar(Fcdr(XCAR(slots)));
406                         int slot_size = XINT(Fffi_size_of_type(slot_type));
407                         if (slot_size > tsize)
408                                 tsize = slot_size;
409                         slots = XCDR(slots);
410                 }
411         } else {
412 #ifdef SXEMACS
413                 signal_simple_error("Unrecognized foreign type", type);
414 #else
415                 signal_error(Qinternal_error, "Unrecognized foreign type", type);
416 #endif  /* SXEMACS */
417         }
418
419         return make_int(tsize);
420 }
421
422 DEFUN("make-ffi-object", Fmake_ffi_object, 1, 2, 0, /*
423 Create a new FFI object of type TYPE.
424 If optional argument SIZE is non-nil it should be an
425 integer, in this case additional storage size to hold data
426 of at least length SIZE is allocated.
427 */
428       (type, size))
429 {
430         int cs_or_cd;
431         Lisp_Object ctype;
432         Lisp_Object result = Qnil;
433         Lisp_EffiObject *ffio;
434         struct gcpro gcpro1;
435
436         GCPRO1(result);
437
438         /* NOTE: ffi_check_type returns canonical type */
439         ctype = ffi_check_type(type);
440         if (NILP(size))
441                 size = Fffi_size_of_type(type);
442         CHECK_INT(size);
443
444         if (CONSP(ctype) && EQ(XCAR(ctype), Qc_data) && INTP(XCDR(ctype)))
445                 size = XCDR(type);
446
447         cs_or_cd = EQ(ctype, Qc_string) || (EQ(ctype, Qc_data));
448         if ((cs_or_cd && (XINT(size) < 1))
449             || (!(cs_or_cd || FFI_POINTERP(ctype))
450                 && (XINT(size) < XINT(Fffi_size_of_type(type)))))
451 #ifdef SXEMACS
452                 signal_simple_error("storage size too small to store type",
453                                     list2(size, type));
454
455         ffio = alloc_lcrecord(sizeof(Lisp_EffiObject)+XINT(size),
456                               &lrecord_ffiobject);
457         XSETEFFIO(result, ffio);
458 #else
459                 signal_error(Qinternal_error,
460                              "storage size too small to store type",
461                              list2(size, type));
462
463         ffio = old_basic_alloc_lcrecord(sizeof(Lisp_EffiObject)+XINT(size),
464                                         &lrecord_ffiobject);
465         result = wrap_effio(ffio);
466 #endif  /* SXEMACS */
467
468         ffio->size = Fffi_size_of_type(type);
469         ffio->type = type;
470         ffio->plist = Qnil;
471
472         /* Initialize foreign pointer */
473         ffio->fotype = EFFI_FOT_NONE;
474         ffio->storage_size = XINT(size);
475         ffio->fop.ptr = ffio->fostorage;
476
477         if (!NILP(Vffi_all_objects))
478                 XWEAK_LIST_LIST(Vffi_all_objects) =
479                         Fcons(result, XWEAK_LIST_LIST(Vffi_all_objects));
480
481         RETURN_UNGCPRO(result);
482 }
483
484 DEFUN("ffi-object-p", Fffi_object_p, 1, 1, 0, /*
485 Return non-nil if FO is an FFI object, nil otherwise.
486 */
487       (fo))
488 {
489         return (EFFIOP(fo) ? Qt : Qnil);
490 }
491
492 DEFUN("ffi-object-address", Fffi_object_address, 1, 1, 0, /*
493 Return the address FO points to.
494 */
495       (fo))
496 {
497         CHECK_EFFIO(fo);
498         return make_float((long)XEFFIO(fo)->fop.ptr);
499 }
500
501 DEFUN("ffi-make-pointer", Fffi_make_pointer, 1, 1, 0, /*
502   "Return a pointer pointing to ADDRESS."
503 */
504       (address))
505 {
506         long addr;
507         Lisp_Object ptr;
508
509         if (INTP(address))
510                 addr = XINT(address);
511         else if (FLOATP(address))
512                 addr = XFLOATINT(address);
513         else {
514 #ifdef SXEMACS
515                 signal_simple_error("FFI: invalid address type", address);
516 #else
517                 signal_error(Qinternal_error, "FFI: invalid address type",
518                              address);
519 #endif  /* SXEMACS */
520         }
521
522         ptr = Fmake_ffi_object(Qpointer, Qnil);
523         XEFFIO(ptr)->fop.ptr = (void*)addr;
524         return ptr;
525 }
526
527 DEFUN("ffi-object-canonical-type", Fffi_object_canonical_type, 1, 1, 0, /*
528 Return FO's real type, that is after resolving user defined types.
529 */
530       (fo))
531 {
532         CHECK_EFFIO(fo);
533         return ffi_canonicalise_type(XEFFIO(fo)->type);
534 }
535
536 DEFUN("ffi-object-type", Fffi_object_type, 1, 1, 0, /*
537 Return FO's type.
538 */
539       (fo))
540 {
541         CHECK_EFFIO(fo);
542         return (XEFFIO(fo)->type);
543 }
544
545 DEFUN("ffi-set-object-type", Fffi_set_object_type, 2, 2, 0, /*
546 Cast FO to type TYPE and reassign the cast value.
547 */
548       (fo, type))
549 {
550         CHECK_EFFIO(fo);
551
552         ffi_check_type(type);
553         XEFFIO(fo)->type = type;
554
555         return type;
556 }
557
558 DEFUN("ffi-object-size", Fffi_object_size, 1, 1, 0, /*
559 Return the size of the allocated space of FO.
560 */
561       (fo))
562 {
563         CHECK_EFFIO(fo);
564         return (XEFFIO(fo)->size);
565 }
566
567 DEFUN("ffi-set-storage-size", Fffi_set_storage_size, 2, 2, 0, /*
568 Set the size of the allocated space of FO.
569 */
570       (fo, size))
571 {
572         CHECK_EFFIO(fo);
573         CHECK_INT(size);
574         XEFFIO(fo)->storage_size = XUINT(size);
575         return Qt;
576 }
577
578 DEFUN("ffi-load-library", Fffi_load_library, 1, 1, 0, /*
579 Load library LIBNAME and return a foreign object handle if successful,
580 or `nil' if the library cannot be loaded.
581
582 The argument LIBNAME should be the file-name string of a shared object
583 library.  Normally you should omit the file extension, as this
584 function will add the appripriate extension for the current platform
585 if one is missing.
586
587 The library should reside in one of the directories specified by the
588 $LD_LIBRARY_PATH environment variable or the more global ld.so.cache.
589 */
590       (libname))
591 {
592
593 #ifdef LTDL_SHLIB_EXT
594 #  define EXT LTDL_SHLIB_EXT
595 #elif defined(HAVE_DYLD) || defined(HAVE_MACH_O_DYLD_H)
596 #    define EXT ".dylib"
597 #  else
598 #    define EXT ".so"
599 #endif  /* LTDL_SHLIB_EXT */
600
601         void *handler, *dotpos;
602         Lisp_Object fo = Qnil;
603         Lisp_EffiObject *ffio;
604         struct gcpro gcpro1;
605         char *soname = NULL;
606
607         CHECK_STRING(libname);
608
609         /* Add an extension if we need to */
610         dotpos = strrchr((char *)XSTRING_DATA(libname),'.');
611         if ( dotpos == NULL || strncmp(dotpos, EXT, sizeof(EXT))) {
612                 ssize_t liblen = XSTRING_LENGTH(libname);
613                 ssize_t soname_len = liblen + sizeof(EXT) + 1;
614                 soname = xmalloc( soname_len);
615                 xstrncpy(soname, (char *)XSTRING_DATA(libname), soname_len);
616                 xstrncpy(soname+liblen, EXT, soname_len-liblen);
617         }
618
619         if ( soname == NULL ) {
620                 handler = dlopen((const char *)XSTRING_DATA(libname),
621                                  RTLD_GLOBAL|RTLD_NOW);
622         } else {
623                 handler = dlopen(soname, RTLD_GLOBAL|RTLD_NOW);
624                 xfree(soname);
625         }
626
627         if (handler == NULL)
628                 return Qnil;
629
630         GCPRO1(fo);
631         fo = Fmake_ffi_object(Qpointer, Qnil);
632         ffio = XEFFIO(fo);
633
634         ffio->fotype = EFFI_FOT_BIND;
635         ffio->fop.ptr = handler;
636
637         RETURN_UNGCPRO(fo);
638 }
639
640 DEFUN("ffi-bind", Fffi_bind, 2, 2, 0, /*
641 Make and return a foreign object of type TYPE and bind it to the
642 external symbol SYM.
643
644 The argument TYPE can be any type-cell.
645 The argument SYM should be a string naming an arbitrary symbol
646 in one of the loaded libraries.
647
648 If SYM does not exist in any of the loaded libraries, `nil' is
649 returned.
650 */
651       (type, sym))
652 {
653         Lisp_Object fo = Qnil;
654         Lisp_EffiObject *ffio;
655         struct gcpro gcpro1;
656
657         ffi_check_type(type);
658         CHECK_STRING(sym);
659
660         GCPRO1(fo);
661         fo = Fmake_ffi_object(type, Qnil);
662         ffio = XEFFIO(fo);
663         ffio->fop.ptr = dlsym(RTLD_DEFAULT, (const char*)XSTRING_DATA(sym));
664         if (ffio->fop.ptr == NULL) {
665                 UNGCPRO;
666                 return Qnil;
667         }
668
669         ffio->fotype = EFFI_FOT_BIND;
670
671         RETURN_UNGCPRO(fo);
672 }
673
674 DEFUN("ffi-dlerror", Fffi_dlerror, 0, 0, 0, /*
675 Return dl error string.
676 */
677       ())
678 {
679         const char *dles = dlerror();
680
681         if (LIKELY(dles != NULL)) {
682                 size_t sz = strlen(dles);
683                 return make_ext_string((const Extbyte*)dles, sz, EFFI_CODING);
684         } else {
685                 return Qnil;
686         }
687 }
688
689 DEFUN("ffi-defun", Fffi_defun, 2, 2, 0, /*
690 Make and return a foreign object of type TYPE and bind it to the
691 external symbol SYM.
692
693 The argument TYPE should be a function type-cell.
694 The argument SYM should be a string naming a function in one of
695 the loaded libraries.
696
697 If SYM does not exist in any of the loaded libraries, an error
698 is indicated.
699
700 This is like `ffi-bind' but for function objects.
701 */
702       (type, sym))
703 {
704         Lisp_Object fo = Qnil;
705         Lisp_EffiObject *ffio;
706         struct gcpro gcpro1;
707
708         ffi_check_type(type);
709         CHECK_STRING(sym);
710
711         GCPRO1(fo);
712
713         fo = Fmake_ffi_object(type, Qnil);
714         ffio = XEFFIO(fo);
715         ffio->fop.fun = dlsym(RTLD_DEFAULT, (const char *)XSTRING_DATA(sym));
716         if (ffio->fop.fun == NULL) {
717 #ifdef SXEMACS
718                 signal_simple_error("Can't define function", sym);
719 #else
720                 signal_error(Qinternal_error, "Can't define function", sym);
721 #endif  /* SXEMACS */
722         }
723
724         ffio->fotype = EFFI_FOT_FUNC;
725
726         RETURN_UNGCPRO(fo);
727 }
728
729 /*
730  * Return alignment policy for struct or union FFI_SU.
731  * x86: Return 1, 2 or 4.
732  * mips: Return 1, 2, 4 or 8.
733  */
734 static int
735 ffi_type_align(Lisp_Object type)
736 {
737         type = ffi_canonicalise_type(type);
738         if (SYMBOLP(type)) {
739                 if (EQ(type, Qbyte) || EQ(type, Qunsigned_byte)
740                     || EQ(type, Qchar) || EQ(type, Qunsigned_char))
741                         return 1;
742                 if (EQ(type, Qshort) || EQ(type, Qunsigned_short))
743                         return 2;
744 #ifdef FFI_MIPS
745                 if (EQ(type, Qdouble))
746                         return 8;
747 #endif  /* FFI_MIPS */
748                 return 4;
749                 /* NOT REACHED */
750         } else if (CONSP(type)
751                    && (EQ(XCAR(type), Qstruct) || EQ(XCAR(type), Qunion))) {
752                 int al;
753
754                 for (al = 0, type = Fcdr(Fcdr(type));
755                      !NILP(type);
756                      type = Fcdr(type))
757                 {
758                         Lisp_Object stype = Fcar(Fcdr(Fcar(type)));
759                         int tmp_al = ffi_type_align(stype);
760
761                         if (tmp_al > al)
762                                 al = tmp_al;
763                 }
764
765                 return al;
766         }
767
768         return 4;
769 }
770
771 DEFUN("ffi-type-alignment", Fffi_type_alignment, 1, 1, 0, /*
772 Return TYPE alignment.
773 */
774       (type))
775 {
776         return make_int(ffi_type_align(type));
777 }
778
779 DEFUN("ffi-slot-offset", Fffi_slot_offset, 2, 2, 0, /*
780 Return the offset of SLOT in TYPE.
781 SLOT can be either a valid (named) slot in TYPE or `nil'.
782 If SLOT is `nil' return the size of the struct.
783 */
784       (type, slot))
785 {
786         Lisp_Object slots;
787         int lpad, align, retoff;
788
789         type = ffi_canonicalise_type(type);
790         if (!CONSP(type)) {
791 #ifdef SXEMACS
792                 error("Not struct or union");
793 #else
794                 Fsignal(Qwrong_type_argument,
795                         list2(Qstringp, build_string("Not struct or union")));
796 #endif  /* SXEMACS */
797         }
798
799         retoff = 0;
800         lpad = align = ffi_type_align(type);
801         slots = Fcdr(XCDR(type));
802         CHECK_CONS(slots);
803         while (!NILP(slots)) {
804                 Lisp_Object tmp_slot = Fcar(Fcdr(XCAR(slots)));
805                 int tmp_align;
806                 int tmp_size;
807
808                 /*
809                  * NOTE:
810                  *  - for basic types TMP_ALIGN and TMP_SIZE are equal
811                  */
812                 tmp_align = ffi_type_align(tmp_slot);
813
814                 if (EQ(XCAR(XCAR(slots)), slot)) {
815                         /* SLOT found */
816                         /* TODO: add support for :offset keyword in SLOT */
817                         if (lpad < tmp_align) {
818                                 retoff += lpad;
819                                 lpad = 0;
820                         } else
821                                 lpad -= tmp_align;
822                         break;
823                 }
824
825                 tmp_size = XINT(Fffi_size_of_type(tmp_slot));
826                 while (tmp_size > 0) {
827                         if (lpad < tmp_align) {
828                                 retoff += lpad;
829                                 lpad = align;
830                         }
831                         tmp_size -= tmp_align;
832                         lpad -= tmp_align;
833                         retoff += tmp_align;
834                 }
835
836                 slots = XCDR(slots);
837         }
838         if (NILP(slots) && !NILP(slot)) {
839 #ifdef SXEMACS
840                 signal_simple_error("FFI: Slot not found", slot);
841 #else
842                 signal_error(Qinternal_error, "FFI: Slot not found", slot);
843 #endif  /* SXEMACS */
844         }
845         return make_int(retoff + lpad);
846 }
847
848 /*
849  * TYPE must be already canonicalised
850  */
851 static Lisp_Object
852 ffi_fetch_foreign(void *ptr, Lisp_Object type)
853 {
854 /* this function canNOT GC */
855         Lisp_Object retval = Qnone;
856
857         if (EQ(type, Qchar))
858                 retval = make_char(*(char*)ptr);
859         else if (EQ(type, Qunsigned_char))
860                 retval = make_char(*(char unsigned*)ptr);
861         else if (EQ(type, Qbyte))
862                 retval = make_int(*(char*)ptr);
863         else if (EQ(type, Qunsigned_byte))
864                 retval = make_int(*(unsigned char*)ptr);
865         else if (EQ(type, Qshort))
866                 retval = make_int(*(short*)ptr);
867         else if (EQ(type, Qunsigned_short))
868                 retval = make_int(*(unsigned short*)ptr);
869         else if (EQ(type, Qint))
870                 retval = make_int(*(int*)ptr);
871         else if (EQ(type, Qunsigned_int))
872                 retval = make_int(*(unsigned int*)ptr);
873         else if (EQ(type, Qlong))
874                 retval = make_int(*(long*)ptr);
875         else if (EQ(type, Qunsigned_long))
876                 retval = make_int(*(unsigned long*)ptr);
877         else if (EQ(type, Qfloat))
878                 retval = make_float(*(float*)ptr);
879         else if (EQ(type, Qdouble))
880                 retval = make_float(*(double*)ptr);
881         else if (EQ(type, Qc_string)) {
882                 retval = build_ext_string((char*)ptr, Qbinary);
883         } else if (EQ(type, Qvoid)) {
884                 retval = Qnil;
885         } else if (FFI_POINTERP(type)) {
886                 retval = Fmake_ffi_object(type, Qnil);
887                 XEFFIO(retval)->fop.ptr = *(void**)ptr;
888         } else if (CONSP(type) && EQ(XCAR(type), Qfunction)) {
889                 retval = Fmake_ffi_object(type, Qnil);
890                 XEFFIO(retval)->fop.fun = (void*)ptr;
891                 XEFFIO(retval)->fotype = EFFI_FOT_FUNC;
892         }
893
894         return retval;
895 }
896
897 DEFUN("ffi-fetch", Fffi_fetch, 3, 3, 0, /*
898 Fetch value from the foreign object FO from OFFSET position.
899 TYPE specifies value for data to be fetched.
900 */
901       (fo, offset, type))
902 {
903         Lisp_Object origtype = type;
904         Lisp_Object retval = Qnil;
905         Lisp_EffiObject *ffio;
906         void *ptr;
907         struct gcpro gcpro1;
908
909         CHECK_EFFIO(fo);
910         CHECK_INT(offset);
911
912         ffio = XEFFIO(fo);
913         ptr = (void*)((char*)ffio->fop.ptr + XINT(offset));
914
915         type = ffi_canonicalise_type(type);
916
917         GCPRO1(retval);
918         /* Fetch value and translate it according to translators */
919         retval = ffi_fetch_foreign(ptr, type);
920         if (EQ(retval, Qnone)) {
921                 /* Special case for c-data */
922                 if (EQ(type, Qc_data) ||
923                     (CONSP(type) && EQ(XCAR(type), Qc_data)))
924                 {
925                         size_t tlen;
926                         if (EQ(type, Qc_data)) {
927                                 tlen = ffio->storage_size - XINT(offset);
928                         } else {
929                                 CHECK_INT(XCDR(type));
930                                 tlen = XUINT(XCDR(type));
931                         }
932
933                         retval = make_ext_string(ptr, tlen, Qbinary);
934                 } else {
935 #ifdef SXEMACS
936                         signal_simple_error("Can't fetch for this type", origtype);
937 #else
938                         signal_error(Qinternal_error, "Can't fetch for this type",
939                                      origtype);
940 #endif  /* SXEMACS */
941                 }
942         }
943         retval = apply1(Findirect_function(Qffi_translate_from_foreign),
944                         list2(retval, origtype));
945
946         RETURN_UNGCPRO(retval);
947 }
948
949 DEFUN("ffi-aref", Fffi_aref, 2, 2, 0, /*
950 Return the element of FARRAY at index IDX (starting with 0).
951 */
952       (farray, idx))
953 {
954         Lisp_Object type;
955
956         CHECK_EFFIO(farray);
957         CHECK_INT(idx);
958
959         type = ffi_canonicalise_type(XEFFIO(farray)->type);
960         if (!FFI_TPTR(type)) {
961 #ifdef SXEMACS
962                 signal_simple_error("Not an array type", type);
963 #else
964                 signal_error(Qinternal_error, "Not an array type", type);
965 #endif  /* SXEMACS */
966         }
967         if (EQ(type, Qc_string))
968                 type = Qchar;
969         else
970                 type = Fcar(XCDR(type));
971
972         return Fffi_fetch(farray,
973                           make_int(XINT(Fffi_size_of_type(type)) * XINT(idx)),
974                           type);
975 }
976
977 DEFUN("ffi-store", Fffi_store, 4, 4, 0, /*
978 For foreign object FO at specified OFFSET store data.
979 Type of data is specified by VAL-TYPE and data itself specified in VAL.
980
981 VAL-TYPE can be either a basic FFI type or an FFI pointer.
982 If VAL-TYPE is a basic FFI type, then VAL can be an
983 ordinary, but suitable Emacs lisp object.
984 If VAL-TYPE is an FFI pointer then VAL _must_ be an FFI
985 object of the underlying type pointed to.
986 */
987       (fo, offset, val_type, val))
988 {
989         Lisp_Object origtype = val_type;
990         Lisp_EffiObject *ffio;
991         void *ptr;
992
993         CHECK_EFFIO(fo);
994         CHECK_INT(offset);
995
996         ffio = XEFFIO(fo);
997         ptr = (void*)((char*)ffio->fop.ptr + XINT(offset));
998
999         val_type = ffi_canonicalise_type(val_type);
1000
1001         /* Translate value */
1002         val = apply1(Findirect_function(Qffi_translate_to_foreign),
1003                      list2(val, origtype));
1004
1005         if (EQ(val_type, Qchar) || EQ(val_type, Qunsigned_char)) {
1006                 if (!CHARP(val)) {
1007                         SIGNAL_ERROR(Qwrong_type_argument,
1008                                      list2(Qcharacterp, val));
1009                 }
1010                 *(char*)ptr = XCHAR(val);
1011         } else if (EQ(val_type, Qbyte) || EQ(val_type, Qunsigned_byte)) {
1012                 if (!INTP(val)) {
1013                         SIGNAL_ERROR(Qwrong_type_argument,
1014                                      list2(Qintegerp, val));
1015                 }
1016                 *(char*)ptr = XINT(val);
1017         } else if (EQ(val_type, Qshort) || EQ(val_type, Qunsigned_short)) {
1018                 if (!INTP(val)) {
1019                         SIGNAL_ERROR(Qwrong_type_argument,
1020                                      list2(Qintegerp, val));
1021                 }
1022                 *(short*)ptr = (short)XINT(val);
1023         } else if (EQ(val_type, Qint) || EQ(val_type, Qunsigned_int)) {
1024                 if (INTP(val)) {
1025                         *(int*)ptr = XINT(val);
1026                 } else if (FLOATP(val)) {
1027                         fpfloat tmp = XFLOATINT(val);
1028                         *(int*)ptr = (int)tmp;
1029                 } else {
1030                         SIGNAL_ERROR(Qwrong_type_argument,
1031                                      list2(Qfloatp, val));
1032                 }
1033         } else if (EQ(val_type, Qlong) || EQ(val_type, Qunsigned_long)) {
1034                 if (INTP(val)) {
1035                         *(long*)ptr = (long)XINT(val);
1036                 } else if (FLOATP(val)) {
1037                         fpfloat tmp = XFLOATINT(val);
1038                         *(long*)ptr = (long int)tmp;
1039                 } else {
1040                         SIGNAL_ERROR(Qwrong_type_argument, list2(Qfloatp, val));
1041                 }
1042         } else if (EQ(val_type, Qfloat)) {
1043                 if (!FLOATP(val))
1044                         SIGNAL_ERROR(Qwrong_type_argument, list2(Qfloatp, val));
1045                 *(float*)ptr = XFLOATINT(val);
1046         } else if (EQ(val_type, Qdouble)) {
1047                 if (!FLOATP(val))
1048                         SIGNAL_ERROR(Qwrong_type_argument, list2(Qfloatp, val));
1049                 *(double*)ptr = XFLOAT_DATA(val);
1050         } else if (EQ(val_type, Qc_string)) {
1051                 char *tmp = NULL;
1052                 int tmplen;
1053                 if (!STRINGP(val))
1054                         SIGNAL_ERROR(Qwrong_type_argument, list2(Qstringp, val));
1055 #if defined(MULE)
1056                 TO_EXTERNAL_FORMAT(LISP_STRING, val,
1057                                    ALLOCA, (tmp, tmplen), Qnil);
1058                 if ( tmp != NULL ) {
1059                              memcpy((char*)ptr, tmp, tmplen + 1);
1060                 }
1061 #else
1062                 memcpy((char*)ptr,
1063                        (const char *)XSTRING_DATA(val),
1064                        XSTRING_LENGTH(val) + 1);
1065 #endif
1066         } else if (EQ(val_type, Qc_data) ||
1067                    (CONSP(val_type) &&
1068                     EQ(XCAR(val_type), Qc_data) && INTP(XCDR(val_type)))) {
1069                 char *val_ext = NULL;
1070                 unsigned int val_ext_len;
1071                 if (!STRINGP(val))
1072                         SIGNAL_ERROR(Qwrong_type_argument, list2(Qstringp, val));
1073
1074                 TO_EXTERNAL_FORMAT(LISP_STRING, val, ALLOCA,
1075                                    (val_ext, val_ext_len), Qbinary);
1076                 if (val_ext == NULL ||
1077                     (CONSP(val_type) && (val_ext_len > XINT(XCDR(val_type))))) {
1078 #ifdef SXEMACS
1079                         error("storage size too small");
1080 #else
1081                         Fsignal(Qrange_error,
1082                                 list2(Qstringp,
1083                                       build_string("storage size too small")));
1084 #endif  /* SXEMACS */
1085                 } else {
1086                         memcpy((char*)ptr, (const char *)val_ext, val_ext_len);
1087                 }
1088         } else if (FFI_POINTERP(val_type)) {
1089                 if (!EFFIOP(val)) {
1090 #ifdef SXEMACS
1091                         signal_simple_error("FFI: Value not of pointer type", \
1092                                             list2(origtype, val));
1093 #else
1094                         Fsignal(Qwrong_type_argument,
1095                                 list2(Qstringp, build_string("type")));
1096 #endif  /* SXEMACS */
1097                 }
1098                 *(void**)ptr = (void*)XEFFIO(val)->fop.ptr;
1099         } else if (CONSP(val_type) && EQ(XCAR(val_type), Qstruct)) {
1100                 if (!EFFIOP(val)) {
1101 #ifdef SXEMACS
1102                         signal_simple_error("FFI: Value not FFI object", \
1103                                             list2(origtype, val));
1104 #else
1105                         Fsignal(Qwrong_type_argument,
1106                                 list2(Qstringp, build_string("type")));
1107 #endif  /* SXEMACS */
1108                 }
1109                 memcpy((char*)ptr, (const char *)XEFFIO(val)->fop.ptr, \
1110                        XINT(Fffi_size_of_type(val_type)));
1111         } else {
1112 #ifdef SXEMACS
1113                 signal_simple_error("FFI: Non basic or pointer type", origtype);
1114 #else
1115                 Fsignal(Qinternal_error,
1116                         list2(Qstringp,
1117                               build_string("non basic or pointer type")));
1118 #endif  /* SXEMACS */
1119         }
1120
1121         return val;
1122 }
1123
1124 DEFUN("ffi-aset", Fffi_aset, 3, 3, 0, /*
1125 Store the element VALUE in FARRAY at index IDX (starting with 0).
1126 */
1127       (farray, idx, value))
1128 {
1129         Lisp_Object type;
1130
1131         CHECK_EFFIO(farray);
1132         CHECK_INT(idx);
1133
1134         type = ffi_canonicalise_type(XEFFIO(farray)->type);
1135         if (!FFI_TPTR(type)) {
1136 #ifdef SXEMACS
1137                 signal_simple_error("Not an array type", type);
1138 #else
1139                 signal_error(Qinternal_error, "Not an array type", type);
1140 #endif  /* SXEMACS */
1141         }
1142         if (EQ(type, Qc_string))
1143                 type = Qchar;
1144         else
1145                 type = Fcar(XCDR(type));
1146
1147         return Fffi_store(farray,
1148                           make_int(XINT(Fffi_size_of_type(type)) * XINT(idx)),
1149                           type, value);
1150 }
1151
1152 DEFUN("ffi-address-of", Fffi_address_of, 1, 1, 0, /*
1153 Return the FFI object that stores the address of given FFI object FO.
1154
1155 This is the equivalent of the `&' operator in C.
1156 */
1157       (fo))
1158 {
1159         Lisp_Object newfo = Qnil;
1160         Lisp_EffiObject *ffio, *newffio;
1161         struct gcpro gcpro1;
1162
1163         CHECK_EFFIO(fo);
1164         ffio = XEFFIO(fo);
1165
1166         GCPRO1(newfo);
1167         newfo = Fmake_ffi_object(Qpointer, Qnil);
1168         newffio = XEFFIO(newfo);
1169
1170         newffio->fotype = EFFI_FOT_BIND;
1171         if (FFI_TPTR(ffio->type))
1172                 newffio->fop.ptr = (void*)&ffio->fop.ptr;
1173         else
1174                 newffio->fop.ptr = ffio->fop.ptr;
1175
1176         RETURN_UNGCPRO(newfo);
1177 }
1178
1179 DEFUN("ffi-lisp-object-to-pointer", Fffi_lisp_object_to_pointer, 1, 1, 0, /*
1180 Convert lisp object to FFI pointer.
1181 */
1182       (obj))
1183 {
1184         Lisp_Object newfo = Qnil;
1185         Lisp_EffiObject *newffio;
1186         struct gcpro gcpro1;
1187
1188         GCPRO1(obj);
1189
1190         newfo = Fmake_ffi_object(Qpointer, Qnil);
1191         newffio = XEFFIO(newfo);
1192         newffio->fotype = EFFI_FOT_BIND;
1193         newffio->fop.ptr = (void*)obj;
1194
1195         /* Hold a reference to OBJ in NEWFO's plist */
1196         Fput(newfo, intern("lisp-object"), obj);
1197
1198         RETURN_UNGCPRO(newfo);
1199 }
1200
1201 DEFUN("ffi-pointer-to-lisp-object", Fffi_pointer_to_lisp_object, 1, 1, 0, /*
1202 Convert FFI pointer to lisp object.
1203 */
1204       (ptr))
1205 {
1206         CHECK_EFFIO(ptr);
1207         return (Lisp_Object)XEFFIO(ptr)->fop.ptr;
1208 }
1209
1210 DEFUN("ffi-plist", Fffi_plist, 1, 1, 0, /*
1211 Return properties list for FFI object FO.
1212 */
1213       (fo))
1214 {
1215         CHECK_EFFIO(fo);
1216         return (XEFFIO(fo)->plist);
1217 }
1218
1219 #ifdef HAVE_LIBFFI
1220
1221 static int lf_cindex = 0;
1222
1223 /*
1224  * XXX
1225  *  This will work in most cases.
1226  *  However it might not work for large structures,
1227  *  In general we should allocate these spaces dynamically
1228  */
1229 #define MAX_TYPES_VALUES 1024
1230 /* ex_ffitypes_dummies used for structure types */
1231 static ffi_type ex_ffitypes_dummies[MAX_TYPES_VALUES + 1];
1232 static ffi_type *ex_ffitypes[MAX_TYPES_VALUES + 1];
1233 static void *ex_values[MAX_TYPES_VALUES + 1];
1234
1235 #if SIZEOF_LONG == 4
1236 #  define effi_type_ulong ffi_type_uint32
1237 #  define effi_type_slong ffi_type_sint32
1238 #elif SIZEOF_LONG == 8
1239 #  define effi_type_ulong ffi_type_uint64
1240 #  define effi_type_slong ffi_type_sint64
1241 #endif
1242
1243 static void
1244 extffi_setup_argument(Lisp_Object type, ffi_type **ft)
1245 {
1246         type = ffi_canonicalise_type(type);
1247         if (EQ(type, Qchar) || EQ(type, Qbyte))
1248                 *ft = &ffi_type_schar;
1249         else if (EQ(type, Qunsigned_char) || EQ(type, Qunsigned_byte))
1250                 *ft = &ffi_type_uchar;
1251         else if (EQ(type, Qshort))
1252                 *ft = &ffi_type_sshort;
1253         else if (EQ(type, Qunsigned_short))
1254                 *ft = &ffi_type_ushort;
1255         else if (EQ(type, Qint))
1256                 *ft = &ffi_type_sint;
1257         else if (EQ(type, Qunsigned_int))
1258                 *ft = &ffi_type_uint;
1259         else if (EQ(type, Qunsigned_long))
1260                 *ft = &effi_type_ulong;
1261         else if (EQ(type, Qlong))
1262                 *ft = &effi_type_slong;
1263         else if (EQ(type, Qfloat))
1264                 *ft = &ffi_type_float;
1265         else if (EQ(type, Qdouble))
1266                 *ft = &ffi_type_double;
1267         else if (EQ(type, Qvoid))
1268                 *ft = &ffi_type_void;
1269         else if (FFI_TPTR(type))
1270                 *ft = &ffi_type_pointer;
1271         else if (CONSP(type) && EQ(XCAR(type), Qstruct)) {
1272                 Lisp_Object slots = Fcdr(XCDR(type));
1273                 ffi_type **ntypes;
1274                 int nt_size, i;
1275
1276                 CHECK_CONS(slots);
1277
1278                 nt_size = XINT(Flength(slots)) + 1;
1279                 if (nt_size + lf_cindex > MAX_TYPES_VALUES) {
1280                         lf_cindex = 0;  /* reset cindex */
1281 #ifdef SXEMACS
1282                         error("cindex overflow");
1283 #else
1284                         Fsignal(Qoverflow_error,
1285                                 list2(Qstringp,
1286                                       build_string("cindex overflow")));
1287 #endif  /* SXEMACS */
1288                 }
1289                 ntypes = &ex_ffitypes[lf_cindex];
1290                 *ft = &ex_ffitypes_dummies[lf_cindex];
1291
1292                 /* Update lf_cindex in case TYPE struct contains other
1293                  * structures */
1294                 lf_cindex += nt_size;
1295
1296                 (*ft)->type = FFI_TYPE_STRUCT;
1297                 (*ft)->alignment = ffi_type_align(type);
1298                 (*ft)->elements = ntypes;
1299
1300                 for (i = 0; (i < nt_size) && !NILP(slots); slots = XCDR(slots), i++)
1301                         extffi_setup_argument(Fcar(Fcdr(XCAR(slots))), &ntypes[i]);
1302                 ntypes[i] = NULL;
1303         } else {
1304 #ifdef SXEMACS
1305                 signal_simple_error("Can't setup argument for type", type);
1306 #else
1307                 signal_error(Qinternal_error,
1308                              "Can't setup argument for type", type);
1309 #endif  /* SXEMACS */
1310         }
1311 }
1312
1313 static int
1314 ffi_call_using_libffi(Lisp_Object fo_fun, Lisp_Object ret_fo,
1315                       int in_nargs, Lisp_Object *in_args)
1316 {
1317         Lisp_EffiObject *ffio;
1318         Lisp_Object fft;
1319         ffi_cif cif;
1320         ffi_type *rtype;
1321         void *rvalue;
1322         int i;
1323
1324         lf_cindex = in_nargs;           /* reserve */
1325         for (i = 0; i < in_nargs; i++) {
1326                 ffio = XEFFIO(in_args[i]);
1327                 fft = Fffi_canonicalise_type(ffio->type);
1328                 extffi_setup_argument(fft, &ex_ffitypes[i]);
1329                 if (FFI_TPTR(fft))
1330                         ex_values[i] = &ffio->fop.ptr;
1331                 else
1332                         ex_values[i] = ffio->fop.ptr;
1333         }
1334
1335         ffio = XEFFIO(ret_fo);
1336         fft = Fffi_canonicalise_type(ffio->type);
1337         extffi_setup_argument(fft, &rtype);
1338         if (FFI_TPTR(fft))
1339                 rvalue = &ffio->fop.ptr;
1340         else
1341                 rvalue = ffio->fop.ptr;
1342
1343         if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, in_nargs,
1344                          rtype, ex_ffitypes) == FFI_OK)
1345         {
1346                 stop_async_timeouts();
1347                 ffi_call(&cif, (void(*)(void))XEFFIO(fo_fun)->fop.fun, rvalue,
1348                          ex_values);
1349                 start_async_timeouts();
1350                 return 0;
1351         }
1352
1353         /* FAILURE */
1354         return 1;
1355 }
1356 #endif  /* HAVE_LIBFFI */
1357
1358 DEFUN("ffi-call-function", Fffi_call_function, 1, MANY, 0, /*
1359 Call a function referred to by FO with arguments ARGS, maybe
1360 return a foreign object with the result or nil if there is
1361 none.
1362
1363 Arguments are: FO &rest FO-ARGS
1364
1365 FO should be a foreign binding initiated by `ffi-defun', and
1366 ARGS should be foreign data objects or pointers to these.
1367 */
1368       (int nargs, Lisp_Object * args))
1369 {
1370         Lisp_Object faf = Qnil, retfo = Qnil;
1371         Lisp_EffiObject *ffio;
1372         int ret = -1;
1373         struct gcpro gcpro1, gcpro2;
1374
1375         GCPRO2(faf, retfo);
1376
1377         faf =  args[0];
1378         ffio = XEFFIO(faf);
1379         retfo = Fmake_ffi_object(XCAR(XCDR(ffio->type)), Qnil);
1380
1381 #ifdef HAVE_LIBFFI
1382         ret = ffi_call_using_libffi(args[0], retfo, nargs-1, &args[1]);
1383 #endif  /* HAVE_LIBFFI */
1384
1385         RETURN_UNGCPRO(ret == 0 ? retfo : Qnil);
1386 }
1387
1388 #ifdef EF_USE_ASYNEQ
1389 /* handler for asynchronously calling ffi code */
1390 Lisp_Object Qffi_jobp;
1391 #define EFFI_DEBUG_JOB(args...)
1392 static Lisp_Object
1393 exec_sentinel_unwind(Lisp_Object SXE_UNUSED(datum))
1394 {
1395         return Qnil;
1396 }
1397
1398 static inline void
1399 exec_sentinel(void *job, ffi_job_t ffij)
1400         __attribute__((always_inline));
1401 static inline void
1402 exec_sentinel(void *job, ffi_job_t ffij)
1403 {
1404         /* This function can GC */
1405         /* called from main thread */
1406         int speccount = specpdl_depth(), nargs = ffij->sntnl_nargs, i;
1407         Lisp_Object funcell[nargs+2];
1408         struct gcpro gcpro1;
1409
1410         funcell[0] = ffij->sntnl;
1411         funcell[1] = (Lisp_Object)job;
1412         for (i = 0; i < nargs; i++) {
1413                 funcell[2+i] = ffij->sntnl_args[i];
1414         }
1415         GCPROn(funcell, nargs+2);
1416
1417         record_unwind_protect(exec_sentinel_unwind, Qnil);
1418         /* call the funcell */
1419         Ffuncall(nargs+2, funcell);
1420         /* reset to previous state */
1421         restore_match_data();
1422         UNGCPRO;
1423         unbind_to(speccount, Qnil);
1424         return;
1425 }
1426
1427 static inline ffi_job_t
1428 allocate_ffi_job(void)
1429 {
1430         ffi_job_t ffij = xnew(struct ffi_job_s);
1431         EFFI_DEBUG_JOB("allocated: 0x%lx\n", (long unsigned int)ffij);
1432         return ffij;
1433 }
1434
1435 static inline ffi_job_t
1436 make_ffi_job(Lisp_Object fof, int fof_nargs, Lisp_Object *fof_args,
1437              Lisp_Object sntnl, int sntnl_nargs, Lisp_Object *sntnl_args)
1438 {
1439 /* exec'd in the main thread */
1440         ffi_job_t ffij = allocate_ffi_job();
1441         int i;
1442
1443         SXE_MUTEX_INIT(&ffij->mtx);
1444         ffij->fof = fof;
1445         if (fof_nargs > 0) {
1446                 ffij->fof_nargs = fof_nargs;
1447                 ffij->fof_args = xnew_array(Lisp_Object, fof_nargs);
1448                 for (i = 0; i < fof_nargs; i++) {
1449                         ffij->fof_args[i] = fof_args[i];
1450                 }
1451         } else {
1452                 ffij->fof_nargs = 0;
1453                 ffij->fof_args = NULL;
1454         }
1455
1456         ffij->sntnl = sntnl;
1457         if (sntnl_nargs > 0) {
1458                 ffij->sntnl_nargs = sntnl_nargs;
1459                 ffij->sntnl_args = xnew_array(Lisp_Object, sntnl_nargs);
1460                 for (i = 0; i < sntnl_nargs; i++) {
1461                         ffij->sntnl_args[i] = sntnl_args[i];
1462                 }
1463         } else {
1464                 ffij->sntnl_nargs = 0;
1465                 ffij->sntnl_args = NULL;
1466         }
1467
1468         ffij->result = Qnil;
1469         ffij->retfo = Fmake_ffi_object(XCAR(XCDR(XEFFIO(fof)->type)), Qnil);
1470         return ffij;
1471 }
1472
1473 static void
1474 mark_ffi_job(worker_job_t job)
1475 {
1476         ffi_job_t ffij = ffi_job(job);
1477         int i;
1478
1479         if (!ffij)
1480                 return;
1481
1482         SXE_MUTEX_LOCK(&ffij->mtx);
1483         mark_object(ffij->fof);
1484         for (i = 0; i < ffij->fof_nargs; i++) {
1485                 mark_object(ffij->fof_args[i]);
1486         }
1487         mark_object(ffij->sntnl);
1488         for (i = 0; i < ffij->sntnl_nargs; i++) {
1489                 mark_object(ffij->sntnl_args[i]);
1490         }
1491         mark_object(ffij->retfo);
1492         mark_object(ffij->result);
1493         SXE_MUTEX_UNLOCK(&ffij->mtx);
1494         return;
1495 }
1496
1497 static void
1498 print_ffi_job(worker_job_t job, Lisp_Object pcf)
1499 {
1500         ffi_job_t ffij = ffi_job(job);
1501
1502         SXE_MUTEX_LOCK(&ffij->mtx);
1503         WRITE_FMT_STRING(pcf, " carrying  #<ffi-job 0x%lx>",
1504                          (long unsigned int)ffij);
1505         SXE_MUTEX_UNLOCK(&ffij->mtx);
1506         return;
1507 }
1508
1509 static inline void
1510 finish_ffi_job_data(ffi_job_t ffij)
1511 {
1512         SXE_MUTEX_LOCK(&ffij->mtx);
1513         xfree(ffij->fof_args);
1514         xfree(ffij->sntnl_args);
1515         SXE_MUTEX_UNLOCK(&ffij->mtx);
1516         SXE_MUTEX_FINI(&ffij->mtx);
1517
1518         EFFI_DEBUG_JOB("finished: 0x%lx\n", (long unsigned int)ffij);
1519         xfree(ffij);
1520 }
1521
1522 static void
1523 finish_ffi_job(worker_job_t job)
1524 {
1525         ffi_job_t ffij;
1526
1527         lock_worker_job(job);
1528         ffij = ffi_job(job);
1529
1530         if (ffij) {
1531                 finish_ffi_job_data(ffij);
1532         }
1533         worker_job_data(job) = NULL;
1534         unlock_worker_job(job);
1535         return;
1536 }
1537
1538 static void
1539 ffi_job_handle(worker_job_t job)
1540 {
1541         /* thread-safe */
1542         /* usually called from aux threads */
1543         ffi_job_t ffij;
1544         Lisp_Object fof = Qnil, retfo = Qnil, *args = NULL;
1545         int nargs, ret = -1;
1546
1547         lock_worker_job(job);
1548         ffij = ffi_job(job);
1549         unlock_worker_job(job);
1550         SXE_MUTEX_LOCK(&ffij->mtx);
1551         fof = ffij->fof;
1552         nargs = ffij->fof_nargs;
1553         args = ffij->fof_args;
1554         SXE_MUTEX_UNLOCK(&ffij->mtx);
1555
1556         /* can't ... Fmake_ffi_object is not mt-safe */
1557         /* retfo = Fmake_ffi_object(XCAR(XCDR(XEFFIO(fof)->type)), Qnil); */
1558         retfo = ffij->retfo;
1559
1560 #ifdef HAVE_LIBFFI
1561         ret = ffi_call_using_libffi(fof, retfo, nargs, args);
1562 #endif  /* HAVE_LIBFFI */
1563         if (ret == 0) {
1564                 SXE_MUTEX_LOCK(&ffij->mtx);
1565                 ffij->result = retfo;
1566                 SXE_MUTEX_UNLOCK(&ffij->mtx);
1567         }
1568
1569         EFFI_DEBUG_JOB("job 0x%lx succeeded\n", (long unsigned int)ffij);
1570         return;
1571 }
1572
1573 static void
1574 ffi_job_finished(worker_job_t job)
1575 {
1576         if (NILP(ffi_job_sentinel(job) /* sentinel */)) {
1577                 return;
1578         }
1579         /* called from main thread */
1580         exec_sentinel(job, ffi_job(job));
1581         return;
1582 }
1583
1584 static struct work_handler_s ffi_job_handler = {
1585         mark_ffi_job, print_ffi_job, finish_ffi_job,
1586         ffi_job_handle, NULL, ffi_job_finished
1587 };
1588
1589 static Lisp_Object
1590 make_ffi_asyneq_job(ffi_job_t ffij)
1591 {
1592         /* create a job digestible by the asyneq */
1593         Lisp_Object job = Qnil;
1594         struct gcpro gcpro1;
1595
1596         GCPRO1(job);
1597         job = wrap_object(make_worker_job(&ffi_job_handler));
1598         XWORKER_JOB_DATA(job) = ffij;
1599         /* the scratch buffer thingie */
1600         UNGCPRO;
1601         return job;
1602 }
1603
1604 DEFUN("ffi-call-function&", Fffi_call_functionX, 1, MANY, 0, /*
1605 Call a function referred to by FO with arguments ARGS asynchronously,
1606 return a job object.
1607
1608 Arguments are: FO &rest FO-ARGS &aux SENTINEL &rest SENTINEL-ARGS
1609
1610 FO should be a foreign binding initiated by `ffi-defun'.
1611 FO-ARGS should be exactly as many foreign data objects as FO needs.
1612 SENTINEL is a lisp sentinel function called when the job finished,
1613   the function should take at least one argument JOB, further arguments
1614   may be specified by passing further SENTINEL-ARGS.
1615 */
1616       (int nargs, Lisp_Object *args))
1617 {
1618         Lisp_Object job = Qnil;
1619         Lisp_Object sntnl, fof, *sntnl_args, *fof_args;
1620         int sntnl_nargs, fof_nargs;
1621         ffi_job_t ffij;
1622         struct gcpro gcpro1, gcpro2;
1623
1624         CHECK_EFFIO(args[0]);
1625         GCPRO1n(job, args, nargs);
1626
1627         fof = args[0];
1628         /* determine how many args belong to the fof */
1629         fof_nargs = XINT(Flength(XCDR(XEFFIO(fof)->type)))-1;
1630         fof_args = &args[1];
1631
1632         if ((sntnl_nargs = nargs - fof_nargs - 2) >= 0) {
1633                 sntnl = args[fof_nargs+1];
1634                 sntnl_args = &args[fof_nargs+2];
1635         } else {
1636                 sntnl = Qnil;
1637                 sntnl_args = NULL;
1638         }
1639
1640         /* create the job data object */
1641         ffij = make_ffi_job(fof, fof_nargs, fof_args,
1642                             sntnl, sntnl_nargs, sntnl_args);
1643         /* now prepare the job to dispatch */
1644         job = make_ffi_asyneq_job(ffij);
1645         /* ... and dispatch it, change its state to queued */
1646         XWORKER_JOB_STATE(job) = WORKER_JOB_QUEUED;
1647         eq_enqueue(delegate_eq, job);
1648         /* brag about new jobs in the queue */
1649         eq_queue_trigger_all(delegate_eq);
1650
1651         UNGCPRO;
1652         return job;
1653 }
1654 #endif  /* EF_USE_ASYNEQ */
1655
1656 extern struct device *decode_x_device(Lisp_Object device);
1657
1658 DEFUN("x-device-display", Fx_device_display, 0, 1, 0,   /*
1659 Return DEVICE display as FFI object.
1660 */
1661       (device))
1662 {
1663 #if HAVE_X_WINDOWS
1664         Lisp_Object fo;
1665
1666         fo = Fmake_ffi_object(Qpointer, Qnil);
1667         XEFFIO(fo)->fotype = EFFI_FOT_BIND;
1668         XEFFIO(fo)->fop.ptr = (void*)DEVICE_X_DISPLAY(decode_x_device(device));
1669         return fo;
1670 #else
1671         return Qnil;
1672 #endif
1673 }
1674
1675 /* Callbacks */
1676 #define FFI_CC_CDECL 0
1677
1678 #if defined __i386__
1679 static void
1680 ffi_callback_call_x86(Lisp_Object cbk_info, char *arg_buffer)
1681 {
1682         Lisp_Object fun, alist = Qnil, retlo, foret;
1683         Lisp_Object rtype, argtypes;
1684         struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
1685         void *ptr;
1686
1687         fun = Fcar(cbk_info);
1688         rtype = Fcar(Fcdr(cbk_info));
1689         argtypes = Fcar(Fcdr(Fcdr(cbk_info)));
1690
1691         CHECK_LIST(argtypes);
1692
1693         arg_buffer += 4;                /* Skip return address */
1694         while (!NILP(argtypes)) {
1695                 Lisp_Object result, ctype;
1696                 int size;
1697
1698                 ctype = ffi_canonicalise_type(XCAR(argtypes));
1699                 size = XINT(Fffi_size_of_type(ctype));
1700                 if (EQ(ctype, Qc_string)) {
1701                         char *aptr = *(char**)arg_buffer;
1702                         if (aptr)
1703                                 result = ffi_fetch_foreign(aptr, ctype);
1704                         else
1705                                 result = Qnil;
1706                 } else
1707                         result = ffi_fetch_foreign(arg_buffer, ctype);
1708                 /* Apply translators and put the result into alist */
1709                 result = apply1(Findirect_function(Qffi_translate_from_foreign),
1710                                 list2(result, XCAR(argtypes)));
1711                 alist = Fcons(result, alist);
1712                 {
1713                         int mask = 3;
1714                         int sp = (size + mask) & ~mask;
1715                         arg_buffer += (sp);
1716                 }
1717                 argtypes = XCDR(argtypes);
1718         }
1719         alist = Fnreverse(alist);
1720
1721         /* Special case, we have no return value */
1722         if (EQ(rtype, Qvoid)) {
1723                 GCPRO3(fun, alist, rtype);
1724                 apply1(fun, alist);
1725                 UNGCPRO;
1726                 return;
1727         }
1728
1729         GCPRO5(fun, alist, rtype, retlo, foret);
1730         retlo = apply1(fun, alist);
1731         foret = Fmake_ffi_object(rtype, Qnil);
1732         Fffi_store(foret, make_int(0), rtype, retlo);
1733         ptr = (void*)XEFFIO(foret)->fop.ptr;
1734         if (EQ(rtype, Qdouble)) {
1735                 UNGCPRO;
1736                 {
1737                 asm volatile ("fldl (%0)" :: "a" (ptr));
1738                 }
1739                 return;
1740         } else if (EQ(rtype, Qfloat)) {
1741                 UNGCPRO;
1742                 {
1743                 asm volatile ("flds (%0)" :: "a" (ptr));
1744                 }
1745                 return;
1746         } else {
1747                 int iv;
1748
1749                 if (EQ(rtype, Qbyte) || EQ(rtype, Qchar))
1750                         iv = *(char*)ptr;
1751                 else if (EQ(rtype, Qunsigned_byte) || EQ(rtype, Qunsigned_char))
1752                         iv = *(char unsigned*)ptr;
1753                 else if (EQ(rtype, Qshort))
1754                         iv = *(short*)ptr;
1755                 else if (EQ(rtype, Qunsigned_short))
1756                         iv = *(unsigned short*)ptr;
1757                 else
1758                         iv = *(int*)ptr;
1759                 UNGCPRO;
1760                 {
1761                         asm volatile ("movl %0,%%eax;" :: "r" (iv) : "%eax");
1762                 }
1763                 return;
1764         }
1765 }
1766
1767 void*
1768 ffi_make_callback_x86(Lisp_Object data, int cc_type)
1769 {
1770         /*
1771          *      push    %esp                            54
1772          *      pushl   <data>                          68 <addr32>
1773          *      call    ffi_callback_call_x86           E8 <disp32>
1774          *      pop     %ecx                            59
1775          *      pop     %ecx                            59
1776          *      ret                                     c3
1777          *      nop                                     90
1778          *      nop                                     90
1779          */
1780
1781         char *buf = xmalloc(sizeof(char)*16);
1782         *(char*) (buf+0)  = 0x54;
1783         *(char*) (buf+1)  = 0x68;
1784         *(long*) (buf+2)  = (long)data;
1785         *(char*) (buf+6)  = 0xE8;
1786         *(long*) (buf+7)  = (long)ffi_callback_call_x86 - (long)(buf+11);
1787         *(char*) (buf+11) = 0x59;
1788         *(char*) (buf+12) = 0x59;
1789         if (cc_type == FFI_CC_CDECL) {
1790                 *(char*) (buf+13) = 0xc3;
1791                 *(short*)(buf+14) = 0x9090;
1792         } else {
1793                 Lisp_Object arg_types = Fcar(Fcdr(Fcdr(data)));
1794                 int byte_size = 0;
1795                 int mask = 3;
1796
1797                 CHECK_CONS(arg_types);
1798
1799                 while (!NILP(arg_types)) {
1800                         int sz = XINT(Fffi_size_of_type(XCAR(arg_types)));
1801                         byte_size += ((sz+mask)&(~mask));
1802                         arg_types = XCDR(arg_types);
1803                 }
1804
1805                 *(char*) (buf+13) = 0xc2;
1806                 *(short*)(buf+14) = (short)byte_size;
1807         }
1808
1809         return buf;
1810 }
1811 #endif  /* __i386__ */
1812
1813 DEFUN("ffi-make-callback", Fffi_make_callback, 4, 4, 0, /*
1814 Create dynamic callback and return pointer to it.
1815 */
1816       (fun, rtype, argtypes, cctype))
1817 {
1818         Lisp_Object data;
1819         Lisp_Object ptr;
1820
1821         CHECK_INT(cctype);
1822
1823         data = list3(fun, rtype, argtypes);
1824         /* Put data as property of the fun, so it(data) wont be GCed */
1825         Fput(fun, Qffi_callback, data);
1826         ptr = Fmake_ffi_object(Qpointer, Qnil);
1827 #ifdef __i386__
1828         XEFFIO(ptr)->fop.ptr = ffi_make_callback_x86(data, XINT(cctype));
1829 #endif /* __i386__ */
1830         return ptr;
1831 }
1832
1833 void
1834 syms_of_ffi(void)
1835 {
1836         INIT_LRECORD_IMPLEMENTATION(ffiobject);
1837
1838         DEFSYMBOL(Qarray);
1839         DEFSYMBOL(Qbyte);
1840         DEFSYMBOL(Qc_data);
1841         DEFSYMBOL(Qc_string);
1842         DEFSYMBOL(Qdouble);
1843         DEFSYMBOL(Qlong);
1844         DEFSYMBOL(Qstruct);
1845         DEFSYMBOL(Qunion);
1846         DEFSYMBOL(Qunsigned_byte);
1847         DEFSYMBOL(Qunsigned_char);
1848         DEFSYMBOL(Qunsigned_int);
1849         DEFSYMBOL(Qunsigned_long);
1850         DEFSYMBOL(Qunsigned_short);
1851
1852         /* ### This is broken, the lrecord needs to be called ffi_object,
1853            and then this would be a DEFSYMBOL_MULTIWORD_PREDICATE(). Not
1854            doing it in this commit, though. */
1855         defsymbol(&Qffiobjectp, "ffi-object-p");
1856
1857         DEFSYMBOL(Qffi_translate_to_foreign);
1858         DEFSYMBOL(Qffi_translate_from_foreign);
1859
1860         DEFSYMBOL(Qffi_callback);
1861
1862         DEFSUBR(Fffi_basic_type_p);
1863         DEFSUBR(Fffi_canonicalise_type);
1864         DEFSUBR(Fffi_size_of_type);
1865         DEFSUBR(Fmake_ffi_object);
1866         DEFSUBR(Fffi_object_p);
1867         DEFSUBR(Fffi_make_pointer);
1868         DEFSUBR(Fffi_object_address);
1869         DEFSUBR(Fffi_object_canonical_type);
1870         DEFSUBR(Fffi_object_type);
1871         DEFSUBR(Fffi_object_size);
1872         DEFSUBR(Fffi_set_storage_size);
1873         DEFSUBR(Fffi_set_object_type);
1874         DEFSUBR(Fffi_fetch);
1875         DEFSUBR(Fffi_aref);
1876         DEFSUBR(Fffi_store);
1877         DEFSUBR(Fffi_aset);
1878         DEFSUBR(Fffi_address_of);
1879         DEFSUBR(Fffi_type_alignment);
1880         DEFSUBR(Fffi_slot_offset);
1881         DEFSUBR(Fffi_load_library);
1882         DEFSUBR(Fffi_bind);
1883         DEFSUBR(Fffi_dlerror);
1884         DEFSUBR(Fffi_defun);
1885         DEFSUBR(Fffi_call_function);
1886
1887         DEFSUBR(Fffi_lisp_object_to_pointer);
1888         DEFSUBR(Fffi_pointer_to_lisp_object);
1889         DEFSUBR(Fffi_plist);
1890
1891 #ifdef EF_USE_ASYNEQ
1892         DEFSUBR(Fffi_call_functionX);
1893         defsymbol(&Qffi_jobp, "ffi-job-p");
1894 #endif
1895
1896         DEFSUBR(Fx_device_display);
1897
1898         DEFSUBR(Fffi_make_callback);
1899 }
1900
1901 void
1902 reinit_vars_of_ffi(void)
1903 {
1904         staticpro_nodump(&Vffi_all_objects);
1905         Vffi_all_objects = make_weak_list(WEAK_LIST_SIMPLE);
1906 }
1907
1908 void
1909 vars_of_ffi(void)
1910 {
1911         reinit_vars_of_ffi();
1912
1913         DEFVAR_LISP("ffi-named-types", &Vffi_named_types        /*
1914 Alist of named FFI types with elements of the form (NAME . FFI-TYPE).
1915                                                  */ );
1916         Vffi_named_types = Qnil;
1917
1918         DEFVAR_LISP("ffi-loaded-libraries", &Vffi_loaded_libraries /*
1919 Alist of loaded libraries with elements of the form (LIB-NAME . FFIO).
1920                                                  */ );
1921         Vffi_loaded_libraries = Qnil;
1922
1923         DEFVAR_LISP("ffi-type-checker", &Vffi_type_checker /*
1924 Function to call when the validity of an FFI type shall be checked.
1925                                                            */ );
1926         Vffi_type_checker = intern("ffi-type-p");
1927 }