+static void psi_ffi_prep_va(ffi_cif *base, ffi_cif *signature, size_t argc, size_t va_count,
+ ffi_type **param_types) {
+ ffi_status rc;
+
+#ifdef PSI_HAVE_FFI_PREP_CIF_VAR
+ rc = ffi_prep_cif_var(signature, base->abi, argc, argc + va_count,
+ base->rtype, param_types);
+#else
+ /* FIXME: test in config.m4; assume we can just call anyway */
+ rc = ffi_prep_cif(signature, base->abi, argc + va_count, base->rtype, param_types);
+#endif
+
+ assert(FFI_OK == rc);
+}
+
+static inline ffi_type *psi_ffi_decl_arg_type(struct psi_decl_arg *darg);
+
+struct psi_ffi_context {
+ ffi_cif signature;
+ ffi_type *params[2];
+};
+
+struct psi_ffi_call {
+ struct psi_context *context;
+ union {
+ struct {
+ struct psi_impl *impl;
+ struct psi_call_frame *frame;
+ } fn;
+ struct {
+ struct psi_let_exp *let_exp;
+ struct psi_ffi_call *impl_call;
+ } cb;
+ } impl;
+ void *code;
+ ffi_closure *closure;
+ ffi_cif signature;
+ ffi_type *params[1]; /* [type1, type2, ... ] */
+};
+
+static void psi_ffi_handler(ffi_cif *sig, void *result, void **args, void *data)
+{
+ struct psi_ffi_call *call = data;
+
+ psi_context_call(call->context, *(zend_execute_data **)args[0], *(zval **)args[1], call->impl.fn.impl);
+}
+
+static void psi_ffi_callback(ffi_cif *sig, void *result, void **args, void *data)
+{
+ struct psi_ffi_call *call = data, *impl_call = call->impl.cb.impl_call;
+
+ if (impl_call->impl.fn.frame) {
+ struct psi_call_frame_callback cbdata;
+
+ cbdata.cb = call->impl.cb.let_exp;
+ cbdata.argc = sig->nargs;
+ cbdata.argv = args;
+ cbdata.rval = result;
+
+ psi_call_frame_do_callback(impl_call->impl.fn.frame, &cbdata);
+ } else {
+ assert(0);
+ }
+}