Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903#include <compat.h> // IWYU pragma: keep
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ffi.h>#include <math.h>#include <stdint.h>#include <inttypes.h>#include <signal.h>
#ifndef _WIN32#include <dlfcn.h>#endif
#include "ant.h"#include "cfunc.h"#include "crash.h"#include "gc/roots.h"#include "ptr.h"#include "internal.h"#include "modules/bigint.h"#include "modules/cjit.h"
#pragma GCC diagnostic push#pragma GCC diagnostic ignored "-Wmacro-redefined"#include "c2mir/c2mir.h"#include "mir-gen.h"#pragma GCC diagnostic pop
typedef struct { const char *data; size_t len; size_t offset;} ant_c_source_t;
typedef enum { ANT_C_RETURNS_STATUS, ANT_C_RETURNS_STRING, ANT_C_RETURNS_NUMBER,} ant_c_returns_t;
typedef enum { ANT_C_ARG_INT8, ANT_C_ARG_UINT8, ANT_C_ARG_INT16, ANT_C_ARG_UINT16, ANT_C_ARG_INT32, ANT_C_ARG_UINT32, ANT_C_ARG_INT64, ANT_C_ARG_UINT64, ANT_C_ARG_FLOAT, ANT_C_ARG_DOUBLE,} ant_c_arg_type_t;
typedef union { int8_t i8; uint8_t u8; int16_t i16; uint16_t u16; int32_t i32; uint32_t u32; int64_t i64; uint64_t u64; float f32; double f64; const char *string; ffi_arg raw;} ant_c_arg_value_t;
typedef struct { const char *name; ant_c_arg_type_t type; ffi_type *ffi; MIR_type_t mir;} ant_c_arg_mapping_t;
static constexpr size_t ANT_C_MAX_ARGS = 16;static constexpr uint32_t ANT_C_FUNCTION_NATIVE_TAG = Ant_NativeTag("CJIT");
typedef struct { ant_value_t entry_value; ant_value_t returns_value; ant_value_t args_value; const char *entry_name; const char *returns_name; ant_c_returns_t returns; ant_c_arg_type_t return_type; ffi_type *ffi_return_type; MIR_type_t mir_return_type; bool return_function; size_t arg_count; ant_c_arg_type_t arg_types[ANT_C_MAX_ARGS]; ffi_type *ffi_arg_types[ANT_C_MAX_ARGS]; MIR_type_t mir_arg_types[ANT_C_MAX_ARGS];} ant_c_signature_t;
typedef struct { MIR_context_t ctx; MIR_item_t entry_item; ffi_cif cif; ffi_type **ffi_arg_types; ant_c_arg_type_t *arg_types; ant_c_returns_t returns; ant_c_arg_type_t return_type; size_t arg_count; char *entry_name;} ant_c_function_t;
typedef struct { ffi_cif *cif; void (*entry)(void); void *result; void **args;} ant_c_ffi_call_t;
static const ant_c_arg_mapping_t ANT_C_ARG_MAPPINGS[] = { {"int8", ANT_C_ARG_INT8, &ffi_type_sint8, MIR_T_I8}, {"uint8", ANT_C_ARG_UINT8, &ffi_type_uint8, MIR_T_U8}, {"int16", ANT_C_ARG_INT16, &ffi_type_sint16, MIR_T_I16}, {"uint16", ANT_C_ARG_UINT16, &ffi_type_uint16, MIR_T_U16}, {"int", ANT_C_ARG_INT32, &ffi_type_sint32, MIR_T_I32}, {"int32", ANT_C_ARG_INT32, &ffi_type_sint32, MIR_T_I32}, {"uint32", ANT_C_ARG_UINT32, &ffi_type_uint32, MIR_T_U32}, {"int64", ANT_C_ARG_INT64, &ffi_type_sint64, MIR_T_I64}, {"uint64", ANT_C_ARG_UINT64, &ffi_type_uint64, MIR_T_U64}, {"float", ANT_C_ARG_FLOAT, &ffi_type_float, MIR_T_F}, {"double", ANT_C_ARG_DOUBLE, &ffi_type_double, MIR_T_D},};
static bool ant_c_append( char **buffer, size_t *length, size_t *capacity, const char *data, size_t data_length) { if (data_length > SIZE_MAX - *length - 1) return false; size_t required = *length + data_length + 1; if (required > *capacity) { size_t next_capacity = *capacity ? *capacity : 256; while (next_capacity < required) { if (next_capacity > SIZE_MAX / 2) { next_capacity = required; break; } next_capacity *= 2; } char *next = realloc(*buffer, next_capacity); if (!next) return false; *buffer = next; *capacity = next_capacity; }
memcpy(*buffer + *length, data, data_length); *length += data_length; (*buffer)[*length] = '\0'; return true;}
static ant_value_t ant_c_template_source(ant_native_params_t) { if (nargs < 1 || vtype(args[0]) != kTypeArray) return js_mkerr(js, "Ant.unsafe.c must be used as a tagged template");
ant_value_t raw = js_get(js, args[0], "raw"); if (is_err(raw)) return raw; if (vtype(raw) != kTypeArray) return js_mkerr(js, "Ant.unsafe.c must be used as a tagged template");
ant_offset_t literal_count = js_arr_len(js, raw); if ((size_t)literal_count != (size_t)nargs) return js_mkerr(js, "Ant.unsafe.c received an invalid template object");
char *buffer = NULL; size_t length = 0; size_t capacity = 0; for (ant_offset_t i = 0; i < literal_count; i++) { ant_value_t literal = js_arr_get(js, raw, i); if (vtype(literal) != kTypeString) { free(buffer); return js_mkerr(js, "Ant.unsafe.c received an invalid template object"); }
size_t part_length; const char *part = js_getstr(js, literal, &part_length); if (!ant_c_append(&buffer, &length, &capacity, part, part_length)) { free(buffer); return js_mkerr(js, "Out of memory"); }
if (i + 1 >= literal_count) continue; ant_value_t value = js_template_to_string(js, args[i + 1]); if (is_err(value)) { free(buffer); return value; } part = js_getstr(js, value, &part_length); if (!ant_c_append(&buffer, &length, &capacity, part, part_length)) { free(buffer); return js_mkerr(js, "Out of memory"); } }
ant_value_t source = js_mkstr(js, buffer ? buffer : "", length); free(buffer); return source;}
static int ant_c_getc(void *data) { ant_c_source_t *source = data; if (source->offset >= source->len) return EOF; return (unsigned char)source->data[source->offset++];}
static void *ant_c_import_resolver(const char *name) {#ifdef _WIN32 const char *modules[] = {NULL, "ucrtbase.dll", "msvcrt.dll", "kernel32.dll"}; for (size_t i = 0; i < sizeof(modules) / sizeof(modules[0]); i++) { HMODULE module = modules[i] ? GetModuleHandleA(modules[i]) : GetModuleHandleA(NULL); if (!module) continue; FARPROC symbol = GetProcAddress(module, name); if (symbol) return (void *)(uintptr_t)symbol; } return NULL;#else return dlsym(RTLD_DEFAULT, name);#endif}
static char *ant_c_read_diagnostics(FILE *file) { if (!file || fflush(file) != 0 || fseek(file, 0, SEEK_END) != 0) return NULL; long size = ftell(file); if (size <= 0 || fseek(file, 0, SEEK_SET) != 0) return NULL;
char *diagnostics = malloc((size_t)size + 1); if (!diagnostics) return NULL; size_t read = fread(diagnostics, 1, (size_t)size, file);
diagnostics[read] = '\0'; return diagnostics;}
static void ant_c_load_modules(MIR_context_t ctx) { for ( MIR_module_t module = DLIST_HEAD(MIR_module_t, *MIR_get_module_list(ctx)); module != NULL; module = DLIST_NEXT(MIR_module_t, module) ) MIR_load_module(ctx, module);}
static bool ant_c_pointer_sized_type(MIR_type_t type) {#if MIR_PTR64 return type == MIR_T_I64 || type == MIR_T_U64;#else return type == MIR_T_I32 || type == MIR_T_U32;#endif}
static bool ant_c_identifier(const char *name, size_t length) { if (length == 0) return false; unsigned char first = (unsigned char)name[0]; if ( first != '_' && (first < 'A' || first > 'Z') && (first < 'a' || first > 'z') ) return false;
for (size_t i = 1; i < length; i++) { unsigned char c = (unsigned char)name[i]; if ( c != '_' && (c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c > '9') ) return false; } return true;}
static char *ant_c_string_checked_source( const char *source, size_t source_length, const ant_c_signature_t *signature, size_t *checked_length) { static const char prefix[] = "\n_Static_assert(_Generic(("; static const char call_start[] = ")("; static const char suffix[] = "), char *: 1, default: 0), " "\"Ant.unsafe.c() string entry must return char *\");\n";
char *buffer = NULL; size_t length = 0; size_t capacity = 0;
if (!ant_c_append(&buffer, &length, &capacity, source, source_length) || !ant_c_append(&buffer, &length, &capacity, prefix, sizeof(prefix) - 1) || !ant_c_append( &buffer, &length, &capacity, signature->entry_name, strlen(signature->entry_name) ) || !ant_c_append(&buffer, &length, &capacity, call_start, sizeof(call_start) - 1)) goto oom;
size_t arg_count = signature->return_function ? signature->arg_count : 0; for (size_t i = 0; i < arg_count; i++) if ((i > 0 && !ant_c_append(&buffer, &length, &capacity, ", ", 2)) || !ant_c_append(&buffer, &length, &capacity, "0", 1)) goto oom;
if (!ant_c_append(&buffer, &length, &capacity, suffix, sizeof(suffix) - 1)) goto oom; *checked_length = length; return buffer;
oom: free(buffer); return NULL;}
static bool ant_c_parse_type( const char *name, ant_c_arg_type_t *type, ffi_type **ffi_type_out, MIR_type_t *mir_type_out) { for (size_t i = 0; i < sizeof(ANT_C_ARG_MAPPINGS) / sizeof(ANT_C_ARG_MAPPINGS[0]); i++) { const ant_c_arg_mapping_t *mapping = &ANT_C_ARG_MAPPINGS[i]; if (strcmp(name, mapping->name) != 0) continue; if (type) *type = mapping->type; if (ffi_type_out) *ffi_type_out = mapping->ffi; if (mir_type_out) *mir_type_out = mapping->mir; return true; } return false;}
static ant_value_t ant_c_parse_signature( ant_t *js, ant_value_t options_value, ant_c_signature_t *signature) { *signature = (ant_c_signature_t){ .entry_value = js_mkundef(), .returns_value = js_mkundef(), .args_value = js_mkundef(), .entry_name = "main", .returns_name = "int", .returns = ANT_C_RETURNS_STATUS, .return_type = ANT_C_ARG_INT32, .ffi_return_type = &ffi_type_sint32, .mir_return_type = MIR_T_I32, }; if (vtype(options_value) == kTypeUndefined) return js_mkundef(); if (!is_object_type(options_value)) return js_mkerr(js, "Ant.unsafe.c() options must be an object");
signature->entry_value = js_get(js, options_value, "entry"); if (is_err(signature->entry_value)) return signature->entry_value; if (vtype(signature->entry_value) != kTypeString) return js_mkerr(js, "Ant.unsafe.c() options.entry must be a string");
size_t entry_length; signature->entry_name = js_getstr(js, signature->entry_value, &entry_length); if (!ant_c_identifier(signature->entry_name, entry_length)) return js_mkerr(js, "Ant.unsafe.c() options.entry must be a C identifier");
signature->returns_value = js_get(js, options_value, "returns"); if (is_err(signature->returns_value)) return signature->returns_value; if (vtype(signature->returns_value) != kTypeString) return js_mkerr(js, "Ant.unsafe.c() options.returns must be a supported type");
signature->returns_name = js_getstr(js, signature->returns_value, NULL); if (strcmp(signature->returns_name, "string") == 0) { signature->returns = ANT_C_RETURNS_STRING; signature->ffi_return_type = &ffi_type_pointer; } else { signature->returns = ANT_C_RETURNS_NUMBER; if (!ant_c_parse_type( signature->returns_name, &signature->return_type, &signature->ffi_return_type, &signature->mir_return_type )) return js_mkerr(js, "Ant.unsafe.c() options.returns has an unsupported type"); }
signature->args_value = js_get(js, options_value, "args"); if (is_err(signature->args_value) || vtype(signature->args_value) == kTypeUndefined) return signature->args_value; if (!is_object_type(signature->args_value)) return js_mkerr(js, "Ant.unsafe.c() options.args must be an array");
ant_value_t length_value = js_get(js, signature->args_value, "length"); if (is_err(length_value)) return length_value; if (vtype(length_value) != kTypeNumber) return js_mkerr(js, "Ant.unsafe.c() options.args must be an array");
double length = js_getnum(length_value); if ( !isfinite(length) || length < 0 || length > (double)ANT_C_MAX_ARGS || length != (double)(size_t)length ) return js_mkerr(js, "Ant.unsafe.c() options.args supports at most %zu arguments", ANT_C_MAX_ARGS);
signature->arg_count = (size_t)length; signature->return_function = true; for (size_t i = 0; i < signature->arg_count; i++) { char index[24]; snprintf(index, sizeof(index), "%zu", i); ant_value_t type_value = js_get(js, signature->args_value, index); if (is_err(type_value)) return type_value; if (vtype(type_value) != kTypeString || !ant_c_parse_type( js_getstr(js, type_value, NULL), &signature->arg_types[i], &signature->ffi_arg_types[i], &signature->mir_arg_types[i] )) return js_mkerr(js, "Ant.unsafe.c() options.args[%zu] has an unsupported type", i); } return js_mkundef();}
static bool ant_c_signature_matches(MIR_func_t entry_func, const ant_c_signature_t *signature) { if (signature->returns == ANT_C_RETURNS_STATUS) { if ( (entry_func->nargs != 0 && entry_func->nargs != 2 && entry_func->nargs != 3) || entry_func->vararg_p || entry_func->nres != 1 || entry_func->res_types[0] != MIR_T_I32 ) return false; if (entry_func->nargs == 0) return true; if ( VARR_GET(MIR_var_t, entry_func->vars, 0).type != MIR_T_I32 || !ant_c_pointer_sized_type(VARR_GET(MIR_var_t, entry_func->vars, 1).type) ) return false; return entry_func->nargs == 2 || ant_c_pointer_sized_type( VARR_GET(MIR_var_t, entry_func->vars, 2).type ); }
size_t arg_count = signature->return_function ? signature->arg_count : 0; if (entry_func->nargs != arg_count || entry_func->vararg_p || entry_func->nres != 1) return false; for (size_t i = 0; i < arg_count; i++) if (VARR_GET(MIR_var_t, entry_func->vars, i).type != signature->mir_arg_types[i]) return false;
return signature->returns == ANT_C_RETURNS_STRING ? ant_c_pointer_sized_type(entry_func->res_types[0]) : entry_func->res_types[0] == signature->mir_return_type;}
static ant_value_t ant_c_signature_error( ant_t *js, const ant_c_signature_t *signature) { size_t arg_count = signature->return_function ? signature->arg_count : 0; if (signature->returns == ANT_C_RETURNS_STRING) return js_mkerr( js, "Ant.unsafe.c() string entry \"%s\" must return char * and match %zu configured arguments", signature->entry_name, arg_count ); if (signature->returns == ANT_C_RETURNS_NUMBER) return js_mkerr( js, "Ant.unsafe.c() entry \"%s\" must match configured signature with return type \"%s\" and %zu arguments", signature->entry_name, signature->returns_name, arg_count ); return js_mkerr( js, "Ant.unsafe.c() main must return int and accept no arguments or int " "followed by one or two pointer-sized arguments" );}
static ant_value_t ant_c_result_to_js( ant_t *js, ant_c_returns_t returns, ant_c_arg_type_t return_type, const ant_c_arg_value_t *result) { if (returns == ANT_C_RETURNS_STRING) return result->string ? js_mkstr(js, result->string, strlen(result->string)) : js_mknull();
switch (return_type) { case ANT_C_ARG_INT8: return js_mknum((double)result->i8); case ANT_C_ARG_UINT8: return js_mknum((double)result->u8); case ANT_C_ARG_INT16: return js_mknum((double)result->i16); case ANT_C_ARG_UINT16: return js_mknum((double)result->u16); case ANT_C_ARG_INT32: return js_mknum((double)result->i32); case ANT_C_ARG_UINT32: return js_mknum((double)result->u32); case ANT_C_ARG_INT64: return bigint_from_int64(js, result->i64); case ANT_C_ARG_UINT64: return bigint_from_uint64(js, result->u64); case ANT_C_ARG_FLOAT: return js_mknum((double)result->f32); case ANT_C_ARG_DOUBLE: return js_mknum(result->f64); }
return js_mkerr(js, "Ant.unsafe.c() has an unsupported return type");}
static void ant_c_invoke_ffi(void *context) { ant_c_ffi_call_t *call = context; ffi_call(call->cif, call->entry, call->result, call->args);}
static ant_value_t ant_c_native_fault( ant_t *js, const char *entry_name, int signal_number, uintptr_t fault_address) { if (signal_number == SIGABRT) return js_mkerr( js, "Ant.unsafe.c() entry \"%s\" aborted with signal %d", entry_name, signal_number );
return js_mkerr(js, "Ant.unsafe.c() entry \"%s\" faulted with signal %d at address 0x%" PRIxPTR, entry_name, signal_number, fault_address );}
static int ant_c_call_ffi_guarded( ffi_cif *cif, void (*entry)(void), void *result, void **args, uintptr_t *fault_address) { ant_c_ffi_call_t call = { .cif = cif, .entry = entry, .result = result, .args = args }; return ant_crash_guard_native_call(ant_c_invoke_ffi, &call, fault_address);}
static void ant_c_function_destroy(ant_c_function_t *function) { if (!function) return; MIR_gen_finish(function->ctx); c2mir_finish(function->ctx); MIR_finish(function->ctx); free(function->ffi_arg_types); free(function->arg_types); free(function->entry_name); free(function);}
static void ant_c_function_finalize(ant_t *js, ant_object_t *obj) { ant_value_t value = js_obj_from_ptr(obj); ant_c_function_t *function = js_get_native(value, ANT_C_FUNCTION_NATIVE_TAG); if (!function) return; ant_c_function_destroy(function); js_clear_native(value, ANT_C_FUNCTION_NATIVE_TAG);}
static bool ant_c_number_to_arg( double number, ant_c_arg_type_t type, ant_c_arg_value_t *value) { if (type == ANT_C_ARG_FLOAT) { value->f32 = (float)number; return true; } if (type == ANT_C_ARG_DOUBLE) { value->f64 = number; return true; } if (!isfinite(number) || trunc(number) != number) return false;
switch (type) { case ANT_C_ARG_INT8: if (number < INT8_MIN || number > INT8_MAX) return false; value->i8 = (int8_t)number; return true; case ANT_C_ARG_UINT8: if (number < 0 || number > UINT8_MAX) return false; value->u8 = (uint8_t)number; return true; case ANT_C_ARG_INT16: if (number < INT16_MIN || number > INT16_MAX) return false; value->i16 = (int16_t)number; return true; case ANT_C_ARG_UINT16: if (number < 0 || number > UINT16_MAX) return false; value->u16 = (uint16_t)number; return true; case ANT_C_ARG_INT32: if (number < INT32_MIN || number > INT32_MAX) return false; value->i32 = (int32_t)number; return true; case ANT_C_ARG_UINT32: if (number < 0 || number > UINT32_MAX) return false; value->u32 = (uint32_t)number; return true; case ANT_C_ARG_INT64: case ANT_C_ARG_UINT64: case ANT_C_ARG_FLOAT: case ANT_C_ARG_DOUBLE: return false; } return false;}
static ant_value_t ant_c_function_call(ant_params_t) { ant_c_function_t *function = js_get_native(js->current_func, ANT_C_FUNCTION_NATIVE_TAG); if (!function) return js_mkerr(js, "Ant.unsafe.c() compiled function is no longer available"); if (nargs != (int)function->arg_count) return js_mkerr(js, "Ant.unsafe.c() entry \"%s\" expects %zu arguments, got %d", function->entry_name, function->arg_count, nargs );
ant_c_arg_value_t values[ANT_C_MAX_ARGS]; void *ffi_args[ANT_C_MAX_ARGS]; memset(values, 0, sizeof(values));
for (size_t i = 0; i < function->arg_count; i++) { ffi_args[i] = &values[i];
if (function->arg_types[i] == ANT_C_ARG_INT64) { if (vtype(args[i]) != kTypeBigInt || !bigint_to_int64_wrapping(js, args[i], &values[i].i64)) return js_mkerr( js, "Ant.unsafe.c() entry \"%s\" argument %zu must be a bigint", function->entry_name, i + 1 ); continue; }
if (function->arg_types[i] == ANT_C_ARG_UINT64) { if (vtype(args[i]) != kTypeBigInt || !bigint_to_uint64_wrapping(js, args[i], &values[i].u64)) return js_mkerr( js, "Ant.unsafe.c() entry \"%s\" argument %zu must be a bigint", function->entry_name, i + 1 ); continue; }
if (vtype(args[i]) != kTypeNumber) return js_mkerr( js, "Ant.unsafe.c() entry \"%s\" argument %zu must be a number", function->entry_name, i + 1 );
double number = js_getnum(args[i]); if (!ant_c_number_to_arg(number, function->arg_types[i], &values[i])) return js_mkerr( js, "Ant.unsafe.c() entry \"%s\" argument %zu must be a finite integer within range", function->entry_name, i + 1 ); }
ant_c_arg_value_t result = {0}; uintptr_t fault_address = 0;
int signal_number = ant_c_call_ffi_guarded( &function->cif, function->entry_item->addr, &result, function->arg_count == 0 ? NULL : ffi_args, &fault_address );
if (signal_number != 0) return ant_c_native_fault(js, function->entry_name, signal_number, fault_address); return ant_c_result_to_js(js, function->returns, function->return_type, &result);}
static ant_value_t ant_c_make_function( ant_t *js, MIR_context_t ctx, MIR_item_t entry_item, const char *entry_name, ant_c_returns_t returns, ant_c_arg_type_t return_type, ffi_type *ffi_return_type, size_t arg_count, ant_c_arg_type_t *arg_types, ffi_type **ffi_arg_types) { ant_c_function_t *function = calloc(1, sizeof(*function)); if (!function) { MIR_gen_finish(ctx); c2mir_finish(ctx); MIR_finish(ctx); return js_mkerr(js, "Out of memory"); }
function->ctx = ctx; function->entry_item = entry_item; function->returns = returns; function->return_type = return_type; function->arg_count = arg_count; function->entry_name = strdup(entry_name); if (arg_count > 0) { function->arg_types = malloc(arg_count * sizeof(*function->arg_types)); function->ffi_arg_types = malloc(arg_count * sizeof(*function->ffi_arg_types)); } if (!function->entry_name || (arg_count > 0 && (!function->arg_types || !function->ffi_arg_types))) { ant_c_function_destroy(function); return js_mkerr(js, "Out of memory"); } if (arg_count > 0) { memcpy(function->arg_types, arg_types, arg_count * sizeof(*arg_types)); memcpy(function->ffi_arg_types, ffi_arg_types, arg_count * sizeof(*ffi_arg_types)); }
if (ffi_prep_cif( &function->cif, FFI_DEFAULT_ABI, (unsigned int)arg_count, ffi_return_type, function->ffi_arg_types) != FFI_OK ) { ant_c_function_destroy(function); return js_mkerr(js, "Ant.unsafe.c() failed to prepare the entry call interface"); }
ant_value_t obj = Ant_Object_Create(js); if (is_err(obj)) { ant_c_function_destroy(function); return obj; }
GC_ROOT_SAVE(root_mark, js); GC_ROOT_PIN(js, obj); js_set_slot(obj, SLOT_CFUNC, Ant_Function_Create(ant_c_function_call)); js_set_native(obj, function, ANT_C_FUNCTION_NATIVE_TAG); js_set_finalizer(obj, ant_c_function_finalize);
ant_value_t result = js_obj_to_func(js, obj); if (is_err(result)) { js_clear_native(obj, ANT_C_FUNCTION_NATIVE_TAG); ant_c_function_destroy(function); }
GC_ROOT_RESTORE(js, root_mark); return result;}
static ant_value_t ant_c_call_configured_entry( ant_t *js, MIR_item_t entry_item, const ant_c_signature_t *signature) { ffi_cif cif; if (ffi_prep_cif( &cif, FFI_DEFAULT_ABI, 0, signature->ffi_return_type, NULL ) != FFI_OK) return js_mkerr( js, "Ant.unsafe.c() failed to prepare the entry call interface" );
ant_c_arg_value_t native_result = {0}; uintptr_t fault_address = 0; int signal_number = ant_c_call_ffi_guarded(&cif, entry_item->addr, &native_result, NULL, &fault_address); if (signal_number != 0) return ant_c_native_fault(js, signature->entry_name, signal_number, fault_address); return ant_c_result_to_js(js, signature->returns, signature->return_type, &native_result);}
static ant_value_t ant_c_call_status_entry( ant_t *js, MIR_item_t entry_item, MIR_func_t entry_func) { ffi_type *arg_types[] = { &ffi_type_sint32, &ffi_type_pointer, &ffi_type_pointer, }; ffi_cif cif; if (ffi_prep_cif( &cif, FFI_DEFAULT_ABI, (unsigned int)entry_func->nargs, &ffi_type_sint32, arg_types ) != FFI_OK) return js_mkerr(js, "Ant.unsafe.c() failed to prepare the main call interface");
int argc = 1; char *argv_data[] = {"Ant.unsafe.c", NULL}; char **argv = argv_data; char **envp = NULL; void *args[] = {&argc, &argv, &envp}; int result = 0; uintptr_t fault_address = 0; int signal_number = ant_c_call_ffi_guarded( &cif, entry_item->addr, &result, entry_func->nargs == 0 ? NULL : args, &fault_address ); if (signal_number != 0) return ant_c_native_fault(js, "main", signal_number, fault_address); return js_mknum((double)result);}
static ant_value_t ant_c_compile(ant_t *js, ant_value_t source_value, ant_value_t options_value) { if (vtype(source_value) != kTypeString) return js_mkerr(js, "Ant.unsafe.c must be used as a tagged template");
ant_c_signature_t signature = {0}; GC_ROOT_SAVE(root_mark, js); GC_ROOT_PIN(js, source_value); GC_ROOT_PIN(js, options_value); GC_ROOT_PIN(js, signature.entry_value); GC_ROOT_PIN(js, signature.returns_value); GC_ROOT_PIN(js, signature.args_value);
ant_value_t signature_error = ant_c_parse_signature(js, options_value, &signature); if (is_err(signature_error)) { GC_ROOT_RESTORE(js, root_mark); return signature_error; }
size_t source_len; const char *source_data = js_getstr(js, source_value, &source_len); char *checked_source = NULL; if (signature.returns == ANT_C_RETURNS_STRING) { checked_source = ant_c_string_checked_source( source_data, source_len, &signature, &source_len ); if (!checked_source) { ant_value_t error = js_mkerr(js, "Out of memory"); GC_ROOT_RESTORE(js, root_mark); return error; } source_data = checked_source; } ant_c_source_t source = {.data = source_data, .len = source_len, .offset = 0}; FILE *diagnostic_file = tmpfile();
MIR_context_t ctx = MIR_init(); c2mir_init(ctx);
struct c2mir_options options = {0}; options.message_file = diagnostic_file ? diagnostic_file : stderr; int compiled = c2mir_compile(ctx, &options, ant_c_getc, &source, "<Ant.unsafe.c>", NULL); free(checked_source);
if (!compiled) { char *diagnostics = ant_c_read_diagnostics(diagnostic_file); c2mir_finish(ctx); MIR_finish(ctx); if (diagnostic_file) fclose(diagnostic_file);
ant_value_t error = diagnostics && diagnostics[0] ? js_mkerr(js, "Ant.unsafe.c() compilation failed:\n%s", diagnostics) : js_mkerr(js, "Ant.unsafe.c() compilation failed");
free(diagnostics); GC_ROOT_RESTORE(js, root_mark); return error; }
ant_c_load_modules(ctx); MIR_item_t entry_item = MIR_get_global_item(ctx, signature.entry_name);
if (!entry_item || entry_item->item_type != MIR_func_item) { c2mir_finish(ctx); MIR_finish(ctx); if (diagnostic_file) fclose(diagnostic_file); ant_value_t error = signature.returns == ANT_C_RETURNS_STATUS ? js_mkerr(js, "Ant.unsafe.c() source must define main()") : js_mkerr( js, "Ant.unsafe.c() entry \"%s\" was not found", signature.entry_name ); GC_ROOT_RESTORE(js, root_mark); return error; }
MIR_func_t entry_func = MIR_get_item_func(ctx, entry_item); if (!ant_c_signature_matches(entry_func, &signature)) { c2mir_finish(ctx); MIR_finish(ctx); if (diagnostic_file) fclose(diagnostic_file); ant_value_t error = ant_c_signature_error(js, &signature); GC_ROOT_RESTORE(js, root_mark); return error; }
MIR_gen_init(ctx); MIR_gen_set_optimize_level(ctx, 1); MIR_link(ctx, MIR_set_gen_interface, ant_c_import_resolver);
if (signature.return_function) { if (diagnostic_file) fclose(diagnostic_file); ant_value_t result = ant_c_make_function( js, ctx, entry_item, signature.entry_name, signature.returns, signature.return_type, signature.ffi_return_type, signature.arg_count, signature.arg_types, signature.ffi_arg_types ); GC_ROOT_RESTORE(js, root_mark); return result; }
ant_value_t result = signature.returns == ANT_C_RETURNS_STATUS ? ant_c_call_status_entry(js, entry_item, entry_func) : ant_c_call_configured_entry(js, entry_item, &signature);
MIR_gen_finish(ctx); c2mir_finish(ctx); MIR_finish(ctx); if (diagnostic_file) fclose(diagnostic_file); GC_ROOT_RESTORE(js, root_mark); return result;}
static ant_value_t ant_c_configured_tag(ant_params_t) { ant_value_t source = ant_c_template_source(js, args, nargs); if (is_err(source)) return source; return ant_c_compile(js, source, js_get_slot(js->current_func, SLOT_DATA));}
static ant_value_t js_unsafe_c(ant_params_t) { if (nargs >= 1 && vtype(args[0]) == kTypeArray) { ant_value_t source = ant_c_template_source(js, args, nargs); if (is_err(source)) return source; return ant_c_compile(js, source, js_mkundef()); }
if (nargs == 1 && is_object_type(args[0])) { ant_value_t tag = Ant_Object_Create(js); if (is_err(tag)) return tag;
GC_ROOT_SAVE(root_mark, js); GC_ROOT_PIN(js, tag); js_set_slot(tag, SLOT_CFUNC, Ant_Function_Create(ant_c_configured_tag)); js_set_slot_wb(js, tag, SLOT_DATA, args[0]); ant_value_t result = js_obj_to_func(js, tag); GC_ROOT_RESTORE(js, root_mark); return result; }
return js_mkerr(js, "Ant.unsafe.c must be used as a tagged template or called with options");}
void init_cjit_module(ant_t *js, ant_value_t unsafe_obj) { js_set(js, unsafe_obj, "c", Ant_Function_CreateWithArity(js_unsafe_c, 1));}