faster
This commit is contained in:
1029
qbe_emit.cm
1029
qbe_emit.cm
File diff suppressed because it is too large
Load Diff
@@ -17,10 +17,6 @@ JSValue qbe_new_float64(JSContext *ctx, double d) {
|
||||
return __JS_NewFloat64(ctx, d);
|
||||
}
|
||||
|
||||
JSValue qbe_new_string(JSContext *ctx, const char *str) {
|
||||
return JS_NewString(ctx, str);
|
||||
}
|
||||
|
||||
/* Comparison op IDs (must match qbe.cm float_cmp_op_id values) */
|
||||
enum {
|
||||
QBE_CMP_EQ = 0,
|
||||
@@ -31,128 +27,89 @@ enum {
|
||||
QBE_CMP_GE = 5
|
||||
};
|
||||
|
||||
/* ============================================================
|
||||
Float binary arithmetic
|
||||
============================================================ */
|
||||
|
||||
static inline JSValue qbe_float_binop(JSContext *ctx, JSValue a, JSValue b,
|
||||
double (*op)(double, double)) {
|
||||
double da, db;
|
||||
JS_ToFloat64(ctx, &da, a);
|
||||
JS_ToFloat64(ctx, &db, b);
|
||||
double r = op(da, db);
|
||||
if (!isfinite(r))
|
||||
return JS_NULL;
|
||||
return JS_NewFloat64(ctx, r);
|
||||
}
|
||||
|
||||
static double op_add(double a, double b) { return a + b; }
|
||||
static double op_sub(double a, double b) { return a - b; }
|
||||
static double op_mul(double a, double b) { return a * b; }
|
||||
|
||||
JSValue qbe_float_add(JSContext *ctx, JSValue a, JSValue b) {
|
||||
return qbe_float_binop(ctx, a, b, op_add);
|
||||
}
|
||||
|
||||
/* Generic add: concat if both text, float add if both numeric, else type error */
|
||||
JSValue cell_rt_add(JSContext *ctx, JSValue a, JSValue b) {
|
||||
if (JS_IsText(a) && JS_IsText(b))
|
||||
return JS_ConcatString(ctx, a, b);
|
||||
if (JS_IsNumber(a) && JS_IsNumber(b))
|
||||
return qbe_float_binop(ctx, a, b, op_add);
|
||||
JS_RaiseDisrupt(ctx, "cannot add incompatible types");
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
JSValue qbe_float_sub(JSContext *ctx, JSValue a, JSValue b) {
|
||||
return qbe_float_binop(ctx, a, b, op_sub);
|
||||
}
|
||||
|
||||
JSValue qbe_float_mul(JSContext *ctx, JSValue a, JSValue b) {
|
||||
return qbe_float_binop(ctx, a, b, op_mul);
|
||||
}
|
||||
|
||||
JSValue qbe_float_div(JSContext *ctx, JSValue a, JSValue b) {
|
||||
double da, db;
|
||||
JS_ToFloat64(ctx, &da, a);
|
||||
JS_ToFloat64(ctx, &db, b);
|
||||
if (db == 0.0)
|
||||
return JS_NULL;
|
||||
double r = da / db;
|
||||
if (!isfinite(r))
|
||||
return JS_NULL;
|
||||
return JS_NewFloat64(ctx, r);
|
||||
}
|
||||
|
||||
JSValue qbe_float_mod(JSContext *ctx, JSValue a, JSValue b) {
|
||||
double da, db;
|
||||
JS_ToFloat64(ctx, &da, a);
|
||||
JS_ToFloat64(ctx, &db, b);
|
||||
if (db == 0.0)
|
||||
return JS_NULL;
|
||||
double r = fmod(da, db);
|
||||
if (!isfinite(r))
|
||||
return JS_NULL;
|
||||
return JS_NewFloat64(ctx, r);
|
||||
}
|
||||
|
||||
JSValue qbe_float_pow(JSContext *ctx, JSValue a, JSValue b) {
|
||||
double da, db;
|
||||
JS_ToFloat64(ctx, &da, a);
|
||||
JS_ToFloat64(ctx, &db, b);
|
||||
double r = pow(da, db);
|
||||
if (!isfinite(r) && isfinite(da) && isfinite(db))
|
||||
return JS_NULL;
|
||||
return JS_NewFloat64(ctx, r);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
Float unary ops
|
||||
============================================================ */
|
||||
|
||||
JSValue qbe_float_neg(JSContext *ctx, JSValue v) {
|
||||
double d;
|
||||
JS_ToFloat64(ctx, &d, v);
|
||||
return JS_NewFloat64(ctx, -d);
|
||||
}
|
||||
|
||||
JSValue qbe_float_inc(JSContext *ctx, JSValue v) {
|
||||
double d;
|
||||
JS_ToFloat64(ctx, &d, v);
|
||||
return JS_NewFloat64(ctx, d + 1);
|
||||
}
|
||||
|
||||
JSValue qbe_float_dec(JSContext *ctx, JSValue v) {
|
||||
double d;
|
||||
JS_ToFloat64(ctx, &d, v);
|
||||
return JS_NewFloat64(ctx, d - 1);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
Float comparison — returns 0 or 1 for QBE branching
|
||||
============================================================ */
|
||||
|
||||
int qbe_float_cmp(JSContext *ctx, int op, JSValue a, JSValue b) {
|
||||
double da, db;
|
||||
JS_ToFloat64(ctx, &da, a);
|
||||
JS_ToFloat64(ctx, &db, b);
|
||||
switch (op) {
|
||||
case QBE_CMP_EQ: return da == db;
|
||||
case QBE_CMP_NE: return da != db;
|
||||
case QBE_CMP_LT: return da < db;
|
||||
case QBE_CMP_LE: return da <= db;
|
||||
case QBE_CMP_GT: return da > db;
|
||||
case QBE_CMP_GE: return da >= db;
|
||||
default: return 0;
|
||||
/* Generic comparison helper matching MACH eq/ne/lt/le/gt/ge semantics. */
|
||||
JSValue cell_rt_cmp(JSContext *ctx, int op, JSValue a, JSValue b) {
|
||||
if (JS_VALUE_IS_BOTH_INT(a, b)) {
|
||||
int32_t ia = JS_VALUE_GET_INT(a);
|
||||
int32_t ib = JS_VALUE_GET_INT(b);
|
||||
switch (op) {
|
||||
case QBE_CMP_EQ: return JS_NewBool(ctx, ia == ib);
|
||||
case QBE_CMP_NE: return JS_NewBool(ctx, ia != ib);
|
||||
case QBE_CMP_LT: return JS_NewBool(ctx, ia < ib);
|
||||
case QBE_CMP_LE: return JS_NewBool(ctx, ia <= ib);
|
||||
case QBE_CMP_GT: return JS_NewBool(ctx, ia > ib);
|
||||
case QBE_CMP_GE: return JS_NewBool(ctx, ia >= ib);
|
||||
default: return JS_NewBool(ctx, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
Boolean conversion wrapper
|
||||
============================================================ */
|
||||
/* Fast path: identity after chasing forward pointers (matches MACH). */
|
||||
{
|
||||
JSValue ca = JS_IsPtr(a) ? JS_MKPTR(chase(a)) : a;
|
||||
JSValue cb = JS_IsPtr(b) ? JS_MKPTR(chase(b)) : b;
|
||||
if (ca == cb) {
|
||||
if (op == QBE_CMP_EQ || op == QBE_CMP_LE || op == QBE_CMP_GE)
|
||||
return JS_TRUE;
|
||||
if (op == QBE_CMP_NE)
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
int qbe_to_bool(JSContext *ctx, JSValue v) {
|
||||
return JS_ToBool(ctx, v);
|
||||
if (JS_IsNumber(a) && JS_IsNumber(b)) {
|
||||
double da, db;
|
||||
JS_ToFloat64(ctx, &da, a);
|
||||
JS_ToFloat64(ctx, &db, b);
|
||||
switch (op) {
|
||||
case QBE_CMP_EQ: return JS_NewBool(ctx, da == db);
|
||||
case QBE_CMP_NE: return JS_NewBool(ctx, da != db);
|
||||
case QBE_CMP_LT: return JS_NewBool(ctx, da < db);
|
||||
case QBE_CMP_LE: return JS_NewBool(ctx, da <= db);
|
||||
case QBE_CMP_GT: return JS_NewBool(ctx, da > db);
|
||||
case QBE_CMP_GE: return JS_NewBool(ctx, da >= db);
|
||||
default: return JS_NewBool(ctx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (mist_is_text(a) && mist_is_text(b)) {
|
||||
int cmp = js_string_compare_value(ctx, a, b, FALSE);
|
||||
switch (op) {
|
||||
case QBE_CMP_EQ: return JS_NewBool(ctx, cmp == 0);
|
||||
case QBE_CMP_NE: return JS_NewBool(ctx, cmp != 0);
|
||||
case QBE_CMP_LT: return JS_NewBool(ctx, cmp < 0);
|
||||
case QBE_CMP_LE: return JS_NewBool(ctx, cmp <= 0);
|
||||
case QBE_CMP_GT: return JS_NewBool(ctx, cmp > 0);
|
||||
case QBE_CMP_GE: return JS_NewBool(ctx, cmp >= 0);
|
||||
default: return JS_NewBool(ctx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (JS_IsNull(a) && JS_IsNull(b)) {
|
||||
if (op == QBE_CMP_EQ || op == QBE_CMP_LE || op == QBE_CMP_GE)
|
||||
return JS_TRUE;
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (JS_IsBool(a) && JS_IsBool(b)) {
|
||||
int ba = JS_VALUE_GET_BOOL(a);
|
||||
int bb = JS_VALUE_GET_BOOL(b);
|
||||
switch (op) {
|
||||
case QBE_CMP_EQ: return JS_NewBool(ctx, ba == bb);
|
||||
case QBE_CMP_NE: return JS_NewBool(ctx, ba != bb);
|
||||
case QBE_CMP_LT: return JS_NewBool(ctx, ba < bb);
|
||||
case QBE_CMP_LE: return JS_NewBool(ctx, ba <= bb);
|
||||
case QBE_CMP_GT: return JS_NewBool(ctx, ba > bb);
|
||||
case QBE_CMP_GE: return JS_NewBool(ctx, ba >= bb);
|
||||
default: return JS_NewBool(ctx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (op == QBE_CMP_EQ)
|
||||
return JS_NewBool(ctx, 0);
|
||||
if (op == QBE_CMP_NE)
|
||||
return JS_NewBool(ctx, 1);
|
||||
|
||||
JS_RaiseDisrupt(ctx, "cannot compare: operands must be same type");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
@@ -190,29 +147,37 @@ JSValue qbe_bitwise_xor(JSContext *ctx, JSValue a, JSValue b) {
|
||||
return JS_NewInt32(ctx, ia ^ ib);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
Shift ops on floats (convert to int32, shift, re-tag)
|
||||
============================================================ */
|
||||
/* 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;
|
||||
int rlen = js_string_value_len(right);
|
||||
int cap = (int)objhdr_cap56(s->hdr);
|
||||
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;
|
||||
}
|
||||
|
||||
JSValue qbe_shift_shl(JSContext *ctx, JSValue a, JSValue b) {
|
||||
int32_t ia, ib;
|
||||
JS_ToInt32(ctx, &ia, a);
|
||||
JS_ToInt32(ctx, &ib, b);
|
||||
return JS_NewInt32(ctx, ia << (ib & 31));
|
||||
}
|
||||
|
||||
JSValue qbe_shift_sar(JSContext *ctx, JSValue a, JSValue b) {
|
||||
int32_t ia, ib;
|
||||
JS_ToInt32(ctx, &ia, a);
|
||||
JS_ToInt32(ctx, &ib, b);
|
||||
return JS_NewInt32(ctx, ia >> (ib & 31));
|
||||
}
|
||||
|
||||
JSValue qbe_shift_shr(JSContext *ctx, JSValue a, JSValue b) {
|
||||
int32_t ia, ib;
|
||||
JS_ToInt32(ctx, &ia, a);
|
||||
JS_ToInt32(ctx, &ib, b);
|
||||
return JS_NewInt32(ctx, (uint32_t)ia >> (ib & 31));
|
||||
/* Different target: exact-fit stoned path. */
|
||||
JSValue res = JS_ConcatString(ctx, left, right);
|
||||
if (JS_IsException(res))
|
||||
return JS_EXCEPTION;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
@@ -381,13 +346,6 @@ static JSValue cell_rt_load_field_key(JSContext *ctx, JSValue obj, JSValue key)
|
||||
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))
|
||||
@@ -395,29 +353,12 @@ JSValue cell_rt_load_field_lit(JSContext *ctx, JSValue obj, int64_t lit_idx) {
|
||||
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) {
|
||||
JSValue key = aot_key_from_cstr(ctx, name);
|
||||
if (JS_IsException(key))
|
||||
return JS_EXCEPTION;
|
||||
return JS_GetProperty(ctx, obj, key);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -455,25 +396,6 @@ int cell_rt_store_dynamic(JSContext *ctx, JSValue val, JSValue obj,
|
||||
}
|
||||
}
|
||||
|
||||
JSValue cell_rt_load_index(JSContext *ctx, JSValue arr, JSValue idx) {
|
||||
if (JS_IsInt(idx))
|
||||
return JS_GetPropertyNumber(ctx, arr, (uint32_t)JS_VALUE_GET_INT(idx));
|
||||
return JS_GetProperty(ctx, arr, 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))
|
||||
nr = JS_SetPropertyNumber(ctx, arr, (uint32_t)JS_VALUE_GET_INT(idx), val);
|
||||
else
|
||||
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 --- */
|
||||
|
||||
void cell_rt_set_native_env(JSContext *ctx, JSValue env) {
|
||||
@@ -895,6 +817,12 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
||||
|
||||
if (!JS_IsFunction(callee_fn_val)) {
|
||||
JS_RaiseDisrupt(ctx, "not a function");
|
||||
{
|
||||
int ret_info = JS_VALUE_GET_INT(frame->address);
|
||||
int ret_slot = ret_info & 0xFFFF;
|
||||
if (ret_slot != 0xFFFF)
|
||||
fp[ret_slot] = JS_EXCEPTION;
|
||||
}
|
||||
/* Resume caller with exception pending */
|
||||
JSFunction *exc_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(exc_fn))->u.native.fn_ptr;
|
||||
@@ -905,6 +833,10 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
||||
|
||||
JSFunction *callee_fn = JS_VALUE_GET_FUNCTION(callee_fn_val);
|
||||
if (!cell_check_call_arity(ctx, callee_fn, callee_argc)) {
|
||||
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_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(exc_fn))->u.native.fn_ptr;
|
||||
cell_sync_dl_from_native_fn(st, exc_fn);
|
||||
@@ -1191,56 +1123,10 @@ JSValue cell_rt_frame(JSContext *ctx, JSValue fn, int64_t nargs) {
|
||||
return JS_MKPTR(new_frame);
|
||||
}
|
||||
|
||||
void cell_rt_setarg(JSValue frame_val, int64_t idx, JSValue val) {
|
||||
if (frame_val == JS_EXCEPTION || frame_val == JS_NULL) return;
|
||||
JSFrameRegister *fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
|
||||
fr->slots[idx] = val;
|
||||
}
|
||||
|
||||
/* cell_rt_invoke — still used for non-dispatch-loop paths (e.g. old code) */
|
||||
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 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)) {
|
||||
JS_RaiseDisrupt(ctx, "not a function");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
JSFunction *fn = JS_VALUE_GET_FUNCTION(fn_val);
|
||||
JSValue result;
|
||||
if (!cell_check_call_arity(ctx, fn, c_argc))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
if (fn->kind == JS_FUNC_KIND_C) {
|
||||
result = js_call_c_function(ctx, fn_val, fr->slots[0], c_argc, &fr->slots[1]);
|
||||
} else if (fn->kind == JS_FUNC_KIND_NATIVE) {
|
||||
result = cell_native_dispatch(ctx, fn_val, fr->slots[0], c_argc, &fr->slots[1]);
|
||||
} else {
|
||||
JSValue args[c_argc > 0 ? c_argc : 1];
|
||||
for (int i = 0; i < c_argc; i++)
|
||||
args[i] = fr->slots[i + 1];
|
||||
result = JS_CallInternal(ctx, fn_val, fr->slots[0], c_argc, args, 0);
|
||||
}
|
||||
|
||||
if (JS_IsException(result))
|
||||
return JS_EXCEPTION;
|
||||
if (JS_HasException(ctx))
|
||||
JS_GetException(ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
JSValue cell_rt_goframe(JSContext *ctx, JSValue fn, int64_t nargs) {
|
||||
return cell_rt_frame(ctx, fn, nargs);
|
||||
}
|
||||
|
||||
JSValue cell_rt_goinvoke(JSContext *ctx, JSValue frame_val) {
|
||||
return cell_rt_invoke(ctx, frame_val);
|
||||
}
|
||||
|
||||
/* --- Array push/pop --- */
|
||||
|
||||
JSValue cell_rt_push(JSContext *ctx, JSValue arr, JSValue val) {
|
||||
@@ -1268,13 +1154,6 @@ static JSValue cell_rt_delete_key(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 = 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))
|
||||
@@ -1282,49 +1161,6 @@ JSValue cell_rt_delete_lit(JSContext *ctx, JSValue obj, int64_t lit_idx) {
|
||||
return cell_rt_delete_key(ctx, obj, key);
|
||||
}
|
||||
|
||||
/* --- Typeof --- */
|
||||
|
||||
JSValue cell_rt_typeof(JSContext *ctx, JSValue val) {
|
||||
if (JS_IsNull(val)) return JS_NewString(ctx, "null");
|
||||
if (JS_IsInt(val) || JS_IsNumber(val)) return JS_NewString(ctx, "number");
|
||||
if (JS_IsBool(val)) return JS_NewString(ctx, "logical");
|
||||
if (JS_IsText(val)) return JS_NewString(ctx, "text");
|
||||
if (JS_IsFunction(val)) return JS_NewString(ctx, "function");
|
||||
if (JS_IsArray(val)) return JS_NewString(ctx, "array");
|
||||
if (JS_IsRecord(val)) return JS_NewString(ctx, "object");
|
||||
return JS_NewString(ctx, "unknown");
|
||||
}
|
||||
|
||||
/* --- Text comparison stubs (called from QBE type-dispatch branches) --- */
|
||||
|
||||
JSValue cell_rt_lt_text(JSContext *ctx, JSValue a, JSValue b) {
|
||||
const char *sa = JS_ToCString(ctx, a);
|
||||
const char *sb = JS_ToCString(ctx, b);
|
||||
int r = (sa && sb) ? strcmp(sa, sb) < 0 : 0;
|
||||
return JS_NewBool(ctx, r);
|
||||
}
|
||||
|
||||
JSValue cell_rt_gt_text(JSContext *ctx, JSValue a, JSValue b) {
|
||||
const char *sa = JS_ToCString(ctx, a);
|
||||
const char *sb = JS_ToCString(ctx, b);
|
||||
int r = (sa && sb) ? strcmp(sa, sb) > 0 : 0;
|
||||
return JS_NewBool(ctx, r);
|
||||
}
|
||||
|
||||
JSValue cell_rt_le_text(JSContext *ctx, JSValue a, JSValue b) {
|
||||
const char *sa = JS_ToCString(ctx, a);
|
||||
const char *sb = JS_ToCString(ctx, b);
|
||||
int r = (sa && sb) ? strcmp(sa, sb) <= 0 : 0;
|
||||
return JS_NewBool(ctx, r);
|
||||
}
|
||||
|
||||
JSValue cell_rt_ge_text(JSContext *ctx, JSValue a, JSValue b) {
|
||||
const char *sa = JS_ToCString(ctx, a);
|
||||
const char *sb = JS_ToCString(ctx, b);
|
||||
int r = (sa && sb) ? strcmp(sa, sb) >= 0 : 0;
|
||||
return JS_NewBool(ctx, r);
|
||||
}
|
||||
|
||||
static int cell_rt_tol_eq_inner(JSContext *ctx, JSValue a, JSValue b,
|
||||
JSValue tol) {
|
||||
if (JS_IsNumber(a) && JS_IsNumber(b) && JS_IsNumber(tol)) {
|
||||
@@ -1358,6 +1194,108 @@ 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))
|
||||
result = JS_HasPropertyKey(ctx, v, ctx->actor_sym) > 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --- Type check: is_proxy (function with arity 2) --- */
|
||||
|
||||
int cell_rt_is_proxy(JSContext *ctx, JSValue v) {
|
||||
@@ -1367,14 +1305,6 @@ int cell_rt_is_proxy(JSContext *ctx, JSValue v) {
|
||||
return fn->length == 2;
|
||||
}
|
||||
|
||||
/* --- Identity check (chases forwarding pointers) --- */
|
||||
|
||||
JSValue cell_rt_is_identical(JSContext *ctx, JSValue a, JSValue b) {
|
||||
if (JS_IsPtr(a)) a = JS_MKPTR(chase(a));
|
||||
if (JS_IsPtr(b)) b = JS_MKPTR(chase(b));
|
||||
return JS_NewBool(ctx, a == b);
|
||||
}
|
||||
|
||||
/* --- Short-circuit and/or (non-allocating) --- */
|
||||
|
||||
JSValue cell_rt_and(JSContext *ctx, JSValue left, JSValue right) {
|
||||
|
||||
Reference in New Issue
Block a user