Coding style cleanups from Aidan
[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 "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 WRITE_FMT_STRING(x,y,...) write_fmt_string((x),(y),__VA_ARGS__)
59 #  define LRECORD_DESCRIPTION lrecord_description
60 #else
61 #  define SIGNAL_ERROR Fsignal
62 #  define FFIBYTE Ibyte
63 #  define WRITE_C_STRING(x,y) write_c_string((y),(x))
64 #  define WRITE_FMT_STRING(x,y,...)                     \
65         do {                                            \
66                 char wcsb[128];                         \
67                 int wcss = snprintf(wcsb, sizeof(wcsb), \
68                                     (y),__VA_ARGS__);   \
69                 write_c_string((y),wcsb);               \
70         } while(0)
71 #  define LRECORD_DESCRIPTION memory_description
72 #endif  /* SXEMACS */
73
74 /*
75  * Built-in types:
76  *   byte, ubyte, char, uchar,
77  *   short, ushort, int, uint,
78  *   long, ulong,
79  *   float, double,
80  *   void, pointer, c-string
81  *
82  * Function type:
83  *
84  *   (function RET-TYPE IN-TYPE .. IN-TYPE)
85  *
86  * Array types:
87  *
88  *   (array TYPE SIZE)
89  *
90  * Structures and unions types:
91  * 
92  *   (struct|union NAME
93  *     (SLOT-NAME TYPE)
94  *     (SLOT-NAME TYPE)
95  *     ...
96  *     (SLOT-NAME TYPE))
97  *
98  * Pointers:
99  *
100  *   pointer or (pointer TYPE)
101  */
102
103 /* Foreign types, not defined as symbols elsewhere. */
104 Lisp_Object Qarray, Qbyte, Qc_data, Qc_string, Qdouble, Qlong, Qstruct;
105 Lisp_Object Qunion, Qunsigned_byte, Qunsigned_char, Qunsigned_int;
106 Lisp_Object Qunsigned_long, Qunsigned_short;
107
108 #define FFI_POINTERP(type) (EQ(type, Qpointer)                                \
109                             || (CONSP(type) && EQ(XCAR(type), Qpointer)))
110
111 #define FFI_TPTR(type) (EQ(type, Qc_string)                                   \
112                         || EQ(type, Qc_data)                                  \
113                         || FFI_POINTERP(type)                                  \
114                         || (CONSP(type) && ((EQ(XCAR(type), Qc_data))         \
115                                             || EQ(XCAR(type), Qarray))))
116 Lisp_Object Qffiobjectp;
117 Lisp_Object Qffi_translate_to_foreign;
118 Lisp_Object Qffi_translate_from_foreign;
119
120 /* Alist with elements in form (NAME . TYPE) */
121 Lisp_Object Vffi_loaded_libraries;
122 Lisp_Object Vffi_named_types;
123
124 Lisp_Object Vffi_type_checker;
125
126 static Lisp_Object Vffi_all_objects;
127
128 Lisp_Object Qffi_callback;
129
130 static Lisp_Object
131 mark_ffiobject(Lisp_Object obj)
132 {
133         Lisp_EffiObject *ffio = XEFFIO(obj);
134         mark_object(ffio->type);
135         mark_object(ffio->size);
136         mark_object(ffio->plist);
137         return (ffio->plist);
138 }
139
140 static void
141 print_ffiobject(Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
142 {
143         /* This function can GC */
144         Lisp_EffiObject *ffio = XEFFIO(obj);
145         escapeflag = escapeflag;        /* shutup compiler */
146         if (print_readably) {
147 #ifdef SXEMACS
148                 error("printing unreadable object #<ffiobject 0x%x>",
149                       ffio->header.uid);
150 #else
151                 signal_ferror(Qinternal_error,
152                               "printing unreadable object #<ffiobject 0x%x>",
153                               ffio->header.uid);
154 #endif  /* SXEMACS */
155         }
156         WRITE_C_STRING("#<ffiobject ", printcharfun);
157         /* Print FFIO type */
158         if (!NILP(ffio->type)) {
159                 WRITE_C_STRING("type=", printcharfun);
160                 print_internal(ffio->type, printcharfun, 1);
161                 WRITE_C_STRING(" ", printcharfun);
162         }
163         WRITE_FMT_STRING(printcharfun,"size=%ld fotype=%d foptr=%p>",
164                          (long)XINT(ffio->size), ffio->fotype, ffio->fop.generic);
165 }
166
167 static const struct LRECORD_DESCRIPTION ffiobject_description[] = {
168         {XD_LISP_OBJECT, offsetof(Lisp_EffiObject, type)},
169         {XD_LISP_OBJECT, offsetof(Lisp_EffiObject, size)},
170         {XD_LISP_OBJECT, offsetof(Lisp_EffiObject, plist)},
171         {XD_INT, offsetof(Lisp_EffiObject, fotype)},
172         {XD_OPAQUE_PTR, offsetof(Lisp_EffiObject, fop)},
173 #ifdef SXEMACS
174         {XD_SIZE_T, offsetof(Lisp_EffiObject, storage_size)},
175 #else
176         {XD_ELEMCOUNT, offsetof(Lisp_EffiObject, storage_size)},
177 #endif  /* SXEMACS */
178         {XD_END}
179 };
180
181 static Lisp_Object
182 ffi_getprop(Lisp_Object fo, Lisp_Object property)
183 {
184         return external_plist_get(&XEFFIO(fo)->plist, property, 0, ERROR_ME);
185 }
186
187 static int
188 ffi_putprop(Lisp_Object fo, Lisp_Object property, Lisp_Object value)
189 {
190         external_plist_put(&XEFFIO(fo)->plist, property, value, 0, ERROR_ME);
191         return 1;
192 }
193
194 static int
195 ffi_remprop(Lisp_Object fo, Lisp_Object property)
196 {
197         return external_remprop(&XEFFIO(fo)->plist, property, 0, ERROR_ME);
198 }
199
200 #ifdef SXEMACS
201 static size_t
202 sizeof_ffiobject(const void *header)
203 {
204         const Lisp_EffiObject *effio = (const Lisp_EffiObject *)header;
205         return (sizeof(Lisp_EffiObject) + effio->storage_size);
206 }
207 #else
208 static Bytecount
209 sizeof_ffiobject(const void *header)
210 {
211         const Lisp_EffiObject *effio = (const Lisp_EffiObject *)header;
212         return (sizeof(Lisp_EffiObject) + effio->storage_size);
213 }
214 #endif  /* SXEMACS */
215
216 /* Define ffiobject implementation */
217 const struct lrecord_implementation lrecord_ffiobject = {
218         .name = "ffiobject",
219         .marker = mark_ffiobject,
220         .printer = print_ffiobject,
221         .finalizer = 0,
222         .equal = 0,
223         .hash = 0,
224         .description = ffiobject_description,
225         .getprop = ffi_getprop,
226         .putprop = ffi_putprop,
227         .remprop = ffi_remprop,
228         .plist = Fffi_plist,
229         .static_size = 0,
230         .size_in_bytes_method = sizeof_ffiobject,
231         .lrecord_type_index = lrecord_type_ffiobject,
232         .basic_p = 0
233 };
234
235 \f
236 /** alignment in union and structures **/
237 /*
238  * x86:
239  *
240  *   - An entire structure or union is aligned on the same boundary as
241  *     its most strictly aligned member.
242  *
243  *   - Each member is assigned to the lowest available offset with the
244  *     appropriate alignment.  This may require /internal padding/,
245  *     depending on the previous member.
246  *
247  *   - A structure's size is increased, if necessary, to make it a
248  *     multiple of the alignment.  This may require /tail padding/,
249  *     depending on the last member.
250  *
251  *  Internal padding:
252  *
253  *    struct {
254  *     char c;            .-------2+---1+---0.
255  *     short s;           |  s     |pad |  c |
256  *    }                   `--------+----+----'
257  *
258  *  Internal and Tail padding:
259  *
260  *    struct {            .------------1+---0.
261  *     char c;            |     pad     |  c |
262  *     double d;          |-------------+---4|
263  *     short s;           |         d        |
264  *    }                   |-----------------8|
265  *                        |         d        |
266  *                        |------14+-------12|
267  *                        |   pad  |    s    |
268  *                        `--------+---------'
269  *
270  *  Union allocation:
271  *
272  *    union {             .------------1+---0.
273  *     char c;            |     pad     |  c |
274  *     short s;           |-------2+----+---0|
275  *     int j;             |  pad   |    s    |
276  *    }                   |--------+--------0|
277  *                        |        j         |
278  *                        `------------------'
279  */
280 static Lisp_Object
281 ffi_check_type(Lisp_Object type)
282 {
283         return apply1(Vffi_type_checker, Fcons(type, Fcons(Qt, Qnil)));
284 }
285
286 DEFUN("ffi-basic-type-p", Fffi_basic_type_p, 1, 1, 0, /*
287 Return non-nil if TYPE is a basic FFI type.
288
289 A type is said to be basic, if it is neither a pointer nor a
290 function, and there is a corresponding built-in type in C.
291 */
292       (type))
293 {
294         if (EQ(type, Qbyte) || EQ(type, Qunsigned_byte) || EQ(type, Qchar)
295             || EQ(type, Qunsigned_char) || EQ(type, Qshort)
296             || EQ(type, Qunsigned_short) || EQ(type, Qint)
297             || EQ(type, Qunsigned_int) || EQ(type, Qlong)
298             || EQ(type, Qunsigned_long) || EQ(type, Qfloat)
299             || EQ(type, Qdouble) || EQ(type, Qvoid)
300             || EQ(type, Qc_string) || EQ(type, Qc_data)
301             || (CONSP(type) && EQ(XCAR(type), Qc_data)))
302                 return Qt;
303         else
304                 return Qnil;
305 }
306
307
308 static Lisp_Object
309 ffi_canonicalise_type(Lisp_Object type)
310 {
311 /* this function canNOT GC */
312
313         while (!NILP(type) && NILP(Fffi_basic_type_p(type)) && SYMBOLP(type)) {
314                 if EQ(type, Qpointer)
315                         break;
316                 type = Fcdr(Fassq(type, Vffi_named_types));
317         }
318
319         return type;
320 }
321
322 DEFUN("ffi-canonicalise-type", Fffi_canonicalise_type, 1, 1, 0, /*
323 Return FFI type TYPE in a canonical form.
324 */
325       (type))
326 {
327         Lisp_Object canon_type = ffi_canonicalise_type(type);
328         if (NILP(canon_type)) {
329 #ifdef SXEMACS
330                 signal_simple_error("No such FFI type", type);
331 #else
332                 signal_error(Qinternal_error, "No such FFI type", type);
333 #endif  /* SXEMACS */
334         }
335         return canon_type;
336 }
337
338 DEFUN("ffi-size-of-type", Fffi_size_of_type, 1, 1, 0,   /*
339 Return the size of the foreign type TYPE.
340
341 Valid foreign types are: `byte', `unsigned-byte', `char',
342 `unsigned-char', `short', `unsigned-short', `int', `unsigned-int',
343 `long', `unsigned-long', `pointer', `float', `double', 
344 `object', and `c-string'.
345 */
346       (type))
347 {
348         int tsize;
349
350         type = ffi_canonicalise_type(type);
351         if (EQ(type, Qvoid))
352                 tsize = 0;
353         else if (EQ(type, Qbyte))
354                 tsize = sizeof(int8_t);
355         else if (EQ(type, Qunsigned_byte))
356                 tsize = sizeof(uint8_t);
357         else if (EQ(type, Qchar))
358                 tsize = sizeof(char);
359         else if (EQ(type, Qunsigned_char))
360                 tsize = sizeof(unsigned char);
361         else if (EQ(type, Qshort))
362                 tsize = sizeof(short);
363         else if (EQ(type, Qunsigned_short))
364                 tsize = sizeof(unsigned short);
365         else if (EQ(type, Qint))
366                 tsize = sizeof(int);
367         else if (EQ(type, Qunsigned_int))
368                 tsize = sizeof(unsigned int);
369         else if (EQ(type, Qlong))
370                 tsize = sizeof(long);
371         else if (EQ(type, Qunsigned_long))
372                 tsize = sizeof(unsigned long);
373         else if (EQ(type, Qfloat))
374                 tsize = sizeof(float);
375         else if (EQ(type, Qdouble))
376                 tsize = sizeof(double);
377         else if (EQ(type, Qc_string))
378                 tsize = sizeof(char *);
379         else if (FFI_POINTERP(type))
380                 tsize = sizeof(void *);
381         else if (EQ(type, Qc_data))
382                 tsize = sizeof(void *);
383         else if (CONSP(type) && EQ(XCAR(type), Qc_data)) {
384                 Lisp_Object cdsize = XCDR(type);
385                 CHECK_INT(cdsize);
386                 tsize = XINT(cdsize);
387         } else if (CONSP(type) && EQ(XCAR(type), Qfunction))
388                 tsize = sizeof(void(*));
389         else if (CONSP(type) && EQ(XCAR(type), Qarray)) {
390                 Lisp_Object atype = Fcar(XCDR(type));
391                 Lisp_Object asize = Fcar(Fcdr(XCDR(type)));
392
393                 CHECK_INT(asize);
394                 tsize = XINT(asize) * XINT(Fffi_size_of_type(atype));
395         } else if (CONSP(type) && EQ(XCAR(type), Qstruct)) {
396                 return Fffi_slot_offset(type, Qnil);
397         } else if (CONSP(type) && EQ(XCAR(type), Qunion)) {
398                 Lisp_Object slots = Fcdr(XCDR(type));
399
400                 CHECK_CONS(slots);
401
402                 tsize = 0;
403                 while (!NILP(slots)) {
404                         Lisp_Object slot_type = Fcar(Fcdr(XCAR(slots)));
405                         int slot_size = XINT(Fffi_size_of_type(slot_type));
406                         if (slot_size > tsize)
407                                 tsize = slot_size;
408                         slots = XCDR(slots);
409                 }
410         } else {
411 #ifdef SXEMACS
412                 signal_simple_error("Unrecognized foreign type", type);
413 #else
414                 signal_error(Qinternal_error, "Unrecognized foreign type", type);
415 #endif  /* SXEMACS */
416         }
417
418         return make_int(tsize);
419 }
420
421 DEFUN("make-ffi-object", Fmake_ffi_object, 1, 2, 0, /*
422 Create a new FFI object of type TYPE.
423 If optional argument SIZE is non-nil it should be an
424 integer, in this case additional storage size to hold data 
425 of at least length SIZE is allocated.
426 */
427       (type, size))
428 {
429         int cs_or_cd;
430         Lisp_Object ctype;
431         Lisp_Object result = Qnil;
432         Lisp_EffiObject *ffio;
433         struct gcpro gcpro1;
434
435         GCPRO1(result);
436
437         /* NOTE: ffi_check_type returns canonical type */
438         ctype = ffi_check_type(type);
439         if (NILP(size))
440                 size = Fffi_size_of_type(type);
441         CHECK_INT(size);
442
443         if (CONSP(ctype) && EQ(XCAR(ctype), Qc_data) && INTP(XCDR(ctype)))
444                 size = XCDR(type);
445
446         cs_or_cd = EQ(ctype, Qc_string) || (EQ(ctype, Qc_data));
447         if ((cs_or_cd && (XINT(size) < 1))
448             || (!(cs_or_cd || FFI_POINTERP(ctype))
449                 && (XINT(size) < XINT(Fffi_size_of_type(type)))))
450 #ifdef SXEMACS
451                 signal_simple_error("storage size too small to store type",
452                                     list2(size, type));
453
454         ffio = alloc_lcrecord(sizeof(Lisp_EffiObject)+XINT(size),
455                               &lrecord_ffiobject);
456         XSETEFFIO(result, ffio);
457 #else
458                 signal_error(Qinternal_error,
459                              "storage size too small to store type",
460                              list2(size, type));
461
462         ffio = old_basic_alloc_lcrecord(sizeof(Lisp_EffiObject)+XINT(size),
463                                         &lrecord_ffiobject);
464         result = wrap_effio(ffio);
465 #endif  /* SXEMACS */
466
467         ffio->size = Fffi_size_of_type(type);
468         ffio->type = type;
469         ffio->plist = Qnil;
470
471         /* Initialize foreign pointer */
472         ffio->fotype = EFFI_FOT_NONE;
473         ffio->storage_size = XINT(size);
474         ffio->fop.ptr = ffio->fostorage;
475
476         if (!NILP(Vffi_all_objects))
477                 XWEAK_LIST_LIST(Vffi_all_objects) =
478                         Fcons(result, XWEAK_LIST_LIST(Vffi_all_objects));
479
480         RETURN_UNGCPRO(result);
481 }
482
483 DEFUN("ffi-object-p", Fffi_object_p, 1, 1, 0, /*
484 Return non-nil if FO is an FFI object, nil otherwise.
485 */
486       (fo))
487 {
488         return (EFFIOP(fo) ? Qt : Qnil);
489 }
490
491 DEFUN("ffi-object-address", Fffi_object_address, 1, 1, 0, /*
492 Return the address FO points to.
493 */
494       (fo))
495 {
496         CHECK_EFFIO(fo);
497         return make_float((long)XEFFIO(fo)->fop.ptr);
498 }
499
500 DEFUN("ffi-make-pointer", Fffi_make_pointer, 1, 1, 0, /*
501   "Return a pointer pointing to ADDRESS."
502 */
503       (address))
504 {
505         long addr;
506         Lisp_Object ptr;
507
508         if (INTP(address))
509                 addr = XINT(address);
510         else if (FLOATP(address))
511                 addr = XFLOATINT(address);
512         else {
513 #ifdef SXEMACS
514                 signal_simple_error("FFI: invalid address type", address);
515 #else
516                 signal_error(Qinternal_error, "FFI: invalid address type",
517                              address);
518 #endif  /* SXEMACS */
519         }
520
521         ptr = Fmake_ffi_object(Qpointer, Qnil);
522         XEFFIO(ptr)->fop.ptr = (void*)addr;
523         return ptr;
524 }
525
526 DEFUN("ffi-object-canonical-type", Fffi_object_canonical_type, 1, 1, 0, /*
527 Return FO's real type, that is after resolving user defined types.
528 */
529       (fo))
530 {
531         CHECK_EFFIO(fo);
532         return ffi_canonicalise_type(XEFFIO(fo)->type);
533 }
534
535 DEFUN("ffi-object-type", Fffi_object_type, 1, 1, 0, /*
536 Return FO's type.
537 */
538       (fo))
539 {
540         CHECK_EFFIO(fo);
541         return (XEFFIO(fo)->type);
542 }
543
544 DEFUN("ffi-set-object-type", Fffi_set_object_type, 2, 2, 0, /*
545 Cast FO to type TYPE and reassign the cast value.
546 */
547       (fo, type))
548 {
549         CHECK_EFFIO(fo);
550
551         ffi_check_type(type);
552         XEFFIO(fo)->type = type;
553
554         return type;
555 }
556
557 DEFUN("ffi-object-size", Fffi_object_size, 1, 1, 0, /*
558 Return the size of the allocated space of FO.
559 */
560       (fo))
561 {
562         CHECK_EFFIO(fo);
563         return (XEFFIO(fo)->size);
564 }
565
566 DEFUN("ffi-set-storage-size", Fffi_set_storage_size, 2, 2, 0, /*
567 Set the size of the allocated space of FO.
568 */
569       (fo, size))
570 {
571         CHECK_EFFIO(fo);
572         CHECK_INT(size);
573         XEFFIO(fo)->storage_size = XUINT(size);
574         return Qt;
575 }
576
577 DEFUN("ffi-load-library", Fffi_load_library, 1, 1, 0, /*
578 Load library LIBNAME and return a foreign object handle if successful,
579 or `nil' if the library cannot be loaded.
580
581 The argument LIBNAME should be the file-name string of a shared object
582 library.  Normally you should omit the file extension, as this
583 function will add the appripriate extension for the current platform
584 if one is missing.
585
586 The library should reside in one of the directories specified by the
587 $LD_LIBRARY_PATH environment variable or the more global ld.so.cache.
588 */
589       (libname))
590 {
591
592 #ifdef LTDL_SHLIB_EXT
593 #  define EXT LTDL_SHLIB_EXT
594 #elif defined(HAVE_DYLD) || defined(HAVE_MACH_O_DYLD_H)
595 #    define EXT ".dylib"
596 #  else
597 #    define EXT ".so"
598 #endif  /* LTDL_SHLIB_EXT */
599
600         void *handler, *dotpos;
601         Lisp_Object fo = Qnil;
602         Lisp_EffiObject *ffio;
603         struct gcpro gcpro1;
604         char *soname = NULL;
605
606         CHECK_STRING(libname);
607
608         /* Add an extension if we need to */
609         dotpos = strrchr((char *)XSTRING_DATA(libname),'.');
610         if ( dotpos == NULL || strncmp(dotpos, EXT, sizeof(EXT))) {
611                 ssize_t liblen = XSTRING_LENGTH(libname);
612                 ssize_t soname_len = liblen + sizeof(EXT);
613                 soname = xmalloc( soname_len + 1);
614                 strncpy(soname, (char *)XSTRING_DATA(libname), liblen+1);
615                 strncat(soname, EXT, sizeof(EXT)+1);
616         }
617
618         if ( soname == NULL ) {
619                 handler = dlopen((const char *)XSTRING_DATA(libname),
620                                  RTLD_GLOBAL|RTLD_NOW);
621         } else {
622                 handler = dlopen(soname, RTLD_GLOBAL|RTLD_NOW);
623                 xfree(soname);
624         }
625
626         if (handler == NULL)
627                 return Qnil;
628         
629         GCPRO1(fo);
630         fo = Fmake_ffi_object(Qpointer, Qnil);
631         ffio = XEFFIO(fo);
632
633         ffio->fotype = EFFI_FOT_BIND;
634         ffio->fop.ptr = handler;
635         
636         RETURN_UNGCPRO(fo);
637 }
638
639 DEFUN("ffi-bind", Fffi_bind, 2, 2, 0, /*
640 Make and return a foreign object of type TYPE and bind it to the
641 external symbol SYM.
642
643 The argument TYPE can be any type-cell.
644 The argument SYM should be a string naming an arbitrary symbol
645 in one of the loaded libraries.
646
647 If SYM does not exist in any of the loaded libraries, `nil' is
648 returned.
649 */
650       (type, sym))
651 {
652         Lisp_Object fo = Qnil;
653         Lisp_EffiObject *ffio;
654         struct gcpro gcpro1;
655
656         ffi_check_type(type);
657         CHECK_STRING(sym);
658
659         GCPRO1(fo);
660         fo = Fmake_ffi_object(type, Qnil);
661         ffio = XEFFIO(fo);
662         ffio->fop.ptr = dlsym(RTLD_DEFAULT, (const char*)XSTRING_DATA(sym));
663         if (ffio->fop.ptr == NULL) {
664                 UNGCPRO;
665                 return Qnil;
666         }
667
668         ffio->fotype = EFFI_FOT_BIND;
669
670         RETURN_UNGCPRO(fo);
671 }
672
673 DEFUN("ffi-dlerror", Fffi_dlerror, 0, 0, 0, /*
674 Return dl error string.
675 */
676       ())
677 {
678         const char *dles = dlerror();
679
680         if (LIKELY(dles != NULL)) {
681                 size_t sz = strlen(dles);
682                 return make_ext_string((const Extbyte*)dles, sz, EFFI_CODING);
683         } else {
684                 return Qnil;
685         }
686 }
687
688 DEFUN("ffi-defun", Fffi_defun, 2, 2, 0, /*
689 Make and return a foreign object of type TYPE and bind it to the
690 external symbol SYM.
691
692 The argument TYPE should be a function type-cell.
693 The argument SYM should be a string naming a function in one of
694 the loaded libraries.
695
696 If SYM does not exist in any of the loaded libraries, an error
697 is indicated.
698
699 This is like `ffi-bind' but for function objects.
700 */
701       (type, sym))
702 {
703         Lisp_Object fo = Qnil;
704         Lisp_EffiObject *ffio;
705         struct gcpro gcpro1;
706
707         ffi_check_type(type);
708         CHECK_STRING(sym);
709         
710         GCPRO1(fo);
711
712         fo = Fmake_ffi_object(type, Qnil);
713         ffio = XEFFIO(fo);
714         ffio->fop.fun = dlsym(RTLD_DEFAULT, (const char *)XSTRING_DATA(sym));
715         if (ffio->fop.fun == NULL) {
716 #ifdef SXEMACS
717                 signal_simple_error("Can't define function", sym);
718 #else
719                 signal_error(Qinternal_error, "Can't define function", sym);
720 #endif  /* SXEMACS */
721         }
722
723         ffio->fotype = EFFI_FOT_FUNC;
724
725         RETURN_UNGCPRO(fo);
726 }
727
728 /*
729  * Return alignment policy for struct or union FFI_SU.
730  * x86: Return 1, 2 or 4.
731  * mips: Return 1, 2, 4 or 8.
732  */
733 static int
734 ffi_type_align(Lisp_Object type)
735 {
736         type = ffi_canonicalise_type(type);
737         if (SYMBOLP(type)) {
738                 if (EQ(type, Qbyte) || EQ(type, Qunsigned_byte)
739                     || EQ(type, Qchar) || EQ(type, Qunsigned_char))
740                         return 1;
741                 if (EQ(type, Qshort) || EQ(type, Qunsigned_short))
742                         return 2;
743 #ifdef FFI_MIPS
744                 if (EQ(type, Qdouble))
745                         return 8;
746 #endif  /* FFI_MIPS */
747                 return 4;
748                 /* NOT REACHED */
749         } else if (CONSP(type)
750                    && (EQ(XCAR(type), Qstruct) || EQ(XCAR(type), Qunion))) {
751                 int al;
752
753                 for (al = 0, type = Fcdr(Fcdr(type));
754                      !NILP(type);
755                      type = Fcdr(type))
756                 {
757                         Lisp_Object stype = Fcar(Fcdr(Fcar(type)));
758                         int tmp_al = ffi_type_align(stype);
759
760                         if (tmp_al > al)
761                                 al = tmp_al;
762                 }
763
764                 return al;
765         }
766
767         return 4;
768 }
769
770 DEFUN("ffi-type-alignment", Fffi_type_alignment, 1, 1, 0, /*
771 Return TYPE alignment.
772 */
773       (type))
774 {
775         return make_int(ffi_type_align(type));
776 }
777
778 DEFUN("ffi-slot-offset", Fffi_slot_offset, 2, 2, 0, /*
779 Return the offset of SLOT in TYPE.
780 SLOT can be either a valid (named) slot in TYPE or `nil'.
781 If SLOT is `nil' return the size of the struct.
782 */
783       (type, slot))
784 {
785         Lisp_Object slots;
786         int lpad, align, retoff;
787
788         type = ffi_canonicalise_type(type);
789         if (!CONSP(type)) {
790 #ifdef SXEMACS
791                 error("Not struct or union");
792 #else
793                 Fsignal(Qwrong_type_argument,
794                         list2(Qstringp, build_string("Not struct or union")));
795 #endif  /* SXEMACS */
796         }
797
798         retoff = 0;
799         lpad = align = ffi_type_align(type);
800         slots = Fcdr(XCDR(type));
801         CHECK_CONS(slots);
802         while (!NILP(slots)) {
803                 Lisp_Object tmp_slot = Fcar(Fcdr(XCAR(slots)));
804                 int tmp_align;
805                 int tmp_size;
806
807                 /*
808                  * NOTE:
809                  *  - for basic types TMP_ALIGN and TMP_SIZE are equal
810                  */
811                 tmp_align = ffi_type_align(tmp_slot);
812
813                 if (EQ(XCAR(XCAR(slots)), slot)) {
814                         /* SLOT found */
815                         /* TODO: add support for :offset keyword in SLOT */
816                         if (lpad < tmp_align) {
817                                 retoff += lpad;
818                                 lpad = 0;
819                         } else
820                                 lpad -= tmp_align;
821                         break;
822                 }
823
824                 tmp_size = XINT(Fffi_size_of_type(tmp_slot));
825                 while (tmp_size > 0) {
826                         if (lpad < tmp_align) {
827                                 retoff += lpad;
828                                 lpad = align;
829                         }
830                         tmp_size -= tmp_align;
831                         lpad -= tmp_align;
832                         retoff += tmp_align;
833                 }
834
835                 slots = XCDR(slots);
836         }
837         if (NILP(slots) && !NILP(slot)) {
838 #ifdef SXEMACS
839                 signal_simple_error("FFI: Slot not found", slot);
840 #else
841                 signal_error(Qinternal_error, "FFI: Slot not found", slot);
842 #endif  /* SXEMACS */
843         }
844         return make_int(retoff + lpad);
845 }
846
847 /*
848  * TYPE must be already canonicalised
849  */
850 static Lisp_Object
851 ffi_fetch_foreign(void *ptr, Lisp_Object type)
852 {
853 /* this function canNOT GC */
854         Lisp_Object retval = Qnone;
855
856         if (EQ(type, Qchar))
857                 retval = make_char(*(char*)ptr);
858         else if (EQ(type, Qunsigned_char))
859                 retval = make_char(*(char unsigned*)ptr);
860         else if (EQ(type, Qbyte))
861                 retval = make_int(*(char*)ptr);
862         else if (EQ(type, Qunsigned_byte))
863                 retval = make_int(*(unsigned char*)ptr);
864         else if (EQ(type, Qshort))
865                 retval = make_int(*(short*)ptr);
866         else if (EQ(type, Qunsigned_short))
867                 retval = make_int(*(unsigned short*)ptr);
868         else if (EQ(type, Qint))
869                 retval = make_int(*(int*)ptr);
870         else if (EQ(type, Qunsigned_int))
871                 retval = make_int(*(unsigned int*)ptr);
872         else if (EQ(type, Qlong))
873                 retval = make_int(*(long*)ptr);
874         else if (EQ(type, Qunsigned_long))
875                 retval = make_int(*(unsigned long*)ptr);
876         else if (EQ(type, Qfloat))
877                 retval = make_float(*(float*)ptr);
878         else if (EQ(type, Qdouble))
879                 retval = make_float(*(double*)ptr);
880         else if (EQ(type, Qc_string)) {
881                 retval = build_ext_string((char*)ptr, Qbinary);
882         } else if (EQ(type, Qvoid)) {
883                 retval = Qnil;
884         } else if (FFI_POINTERP(type)) {
885                 retval = Fmake_ffi_object(type, Qnil);
886                 XEFFIO(retval)->fop.ptr = *(void**)ptr;
887         } else if (CONSP(type) && EQ(XCAR(type), Qfunction)) {
888                 retval = Fmake_ffi_object(type, Qnil);
889                 XEFFIO(retval)->fop.fun = (void*)ptr;
890                 XEFFIO(retval)->fotype = EFFI_FOT_FUNC;
891         }
892
893         return retval;
894 }
895
896 DEFUN("ffi-fetch", Fffi_fetch, 3, 3, 0, /*
897 Fetch value from the foreign object FO from OFFSET position.
898 TYPE specifies value for data to be fetched.
899 */
900       (fo, offset, type))
901 {
902         Lisp_Object origtype = type;
903         Lisp_Object retval = Qnil;
904         Lisp_EffiObject *ffio;
905         void *ptr;
906         struct gcpro gcpro1;
907
908         CHECK_EFFIO(fo);
909         CHECK_INT(offset);
910
911         ffio = XEFFIO(fo);
912         ptr = (void*)((char*)ffio->fop.ptr + XINT(offset));
913
914         type = ffi_canonicalise_type(type);
915
916         GCPRO1(retval);
917         /* Fetch value and translate it according to translators */
918         retval = ffi_fetch_foreign(ptr, type);
919         if (EQ(retval, Qnone)) {
920                 /* Special case for c-data */
921                 if (EQ(type, Qc_data) ||
922                     (CONSP(type) && EQ(XCAR(type), Qc_data)))
923                 {
924                         size_t tlen;
925                         if (EQ(type, Qc_data)) {
926                                 tlen = ffio->storage_size - XINT(offset);
927                         } else {
928                                 CHECK_INT(XCDR(type));
929                                 tlen = XUINT(XCDR(type));
930                         }
931
932                         retval = make_ext_string(ptr, tlen, Qbinary);
933                 } else {
934 #ifdef SXEMACS
935                         signal_simple_error("Can't fetch for this type", origtype);
936 #else
937                         signal_error(Qinternal_error, "Can't fetch for this type",
938                                      origtype);
939 #endif  /* SXEMACS */
940                 }
941         }
942         retval = apply1(Findirect_function(Qffi_translate_from_foreign),
943                         list2(retval, origtype));
944
945         RETURN_UNGCPRO(retval);
946 }
947
948 DEFUN("ffi-aref", Fffi_aref, 2, 2, 0, /*
949 Return the element of FARRAY at index IDX (starting with 0).
950 */
951       (farray, idx))
952 {
953         Lisp_Object type;
954         
955         CHECK_EFFIO(farray);
956         CHECK_INT(idx);
957         
958         type = ffi_canonicalise_type(XEFFIO(farray)->type);
959         if (!FFI_TPTR(type)) {
960 #ifdef SXEMACS
961                 signal_simple_error("Not an array type", type);
962 #else
963                 signal_error(Qinternal_error, "Not an array type", type);
964 #endif  /* SXEMACS */
965         }
966         if (EQ(type, Qc_string))
967                 type = Qchar;
968         else
969                 type = Fcar(XCDR(type));
970
971         return Fffi_fetch(farray,
972                           make_int(XINT(Fffi_size_of_type(type)) * XINT(idx)),
973                           type);
974 }
975
976 DEFUN("ffi-store", Fffi_store, 4, 4, 0, /*
977 For foreign object FO at specified OFFSET store data.
978 Type of data is specified by VAL-TYPE and data itself specified in VAL.
979
980 VAL-TYPE can be either a basic FFI type or an FFI pointer.
981 If VAL-TYPE is a basic FFI type, then VAL can be an
982 ordinary, but suitable Emacs lisp object.
983 If VAL-TYPE is an FFI pointer then VAL _must_ be an FFI
984 object of the underlying type pointed to.
985 */
986       (fo, offset, val_type, val))
987 {
988         Lisp_Object origtype = val_type;
989         Lisp_EffiObject *ffio;
990         void *ptr;
991
992         CHECK_EFFIO(fo);
993         CHECK_INT(offset);
994
995         ffio = XEFFIO(fo);
996         ptr = (void*)((char*)ffio->fop.ptr + XINT(offset));
997
998         val_type = ffi_canonicalise_type(val_type);
999
1000         /* Translate value */
1001         val = apply1(Findirect_function(Qffi_translate_to_foreign),
1002                      list2(val, origtype));
1003
1004         if (EQ(val_type, Qchar) || EQ(val_type, Qunsigned_char)) {
1005                 if (!CHARP(val)) {
1006                         SIGNAL_ERROR(Qwrong_type_argument,
1007                                      list2(Qcharacterp, val));
1008                 }
1009                 *(char*)ptr = XCHAR(val);
1010         } else if (EQ(val_type, Qbyte) || EQ(val_type, Qunsigned_byte)) {
1011                 if (!INTP(val)) {
1012                         SIGNAL_ERROR(Qwrong_type_argument,
1013                                      list2(Qintegerp, val));
1014                 }
1015                 *(char*)ptr = XINT(val);
1016         } else if (EQ(val_type, Qshort) || EQ(val_type, Qunsigned_short)) {
1017                 if (!INTP(val)) {
1018                         SIGNAL_ERROR(Qwrong_type_argument,
1019                                      list2(Qintegerp, val));
1020                 }
1021                 *(short*)ptr = (short)XINT(val);
1022         } else if (EQ(val_type, Qint) || EQ(val_type, Qunsigned_int)) {
1023                 if (INTP(val)) {
1024                         *(int*)ptr = XINT(val);
1025                 } else if (FLOATP(val)) {
1026                         fpfloat tmp = XFLOATINT(val);
1027                         *(int*)ptr = (int)tmp;
1028                 } else {
1029                         SIGNAL_ERROR(Qwrong_type_argument,
1030                                      list2(Qfloatp, val));
1031                 }
1032         } else if (EQ(val_type, Qlong) || EQ(val_type, Qunsigned_long)) {
1033                 if (INTP(val)) {
1034                         *(long*)ptr = (long)XINT(val);
1035                 } else if (FLOATP(val)) {
1036                         fpfloat tmp = XFLOATINT(val);
1037                         *(long*)ptr = (long int)tmp;
1038                 } else {
1039                         SIGNAL_ERROR(Qwrong_type_argument, list2(Qfloatp, val));
1040                 }
1041         } else if (EQ(val_type, Qfloat)) {
1042                 if (!FLOATP(val))
1043                         SIGNAL_ERROR(Qwrong_type_argument, list2(Qfloatp, val));
1044                 *(float*)ptr = XFLOATINT(val);
1045         } else if (EQ(val_type, Qdouble)) {
1046                 if (!FLOATP(val))
1047                         SIGNAL_ERROR(Qwrong_type_argument, list2(Qfloatp, val));
1048                 *(double*)ptr = XFLOAT_DATA(val);
1049         } else if (EQ(val_type, Qc_string)) {
1050                 char *tmp = NULL;
1051                 int tmplen;
1052                 if (!STRINGP(val))
1053                         SIGNAL_ERROR(Qwrong_type_argument, list2(Qstringp, val));
1054 #if defined(MULE)
1055                 TO_EXTERNAL_FORMAT(LISP_STRING, val,
1056                                    ALLOCA, (tmp, tmplen), Qnil);
1057                 if ( tmp != NULL ) {
1058                              memcpy((char*)ptr, tmp, tmplen + 1);
1059                 }
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, Qc_data) ||
1066                    (CONSP(val_type) &&
1067                     EQ(XCAR(val_type), Qc_data) && INTP(XCDR(val_type)))) {
1068                 char *val_ext = NULL;
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 (val_ext == NULL || 
1076                     (CONSP(val_type) && (val_ext_len > XINT(XCDR(val_type))))) {
1077 #ifdef SXEMACS
1078                         error("storage size too small");
1079 #else
1080                         Fsignal(Qrange_error,
1081                                 list2(Qstringp,
1082                                       build_string("storage size too small")));
1083 #endif  /* SXEMACS */
1084                 } else {
1085                         memcpy((char*)ptr, (const char *)val_ext, val_ext_len);
1086                 }
1087         } else if (FFI_POINTERP(val_type)) {
1088                 if (!EFFIOP(val)) {
1089 #ifdef SXEMACS
1090                         signal_simple_error("FFI: Value not of pointer type", \
1091                                             list2(origtype, val));
1092 #else
1093                         Fsignal(Qwrong_type_argument,
1094                                 list2(Qstringp, build_string("type")));
1095 #endif  /* SXEMACS */
1096                 }
1097                 *(void**)ptr = (void*)XEFFIO(val)->fop.ptr;
1098         } else if (CONSP(val_type) && EQ(XCAR(val_type), Qstruct)) {
1099                 if (!EFFIOP(val)) {
1100 #ifdef SXEMACS
1101                         signal_simple_error("FFI: Value not FFI object", \
1102                                             list2(origtype, val));
1103 #else
1104                         Fsignal(Qwrong_type_argument,
1105                                 list2(Qstringp, build_string("type")));
1106 #endif  /* SXEMACS */
1107                 }
1108                 memcpy((char*)ptr, (const char *)XEFFIO(val)->fop.ptr, \
1109                        XINT(Fffi_size_of_type(val_type)));
1110         } else {
1111 #ifdef SXEMACS
1112                 signal_simple_error("FFI: Non basic or pointer type", origtype);
1113 #else
1114                 Fsignal(Qinternal_error,
1115                         list2(Qstringp,
1116                               build_string("non basic or pointer type")));
1117 #endif  /* SXEMACS */
1118         }
1119
1120         return val;
1121 }
1122
1123 DEFUN("ffi-aset", Fffi_aset, 3, 3, 0, /*
1124 Store the element VALUE in FARRAY at index IDX (starting with 0).
1125 */
1126       (farray, idx, value))
1127 {
1128         Lisp_Object type;
1129         
1130         CHECK_EFFIO(farray);
1131         CHECK_INT(idx);
1132         
1133         type = ffi_canonicalise_type(XEFFIO(farray)->type);
1134         if (!FFI_TPTR(type)) {
1135 #ifdef SXEMACS
1136                 signal_simple_error("Not an array type", type);
1137 #else
1138                 signal_error(Qinternal_error, "Not an array type", type);
1139 #endif  /* SXEMACS */
1140         }
1141         if (EQ(type, Qc_string))
1142                 type = Qchar;
1143         else
1144                 type = Fcar(XCDR(type));
1145
1146         return Fffi_store(farray,
1147                           make_int(XINT(Fffi_size_of_type(type)) * XINT(idx)),
1148                           type, value);
1149 }
1150
1151 DEFUN("ffi-address-of", Fffi_address_of, 1, 1, 0, /*
1152 Return the FFI object that stores the address of given FFI object FO.
1153
1154 This is the equivalent of the `&' operator in C.
1155 */
1156       (fo))
1157 {
1158         Lisp_Object newfo = Qnil;
1159         Lisp_EffiObject *ffio, *newffio;
1160         struct gcpro gcpro1;
1161
1162         CHECK_EFFIO(fo);
1163         ffio = XEFFIO(fo);
1164
1165         GCPRO1(newfo);
1166         newfo = Fmake_ffi_object(Qpointer, Qnil);
1167         newffio = XEFFIO(newfo);
1168
1169         newffio->fotype = EFFI_FOT_BIND;
1170         if (FFI_TPTR(ffio->type))
1171                 newffio->fop.ptr = (void*)&ffio->fop.ptr;
1172         else
1173                 newffio->fop.ptr = ffio->fop.ptr;
1174
1175         RETURN_UNGCPRO(newfo);
1176 }
1177
1178 DEFUN("ffi-lisp-object-to-pointer", Fffi_lisp_object_to_pointer, 1, 1, 0, /*
1179 Convert lisp object to FFI pointer.
1180 */
1181       (obj))
1182 {
1183         Lisp_Object newfo = Qnil;
1184         Lisp_EffiObject *newffio;
1185         struct gcpro gcpro1;
1186
1187         GCPRO1(obj);
1188
1189         newfo = Fmake_ffi_object(Qpointer, Qnil);
1190         newffio = XEFFIO(newfo);
1191         newffio->fotype = EFFI_FOT_BIND;
1192         newffio->fop.ptr = (void*)obj;
1193
1194         /* Hold a reference to OBJ in NEWFO's plist */
1195         Fput(newfo, intern("lisp-object"), obj);
1196
1197         RETURN_UNGCPRO(newfo);
1198 }
1199
1200 DEFUN("ffi-pointer-to-lisp-object", Fffi_pointer_to_lisp_object, 1, 1, 0, /*
1201 Convert FFI pointer to lisp object.
1202 */
1203       (ptr))
1204 {
1205         CHECK_EFFIO(ptr);
1206         return (Lisp_Object)XEFFIO(ptr)->fop.ptr;
1207 }
1208
1209 DEFUN("ffi-plist", Fffi_plist, 1, 1, 0, /*
1210 Return properties list for FFI object FO.
1211 */
1212       (fo))
1213 {
1214         CHECK_EFFIO(fo);
1215         return (XEFFIO(fo)->plist);
1216 }
1217
1218 #ifdef HAVE_LIBFFI
1219
1220 static int lf_cindex = 0;
1221
1222 /*
1223  * XXX
1224  *  This will work in most cases.
1225  *  However it might not work for large structures,
1226  *  In general we should allocate these spaces dynamically
1227  */
1228 #define MAX_TYPES_VALUES 1024
1229 /* ex_ffitypes_dummies used for structure types */
1230 static ffi_type ex_ffitypes_dummies[MAX_TYPES_VALUES + 1];
1231 static ffi_type *ex_ffitypes[MAX_TYPES_VALUES + 1];
1232 static void *ex_values[MAX_TYPES_VALUES + 1];
1233
1234 #if SIZEOF_LONG == 4
1235 #  define effi_type_ulong ffi_type_uint32
1236 #  define effi_type_slong ffi_type_sint32
1237 #elif SIZEOF_LONG == 8
1238 #  define effi_type_ulong ffi_type_uint64
1239 #  define effi_type_slong ffi_type_sint64
1240 #endif
1241
1242 static void
1243 extffi_setup_argument(Lisp_Object type, ffi_type **ft)
1244 {
1245         type = ffi_canonicalise_type(type);
1246         if (EQ(type, Qchar) || EQ(type, Qbyte))
1247                 *ft = &ffi_type_schar;
1248         else if (EQ(type, Qunsigned_char) || EQ(type, Qunsigned_byte))
1249                 *ft = &ffi_type_uchar;
1250         else if (EQ(type, Qshort))
1251                 *ft = &ffi_type_sshort;
1252         else if (EQ(type, Qunsigned_short))
1253                 *ft = &ffi_type_ushort;
1254         else if (EQ(type, Qint))
1255                 *ft = &ffi_type_sint;
1256         else if (EQ(type, Qunsigned_int))
1257                 *ft = &ffi_type_uint;
1258         else if (EQ(type, Qunsigned_long))
1259                 *ft = &effi_type_ulong;
1260         else if (EQ(type, Qlong))
1261                 *ft = &effi_type_slong;
1262         else if (EQ(type, Qfloat))
1263                 *ft = &ffi_type_float;
1264         else if (EQ(type, Qdouble))
1265                 *ft = &ffi_type_double;
1266         else if (EQ(type, Qvoid))
1267                 *ft = &ffi_type_void;
1268         else if (FFI_TPTR(type))
1269                 *ft = &ffi_type_pointer;
1270         else if (CONSP(type) && EQ(XCAR(type), Qstruct)) {
1271                 Lisp_Object slots = Fcdr(XCDR(type));
1272                 ffi_type **ntypes;
1273                 int nt_size, i;
1274
1275                 CHECK_CONS(slots);
1276
1277                 nt_size = XINT(Flength(slots)) + 1;
1278                 if (nt_size + lf_cindex > MAX_TYPES_VALUES) {
1279                         lf_cindex = 0;  /* reset cindex */
1280 #ifdef SXEMACS
1281                         error("cindex overflow");
1282 #else
1283                         Fsignal(Qoverflow_error,
1284                                 list2(Qstringp,
1285                                       build_string("cindex overflow")));
1286 #endif  /* SXEMACS */
1287                 }
1288                 ntypes = &ex_ffitypes[lf_cindex];
1289                 *ft = &ex_ffitypes_dummies[lf_cindex];
1290
1291                 /* Update lf_cindex in case TYPE struct contains other
1292                  * structures */
1293                 lf_cindex += nt_size;
1294
1295                 (*ft)->type = FFI_TYPE_STRUCT;
1296                 (*ft)->alignment = ffi_type_align(type);
1297                 (*ft)->elements = ntypes;
1298
1299                 for (i = 0; (i < nt_size) && !NILP(slots); slots = XCDR(slots), i++)
1300                         extffi_setup_argument(Fcar(Fcdr(XCAR(slots))), &ntypes[i]);
1301                 ntypes[i] = NULL;
1302         } else {
1303 #ifdef SXEMACS
1304                 signal_simple_error("Can't setup argument for type", type);
1305 #else
1306                 signal_error(Qinternal_error,
1307                              "Can't setup argument for type", type);
1308 #endif  /* SXEMACS */
1309         }
1310 }
1311
1312 static int
1313 ffi_call_using_libffi(Lisp_Object fo_fun, Lisp_Object ret_fo,
1314                       int in_nargs, Lisp_Object *in_args)
1315 {
1316         Lisp_EffiObject *ffio;
1317         Lisp_Object fft;
1318         ffi_cif cif;
1319         ffi_type *rtype;
1320         void *rvalue;
1321         int i;
1322
1323         lf_cindex = in_nargs;           /* reserve */
1324         for (i = 0; i < in_nargs; i++) {
1325                 ffio = XEFFIO(in_args[i]);
1326                 fft = Fffi_canonicalise_type(ffio->type);
1327                 extffi_setup_argument(fft, &ex_ffitypes[i]);
1328                 if (FFI_TPTR(fft))
1329                         ex_values[i] = &ffio->fop.ptr;
1330                 else
1331                         ex_values[i] = ffio->fop.ptr;
1332         }
1333
1334         ffio = XEFFIO(ret_fo);
1335         fft = Fffi_canonicalise_type(ffio->type);
1336         extffi_setup_argument(fft, &rtype);
1337         if (FFI_TPTR(fft))
1338                 rvalue = &ffio->fop.ptr;
1339         else
1340                 rvalue = ffio->fop.ptr;
1341
1342         if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, in_nargs,
1343                          rtype, ex_ffitypes) == FFI_OK)
1344         {
1345                 stop_async_timeouts();
1346                 ffi_call(&cif, (void(*)(void))XEFFIO(fo_fun)->fop.fun, rvalue,
1347                          ex_values);
1348                 start_async_timeouts();
1349                 return 0;
1350         }
1351
1352         /* FAILURE */
1353         return 1;
1354 }
1355 #endif  /* HAVE_LIBFFI */
1356
1357 DEFUN("ffi-call-function", Fffi_call_function, 1, MANY, 0, /*
1358 Call a function referred to by FO with arguments ARGS, maybe
1359 return a foreign object with the result or nil if there is
1360 none.
1361
1362 Arguments are: FO &rest FO-ARGS
1363
1364 FO should be a foreign binding initiated by `ffi-defun', and
1365 ARGS should be foreign data objects or pointers to these.
1366 */
1367       (int nargs, Lisp_Object * args))
1368 {
1369         Lisp_Object faf = Qnil, retfo = Qnil;
1370         Lisp_EffiObject *ffio;
1371         int ret = -1;
1372         struct gcpro gcpro1, gcpro2;
1373
1374         GCPRO2(faf, retfo);
1375
1376         faf =  args[0];
1377         ffio = XEFFIO(faf);
1378         retfo = Fmake_ffi_object(XCAR(XCDR(ffio->type)), Qnil);
1379
1380 #ifdef HAVE_LIBFFI
1381         ret = ffi_call_using_libffi(args[0], retfo, nargs-1, &args[1]);
1382 #endif  /* HAVE_LIBFFI */
1383
1384         RETURN_UNGCPRO(ret == 0 ? retfo : Qnil);
1385 }
1386
1387 #ifdef EF_USE_ASYNEQ
1388 /* handler for asynchronously calling ffi code */
1389 Lisp_Object Qffi_jobp;
1390 #define EFFI_DEBUG_JOB(args...)
1391 static Lisp_Object
1392 exec_sentinel_unwind(Lisp_Object SXE_UNUSED(datum))
1393 {
1394         return Qnil;
1395 }
1396
1397 static inline void
1398 exec_sentinel(void *job, ffi_job_t ffij)
1399         __attribute__((always_inline));
1400 static inline void
1401 exec_sentinel(void *job, ffi_job_t ffij)
1402 {
1403         /* This function can GC */
1404         /* called from main thread */
1405         int speccount = specpdl_depth(), nargs = ffij->sntnl_nargs, i;
1406         Lisp_Object funcell[nargs+2];
1407         struct gcpro gcpro1;
1408
1409         funcell[0] = ffij->sntnl;
1410         funcell[1] = (Lisp_Object)job;
1411         for (i = 0; i < nargs; i++) {
1412                 funcell[2+i] = ffij->sntnl_args[i];
1413         }
1414         GCPROn(funcell, nargs+2);
1415
1416         record_unwind_protect(exec_sentinel_unwind, Qnil);
1417         /* call the funcell */
1418         Ffuncall(nargs+2, funcell);
1419         /* reset to previous state */
1420         restore_match_data();
1421         UNGCPRO;
1422         unbind_to(speccount, Qnil);
1423         return;
1424 }
1425
1426 static inline ffi_job_t
1427 allocate_ffi_job(void)
1428 {
1429         ffi_job_t ffij = xnew(struct ffi_job_s);
1430         EFFI_DEBUG_JOB("allocated: 0x%lx\n", (long unsigned int)ffij);
1431         return ffij;
1432 }
1433
1434 static inline ffi_job_t
1435 make_ffi_job(Lisp_Object fof, int fof_nargs, Lisp_Object *fof_args,
1436              Lisp_Object sntnl, int sntnl_nargs, Lisp_Object *sntnl_args)
1437 {
1438 /* exec'd in the main thread */
1439         ffi_job_t ffij = allocate_ffi_job();
1440         int i;
1441
1442         SXE_MUTEX_INIT(&ffij->mtx);
1443         ffij->fof = fof;
1444         if (fof_nargs > 0) {
1445                 ffij->fof_nargs = fof_nargs;
1446                 ffij->fof_args = xnew_array(Lisp_Object, fof_nargs);
1447                 for (i = 0; i < fof_nargs; i++) {
1448                         ffij->fof_args[i] = fof_args[i];
1449                 }
1450         } else {
1451                 ffij->fof_nargs = 0;
1452                 ffij->fof_args = NULL;
1453         }
1454
1455         ffij->sntnl = sntnl;
1456         if (sntnl_nargs > 0) {
1457                 ffij->sntnl_nargs = sntnl_nargs;
1458                 ffij->sntnl_args = xnew_array(Lisp_Object, sntnl_nargs);
1459                 for (i = 0; i < sntnl_nargs; i++) {
1460                         ffij->sntnl_args[i] = sntnl_args[i];
1461                 }
1462         } else {
1463                 ffij->sntnl_nargs = 0;
1464                 ffij->sntnl_args = NULL;
1465         }
1466
1467         ffij->result = Qnil;
1468         ffij->retfo = Fmake_ffi_object(XCAR(XCDR(XEFFIO(fof)->type)), Qnil);
1469         return ffij;
1470 }
1471
1472 static void
1473 mark_ffi_job(worker_job_t job)
1474 {
1475         ffi_job_t ffij = ffi_job(job);
1476         int i;
1477
1478         if (!ffij)
1479                 return;
1480
1481         SXE_MUTEX_LOCK(&ffij->mtx);
1482         mark_object(ffij->fof);
1483         for (i = 0; i < ffij->fof_nargs; i++) {
1484                 mark_object(ffij->fof_args[i]);
1485         }
1486         mark_object(ffij->sntnl);
1487         for (i = 0; i < ffij->sntnl_nargs; i++) {
1488                 mark_object(ffij->sntnl_args[i]);
1489         }
1490         mark_object(ffij->retfo);
1491         mark_object(ffij->result);
1492         SXE_MUTEX_UNLOCK(&ffij->mtx);
1493         return;
1494 }
1495
1496 static void
1497 print_ffi_job(worker_job_t job, Lisp_Object pcf)
1498 {
1499         ffi_job_t ffij = ffi_job(job);
1500
1501         SXE_MUTEX_LOCK(&ffij->mtx);
1502         WRITE_FMT_STRING(pcf, " carrying  #<ffi-job 0x%lx>", 
1503                          (long unsigned int)ffij);
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(Qpointer, 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, Qc_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, Qvoid)) {
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, Qdouble)) {
1734                 UNGCPRO;
1735                 {
1736                 asm volatile ("fldl (%0)" :: "a" (ptr));
1737                 }
1738                 return;
1739         } else if (EQ(rtype, Qfloat)) {
1740                 UNGCPRO;
1741                 {
1742                 asm volatile ("flds (%0)" :: "a" (ptr));
1743                 }
1744                 return;
1745         } else {
1746                 int iv;
1747
1748                 if (EQ(rtype, Qbyte) || EQ(rtype, Qchar))
1749                         iv = *(char*)ptr;
1750                 else if (EQ(rtype, Qunsigned_byte) || EQ(rtype, Qunsigned_char))
1751                         iv = *(char unsigned*)ptr;
1752                 else if (EQ(rtype, Qshort))
1753                         iv = *(short*)ptr;
1754                 else if (EQ(rtype, Qunsigned_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, Qffi_callback, data);
1825         ptr = Fmake_ffi_object(Qpointer, 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(Qarray);
1838         DEFSYMBOL(Qbyte);
1839         DEFSYMBOL(Qc_data);
1840         DEFSYMBOL(Qc_string);
1841         DEFSYMBOL(Qdouble);
1842         DEFSYMBOL(Qlong);
1843         DEFSYMBOL(Qstruct);
1844         DEFSYMBOL(Qunion);
1845         DEFSYMBOL(Qunsigned_byte);
1846         DEFSYMBOL(Qunsigned_char);
1847         DEFSYMBOL(Qunsigned_int);
1848         DEFSYMBOL(Qunsigned_long);
1849         DEFSYMBOL(Qunsigned_short);
1850
1851         /* ### This is broken, the lrecord needs to be called ffi_object,
1852            and then this would be a DEFSYMBOL_MULTIWORD_PREDICATE(). Not
1853            doing it in this commit, though. */
1854         defsymbol(&Qffiobjectp, "ffi-object-p");
1855
1856         DEFSYMBOL(Qffi_translate_to_foreign);
1857         DEFSYMBOL(Qffi_translate_from_foreign);
1858
1859         DEFSYMBOL(Qffi_callback);
1860
1861         DEFSUBR(Fffi_basic_type_p);
1862         DEFSUBR(Fffi_canonicalise_type);
1863         DEFSUBR(Fffi_size_of_type);
1864         DEFSUBR(Fmake_ffi_object);
1865         DEFSUBR(Fffi_object_p);
1866         DEFSUBR(Fffi_make_pointer);
1867         DEFSUBR(Fffi_object_address);
1868         DEFSUBR(Fffi_object_canonical_type);
1869         DEFSUBR(Fffi_object_type);
1870         DEFSUBR(Fffi_object_size);
1871         DEFSUBR(Fffi_set_storage_size);
1872         DEFSUBR(Fffi_set_object_type);
1873         DEFSUBR(Fffi_fetch);
1874         DEFSUBR(Fffi_aref);
1875         DEFSUBR(Fffi_store);
1876         DEFSUBR(Fffi_aset);
1877         DEFSUBR(Fffi_address_of);
1878         DEFSUBR(Fffi_type_alignment);
1879         DEFSUBR(Fffi_slot_offset);
1880         DEFSUBR(Fffi_load_library);
1881         DEFSUBR(Fffi_bind);
1882         DEFSUBR(Fffi_dlerror);
1883         DEFSUBR(Fffi_defun);
1884         DEFSUBR(Fffi_call_function);
1885
1886         DEFSUBR(Fffi_lisp_object_to_pointer);
1887         DEFSUBR(Fffi_pointer_to_lisp_object);
1888         DEFSUBR(Fffi_plist);
1889
1890 #ifdef EF_USE_ASYNEQ
1891         DEFSUBR(Fffi_call_functionX);
1892         defsymbol(&Qffi_jobp, "ffi-job-p");
1893 #endif
1894
1895         DEFSUBR(Fx_device_display);
1896
1897         DEFSUBR(Fffi_make_callback);
1898 }
1899
1900 void
1901 reinit_vars_of_ffi(void)
1902 {
1903         staticpro_nodump(&Vffi_all_objects);
1904         Vffi_all_objects = make_weak_list(WEAK_LIST_SIMPLE);
1905 }
1906
1907 void
1908 vars_of_ffi(void)
1909 {
1910         reinit_vars_of_ffi();
1911
1912         DEFVAR_LISP("ffi-named-types", &Vffi_named_types        /*
1913 Alist of named FFI types with elements of the form (NAME . FFI-TYPE).
1914                                                  */ );
1915         Vffi_named_types = Qnil;
1916
1917         DEFVAR_LISP("ffi-loaded-libraries", &Vffi_loaded_libraries /*
1918 Alist of loaded libraries with elements of the form (LIB-NAME . FFIO).
1919                                                  */ );
1920         Vffi_loaded_libraries = Qnil;
1921
1922         DEFVAR_LISP("ffi-type-checker", &Vffi_type_checker /*
1923 Function to call when the validity of an FFI type shall be checked.
1924                                                            */ );
1925         Vffi_type_checker = intern("ffi-type-p");
1926 }