2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
19 #define MAX_FFI_TYPE_SIZE 16
22 struct dl_handle_t *dlh;
24 ajla_option_t err_type;
26 uchar_efficient_t *ctx_args;
28 ffi_type *ret_ffi_type;
29 ajla_option_t ret_type;
33 struct resource_ffi_structure {
39 static void free_type(ffi_type *ft)
41 if (ft->type == FFI_TYPE_STRUCT) {
42 struct resource_ffi_structure *rfs = get_struct(ft, struct resource_ffi_structure, typ);
43 struct data *da = cast_ptr(struct data *, cast_ptr(char *, rfs) - data_resource_offset);
44 pointer_dereference(pointer_data(da));
48 static void resource_ffi_close(struct data *d)
51 struct resource_ffi *rf = da_resource(d);
53 for (i = 0; i < rf->n_args; i++)
54 free_type(rf->args[i]);
56 mem_free(rf->ctx_args);
57 free_type(rf->ret_ffi_type);
60 static void resource_ffi_structure_close(struct data *d)
63 struct resource_ffi_structure *rfs = da_resource(d);
64 for (i = 0; i < rfs->n_elements; i++)
65 free_type(rfs->typ.elements[i]);
66 mem_free(rfs->typ.elements);
67 mem_free(rfs->offsets);
75 /* this must be in sync with option ffi_type */
80 } ajla_type_to_ffi_type_table[24] = {
81 { &ffi_type_void, 0, T_VOID, },
82 { &ffi_type_uint8, 1, T_UNSIGNED, },
83 { &ffi_type_sint8, 1, T_SIGNED, },
84 { &ffi_type_uint16, 2, T_UNSIGNED, },
85 { &ffi_type_sint16, 2, T_SIGNED, },
86 { &ffi_type_uint32, 4, T_UNSIGNED, },
87 { &ffi_type_sint32, 4, T_SIGNED, },
88 { &ffi_type_uint64, 8, T_UNSIGNED, },
89 { &ffi_type_sint64, 8, T_SIGNED, },
90 { &ffi_type_float, sizeof(float), T_REAL, },
91 { &ffi_type_double, sizeof(double), T_REAL, },
93 { &ffi_type_longdouble, sizeof(real80_t), T_REAL, },
94 #elif REAL_MASK & 0x10
95 { &ffi_type_longdouble, sizeof(real128_t), T_REAL, },
97 { &ffi_type_longdouble, sizeof(double), T_REAL, },
99 { &ffi_type_pointer, sizeof(void *), T_UNSIGNED, },
100 { &ffi_type_uchar, 1, T_UNSIGNED, },
101 { &ffi_type_schar, 1, T_SIGNED, },
102 { &ffi_type_ushort, sizeof(unsigned short), T_UNSIGNED, },
103 { &ffi_type_sshort, sizeof(short), T_SIGNED, },
104 { &ffi_type_uint, sizeof(unsigned), T_UNSIGNED, },
105 { &ffi_type_sint, sizeof(int), T_SIGNED, },
106 { &ffi_type_ulong, sizeof(unsigned long), T_UNSIGNED, },
107 { &ffi_type_slong, sizeof(long), T_SIGNED, },
108 { sizeof(size_t) == 4 ? &ffi_type_uint32 : sizeof(size_t) == 8 ? &ffi_type_uint64 : NULL, sizeof(size_t), T_UNSIGNED, },
109 { sizeof(ssize_t) == 4 ? &ffi_type_sint32 : sizeof(ssize_t) == 8 ? &ffi_type_sint64 : NULL, sizeof(ssize_t), T_SIGNED, },
110 { sizeof(bool) == 1 ? &ffi_type_uint8 : sizeof(bool) == 4 ? &ffi_type_uint32 : NULL, sizeof(bool), T_UNSIGNED, },
113 static const struct type *ajla_type_to_type(ajla_option_t a)
115 if (a >= 1 && a < 9) {
117 return type_get_fixed(a >> 1, !(a & 1));
120 return type_get_real(1);
122 return type_get_real(2);
125 return type_get_real(3);
127 return type_get_real(2);
133 static ffi_type *ajla_type_to_ffi_type(ajla_option_t a, struct data *struc, bool ref)
136 struct resource_ffi_structure *str;
138 pointer_reference_owned(pointer_data(struc));
139 str = da_resource(struc);
142 if (unlikely(a >= n_array_elements(ajla_type_to_ffi_type_table)))
143 internal(file_line, "ajla_type_to_ffi_type: invalid ffi type %u", (unsigned)a);
144 return ajla_type_to_ffi_type_table[a].ft;
147 static void * attr_fastcall io_ffi_get_size_alignment_handler(struct io_ctx *ctx)
155 test = io_deep_eval(ctx, "0", false);
156 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
159 io_get_option(ctx, get_input(ctx, 0), &a, &struc);
161 ft = ajla_type_to_ffi_type(a, struc, false);
162 switch (get_param(ctx, 0)) {
163 case 0: v = ft->size;
165 case 1: v = ft->alignment;
167 default:internal(file_line, "io_ffi_get_size_alignment_handler: invalid parameter %u", (unsigned)get_param(ctx, 0));
170 io_store_typed_number(ctx, get_output(ctx, 0), int_default_t, INT_DEFAULT_N, size_t, v);
171 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
174 test = POINTER_FOLLOW_THUNK_GO;
181 static bool io_ffi_argument(struct io_ctx *ctx, ajla_option_t opt, struct data *struc)
183 if (unlikely(!array_add_mayfail(uchar_efficient_t, &ctx->args, &ctx->args_l, opt, NULL, &ctx->err)))
185 if (unlikely(!array_add_mayfail(void *, &ctx->ptrs, &ctx->ptrs_l, struc, NULL, &ctx->err)))
190 static int_default_t io_get_argument_type_callback(unsigned char *flat, const struct type *type, int_default_t n_elements, pointer_t *ptr, void *ctx_)
192 struct io_ctx *ctx = cast_ptr(struct io_ctx *, ctx_);
195 for (i = n_elements; i; i--, flat += type->size) {
196 if (unlikely(!io_ffi_argument(ctx, *cast_ptr(ajla_flat_option_t *, flat), NULL)))
200 struct data *opt = pointer_get_data(*ptr);
201 pointer_t ptr = da(opt,option)->pointer;
202 struct data *struc = pointer_is_empty(ptr) ? NULL : pointer_get_data(ptr);
203 if (unlikely(!io_ffi_argument(ctx, da(opt,option)->option, struc)))
209 static void io_ffi_free_args(struct io_ctx *ctx, ffi_type **args)
213 for (i = 0; i < ctx->args_l; i++)
220 static ffi_type **io_ffi_load_arg_list(struct io_ctx *ctx, unsigned slot)
226 if (unlikely(!array_init_mayfail(uchar_efficient_t, &ctx->args, &ctx->args_l, &ctx->err)))
229 if (unlikely(!array_init_mayfail(void *, &ctx->ptrs, &ctx->ptrs_l, &ctx->err)))
232 index_from_int(&idx, 0);
233 if (!array_onstack_iterate(ctx->fp, get_input(ctx, slot), &idx, io_get_argument_type_callback, ctx)) {
238 if (unlikely(ctx->args_l >= -1U)) {
239 ctx->err = error_ajla(EC_ASYNC, AJLA_ERROR_SIZE_OVERFLOW);
243 args = mem_alloc_array_mayfail(mem_calloc_mayfail, ffi_type **, 0, 0, ctx->args_l + 1, sizeof(ffi_type *), &ctx->err);
246 for (i = 0; i < ctx->args_l; i++) {
247 if (unlikely(!ctx->args[i])) {
248 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION);
251 args[i] = ajla_type_to_ffi_type(ctx->args[i], ctx->ptrs[i], true);
252 if (unlikely(!args[i]))
259 io_ffi_free_args(ctx, args);
263 static void * attr_fastcall io_ffi_create_structure_handler(struct io_ctx *ctx)
266 struct data *d = NULL;
267 struct data *a = NULL;
268 struct resource_ffi_structure *rfs;
269 ffi_type **args = NULL;
270 size_t *offsets = NULL;
278 test = io_deep_eval(ctx, "0", false);
279 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
282 args = io_ffi_load_arg_list(ctx, 0);
286 test = POINTER_FOLLOW_THUNK_GO;
288 offsets = mem_alloc_array_mayfail(mem_alloc_mayfail, size_t *, 0, 0, ctx->args_l, sizeof(size_t), &ctx->err);
289 if (unlikely(!offsets))
292 d = data_alloc_resource_mayfail(sizeof(struct resource_ffi_structure), resource_ffi_structure_close, &ctx->err pass_file_line);
295 rfs = da_resource(d);
297 rfs->n_elements = ctx->args_l;
298 rfs->offsets = offsets;
300 rfs->typ.alignment = 0;
301 rfs->typ.type = FFI_TYPE_STRUCT;
302 rfs->typ.elements = args;
305 if (unlikely(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 0, &rfs->typ, NULL) != FFI_OK)) {
306 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION);
310 a = data_alloc_array_flat_mayfail(type_get_int(INT_DEFAULT_N), ctx->args_l, ctx->args_l, false, &ctx->err pass_file_line);
315 for (i = 0; i < ctx->args_l; i++) {
317 pos = round_up(pos, rfs->typ.elements[i]->alignment);
320 if (unlikely(p < 0) || unlikely((uint_default_t)p != pos)) {
321 ctx->err = error_ajla(EC_ASYNC, AJLA_ERROR_SIZE_OVERFLOW);
324 cast_ptr(int_default_t *, da_array_flat(a))[i] = p;
325 pos += rfs->typ.elements[i]->size;
326 if (unlikely(pos < rfs->typ.elements[i]->size)) {
327 ctx->err = error_ajla(EC_ASYNC, AJLA_ERROR_SIZE_OVERFLOW);
332 frame_set_pointer(ctx->fp, get_output(ctx, 0), pointer_data(d));
334 frame_set_pointer(ctx->fp, get_output(ctx, 1), pointer_data(a));
338 test = POINTER_FOLLOW_THUNK_GO;
347 io_ffi_free_args(ctx, args);
355 io_terminate_with_error(ctx, ctx->err, true, NULL);
356 test = POINTER_FOLLOW_THUNK_EXCEPTION;
360 static void * attr_fastcall io_ffi_structure_offset_handler(struct io_ctx *ctx)
364 struct resource_ffi_structure *s;
368 test = io_deep_eval(ctx, "01", false);
369 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
372 ptr = *frame_pointer(ctx->fp, get_input(ctx, 0));
373 ajla_assert_lo(!pointer_is_thunk(ptr), (file_line, "io_ffi_structure_offset_handler: pointer is thunk"));
374 s = da_resource(pointer_get_data(ptr));
376 io_get_number(ctx, get_input(ctx, 1), int_default_t, unsigned, n);
377 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
380 if (unlikely(n >= s->n_elements)) {
381 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION);
385 io_store_typed_number(ctx, get_output(ctx, 0), int_default_t, INT_DEFAULT_N, unsigned, off);
386 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
389 test = POINTER_FOLLOW_THUNK_GO;
395 io_terminate_with_error(ctx, ctx->err, true, NULL);
396 test = POINTER_FOLLOW_THUNK_EXCEPTION;
400 static bool io_get_argument_from_flat(struct io_ctx *ctx, uchar_efficient_t t, int_default_t num);
401 static bool io_get_argument_from_longint(struct io_ctx *ctx, uchar_efficient_t t, const mpint_t *m);
403 static void * attr_fastcall io_ffi_poke_handler(struct io_ctx *ctx)
411 test = io_deep_eval(ctx, "0123", true);
412 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
415 io_get_number(ctx, get_input(ctx, 1), int_default_t, uintptr_t, addr);
416 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
419 io_get_option(ctx, get_input(ctx, 2), &a, &struc);
420 if (unlikely(struc != NULL)) {
421 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION);
425 slot = get_input(ctx, 3);
428 ctx->str = num_to_ptr(addr);
429 if (likely(!frame_test_flag(ctx->fp, slot))) {
432 in = *frame_slot(ctx->fp, slot, int_default_t);
434 if (unlikely(!io_get_argument_from_flat(ctx, a, in)))
437 struct data *d = pointer_get_data(*frame_pointer(ctx->fp, slot));
438 if (da_tag(d) == DATA_TAG_flat) {
439 if (unlikely(!io_get_argument_from_flat(ctx, a, *cast_ptr(int_default_t *, da_flat(d)))))
442 if (unlikely(!io_get_argument_from_longint(ctx, a, &da(d,longint)->mp)))
447 test = POINTER_FOLLOW_THUNK_GO;
453 io_terminate_with_error(ctx, ctx->err, true, NULL);
454 test = POINTER_FOLLOW_THUNK_EXCEPTION;
458 static void * attr_fastcall io_ffi_peek_handler(struct io_ctx *ctx)
479 test = io_deep_eval(ctx, "012", true);
480 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
483 io_get_number(ctx, get_input(ctx, 1), int_default_t, uintptr_t, addr);
484 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
486 ad = num_to_ptr(addr);
488 io_get_option(ctx, get_input(ctx, 2), &a, &struc);
489 if (unlikely(struc != NULL)) {
490 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION);
494 slot = get_output(ctx, 1);
496 sz = ajla_type_to_ffi_type_table[a].size;
497 switch (ajla_type_to_ffi_type_table[a].sign) {
499 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION);
503 struct data *dl = data_alloc_longint_mayfail(sz * 8, &ctx->err pass_file_line);
506 mpz_import(&da(dl,longint)->mp, sz, -1, 1, 0, 0, ad);
507 frame_set_pointer(ctx->fp, slot, pointer_data(dl));
513 case 1: io_store_typed_number(ctx, slot, int_default_t, INT_DEFAULT_N, uint8_t, u.u8); if (unlikely(test != POINTER_FOLLOW_THUNK_GO)) goto ret_test; break;
514 case 2: io_store_typed_number(ctx, slot, int_default_t, INT_DEFAULT_N, uint16_t, u.u16); if (unlikely(test != POINTER_FOLLOW_THUNK_GO)) goto ret_test; break;
515 case 4: io_store_typed_number(ctx, slot, int_default_t, INT_DEFAULT_N, uint32_t, u.u32); if (unlikely(test != POINTER_FOLLOW_THUNK_GO)) goto ret_test; break;
516 case 8: io_store_typed_number(ctx, slot, int_default_t, INT_DEFAULT_N, uint64_t, u.u64); if (unlikely(test != POINTER_FOLLOW_THUNK_GO)) goto ret_test; break;
517 default:internal(file_line, "io_ffi_peek_handler: invalid size %"PRIuMAX"", (uintmax_t)sz);
524 case 1: io_store_typed_number(ctx, slot, int_default_t, INT_DEFAULT_N, int8_t, u.s8); if (unlikely(test != POINTER_FOLLOW_THUNK_GO)) goto ret_test; break;
525 case 2: io_store_typed_number(ctx, slot, int_default_t, INT_DEFAULT_N, int16_t, u.s16); if (unlikely(test != POINTER_FOLLOW_THUNK_GO)) goto ret_test; break;
526 case 4: io_store_typed_number(ctx, slot, int_default_t, INT_DEFAULT_N, int32_t, u.s32); if (unlikely(test != POINTER_FOLLOW_THUNK_GO)) goto ret_test; break;
527 case 8: io_store_typed_number(ctx, slot, int_default_t, INT_DEFAULT_N, int64_t, u.s64); if (unlikely(test != POINTER_FOLLOW_THUNK_GO)) goto ret_test; break;
528 default:internal(file_line, "io_ffi_peek_handler: invalid size %"PRIuMAX"", (uintmax_t)sz);
533 internal(file_line, "io_ffi_peek_handler: invalid type %u", (unsigned)a);
536 test = POINTER_FOLLOW_THUNK_GO;
542 io_terminate_with_error(ctx, ctx->err, true, NULL);
543 test = POINTER_FOLLOW_THUNK_EXCEPTION;
548 static int_default_t io_ffi_poke_array_callback(unsigned char *flat, const struct type *type, int_default_t n_elements, pointer_t attr_unused *ptr, void *ctx_)
550 struct io_ctx *ctx = cast_ptr(struct io_ctx *, ctx_);
552 if (unlikely(!TYPE_TAG_IS_FIXED(type->tag)) &&
553 unlikely(!TYPE_TAG_IS_REAL(type->tag))) {
554 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION);
557 ctx->str = mempcpy(ctx->str, flat, (size_t)type->size * (size_t)n_elements);
560 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION);
565 static void * attr_fastcall io_ffi_poke_array_handler(struct io_ctx *ctx)
573 test = io_deep_eval(ctx, "012", true);
574 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
577 io_get_number(ctx, get_input(ctx, 1), int_default_t, uintptr_t, addr);
578 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
580 ctx->str = num_to_ptr(addr);
582 slot = get_input(ctx, 2);
584 index_from_int(&idx, 0);
585 ret = array_onstack_iterate(ctx->fp, slot, &idx, io_ffi_poke_array_callback, ctx);
591 test = POINTER_FOLLOW_THUNK_GO;
597 io_terminate_with_error(ctx, ctx->err, true, NULL);
598 test = POINTER_FOLLOW_THUNK_EXCEPTION;
602 static void * attr_fastcall io_ffi_peek_array_handler(struct io_ctx *ctx)
608 const struct type *type;
612 test = io_deep_eval(ctx, "0123", true);
613 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
616 io_get_number(ctx, get_input(ctx, 1), int_default_t, uintptr_t, addr);
617 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
620 io_get_number(ctx, get_input(ctx, 2), int_default_t, int_default_t, size);
621 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
623 if (unlikely(size < 0)) {
624 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_NEGATIVE_INDEX);
628 io_get_option(ctx, get_input(ctx, 3), &a, &struc);
629 if (unlikely(struc != NULL) || unlikely(!(type = ajla_type_to_type(a)))) {
630 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION);
634 d = data_alloc_array_flat_mayfail(type, size, size, false, &ctx->err pass_file_line);
638 memcpy(da_array_flat(d), num_to_ptr(addr), (size_t)size * type->size);
640 frame_set_pointer(ctx->fp, get_output(ctx, 1), pointer_data(d));
642 test = POINTER_FOLLOW_THUNK_GO;
648 io_terminate_with_error(ctx, ctx->err, true, NULL);
649 test = POINTER_FOLLOW_THUNK_EXCEPTION;
653 static void * attr_fastcall io_ffi_create_function_handler(struct io_ctx *ctx)
656 struct dl_handle_t *dlh = NULL;
658 struct data *d = NULL;
659 struct resource_ffi *rf;
660 char *open_msg = NULL;
661 unsigned attr_unused nvargs;
662 ajla_option_t err_type;
664 struct data *r_struc;
665 ffi_type *ret = NULL;
666 ffi_type **args = NULL;
673 test = io_deep_eval(ctx, "012345", false);
674 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
677 io_get_bytes(ctx, get_input(ctx, 0));
678 io_get_bytes2(ctx, get_input(ctx, 1));
680 dlh = os_dlopen(*ctx->str ? ctx->str : NULL, &ctx->err, &open_msg);
683 if (unlikely(!os_dlsym(dlh, ctx->str2, &sym))) {
684 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_SYMBOL_NOT_FOUND);
688 io_get_option(ctx, get_input(ctx, 2), &err_type, NULL);
690 io_get_positive_number(ctx, ctx->fp, get_input(ctx, 3), unsigned, nvargs);
691 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
694 io_get_option(ctx, get_input(ctx, 4), &rtype, &r_struc);
696 args = io_ffi_load_arg_list(ctx, 5);
700 ret = ajla_type_to_ffi_type(rtype, r_struc, true);
704 d = data_alloc_resource_mayfail(sizeof(struct resource_ffi), resource_ffi_close, &ctx->err pass_file_line);
709 #ifdef HAVE_FFI_PREP_CIF_VAR
712 if (unlikely(ffi_prep_cif(&rf->cif, FFI_DEFAULT_ABI, ctx->args_l, ret, args) != FFI_OK)) {
713 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION);
716 #ifdef HAVE_FFI_PREP_CIF_VAR
718 if (unlikely(ffi_prep_cif_var(&rf->cif, FFI_DEFAULT_ABI, ctx->args_l - nvargs, ctx->args_l, ret, args) != FFI_OK)) {
719 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION);
728 rf->err_type = err_type;
729 rf->ctx_args = ctx->args;
730 rf->n_args = ctx->args_l;
734 rf->ret_ffi_type = ret;
735 rf->ret_type = rtype;
738 frame_set_pointer(ctx->fp, get_output(ctx, 0), pointer_data(d));
741 test = POINTER_FOLLOW_THUNK_GO;
746 io_ffi_free_args(ctx, args);
762 io_terminate_with_error(ctx, ctx->err, true, open_msg);
763 test = POINTER_FOLLOW_THUNK_EXCEPTION;
767 static bool io_add_argument(struct io_ctx *ctx, void *ptr, size_t size)
772 memcpy_fast(ctx->str, ptr, size);
775 cpy = mem_alloc_mayfail(void *, size, &ctx->err);
778 memcpy_fast(cpy, ptr, size);
779 if (unlikely(!array_add_mayfail(void *, &ctx->ptrs, &ctx->ptrs_l, cpy, &xerr, &ctx->err))) {
786 static bool io_get_argument_from_longint(struct io_ctx *ctx, uchar_efficient_t t, const mpint_t *m)
788 size_t size = ajla_type_to_ffi_type_table[t].size;
795 unsigned char pool[MAX_FFI_TYPE_SIZE];
797 /* force alignment */
801 #ifdef HAVE_LONG_LONG
805 #ifdef HAVE_LONG_DOUBLE
810 if (ajla_type_to_ffi_type_table[t].sign == T_REAL) {
811 if (unlikely(mpz_sgn(m) < 0))
813 if (mpz_sizeinbase(m, 2) > 8 * sizeof u.pool)
815 memset(u.pool, 0, sizeof u.pool);
816 mpz_export(u.pool, NULL, -1, 1, 0, 0, m);
821 uns = ajla_type_to_ffi_type_table[t].sign == T_UNSIGNED;
824 if (unlikely(!mpint_export(m, u.pool, idx, &ctx->err)))
827 if (unlikely(!mpint_export_unsigned(m, u.pool, idx, &ctx->err)))
832 return io_add_argument(ctx, u.pool, size);
835 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_DOESNT_FIT);
839 static bool io_get_argument_from_flat(struct io_ctx *ctx, uchar_efficient_t t, int_default_t num)
841 size_t size = ajla_type_to_ffi_type_table[t].size;
845 if (ajla_type_to_ffi_type_table[t].sign == T_REAL) {
848 if (unlikely(!cat(mpint_init_from_,int_default_t)(&m, 0, &ctx->err)))
850 ret = io_get_argument_from_longint(ctx, t, &m);
856 uns = ajla_type_to_ffi_type_table[t].sign == T_UNSIGNED;
858 #define f(n, stype, utype, sz, bits) \
865 if (unlikely(num < 0) || unlikely((uint_default_t)num > (utype)(-1 + zero)))\
869 if (unlikely(num < sign_bit(stype) + zero) || unlikely(num > signed_maximum(stype) + zero))\
873 if (unlikely(!io_add_argument(ctx, &u, sizeof u))) \
880 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED);
887 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_DOESNT_FIT);
891 static int_default_t io_get_argument_callback(unsigned char *flat, const struct type attr_unused *type, int_default_t n_elements, pointer_t *ptr, void *ctx_)
893 struct io_ctx *ctx = cast_ptr(struct io_ctx *, ctx_);
894 if (unlikely(flat != NULL)) {
896 for (i = n_elements; i; i--, flat += sizeof(int_default_t)) {
897 int_default_t num = *cast_ptr(int_default_t *, flat);
898 if (unlikely(!io_get_argument_from_flat(ctx, ctx->rf->ctx_args[ctx->ptrs_l], num)))
902 struct data *d = pointer_get_data(*ptr);
903 if (unlikely(!io_get_argument_from_longint(ctx, ctx->rf->ctx_args[ctx->ptrs_l], &da(d,longint)->mp)))
909 static void * attr_fastcall io_ffi_call_function_handler(struct io_ctx *ctx)
914 struct resource_ffi *rf = NULL; /* avoid warning */
919 unsigned char pool[MAX_FFI_TYPE_SIZE];
921 /* force alignment */
925 #ifdef HAVE_LONG_LONG
929 #ifdef HAVE_LONG_DOUBLE
934 uint32_t e = 0; /* avoid warning */
940 test = io_deep_eval(ctx, "012", true);
941 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
944 ptr = *frame_pointer(ctx->fp, get_input(ctx, 1));
945 ajla_assert_lo(!pointer_is_thunk(ptr), (file_line, "io_ffi_call_function_handler: pointer is thunk"));
946 d = pointer_get_data(ptr);
947 ctx->rf = rf = da_resource(d);
949 if (unlikely(!array_init_mayfail(void *, &ctx->ptrs, &ctx->ptrs_l, &ctx->err)))
952 index_from_int(&idx, 0);
953 if (!array_onstack_iterate(ctx->fp, get_input(ctx, 2), &idx, io_get_argument_callback, ctx)) {
958 if (unlikely(ctx->ptrs_l != rf->n_args)) {
959 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION);
963 ffi_call(&rf->cif, rf->sym, &u.rc, ctx->ptrs);
966 switch (rf->err_type) {
967 case 0: e = 0; break;
968 case 1: e = errno; break;
969 case 2: e = os_get_last_error(); break;
970 case 3: e = os_get_last_socket_error(); break;
972 internal(file_line, "io_ffi_call_function_handler: invalid error specifier %u", (unsigned)rf->err_type);
975 test = POINTER_FOLLOW_THUNK_GO;
978 for (i = 0; i < ctx->ptrs_l; i++)
979 mem_free(ctx->ptrs[i]);
984 if (ajla_type_to_ffi_type_table[rf->ret_type].sign == T_VOID) {
985 io_store_typed_number(ctx, get_output(ctx, 1), int_default_t, INT_DEFAULT_N, int, 0);
986 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
988 } else if (ajla_type_to_ffi_type_table[rf->ret_type].sign == T_REAL) {
989 struct data *dl = data_alloc_longint_mayfail(ajla_type_to_ffi_type_table[rf->ret_type].size * 8, &ctx->err pass_file_line);
991 io_terminate_with_error(ctx, ctx->err, true, NULL);
992 return POINTER_FOLLOW_THUNK_EXCEPTION;
994 mpz_import(&da(dl,longint)->mp, ajla_type_to_ffi_type_table[rf->ret_type].size, -1, 1, 0, 0, &u.pool);
995 frame_set_pointer(ctx->fp, get_output(ctx, 1), pointer_data(dl));
996 } else if (ajla_type_to_ffi_type_table[rf->ret_type].size > sizeof(u.rc)) {
997 if (ajla_type_to_ffi_type_table[rf->ret_type].sign != T_SIGNED) {
998 io_store_typed_number(ctx, get_output(ctx, 1), int_default_t, INT_DEFAULT_N, uint64_t, u.ui64);
999 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1002 io_store_typed_number(ctx, get_output(ctx, 1), int_default_t, INT_DEFAULT_N, int64_t, u.i64);
1003 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1006 } else if (ajla_type_to_ffi_type_table[rf->ret_type].sign != T_SIGNED) {
1007 io_store_typed_number(ctx, get_output(ctx, 1), int_default_t, INT_DEFAULT_N, ffi_arg, u.rc);
1008 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1011 io_store_typed_number(ctx, get_output(ctx, 1), int_default_t, INT_DEFAULT_N, ffi_sarg, u.src);
1012 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1015 io_store_typed_number(ctx, get_output(ctx, 2), int_default_t, INT_DEFAULT_N, uint32_t, e);
1016 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1024 io_terminate_with_error(ctx, ctx->err, true, NULL);
1025 test = POINTER_FOLLOW_THUNK_EXCEPTION;
1029 struct destructor_call {
1035 struct resource_ffi_destructor {
1038 struct destructor_call *calls;
1044 static void resource_ffi_destructor_close(struct data *d)
1047 struct resource_ffi_destructor *rfd = da_resource(d);
1054 unsigned char pool[MAX_FFI_TYPE_SIZE];
1056 /* force alignment */
1060 #ifdef HAVE_LONG_LONG
1064 #ifdef HAVE_LONG_DOUBLE
1068 struct destructor_call *call = &rfd->calls[--i];
1069 struct resource_ffi *rf = da_resource(call->d);
1070 ffi_call(&rf->cif, rf->sym, &u, call->args);
1071 pointer_dereference(pointer_data(call->d));
1072 for (j = 0; j < call->args_l; j++)
1073 mem_free(call->args[j]);
1074 mem_free(call->args);
1076 mem_free(rfd->calls);
1077 for (i = 0; i < rfd->handles_l; i++)
1078 pointer_dereference(rfd->handles[i]);
1079 mem_free(rfd->handles);
1080 for (i = 0; i < rfd->ptrs_l; i++)
1081 mem_free_aligned(rfd->ptrs[i]);
1082 mem_free(rfd->ptrs);
1085 static void * attr_fastcall io_ffi_destructor_new_handler(struct io_ctx *ctx)
1089 struct resource_ffi_destructor *rfd;
1091 test = io_deep_eval(ctx, "0", true);
1092 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1095 d = data_alloc_resource_mayfail(sizeof(struct resource_ffi_destructor), resource_ffi_destructor_close, &ctx->err pass_file_line);
1098 rfd = da_resource(d);
1100 if (unlikely(!array_init_mayfail(void *, &rfd->ptrs, &rfd->ptrs_l, &ctx->err))) {
1104 if (unlikely(!array_init_mayfail(struct destructor_call, &rfd->calls, &rfd->calls_l, &ctx->err))) {
1105 mem_free(rfd->ptrs);
1109 if (unlikely(!array_init_mayfail(pointer_t, &rfd->handles, &rfd->handles_l, &ctx->err))) {
1110 mem_free(rfd->calls);
1111 mem_free(rfd->ptrs);
1116 frame_set_pointer(ctx->fp, get_output(ctx, 1), pointer_data(d));
1118 test = POINTER_FOLLOW_THUNK_GO;
1124 io_terminate_with_error(ctx, ctx->err, true, NULL);
1125 test = POINTER_FOLLOW_THUNK_EXCEPTION;
1129 static void * attr_fastcall io_ffi_destructor_allocate_handler(struct io_ctx *ctx)
1133 struct resource_ffi_destructor *rfd = NULL;
1139 test = io_deep_eval(ctx, "01234", true);
1140 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1143 ptr = *frame_pointer(ctx->fp, get_input(ctx, 1));
1144 ajla_assert_lo(!pointer_is_thunk(ptr), (file_line, "io_ffi_destructor_allocate_handler: pointer is thunk"));
1145 rfd = da_resource(pointer_get_data(ptr));
1147 io_get_number(ctx, get_input(ctx, 2), int_default_t, size_t, size);
1148 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1151 io_get_number(ctx, get_input(ctx, 3), int_default_t, size_t, align);
1152 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1155 io_get_option(ctx, get_input(ctx, 4), &z, NULL);
1157 if (unlikely(!is_power_of_2(align))) {
1158 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION);
1163 n = mem_align_mayfail(void *, size, align, &ctx->err);
1165 n = mem_calign_mayfail(void *, size, align, &ctx->err);
1169 address_lock(rfd, DEPTH_AUX);
1170 if (unlikely(!array_add_mayfail(void *, &rfd->ptrs, &rfd->ptrs_l, n, &old, &ctx->err))) {
1172 address_unlock(rfd, DEPTH_AUX);
1173 mem_free_aligned(n);
1176 address_unlock(rfd, DEPTH_AUX);
1178 io_store_typed_number(ctx, get_output(ctx, 1), int_default_t, INT_DEFAULT_N, uintptr_t, ptr_to_num(n));
1179 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1182 test = POINTER_FOLLOW_THUNK_GO;
1188 io_terminate_with_error(ctx, ctx->err, true, NULL);
1189 test = POINTER_FOLLOW_THUNK_EXCEPTION;
1193 static void * attr_fastcall io_ffi_destructor_free_handler(struct io_ctx *ctx)
1197 struct resource_ffi_destructor *rfd = NULL;
1202 test = io_deep_eval(ctx, "012", true);
1203 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1206 ptr = *frame_pointer(ctx->fp, get_input(ctx, 1));
1207 ajla_assert_lo(!pointer_is_thunk(ptr), (file_line, "io_ffi_destructor_free_handler: pointer is thunk"));
1208 rfd = da_resource(pointer_get_data(ptr));
1210 io_get_number(ctx, get_input(ctx, 2), int_default_t, uintptr_t, nn);
1211 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1215 address_lock(rfd, DEPTH_AUX);
1216 for (i = 0; i < rfd->ptrs_l; i++) {
1217 if (rfd->ptrs[i] == n) {
1218 memmove(&rfd->ptrs[i], &rfd->ptrs[i + 1], (rfd->ptrs_l - i - 1) * sizeof(void *));
1220 address_unlock(rfd, DEPTH_AUX);
1221 mem_free_aligned(n);
1222 test = POINTER_FOLLOW_THUNK_GO;
1226 address_unlock(rfd, DEPTH_AUX);
1228 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION);
1235 io_terminate_with_error(ctx, ctx->err, true, NULL);
1236 test = POINTER_FOLLOW_THUNK_EXCEPTION;
1240 static void * attr_fastcall io_ffi_destructor_call_handler(struct io_ctx *ctx)
1244 struct resource_ffi_destructor *rfd = NULL;
1248 struct destructor_call call;
1253 test = io_deep_eval(ctx, "0123", true);
1254 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1257 ptr = *frame_pointer(ctx->fp, get_input(ctx, 1));
1258 ajla_assert_lo(!pointer_is_thunk(ptr), (file_line, "io_ffi_destructor_call_handler: pointer is thunk"));
1259 rfd = da_resource(pointer_get_data(ptr));
1261 ptr = *frame_pointer(ctx->fp, get_input(ctx, 2));
1262 ajla_assert_lo(!pointer_is_thunk(ptr), (file_line, "io_ffi_destructor_call_handler: pointer is thunk"));
1263 call.d = pointer_get_data(ptr);
1264 ctx->rf = da_resource(call.d);
1266 if (unlikely(!array_init_mayfail(void *, &ctx->ptrs, &ctx->ptrs_l, &ctx->err)))
1269 index_from_int(&idx, 0);
1270 if (!array_onstack_iterate(ctx->fp, get_input(ctx, 3), &idx, io_get_argument_callback, ctx)) {
1275 if (unlikely(ctx->ptrs_l != ctx->rf->n_args)) {
1276 ctx->err = error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION);
1280 call.args = ctx->ptrs;
1281 call.args_l = ctx->ptrs_l;
1283 address_lock(rfd, DEPTH_AUX);
1284 if (unlikely(!array_add_mayfail(struct destructor_call, &rfd->calls, &rfd->calls_l, call, &old, &ctx->err))) {
1286 address_unlock(rfd, DEPTH_AUX);
1289 address_unlock(rfd, DEPTH_AUX);
1293 pointer_reference_owned(pointer_data(call.d));
1295 test = POINTER_FOLLOW_THUNK_GO;
1298 for (i = 0; i < ctx->ptrs_l; i++)
1299 mem_free(ctx->ptrs[i]);
1301 mem_free(ctx->ptrs);
1306 io_terminate_with_error(ctx, ctx->err, true, NULL);
1307 test = POINTER_FOLLOW_THUNK_EXCEPTION;
1311 static void * attr_fastcall io_ffi_handle_to_number_handler(struct io_ctx *ctx)
1314 pointer_t ptr, hptr;
1315 struct resource_ffi_destructor *rfd;
1319 test = io_deep_eval(ctx, "012", true);
1320 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1323 ptr = *frame_pointer(ctx->fp, get_input(ctx, 1));
1324 ajla_assert_lo(!pointer_is_thunk(ptr), (file_line, "io_ffi_handle_to_number_handler: pointer is thunk"));
1325 rfd = da_resource(pointer_get_data(ptr));
1327 hptr = *frame_pointer(ctx->fp, get_input(ctx, 2));
1329 address_lock(rfd, DEPTH_AUX);
1330 if (unlikely(!array_add_mayfail(pointer_t, &rfd->handles, &rfd->handles_l, hptr, &old, &ctx->err))) {
1332 address_unlock(rfd, DEPTH_AUX);
1335 pointer_reference_owned(hptr);
1336 address_unlock(rfd, DEPTH_AUX);
1338 io_get_handle(ctx, get_input(ctx, 2));
1340 n = os_handle_to_number(ctx->handle->fd);
1342 io_store_typed_number(ctx, get_output(ctx, 1), int_default_t, INT_DEFAULT_N, uintptr_t, n);
1343 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1346 test = POINTER_FOLLOW_THUNK_GO;
1352 io_terminate_with_error(ctx, ctx->err, true, NULL);
1353 test = POINTER_FOLLOW_THUNK_EXCEPTION;
1357 static void * attr_fastcall io_ffi_number_to_handle_handler(struct io_ctx *ctx)
1360 uintptr_t n = 0; /* avoid warning */
1363 struct data *d = NULL;
1364 struct resource_handle *h;
1366 test = io_deep_eval(ctx, "012", true);
1367 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1370 io_get_number(ctx, get_input(ctx, 1), int_default_t, uintptr_t, n);
1371 if (unlikely(test != POINTER_FOLLOW_THUNK_GO))
1374 io_get_option(ctx, get_input(ctx, 2), &sckt, NULL);
1376 d = data_alloc_resource_mayfail(sizeof(struct resource_handle), handle_close, &ctx->err pass_file_line);
1381 hn = os_number_to_handle(n, !!sckt, &ctx->err);
1382 if (unlikely(!handle_is_valid(hn)))
1387 frame_set_pointer(ctx->fp, get_output(ctx, 1), pointer_data(d));
1390 test = POINTER_FOLLOW_THUNK_GO;
1398 io_terminate_with_error(ctx, ctx->err, true, NULL);
1399 test = POINTER_FOLLOW_THUNK_EXCEPTION;