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