fixes to allow native to work - should revert when recursion is fixed

This commit is contained in:
2026-02-17 15:44:17 -06:00
parent 78051e24f3
commit 8a24a69120
3 changed files with 61 additions and 7 deletions

View File

@@ -8,6 +8,7 @@
#include "quickjs-internal.h"
#include <math.h>
#include <pthread.h>
/* Non-inline wrappers for static inline functions in quickjs.h */
JSValue qbe_new_float64(JSContext *ctx, double d) {
@@ -338,7 +339,8 @@ JSValue cell_rt_get_closure(JSContext *ctx, void *fp, int64_t depth,
if GC moved the frame. */
int magic = (int)(int64_t)frame[QBE_FRAME_OUTER_SLOT];
frame = derive_outer_fp(magic);
if (!frame) return JS_NULL;
if (!frame)
return JS_NULL;
}
return frame[slot];
}
@@ -359,13 +361,31 @@ void cell_rt_put_closure(JSContext *ctx, void *fp, JSValue val, int64_t depth,
update frame pointers when it moves objects. cell_rt_refresh_fp
re-derives the slot pointer after any GC-triggering call. */
#define MAX_AOT_DEPTH 256
#define MAX_AOT_DEPTH 65536
static JSGCRef g_aot_gc_refs[MAX_AOT_DEPTH];
static int g_aot_depth = 0;
/* Check remaining C stack space to prevent segfaults from deep recursion */
static int stack_space_ok(void) {
#ifdef __APPLE__
char local;
void *stack_addr = pthread_get_stackaddr_np(pthread_self());
size_t stack_size = pthread_get_stacksize_np(pthread_self());
/* stack_addr is the TOP of the stack (highest address); stack grows down */
uintptr_t stack_bottom = (uintptr_t)stack_addr - stack_size;
uintptr_t current = (uintptr_t)&local;
/* Keep 128KB of reserve for unwinding and error handling */
return (current - stack_bottom) > (128 * 1024);
#else
return g_aot_depth < MAX_AOT_DEPTH;
#endif
}
JSValue *cell_rt_enter_frame(JSContext *ctx, int64_t nr_slots) {
if (g_aot_depth >= MAX_AOT_DEPTH)
if (g_aot_depth >= MAX_AOT_DEPTH || !stack_space_ok()) {
JS_ThrowTypeError(ctx, "native call stack overflow (depth %d)", g_aot_depth);
return NULL;
}
JSFrameRegister *frame = alloc_frame_register(ctx, (int)nr_slots);
if (!frame) return NULL;
JSGCRef *ref = &g_aot_gc_refs[g_aot_depth];
@@ -423,7 +443,7 @@ typedef JSValue (*cell_compiled_fn)(JSContext *ctx, void *fp);
Each native .cm module gets its own dylib. When a module creates closures
via cell_rt_make_function, we record the dylib handle so the trampoline
can look up the correct cell_fn_N in the right dylib. */
#define MAX_NATIVE_FN 4096
#define MAX_NATIVE_FN 32768
static struct {
void *dl_handle;
@@ -447,6 +467,17 @@ static JSValue *derive_outer_fp(int magic) {
return (JSValue *)frame->slots;
}
static void reclaim_native_fns(JSContext *ctx, int saved_count) {
/* Free GC refs for temporary closures created during a call */
for (int i = saved_count; i < g_native_fn_count; i++) {
if (g_native_fn_registry[i].has_frame_ref) {
JS_DeleteGCRef(ctx, &g_native_fn_registry[i].frame_ref);
g_native_fn_registry[i].has_frame_ref = 0;
}
}
g_native_fn_count = saved_count;
}
static JSValue cell_fn_trampoline(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv, int magic) {
if (magic < 0 || magic >= g_native_fn_count)
@@ -474,10 +505,31 @@ static JSValue cell_fn_trampoline(JSContext *ctx, JSValue this_val,
surviving GC moves */
fp[QBE_FRAME_OUTER_SLOT] = (JSValue)(int64_t)magic;
/* Set g_current_dl_handle so any closures created during this call
(e.g. inner functions returned by factory functions) are registered
against the correct dylib */
void *prev_handle = g_current_dl_handle;
g_current_dl_handle = handle;
/* At top-level (depth 1 = this is the outermost native call),
save the fn count so we can reclaim temporary closures after */
int saved_fn_count = (g_aot_depth == 1) ? g_native_fn_count : -1;
JSValue result = fn(ctx, fp);
cell_rt_leave_frame(ctx);
if (result == JS_EXCEPTION)
g_current_dl_handle = prev_handle;
/* Reclaim temporary closures created during this top-level call */
if (saved_fn_count >= 0)
reclaim_native_fns(ctx, saved_fn_count);
if (result == JS_EXCEPTION) {
/* Ensure there is a pending exception. QBE @_exc_ret returns 15
but may not have set one (e.g. if cell_rt_enter_frame failed). */
if (!JS_HasException(ctx))
JS_Throw(ctx, JS_NULL);
return JS_EXCEPTION;
}
return result;
}