+#ifndef PSI_HAVE_FFI_CLOSURE_ALLOC
+# if HAVE_UNISTD_H
+# include <unistd.h>
+# endif
+# if HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+# ifndef MAP_ANONYMOUS
+# define MAP_ANONYMOUS MAP_ANON
+# endif
+# endif
+#endif
+
+static void *psi_ffi_closure_alloc(size_t s, void **code)
+{
+#ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
+ return ffi_closure_alloc(s, code);
+#elif HAVE_MMAP
+ *code = mmap(NULL, s, PROT_EXEC|PROT_WRITE|PROT_READ,
+ MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+ if (MAP_FAILED == *code) {
+ return NULL;
+ }
+ return *code;
+#else
+# error "Neither ffi_closure_alloc() nor mmap() available"
+#endif
+}
+
+static ffi_status psi_ffi_prep_closure(ffi_closure **closure, void **code, ffi_cif *sig, void (*handler)(ffi_cif*,void*,void**,void*), void *data) {
+ *closure = psi_ffi_closure_alloc(sizeof(ffi_closure), code);
+ ZEND_ASSERT(*closure != NULL);
+
+#if PSI_HAVE_FFI_PREP_CLOSURE_LOC
+ return ffi_prep_closure_loc(*closure, sig, handler, data, *code);
+
+#elif PSI_HAVE_FFI_PREP_CLOSURE
+ return ffi_prep_closure(*code, sig, handler, data);
+#else
+# error "Neither ffi_prep_closure() nor ffi_prep_closure_loc() is available"
+#endif
+
+}
+
+static void psi_ffi_closure_free(void *c)
+{
+#ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
+ ffi_closure_free(c);
+#elif HAVE_MMAP
+ munmap(c, sizeof(ffi_closure));
+#endif
+}
+
+static void psi_ffi_handler(ffi_cif *_sig, void *_result, void **_args, void *_data)
+{
+ psi_call(*(zend_execute_data **)_args[0], *(zval **)_args[1], _data);
+}
+
+static void psi_ffi_callback(ffi_cif *_sig, void *_result, void **_args, void *_data)
+{
+ unsigned argc = _sig->nargs;
+ void **argv = _args;
+ ffi_arg *res = _result;
+ let_stmt *let;
+ decl_arg *darg = let->var->arg;
+ decl *decl_cb = darg->type->func;
+ let_callback *cb = let->val->data.callback;
+ impl_arg *iarg = cb->func->arg;
+ size_t i, argc = cb->args->count;
+ zval return_value, *argv = calloc(argc, sizeof(*argv));
+
+ // prepare args for the userland call
+ for (i = 0; i < decl_cb->args->count; ++i) {
+
+ }
+ for (i = 0; i < cb->args->count; ++i) {
+ psi_do_set(&argv[i], cb->args->vals[i]);
+ }
+ zend_fcall_info_argp(iarg->val.zend.cb->fci, argc, argv);
+ zend_fcall_info_call(&iarg->val.zend.cb->fci, &iarg->val.zend.cb->fcc,
+ &return_value, NULL);
+ // marshal return value of the userland call
+ switch (cb->func->type) {
+ case PSI_T_BOOLVAL:
+ break;
+ case PSI_T_INTVAL:
+ break;
+ case PSI_T_FLOATVAL:
+ break;
+ case PSI_T_PATHVAL:
+ case PSI_T_STRVAL:
+ break;
+ case PSI_T_STRLEN:
+ break;
+ case PSI_T_ARRVAL:
+ break;
+ case PSI_T_OBJVAL:
+ break;
+ case PSI_T_CALLBACK:
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE();
+ }
+ darg->ptr = psi_let_val(cb->func->type, iarg, darg->ptr, real_decl_type(darg->type)->strct, &darg->mem);
+}
+
+static inline ffi_type *psi_ffi_decl_arg_type(decl_arg *darg);
+
+typedef struct PSI_LibffiContext {
+ ffi_cif signature;
+ ffi_type *params[2];
+} PSI_LibffiContext;
+
+typedef struct PSI_LibffiCall {
+ void *code;
+ ffi_closure *closure;
+ ffi_cif signature;
+ void *params[1]; /* [type1, type2, NULL, arg1, arg2] ... */
+} PSI_LibffiCall;