asserts only for frame gets

This commit is contained in:
2026-02-21 19:06:41 -06:00
parent bbeb757e40
commit 99fa86a09c
3 changed files with 490 additions and 284 deletions

View File

@@ -150,7 +150,6 @@ JSValue qbe_bitwise_xor(JSContext *ctx, JSValue a, JSValue b) {
/* Concat helper matching MACH_CONCAT semantics exactly. */
JSValue cell_rt_concat(JSContext *ctx, JSValue left, JSValue right, int self_assign) {
if (self_assign) {
/* Self-assign pattern: slot[a] = slot[a] + slot[c]. */
if (JS_IsPtr(left)) {
JSText *s = (JSText *)chase(left);
int slen = (int)s->length;
@@ -159,21 +158,18 @@ JSValue cell_rt_concat(JSContext *ctx, JSValue left, JSValue right, int self_ass
if (objhdr_type(s->hdr) == OBJ_TEXT
&& !(s->hdr & OBJHDR_S_MASK)
&& slen + rlen <= cap) {
/* In-place append, no allocation. */
for (int i = 0; i < rlen; i++)
string_put(s, slen + i, js_string_value_get(right, i));
s->length = slen + rlen;
return left;
}
}
/* Allocate with growth factor, leave unstoned. */
JSValue res = JS_ConcatStringGrow(ctx, left, right);
if (JS_IsException(res))
return JS_EXCEPTION;
return res;
}
/* Different target: exact-fit stoned path. */
JSValue res = JS_ConcatString(ctx, left, right);
if (JS_IsException(res))
return JS_EXCEPTION;
@@ -468,52 +464,6 @@ JSValue cell_rt_get_intrinsic_lit(JSContext *ctx, int64_t lit_idx) {
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
u.native.outer_frame points to the enclosing frame.
GC traces outer_frame naturally — no registry needed. */
/* Get the outer frame's slots from a frame pointer.
The frame's function must be JS_FUNC_KIND_NATIVE. */
static JSValue *get_outer_frame_slots(JSValue *fp) {
/* fp points to frame->slots[0]; frame header is before it */
JSFrameRegister *frame = (JSFrameRegister *)((char *)fp - offsetof(JSFrameRegister, slots));
if (JS_IsNull(frame->function))
return NULL;
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
if (fn->kind != JS_FUNC_KIND_NATIVE)
return NULL;
JSValue outer = fn->u.cell.outer_frame;
if (JS_IsNull(outer))
return NULL;
JSFrameRegister *outer_frame = (JSFrameRegister *)JS_VALUE_GET_PTR(outer);
return (JSValue *)outer_frame->slots;
}
JSValue cell_rt_get_closure(JSContext *ctx, void *fp, int64_t depth,
int64_t slot) {
(void)ctx;
JSValue *frame = (JSValue *)fp;
for (int64_t d = 0; d < depth; d++) {
frame = get_outer_frame_slots(frame);
if (!frame)
return JS_NULL;
}
return frame[slot];
}
void cell_rt_put_closure(JSContext *ctx, void *fp, JSValue val, int64_t depth,
int64_t slot) {
(void)ctx;
JSValue *frame = (JSValue *)fp;
for (int64_t d = 0; d < depth; d++) {
frame = get_outer_frame_slots(frame);
if (!frame) return;
}
frame[slot] = val;
}
/* --- GC-managed AOT frame stack ---
Each native dispatch loop pushes a GC ref so the GC can find and
update the current frame pointer when it moves objects.
@@ -662,18 +612,6 @@ typedef JSValue (*cell_compiled_fn)(JSContext *ctx, void *fp);
to call another function (instead of recursing via C stack).
============================================================ */
/* 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
pauses by clearing pause_flag and continuing the current turn. */
int cell_rt_check_backedge(JSContext *ctx) {
int pf = atomic_load_explicit(&ctx->pause_flag, memory_order_relaxed);
if (pf >= 1) {
atomic_store_explicit(&ctx->pause_flag, 0, memory_order_relaxed);
}
return 0;
}
void cell_rt_signal_call(JSContext *ctx, void *fp, int64_t frame_slot) {
NativeRTState *st = native_state(ctx);
if (!st) return;
@@ -1194,101 +1132,6 @@ JSValue cell_rt_ne_tol(JSContext *ctx, JSValue a, JSValue b, JSValue tol) {
return JS_NewBool(ctx, !cell_rt_tol_eq_inner(ctx, a, b, tol));
}
/* --- Extended type checks and text stoning --- */
void cell_rt_stone_text(JSValue v) {
stone_mutable_text(v);
}
int cell_rt_is_blob(JSValue v) {
return mist_is_blob(v);
}
int cell_rt_is_data(JSValue v) {
return mist_is_gc_object(v) && !mist_is_array(v)
&& !mist_is_function(v) && !mist_is_blob(v);
}
int cell_rt_is_fit(JSValue v) {
if (JS_IsInt(v))
return 1;
if (JS_IsShortFloat(v)) {
double d = JS_VALUE_GET_FLOAT64(v);
return isfinite(d) && trunc(d) == d && fabs(d) <= 9007199254740992.0;
}
return 0;
}
int cell_rt_is_char(JSValue v) {
if (MIST_IsImmediateASCII(v))
return MIST_GetImmediateASCIILen(v) == 1;
if (mist_is_text(v))
return js_string_value_len(v) == 1;
return 0;
}
int cell_rt_is_digit(JSValue v) {
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
int ch = MIST_GetImmediateASCIIChar(v, 0);
return ch >= '0' && ch <= '9';
}
if (mist_is_text(v) && js_string_value_len(v) == 1) {
uint32_t ch = js_string_value_get(v, 0);
return ch >= '0' && ch <= '9';
}
return 0;
}
int cell_rt_is_letter(JSValue v) {
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
int ch = MIST_GetImmediateASCIIChar(v, 0);
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
}
if (mist_is_text(v) && js_string_value_len(v) == 1) {
uint32_t ch = js_string_value_get(v, 0);
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
}
return 0;
}
int cell_rt_is_lower(JSValue v) {
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
int ch = MIST_GetImmediateASCIIChar(v, 0);
return ch >= 'a' && ch <= 'z';
}
if (mist_is_text(v) && js_string_value_len(v) == 1) {
uint32_t ch = js_string_value_get(v, 0);
return ch >= 'a' && ch <= 'z';
}
return 0;
}
int cell_rt_is_upper(JSValue v) {
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
int ch = MIST_GetImmediateASCIIChar(v, 0);
return ch >= 'A' && ch <= 'Z';
}
if (mist_is_text(v) && js_string_value_len(v) == 1) {
uint32_t ch = js_string_value_get(v, 0);
return ch >= 'A' && ch <= 'Z';
}
return 0;
}
int cell_rt_is_ws(JSValue v) {
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
int ch = MIST_GetImmediateASCIIChar(v, 0);
return ch == ' ' || ch == '\t' || ch == '\n'
|| ch == '\r' || ch == '\f' || ch == '\v';
}
if (mist_is_text(v) && js_string_value_len(v) == 1) {
uint32_t ch = js_string_value_get(v, 0);
return ch == ' ' || ch == '\t' || ch == '\n'
|| ch == '\r' || ch == '\f' || ch == '\v';
}
return 0;
}
int cell_rt_is_actor(JSContext *ctx, JSValue v) {
int result = 0;
if (mist_is_record(v) && !JS_IsNull(ctx->actor_sym))
@@ -1296,25 +1139,6 @@ int cell_rt_is_actor(JSContext *ctx, JSValue v) {
return result;
}
/* --- Type check: is_proxy (function with arity 2) --- */
int cell_rt_is_proxy(JSContext *ctx, JSValue v) {
(void)ctx;
if (!JS_IsFunction(v)) return 0;
JSFunction *fn = JS_VALUE_GET_FUNCTION(v);
return fn->length == 2;
}
/* --- Short-circuit and/or (non-allocating) --- */
JSValue cell_rt_and(JSContext *ctx, JSValue left, JSValue right) {
return JS_ToBool(ctx, left) ? right : left;
}
JSValue cell_rt_or(JSContext *ctx, JSValue left, JSValue right) {
return JS_ToBool(ctx, left) ? left : right;
}
/* --- Exception checking ---
After potentially-throwing runtime calls, QBE-generated code needs to
check for pending exceptions and branch to the disruption handler. */