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.

ffi callbacks

+318 -1
+49
examples/ffi/sqlite/demo.js
··· 1 + import { sqlite3 } from './sqlite3'; 2 + import { alloc, free, read, callback, freeCallback, readPtr, FFIType } from 'ant:ffi'; 3 + 4 + const dbPtrPtr = alloc(8); 5 + const result = sqlite3.call('sqlite3_open', ':memory:', dbPtrPtr); 6 + 7 + if (result !== 0) { 8 + console.log('Failed to open database'); 9 + free(dbPtrPtr); 10 + } else { 11 + const db = read(dbPtrPtr, FFIType.pointer); 12 + free(dbPtrPtr); 13 + 14 + sqlite3.call('sqlite3_exec', db, 'CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)', 0, 0, 0); 15 + sqlite3.call('sqlite3_exec', db, "INSERT INTO users (name, age) VALUES ('Alice', 30)", 0, 0, 0); 16 + sqlite3.call('sqlite3_exec', db, "INSERT INTO users (name, age) VALUES ('Bob', 25)", 0, 0, 0); 17 + sqlite3.call('sqlite3_exec', db, "INSERT INTO users (name, age) VALUES ('Charlie', 35)", 0, 0, 0); 18 + 19 + console.log('\nQuerying users with callback:'); 20 + 21 + const rowCallback = callback( 22 + function (_, argc, argv, colNames) { 23 + let row = ''; 24 + for (let i = 0; i < argc; i++) { 25 + const colNamePtr = readPtr(colNames + i * 8, FFIType.pointer); 26 + const valuePtr = readPtr(argv + i * 8, FFIType.pointer); 27 + const colName = colNamePtr ? readPtr(colNamePtr, FFIType.string) : 'NULL'; 28 + const value = valuePtr ? readPtr(valuePtr, FFIType.string) : 'NULL'; 29 + row += `${colName}=${value} `; 30 + } 31 + console.log(` Row: ${row}`); 32 + return 0; 33 + }, 34 + { 35 + args: [FFIType.pointer, FFIType.int, FFIType.pointer, FFIType.pointer], 36 + returns: FFIType.int 37 + } 38 + ); 39 + 40 + const execResult = sqlite3.call('sqlite3_exec', db, 'SELECT * FROM users', rowCallback, 0, 0); 41 + 42 + if (execResult !== 0) { 43 + console.log(`Query error: ${sqlite3.call('sqlite3_errmsg', db)}`); 44 + } 45 + 46 + freeCallback(rowCallback); 47 + sqlite3.call('sqlite3_close', db); 48 + console.log('\nDatabase closed.'); 49 + }
+30
examples/ffi/sqlite/sqlite3.js
··· 1 + import { dlopen, suffix, FFIType } from 'ant:ffi'; 2 + 3 + export const sqlite3 = dlopen(`libsqlite3.${suffix}`); 4 + 5 + sqlite3.define('sqlite3_libversion', { 6 + args: [], 7 + returns: FFIType.string 8 + }); 9 + 10 + sqlite3.define('sqlite3_open', { 11 + args: [FFIType.string, FFIType.pointer], 12 + returns: FFIType.int 13 + }); 14 + 15 + sqlite3.define('sqlite3_close', { 16 + args: [FFIType.pointer], 17 + returns: FFIType.int 18 + }); 19 + 20 + sqlite3.define('sqlite3_errmsg', { 21 + args: [FFIType.pointer], 22 + returns: FFIType.string 23 + }); 24 + 25 + sqlite3.define('sqlite3_exec', { 26 + args: [FFIType.pointer, FFIType.string, FFIType.pointer, FFIType.pointer, FFIType.pointer], 27 + returns: FFIType.int 28 + }); 29 + 30 + console.log(`SQLite version: ${sqlite3.call('sqlite3_libversion')}`);
+2 -1
meson.build
··· 50 50 minicoro_dep = subproject('minicoro').get_variable('minicoro_dep') 51 51 52 52 libffi_dep = subproject('libffi', default_options: [ 53 + 'warning_level=0', 53 54 'tests=false' 54 55 ]).get_variable('ffi_dep') 55 56 ··· 73 74 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 74 75 75 76 version_conf = configuration_data() 76 - version_conf.set('ANT_VERSION', '0.0.7.12') 77 + version_conf.set('ANT_VERSION', '0.0.7.13') 77 78 version_conf.set('ANT_GIT_HASH', git_hash) 78 79 version_conf.set('ANT_BUILD_DATE', build_date) 79 80
+237
src/modules/ffi.c
··· 35 35 UT_hash_handle hh; 36 36 } ffi_ptr_t; 37 37 38 + typedef struct ffi_callback { 39 + ffi_closure *closure; 40 + void *code_ptr; 41 + ffi_cif cif; 42 + ffi_type **arg_types; 43 + ffi_type *ret_type; 44 + char ret_type_str[32]; 45 + char **arg_type_strs; 46 + int arg_count; 47 + struct js *js; 48 + jsval_t js_func; 49 + uint64_t cb_key; 50 + UT_hash_handle hh; 51 + } ffi_callback_t; 52 + 38 53 static ffi_lib_t *ffi_libraries = NULL; 39 54 static ffi_ptr_t *ffi_pointers = NULL; 55 + static ffi_callback_t *ffi_callbacks = NULL; 40 56 static UT_array *ffi_functions_array = NULL; 41 57 42 58 static pthread_mutex_t ffi_libraries_mutex = PTHREAD_MUTEX_INITIALIZER; 43 59 static pthread_mutex_t ffi_functions_mutex = PTHREAD_MUTEX_INITIALIZER; 44 60 static pthread_mutex_t ffi_pointers_mutex = PTHREAD_MUTEX_INITIALIZER; 61 + static pthread_mutex_t ffi_callbacks_mutex = PTHREAD_MUTEX_INITIALIZER; 45 62 46 63 static const UT_icd ffi_func_icd = { 47 64 .sz = sizeof(ffi_func_t *), ··· 59 76 static jsval_t ffi_read_memory(struct js *js, jsval_t *args, int nargs); 60 77 static jsval_t ffi_write_memory(struct js *js, jsval_t *args, int nargs); 61 78 static jsval_t ffi_get_pointer(struct js *js, jsval_t *args, int nargs); 79 + static jsval_t ffi_create_callback(struct js *js, jsval_t *args, int nargs); 80 + static jsval_t ffi_free_callback(struct js *js, jsval_t *args, int nargs); 81 + static jsval_t ffi_read_ptr(struct js *js, jsval_t *args, int nargs); 62 82 63 83 static ffi_type *get_ffi_type(const char *type_str); 64 84 static void *js_to_ffi_value(struct js *js, jsval_t val, ffi_type *type, void *buffer); 65 85 static jsval_t ffi_to_js_value(struct js *js, void *val, ffi_type *type, const char *type_str); 86 + static void ffi_callback_handler(ffi_cif *cif, void *ret, void **args, void *user_data); 66 87 67 88 jsval_t ffi_library(struct js *js) { 68 89 jsval_t ffi_obj = js_mkobj(js); ··· 73 94 js_set(js, ffi_obj, "read", js_mkfun(ffi_read_memory)); 74 95 js_set(js, ffi_obj, "write", js_mkfun(ffi_write_memory)); 75 96 js_set(js, ffi_obj, "pointer", js_mkfun(ffi_get_pointer)); 97 + js_set(js, ffi_obj, "callback", js_mkfun(ffi_create_callback)); 98 + js_set(js, ffi_obj, "freeCallback", js_mkfun(ffi_free_callback)); 99 + js_set(js, ffi_obj, "readPtr", js_mkfun(ffi_read_ptr)); 76 100 77 101 const char *suffix; 78 102 #ifdef __APPLE__ ··· 540 564 pthread_mutex_unlock(&ffi_pointers_mutex); 541 565 542 566 return exists ? js_mktrue() : js_mkfalse(); 567 + } 568 + 569 + static jsval_t ffi_read_ptr(struct js *js, jsval_t *args, int nargs) { 570 + if (nargs < 2 || js_type(args[0]) != JS_NUM || js_type(args[1]) != JS_STR) { 571 + return js_mkerr(js, "readPtr() requires pointer and type"); 572 + } 573 + 574 + void *ptr = (void *)(uint64_t)js_getnum(args[0]); 575 + if (!ptr) return js_mknull(); 576 + 577 + const char *type_str = js_getstr(js, args[1], NULL); 578 + 579 + if (strcmp(type_str, "string") == 0) { 580 + const char *str = (const char *)ptr; 581 + return js_mkstr(js, str, strlen(str)); 582 + } 583 + 584 + ffi_type *type = get_ffi_type(type_str); 585 + if (!type) return js_mkerr(js, "Unknown type: %s", type_str); 586 + 587 + return ffi_to_js_value(js, ptr, type, type_str); 588 + } 589 + 590 + static void ffi_callback_handler(ffi_cif *cif, void *ret, void **args, void *user_data) { 591 + (void)cif; 592 + ffi_callback_t *cb = (ffi_callback_t *)user_data; 593 + struct js *js = cb->js; 594 + 595 + jsval_t js_args[32]; 596 + int arg_count = cb->arg_count > 32 ? 32 : cb->arg_count; 597 + 598 + for (int i = 0; i < arg_count; i++) { 599 + js_args[i] = ffi_to_js_value(js, args[i], cb->arg_types[i], cb->arg_type_strs[i]); 600 + } 601 + 602 + jsval_t result = js_call(js, cb->js_func, js_args, arg_count); 603 + 604 + if (cb->ret_type != &ffi_type_void) { 605 + if (cb->ret_type == &ffi_type_sint8) { 606 + *(int8_t *)ret = (int8_t)js_getnum(result); 607 + } else if (cb->ret_type == &ffi_type_sint16) { 608 + *(int16_t *)ret = (int16_t)js_getnum(result); 609 + } else if (cb->ret_type == &ffi_type_sint32) { 610 + *(int32_t *)ret = (int32_t)js_getnum(result); 611 + } else if (cb->ret_type == &ffi_type_sint64) { 612 + *(int64_t *)ret = (int64_t)js_getnum(result); 613 + } else if (cb->ret_type == &ffi_type_uint8) { 614 + *(uint8_t *)ret = (uint8_t)js_getnum(result); 615 + } else if (cb->ret_type == &ffi_type_uint16) { 616 + *(uint16_t *)ret = (uint16_t)js_getnum(result); 617 + } else if (cb->ret_type == &ffi_type_uint64) { 618 + *(uint64_t *)ret = (uint64_t)js_getnum(result); 619 + } else if (cb->ret_type == &ffi_type_float) { 620 + *(float *)ret = (float)js_getnum(result); 621 + } else if (cb->ret_type == &ffi_type_double) { 622 + *(double *)ret = js_getnum(result); 623 + } else if (cb->ret_type == &ffi_type_pointer) { 624 + if (js_type(result) == JS_NUM) { 625 + *(void **)ret = (void *)(uint64_t)js_getnum(result); 626 + } else { 627 + *(void **)ret = NULL; 628 + } 629 + } 630 + } 631 + } 632 + 633 + static jsval_t ffi_create_callback(struct js *js, jsval_t *args, int nargs) { 634 + if (nargs < 2) return js_mkerr(js, "callback() requires function and signature"); 635 + 636 + jsval_t js_func = args[0]; 637 + jsval_t sig = args[1]; 638 + int sig_type = js_type(sig); 639 + if (sig_type == JS_STR || sig_type == JS_NUM || sig_type == JS_NULL || sig_type == JS_UNDEF) { 640 + return js_mkerr(js, "Signature must be an object {args: [...], returns: type}"); 641 + } 642 + 643 + const char *ret_type_str; 644 + jsval_t arg_types_arr; 645 + int arg_count; 646 + 647 + jsval_t returns_val = js_get(js, sig, "returns"); 648 + jsval_t args_val = js_get(js, sig, "args"); 649 + 650 + if (js_type(returns_val) != JS_UNDEF && js_type(args_val) != JS_UNDEF) { 651 + if (js_type(returns_val) != JS_STR) return js_mkerr(js, "Return type must be a string"); 652 + ret_type_str = js_getstr(js, returns_val, NULL); 653 + arg_types_arr = args_val; 654 + jsval_t length_val = js_get(js, arg_types_arr, "length"); 655 + arg_count = (int)js_getnum(length_val); 656 + } else { 657 + jsval_t ret_type_val = js_get(js, sig, "0"); 658 + if (js_type(ret_type_val) != JS_STR) return js_mkerr(js, "Return type must be a string"); 659 + ret_type_str = js_getstr(js, ret_type_val, NULL); 660 + arg_types_arr = js_get(js, sig, "1"); 661 + jsval_t length_val = js_get(js, arg_types_arr, "length"); 662 + arg_count = (int)js_getnum(length_val); 663 + } 664 + 665 + ffi_type *ret_type = get_ffi_type(ret_type_str); 666 + if (!ret_type) return js_mkerr(js, "Unknown return type: %s", ret_type_str); 667 + 668 + ffi_callback_t *cb = (ffi_callback_t *)malloc(sizeof(ffi_callback_t)); 669 + if (!cb) return js_mkerr(js, "Out of memory"); 670 + 671 + cb->js = js; 672 + cb->js_func = js_func; 673 + cb->ret_type = ret_type; 674 + strncpy(cb->ret_type_str, ret_type_str, sizeof(cb->ret_type_str) - 1); 675 + cb->ret_type_str[sizeof(cb->ret_type_str) - 1] = '\0'; 676 + cb->arg_count = arg_count; 677 + 678 + if (arg_count > 0) { 679 + cb->arg_types = (ffi_type **)malloc(sizeof(ffi_type *) * arg_count); 680 + cb->arg_type_strs = (char **)malloc(sizeof(char *) * arg_count); 681 + if (!cb->arg_types || !cb->arg_type_strs) { 682 + if (cb->arg_types) free(cb->arg_types); 683 + if (cb->arg_type_strs) free(cb->arg_type_strs); 684 + free(cb); 685 + return js_mkerr(js, "Out of memory"); 686 + } 687 + 688 + for (int i = 0; i < arg_count; i++) { 689 + char idx_str[16]; 690 + snprintf(idx_str, sizeof(idx_str), "%d", i); 691 + jsval_t arg_type_val = js_get(js, arg_types_arr, idx_str); 692 + 693 + if (js_type(arg_type_val) != JS_STR) { 694 + for (int j = 0; j < i; j++) free(cb->arg_type_strs[j]); 695 + free(cb->arg_types); 696 + free(cb->arg_type_strs); 697 + free(cb); 698 + return js_mkerr(js, "Argument type must be a string"); 699 + } 700 + 701 + const char *arg_type_str = js_getstr(js, arg_type_val, NULL); 702 + cb->arg_types[i] = get_ffi_type(arg_type_str); 703 + if (!cb->arg_types[i]) { 704 + for (int j = 0; j < i; j++) free(cb->arg_type_strs[j]); 705 + free(cb->arg_types); 706 + free(cb->arg_type_strs); 707 + free(cb); 708 + return js_mkerr(js, "Unknown argument type: %s", arg_type_str); 709 + } 710 + cb->arg_type_strs[i] = strdup(arg_type_str); 711 + } 712 + } else { 713 + cb->arg_types = NULL; 714 + cb->arg_type_strs = NULL; 715 + } 716 + 717 + ffi_status status = ffi_prep_cif(&cb->cif, FFI_DEFAULT_ABI, arg_count, ret_type, cb->arg_types); 718 + if (status != FFI_OK) { 719 + for (int i = 0; i < arg_count; i++) free(cb->arg_type_strs[i]); 720 + if (cb->arg_types) free(cb->arg_types); 721 + if (cb->arg_type_strs) free(cb->arg_type_strs); 722 + free(cb); 723 + return js_mkerr(js, "Failed to prepare callback CIF (status=%d)", status); 724 + } 725 + 726 + cb->closure = (ffi_closure *)ffi_closure_alloc(sizeof(ffi_closure), &cb->code_ptr); 727 + if (!cb->closure) { 728 + for (int i = 0; i < arg_count; i++) free(cb->arg_type_strs[i]); 729 + if (cb->arg_types) free(cb->arg_types); 730 + if (cb->arg_type_strs) free(cb->arg_type_strs); 731 + free(cb); 732 + return js_mkerr(js, "Failed to allocate closure"); 733 + } 734 + 735 + status = ffi_prep_closure_loc(cb->closure, &cb->cif, ffi_callback_handler, cb, cb->code_ptr); 736 + if (status != FFI_OK) { 737 + ffi_closure_free(cb->closure); 738 + for (int i = 0; i < arg_count; i++) free(cb->arg_type_strs[i]); 739 + if (cb->arg_types) free(cb->arg_types); 740 + if (cb->arg_type_strs) free(cb->arg_type_strs); 741 + free(cb); 742 + return js_mkerr(js, "Failed to prepare closure (status=%d)", status); 743 + } 744 + 745 + cb->cb_key = (uint64_t)cb->code_ptr; 746 + 747 + pthread_mutex_lock(&ffi_callbacks_mutex); 748 + HASH_ADD(hh, ffi_callbacks, cb_key, sizeof(uint64_t), cb); 749 + pthread_mutex_unlock(&ffi_callbacks_mutex); 750 + 751 + return js_mknum((double)cb->cb_key); 752 + } 753 + 754 + static jsval_t ffi_free_callback(struct js *js, jsval_t *args, int nargs) { 755 + if (nargs < 1 || js_type(args[0]) != JS_NUM) { 756 + return js_mkerr(js, "freeCallback() requires callback pointer"); 757 + } 758 + 759 + uint64_t cb_key = (uint64_t)js_getnum(args[0]); 760 + 761 + pthread_mutex_lock(&ffi_callbacks_mutex); 762 + ffi_callback_t *cb = NULL; 763 + HASH_FIND(hh, ffi_callbacks, &cb_key, sizeof(uint64_t), cb); 764 + 765 + if (!cb) { 766 + pthread_mutex_unlock(&ffi_callbacks_mutex); 767 + return js_mkerr(js, "Invalid callback pointer"); 768 + } 769 + 770 + HASH_DEL(ffi_callbacks, cb); 771 + pthread_mutex_unlock(&ffi_callbacks_mutex); 772 + 773 + ffi_closure_free(cb->closure); 774 + for (int i = 0; i < cb->arg_count; i++) free(cb->arg_type_strs[i]); 775 + if (cb->arg_types) free(cb->arg_types); 776 + if (cb->arg_type_strs) free(cb->arg_type_strs); 777 + free(cb); 778 + 779 + return js_mkundef(); 543 780 } 544 781 545 782 static ffi_type *get_ffi_type(const char *type_str) {