MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at mir/inline-method 1467 lines 48 kB view raw
1#ifdef _WIN32 2#define WIN32_LEAN_AND_MEAN 3#include <windows.h> 4#define dlopen(name, flags) ((void *)LoadLibraryA(name)) 5#define dlsym(handle, name) ((void *)GetProcAddress((HMODULE)(handle), (name))) 6#define dlclose(handle) FreeLibrary((HMODULE)(handle)) 7#define dlerror() "LoadLibrary failed" 8#define RTLD_LAZY 0 9#else 10#include <dlfcn.h> 11#endif 12 13#include <ffi.h> 14#include <pthread.h> 15#include <stdbool.h> 16#include <stdint.h> 17#include <stdio.h> 18#include <stdlib.h> 19#include <string.h> 20 21#include "ant.h" 22#include "ptr.h" 23#include "errors.h" 24#include "internal.h" 25#include "silver/engine.h" 26 27#include "modules/buffer.h" 28#include "modules/ffi.h" 29#include "modules/symbol.h" 30 31enum { 32 FFI_LIBRARY_NATIVE_TAG = 0x4646494cu, // FFIL 33 FFI_FUNCTION_NATIVE_TAG = 0x46464946u, // FFIF 34 FFI_POINTER_NATIVE_TAG = 0x46464950u, // FFIP 35 FFI_CALLBACK_NATIVE_TAG = 0x46464943u, // FFIC 36}; 37 38typedef enum { 39 FFI_VALUE_VOID = 0, 40 FFI_VALUE_INT8, 41 FFI_VALUE_INT16, 42 FFI_VALUE_INT, 43 FFI_VALUE_INT64, 44 FFI_VALUE_UINT8, 45 FFI_VALUE_UINT16, 46 FFI_VALUE_UINT64, 47 FFI_VALUE_FLOAT, 48 FFI_VALUE_DOUBLE, 49 FFI_VALUE_POINTER, 50 FFI_VALUE_STRING, 51 FFI_VALUE_SPREAD, 52 FFI_VALUE_UNKNOWN, 53} ffi_value_type_id_t; 54 55typedef struct { 56 ffi_value_type_id_t id; 57 ffi_type *ffi_type; 58 const char *name; 59} ffi_marshaled_type_t; 60 61typedef struct { 62 ffi_marshaled_type_t returns; 63 ffi_marshaled_type_t *args; 64 ffi_type **ffi_arg_types; 65 size_t arg_count; 66 size_t fixed_arg_count; 67 bool variadic; 68} ffi_signature_t; 69 70typedef struct { 71 ant_value_t obj; 72 void *handle; 73 char *path; 74 bool closed; 75} ffi_library_handle_t; 76 77typedef struct { 78 ffi_library_handle_t *library; 79 ffi_signature_t signature; 80 ffi_cif cif; 81 void *func_ptr; 82 char *symbol_name; 83} ffi_function_handle_t; 84 85typedef struct ffi_pointer_region_s { 86 uint8_t *ptr; 87 size_t size; 88 size_t ref_count; 89 bool size_known; 90 bool owned; 91 bool freed; 92} ffi_pointer_region_t; 93 94typedef struct { 95 ffi_pointer_region_t *region; 96 size_t byte_offset; 97} ffi_pointer_handle_t; 98 99typedef struct { 100 ant_t *js; 101 ant_value_t owner_obj; 102 ffi_signature_t signature; 103 ffi_cif cif; 104 ffi_closure *closure; 105 void *code_ptr; 106 pthread_t owner_thread; 107 bool closed; 108} ffi_callback_handle_t; 109 110typedef union { 111 int8_t i8; 112 int16_t i16; 113 int32_t i32; 114 int64_t i64; 115 uint8_t u8; 116 uint16_t u16; 117 uint32_t u32; 118 uint64_t u64; 119 float f32; 120 double f64; 121 void *ptr; 122 ffi_arg raw; 123} ffi_value_box_t; 124 125static ant_value_t g_ffi_library_proto = 0; 126static ant_value_t g_ffi_function_proto = 0; 127static ant_value_t g_ffi_pointer_proto = 0; 128static ant_value_t g_ffi_callback_proto = 0; 129 130static inline bool ffi_is_nullish(ant_value_t value) { 131 return is_null(value) || is_undefined(value); 132} 133 134static ffi_marshaled_type_t ffi_marshaled_type_unknown(void) { 135 ffi_marshaled_type_t type = {0}; 136 type.id = FFI_VALUE_UNKNOWN; 137 type.ffi_type = NULL; 138 type.name = NULL; 139 return type; 140} 141 142static ffi_marshaled_type_t ffi_marshaled_type_make(ffi_value_type_id_t id, const char *name) { 143 ffi_marshaled_type_t type = ffi_marshaled_type_unknown(); 144 type.id = id; 145 type.name = name; 146 147 switch (id) { 148 case FFI_VALUE_VOID: type.ffi_type = &ffi_type_void; break; 149 case FFI_VALUE_INT8: type.ffi_type = &ffi_type_sint8; break; 150 case FFI_VALUE_INT16: type.ffi_type = &ffi_type_sint16; break; 151 case FFI_VALUE_INT: type.ffi_type = &ffi_type_sint32; break; 152 case FFI_VALUE_INT64: type.ffi_type = &ffi_type_sint64; break; 153 case FFI_VALUE_UINT8: type.ffi_type = &ffi_type_uint8; break; 154 case FFI_VALUE_UINT16: type.ffi_type = &ffi_type_uint16; break; 155 case FFI_VALUE_UINT64: type.ffi_type = &ffi_type_uint64; break; 156 case FFI_VALUE_FLOAT: type.ffi_type = &ffi_type_float; break; 157 case FFI_VALUE_DOUBLE: type.ffi_type = &ffi_type_double; break; 158 case FFI_VALUE_POINTER: type.ffi_type = &ffi_type_pointer; break; 159 case FFI_VALUE_STRING: type.ffi_type = &ffi_type_pointer; break; 160 case FFI_VALUE_SPREAD: 161 case FFI_VALUE_UNKNOWN: 162 type.ffi_type = NULL; 163 break; 164 } 165 166 return type; 167} 168 169static ffi_value_type_id_t ffi_type_id_from_name(const char *name) { 170 if (!name) return FFI_VALUE_UNKNOWN; 171 if (strcmp(name, "void") == 0) return FFI_VALUE_VOID; 172 if (strcmp(name, "int8") == 0) return FFI_VALUE_INT8; 173 if (strcmp(name, "int16") == 0) return FFI_VALUE_INT16; 174 if (strcmp(name, "int") == 0) return FFI_VALUE_INT; 175 if (strcmp(name, "int64") == 0) return FFI_VALUE_INT64; 176 if (strcmp(name, "uint8") == 0) return FFI_VALUE_UINT8; 177 if (strcmp(name, "uint16") == 0) return FFI_VALUE_UINT16; 178 if (strcmp(name, "uint64") == 0) return FFI_VALUE_UINT64; 179 if (strcmp(name, "float") == 0) return FFI_VALUE_FLOAT; 180 if (strcmp(name, "double") == 0) return FFI_VALUE_DOUBLE; 181 if (strcmp(name, "pointer") == 0) return FFI_VALUE_POINTER; 182 if (strcmp(name, "string") == 0) return FFI_VALUE_STRING; 183 if (strcmp(name, "...") == 0) return FFI_VALUE_SPREAD; 184 return FFI_VALUE_UNKNOWN; 185} 186 187static ffi_marshaled_type_t ffi_marshaled_type_from_value(ant_t *js, ant_value_t value) { 188 if (vtype(value) != T_STR) return ffi_marshaled_type_unknown(); 189 return ffi_marshaled_type_make( 190 ffi_type_id_from_name(js_getstr(js, value, NULL)), 191 js_getstr(js, value, NULL) 192 ); 193} 194 195static void ffi_signature_cleanup(ffi_signature_t *signature) { 196 if (!signature) return; 197 free(signature->args); 198 free(signature->ffi_arg_types); 199 signature->args = NULL; 200 signature->ffi_arg_types = NULL; 201 signature->arg_count = 0; 202 signature->fixed_arg_count = 0; 203 signature->variadic = false; 204 signature->returns = ffi_marshaled_type_unknown(); 205} 206 207static bool ffi_parse_signature( 208 ant_t *js, 209 ant_value_t value, 210 bool allow_variadic, 211 bool allow_string_return, 212 ffi_signature_t *out, 213 ant_value_t *error_out 214) { 215 ant_value_t returns_val = js_mkundef(); 216 ant_value_t args_val = js_mkundef(); 217 size_t arg_count = 0; 218 219 memset(out, 0, sizeof(*out)); 220 out->returns = ffi_marshaled_type_unknown(); 221 if (error_out) *error_out = js_mkundef(); 222 223 if (!is_object_type(value)) { 224 if (error_out) *error_out = js_mkerr_typed( 225 js, JS_ERR_TYPE, 226 "FFI signature must be [returnType, argTypes] or { returns, args }" 227 ); 228 return false; 229 } 230 231 returns_val = js_get(js, value, "returns"); 232 args_val = js_get(js, value, "args"); 233 234 if (vtype(returns_val) == T_UNDEF || vtype(args_val) == T_UNDEF) { 235 returns_val = js_get(js, value, "0"); 236 args_val = js_get(js, value, "1"); 237 } 238 239 if (vtype(returns_val) != T_STR) { 240 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "FFI return type must be a string"); 241 return false; 242 } 243 244 if (!is_object_type(args_val)) { 245 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "FFI argument list must be an array-like object"); 246 return false; 247 } 248 249 out->returns = ffi_marshaled_type_from_value(js, returns_val); 250 if (out->returns.id == FFI_VALUE_UNKNOWN || out->returns.id == FFI_VALUE_SPREAD) { 251 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported FFI return type"); 252 return false; 253 } 254 255 if (!allow_string_return && out->returns.id == FFI_VALUE_STRING) { 256 if (error_out) *error_out = js_mkerr_typed( 257 js, JS_ERR_TYPE, 258 "FFICallback does not support string return values" 259 ); 260 return false; 261 } 262 263 ant_value_t len_val = js_get(js, args_val, "length"); 264 arg_count = vtype(len_val) == T_NUM ? (size_t)js_getnum(len_val) : 0; 265 266 if (arg_count > 0) { 267 out->args = calloc(arg_count, sizeof(*out->args)); 268 out->ffi_arg_types = calloc(arg_count, sizeof(*out->ffi_arg_types)); 269 if (!out->args || !out->ffi_arg_types) { 270 free(out->args); 271 free(out->ffi_arg_types); 272 out->args = NULL; 273 out->ffi_arg_types = NULL; 274 if (error_out) *error_out = js_mkerr(js, "Out of memory"); 275 return false; 276 } 277 } 278 279 out->arg_count = arg_count; 280 out->fixed_arg_count = arg_count; 281 out->variadic = false; 282 283 for (size_t i = 0; i < arg_count; i++) { 284 char idx[32]; 285 ant_value_t arg_val; 286 ffi_marshaled_type_t arg_type; 287 288 snprintf(idx, sizeof(idx), "%zu", i); 289 arg_val = js_get(js, args_val, idx); 290 arg_type = ffi_marshaled_type_from_value(js, arg_val); 291 292 if (arg_type.id == FFI_VALUE_UNKNOWN) { 293 ffi_signature_cleanup(out); 294 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported FFI argument type"); 295 return false; 296 } 297 298 if (arg_type.id == FFI_VALUE_SPREAD) { 299 if (!allow_variadic) { 300 ffi_signature_cleanup(out); 301 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Variadic signatures are not supported here"); 302 return false; 303 } 304 305 if (i + 1 != arg_count) { 306 ffi_signature_cleanup(out); 307 if (error_out) *error_out = js_mkerr_typed( 308 js, JS_ERR_TYPE, 309 "FFI spread marker must be the last argument type" 310 ); 311 return false; 312 } 313 314 out->variadic = true; 315 out->fixed_arg_count = i; 316 out->arg_count = i; 317 break; 318 } 319 320 out->args[i] = arg_type; 321 out->ffi_arg_types[i] = arg_type.ffi_type; 322 } 323 324 return true; 325} 326 327static ffi_marshaled_type_t ffi_infer_variadic_type(ant_value_t value) { 328 if (vtype(value) == T_STR) return ffi_marshaled_type_make(FFI_VALUE_STRING, "string"); 329 if (ffi_is_nullish(value)) return ffi_marshaled_type_make(FFI_VALUE_POINTER, "pointer"); 330 331 if (is_object_type(value) && js_check_native_tag(value, FFI_POINTER_NATIVE_TAG)) 332 return ffi_marshaled_type_make(FFI_VALUE_POINTER, "pointer"); 333 334 if (is_object_type(value) && js_check_native_tag(value, FFI_CALLBACK_NATIVE_TAG)) 335 return ffi_marshaled_type_make(FFI_VALUE_POINTER, "pointer"); 336 337 if (vtype(value) == T_NUM) { 338 double number = js_getnum(value); 339 double truncated = js_to_int32(number); 340 if (number == truncated) return ffi_marshaled_type_make(FFI_VALUE_INT, "int"); 341 return ffi_marshaled_type_make(FFI_VALUE_DOUBLE, "double"); 342 } 343 344 if (vtype(value) == T_BOOL) return ffi_marshaled_type_make(FFI_VALUE_INT, "int"); 345 return ffi_marshaled_type_make(FFI_VALUE_POINTER, "pointer"); 346} 347 348static ffi_library_handle_t *ffi_library_data(ant_value_t value) { 349 if (!js_check_native_tag(value, FFI_LIBRARY_NATIVE_TAG)) return NULL; 350 return (ffi_library_handle_t *)js_get_native_ptr(value); 351} 352 353static ffi_function_handle_t *ffi_function_data(ant_value_t value) { 354 if (!js_check_native_tag(value, FFI_FUNCTION_NATIVE_TAG)) return NULL; 355 return (ffi_function_handle_t *)js_get_native_ptr(value); 356} 357 358static ffi_pointer_handle_t *ffi_pointer_data(ant_value_t value) { 359 if (!js_check_native_tag(value, FFI_POINTER_NATIVE_TAG)) return NULL; 360 return (ffi_pointer_handle_t *)js_get_native_ptr(value); 361} 362 363static ffi_callback_handle_t *ffi_callback_data(ant_value_t value) { 364 if (!js_check_native_tag(value, FFI_CALLBACK_NATIVE_TAG)) return NULL; 365 return (ffi_callback_handle_t *)js_get_native_ptr(value); 366} 367 368static void ffi_library_close_handle(ffi_library_handle_t *library) { 369 if (!library || library->closed) return; 370 library->closed = true; 371 if (library->handle) dlclose(library->handle); 372 library->handle = NULL; 373} 374 375static void ffi_pointer_region_release(ffi_pointer_region_t *region) { 376 if (!region) return; 377 if (region->ref_count > 0) region->ref_count--; 378 if (region->ref_count != 0) return; 379 if (region->owned && !region->freed && region->ptr) free(region->ptr); 380 free(region); 381} 382 383static bool ffi_pointer_region_free(ffi_pointer_region_t *region) { 384 if (!region || !region->owned || region->freed) return false; 385 if (region->ptr) free(region->ptr); 386 region->ptr = NULL; 387 region->freed = true; 388 region->size = 0; 389 region->size_known = true; 390 return true; 391} 392 393static void ffi_pointer_close_handle(ffi_pointer_handle_t *handle, bool free_region_memory) { 394 if (!handle) return; 395 if (handle->region && free_region_memory) (void)ffi_pointer_region_free(handle->region); 396 ffi_pointer_region_release(handle->region); 397 handle->region = NULL; 398} 399 400static void ffi_callback_close_handle(ffi_callback_handle_t *callback) { 401 if (!callback || callback->closed) return; 402 callback->closed = true; 403 if (callback->closure) ffi_closure_free(callback->closure); 404 callback->closure = NULL; 405 callback->code_ptr = NULL; 406 ffi_signature_cleanup(&callback->signature); 407} 408 409static uint8_t *ffi_pointer_address_raw(ffi_pointer_handle_t *handle) { 410 if (!handle || !handle->region || handle->region->freed || !handle->region->ptr) return NULL; 411 return handle->region->ptr + handle->byte_offset; 412} 413 414static bool ffi_pointer_ensure_readable( 415 ant_t *js, 416 ffi_pointer_handle_t *handle, 417 size_t size, 418 const char *op, 419 ant_value_t *error_out 420) { 421 size_t remaining = 0; 422 423 if (error_out) *error_out = js_mkundef(); 424 if (!handle || !handle->region) { 425 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Invalid FFIPointer"); 426 return false; 427 } 428 429 if (handle->region->freed) { 430 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "FFIPointer has been freed"); 431 return false; 432 } 433 434 if (!handle->region->ptr) { 435 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Cannot %s through a null pointer", op); 436 return false; 437 } 438 439 if (handle->region->size_known) { 440 if (handle->byte_offset > handle->region->size) { 441 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_RANGE, "Pointer offset is out of bounds"); 442 return false; 443 } 444 445 remaining = handle->region->size - handle->byte_offset; 446 if (size > remaining) { 447 if (error_out) *error_out = js_mkerr_typed( 448 js, JS_ERR_RANGE, 449 "FFIPointer %s would read past the tracked allocation", 450 op 451 ); 452 return false; 453 } 454 } 455 456 return true; 457} 458 459static ant_value_t ffi_make_pointer(ant_t *js, ffi_pointer_region_t *region, size_t byte_offset) { 460 ant_value_t obj = js_mkobj(js); 461 ffi_pointer_handle_t *handle = calloc(1, sizeof(*handle)); 462 463 if (!handle) { 464 ffi_pointer_region_release(region); 465 return js_mkerr(js, "Out of memory"); 466 } 467 468 handle->region = region; 469 handle->byte_offset = byte_offset; 470 if (region) region->ref_count++; 471 472 if (g_ffi_pointer_proto) js_set_proto_init(obj, g_ffi_pointer_proto); 473 js_set_native_ptr(obj, handle); 474 js_set_native_tag(obj, FFI_POINTER_NATIVE_TAG); 475 js_set_finalizer(obj, ffi_pointer_finalize); 476 477 return obj; 478} 479 480static ant_value_t ffi_make_pointer_from_raw(ant_t *js, void *ptr) { 481 ffi_pointer_region_t *region = calloc(1, sizeof(*region)); 482 if (!region) return js_mkerr(js, "Out of memory"); 483 region->ptr = (uint8_t *)ptr; 484 region->owned = false; 485 region->freed = false; 486 region->size_known = false; 487 return ffi_make_pointer(js, region, 0); 488} 489 490static ant_value_t ffi_make_pointer_or_null(ant_t *js, void *ptr) { 491 if (!ptr) return js_mknull(); 492 return ffi_make_pointer_from_raw(js, ptr); 493} 494 495static bool ffi_pointer_from_js( 496 ant_t *js, 497 ant_value_t value, 498 void **out, 499 ant_value_t *error_out 500) { 501 ffi_pointer_handle_t *ptr_handle = NULL; 502 ffi_callback_handle_t *cb_handle = NULL; 503 504 if (error_out) *error_out = js_mkundef(); 505 if (out) *out = NULL; 506 507 if (ffi_is_nullish(value)) return true; 508 509 ptr_handle = ffi_pointer_data(value); 510 if (ptr_handle) { 511 if (ptr_handle->region && ptr_handle->region->freed) { 512 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "FFIPointer has been freed"); 513 return false; 514 } 515 if (out) *out = (void *)ffi_pointer_address_raw(ptr_handle); 516 return true; 517 } 518 519 cb_handle = ffi_callback_data(value); 520 if (cb_handle) { 521 if (cb_handle->closed) { 522 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "FFICallback has been closed"); 523 return false; 524 } 525 if (out) *out = cb_handle->code_ptr; 526 return true; 527 } 528 529 if (error_out) *error_out = js_mkerr_typed( 530 js, JS_ERR_TYPE, 531 "Pointer arguments require FFIPointer, FFICallback, null, or undefined" 532 ); 533 534 return false; 535} 536 537static bool ffi_copy_js_string( 538 ant_t *js, 539 ant_value_t value, 540 char **out, 541 size_t *len_out, 542 ant_value_t *error_out 543) { 544 ant_value_t str_val = js_tostring_val(js, value); 545 const char *src = NULL; 546 size_t len = 0; 547 char *copy = NULL; 548 549 if (error_out) *error_out = js_mkundef(); 550 if (out) *out = NULL; 551 if (len_out) *len_out = 0; 552 553 if (is_err(str_val)) { 554 if (error_out) *error_out = str_val; 555 return false; 556 } 557 558 src = js_getstr(js, str_val, &len); 559 if (!src) { 560 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Invalid string value"); 561 return false; 562 } 563 564 copy = malloc(len + 1); 565 if (!copy) { 566 if (error_out) *error_out = js_mkerr(js, "Out of memory"); 567 return false; 568 } 569 570 memcpy(copy, src, len); 571 copy[len] = '\0'; 572 573 if (out) *out = copy; 574 if (len_out) *len_out = len; 575 576 return true; 577} 578 579static bool ffi_value_to_c( 580 ant_t *js, 581 ant_value_t value, 582 ffi_marshaled_type_t type, 583 ffi_value_box_t *box, 584 void **scratch_alloc, 585 ant_value_t *error_out 586) { 587 void *ptr = NULL; 588 char *str_copy = NULL; 589 590 if (error_out) *error_out = js_mkundef(); 591 if (scratch_alloc) *scratch_alloc = NULL; 592 593 switch (type.id) { 594 case FFI_VALUE_INT8: box->i8 = (int8_t)js_getnum(value); return true; 595 case FFI_VALUE_INT16: box->i16 = (int16_t)js_getnum(value); return true; 596 case FFI_VALUE_INT: box->i32 = (int32_t)js_getnum(value); return true; 597 case FFI_VALUE_INT64: box->i64 = (int64_t)js_getnum(value); return true; 598 case FFI_VALUE_UINT8: box->u8 = (uint8_t)js_getnum(value); return true; 599 case FFI_VALUE_UINT16: box->u16 = (uint16_t)js_getnum(value); return true; 600 case FFI_VALUE_UINT64: box->u64 = (uint64_t)js_getnum(value); return true; 601 case FFI_VALUE_FLOAT: box->f32 = (float)js_getnum(value); return true; 602 case FFI_VALUE_DOUBLE: box->f64 = js_getnum(value); return true; 603 case FFI_VALUE_POINTER: 604 if (!ffi_pointer_from_js(js, value, &ptr, error_out)) return false; 605 box->ptr = ptr; 606 return true; 607 case FFI_VALUE_STRING: 608 if (!ffi_is_nullish(value) && ffi_pointer_from_js(js, value, &ptr, NULL)) { 609 box->ptr = ptr; 610 return true; 611 } 612 if (!ffi_copy_js_string(js, value, &str_copy, NULL, error_out)) return false; 613 if (scratch_alloc) *scratch_alloc = str_copy; 614 box->ptr = str_copy; 615 return true; 616 case FFI_VALUE_VOID: 617 case FFI_VALUE_SPREAD: 618 case FFI_VALUE_UNKNOWN: 619 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported FFI argument conversion"); 620 return false; 621 } 622 623 return false; 624} 625 626static ant_value_t ffi_value_from_c(ant_t *js, const void *value, ffi_marshaled_type_t type) { 627 switch (type.id) { 628 case FFI_VALUE_VOID: return js_mkundef(); 629 case FFI_VALUE_INT8: return js_mknum((double)*(const int8_t *)value); 630 case FFI_VALUE_INT16: return js_mknum((double)*(const int16_t *)value); 631 case FFI_VALUE_INT: return js_mknum((double)*(const int32_t *)value); 632 case FFI_VALUE_INT64: return js_mknum((double)*(const int64_t *)value); 633 case FFI_VALUE_UINT8: return js_mknum((double)*(const uint8_t *)value); 634 case FFI_VALUE_UINT16: return js_mknum((double)*(const uint16_t *)value); 635 case FFI_VALUE_UINT64: return js_mknum((double)*(const uint64_t *)value); 636 case FFI_VALUE_FLOAT: return js_mknum((double)*(const float *)value); 637 case FFI_VALUE_DOUBLE: return js_mknum(*(const double *)value); 638 case FFI_VALUE_POINTER: return ffi_make_pointer_or_null(js, *(void *const *)value); 639 case FFI_VALUE_STRING: { 640 const char *str = *(const char *const *)value; 641 return str ? js_mkstr(js, str, strlen(str)) : js_mknull(); 642 } 643 case FFI_VALUE_SPREAD: 644 case FFI_VALUE_UNKNOWN: 645 return js_mkundef(); 646 } 647 648 return js_mkundef(); 649} 650 651static size_t ffi_marshaled_type_size(ffi_marshaled_type_t type) { 652 switch (type.id) { 653 case FFI_VALUE_STRING: return 1; 654 case FFI_VALUE_VOID: 655 case FFI_VALUE_SPREAD: 656 case FFI_VALUE_UNKNOWN: 657 return 0; 658 default: 659 return type.ffi_type ? type.ffi_type->size : 0; 660 } 661} 662 663static void ffi_zero_return_value(void *ret, ffi_marshaled_type_t type) { 664 size_t size = ffi_marshaled_type_size(type); 665 if (size > 0) memset(ret, 0, size); 666} 667 668static ant_value_t ffi_read_from_pointer(ant_t *js, ffi_pointer_handle_t *handle, ffi_marshaled_type_t type) { 669 ant_value_t err = js_mkundef(); 670 uint8_t *addr = ffi_pointer_address_raw(handle); 671 672 if (type.id == FFI_VALUE_STRING) { 673 size_t len = 0; 674 if (!ffi_pointer_ensure_readable(js, handle, 1, "read", &err)) return err; 675 if (handle->region && handle->region->size_known) { 676 size_t remaining = handle->region->size - handle->byte_offset; 677 const char *nul = memchr(addr, '\0', remaining); 678 if (!nul) return js_mkerr_typed(js, JS_ERR_RANGE, "String read exceeded the tracked allocation"); 679 len = (size_t)(nul - (const char *)addr); 680 } else len = strlen((const char *)addr); 681 return js_mkstr(js, addr, len); 682 } 683 684 if (type.id == FFI_VALUE_POINTER) { 685 if (!ffi_pointer_ensure_readable(js, handle, sizeof(void *), "read", &err)) return err; 686 return ffi_make_pointer_or_null(js, *(void **)addr); 687 } 688 689 if (!ffi_pointer_ensure_readable(js, handle, ffi_marshaled_type_size(type), "read", &err)) return err; 690 return ffi_value_from_c(js, addr, type); 691} 692 693static ant_value_t ffi_write_to_pointer( 694 ant_t *js, 695 ffi_pointer_handle_t *handle, 696 ffi_marshaled_type_t type, 697 ant_value_t value 698) { 699 ant_value_t err = js_mkundef(); 700 uint8_t *addr = ffi_pointer_address_raw(handle); 701 ffi_value_box_t box; 702 void *scratch = NULL; 703 704 memset(&box, 0, sizeof(box)); 705 706 if (type.id == FFI_VALUE_STRING) { 707 char *copy = NULL; 708 size_t len = 0; 709 if (!ffi_copy_js_string(js, value, &copy, &len, &err)) return err; 710 if (!ffi_pointer_ensure_readable(js, handle, len + 1, "write", &err)) { 711 free(copy); 712 return err; 713 } 714 memcpy(addr, copy, len + 1); 715 free(copy); 716 return js_getthis(js); 717 } 718 719 if (!ffi_pointer_ensure_readable(js, handle, ffi_marshaled_type_size(type), "write", &err)) return err; 720 if (!ffi_value_to_c(js, value, type, &box, &scratch, &err)) return err; 721 722 switch (type.id) { 723 case FFI_VALUE_INT8: memcpy(addr, &box.i8, sizeof(box.i8)); break; 724 case FFI_VALUE_INT16: memcpy(addr, &box.i16, sizeof(box.i16)); break; 725 case FFI_VALUE_INT: memcpy(addr, &box.i32, sizeof(box.i32)); break; 726 case FFI_VALUE_INT64: memcpy(addr, &box.i64, sizeof(box.i64)); break; 727 case FFI_VALUE_UINT8: memcpy(addr, &box.u8, sizeof(box.u8)); break; 728 case FFI_VALUE_UINT16: memcpy(addr, &box.u16, sizeof(box.u16)); break; 729 case FFI_VALUE_UINT64: memcpy(addr, &box.u64, sizeof(box.u64)); break; 730 case FFI_VALUE_FLOAT: memcpy(addr, &box.f32, sizeof(box.f32)); break; 731 case FFI_VALUE_DOUBLE: memcpy(addr, &box.f64, sizeof(box.f64)); break; 732 case FFI_VALUE_POINTER: memcpy(addr, &box.ptr, sizeof(box.ptr)); break; 733 default: break; 734 } 735 736 free(scratch); 737 return js_getthis(js); 738} 739 740static ant_value_t ffi_make_function(ant_t *js, ffi_library_handle_t *library, const char *symbol_name, ffi_signature_t *signature, void *func_ptr) { 741 ant_value_t obj = js_mkobj(js); 742 ant_value_t fn = 0; 743 ffi_function_handle_t *handle = calloc(1, sizeof(*handle)); 744 745 if (!handle) { 746 ffi_signature_cleanup(signature); 747 return js_mkerr(js, "Out of memory"); 748 } 749 750 handle->library = library; 751 handle->signature = *signature; 752 handle->func_ptr = func_ptr; 753 handle->symbol_name = strdup(symbol_name); 754 if (!handle->symbol_name) { 755 free(handle); 756 ffi_signature_cleanup(signature); 757 return js_mkerr(js, "Out of memory"); 758 } 759 760 if (!handle->signature.variadic) if ( 761 ffi_prep_cif( 762 &handle->cif, 763 FFI_DEFAULT_ABI, 764 (unsigned int)handle->signature.arg_count, 765 handle->signature.returns.ffi_type, 766 handle->signature.arg_count > 0 ? handle->signature.ffi_arg_types : NULL 767 ) != FFI_OK) { 768 free(handle->symbol_name); 769 ffi_signature_cleanup(&handle->signature); 770 free(handle); 771 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to prepare FFI call interface"); 772 } 773 774 if (g_ffi_function_proto) js_set_proto_init(obj, g_ffi_function_proto); 775 js_set_slot(obj, SLOT_CFUNC, js_mkfun(ffi_function_call)); 776 js_set_native_ptr(obj, handle); 777 js_set_native_tag(obj, FFI_FUNCTION_NATIVE_TAG); 778 js_set_slot_wb(js, obj, SLOT_ENTRIES, library ? library->obj : js_mkundef()); 779 js_set_finalizer(obj, ffi_function_finalize); 780 781 fn = js_obj_to_func(obj); 782 return fn; 783} 784 785void ffi_library_finalize(ant_t *js, ant_object_t *obj) { 786 ffi_library_handle_t *library; 787 if (obj->native.tag != FFI_LIBRARY_NATIVE_TAG) return; 788 789 library = (ffi_library_handle_t *)obj->native.ptr; 790 if (!library) return; 791 792 ffi_library_close_handle(library); 793 free(library->path); 794 free(library); 795 obj->native.ptr = NULL; 796 obj->native.tag = 0; 797} 798 799void ffi_function_finalize(ant_t *js, ant_object_t *obj) { 800 ffi_function_handle_t *handle; 801 if (obj->native.tag != FFI_FUNCTION_NATIVE_TAG) return; 802 803 handle = (ffi_function_handle_t *)obj->native.ptr; 804 if (!handle) return; 805 806 free(handle->symbol_name); 807 ffi_signature_cleanup(&handle->signature); 808 free(handle); 809 obj->native.ptr = NULL; 810 obj->native.tag = 0; 811} 812 813void ffi_pointer_finalize(ant_t *js, ant_object_t *obj) { 814 ffi_pointer_handle_t *handle; 815 if (obj->native.tag != FFI_POINTER_NATIVE_TAG) return; 816 817 handle = (ffi_pointer_handle_t *)obj->native.ptr; 818 if (!handle) return; 819 820 ffi_pointer_close_handle(handle, false); 821 free(handle); 822 obj->native.ptr = NULL; 823 obj->native.tag = 0; 824} 825 826void ffi_callback_finalize(ant_t *js, ant_object_t *obj) { 827 ffi_callback_handle_t *handle; 828 if (obj->native.tag != FFI_CALLBACK_NATIVE_TAG) return; 829 830 handle = (ffi_callback_handle_t *)obj->native.ptr; 831 if (!handle) return; 832 833 ffi_callback_close_handle(handle); 834 free(handle); 835 obj->native.ptr = NULL; 836 obj->native.tag = 0; 837} 838 839static bool ffi_callback_result_to_c( 840 ffi_callback_handle_t *callback, 841 ant_value_t result, 842 void *ret, 843 ant_value_t *error_out 844) { 845 ffi_value_box_t box; 846 ant_value_t err = js_mkundef(); 847 void *scratch = NULL; 848 849 memset(&box, 0, sizeof(box)); 850 if (error_out) *error_out = js_mkundef(); 851 if (callback->signature.returns.id == FFI_VALUE_VOID) return true; 852 853 if (!ffi_value_to_c(callback->js, result, callback->signature.returns, &box, &scratch, &err)) { 854 if (error_out) *error_out = err; 855 free(scratch); 856 return false; 857 } 858 859 switch (callback->signature.returns.id) { 860 case FFI_VALUE_INT8: memcpy(ret, &box.i8, sizeof(box.i8)); break; 861 case FFI_VALUE_INT16: memcpy(ret, &box.i16, sizeof(box.i16)); break; 862 case FFI_VALUE_INT: memcpy(ret, &box.i32, sizeof(box.i32)); break; 863 case FFI_VALUE_INT64: memcpy(ret, &box.i64, sizeof(box.i64)); break; 864 case FFI_VALUE_UINT8: memcpy(ret, &box.u8, sizeof(box.u8)); break; 865 case FFI_VALUE_UINT16: memcpy(ret, &box.u16, sizeof(box.u16)); break; 866 case FFI_VALUE_UINT64: memcpy(ret, &box.u64, sizeof(box.u64)); break; 867 case FFI_VALUE_FLOAT: memcpy(ret, &box.f32, sizeof(box.f32)); break; 868 case FFI_VALUE_DOUBLE: memcpy(ret, &box.f64, sizeof(box.f64)); break; 869 case FFI_VALUE_POINTER: 870 memcpy(ret, &box.ptr, sizeof(box.ptr)); 871 break; 872 default: 873 free(scratch); 874 return false; 875 } 876 877 free(scratch); 878 return true; 879} 880 881static void ffi_callback_trampoline(ffi_cif *cif, void *ret, void **args, void *user_data) { 882 ffi_callback_handle_t *callback = (ffi_callback_handle_t *)user_data; 883 ant_value_t js_args[32]; 884 ant_value_t fn = js_mkundef(); 885 ant_value_t result = js_mkundef(); 886 size_t argc = 0; 887 888 (void)cif; 889 if (!callback || callback->closed || !callback->js) return; 890 ffi_zero_return_value(ret, callback->signature.returns); 891 argc = callback->signature.arg_count; 892 893 if (!pthread_equal(pthread_self(), callback->owner_thread)) { 894 fprintf(stderr, "ant:ffi callback invoked off the JS thread; returning a zero value\n"); 895 return; 896 } 897 898 if (argc > 32) argc = 32; 899 for (size_t i = 0; i < argc; i++) { 900 js_args[i] = ffi_value_from_c(callback->js, args[i], callback->signature.args[i]); 901 } 902 903 fn = js_get_slot(callback->owner_obj, SLOT_DATA); 904 if (!is_callable(fn)) { 905 fprintf(stderr, "ant:ffi callback target is no longer callable; returning a zero value\n"); 906 return; 907 } 908 909 result = sv_vm_call(callback->js->vm, callback->js, fn, js_mkundef(), js_args, (int)argc, NULL, false); 910 if (is_err(result)) { 911 fprintf(stderr, "ant:ffi callback threw an exception; returning a zero value\n"); 912 callback->js->thrown_exists = 0; 913 return; 914 } 915 916 if (!ffi_callback_result_to_c(callback, result, ret, NULL)) { 917 fprintf(stderr, "ant:ffi callback returned an incompatible value; returning a zero value\n"); 918 } 919} 920 921static void ffi_init_prototypes(ant_t *js) { 922 ant_value_t function_proto = 0; 923 924 if (g_ffi_library_proto) return; 925 926 function_proto = js_get_slot(js_glob(js), SLOT_FUNC_PROTO); 927 if (vtype(function_proto) == T_UNDEF) function_proto = js_get_ctor_proto(js, "Function", 8); 928 929 g_ffi_library_proto = js_mkobj(js); 930 g_ffi_function_proto = js_mkobj(js); 931 g_ffi_pointer_proto = js_mkobj(js); 932 g_ffi_callback_proto = js_mkobj(js); 933 934 if (is_object_type(js->sym.object_proto)) { 935 js_set_proto_init(g_ffi_library_proto, js->sym.object_proto); 936 js_set_proto_init(g_ffi_pointer_proto, js->sym.object_proto); 937 js_set_proto_init(g_ffi_callback_proto, js->sym.object_proto); 938 } 939 940 if (is_object_type(function_proto)) js_set_proto_init(g_ffi_function_proto, function_proto); 941 942 js_set_sym(js, g_ffi_library_proto, get_toStringTag_sym(), ANT_STRING("FFILibrary")); 943 js_set_sym(js, g_ffi_function_proto, get_toStringTag_sym(), ANT_STRING("FFIFunction")); 944 js_set_sym(js, g_ffi_pointer_proto, get_toStringTag_sym(), ANT_STRING("FFIPointer")); 945 js_set_sym(js, g_ffi_callback_proto, get_toStringTag_sym(), ANT_STRING("FFICallback")); 946 947 js_set(js, g_ffi_library_proto, "define", js_mkfun(ffi_library_define)); 948 js_set(js, g_ffi_library_proto, "call", js_mkfun(ffi_library_call)); 949 js_set(js, g_ffi_library_proto, "close", js_mkfun(ffi_library_close)); 950 951 js_set(js, g_ffi_pointer_proto, "address", js_mkfun(ffi_pointer_address)); 952 js_set(js, g_ffi_pointer_proto, "isNull", js_mkfun(ffi_pointer_is_null)); 953 js_set(js, g_ffi_pointer_proto, "read", js_mkfun(ffi_pointer_read)); 954 js_set(js, g_ffi_pointer_proto, "write", js_mkfun(ffi_pointer_write)); 955 js_set(js, g_ffi_pointer_proto, "offset", js_mkfun(ffi_pointer_offset)); 956 js_set(js, g_ffi_pointer_proto, "free", js_mkfun(ffi_pointer_free)); 957 958 js_set(js, g_ffi_callback_proto, "address", js_mkfun(ffi_callback_address)); 959 js_set(js, g_ffi_callback_proto, "close", js_mkfun(ffi_callback_close)); 960 961 js_set(js, g_ffi_function_proto, "address", js_mkfun(ffi_function_address)); 962} 963 964static ant_value_t ffi_dlopen(ant_t *js, ant_value_t *args, int nargs) { 965 ant_value_t path_val = js_mkundef(); 966 const char *path = NULL; 967 968 size_t path_len = 0; 969 void *dl = NULL; 970 971 ffi_library_handle_t *library = NULL; 972 ant_value_t obj = 0; 973 974 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "dlopen(path) requires a library path"); 975 ffi_init_prototypes(js); 976 977 path_val = js_tostring_val(js, args[0]); 978 if (is_err(path_val) || vtype(path_val) != T_STR) return path_val; 979 path = js_getstr(js, path_val, &path_len); 980 if (!path) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid library path"); 981 982 dl = dlopen(path, RTLD_LAZY); 983 if (!dl) return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to load library: %s", dlerror()); 984 985 library = calloc(1, sizeof(*library)); 986 if (!library) { 987 dlclose(dl); 988 return js_mkerr(js, "Out of memory"); 989 } 990 991 library->path = malloc(path_len + 1); 992 if (!library->path) { 993 dlclose(dl); 994 free(library); 995 return js_mkerr(js, "Out of memory"); 996 } 997 998 memcpy(library->path, path, path_len); 999 library->path[path_len] = '\0'; 1000 library->handle = dl; 1001 library->closed = false; 1002 1003 obj = js_mkobj(js); 1004 if (g_ffi_library_proto) js_set_proto_init(obj, g_ffi_library_proto); 1005 library->obj = obj; 1006 js_set_native_ptr(obj, library); 1007 js_set_native_tag(obj, FFI_LIBRARY_NATIVE_TAG); 1008 js_set_finalizer(obj, ffi_library_finalize); 1009 return obj; 1010} 1011 1012ant_value_t ffi_library_close(ant_t *js, ant_value_t *args, int nargs) { 1013 ffi_library_handle_t *library = ffi_library_data(js_getthis(js)); 1014 (void)args; 1015 (void)nargs; 1016 if (!library) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected an FFILibrary"); 1017 ffi_library_close_handle(library); 1018 return js_getthis(js); 1019} 1020 1021ant_value_t ffi_library_define(ant_t *js, ant_value_t *args, int nargs) { 1022 ffi_library_handle_t *library = ffi_library_data(js_getthis(js)); 1023 ffi_signature_t signature; 1024 1025 ant_value_t error = js_mkundef(); 1026 ant_value_t fn = js_mkundef(); 1027 1028 const char *symbol_name = NULL; 1029 void *sym = NULL; 1030 memset(&signature, 0, sizeof(signature)); 1031 1032 if (!library) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected an FFILibrary"); 1033 if (library->closed) return js_mkerr_typed(js, JS_ERR_TYPE, "FFILibrary is closed"); 1034 1035 if (nargs < 2 || vtype(args[0]) != T_STR) { 1036 return js_mkerr_typed(js, JS_ERR_TYPE, "define(name, signature) requires a symbol name and signature"); 1037 } 1038 1039 if (!ffi_parse_signature(js, args[1], true, true, &signature, &error)) return error; 1040 1041 symbol_name = js_getstr(js, args[0], NULL); 1042 sym = dlsym(library->handle, symbol_name); 1043 1044 if (!sym) { 1045 ffi_signature_cleanup(&signature); 1046 return js_mkerr_typed(js, JS_ERR_TYPE, "Symbol '%s' was not found", symbol_name); 1047 } 1048 1049 fn = ffi_make_function(js, library, symbol_name, &signature, sym); 1050 if (is_err(fn)) { 1051 ffi_signature_cleanup(&signature); 1052 return fn; 1053 } 1054 1055 js_set(js, js_getthis(js), symbol_name, fn); 1056 return fn; 1057} 1058 1059ant_value_t ffi_library_call(ant_t *js, ant_value_t *args, int nargs) { 1060 ant_value_t fn = js_mkundef(); 1061 ffi_library_handle_t *library = ffi_library_data(js_getthis(js)); 1062 1063 if (!library) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected an FFILibrary"); 1064 if (library->closed) return js_mkerr_typed(js, JS_ERR_TYPE, "FFILibrary is closed"); 1065 if (nargs < 1 || vtype(args[0]) != T_STR) { 1066 return js_mkerr_typed(js, JS_ERR_TYPE, "call(name, ...args) requires a symbol name"); 1067 } 1068 1069 fn = js_get(js, js_getthis(js), js_getstr(js, args[0], NULL)); 1070 if (!is_callable(fn)) { 1071 return js_mkerr_typed(js, JS_ERR_TYPE, "Symbol '%s' has not been defined", js_getstr(js, args[0], NULL)); 1072 } 1073 1074 return sv_vm_call(js->vm, js, fn, js_mkundef(), args + 1, nargs - 1, NULL, false); 1075} 1076 1077ant_value_t ffi_function_call(ant_t *js, ant_value_t *args, int nargs) { 1078 ffi_function_handle_t *function = ffi_function_data(js->current_func); 1079 ffi_type **call_types = NULL; 1080 ffi_value_box_t *values = NULL; 1081 1082 void **call_args = NULL; 1083 void **scratch = NULL; 1084 1085 ffi_marshaled_type_t *dynamic_types = NULL; 1086 ffi_cif call_cif; 1087 ffi_value_box_t result; 1088 ant_value_t error = js_mkundef(); 1089 1090 int status = FFI_OK; 1091 size_t actual_argc = 0; 1092 1093 memset(&result, 0, sizeof(result)); 1094 1095 if (!function) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected an FFIFunction"); 1096 if (!function->library || function->library->closed) { 1097 return js_mkerr_typed(js, JS_ERR_TYPE, "FFIFunction '%s' belongs to a closed library", function->symbol_name); 1098 } 1099 1100 if (!function->signature.variadic && nargs != (int)function->signature.arg_count) return js_mkerr_typed( 1101 js, JS_ERR_TYPE, 1102 "FFIFunction '%s' expects %zu arguments, got %d", 1103 function->symbol_name, 1104 function->signature.arg_count, 1105 nargs 1106 ); 1107 1108 if (function->signature.variadic && nargs < (int)function->signature.fixed_arg_count) return js_mkerr_typed( 1109 js, JS_ERR_TYPE, 1110 "FFIFunction '%s' expects at least %zu arguments, got %d", 1111 function->symbol_name, 1112 function->signature.fixed_arg_count, 1113 nargs 1114 ); 1115 1116 actual_argc = (size_t)nargs; 1117 if (actual_argc > 0) { 1118 call_types = calloc(actual_argc, sizeof(*call_types)); 1119 values = calloc(actual_argc, sizeof(*values)); 1120 call_args = calloc(actual_argc, sizeof(*call_args)); 1121 scratch = calloc(actual_argc, sizeof(*scratch)); 1122 dynamic_types = calloc(actual_argc, sizeof(*dynamic_types)); 1123 if (!call_types || !values || !call_args || !scratch || !dynamic_types) { 1124 error = js_mkerr(js, "Out of memory"); 1125 goto cleanup; 1126 } 1127 } 1128 1129 for (size_t i = 0; i < actual_argc; i++) { 1130 ffi_marshaled_type_t type = i < function->signature.fixed_arg_count 1131 ? function->signature.args[i] 1132 : ffi_infer_variadic_type(args[i]); 1133 1134 dynamic_types[i] = type; 1135 call_types[i] = type.ffi_type; 1136 call_args[i] = &values[i]; 1137 1138 if (!ffi_value_to_c(js, args[i], type, &values[i], &scratch[i], &error)) goto cleanup; 1139 } 1140 1141 if (function->signature.variadic) { 1142 status = ffi_prep_cif_var( 1143 &call_cif, 1144 FFI_DEFAULT_ABI, 1145 (unsigned int)function->signature.fixed_arg_count, 1146 (unsigned int)actual_argc, 1147 function->signature.returns.ffi_type, 1148 call_types 1149 ); 1150 1151 if (status != FFI_OK) { 1152 error = js_mkerr_typed(js, JS_ERR_TYPE, "Failed to prepare variadic FFI call"); 1153 goto cleanup; 1154 } 1155 1156 ffi_call(&call_cif, function->func_ptr, &result, call_args); 1157 } else ffi_call(&function->cif, function->func_ptr, &result, call_args); 1158 1159cleanup: 1160 if (vtype(error) != T_UNDEF) { 1161 size_t i; 1162 for (i = 0; i < actual_argc; i++) free(scratch ? scratch[i] : NULL); 1163 free(dynamic_types); 1164 free(scratch); 1165 free(call_args); 1166 free(values); 1167 free(call_types); 1168 return error; 1169 } 1170 1171 { 1172 ant_value_t out = ffi_value_from_c(js, &result, function->signature.returns); 1173 size_t i; 1174 for (i = 0; i < actual_argc; i++) free(scratch ? scratch[i] : NULL); 1175 free(dynamic_types); 1176 free(scratch); 1177 free(call_args); 1178 free(values); 1179 free(call_types); 1180 return out; 1181 } 1182} 1183 1184ant_value_t ffi_function_address(ant_t *js, ant_value_t *args, int nargs) { 1185 ffi_function_handle_t *function = ffi_function_data(js_getthis(js)); 1186 (void)args; 1187 (void)nargs; 1188 if (!function) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected an FFIFunction"); 1189 return js_mknum((double)(uintptr_t)function->func_ptr); 1190} 1191 1192static ant_value_t ffi_alloc_memory(ant_t *js, ant_value_t *args, int nargs) { 1193 ffi_pointer_region_t *region = NULL; 1194 size_t size = 0; 1195 1196 if (nargs < 1 || vtype(args[0]) != T_NUM) { 1197 return js_mkerr_typed(js, JS_ERR_TYPE, "alloc(size) requires a numeric size"); 1198 } 1199 1200 size = (size_t)js_getnum(args[0]); 1201 if (size == 0) return js_mkerr_typed(js, JS_ERR_RANGE, "alloc(size) requires a positive size"); 1202 1203 region = calloc(1, sizeof(*region)); 1204 if (!region) return js_mkerr(js, "Out of memory"); 1205 1206 region->ptr = malloc(size); 1207 if (!region->ptr) { 1208 free(region); 1209 return js_mkerr(js, "Out of memory"); 1210 } 1211 1212 region->size = size; 1213 region->owned = true; 1214 region->freed = false; 1215 region->size_known = true; 1216 return ffi_make_pointer(js, region, 0); 1217} 1218 1219static ant_value_t ffi_pointer_value(ant_t *js, ant_value_t *args, int nargs) { 1220 ffi_pointer_region_t *region = NULL; 1221 ant_value_t error = js_mkundef(); 1222 char *str_copy = NULL; 1223 size_t str_len = 0; 1224 const uint8_t *buffer_bytes = NULL; 1225 size_t buffer_len = 0; 1226 1227 if (nargs < 1 || ffi_is_nullish(args[0])) return ffi_make_pointer_from_raw(js, NULL); 1228 if (ffi_pointer_data(args[0])) return args[0]; 1229 1230 if (ffi_callback_data(args[0])) { 1231 void *ptr = NULL; 1232 if (!ffi_pointer_from_js(js, args[0], &ptr, &error)) return error; 1233 return ffi_make_pointer_or_null(js, ptr); 1234 } 1235 1236 region = calloc(1, sizeof(*region)); 1237 if (!region) return js_mkerr(js, "Out of memory"); 1238 1239 if (buffer_source_get_bytes(js, args[0], &buffer_bytes, &buffer_len)) { 1240 region->ptr = (uint8_t *)buffer_bytes; 1241 region->size = buffer_len; 1242 region->size_known = true; 1243 region->owned = false; 1244 region->freed = false; 1245 return ffi_make_pointer(js, region, 0); 1246 } 1247 1248 if (!ffi_copy_js_string(js, args[0], &str_copy, &str_len, &error)) { 1249 free(region); 1250 return error; 1251 } 1252 1253 region->ptr = (uint8_t *)str_copy; 1254 region->size = str_len + 1; 1255 region->size_known = true; 1256 region->owned = true; 1257 region->freed = false; 1258 return ffi_make_pointer(js, region, 0); 1259} 1260 1261ant_value_t ffi_pointer_address(ant_t *js, ant_value_t *args, int nargs) { 1262 ffi_pointer_handle_t *handle = ffi_pointer_data(js_getthis(js)); 1263 (void)args; 1264 (void)nargs; 1265 if (!handle) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected an FFIPointer"); 1266 return js_mknum((double)(uintptr_t)ffi_pointer_address_raw(handle)); 1267} 1268 1269ant_value_t ffi_pointer_is_null(ant_t *js, ant_value_t *args, int nargs) { 1270 ffi_pointer_handle_t *handle = ffi_pointer_data(js_getthis(js)); 1271 (void)args; 1272 (void)nargs; 1273 if (!handle) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected an FFIPointer"); 1274 return js_bool(ffi_pointer_address_raw(handle) == NULL); 1275} 1276 1277ant_value_t ffi_pointer_read(ant_t *js, ant_value_t *args, int nargs) { 1278 ffi_pointer_handle_t *handle = ffi_pointer_data(js_getthis(js)); 1279 ffi_marshaled_type_t type; 1280 1281 if (!handle) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected an FFIPointer"); 1282 type = nargs > 0 ? ffi_marshaled_type_from_value(js, args[0]) : ffi_marshaled_type_make(FFI_VALUE_POINTER, "pointer"); 1283 if (type.id == FFI_VALUE_UNKNOWN || type.id == FFI_VALUE_SPREAD || type.id == FFI_VALUE_VOID) { 1284 return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported FFIPointer.read() type"); 1285 } 1286 return ffi_read_from_pointer(js, handle, type); 1287} 1288 1289ant_value_t ffi_pointer_write(ant_t *js, ant_value_t *args, int nargs) { 1290 ffi_pointer_handle_t *handle = ffi_pointer_data(js_getthis(js)); 1291 ffi_marshaled_type_t type; 1292 1293 if (!handle) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected an FFIPointer"); 1294 if (nargs < 2) { 1295 return js_mkerr_typed(js, JS_ERR_TYPE, "FFIPointer.write(type, value) requires a type and value"); 1296 } 1297 1298 type = ffi_marshaled_type_from_value(js, args[0]); 1299 if (type.id == FFI_VALUE_UNKNOWN || type.id == FFI_VALUE_SPREAD || type.id == FFI_VALUE_VOID) { 1300 return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported FFIPointer.write() type"); 1301 } 1302 1303 return ffi_write_to_pointer(js, handle, type, args[1]); 1304} 1305 1306ant_value_t ffi_pointer_offset(ant_t *js, ant_value_t *args, int nargs) { 1307 ffi_pointer_handle_t *handle = ffi_pointer_data(js_getthis(js)); 1308 ffi_pointer_handle_t *next = NULL; 1309 ant_value_t out = 0; 1310 size_t offset = 0; 1311 1312 if (!handle) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected an FFIPointer"); 1313 if (nargs < 1 || vtype(args[0]) != T_NUM) { 1314 return js_mkerr_typed(js, JS_ERR_TYPE, "FFIPointer.offset(bytes) requires a numeric byte offset"); 1315 } 1316 1317 if (js_getnum(args[0]) < 0) return js_mkerr_typed(js, JS_ERR_RANGE, "FFIPointer.offset() requires a non-negative offset"); 1318 offset = (size_t)js_getnum(args[0]); 1319 1320 if (handle->region && handle->region->size_known && handle->byte_offset + offset > handle->region->size) { 1321 return js_mkerr_typed(js, JS_ERR_RANGE, "FFIPointer.offset() is out of bounds"); 1322 } 1323 1324 out = ffi_make_pointer(js, handle->region, handle->byte_offset + offset); 1325 next = ffi_pointer_data(out); 1326 if (!next) return out; 1327 return out; 1328} 1329 1330ant_value_t ffi_pointer_free(ant_t *js, ant_value_t *args, int nargs) { 1331 ffi_pointer_handle_t *handle = ffi_pointer_data(js_getthis(js)); 1332 (void)args; 1333 (void)nargs; 1334 if (!handle) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected an FFIPointer"); 1335 if (!handle->region || !handle->region->owned) { 1336 return js_mkerr_typed(js, JS_ERR_TYPE, "Only owned FFIPointers can be freed"); 1337 } 1338 if (!ffi_pointer_region_free(handle->region)) { 1339 return js_mkerr_typed(js, JS_ERR_TYPE, "FFIPointer has already been freed"); 1340 } 1341 return js_getthis(js); 1342} 1343 1344static ant_value_t ffi_create_callback(ant_t *js, ant_value_t *args, int nargs) { 1345 ant_value_t signature_val = js_mkundef(); 1346 ant_value_t fn = js_mkundef(); 1347 ant_value_t obj = 0; 1348 ffi_callback_handle_t *callback = NULL; 1349 ant_value_t error = js_mkundef(); 1350 1351 if (nargs < 2) { 1352 return js_mkerr_typed(js, JS_ERR_TYPE, "callback(signature, fn) requires a signature and function"); 1353 } 1354 1355 if (is_callable(args[0]) && is_object_type(args[1])) { 1356 fn = args[0]; 1357 signature_val = args[1]; 1358 } else if (is_object_type(args[0]) && is_callable(args[1])) { 1359 signature_val = args[0]; 1360 fn = args[1]; 1361 } else return js_mkerr_typed(js, JS_ERR_TYPE, "callback(signature, fn) requires a signature object and callable function"); 1362 1363 ffi_init_prototypes(js); 1364 1365 callback = calloc(1, sizeof(*callback)); 1366 if (!callback) return js_mkerr(js, "Out of memory"); 1367 1368 if (!ffi_parse_signature(js, signature_val, false, false, &callback->signature, &error)) { 1369 free(callback); 1370 return error; 1371 } 1372 1373 if (ffi_prep_cif( 1374 &callback->cif, 1375 FFI_DEFAULT_ABI, 1376 (unsigned int)callback->signature.arg_count, 1377 callback->signature.returns.ffi_type, 1378 callback->signature.arg_count > 0 ? callback->signature.ffi_arg_types : NULL 1379 ) != FFI_OK) { 1380 ffi_signature_cleanup(&callback->signature); 1381 free(callback); 1382 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to prepare FFICallback signature"); 1383 } 1384 1385 callback->closure = ffi_closure_alloc(sizeof(*callback->closure), &callback->code_ptr); 1386 if (!callback->closure) { 1387 ffi_signature_cleanup(&callback->signature); 1388 free(callback); 1389 return js_mkerr(js, "Failed to allocate FFICallback closure"); 1390 } 1391 1392 if (ffi_prep_closure_loc(callback->closure, &callback->cif, ffi_callback_trampoline, callback, callback->code_ptr) != FFI_OK) { 1393 ffi_closure_free(callback->closure); 1394 ffi_signature_cleanup(&callback->signature); 1395 free(callback); 1396 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to prepare FFICallback closure"); 1397 } 1398 1399 obj = js_mkobj(js); 1400 if (g_ffi_callback_proto) js_set_proto_init(obj, g_ffi_callback_proto); 1401 callback->js = js; 1402 callback->owner_obj = obj; 1403 callback->owner_thread = pthread_self(); 1404 callback->closed = false; 1405 js_set_native_ptr(obj, callback); 1406 js_set_native_tag(obj, FFI_CALLBACK_NATIVE_TAG); 1407 js_set_slot_wb(js, obj, SLOT_DATA, fn); 1408 js_set_finalizer(obj, ffi_callback_finalize); 1409 return obj; 1410} 1411 1412ant_value_t ffi_callback_address(ant_t *js, ant_value_t *args, int nargs) { 1413 ffi_callback_handle_t *callback = ffi_callback_data(js_getthis(js)); 1414 (void)args; 1415 (void)nargs; 1416 if (!callback) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected an FFICallback"); 1417 return js_mknum((double)(uintptr_t)callback->code_ptr); 1418} 1419 1420ant_value_t ffi_callback_close(ant_t *js, ant_value_t *args, int nargs) { 1421 ffi_callback_handle_t *callback = ffi_callback_data(js_getthis(js)); 1422 (void)args; 1423 (void)nargs; 1424 if (!callback) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected an FFICallback"); 1425 ffi_callback_close_handle(callback); 1426 return js_getthis(js); 1427} 1428 1429ant_value_t ffi_library(ant_t *js) { 1430 ant_value_t ffi_obj = js_mkobj(js); 1431 ant_value_t ffi_types = js_mkobj(js); 1432 const char *suffix = "so"; 1433 1434 ffi_init_prototypes(js); 1435 1436 js_set(js, ffi_obj, "dlopen", js_mkfun(ffi_dlopen)); 1437 js_set(js, ffi_obj, "alloc", js_mkfun(ffi_alloc_memory)); 1438 js_set(js, ffi_obj, "pointer", js_mkfun(ffi_pointer_value)); 1439 js_set(js, ffi_obj, "callback", js_mkfun(ffi_create_callback)); 1440 1441#ifdef __APPLE__ 1442 suffix = "dylib"; 1443#elif defined(_WIN32) 1444 suffix = "dll"; 1445#endif 1446 1447 js_set(js, ffi_obj, "suffix", js_mkstr(js, suffix, strlen(suffix))); 1448 1449 js_set(js, ffi_types, "void", ANT_STRING("void")); 1450 js_set(js, ffi_types, "int8", ANT_STRING("int8")); 1451 js_set(js, ffi_types, "int16", ANT_STRING("int16")); 1452 js_set(js, ffi_types, "int", ANT_STRING("int")); 1453 js_set(js, ffi_types, "int64", ANT_STRING("int64")); 1454 js_set(js, ffi_types, "uint8", ANT_STRING("uint8")); 1455 js_set(js, ffi_types, "uint16", ANT_STRING("uint16")); 1456 js_set(js, ffi_types, "uint64", ANT_STRING("uint64")); 1457 js_set(js, ffi_types, "float", ANT_STRING("float")); 1458 js_set(js, ffi_types, "double", ANT_STRING("double")); 1459 js_set(js, ffi_types, "pointer", ANT_STRING("pointer")); 1460 js_set(js, ffi_types, "string", ANT_STRING("string")); 1461 js_set(js, ffi_types, "spread", ANT_STRING("...")); 1462 1463 js_set(js, ffi_obj, "FFIType", ffi_types); 1464 js_set_sym(js, ffi_obj, get_toStringTag_sym(), ANT_STRING("FFI")); 1465 1466 return ffi_obj; 1467}