simplify eq

This commit is contained in:
2026-02-02 06:56:46 -06:00
parent ba1b92aa78
commit 2d834c37b3
2 changed files with 80 additions and 152 deletions

View File

@@ -498,7 +498,7 @@ static inline objhdr_t objhdr_make (uint64_t cap56, uint8_t type, bool r, bool a
return h;
}
/* Intrinsic array type - tagged as JS_TAG_OBJECT with mist_hdr type OBJ_ARRAY */
/* Intrinsic array type - tagged as JS_TAG_PTR with mist_hdr type OBJ_ARRAY */
typedef struct JSArray {
objhdr_t mist_hdr; /* mist header at offset 8 */
word_t len; /* current length */
@@ -1703,14 +1703,7 @@ static JSValue js_regexp_constructor_internal (JSContext *ctx, JSValue pattern,
static void gc_decref (JSRuntime *rt);
static int JS_NewClass1 (JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def, const char *name);
typedef enum JSStrictEqModeEnum {
JS_EQ_STRICT,
JS_EQ_SAME_VALUE,
} JSStrictEqModeEnum;
static BOOL js_strict_eq2 (JSContext *ctx, JSValue op1, JSValue op2, JSStrictEqModeEnum eq_mode);
static BOOL js_strict_eq (JSContext *ctx, JSValue op1, JSValue op2);
static BOOL js_same_value (JSContext *ctx, JSValue op1, JSValue op2);
static JSValue JS_ToObject (JSContext *ctx, JSValue val);
static JSValue JS_ToObjectFree (JSContext *ctx, JSValue val);
static JSValue js_cell_text (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
@@ -4657,7 +4650,7 @@ static JSValue JS_GetPropertyInt64 (JSContext *ctx, JSValue obj, int64_t idx) {
}
JSValue JS_GetPropertyStr (JSContext *ctx, JSValue this_obj, const char *prop) {
if (JS_VALUE_GET_TAG (this_obj) != JS_TAG_OBJECT) return JS_NULL;
if (JS_VALUE_GET_TAG (this_obj) != JS_TAG_PTR) return JS_NULL;
size_t len = strlen (prop);
JSValue key;
@@ -4886,7 +4879,7 @@ int JS_DeletePropertyKey (JSContext *ctx, JSValue obj, JSValue key) {
/* return TRUE if 'obj' has a non empty 'name' string */
static BOOL js_object_has_name (JSContext *ctx, JSValue obj) {
if (JS_VALUE_GET_TAG (obj) != JS_TAG_OBJECT) return FALSE;
if (JS_VALUE_GET_TAG (obj) != JS_TAG_PTR) return FALSE;
JSRecord *rec = (JSRecord *)JS_VALUE_GET_OBJ (obj);
JSValue name_key = MIST_TryNewImmediateASCII ("name", 4);
int slot = rec_find_slot (rec, name_key);
@@ -5098,7 +5091,7 @@ int JS_DeleteProperty (JSContext *ctx, JSValue obj, JSValue prop) {
return -1;
}
if (JS_VALUE_GET_TAG (obj) != JS_TAG_OBJECT) {
if (JS_VALUE_GET_TAG (obj) != JS_TAG_PTR) {
JS_ThrowTypeErrorNotAnObject (ctx);
return -1;
}
@@ -5135,7 +5128,7 @@ BOOL JS_IsCFunction (JSContext *ctx, JSValue val, JSCFunction *func, int magic)
BOOL JS_IsError (JSContext *ctx, JSValue val) {
JSRecord *p;
if (JS_VALUE_GET_TAG (val) != JS_TAG_OBJECT) return FALSE;
if (JS_VALUE_GET_TAG (val) != JS_TAG_PTR) return FALSE;
p = JS_VALUE_GET_OBJ (val);
return (REC_GET_CLASS_ID(p) == JS_CLASS_ERROR);
}
@@ -5148,7 +5141,7 @@ void JS_SetUncatchableException (JSContext *ctx, JS_BOOL flag) {
void JS_SetOpaque (JSValue obj, void *opaque) {
JSRecord *p;
if (JS_VALUE_GET_TAG (obj) == JS_TAG_OBJECT) {
if (JS_VALUE_GET_TAG (obj) == JS_TAG_PTR) {
p = JS_VALUE_GET_OBJ (obj);
REC_SET_OPAQUE(p, opaque);
}
@@ -5157,7 +5150,7 @@ void JS_SetOpaque (JSValue obj, void *opaque) {
/* return NULL if not an object of class class_id */
void *JS_GetOpaque (JSValue obj, JSClassID class_id) {
JSRecord *p;
if (JS_VALUE_GET_TAG (obj) != JS_TAG_OBJECT) return NULL;
if (JS_VALUE_GET_TAG (obj) != JS_TAG_PTR) return NULL;
p = JS_VALUE_GET_OBJ (obj);
if (REC_GET_CLASS_ID(p) != class_id) return NULL;
return REC_GET_OPAQUE(p);
@@ -6584,10 +6577,6 @@ exception:
return -1;
}
static inline BOOL tag_is_string (uint32_t tag) {
return tag == JS_TAG_STRING || tag == JS_TAG_STRING_IMM;
}
static no_inline int js_relational_slow (JSContext *ctx, JSValue *sp, OPCodeEnum op) {
JSValue op1 = sp[-2], op2 = sp[-1];
uint32_t tag1 = JS_VALUE_GET_NORM_TAG (op1);
@@ -6595,11 +6584,8 @@ static no_inline int js_relational_slow (JSContext *ctx, JSValue *sp, OPCodeEnum
int res;
/* string <=> string */
if (tag_is_string (tag1) && tag_is_string (tag2)) {
if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING)
res = js_string_compare (ctx, JS_VALUE_GET_STRING (op1), JS_VALUE_GET_STRING (op2));
else
res = js_string_compare_value (ctx, op1, op2, FALSE);
if (JS_IsText (op1) && JS_IsText (op2)) {
res = js_string_compare_value (ctx, op1, op2, FALSE);
switch (op) {
case OP_lt:
@@ -6657,112 +6643,59 @@ exception:
return -1;
}
static BOOL tag_is_number (uint32_t tag) {
return (tag == JS_TAG_INT || tag == JS_TAG_FLOAT64);
}
/* Simplified equality: no NaN (becomes null), no coercion, no SameValue distinction */
static BOOL js_strict_eq (JSContext *ctx, JSValue op1, JSValue op2) {
/* Fast path: identical values */
if (op1 == op2) return TRUE;
/* XXX: Should take JSValue arguments */
static BOOL js_strict_eq2 (JSContext *ctx, JSValue op1, JSValue op2, JSStrictEqModeEnum eq_mode) {
BOOL res;
int tag1, tag2;
double d1, d2;
int tag1 = JS_VALUE_GET_NORM_TAG (op1);
int tag2 = JS_VALUE_GET_NORM_TAG (op2);
tag1 = JS_VALUE_GET_NORM_TAG (op1);
tag2 = JS_VALUE_GET_NORM_TAG (op2);
/* Different types are never equal (no coercion) */
/* Special case: INT and FLOAT can be equal */
if (tag1 != tag2) {
if (!((tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64) &&
(tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)))
return FALSE;
}
switch (tag1) {
case JS_TAG_BOOL:
if (tag1 != tag2) {
res = FALSE;
} else {
res = JS_VALUE_GET_INT (op1) == JS_VALUE_GET_INT (op2);
goto done_no_free;
}
break;
case JS_TAG_NULL:
res = (tag1 == tag2);
break;
case JS_TAG_STRING_IMM: {
if (!tag_is_string (tag2)) {
res = FALSE;
} else if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) {
res = (js_string_compare (ctx, JS_VALUE_GET_STRING (op1), JS_VALUE_GET_STRING (op2))
== 0);
} else {
res = (js_string_compare_value (ctx, op1, op2, TRUE) == 0);
}
} break;
case JS_TAG_OBJECT: /* includes arrays (OBJ_ARRAY) via mist_hdr - pointer
comparison */
if (tag1 != tag2)
res = FALSE;
else
res = JS_VALUE_GET_PTR (op1) == JS_VALUE_GET_PTR (op2);
break;
case JS_TAG_INT:
d1 = JS_VALUE_GET_INT (op1);
if (tag2 == JS_TAG_INT) {
d2 = JS_VALUE_GET_INT (op2);
goto number_test;
} else if (tag2 == JS_TAG_FLOAT64) {
d2 = JS_VALUE_GET_FLOAT64 (op2);
goto number_test;
} else {
res = FALSE;
}
break;
case JS_TAG_FLOAT64:
d1 = JS_VALUE_GET_FLOAT64 (op1);
if (tag2 == JS_TAG_FLOAT64) {
d2 = JS_VALUE_GET_FLOAT64 (op2);
} else if (tag2 == JS_TAG_INT) {
d2 = JS_VALUE_GET_INT (op2);
} else {
res = FALSE;
break;
}
number_test:
if (unlikely (eq_mode >= JS_EQ_SAME_VALUE)) {
JSFloat64Union u1, u2;
/* NaN is not always normalized, so this test is necessary */
if (isnan (d1) || isnan (d2)) {
res = isnan (d1) == isnan (d2);
} else {
u1.d = d1;
u2.d = d2;
res = (u1.u64 == u2.u64); /* +0 != -0 */
}
} else {
res = (d1 == d2); /* if NaN return false and +0 == -0 */
}
goto done_no_free;
default:
res = FALSE;
break;
case JS_TAG_FLOAT64: {
/* Numbers: unpack and compare */
double d1 = (tag1 == JS_TAG_INT) ? (double)JS_VALUE_GET_INT (op1)
: JS_VALUE_GET_FLOAT64 (op1);
double d2 = (tag2 == JS_TAG_INT) ? (double)JS_VALUE_GET_INT (op2)
: JS_VALUE_GET_FLOAT64 (op2);
return d1 == d2;
}
case JS_TAG_STRING_IMM:
/* Immediate text vs immediate text (handled by op1 == op2 fast path) */
/* or vs heap text */
if (JS_IsText (op2))
return js_string_compare_value (ctx, op1, op2, TRUE) == 0;
return FALSE;
case JS_TAG_PTR:
/* Heap text vs heap text or vs immediate text */
if (JS_IsText (op1) && JS_IsText (op2))
return js_string_compare_value (ctx, op1, op2, TRUE) == 0;
/* Records/objects: pointer equality (op1 == op2 handles same object) */
return FALSE; /* Different objects */
case JS_TAG_BOOL:
case JS_TAG_NULL:
/* Already handled by op1 == op2 fast path */
return FALSE;
default:
return FALSE;
}
done_no_free:
return res;
}
static BOOL js_strict_eq (JSContext *ctx, JSValue op1, JSValue op2) {
return js_strict_eq2 (ctx, op1, op2, JS_EQ_STRICT);
}
BOOL JS_StrictEq (JSContext *ctx, JSValue op1, JSValue op2) {
return js_strict_eq (ctx, op1, op2);
}
static BOOL js_same_value (JSContext *ctx, JSValue op1, JSValue op2) {
return js_strict_eq2 (ctx, op1, op2, JS_EQ_SAME_VALUE);
}
BOOL JS_SameValue (JSContext *ctx, JSValue op1, JSValue op2) {
return js_same_value (ctx, op1, op2);
}
static no_inline int js_strict_eq_slow (JSContext *ctx, JSValue *sp, BOOL is_neq) {
BOOL res;
res = js_strict_eq2 (ctx, sp[-2], sp[-1], JS_EQ_STRICT);
BOOL res = js_strict_eq (ctx, sp[-2], sp[-1]);
sp[-2] = JS_NewBool (ctx, res ^ is_neq);
return 0;
}
@@ -6774,7 +6707,7 @@ static __exception int js_operator_in (JSContext *ctx, JSValue *sp) {
op1 = sp[-2];
op2 = sp[-1];
if (JS_VALUE_GET_TAG (op2) != JS_TAG_OBJECT) {
if (JS_VALUE_GET_TAG (op2) != JS_TAG_PTR) {
JS_ThrowTypeError (ctx, "invalid 'in' operand");
return -1;
}
@@ -6824,7 +6757,7 @@ static __exception int JS_CopyDataProperties (JSContext *ctx, JSValue target, JS
uint32_t i, key_count;
int ret;
if (JS_VALUE_GET_TAG (source) != JS_TAG_OBJECT) return 0;
if (JS_VALUE_GET_TAG (source) != JS_TAG_PTR) return 0;
/* Get all string keys from source */
keys = JS_GetOwnPropertyNames (ctx, source);
@@ -6838,7 +6771,7 @@ static __exception int JS_CopyDataProperties (JSContext *ctx, JSValue target, JS
if (JS_IsException (key)) goto exception;
/* Check if key is excluded */
if (JS_VALUE_GET_TAG (excluded) == JS_TAG_OBJECT) {
if (JS_VALUE_GET_TAG (excluded) == JS_TAG_PTR) {
/* Check if key exists in excluded object */
JSValue test = JS_GetProperty (ctx, excluded, key);
if (!JS_IsNull (test) && !JS_IsException (test)) {
@@ -7589,7 +7522,7 @@ restart:
pc += 4;
call_argv = sp - call_argc;
sf->cur_pc = pc;
if (js_same_value (ctx, call_argv[-1], ctx->eval_obj)) {
if (js_strict_eq (ctx, call_argv[-1], ctx->eval_obj)) {
if (call_argc >= 1)
obj = call_argv[0];
else
@@ -8368,7 +8301,7 @@ restart:
sf->cur_pc = pc;
/* Check if this is a var ref object */
if (JS_VALUE_GET_TAG (ref_obj) == JS_TAG_OBJECT) {
if (JS_VALUE_GET_TAG (ref_obj) == JS_TAG_PTR) {
JSRecord *p = JS_VALUE_GET_OBJ (ref_obj);
if (REC_GET_CLASS_ID(p) == JS_CLASS_VAR_REF_OBJECT) {
JSVarRef *var_ref = (JSVarRef *)REC_GET_OPAQUE(p);
@@ -8424,7 +8357,7 @@ restart:
sf->cur_pc = pc;
/* Check if this is a var ref object */
if (JS_VALUE_GET_TAG (ref_obj) == JS_TAG_OBJECT) {
if (JS_VALUE_GET_TAG (ref_obj) == JS_TAG_PTR) {
JSRecord *p = JS_VALUE_GET_OBJ (ref_obj);
if (REC_GET_CLASS_ID(p) == JS_CLASS_VAR_REF_OBJECT) {
JSVarRef *var_ref = (JSVarRef *)REC_GET_OPAQUE(p);
@@ -9026,7 +8959,7 @@ restart:
}
BREAK;
CASE (OP_to_object) : if (JS_VALUE_GET_TAG (sp[-1]) != JS_TAG_OBJECT) {
CASE (OP_to_object) : if (JS_VALUE_GET_TAG (sp[-1]) != JS_TAG_PTR) {
sf->cur_pc = pc;
ret_val = JS_ToObject (ctx, sp[-1]);
if (JS_IsException (ret_val)) goto exception;
@@ -18636,28 +18569,25 @@ static int JS_WriteObjectRec (BCWriterState *s, JSValue obj) {
bc_put_u64 (s, u.u64);
} break;
case JS_TAG_STRING_IMM: {
/* Handle both heap strings (JS_TAG_PTR with OBJ_TEXT) and immediate strings */
if (tag_is_string (tag)) {
if (tag == JS_TAG_PTR) {
/* Heap string */
JSText *p = JS_VALUE_GET_STRING (obj);
bc_put_u8 (s, BC_TAG_STRING);
JS_WriteString (s, p);
} else {
/* Immediate ASCII string */
int len = MIST_GetImmediateASCIILen (obj);
char buf[8];
for (int i = 0; i < len; i++)
buf[i] = MIST_GetImmediateASCIIChar (obj, i);
JSValue tmp = js_new_string8_len (s->ctx, buf, len);
if (JS_IsException (tmp)) goto fail;
JSText *p = JS_VALUE_GET_STRING (tmp);
bc_put_u8 (s, BC_TAG_STRING);
JS_WriteString (s, p);
}
}
/* Immediate ASCII string */
int len = MIST_GetImmediateASCIILen (obj);
char buf[8];
for (int i = 0; i < len; i++)
buf[i] = MIST_GetImmediateASCIIChar (obj, i);
JSValue tmp = js_new_string8_len (s->ctx, buf, len);
if (JS_IsException (tmp)) goto fail;
JSText *p = JS_VALUE_GET_STRING (tmp);
bc_put_u8 (s, BC_TAG_STRING);
JS_WriteString (s, p);
} break;
case JS_TAG_OBJECT:
case JS_TAG_PTR:
/* Check if this is a heap string */
if (JS_IsText (obj)) {
JSText *p = JS_VALUE_GET_STRING (obj);
bc_put_u8 (s, BC_TAG_STRING);
JS_WriteString (s, p);
break;
}
/* Check if this is an intrinsic array */
if (JS_IsArray (obj)) {
JSArray *arr = JS_VALUE_GET_ARRAY (obj);
@@ -19487,7 +19417,7 @@ static JSValue JS_ToObject (JSContext *ctx, JSValue val) {
int tag = JS_VALUE_GET_NORM_TAG (val);
switch (tag) {
case JS_TAG_OBJECT:
case JS_TAG_PTR:
case JS_TAG_EXCEPTION:
return val;
default:
@@ -19853,7 +19783,7 @@ static JSValue js_regexp_constructor_internal (JSContext *ctx, JSValue pattern,
}
static JSRegExp *js_get_regexp (JSContext *ctx, JSValue obj, BOOL throw_error) {
if (JS_VALUE_GET_TAG (obj) == JS_TAG_OBJECT) {
if (JS_VALUE_GET_TAG (obj) == JS_TAG_PTR) {
JSRecord *p = JS_VALUE_GET_OBJ (obj);
if (REC_GET_CLASS_ID(p) == JS_CLASS_REGEXP) return (JSRegExp *)REC_GET_OPAQUE(p);
}
@@ -20331,7 +20261,7 @@ static int js_is_standard_regexp (JSContext *ctx, JSValue rx) {
val = JS_GetPropertyStr (ctx, rx, "constructor");
if (JS_IsException (val)) return -1;
// rx.constructor === RegExp
res = js_same_value (ctx, val, ctx->regexp_ctor);
res = js_strict_eq (ctx, val, ctx->regexp_ctor);
if (res) {
val = JS_GetProperty (ctx, rx, JS_KEY_exec);
if (JS_IsException (val)) return -1;
@@ -20608,7 +20538,7 @@ static JSValue js_json_check (JSContext *ctx, JSONStringifyContext *jsc, JSValue
}
switch (JS_VALUE_GET_NORM_TAG (val)) {
case JS_TAG_OBJECT: /* includes arrays (OBJ_ARRAY) via mist_hdr */
case JS_TAG_PTR: /* includes arrays (OBJ_ARRAY) via mist_hdr */
if (JS_IsFunction (val)) break;
/* fall through */
case JS_TAG_STRING_IMM:
@@ -20646,7 +20576,7 @@ static int js_json_to_str (JSContext *ctx, JSONStringifyContext *jsc, JSValue ho
}
if (JS_IsObject (
val)) { /* includes arrays (OBJ_ARRAY) since they have JS_TAG_OBJECT */
val)) { /* includes arrays (OBJ_ARRAY) since they have JS_TAG_PTR */
v = js_array_includes (ctx, jsc->stack, 1, (JSValue *)&val);
if (JS_IsException (v)) goto exception;
if (JS_ToBoolFree (ctx, v)) {
@@ -23626,7 +23556,7 @@ static JSValue js_cell_fn_apply (JSContext *ctx, JSValue this_val, int argc, JSV
/* Helper to check if JSValue is a blob */
static blob *js_get_blob (JSContext *ctx, JSValue val) {
if (JS_VALUE_GET_TAG (val) != JS_TAG_OBJECT) return NULL;
if (JS_VALUE_GET_TAG (val) != JS_TAG_PTR) return NULL;
JSRecord *p = JS_VALUE_GET_OBJ (val);
if (REC_GET_CLASS_ID(p) != JS_CLASS_BLOB) return NULL;
return REC_GET_OPAQUE(p);

View File

@@ -153,7 +153,6 @@ enum {
/* Compatibility tag aliases for external code */
#define JS_TAG_STRING JS_TAG_STRING_IMM /* Alias: text/string type */
#define JS_TAG_OBJECT JS_TAG_PTR /* Alias: use JS_IsRecord/JS_IsArray instead */
#define JS_TAG_FLOAT64 JS_TAG_SHORT_FLOAT /* Alias: floats use short float encoding */
#define JS_EMPTY_TEXT ((JSValue)JS_TAG_STRING_IMM)
@@ -643,7 +642,6 @@ JSValue __js_printf_like (2, 3)
JSValue JS_ThrowOutOfMemory (JSContext *ctx);
JS_BOOL JS_StrictEq (JSContext *ctx, JSValue op1, JSValue op2);
JS_BOOL JS_SameValue (JSContext *ctx, JSValue op1, JSValue op2);
int JS_ToBool (JSContext *ctx, JSValue val); /* return -1 for JS_EXCEPTION */
int JS_ToInt32 (JSContext *ctx, int32_t *pres, JSValue val);