+static inline ffi_abi psi_ffi_abi(const char *convention) {
+ if (FFI_LAST_ABI - 2 != FFI_FIRST_ABI) {
+#ifdef HAVE_FFI_STDCALL
+ if (!strcasecmp(convention, "stdcall")) {
+ return FFI_STDCALL;
+ }
+#endif
+#ifdef HAVE_FFI_FASTCALL
+ if (!strcasecmp(convention, "fastcall")) {
+ return FFI_FASTCALL;
+ }
+#endif
+ }
+ return FFI_DEFAULT_ABI;
+}
+
+static inline struct psi_ffi_decl_info *psi_ffi_decl_init(struct psi_decl *decl) {
+ if (!decl->info) {
+ int rc;
+ size_t i, c = psi_plist_count(decl->args);
+ struct psi_decl_arg *arg;
+ struct psi_ffi_decl_info *info = calloc(1, sizeof(*info) + 2 * c * sizeof(void *));
+
+ decl->info = info;
+
+ for (i = 0; psi_plist_get(decl->args, i, &arg); ++i) {
+ info->params[i] = psi_ffi_decl_arg_type(arg, NULL);
+ }
+ info->params[c] = NULL;
+
+ rc = ffi_prep_cif(&info->signature, psi_ffi_abi(decl->abi->convention),
+ c, psi_ffi_decl_arg_type(decl->func, decl), info->params);
+
+ if (FFI_OK != rc) {
+ free(info);
+ decl->info = NULL;
+ }
+ }
+
+ return decl->info;
+}
+
+static inline void psi_ffi_decl_dtor(struct psi_decl *decl) {
+ if (decl->info) {
+ struct psi_ffi_decl_info *info = decl->info;
+
+ if (info->rv_array) {
+ psi_ffi_type_dtor(info->rv_array);
+ }
+ free(decl->info);
+ decl->info = NULL;
+ }
+}
+
+static void psi_ffi_handler(ffi_cif *sig, void *result, void **args, void *data)
+{
+ struct psi_impl *impl = data;
+ struct psi_ffi_impl_info *info = impl->info;
+
+ psi_context_call(info->context, *(zend_execute_data **)args[0], *(zval **)args[1], impl);
+}
+
+static void psi_ffi_callback(ffi_cif *sig, void *result, void **args, void *data)
+{
+ struct psi_ffi_callback_info *cb_info = data;
+ struct psi_call_frame_callback cb_data;
+
+ assert(cb_info->impl_info->frame);
+
+ cb_data.cb = cb_info->let_exp;
+ cb_data.argc = sig->nargs;
+ cb_data.argv = args;
+ cb_data.rval = result;
+
+ psi_call_frame_do_callback(cb_info->impl_info->frame, &cb_data);
+}
+
+static inline void psi_ffi_callback_init(struct psi_ffi_impl_info *impl_info,
+ struct psi_let_exp *let_exp) {
+ struct psi_ffi_callback_info *cb_info;
+ struct psi_ffi_decl_info *decl_info;
+ struct psi_let_callback *cb;
+ struct psi_let_func *fn = NULL;
+ ffi_status rc;
+
+ switch (let_exp->kind) {
+ case PSI_LET_CALLBACK:
+ cb = let_exp->data.callback;
+ if (cb->decl->info) {
+ decl_info = cb->decl->info;
+ } else {
+ decl_info = psi_ffi_decl_init(cb->decl);
+ }
+
+ cb_info = calloc(1, sizeof(*cb_info));
+ cb_info->impl_info = impl_info;
+ cb_info->let_exp = let_exp;
+ rc = psi_ffi_prep_closure(&cb_info->closure, &cb_info->code,
+ &decl_info->signature, psi_ffi_callback, cb_info);
+
+ if (FFI_OK != rc) {
+ free(cb_info);
+ break;
+ }
+ cb->info = cb_info;
+
+ assert(!cb->decl->sym);
+ cb->decl->sym = cb_info->code;
+ fn = cb->func;
+ /* no break */
+
+ case PSI_LET_FUNC:
+ if (!fn) {
+ fn = let_exp->data.func;
+ }
+ if (fn->inner) {
+ size_t i = 0;
+ struct psi_let_exp *inner_let;
+
+ while (psi_plist_get(fn->inner, i++, &inner_let)) {
+ psi_ffi_callback_init(impl_info, inner_let);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static inline void psi_ffi_callback_dtor(struct psi_let_exp *let_exp) {
+ struct psi_let_callback *cb;
+ struct psi_let_func *fn = NULL;
+
+ switch (let_exp->kind) {
+ case PSI_LET_CALLBACK:
+ cb = let_exp->data.callback;
+
+ psi_ffi_decl_dtor(cb->decl);
+
+ if (cb->info) {
+ struct psi_ffi_callback_info *info = cb->info;
+
+ if (info->closure) {
+ psi_ffi_closure_free(info->closure);
+ }
+ free(info);
+ cb->info = NULL;
+ }
+ fn = cb->func;
+ /* no break */
+ case PSI_LET_FUNC:
+ if (!fn) {
+ fn = let_exp->data.func;
+ }
+
+ if (fn->inner) {
+ size_t i = 0;
+ struct psi_let_exp *cb;
+
+ while (psi_plist_get(fn->inner, i++, &cb)) {
+ psi_ffi_callback_dtor(cb);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static inline struct psi_ffi_impl_info *psi_ffi_impl_init(struct psi_impl *impl,
+ struct psi_context *C) {
+ struct psi_ffi_context *context = C->context;
+ struct psi_ffi_impl_info *info = calloc(1, sizeof(*info));
+ struct psi_let_stmt *let;
+ ffi_status rc;
+ size_t l = 0;
+
+ info->context = C;
+
+ rc = psi_ffi_prep_closure(&info->closure, &info->code,
+ &context->signature, psi_ffi_handler, impl);
+
+ if (FFI_OK != rc) {
+ free(info);
+ return NULL;
+ }
+
+ while (psi_plist_get(impl->stmts.let, l++, &let)) {
+ psi_ffi_callback_init(info, let->exp);
+ }
+
+ return impl->info = info;
+}
+
+static inline void psi_ffi_impl_dtor(struct psi_impl *impl) {
+ struct psi_ffi_impl_info *info = impl->info;
+ struct psi_let_stmt *let;
+ size_t j = 0;
+
+ while (psi_plist_get(impl->stmts.let, j++, &let)) {
+ psi_ffi_callback_dtor(let->exp);
+ }
+
+ if (info) {
+ if (info->closure) {
+ psi_ffi_closure_free(info->closure);
+ }
+ free(info);
+ impl->info = NULL;
+ }
+}
+
+static void psi_ffi_extvar_get(ffi_cif *sig, void *result, void **args, void *data) {
+ struct psi_decl_extvar *evar = data;
+
+ psi_decl_extvar_get(evar, result);
+}
+
+static void psi_ffi_extvar_set(ffi_cif *sig, void *result, void **args, void *data) {
+ struct psi_decl_extvar *evar = data;
+
+ psi_decl_extvar_set(evar, args[0]);
+}
+
+struct psi_ffi_extvar_info {
+ struct {
+ ffi_cif signature;
+ void *code;
+ ffi_closure *closure;
+ } get;
+ struct {
+ ffi_cif signature;
+ ffi_type *params[1];
+ void *code;
+ ffi_closure *closure;
+ } set;
+};
+
+static inline ffi_status psi_ffi_extvar_init(struct psi_decl_extvar *evar) {
+ struct psi_ffi_extvar_info *info = calloc(1, sizeof(*info));
+ ffi_type *type;
+ ffi_status rc;
+
+ evar->info = info;
+
+ psi_ffi_decl_init(evar->getter);
+ psi_ffi_decl_init(evar->setter);
+
+ type = psi_ffi_decl_arg_type(evar->arg, evar->getter);
+
+ rc = ffi_prep_cif(&info->get.signature, FFI_DEFAULT_ABI, 0,
+ type, NULL);
+ if (FFI_OK != rc) {
+ return rc;
+ }
+ rc = psi_ffi_prep_closure(&info->get.closure, &info->get.code,
+ &info->get.signature, psi_ffi_extvar_get, evar);
+ if (FFI_OK != rc) {
+ return rc;
+ }
+
+ info->set.params[0] = type;
+ rc = ffi_prep_cif(&info->set.signature, FFI_DEFAULT_ABI, 1,
+ &ffi_type_void, info->set.params);
+ if (FFI_OK != rc) {
+ return rc;
+ }
+ rc = psi_ffi_prep_closure(&info->set.closure, &info->set.code,
+ &info->set.signature, psi_ffi_extvar_set, evar);
+ if (FFI_OK != rc) {
+ return rc;
+ }
+
+ evar->getter->sym = info->get.code;
+ evar->setter->sym = info->set.code;
+
+ return FFI_OK;
+}
+
+static inline void psi_ffi_extvar_dtor(struct psi_decl_extvar *evar) {
+ if (evar->info) {
+ free(evar->info);
+ evar->info = NULL;
+ }
+}