faster aot

This commit is contained in:
2026-02-18 20:24:12 -06:00
parent 7a4c72025f
commit 621da78de9
4 changed files with 1065 additions and 311 deletions

View File

@@ -81,7 +81,7 @@ function content_hash(str) {
}
// Bump when native codegen/runtime ABI changes so stale dylibs are not reused.
def NATIVE_CACHE_VERSION = "native-v8"
def NATIVE_CACHE_VERSION = "native-v16"
// Enable AOT ASan by creating .cell/asan_aot in the package root.
function native_sanitize_flags() {

6
qbe.cm
View File

@@ -11,7 +11,7 @@ def js_null = 7
def js_false = 3
def js_true = 35
def js_exception = 15
def js_empty_text = 27
def js_empty_text = 11
// Shared closure vars for functions with >4 params
var _qop = null
@@ -67,13 +67,13 @@ var is_ptr = function(p, v) {
var is_imm_text = function(p, v) {
return ` %${p}.t =l and ${v}, 31
%${p} =w ceql %${p}.t, 27
%${p} =w ceql %${p}.t, 11
`
}
var is_text = function(p, v) {
return ` %${p}.imm =l and ${v}, 31
%${p}.is_imm =w ceql %${p}.imm, 27
%${p}.is_imm =w ceql %${p}.imm, 11
jnz %${p}.is_imm, @${p}.yes, @${p}.chk_ptr
@${p}.chk_ptr
%${p}.ptag =l and ${v}, 7

File diff suppressed because it is too large Load Diff

View File

@@ -230,23 +230,188 @@ JSValue qbe_shift_shr(JSContext *ctx, JSValue a, JSValue b) {
/* --- Property access --- */
JSValue cell_rt_load_field(JSContext *ctx, JSValue obj, const char *name) {
/* 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;
static CELL_THREAD_LOCAL AOTLiteralPool g_aot_lit_pool = {0};
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);
}
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;
}
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;
if (!dl_handle)
return 1;
int *count_ptr = (int *)dlsym(dl_handle, "cell_lit_count");
const char **table_ptr = (const char **)dlsym(dl_handle, "cell_lit_table");
int count = count_ptr ? *count_ptr : 0;
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) {
JS_RaiseOOM(ctx);
return 0;
}
g_aot_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();
return 0;
}
}
return 1;
}
static JSValue aot_lit_from_index(JSContext *ctx, int64_t lit_idx) {
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))
return JS_EXCEPTION;
}
if (lit_idx >= g_aot_lit_pool.count) {
JS_RaiseDisrupt(ctx, "literal index out of range");
return JS_EXCEPTION;
}
return g_aot_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
repeated UTF-8 decoding in hot property paths. */
static JSValue aot_key_from_cstr(JSContext *ctx, const char *name) {
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;
}
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;
AOTKeyCacheEntry *new_cache =
(AOTKeyCacheEntry *)realloc(g_aot_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;
}
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++;
return key;
}
static JSValue cell_rt_load_field_key(JSContext *ctx, JSValue obj, JSValue key) {
if (JS_IsFunction(obj)) {
JS_RaiseDisrupt(ctx, "cannot read property of function");
return JS_EXCEPTION;
}
return JS_GetPropertyStr(ctx, obj, name);
return JS_GetProperty(ctx, obj, key);
}
JSValue cell_rt_load_field(JSContext *ctx, JSValue obj, const char *name) {
JSValue key = aot_key_from_cstr(ctx, name);
if (JS_IsException(key))
return JS_EXCEPTION;
return cell_rt_load_field_key(ctx, obj, key);
}
JSValue cell_rt_load_field_lit(JSContext *ctx, JSValue obj, int64_t lit_idx) {
JSValue key = aot_lit_from_index(ctx, lit_idx);
if (JS_IsException(key))
return JS_EXCEPTION;
return cell_rt_load_field_key(ctx, obj, key);
}
/* Like cell_rt_load_field but without the function guard.
Used by load_dynamic when the key happens to be a static string. */
JSValue cell_rt_load_prop_str(JSContext *ctx, JSValue obj, const char *name) {
return JS_GetPropertyStr(ctx, obj, name);
JSValue key = aot_key_from_cstr(ctx, name);
if (JS_IsException(key))
return JS_EXCEPTION;
return JS_GetProperty(ctx, obj, key);
}
void cell_rt_store_field(JSContext *ctx, JSValue val, JSValue obj,
const char *name) {
JS_SetPropertyStr(ctx, obj, name, val);
static int cell_rt_store_field_key(JSContext *ctx, JSValue val, JSValue obj,
JSValue key) {
int ret = JS_SetProperty(ctx, obj, key, val);
return (ret < 0 || JS_HasException(ctx)) ? 0 : 1;
}
int cell_rt_store_field(JSContext *ctx, JSValue val, JSValue obj,
const char *name) {
JSValue key = aot_key_from_cstr(ctx, name);
if (JS_IsException(key))
return 0;
return cell_rt_store_field_key(ctx, val, obj, key);
}
int cell_rt_store_field_lit(JSContext *ctx, JSValue val, JSValue obj,
int64_t lit_idx) {
JSValue key = aot_lit_from_index(ctx, lit_idx);
if (JS_IsException(key))
return 0;
return cell_rt_store_field_key(ctx, val, obj, key);
}
JSValue cell_rt_access_lit(JSContext *ctx, int64_t lit_idx) {
return aot_lit_from_index(ctx, lit_idx);
}
JSValue cell_rt_load_dynamic(JSContext *ctx, JSValue obj, JSValue key) {
@@ -255,16 +420,22 @@ JSValue cell_rt_load_dynamic(JSContext *ctx, JSValue obj, JSValue key) {
return JS_GetProperty(ctx, obj, key);
}
void cell_rt_store_dynamic(JSContext *ctx, JSValue val, JSValue obj,
JSValue key) {
int cell_rt_store_dynamic(JSContext *ctx, JSValue val, JSValue obj,
JSValue key) {
int ret = 0;
JSValue nr = JS_NULL;
if (JS_IsInt(key)) {
JS_SetPropertyNumber(ctx, obj, (uint32_t)JS_VALUE_GET_INT(key), val);
nr = JS_SetPropertyNumber(ctx, obj, (uint32_t)JS_VALUE_GET_INT(key), val);
return JS_IsException(nr) ? 0 : 1;
} else if (JS_IsArray(obj) && !JS_IsInt(key)) {
JS_RaiseDisrupt(ctx, "array index must be a number");
return 0;
} else if (JS_IsBool(key) || JS_IsNull(key) || JS_IsArray(key) || JS_IsFunction(key)) {
JS_RaiseDisrupt(ctx, "object key must be text");
return 0;
} else {
JS_SetProperty(ctx, obj, key, val);
ret = JS_SetProperty(ctx, obj, key, val);
return (ret < 0 || JS_HasException(ctx)) ? 0 : 1;
}
}
@@ -274,12 +445,17 @@ JSValue cell_rt_load_index(JSContext *ctx, JSValue arr, JSValue idx) {
return JS_GetProperty(ctx, arr, idx);
}
void cell_rt_store_index(JSContext *ctx, JSValue val, JSValue arr,
JSValue idx) {
int cell_rt_store_index(JSContext *ctx, JSValue val, JSValue arr,
JSValue idx) {
int ret = 0;
JSValue nr = JS_NULL;
if (JS_IsInt(idx))
JS_SetPropertyNumber(ctx, arr, (uint32_t)JS_VALUE_GET_INT(idx), val);
nr = JS_SetPropertyNumber(ctx, arr, (uint32_t)JS_VALUE_GET_INT(idx), val);
else
JS_SetProperty(ctx, arr, idx, val);
ret = JS_SetProperty(ctx, arr, idx, val);
if (JS_IsInt(idx))
return JS_IsException(nr) ? 0 : 1;
return (ret < 0 || JS_HasException(ctx)) ? 0 : 1;
}
/* --- Intrinsic/global lookup --- */
@@ -294,6 +470,17 @@ void cell_rt_set_native_env(JSContext *ctx, JSValue 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();
/* Native module boundary: clear per-thread 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;
if (g_has_native_env)
JS_DeleteGCRef(ctx, &g_native_env_ref);
if (!JS_IsNull(env)) {
@@ -305,10 +492,10 @@ void cell_rt_set_native_env(JSContext *ctx, JSValue env) {
}
}
JSValue cell_rt_get_intrinsic(JSContext *ctx, const char *name) {
static JSValue cell_rt_get_intrinsic_key(JSContext *ctx, JSValue key) {
/* Check native env first (runtime-provided functions like log) */
if (g_has_native_env) {
JSValue v = JS_GetPropertyStr(ctx, g_native_env_ref.val, name);
JSValue v = JS_GetProperty(ctx, g_native_env_ref.val, key);
if (!JS_IsNull(v))
return v;
}
@@ -319,14 +506,28 @@ JSValue cell_rt_get_intrinsic(JSContext *ctx, const char *name) {
JSRecord *rec = (JSRecord *)chase(gobj);
uint64_t mask = objhdr_cap56(rec->mist_hdr);
for (uint64_t i = 1; i <= mask; i++) {
if (js_key_equal_str(rec->slots[i].key, name))
if (js_key_equal(rec->slots[i].key, key))
return rec->slots[i].val;
}
}
JS_RaiseDisrupt(ctx, "'%s' is not defined", name);
JS_RaiseDisrupt(ctx, "name is not defined");
return JS_EXCEPTION;
}
JSValue cell_rt_get_intrinsic(JSContext *ctx, const char *name) {
JSValue key = aot_key_from_cstr(ctx, name);
if (JS_IsException(key))
return JS_EXCEPTION;
return cell_rt_get_intrinsic_key(ctx, key);
}
JSValue cell_rt_get_intrinsic_lit(JSContext *ctx, int64_t lit_idx) {
JSValue key = aot_lit_from_index(ctx, lit_idx);
if (JS_IsException(key))
return JS_EXCEPTION;
return cell_rt_get_intrinsic_key(ctx, key);
}
/* --- Closure access ---
Walk the outer_frame chain on JSFunction (JS_FUNC_KIND_NATIVE).
The frame's function field links to the JSFunction, whose
@@ -436,6 +637,17 @@ JSValue *cell_rt_enter_frame(JSContext *ctx, int64_t nr_slots) {
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) {
if (!ensure_aot_gc_ref_slot(ctx, g_aot_depth))
return 0;
JSGCRef *ref = aot_gc_ref_at(g_aot_depth);
JS_AddGCRef(ctx, ref);
ref->val = frame_val;
g_aot_depth++;
return 1;
}
JSValue *cell_rt_refresh_fp(JSContext *ctx) {
(void)ctx;
if (g_aot_depth <= 0) {
@@ -484,7 +696,7 @@ 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 */
static CELL_THREAD_LOCAL void *g_current_dl_handle = NULL;
/* g_current_dl_handle is defined near property/literal helpers. */
/* ============================================================
Dispatch loop — the core of native function execution.
@@ -604,8 +816,9 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
JS_PushGCRef(ctx, &callee_ref);
callee_ref.val = callee_frame_val;
JSFrameRegister *callee_fr = (JSFrameRegister *)JS_VALUE_GET_PTR(callee_ref.val);
int callee_argc = (int)objhdr_cap56(callee_fr->header);
callee_argc = (callee_argc >= 2) ? callee_argc - 2 : 0;
int callee_argc = JS_VALUE_GET_INT(callee_fr->address);
if (callee_argc < 0)
callee_argc = 0;
JSValue callee_fn_val = callee_fr->function;
if (!JS_IsFunction(callee_fn_val)) {
@@ -625,47 +838,44 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
if (callee_fn->kind == JS_FUNC_KIND_NATIVE) {
/* Native-to-native call — no C stack growth */
cell_compiled_fn callee_ptr = (cell_compiled_fn)callee_fn->u.native.fn_ptr;
int callee_slots = callee_fn->u.native.nr_slots;
if (pending_is_tail) {
/* Tail call: replace frame instead of mutating in place.
In-place reuse breaks closures that captured the caller frame. */
/* Tail call: replace current frame with the prepared callee frame. */
JSValue saved_caller = frame->caller;
int cc = (callee_argc < callee_fn->length) ? callee_argc : callee_fn->length;
if (cc < 0) cc = callee_argc;
/* Pop old frame */
cell_rt_leave_frame(ctx);
/* Push new right-sized frame */
JSValue *new_fp = cell_rt_enter_frame(ctx, callee_slots);
if (!new_fp) {
callee_fr = (JSFrameRegister *)JS_VALUE_GET_PTR(callee_ref.val);
callee_fn_val = callee_fn_ref.val;
callee_fr->function = callee_fn_val;
callee_fr->caller = saved_caller;
callee_fr->address = JS_NewInt32(ctx, 0);
if (!cell_rt_push_existing_frame(ctx, callee_ref.val)) {
JS_PopGCRef(ctx, &callee_fn_ref);
JS_PopGCRef(ctx, &callee_ref);
RETURN_DISPATCH(JS_EXCEPTION);
}
callee_fr = (JSFrameRegister *)JS_VALUE_GET_PTR(callee_ref.val);
JSFrameRegister *new_frame = (JSFrameRegister *)((char *)new_fp - offsetof(JSFrameRegister, slots));
callee_fn_val = callee_fn_ref.val;
new_frame->function = callee_fn_val;
new_frame->caller = saved_caller;
new_frame->slots[0] = callee_fr->slots[0];
for (int i = 0; i < cc && i < callee_slots - 1; i++)
new_frame->slots[1 + i] = callee_fr->slots[1 + i];
frame = new_frame;
fp = new_fp;
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(aot_gc_ref_at(g_aot_depth - 1)->val);
fp = (JSValue *)frame->slots;
fn = callee_ptr;
} else {
/* Regular call: push new frame, link caller */
/* Regular call: link caller and push prepared callee frame. */
int ret_info = JS_VALUE_GET_INT(frame->address);
int resume_seg = ret_info >> 16;
int ret_slot = ret_info & 0xFFFF;
int cc = (callee_argc < callee_fn->length) ? callee_argc : callee_fn->length;
if (cc < 0) cc = callee_argc;
/* Save return address in caller */
frame->address = JS_NewInt32(ctx, (resume_seg << 16) | ret_slot);
JSValue *new_fp = cell_rt_enter_frame(ctx, callee_slots);
if (!new_fp) {
callee_fr = (JSFrameRegister *)JS_VALUE_GET_PTR(callee_ref.val);
callee_fn_val = callee_fn_ref.val;
callee_fr->function = callee_fn_val;
callee_fr->caller = JS_MKPTR(frame);
callee_fr->address = JS_NewInt32(ctx, 0);
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 = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
@@ -676,29 +886,8 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
JS_PopGCRef(ctx, &callee_ref);
continue;
}
callee_fr = (JSFrameRegister *)JS_VALUE_GET_PTR(callee_ref.val);
/* Re-derive caller frame after alloc */
if (g_aot_depth <= 1) {
fprintf(stderr, "[BUG] native dispatch bad depth while linking caller: %d\n", g_aot_depth);
abort();
}
frame_val = aot_gc_ref_at(g_aot_depth - 2)->val;
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
JSFrameRegister *new_frame = (JSFrameRegister *)((char *)new_fp - offsetof(JSFrameRegister, slots));
callee_fn_val = callee_fn_ref.val;
new_frame->function = callee_fn_val;
new_frame->caller = JS_MKPTR(frame);
new_frame->slots[0] = callee_fr->slots[0];
for (int i = 0; i < cc && i < callee_slots - 1; i++)
new_frame->slots[1 + i] = callee_fr->slots[1 + i];
/* Save return address in caller */
frame->address = JS_NewInt32(ctx, (resume_seg << 16) | ret_slot);
frame = new_frame;
fp = new_fp;
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(aot_gc_ref_at(g_aot_depth - 1)->val);
fp = (JSValue *)frame->slots;
fn = callee_ptr;
}
} else {
@@ -719,11 +908,16 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
if (JS_IsException(ret)) {
/* Non-native callee threw — resume caller with exception pending.
The caller's generated code checks JS_HasException at resume. */
Tag the pending return slot with JS_EXCEPTION so generated code
can branch without an extra JS_HasException C call. */
if (!JS_HasException(ctx))
JS_Disrupt(ctx);
int ret_info = JS_VALUE_GET_INT(frame->address);
int ret_slot = ret_info & 0xFFFF;
if (ret_slot != 0xFFFF)
fp[ret_slot] = JS_EXCEPTION;
/* fn and fp still point to the calling native function's frame.
Just resume it — it will detect the exception. */
Just resume it — it will detect JS_EXCEPTION in the return slot. */
JSFunction *exc_fn = JS_VALUE_GET_FUNCTION(frame->function);
fn = (cell_compiled_fn)exc_fn->u.native.fn_ptr;
JS_PopGCRef(ctx, &callee_ref);
@@ -789,10 +983,14 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
RETURN_DISPATCH(JS_EXCEPTION);
}
/* Resume caller — it will check JS_HasException and branch to handler */
/* Resume caller and tag the return slot with JS_EXCEPTION. */
frame_val = aot_gc_ref_at(g_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);
int ret_slot = ret_info & 0xFFFF;
if (ret_slot != 0xFFFF)
fp[ret_slot] = JS_EXCEPTION;
JSFunction *exc_caller_fn = JS_VALUE_GET_FUNCTION(frame->function);
fn = (cell_compiled_fn)exc_caller_fn->u.native.fn_ptr;
@@ -859,9 +1057,13 @@ JSValue cell_rt_frame(JSContext *ctx, JSValue fn, int64_t nargs) {
return JS_EXCEPTION;
}
int nr_slots = (int)nargs + 2;
JSFunction *f = JS_VALUE_GET_FUNCTION(fn);
if (f->kind == JS_FUNC_KIND_NATIVE && f->u.native.nr_slots > nr_slots)
nr_slots = f->u.native.nr_slots;
JSFrameRegister *new_frame = alloc_frame_register(ctx, nr_slots);
if (!new_frame) return JS_EXCEPTION;
new_frame->function = fn;
new_frame->address = JS_NewInt32(ctx, (int)nargs);
return JS_MKPTR(new_frame);
}
@@ -875,8 +1077,8 @@ void cell_rt_setarg(JSValue frame_val, int64_t idx, JSValue val) {
JSValue cell_rt_invoke(JSContext *ctx, JSValue frame_val) {
if (frame_val == JS_EXCEPTION) return JS_EXCEPTION;
JSFrameRegister *fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
int nr_slots = (int)objhdr_cap56(fr->header);
int c_argc = (nr_slots >= 2) ? nr_slots - 2 : 0;
int c_argc = JS_VALUE_GET_INT(fr->address);
if (c_argc < 0) c_argc = 0;
JSValue fn_val = fr->function;
if (!JS_IsFunction(fn_val)) {
@@ -933,14 +1135,27 @@ JSValue cell_rt_delete(JSContext *ctx, JSValue obj, JSValue key) {
return JS_NewBool(ctx, ret >= 0);
}
JSValue cell_rt_delete_str(JSContext *ctx, JSValue obj, const char *name) {
JSValue key = JS_NewString(ctx, name);
static JSValue cell_rt_delete_key(JSContext *ctx, JSValue obj, JSValue key) {
int ret = JS_DeleteProperty(ctx, obj, key);
if (ret < 0)
return JS_EXCEPTION;
return JS_NewBool(ctx, ret >= 0);
}
JSValue cell_rt_delete_str(JSContext *ctx, JSValue obj, const char *name) {
JSValue key = aot_key_from_cstr(ctx, name);
if (JS_IsException(key))
return JS_EXCEPTION;
return cell_rt_delete_key(ctx, obj, key);
}
JSValue cell_rt_delete_lit(JSContext *ctx, JSValue obj, int64_t lit_idx) {
JSValue key = aot_lit_from_index(ctx, lit_idx);
if (JS_IsException(key))
return JS_EXCEPTION;
return cell_rt_delete_key(ctx, obj, key);
}
/* --- Typeof --- */
JSValue cell_rt_typeof(JSContext *ctx, JSValue val) {