|
|
|
|
@@ -12,12 +12,6 @@
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
#if defined(__GNUC__) || defined(__clang__)
|
|
|
|
|
#define CELL_THREAD_LOCAL __thread
|
|
|
|
|
#else
|
|
|
|
|
#define CELL_THREAD_LOCAL _Thread_local
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Non-inline wrappers for static inline functions in quickjs.h */
|
|
|
|
|
JSValue qbe_new_float64(JSContext *ctx, double d) {
|
|
|
|
|
return __JS_NewFloat64(ctx, d);
|
|
|
|
|
@@ -230,36 +224,72 @@ JSValue qbe_shift_shr(JSContext *ctx, JSValue a, JSValue b) {
|
|
|
|
|
|
|
|
|
|
/* --- Property access --- */
|
|
|
|
|
|
|
|
|
|
/* Current module handle for active native dispatch. */
|
|
|
|
|
static CELL_THREAD_LOCAL void *g_current_dl_handle = NULL;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
void *dl_handle;
|
|
|
|
|
JSContext *ctx;
|
|
|
|
|
JSGCRef *vals;
|
|
|
|
|
int count;
|
|
|
|
|
} AOTLiteralPool;
|
|
|
|
|
typedef struct {
|
|
|
|
|
const char *name;
|
|
|
|
|
JSValue key;
|
|
|
|
|
} AOTKeyCacheEntry;
|
|
|
|
|
typedef struct {
|
|
|
|
|
void *dl_handle;
|
|
|
|
|
int64_t fn_idx;
|
|
|
|
|
JSValue code;
|
|
|
|
|
} AOTCodeCacheEntry;
|
|
|
|
|
|
|
|
|
|
static CELL_THREAD_LOCAL AOTLiteralPool g_aot_lit_pool = {0};
|
|
|
|
|
typedef struct AOTGCRefChunk AOTGCRefChunk;
|
|
|
|
|
|
|
|
|
|
static void aot_clear_lit_pool(void) {
|
|
|
|
|
if (g_aot_lit_pool.vals) {
|
|
|
|
|
if (g_aot_lit_pool.ctx) {
|
|
|
|
|
for (int i = 0; i < g_aot_lit_pool.count; i++)
|
|
|
|
|
JS_DeleteGCRef(g_aot_lit_pool.ctx, &g_aot_lit_pool.vals[i]);
|
|
|
|
|
}
|
|
|
|
|
free(g_aot_lit_pool.vals);
|
|
|
|
|
typedef struct {
|
|
|
|
|
void *current_dl_handle;
|
|
|
|
|
AOTLiteralPool lit_pool;
|
|
|
|
|
AOTKeyCacheEntry *key_cache;
|
|
|
|
|
int key_cache_count;
|
|
|
|
|
int key_cache_cap;
|
|
|
|
|
AOTCodeCacheEntry *code_cache;
|
|
|
|
|
int code_cache_count;
|
|
|
|
|
int code_cache_cap;
|
|
|
|
|
JSGCRef native_env_ref;
|
|
|
|
|
int has_native_env;
|
|
|
|
|
int native_env_ref_inited;
|
|
|
|
|
AOTGCRefChunk **gc_ref_chunks;
|
|
|
|
|
int gc_ref_chunk_count;
|
|
|
|
|
int aot_depth;
|
|
|
|
|
JSValue pending_callee_frame;
|
|
|
|
|
int pending_is_tail;
|
|
|
|
|
} NativeRTState;
|
|
|
|
|
|
|
|
|
|
static NativeRTState *native_state(JSContext *ctx) {
|
|
|
|
|
NativeRTState *st = (NativeRTState *)ctx->native_state;
|
|
|
|
|
if (st) return st;
|
|
|
|
|
st = js_mallocz_rt(sizeof(*st));
|
|
|
|
|
if (!st) {
|
|
|
|
|
JS_RaiseOOM(ctx);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
g_aot_lit_pool.dl_handle = NULL;
|
|
|
|
|
g_aot_lit_pool.ctx = NULL;
|
|
|
|
|
g_aot_lit_pool.vals = NULL;
|
|
|
|
|
g_aot_lit_pool.count = 0;
|
|
|
|
|
ctx->native_state = st;
|
|
|
|
|
return st;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int aot_load_lit_pool(JSContext *ctx, void *dl_handle) {
|
|
|
|
|
aot_clear_lit_pool();
|
|
|
|
|
g_aot_lit_pool.dl_handle = dl_handle;
|
|
|
|
|
g_aot_lit_pool.ctx = ctx;
|
|
|
|
|
static void aot_clear_lit_pool(JSContext *ctx, NativeRTState *st) {
|
|
|
|
|
if (!st) return;
|
|
|
|
|
if (st->lit_pool.vals) {
|
|
|
|
|
for (int i = 0; i < st->lit_pool.count; i++)
|
|
|
|
|
JS_DeleteGCRef(ctx, &st->lit_pool.vals[i]);
|
|
|
|
|
free(st->lit_pool.vals);
|
|
|
|
|
}
|
|
|
|
|
st->lit_pool.dl_handle = NULL;
|
|
|
|
|
st->lit_pool.ctx = NULL;
|
|
|
|
|
st->lit_pool.vals = NULL;
|
|
|
|
|
st->lit_pool.count = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int aot_load_lit_pool(JSContext *ctx, NativeRTState *st, void *dl_handle) {
|
|
|
|
|
aot_clear_lit_pool(ctx, st);
|
|
|
|
|
st->lit_pool.dl_handle = dl_handle;
|
|
|
|
|
st->lit_pool.ctx = ctx;
|
|
|
|
|
if (!dl_handle)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
@@ -269,20 +299,20 @@ static int aot_load_lit_pool(JSContext *ctx, void *dl_handle) {
|
|
|
|
|
if (count <= 0 || !table_ptr)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
g_aot_lit_pool.vals = (JSGCRef *)calloc((size_t)count, sizeof(JSGCRef));
|
|
|
|
|
if (!g_aot_lit_pool.vals) {
|
|
|
|
|
st->lit_pool.vals = (JSGCRef *)calloc((size_t)count, sizeof(JSGCRef));
|
|
|
|
|
if (!st->lit_pool.vals) {
|
|
|
|
|
JS_RaiseOOM(ctx);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
g_aot_lit_pool.count = 0;
|
|
|
|
|
st->lit_pool.count = 0;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
|
const char *cstr = table_ptr[i] ? table_ptr[i] : "";
|
|
|
|
|
JS_AddGCRef(ctx, &g_aot_lit_pool.vals[i]);
|
|
|
|
|
g_aot_lit_pool.count = i + 1;
|
|
|
|
|
g_aot_lit_pool.vals[i].val = js_key_new(ctx, cstr);
|
|
|
|
|
if (JS_IsException(g_aot_lit_pool.vals[i].val)) {
|
|
|
|
|
aot_clear_lit_pool();
|
|
|
|
|
JS_AddGCRef(ctx, &st->lit_pool.vals[i]);
|
|
|
|
|
st->lit_pool.count = i + 1;
|
|
|
|
|
st->lit_pool.vals[i].val = js_key_new(ctx, cstr);
|
|
|
|
|
if (JS_IsException(st->lit_pool.vals[i].val)) {
|
|
|
|
|
aot_clear_lit_pool(ctx, st);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -290,70 +320,56 @@ static int aot_load_lit_pool(JSContext *ctx, void *dl_handle) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static JSValue aot_lit_from_index(JSContext *ctx, int64_t lit_idx) {
|
|
|
|
|
NativeRTState *st = native_state(ctx);
|
|
|
|
|
if (!st) return JS_EXCEPTION;
|
|
|
|
|
if (lit_idx < 0) {
|
|
|
|
|
JS_RaiseDisrupt(ctx, "literal index out of range");
|
|
|
|
|
return JS_EXCEPTION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (g_aot_lit_pool.dl_handle != g_current_dl_handle || g_aot_lit_pool.ctx != ctx) {
|
|
|
|
|
if (!aot_load_lit_pool(ctx, g_current_dl_handle))
|
|
|
|
|
if (st->lit_pool.dl_handle != st->current_dl_handle || st->lit_pool.ctx != ctx) {
|
|
|
|
|
if (!aot_load_lit_pool(ctx, st, st->current_dl_handle))
|
|
|
|
|
return JS_EXCEPTION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lit_idx >= g_aot_lit_pool.count) {
|
|
|
|
|
if (lit_idx >= st->lit_pool.count) {
|
|
|
|
|
JS_RaiseDisrupt(ctx, "literal index out of range");
|
|
|
|
|
return JS_EXCEPTION;
|
|
|
|
|
}
|
|
|
|
|
return g_aot_lit_pool.vals[lit_idx].val;
|
|
|
|
|
return st->lit_pool.vals[lit_idx].val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
const char *name;
|
|
|
|
|
JSValue key;
|
|
|
|
|
} AOTKeyCacheEntry;
|
|
|
|
|
|
|
|
|
|
static CELL_THREAD_LOCAL JSContext *g_aot_key_cache_ctx = NULL;
|
|
|
|
|
static CELL_THREAD_LOCAL AOTKeyCacheEntry *g_aot_key_cache = NULL;
|
|
|
|
|
static CELL_THREAD_LOCAL int g_aot_key_cache_count = 0;
|
|
|
|
|
static CELL_THREAD_LOCAL int g_aot_key_cache_cap = 0;
|
|
|
|
|
|
|
|
|
|
/* Convert a static C string to an interned JSValue key.
|
|
|
|
|
Uses a small thread-local cache keyed by C-string pointer to avoid
|
|
|
|
|
Uses a small per-actor cache keyed by C-string pointer to avoid
|
|
|
|
|
repeated UTF-8 decoding in hot property paths. */
|
|
|
|
|
static JSValue aot_key_from_cstr(JSContext *ctx, const char *name) {
|
|
|
|
|
NativeRTState *st = native_state(ctx);
|
|
|
|
|
if (!st) return JS_EXCEPTION;
|
|
|
|
|
if (!name)
|
|
|
|
|
return JS_NULL;
|
|
|
|
|
|
|
|
|
|
if (g_aot_key_cache_ctx != ctx) {
|
|
|
|
|
free(g_aot_key_cache);
|
|
|
|
|
g_aot_key_cache = NULL;
|
|
|
|
|
g_aot_key_cache_count = 0;
|
|
|
|
|
g_aot_key_cache_cap = 0;
|
|
|
|
|
g_aot_key_cache_ctx = ctx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < g_aot_key_cache_count; i++) {
|
|
|
|
|
if (g_aot_key_cache[i].name == name)
|
|
|
|
|
return g_aot_key_cache[i].key;
|
|
|
|
|
for (int i = 0; i < st->key_cache_count; i++) {
|
|
|
|
|
if (st->key_cache[i].name == name)
|
|
|
|
|
return st->key_cache[i].key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JSValue key = js_key_new(ctx, name);
|
|
|
|
|
if (JS_IsNull(key))
|
|
|
|
|
return JS_RaiseDisrupt(ctx, "invalid property key");
|
|
|
|
|
|
|
|
|
|
if (g_aot_key_cache_count >= g_aot_key_cache_cap) {
|
|
|
|
|
int new_cap = g_aot_key_cache_cap ? (g_aot_key_cache_cap * 2) : 64;
|
|
|
|
|
if (st->key_cache_count >= st->key_cache_cap) {
|
|
|
|
|
int new_cap = st->key_cache_cap ? (st->key_cache_cap * 2) : 64;
|
|
|
|
|
AOTKeyCacheEntry *new_cache =
|
|
|
|
|
(AOTKeyCacheEntry *)realloc(g_aot_key_cache, (size_t)new_cap * sizeof(*new_cache));
|
|
|
|
|
(AOTKeyCacheEntry *)realloc(st->key_cache, (size_t)new_cap * sizeof(*new_cache));
|
|
|
|
|
if (!new_cache)
|
|
|
|
|
return JS_RaiseOOM(ctx);
|
|
|
|
|
g_aot_key_cache = new_cache;
|
|
|
|
|
g_aot_key_cache_cap = new_cap;
|
|
|
|
|
st->key_cache = new_cache;
|
|
|
|
|
st->key_cache_cap = new_cap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_aot_key_cache[g_aot_key_cache_count].name = name;
|
|
|
|
|
g_aot_key_cache[g_aot_key_cache_count].key = key;
|
|
|
|
|
g_aot_key_cache_count++;
|
|
|
|
|
st->key_cache[st->key_cache_count].name = name;
|
|
|
|
|
st->key_cache[st->key_cache_count].key = key;
|
|
|
|
|
st->key_cache_count++;
|
|
|
|
|
return key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -460,42 +476,44 @@ int cell_rt_store_index(JSContext *ctx, JSValue val, JSValue arr,
|
|
|
|
|
|
|
|
|
|
/* --- Intrinsic/global lookup --- */
|
|
|
|
|
|
|
|
|
|
/* Native module environment — set before executing a native module's cell_main.
|
|
|
|
|
Contains runtime functions (starts_with, ends_with, etc.) and use(). */
|
|
|
|
|
static CELL_THREAD_LOCAL JSGCRef g_native_env_ref;
|
|
|
|
|
static CELL_THREAD_LOCAL int g_has_native_env = 0;
|
|
|
|
|
|
|
|
|
|
void cell_rt_set_native_env(JSContext *ctx, JSValue env) {
|
|
|
|
|
NativeRTState *st = native_state(ctx);
|
|
|
|
|
if (!st) return;
|
|
|
|
|
if (!JS_IsNull(env) && !JS_IsStone(env)) {
|
|
|
|
|
fprintf(stderr, "cell_rt_set_native_env: ERROR env not stone\n");
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
/* Drop module literal pool roots before switching native env/module. */
|
|
|
|
|
aot_clear_lit_pool();
|
|
|
|
|
aot_clear_lit_pool(ctx, st);
|
|
|
|
|
|
|
|
|
|
/* Native module boundary: clear per-thread key cache so stale keys
|
|
|
|
|
/* Native module boundary: clear per-actor key cache so stale keys
|
|
|
|
|
cannot survive across context/module lifetimes. */
|
|
|
|
|
free(g_aot_key_cache);
|
|
|
|
|
g_aot_key_cache = NULL;
|
|
|
|
|
g_aot_key_cache_count = 0;
|
|
|
|
|
g_aot_key_cache_cap = 0;
|
|
|
|
|
g_aot_key_cache_ctx = ctx;
|
|
|
|
|
free(st->key_cache);
|
|
|
|
|
st->key_cache = NULL;
|
|
|
|
|
st->key_cache_count = 0;
|
|
|
|
|
st->key_cache_cap = 0;
|
|
|
|
|
|
|
|
|
|
if (g_has_native_env)
|
|
|
|
|
JS_DeleteGCRef(ctx, &g_native_env_ref);
|
|
|
|
|
if (st->has_native_env && st->native_env_ref_inited) {
|
|
|
|
|
JS_DeleteGCRef(ctx, &st->native_env_ref);
|
|
|
|
|
st->native_env_ref_inited = 0;
|
|
|
|
|
}
|
|
|
|
|
if (!JS_IsNull(env)) {
|
|
|
|
|
JS_AddGCRef(ctx, &g_native_env_ref);
|
|
|
|
|
g_native_env_ref.val = env;
|
|
|
|
|
g_has_native_env = 1;
|
|
|
|
|
JS_AddGCRef(ctx, &st->native_env_ref);
|
|
|
|
|
st->native_env_ref_inited = 1;
|
|
|
|
|
st->native_env_ref.val = env;
|
|
|
|
|
st->has_native_env = 1;
|
|
|
|
|
} else {
|
|
|
|
|
g_has_native_env = 0;
|
|
|
|
|
st->has_native_env = 0;
|
|
|
|
|
st->native_env_ref.val = JS_NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static JSValue cell_rt_get_intrinsic_key(JSContext *ctx, JSValue key) {
|
|
|
|
|
NativeRTState *st = native_state(ctx);
|
|
|
|
|
if (!st) return JS_EXCEPTION;
|
|
|
|
|
/* Check native env first (runtime-provided functions like log) */
|
|
|
|
|
if (g_has_native_env) {
|
|
|
|
|
JSValue v = JS_GetProperty(ctx, g_native_env_ref.val, key);
|
|
|
|
|
if (st->has_native_env) {
|
|
|
|
|
JSValue v = JS_GetProperty(ctx, st->native_env_ref.val, key);
|
|
|
|
|
if (!JS_IsNull(v))
|
|
|
|
|
return v;
|
|
|
|
|
}
|
|
|
|
|
@@ -586,75 +604,51 @@ typedef struct AOTGCRefChunk {
|
|
|
|
|
uint8_t inited[AOT_GC_REF_CHUNK_SIZE];
|
|
|
|
|
} AOTGCRefChunk;
|
|
|
|
|
|
|
|
|
|
static CELL_THREAD_LOCAL AOTGCRefChunk **g_aot_gc_ref_chunks = NULL;
|
|
|
|
|
static CELL_THREAD_LOCAL int g_aot_gc_ref_chunk_count = 0;
|
|
|
|
|
static CELL_THREAD_LOCAL int g_aot_depth = 0;
|
|
|
|
|
static CELL_THREAD_LOCAL JSContext *g_aot_gc_ref_ctx = NULL;
|
|
|
|
|
|
|
|
|
|
int cell_rt_native_active(void) {
|
|
|
|
|
return g_aot_depth > 0;
|
|
|
|
|
int cell_rt_native_active(JSContext *ctx) {
|
|
|
|
|
NativeRTState *st = (NativeRTState *)ctx->native_state;
|
|
|
|
|
return st ? (st->aot_depth > 0) : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ensure_aot_gc_ref_slot(JSContext *ctx, int depth_index) {
|
|
|
|
|
static int ensure_aot_gc_ref_slot(JSContext *ctx, NativeRTState *st, int depth_index) {
|
|
|
|
|
if (depth_index < 0)
|
|
|
|
|
return 0;
|
|
|
|
|
int needed_chunks = (depth_index / AOT_GC_REF_CHUNK_SIZE) + 1;
|
|
|
|
|
if (needed_chunks <= g_aot_gc_ref_chunk_count)
|
|
|
|
|
if (needed_chunks <= st->gc_ref_chunk_count)
|
|
|
|
|
return 1;
|
|
|
|
|
AOTGCRefChunk **new_chunks =
|
|
|
|
|
(AOTGCRefChunk **)realloc(g_aot_gc_ref_chunks,
|
|
|
|
|
(AOTGCRefChunk **)realloc(st->gc_ref_chunks,
|
|
|
|
|
(size_t)needed_chunks * sizeof(*new_chunks));
|
|
|
|
|
if (!new_chunks) {
|
|
|
|
|
JS_RaiseOOM(ctx);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
g_aot_gc_ref_chunks = new_chunks;
|
|
|
|
|
for (int i = g_aot_gc_ref_chunk_count; i < needed_chunks; i++) {
|
|
|
|
|
g_aot_gc_ref_chunks[i] = (AOTGCRefChunk *)calloc(1, sizeof(AOTGCRefChunk));
|
|
|
|
|
if (!g_aot_gc_ref_chunks[i]) {
|
|
|
|
|
st->gc_ref_chunks = new_chunks;
|
|
|
|
|
for (int i = st->gc_ref_chunk_count; i < needed_chunks; i++) {
|
|
|
|
|
st->gc_ref_chunks[i] = (AOTGCRefChunk *)calloc(1, sizeof(AOTGCRefChunk));
|
|
|
|
|
if (!st->gc_ref_chunks[i]) {
|
|
|
|
|
JS_RaiseOOM(ctx);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
g_aot_gc_ref_chunk_count = needed_chunks;
|
|
|
|
|
st->gc_ref_chunk_count = needed_chunks;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline JSGCRef *aot_gc_ref_at(int depth_index) {
|
|
|
|
|
static inline JSGCRef *aot_gc_ref_at(NativeRTState *st, int depth_index) {
|
|
|
|
|
int chunk_index = depth_index / AOT_GC_REF_CHUNK_SIZE;
|
|
|
|
|
int slot_index = depth_index % AOT_GC_REF_CHUNK_SIZE;
|
|
|
|
|
return &g_aot_gc_ref_chunks[chunk_index]->refs[slot_index];
|
|
|
|
|
return &st->gc_ref_chunks[chunk_index]->refs[slot_index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline uint8_t *aot_gc_ref_inited_at(int depth_index) {
|
|
|
|
|
static inline uint8_t *aot_gc_ref_inited_at(NativeRTState *st, int depth_index) {
|
|
|
|
|
int chunk_index = depth_index / AOT_GC_REF_CHUNK_SIZE;
|
|
|
|
|
int slot_index = depth_index % AOT_GC_REF_CHUNK_SIZE;
|
|
|
|
|
return &g_aot_gc_ref_chunks[chunk_index]->inited[slot_index];
|
|
|
|
|
return &st->gc_ref_chunks[chunk_index]->inited[slot_index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* GC refs are owned by a specific JSContext. If context changes on this thread,
|
|
|
|
|
unregister previous refs and reset per-slot initialization state. */
|
|
|
|
|
static void aot_gc_ref_reset_ctx(JSContext *ctx) {
|
|
|
|
|
if (g_aot_gc_ref_ctx == ctx)
|
|
|
|
|
return;
|
|
|
|
|
if (g_aot_gc_ref_ctx) {
|
|
|
|
|
for (int ci = 0; ci < g_aot_gc_ref_chunk_count; ci++) {
|
|
|
|
|
AOTGCRefChunk *chunk = g_aot_gc_ref_chunks[ci];
|
|
|
|
|
for (int si = 0; si < AOT_GC_REF_CHUNK_SIZE; si++) {
|
|
|
|
|
if (chunk->inited[si]) {
|
|
|
|
|
JS_DeleteGCRef(g_aot_gc_ref_ctx, &chunk->refs[si]);
|
|
|
|
|
chunk->inited[si] = 0;
|
|
|
|
|
chunk->refs[si].val = JS_NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
g_aot_gc_ref_ctx = ctx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void aot_gc_ref_activate(JSContext *ctx, int depth_index) {
|
|
|
|
|
JSGCRef *ref = aot_gc_ref_at(depth_index);
|
|
|
|
|
uint8_t *inited = aot_gc_ref_inited_at(depth_index);
|
|
|
|
|
static inline void aot_gc_ref_activate(JSContext *ctx, NativeRTState *st, int depth_index) {
|
|
|
|
|
JSGCRef *ref = aot_gc_ref_at(st, depth_index);
|
|
|
|
|
uint8_t *inited = aot_gc_ref_inited_at(st, depth_index);
|
|
|
|
|
if (!*inited) {
|
|
|
|
|
JS_AddGCRef(ctx, ref);
|
|
|
|
|
*inited = 1;
|
|
|
|
|
@@ -662,42 +656,45 @@ static inline void aot_gc_ref_activate(JSContext *ctx, int depth_index) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JSValue *cell_rt_enter_frame(JSContext *ctx, int64_t nr_slots) {
|
|
|
|
|
aot_gc_ref_reset_ctx(ctx);
|
|
|
|
|
if (!ensure_aot_gc_ref_slot(ctx, g_aot_depth)) {
|
|
|
|
|
NativeRTState *st = native_state(ctx);
|
|
|
|
|
if (!st) return NULL;
|
|
|
|
|
if (!ensure_aot_gc_ref_slot(ctx, st, st->aot_depth)) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
JSFrameRegister *frame = alloc_frame_register(ctx, (int)nr_slots);
|
|
|
|
|
if (!frame) return NULL;
|
|
|
|
|
aot_gc_ref_activate(ctx, g_aot_depth);
|
|
|
|
|
JSGCRef *ref = aot_gc_ref_at(g_aot_depth);
|
|
|
|
|
aot_gc_ref_activate(ctx, st, st->aot_depth);
|
|
|
|
|
JSGCRef *ref = aot_gc_ref_at(st, st->aot_depth);
|
|
|
|
|
ref->val = JS_MKPTR(frame);
|
|
|
|
|
g_aot_depth++;
|
|
|
|
|
st->aot_depth++;
|
|
|
|
|
return (JSValue *)frame->slots;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Push an already-allocated frame onto the active AOT frame stack. */
|
|
|
|
|
static int cell_rt_push_existing_frame(JSContext *ctx, JSValue frame_val) {
|
|
|
|
|
aot_gc_ref_reset_ctx(ctx);
|
|
|
|
|
if (!ensure_aot_gc_ref_slot(ctx, g_aot_depth))
|
|
|
|
|
NativeRTState *st = native_state(ctx);
|
|
|
|
|
if (!st) return 0;
|
|
|
|
|
if (!ensure_aot_gc_ref_slot(ctx, st, st->aot_depth))
|
|
|
|
|
return 0;
|
|
|
|
|
aot_gc_ref_activate(ctx, g_aot_depth);
|
|
|
|
|
JSGCRef *ref = aot_gc_ref_at(g_aot_depth);
|
|
|
|
|
aot_gc_ref_activate(ctx, st, st->aot_depth);
|
|
|
|
|
JSGCRef *ref = aot_gc_ref_at(st, st->aot_depth);
|
|
|
|
|
ref->val = frame_val;
|
|
|
|
|
g_aot_depth++;
|
|
|
|
|
st->aot_depth++;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JSValue *cell_rt_refresh_fp(JSContext *ctx) {
|
|
|
|
|
(void)ctx;
|
|
|
|
|
if (g_aot_depth <= 0) {
|
|
|
|
|
fprintf(stderr, "[BUG] cell_rt_refresh_fp: g_aot_depth=%d\n", g_aot_depth);
|
|
|
|
|
NativeRTState *st = native_state(ctx);
|
|
|
|
|
if (!st) return NULL;
|
|
|
|
|
if (st->aot_depth <= 0) {
|
|
|
|
|
fprintf(stderr, "[BUG] cell_rt_refresh_fp: aot_depth=%d\n", st->aot_depth);
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
JSValue val = aot_gc_ref_at(g_aot_depth - 1)->val;
|
|
|
|
|
JSValue val = aot_gc_ref_at(st, st->aot_depth - 1)->val;
|
|
|
|
|
JSFrameRegister *frame = (JSFrameRegister *)JS_VALUE_GET_PTR(val);
|
|
|
|
|
if (!frame) {
|
|
|
|
|
fprintf(stderr, "[BUG] cell_rt_refresh_fp: frame is NULL at depth=%d val=%lld\n",
|
|
|
|
|
g_aot_depth, (long long)val);
|
|
|
|
|
st->aot_depth, (long long)val);
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
return (JSValue *)frame->slots;
|
|
|
|
|
@@ -705,13 +702,15 @@ JSValue *cell_rt_refresh_fp(JSContext *ctx) {
|
|
|
|
|
|
|
|
|
|
/* Combined refresh + exception check in a single call. */
|
|
|
|
|
JSValue *cell_rt_refresh_fp_checked(JSContext *ctx) {
|
|
|
|
|
NativeRTState *st = native_state(ctx);
|
|
|
|
|
if (!st) return NULL;
|
|
|
|
|
if (JS_HasException(ctx))
|
|
|
|
|
return NULL;
|
|
|
|
|
if (g_aot_depth <= 0) {
|
|
|
|
|
fprintf(stderr, "[BUG] cell_rt_refresh_fp_checked: g_aot_depth=%d\n", g_aot_depth);
|
|
|
|
|
if (st->aot_depth <= 0) {
|
|
|
|
|
fprintf(stderr, "[BUG] cell_rt_refresh_fp_checked: aot_depth=%d\n", st->aot_depth);
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
JSValue val = aot_gc_ref_at(g_aot_depth - 1)->val;
|
|
|
|
|
JSValue val = aot_gc_ref_at(st, st->aot_depth - 1)->val;
|
|
|
|
|
JSFrameRegister *frame = (JSFrameRegister *)JS_VALUE_GET_PTR(val);
|
|
|
|
|
if (!frame) {
|
|
|
|
|
fprintf(stderr, "[BUG] cell_rt_refresh_fp_checked: frame is NULL\n");
|
|
|
|
|
@@ -721,34 +720,26 @@ JSValue *cell_rt_refresh_fp_checked(JSContext *ctx) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void cell_rt_leave_frame(JSContext *ctx) {
|
|
|
|
|
(void)ctx;
|
|
|
|
|
if (g_aot_depth <= 0) {
|
|
|
|
|
NativeRTState *st = native_state(ctx);
|
|
|
|
|
if (!st) return;
|
|
|
|
|
if (st->aot_depth <= 0) {
|
|
|
|
|
fprintf(stderr, "[BUG] cell_rt_leave_frame underflow\n");
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
g_aot_depth--;
|
|
|
|
|
aot_gc_ref_at(g_aot_depth)->val = JS_NULL;
|
|
|
|
|
st->aot_depth--;
|
|
|
|
|
aot_gc_ref_at(st, st->aot_depth)->val = JS_NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* --- Function creation and calling --- */
|
|
|
|
|
|
|
|
|
|
typedef JSValue (*cell_compiled_fn)(JSContext *ctx, void *fp);
|
|
|
|
|
|
|
|
|
|
/* Set before executing a native module's cell_main —
|
|
|
|
|
used by cell_rt_make_function to resolve fn_ptr via dlsym */
|
|
|
|
|
/* g_current_dl_handle is defined near property/literal helpers. */
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
Dispatch loop — the core of native function execution.
|
|
|
|
|
Each compiled cell_fn_N returns to this loop when it needs
|
|
|
|
|
to call another function (instead of recursing via C stack).
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
/* Pending call state — set by cell_rt_signal_call / cell_rt_signal_tail_call,
|
|
|
|
|
read by the dispatch loop. */
|
|
|
|
|
static CELL_THREAD_LOCAL JSValue g_pending_callee_frame = 0; /* JSFrameRegister ptr */
|
|
|
|
|
static CELL_THREAD_LOCAL int g_pending_is_tail = 0;
|
|
|
|
|
|
|
|
|
|
/* Poll pause state on taken backward jumps (AOT backedges).
|
|
|
|
|
MACH can suspend/resume a register VM frame at pc granularity; native AOT
|
|
|
|
|
does not currently have an equivalent resume point, so we acknowledge timer
|
|
|
|
|
@@ -762,34 +753,38 @@ int cell_rt_check_backedge(JSContext *ctx) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void cell_rt_signal_call(JSContext *ctx, void *fp, int64_t frame_slot) {
|
|
|
|
|
(void)ctx;
|
|
|
|
|
NativeRTState *st = native_state(ctx);
|
|
|
|
|
if (!st) return;
|
|
|
|
|
JSValue *slots = (JSValue *)fp;
|
|
|
|
|
g_pending_callee_frame = slots[frame_slot];
|
|
|
|
|
g_pending_is_tail = 0;
|
|
|
|
|
st->pending_callee_frame = slots[frame_slot];
|
|
|
|
|
st->pending_is_tail = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void cell_rt_signal_tail_call(JSContext *ctx, void *fp, int64_t frame_slot) {
|
|
|
|
|
(void)ctx;
|
|
|
|
|
NativeRTState *st = native_state(ctx);
|
|
|
|
|
if (!st) return;
|
|
|
|
|
JSValue *slots = (JSValue *)fp;
|
|
|
|
|
g_pending_callee_frame = slots[frame_slot];
|
|
|
|
|
g_pending_is_tail = 1;
|
|
|
|
|
st->pending_callee_frame = slots[frame_slot];
|
|
|
|
|
st->pending_is_tail = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Entry point called from JS_CallInternal / JS_Call / MACH_INVOKE
|
|
|
|
|
for JS_FUNC_KIND_NATIVE functions. */
|
|
|
|
|
JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|
|
|
|
JSValue this_obj, int argc, JSValue *argv) {
|
|
|
|
|
NativeRTState *st = native_state(ctx);
|
|
|
|
|
if (!st) return JS_EXCEPTION;
|
|
|
|
|
JSFunction *f = JS_VALUE_GET_FUNCTION(func_obj);
|
|
|
|
|
cell_compiled_fn fn = (cell_compiled_fn)JS_VALUE_GET_CODE(f->u.cell.code)->u.native.fn_ptr;
|
|
|
|
|
int nr_slots = JS_VALUE_GET_CODE(f->u.cell.code)->u.native.nr_slots;
|
|
|
|
|
int arity = f->length;
|
|
|
|
|
void *prev_dl_handle = g_current_dl_handle;
|
|
|
|
|
g_current_dl_handle = JS_VALUE_GET_CODE(f->u.cell.code)->u.native.dl_handle;
|
|
|
|
|
void *prev_dl_handle = st->current_dl_handle;
|
|
|
|
|
st->current_dl_handle = JS_VALUE_GET_CODE(f->u.cell.code)->u.native.dl_handle;
|
|
|
|
|
|
|
|
|
|
#define RETURN_DISPATCH(v) \
|
|
|
|
|
do { \
|
|
|
|
|
atomic_store_explicit(&ctx->pause_flag, 0, memory_order_relaxed); \
|
|
|
|
|
g_current_dl_handle = prev_dl_handle; \
|
|
|
|
|
st->current_dl_handle = prev_dl_handle; \
|
|
|
|
|
return (v); \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
@@ -820,11 +815,11 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|
|
|
|
JSFrameRegister *frame = (JSFrameRegister *)((char *)fp - offsetof(JSFrameRegister, slots));
|
|
|
|
|
frame->function = func_obj;
|
|
|
|
|
|
|
|
|
|
int base_depth = g_aot_depth; /* remember entry depth for return detection */
|
|
|
|
|
int base_depth = st->aot_depth; /* remember entry depth for return detection */
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
g_pending_callee_frame = 0;
|
|
|
|
|
g_pending_is_tail = 0;
|
|
|
|
|
st->pending_callee_frame = 0;
|
|
|
|
|
st->pending_is_tail = 0;
|
|
|
|
|
if (atomic_load_explicit(&ctx->pause_flag, memory_order_relaxed) >= 1)
|
|
|
|
|
atomic_store_explicit(&ctx->pause_flag, 0, memory_order_relaxed);
|
|
|
|
|
|
|
|
|
|
@@ -832,26 +827,26 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|
|
|
|
if (JS_IsFunction(frame->function)) {
|
|
|
|
|
JSFunction *cur_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
|
|
|
|
if (cur_fn->kind == JS_FUNC_KIND_NATIVE)
|
|
|
|
|
g_current_dl_handle = JS_VALUE_GET_CODE(cur_fn->u.cell.code)->u.native.dl_handle;
|
|
|
|
|
st->current_dl_handle = JS_VALUE_GET_CODE(cur_fn->u.cell.code)->u.native.dl_handle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JSValue result = fn(ctx, fp);
|
|
|
|
|
|
|
|
|
|
/* Re-derive frame after potential GC */
|
|
|
|
|
if (g_aot_depth <= 0) {
|
|
|
|
|
if (st->aot_depth <= 0) {
|
|
|
|
|
fprintf(stderr, "[BUG] native dispatch lost frame depth after fn call\n");
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
JSValue frame_val = aot_gc_ref_at(g_aot_depth - 1)->val;
|
|
|
|
|
JSValue frame_val = aot_gc_ref_at(st, st->aot_depth - 1)->val;
|
|
|
|
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
|
|
|
|
|
fp = (JSValue *)frame->slots;
|
|
|
|
|
|
|
|
|
|
if (g_pending_callee_frame != 0) {
|
|
|
|
|
if (st->pending_callee_frame != 0) {
|
|
|
|
|
/* Function signaled a call — dispatch it */
|
|
|
|
|
JSValue callee_frame_val = g_pending_callee_frame;
|
|
|
|
|
g_pending_callee_frame = 0;
|
|
|
|
|
int pending_is_tail = g_pending_is_tail;
|
|
|
|
|
g_pending_is_tail = 0;
|
|
|
|
|
JSValue callee_frame_val = st->pending_callee_frame;
|
|
|
|
|
st->pending_callee_frame = 0;
|
|
|
|
|
int pending_is_tail = st->pending_is_tail;
|
|
|
|
|
st->pending_is_tail = 0;
|
|
|
|
|
JSGCRef callee_ref;
|
|
|
|
|
JS_PushGCRef(ctx, &callee_ref);
|
|
|
|
|
callee_ref.val = callee_frame_val;
|
|
|
|
|
@@ -897,7 +892,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|
|
|
|
JS_PopGCRef(ctx, &callee_ref);
|
|
|
|
|
RETURN_DISPATCH(JS_EXCEPTION);
|
|
|
|
|
}
|
|
|
|
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(aot_gc_ref_at(g_aot_depth - 1)->val);
|
|
|
|
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(aot_gc_ref_at(st, st->aot_depth - 1)->val);
|
|
|
|
|
fp = (JSValue *)frame->slots;
|
|
|
|
|
fn = callee_ptr;
|
|
|
|
|
} else {
|
|
|
|
|
@@ -917,7 +912,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|
|
|
|
|
|
|
|
|
if (!cell_rt_push_existing_frame(ctx, callee_ref.val)) {
|
|
|
|
|
/* Resume caller with exception pending */
|
|
|
|
|
frame_val = aot_gc_ref_at(g_aot_depth - 1)->val;
|
|
|
|
|
frame_val = aot_gc_ref_at(st, st->aot_depth - 1)->val;
|
|
|
|
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
|
|
|
|
|
fp = (JSValue *)frame->slots;
|
|
|
|
|
JSFunction *exc_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
|
|
|
|
@@ -926,7 +921,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|
|
|
|
JS_PopGCRef(ctx, &callee_ref);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(aot_gc_ref_at(g_aot_depth - 1)->val);
|
|
|
|
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(aot_gc_ref_at(st, st->aot_depth - 1)->val);
|
|
|
|
|
fp = (JSValue *)frame->slots;
|
|
|
|
|
fn = callee_ptr;
|
|
|
|
|
}
|
|
|
|
|
@@ -942,7 +937,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|
|
|
|
callee_argc, &callee_fr->slots[1], 0);
|
|
|
|
|
|
|
|
|
|
/* Re-derive frame after call */
|
|
|
|
|
frame_val = aot_gc_ref_at(g_aot_depth - 1)->val;
|
|
|
|
|
frame_val = aot_gc_ref_at(st, st->aot_depth - 1)->val;
|
|
|
|
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
|
|
|
|
|
fp = (JSValue *)frame->slots;
|
|
|
|
|
|
|
|
|
|
@@ -970,18 +965,18 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|
|
|
|
if (pending_is_tail) {
|
|
|
|
|
/* Tail call to non-native: return its result up the chain */
|
|
|
|
|
/* Pop current frame and return to caller */
|
|
|
|
|
if (g_aot_depth <= base_depth) {
|
|
|
|
|
if (st->aot_depth <= base_depth) {
|
|
|
|
|
cell_rt_leave_frame(ctx);
|
|
|
|
|
JS_PopGCRef(ctx, &callee_ref);
|
|
|
|
|
RETURN_DISPATCH(ret);
|
|
|
|
|
}
|
|
|
|
|
/* Pop current frame, return to caller frame */
|
|
|
|
|
cell_rt_leave_frame(ctx);
|
|
|
|
|
if (g_aot_depth < base_depth) {
|
|
|
|
|
if (st->aot_depth < base_depth) {
|
|
|
|
|
JS_PopGCRef(ctx, &callee_ref);
|
|
|
|
|
RETURN_DISPATCH(ret);
|
|
|
|
|
}
|
|
|
|
|
frame_val = aot_gc_ref_at(g_aot_depth - 1)->val;
|
|
|
|
|
frame_val = aot_gc_ref_at(st, st->aot_depth - 1)->val;
|
|
|
|
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
|
|
|
|
|
fp = (JSValue *)frame->slots;
|
|
|
|
|
int ret_info = JS_VALUE_GET_INT(frame->address);
|
|
|
|
|
@@ -1014,17 +1009,17 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|
|
|
|
if (!JS_HasException(ctx))
|
|
|
|
|
JS_Disrupt(ctx);
|
|
|
|
|
|
|
|
|
|
if (g_aot_depth <= base_depth) {
|
|
|
|
|
if (st->aot_depth <= base_depth) {
|
|
|
|
|
cell_rt_leave_frame(ctx);
|
|
|
|
|
RETURN_DISPATCH(JS_EXCEPTION);
|
|
|
|
|
}
|
|
|
|
|
cell_rt_leave_frame(ctx);
|
|
|
|
|
if (g_aot_depth < base_depth) {
|
|
|
|
|
if (st->aot_depth < base_depth) {
|
|
|
|
|
RETURN_DISPATCH(JS_EXCEPTION);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Resume caller and tag the return slot with JS_EXCEPTION. */
|
|
|
|
|
frame_val = aot_gc_ref_at(g_aot_depth - 1)->val;
|
|
|
|
|
frame_val = aot_gc_ref_at(st, st->aot_depth - 1)->val;
|
|
|
|
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
|
|
|
|
|
fp = (JSValue *)frame->slots;
|
|
|
|
|
int ret_info = JS_VALUE_GET_INT(frame->address);
|
|
|
|
|
@@ -1038,17 +1033,17 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Normal return — pop frame and store result in caller */
|
|
|
|
|
if (g_aot_depth <= base_depth) {
|
|
|
|
|
if (st->aot_depth <= base_depth) {
|
|
|
|
|
cell_rt_leave_frame(ctx);
|
|
|
|
|
RETURN_DISPATCH(result);
|
|
|
|
|
}
|
|
|
|
|
cell_rt_leave_frame(ctx);
|
|
|
|
|
if (g_aot_depth < base_depth) {
|
|
|
|
|
if (st->aot_depth < base_depth) {
|
|
|
|
|
RETURN_DISPATCH(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return to caller frame */
|
|
|
|
|
frame_val = aot_gc_ref_at(g_aot_depth - 1)->val;
|
|
|
|
|
frame_val = aot_gc_ref_at(st, st->aot_depth - 1)->val;
|
|
|
|
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
|
|
|
|
|
fp = (JSValue *)frame->slots;
|
|
|
|
|
int ret_info = JS_VALUE_GET_INT(frame->address);
|
|
|
|
|
@@ -1064,27 +1059,71 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|
|
|
|
#undef RETURN_DISPATCH
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static JSValue aot_get_or_create_native_code(JSContext *ctx, NativeRTState *st,
|
|
|
|
|
void *dl_handle, int64_t fn_idx,
|
|
|
|
|
int arity, uint16_t nr_slots) {
|
|
|
|
|
for (int i = 0; i < st->code_cache_count; i++) {
|
|
|
|
|
AOTCodeCacheEntry *e = &st->code_cache[i];
|
|
|
|
|
if (e->dl_handle == dl_handle && e->fn_idx == fn_idx)
|
|
|
|
|
return e->code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char name[64];
|
|
|
|
|
snprintf(name, sizeof(name), "cell_fn_%lld", (long long)fn_idx);
|
|
|
|
|
void *fn_ptr = dlsym(dl_handle, name);
|
|
|
|
|
if (!fn_ptr)
|
|
|
|
|
return JS_RaiseDisrupt(ctx, "native function %s not found in dylib", name);
|
|
|
|
|
|
|
|
|
|
JSCode *code = ct_alloc(ctx, sizeof(JSCode), 8);
|
|
|
|
|
if (!code)
|
|
|
|
|
return JS_EXCEPTION;
|
|
|
|
|
memset(code, 0, sizeof(*code));
|
|
|
|
|
code->header = objhdr_make(0, OBJ_CODE, 0, 0, 0, 0);
|
|
|
|
|
code->kind = JS_CODE_KIND_NATIVE;
|
|
|
|
|
code->arity = (int16_t)arity;
|
|
|
|
|
code->u.native.fn_ptr = fn_ptr;
|
|
|
|
|
code->u.native.dl_handle = dl_handle;
|
|
|
|
|
code->u.native.nr_slots = nr_slots;
|
|
|
|
|
JSValue code_obj = JS_MKPTR(code);
|
|
|
|
|
|
|
|
|
|
if (st->code_cache_count >= st->code_cache_cap) {
|
|
|
|
|
int new_cap = st->code_cache_cap ? (st->code_cache_cap * 2) : 128;
|
|
|
|
|
AOTCodeCacheEntry *new_cache =
|
|
|
|
|
(AOTCodeCacheEntry *)realloc(st->code_cache, (size_t)new_cap * sizeof(*new_cache));
|
|
|
|
|
if (!new_cache)
|
|
|
|
|
return JS_RaiseOOM(ctx);
|
|
|
|
|
st->code_cache = new_cache;
|
|
|
|
|
st->code_cache_cap = new_cap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
st->code_cache[st->code_cache_count].dl_handle = dl_handle;
|
|
|
|
|
st->code_cache[st->code_cache_count].fn_idx = fn_idx;
|
|
|
|
|
st->code_cache[st->code_cache_count].code = code_obj;
|
|
|
|
|
st->code_cache_count++;
|
|
|
|
|
return code_obj;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create a native function object from a compiled fn_idx.
|
|
|
|
|
Called from QBE-generated code during function creation. */
|
|
|
|
|
JSValue cell_rt_make_function(JSContext *ctx, int64_t fn_idx, void *outer_fp,
|
|
|
|
|
int64_t nr_args, int64_t nr_slots) {
|
|
|
|
|
if (!g_current_dl_handle)
|
|
|
|
|
(void)outer_fp;
|
|
|
|
|
NativeRTState *st = native_state(ctx);
|
|
|
|
|
if (!st) return JS_EXCEPTION;
|
|
|
|
|
if (!st->current_dl_handle)
|
|
|
|
|
return JS_RaiseDisrupt(ctx, "no native module loaded");
|
|
|
|
|
|
|
|
|
|
/* Resolve fn_ptr via dlsym at creation time — cached in the function object */
|
|
|
|
|
char name[64];
|
|
|
|
|
snprintf(name, sizeof(name), "cell_fn_%lld", (long long)fn_idx);
|
|
|
|
|
void *fn_ptr = dlsym(g_current_dl_handle, name);
|
|
|
|
|
if (!fn_ptr)
|
|
|
|
|
return JS_RaiseDisrupt(ctx, "native function %s not found in dylib", name);
|
|
|
|
|
JSValue code_obj = aot_get_or_create_native_code(
|
|
|
|
|
ctx, st, st->current_dl_handle, fn_idx, (int)nr_args, (uint16_t)nr_slots);
|
|
|
|
|
if (JS_IsException(code_obj))
|
|
|
|
|
return JS_EXCEPTION;
|
|
|
|
|
|
|
|
|
|
/* Get the current frame as outer_frame for closures */
|
|
|
|
|
JSValue outer_frame = JS_NULL;
|
|
|
|
|
if (g_aot_depth > 0)
|
|
|
|
|
outer_frame = aot_gc_ref_at(g_aot_depth - 1)->val;
|
|
|
|
|
if (st->aot_depth > 0)
|
|
|
|
|
outer_frame = aot_gc_ref_at(st, st->aot_depth - 1)->val;
|
|
|
|
|
|
|
|
|
|
return js_new_native_function(ctx, fn_ptr, g_current_dl_handle,
|
|
|
|
|
(uint16_t)nr_slots, (int)nr_args, outer_frame);
|
|
|
|
|
return js_new_native_function_with_code(ctx, code_obj, (int)nr_args, outer_frame);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* --- Frame-based function calling ---
|
|
|
|
|
@@ -1338,22 +1377,24 @@ JSValue cell_rt_regexp(JSContext *ctx, const char *pattern, const char *flags) {
|
|
|
|
|
|
|
|
|
|
/* --- Module entry point ---
|
|
|
|
|
Loads a native .cm module from a dylib handle.
|
|
|
|
|
Looks up cell_main, builds a heap-allocated frame, sets
|
|
|
|
|
g_current_dl_handle so closures register in the right module. */
|
|
|
|
|
Looks up cell_main, builds a heap-allocated frame, and
|
|
|
|
|
records active module handle in per-actor native state. */
|
|
|
|
|
|
|
|
|
|
/* Helper: run a native module's entry point through the dispatch loop.
|
|
|
|
|
Creates a temporary JS_FUNC_KIND_NATIVE function so that the full
|
|
|
|
|
dispatch loop (tail calls, closures, etc.) works for module-level code. */
|
|
|
|
|
static JSValue native_module_run(JSContext *ctx, void *dl_handle,
|
|
|
|
|
cell_compiled_fn entry, int nr_slots) {
|
|
|
|
|
void *prev_handle = g_current_dl_handle;
|
|
|
|
|
g_current_dl_handle = dl_handle;
|
|
|
|
|
NativeRTState *st = native_state(ctx);
|
|
|
|
|
if (!st) return JS_EXCEPTION;
|
|
|
|
|
void *prev_handle = st->current_dl_handle;
|
|
|
|
|
st->current_dl_handle = dl_handle;
|
|
|
|
|
|
|
|
|
|
/* Create a native function object for the entry point */
|
|
|
|
|
JSValue func_obj = js_new_native_function(ctx, (void *)entry, dl_handle,
|
|
|
|
|
(uint16_t)nr_slots, 0, JS_NULL);
|
|
|
|
|
if (JS_IsException(func_obj)) {
|
|
|
|
|
g_current_dl_handle = prev_handle;
|
|
|
|
|
st->current_dl_handle = prev_handle;
|
|
|
|
|
return JS_EXCEPTION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1362,7 +1403,7 @@ static JSValue native_module_run(JSContext *ctx, void *dl_handle,
|
|
|
|
|
JS_GetException(ctx);
|
|
|
|
|
|
|
|
|
|
JSValue result = cell_native_dispatch(ctx, func_obj, JS_NULL, 0, NULL);
|
|
|
|
|
g_current_dl_handle = prev_handle;
|
|
|
|
|
st->current_dl_handle = prev_handle;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1409,6 +1450,38 @@ JSValue cell_rt_native_module_load_named(JSContext *ctx, void *dl_handle, const
|
|
|
|
|
return native_module_run(ctx, dl_handle, fn, nr_slots);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void cell_rt_free_native_state(JSContext *ctx) {
|
|
|
|
|
NativeRTState *st = (NativeRTState *)ctx->native_state;
|
|
|
|
|
if (!st) return;
|
|
|
|
|
|
|
|
|
|
aot_clear_lit_pool(ctx, st);
|
|
|
|
|
|
|
|
|
|
if (st->has_native_env && st->native_env_ref_inited) {
|
|
|
|
|
JS_DeleteGCRef(ctx, &st->native_env_ref);
|
|
|
|
|
st->native_env_ref_inited = 0;
|
|
|
|
|
st->native_env_ref.val = JS_NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int ci = 0; ci < st->gc_ref_chunk_count; ci++) {
|
|
|
|
|
AOTGCRefChunk *chunk = st->gc_ref_chunks[ci];
|
|
|
|
|
if (!chunk) continue;
|
|
|
|
|
for (int si = 0; si < AOT_GC_REF_CHUNK_SIZE; si++) {
|
|
|
|
|
if (chunk->inited[si]) {
|
|
|
|
|
JS_DeleteGCRef(ctx, &chunk->refs[si]);
|
|
|
|
|
chunk->inited[si] = 0;
|
|
|
|
|
chunk->refs[si].val = JS_NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free(chunk);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(st->gc_ref_chunks);
|
|
|
|
|
free(st->key_cache);
|
|
|
|
|
free(st->code_cache);
|
|
|
|
|
js_free_rt(st);
|
|
|
|
|
ctx->native_state = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Backward-compat: uses RTLD_DEFAULT (works when dylib opened with RTLD_GLOBAL) */
|
|
|
|
|
JSValue cell_rt_module_entry(JSContext *ctx) {
|
|
|
|
|
void *handle = dlopen(NULL, RTLD_LAZY);
|
|
|
|
|
|