diff --git a/source/quickjs.c b/source/quickjs.c index 3ad27fd5..e8f2c265 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -13452,135 +13452,63 @@ static int js_compare_bigint(JSContext *ctx, OPCodeEnum op, static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { - JSValue op1, op2; - int res; - uint32_t tag1, tag2; + JSValue op1 = sp[-2], op2 = sp[-1]; + uint32_t tag1 = JS_VALUE_GET_NORM_TAG(op1); + uint32_t tag2 = JS_VALUE_GET_NORM_TAG(op2); + int res; - op1 = sp[-2]; - op2 = sp[-1]; - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; + /* 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_rope_compare(ctx, op1, op2, FALSE); + + switch(op) { + case OP_lt: res = (res < 0); break; + case OP_lte: res = (res <= 0); break; + case OP_gt: res = (res > 0); break; + default: res = (res >= 0); break; } - op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; + + /* number <=> number */ + } else if ((tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64) && + (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) { + double d1 = (tag1 == JS_TAG_FLOAT64 + ? JS_VALUE_GET_FLOAT64(op1) + : (double)JS_VALUE_GET_INT(op1)); + double d2 = (tag2 == JS_TAG_FLOAT64 + ? JS_VALUE_GET_FLOAT64(op2) + : (double)JS_VALUE_GET_INT(op2)); + + switch(op) { + case OP_lt: res = (d1 < d2); break; + case OP_lte: res = (d1 <= d2); break; + case OP_gt: res = (d1 > d2); break; + default: res = (d1 >= d2); break; } - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - 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_rope_compare(ctx, op1, op2, FALSE); - } - switch(op) { - case OP_lt: - res = (res < 0); - break; - case OP_lte: - res = (res <= 0); - break; - case OP_gt: - res = (res > 0); - break; - default: - case OP_gte: - res = (res >= 0); - break; - } - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) && - (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) { - /* fast path for float64/int */ - goto float64_compare; - } else { - if ((((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && - tag_is_string(tag2)) || - ((tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) && - tag_is_string(tag1)))) { - if (tag_is_string(tag1)) { - op1 = JS_StringToBigInt(ctx, op1); - if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT && - JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT) - goto invalid_bigint_string; - } - if (tag_is_string(tag2)) { - op2 = JS_StringToBigInt(ctx, op2); - if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT && - JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT) { - invalid_bigint_string: - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - res = FALSE; - goto done; - } - } - } else { - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToNumericFree(ctx, op2); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - } + /* anything else → TypeError */ + } else { + JS_ThrowTypeError(ctx, + "Relational operators only supported on two strings or two numbers"); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + goto exception; + } - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); + /* free the two input values and push the result */ + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = JS_NewBool(ctx, res); + return 0; - if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT || - tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) { - res = js_compare_bigint(ctx, op, op1, op2); - } else { - double d1, d2; - - float64_compare: - /* can use floating point comparison */ - if (tag1 == JS_TAG_FLOAT64) { - d1 = JS_VALUE_GET_FLOAT64(op1); - } else { - d1 = JS_VALUE_GET_INT(op1); - } - if (tag2 == JS_TAG_FLOAT64) { - d2 = JS_VALUE_GET_FLOAT64(op2); - } else { - d2 = JS_VALUE_GET_INT(op2); - } - switch(op) { - case OP_lt: - res = (d1 < d2); /* if NaN return false */ - break; - case OP_lte: - res = (d1 <= d2); /* if NaN return false */ - break; - case OP_gt: - res = (d1 > d2); /* if NaN return false */ - break; - default: - case OP_gte: - res = (d1 >= d2); /* if NaN return false */ - break; - } - } - } - done: - sp[-2] = JS_NewBool(ctx, res); - return 0; - exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; +exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; } static BOOL tag_is_number(uint32_t tag)