+
+ info = pecalloc(1, sizeof(*info), 1);
+ info->eles = psi_plist_init((psi_plist_dtor) psi_ffi_type_free);
+
+ psi_context_composite_type_elements(C, darg, &info->eles);
+
+ /* add terminating NULL; libffi structs do not have an element count */
+ {
+ void *null_ptr = NULL;
+ info->eles = psi_plist_add(info->eles, &null_ptr);
+ }
+
+ info->strct.type = FFI_TYPE_STRUCT;
+ info->strct.alignment = 0;
+ info->strct.size = 0;
+ info->strct.elements = (ffi_type **) psi_plist_eles(info->eles);
+
+ darg->engine.info = info;
+ darg->engine.type = &info->strct;
+
+ return true;
+}
+
+static void psi_ffi_composite_dtor(struct psi_context *C,
+ struct psi_decl_arg *darg)
+{
+ struct psi_ffi_struct_info *info = darg->engine.info;
+
+ if (info) {
+ darg->engine.info = NULL;
+ darg->engine.type = NULL;
+
+ psi_plist_free(info->eles);
+ pefree(info, 1);
+ }
+}
+
+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]);
+}
+
+static bool psi_ffi_decl_init(struct psi_context *, struct psi_decl *);
+
+static bool psi_ffi_extvar_init(struct psi_context *C,
+ struct psi_decl_extvar *evar)
+{
+ struct psi_ffi_extvar_info *info = pecalloc(1, sizeof(*info), 1);
+ ffi_status rc;
+
+ evar->info = info;
+
+ psi_ffi_decl_init(C, evar->getter);
+ psi_ffi_decl_init(C, evar->setter);
+
+ rc = ffi_prep_cif(&info->get.signature, FFI_DEFAULT_ABI, 0,
+ psi_context_decl_arg_call_type(C, evar->getter->func), NULL);
+ if (FFI_OK != rc) {
+ return false;
+ }
+ rc = psi_ffi_prep_closure(&info->get.closure, &info->get.code,
+ &info->get.signature, psi_ffi_extvar_get, evar);
+ if (FFI_OK != rc) {
+ return false;
+ }
+
+ info->set.params[0] = psi_context_decl_arg_call_type(C, evar->arg);
+ rc = ffi_prep_cif(&info->set.signature, FFI_DEFAULT_ABI, 1,
+ &ffi_type_void, info->set.params);
+ if (FFI_OK != rc) {
+ return false;
+ }
+ rc = psi_ffi_prep_closure(&info->set.closure, &info->set.code,
+ &info->set.signature, psi_ffi_extvar_set, evar);
+ if (FFI_OK != rc) {
+ return false;
+ }
+
+ evar->getter->sym = info->get.code;
+ evar->setter->sym = info->set.code;
+
+ return true;
+}
+
+static void psi_ffi_extvar_dtor(struct psi_context *C,
+ struct psi_decl_extvar *evar) {
+ if (evar->info) {
+ pefree(evar->info, 1);
+ evar->info = NULL;
+ }
+}
+
+static bool psi_ffi_decl_init(struct psi_context *C, 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 = pecalloc(1,
+ sizeof(*info) + 2 * c * sizeof(void *), 1);
+
+ decl->info = info;
+
+ for (i = 0; psi_plist_get(decl->args, i, &arg); ++i) {
+ info->params[i] = psi_context_decl_arg_call_type(C, arg);
+ }
+ info->params[c] = NULL;
+
+ rc = ffi_prep_cif(&info->signature, psi_ffi_abi(decl->abi->convention),
+ c, psi_context_decl_arg_call_type(C, decl->func), info->params);
+
+ if (FFI_OK != rc) {
+ pefree(info, 1);
+ decl->info = NULL;
+ return false;
+ }