From bc4051aa7811d46549fc60e51ddd8de966c28bd4 Mon Sep 17 00:00:00 2001 From: Charlie Gordon Date: Sat, 16 Mar 2024 09:24:58 +0100 Subject: [PATCH 1/7] Improve consistency of JS_NewFloat64 API - `JS_NewFloat64()` always creates a `JS_TAG_FLOAT64` value - rename `__JS_NewFloat64` as `JS_NewFloat64__` to avoid reserved name issue - internal `js_float64()` always creates a `JS_TAG_FLOAT64` value - add `js_int64` internal function for consistency - simplify `float_is_int32` and rename as `double_is_int32` - handle `INT32_MIN` in `double_is_int32` - add `js_number(d)` to create a `JS_TAG_FLOAT64` or a `JS_TAG_INT` value if possible - add `JS_NewNumber()` API for the same purpose - use non testing constructor for infinities in `js_atof2` - always store internal time value as a float64 - merge `JS_NewBigInt64_1` into `JS_NewBigInt64` --- quickjs.c | 165 +++++++++++++++++++++++++----------------------------- quickjs.h | 15 +++-- 2 files changed, 87 insertions(+), 93 deletions(-) diff --git a/quickjs.c b/quickjs.c index e1ec8c340..87a85ec94 100644 --- a/quickjs.c +++ b/quickjs.c @@ -1262,41 +1262,22 @@ static const JSClassExoticMethods js_string_exotic_methods; static const JSClassExoticMethods js_proxy_exotic_methods; static const JSClassExoticMethods js_module_ns_exotic_methods; -// Special care is taken to not invoke UB when checking if the result fits -// in an int32_t. Leans on the fact that the input is integral if the lower -// 52 bits of the equation 2**e * (f + 2**52) are zero. -static BOOL float_is_int32(double d) +static inline BOOL double_is_int32(double d) { - uint64_t u, m, e, f; - JSFloat64Union t; + JSFloat64Union t1, t2; - t.d = d; - u = t.u64; - - // special case -0 - m = 1ull << 63; - if (u == m) - return FALSE; - - e = (u >> 52) & 0x7FF; - if (e > 0) - e -= 1023; - - // too large, nan or inf? - if (e > 30) + if (d >= INT32_MIN && d <= INT32_MAX) { + t1.d = d; + t2.d = (int32_t)d; + return t1.u64 == t2.u64; + } else { return FALSE; - - // fractional or subnormal if low bits are non-zero - f = 0xFFFFFFFFFFFFFull & u; - m = 0xFFFFFFFFFFFFFull >> e; - return 0 == (f & m); + } } static JSValue js_float64(double d) { - if (float_is_int32(d)) - return JS_MKVAL(JS_TAG_INT, (int32_t)d); - return __JS_NewFloat64(d); + return JS_NewFloat64__(d); } static int compare_u32(uint32_t a, uint32_t b) @@ -1313,7 +1294,31 @@ static JSValue js_uint32(uint32_t v) { if (v <= INT32_MAX) return js_int32(v); - return js_float64(v); + else + return js_float64(v); +} + +static JSValue js_int64(int64_t v) +{ + if (v == (int32_t)v) + return js_int32(v); + else + return js_float64(v); +} + +#define JS_NewInt64(ctx, val) js_int64(val) + +static JSValue js_number(double d) +{ + if (double_is_int32(d)) + return js_int32((int32_t)d); + else + return js_float64(d); +} + +JSValue JS_NewNumber(JSContext *ctx, double d) +{ + return js_number(d); } static JSValue js_bool(JS_BOOL v) @@ -3167,7 +3172,7 @@ static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) /* -0 case is specific */ if (c == '0' && len == 2) { minus_zero: - return __JS_NewFloat64(-0.0); + return js_float64(-0.0); } } if (!is_num(c)) { @@ -6859,13 +6864,6 @@ static int JS_SetPrototypeInternal(JSContext *ctx, JSValue obj, sh = p->shape; if (sh->proto == proto) return TRUE; - if (p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_OBJECT])) { - if (throw_flag) { - JS_ThrowTypeError(ctx, "'Immutable prototype object \'Object.prototype\' cannot have their prototype set'"); - return -1; - } - return FALSE; - } if (!p->extensible) { if (throw_flag) { JS_ThrowTypeError(ctx, "object is not extensible"); @@ -7954,10 +7952,10 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValue this_obj, return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]); case JS_CLASS_FLOAT32_ARRAY: if (unlikely(idx >= p->u.array.count)) goto slow_path; - return __JS_NewFloat64(p->u.array.u.float_ptr[idx]); + return js_float64(p->u.array.u.float_ptr[idx]); case JS_CLASS_FLOAT64_ARRAY: if (unlikely(idx >= p->u.array.count)) goto slow_path; - return __JS_NewFloat64(p->u.array.u.double_ptr[idx]); + return js_float64(p->u.array.u.double_ptr[idx]); default: goto slow_path; } @@ -10280,7 +10278,7 @@ static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp, double d; d = js_strtod(buf, radix, is_float); /* return int or float64 */ - val = js_float64(d); + val = js_number(d); } break; case ATOD_TYPE_BIG_INT: @@ -10489,7 +10487,7 @@ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) } else { /* convert -0 to +0 */ d = trunc(d) + 0.0; - ret = js_float64(d); + ret = js_number(d); } } break; @@ -11749,12 +11747,7 @@ static double js_pow(double a, double b) } } -JSValue JS_NewFloat64(JSContext *ctx, double d) -{ - return js_float64(d); -} - -JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v) +JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) { JSValue val; bf_t *a; @@ -11769,11 +11762,6 @@ JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v) return val; } -JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) -{ - return JS_NewBigInt64_1(ctx, v); -} - JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) { JSValue val; @@ -12109,7 +12097,7 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx, break; case OP_neg: if (v64 == 0) { - sp[-1] = __JS_NewFloat64(-0.0); + sp[-1] = js_float64(-0.0); return 0; } else { v64 = -v64; @@ -12143,7 +12131,7 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx, default: abort(); } - sp[-1] = __JS_NewFloat64(d); + sp[-1] = js_float64(d); } break; } @@ -12336,23 +12324,23 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s case OP_mul: v = (int64_t)v1 * (int64_t)v2; if (v == 0 && (v1 | v2) < 0) { - sp[-2] = __JS_NewFloat64(-0.0); + sp[-2] = js_float64(-0.0); return 0; } break; case OP_div: - sp[-2] = __JS_NewFloat64((double)v1 / (double)v2); + sp[-2] = js_float64((double)v1 / (double)v2); return 0; case OP_mod: if (v1 < 0 || v2 <= 0) { - sp[-2] = js_float64(fmod(v1, v2)); + sp[-2] = js_number(fmod(v1, v2)); return 0; } else { v = (int64_t)v1 % (int64_t)v2; } break; case OP_pow: - sp[-2] = js_float64(js_pow(v1, v2)); + sp[-2] = js_number(js_pow(v1, v2)); return 0; default: abort(); @@ -12390,7 +12378,7 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s default: abort(); } - sp[-2] = __JS_NewFloat64(dr); + sp[-2] = js_float64(dr); } return 0; exception: @@ -12414,7 +12402,7 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) double d1, d2; d1 = JS_VALUE_GET_FLOAT64(op1); d2 = JS_VALUE_GET_FLOAT64(op2); - sp[-2] = __JS_NewFloat64(d1 + d2); + sp[-2] = js_float64(d1 + d2); return 0; } @@ -12473,7 +12461,7 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) } if (JS_ToFloat64Free(ctx, &d2, op2)) goto exception; - sp[-2] = __JS_NewFloat64(d1 + d2); + sp[-2] = js_float64(d1 + d2); } return 0; exception: @@ -14390,7 +14378,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValue func_obj, ret_val = JS_EXCEPTION; break; } - ret_val = js_float64(func.f_f(d1)); + ret_val = js_number(func.f_f(d1)); } break; case JS_CFUNC_f_f_f: @@ -14405,7 +14393,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValue func_obj, ret_val = JS_EXCEPTION; break; } - ret_val = js_float64(func.f_f_f(d1, d2)); + ret_val = js_number(func.f_f_f(d1, d2)); } break; case JS_CFUNC_iterator_next: @@ -16284,8 +16272,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, sp[-2] = js_int32(r); sp--; } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { - sp[-2] = __JS_NewFloat64(JS_VALUE_GET_FLOAT64(op1) + - JS_VALUE_GET_FLOAT64(op2)); + sp[-2] = js_float64(JS_VALUE_GET_FLOAT64(op1) + + JS_VALUE_GET_FLOAT64(op2)); sp--; } else { add_slow: @@ -16349,8 +16337,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, sp[-2] = js_int32(r); sp--; } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { - sp[-2] = __JS_NewFloat64(JS_VALUE_GET_FLOAT64(op1) - - JS_VALUE_GET_FLOAT64(op2)); + sp[-2] = js_float64(JS_VALUE_GET_FLOAT64(op1) - + JS_VALUE_GET_FLOAT64(op2)); sp--; } else { goto binary_arith_slow; @@ -16383,7 +16371,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); mul_fp_res: - sp[-2] = __JS_NewFloat64(d); + sp[-2] = js_float64(d); sp--; } else { goto binary_arith_slow; @@ -16399,7 +16387,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, int v1, v2; v1 = JS_VALUE_GET_INT(op1); v2 = JS_VALUE_GET_INT(op2); - sp[-2] = js_float64((double)v1 / (double)v2); + sp[-2] = js_number((double)v1 / (double)v2); sp--; } else { goto binary_arith_slow; @@ -16470,7 +16458,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, } else if (JS_TAG_IS_FLOAT64(tag)) { d = -JS_VALUE_GET_FLOAT64(op1); neg_fp_res: - sp[-1] = __JS_NewFloat64(d); + sp[-1] = js_float64(d); } else { if (js_unary_arith_slow(ctx, sp, opcode)) goto exception; @@ -34229,7 +34217,7 @@ static JSValue JS_ReadObjectRec(BCReaderState *s) if (bc_get_u64(s, &u.u64)) return JS_EXCEPTION; bc_read_trace(s, "%g\n", u.d); - obj = __JS_NewFloat64(u.d); + obj = js_float64(u.d); } break; case BC_TAG_STRING: @@ -34532,7 +34520,7 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValue obj, val = JS_NewInt64(ctx, e->u.i64); break; case JS_DEF_PROP_DOUBLE: - val = __JS_NewFloat64(e->u.f64); + val = js_float64(e->u.f64); break; case JS_DEF_PROP_UNDEFINED: val = JS_UNDEFINED; @@ -34596,7 +34584,7 @@ int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, val = JS_NewInt64(ctx, e->u.i64); break; case JS_DEF_PROP_DOUBLE: - val = __JS_NewFloat64(e->u.f64); + val = js_float64(e->u.f64); break; case JS_DEF_OBJECT: val = JS_NewObject(ctx); @@ -36177,6 +36165,7 @@ static JSValue js_function_bind(JSContext *ctx, JSValue this_val, goto exception; if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) { /* most common case */ + // XXX: potential overflow int len1 = JS_VALUE_GET_INT(len_val); if (len1 <= arg_count) len1 = 0; @@ -36194,7 +36183,7 @@ static JSValue js_function_bind(JSContext *ctx, JSValue this_val, else d -= (double)arg_count; /* also converts -0 to +0 */ } - len_val = js_float64(d); + len_val = js_number(d); } else { JS_FreeValue(ctx, len_val); len_val = js_int32(0); @@ -38602,7 +38591,7 @@ static JSValue js_number_constructor(JSContext *ctx, JSValue new_target, double d; bf_get_float64(&p->num, &d, BF_RNDN); JS_FreeValue(ctx, val); - val = __JS_NewFloat64(d); + val = js_float64(d); } break; default: @@ -38758,7 +38747,7 @@ static JSValue js_number_toFixed(JSContext *ctx, JSValue this_val, if (f < 0 || f > 100) return JS_ThrowRangeError(ctx, "invalid number of digits"); if (fabs(d) >= 1e21) { - return JS_ToStringFree(ctx, __JS_NewFloat64(d)); + return JS_ToStringFree(ctx, js_float64(d)); } else { return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT); } @@ -38779,7 +38768,7 @@ static JSValue js_number_toExponential(JSContext *ctx, JSValue this_val, if (JS_ToInt32Sat(ctx, &f, argv[0])) return JS_EXCEPTION; if (!isfinite(d)) { - return JS_ToStringFree(ctx, __JS_NewFloat64(d)); + return JS_ToStringFree(ctx, js_float64(d)); } if (JS_IsUndefined(argv[0])) { flags = 0; @@ -38811,7 +38800,7 @@ static JSValue js_number_toPrecision(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; if (!isfinite(d)) { to_string: - return JS_ToStringFree(ctx, __JS_NewFloat64(d)); + return JS_ToStringFree(ctx, js_float64(d)); } if (p < 1 || p > 100) return JS_ThrowRangeError(ctx, "invalid number of digits"); @@ -40672,7 +40661,7 @@ static JSValue js_math_min_max(JSContext *ctx, JSValue this_val, uint32_t tag; if (unlikely(argc == 0)) { - return __JS_NewFloat64(is_max ? NEG_INF : INF); + return js_float64(is_max ? NEG_INF : INF); } tag = JS_VALUE_GET_TAG(argv[0]); @@ -40712,7 +40701,7 @@ static JSValue js_math_min_max(JSContext *ctx, JSValue this_val, } i++; } - return js_float64(r); + return js_number(r); } } @@ -40841,7 +40830,7 @@ static JSValue js_math_random(JSContext *ctx, JSValue this_val, v = xorshift64star(&ctx->random_state); /* 1.0 <= u.d < 2 */ u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12); - return __JS_NewFloat64(u.d - 1.0); + return js_float64(u.d - 1.0); } static const JSCFunctionListEntry js_math_funcs[] = { @@ -47081,7 +47070,7 @@ static JSValue get_date_field(JSContext *ctx, JSValue this_val, if (magic & 0x100) { // getYear fields[0] -= 1900; } - return js_float64(fields[n]); + return js_number(fields[n]); } static JSValue set_date_field(JSContext *ctx, JSValue this_val, @@ -49173,9 +49162,9 @@ static JSValue js_typed_array_at(JSContext *ctx, JSValue this_val, case JS_CLASS_UINT32_ARRAY: return js_uint32(p->u.array.u.uint32_ptr[idx]); case JS_CLASS_FLOAT32_ARRAY: - return __JS_NewFloat64(p->u.array.u.float_ptr[idx]); + return js_float64(p->u.array.u.float_ptr[idx]); case JS_CLASS_FLOAT64_ARRAY: - return __JS_NewFloat64(p->u.array.u.double_ptr[idx]); + return js_float64(p->u.array.u.double_ptr[idx]); case JS_CLASS_BIG_INT64_ARRAY: return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]); case JS_CLASS_BIG_UINT64_ARRAY: @@ -50187,11 +50176,11 @@ static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) { } static JSValue js_TA_get_float32(JSContext *ctx, const void *a) { - return __JS_NewFloat64(*(const float *)a); + return js_float64(*(const float *)a); } static JSValue js_TA_get_float64(JSContext *ctx, const void *a) { - return __JS_NewFloat64(*(const double *)a); + return js_float64(*(const double *)a); } struct TA_sort_context { @@ -50874,7 +50863,7 @@ static JSValue js_dataview_getValue(JSContext *ctx, if (is_swap) v = bswap32(v); u.i = v; - return __JS_NewFloat64(u.f); + return js_float64(u.f); } case JS_CLASS_FLOAT64_ARRAY: { @@ -50885,7 +50874,7 @@ static JSValue js_dataview_getValue(JSContext *ctx, u.i = get_u64(ptr); if (is_swap) u.i = bswap64(u.i); - return __JS_NewFloat64(u.f); + return js_float64(u.f); } default: abort(); diff --git a/quickjs.h b/quickjs.h index c21890852..4fa432a82 100644 --- a/quickjs.h +++ b/quickjs.h @@ -115,7 +115,7 @@ static inline double JS_VALUE_GET_FLOAT64(JSValue v) #define JS_NAN (0x7ff8000000000000 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32)) -static inline JSValue __JS_NewFloat64(double d) +static inline JSValue JS_NewFloat64__(double d) { union { double d; @@ -179,7 +179,7 @@ typedef struct JSValue { #define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 } -static inline JSValue __JS_NewFloat64(double d) +static inline JSValue JS_NewFloat64__(double d) { JSValue v; v.tag = JS_TAG_FLOAT64; @@ -474,6 +474,11 @@ static js_force_inline JSValue JS_NewInt32(JSContext *ctx, int32_t val) return JS_MKVAL(JS_TAG_INT, val); } +static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double val) +{ + return JS_NewFloat64__(val); +} + static js_force_inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) { return JS_MKVAL(JS_TAG_CATCH_OFFSET, val); @@ -485,7 +490,7 @@ static js_force_inline JSValue JS_NewInt64(JSContext *ctx, int64_t val) if (val == (int32_t)val) { v = JS_NewInt32(ctx, val); } else { - v = __JS_NewFloat64(val); + v = JS_NewFloat64(ctx, val); } return v; } @@ -496,12 +501,12 @@ static js_force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val) if (val <= 0x7fffffff) { v = JS_NewInt32(ctx, val); } else { - v = __JS_NewFloat64(val); + v = JS_NewFloat64(ctx, val); } return v; } -JS_EXTERN JSValue JS_NewFloat64(JSContext *ctx, double d); +JS_EXTERN JSValue JS_NewNumber(JSContext *ctx, double d); JS_EXTERN JSValue JS_NewBigInt64(JSContext *ctx, int64_t v); JS_EXTERN JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v); From 2bb35fa5ca89cc6a016c2b7f19a934ab26ad53ce Mon Sep 17 00:00:00 2001 From: Charlie Gordon Date: Sat, 16 Mar 2024 09:46:12 +0100 Subject: [PATCH 2/7] revert `__JS_NewFloat64` name change --- quickjs.c | 2 +- quickjs.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/quickjs.c b/quickjs.c index 87a85ec94..4eafca757 100644 --- a/quickjs.c +++ b/quickjs.c @@ -1277,7 +1277,7 @@ static inline BOOL double_is_int32(double d) static JSValue js_float64(double d) { - return JS_NewFloat64__(d); + return __JS_NewFloat64(d); } static int compare_u32(uint32_t a, uint32_t b) diff --git a/quickjs.h b/quickjs.h index 4fa432a82..4fe7575b6 100644 --- a/quickjs.h +++ b/quickjs.h @@ -115,7 +115,7 @@ static inline double JS_VALUE_GET_FLOAT64(JSValue v) #define JS_NAN (0x7ff8000000000000 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32)) -static inline JSValue JS_NewFloat64__(double d) +static inline JSValue __JS_NewFloat64(double d) { union { double d; @@ -179,7 +179,7 @@ typedef struct JSValue { #define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 } -static inline JSValue JS_NewFloat64__(double d) +static inline JSValue __JS_NewFloat64(double d) { JSValue v; v.tag = JS_TAG_FLOAT64; @@ -476,7 +476,7 @@ static js_force_inline JSValue JS_NewInt32(JSContext *ctx, int32_t val) static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double val) { - return JS_NewFloat64__(val); + return __JS_NewFloat64(val); } static js_force_inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) From 1f46f99730abfd4e5105997d85488f41ff63a99e Mon Sep 17 00:00:00 2001 From: Charlie Gordon Date: Sat, 16 Mar 2024 11:32:04 +0100 Subject: [PATCH 3/7] restore Object.prototype as immutable prototype object --- quickjs.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/quickjs.c b/quickjs.c index 4eafca757..05c1de7ae 100644 --- a/quickjs.c +++ b/quickjs.c @@ -6864,6 +6864,13 @@ static int JS_SetPrototypeInternal(JSContext *ctx, JSValue obj, sh = p->shape; if (sh->proto == proto) return TRUE; + if (p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_OBJECT])) { + if (throw_flag) { + JS_ThrowTypeError(ctx, "'Immutable prototype object \'Object.prototype\' cannot have their prototype set'"); + return -1; + } + return FALSE; + } if (!p->extensible) { if (throw_flag) { JS_ThrowTypeError(ctx, "object is not extensible"); From cee449dff8cfb713526fe6c99ba0ef05298a6825 Mon Sep 17 00:00:00 2001 From: Charlie Gordon Date: Thu, 21 Mar 2024 15:23:14 +0100 Subject: [PATCH 4/7] use safer and faster int32 test --- quickjs.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/quickjs.c b/quickjs.c index 05c1de7ae..ec4f4aa72 100644 --- a/quickjs.c +++ b/quickjs.c @@ -1264,14 +1264,19 @@ static const JSClassExoticMethods js_module_ns_exotic_methods; static inline BOOL double_is_int32(double d) { - JSFloat64Union t1, t2; + uint64_t u, e; + JSFloat64Union t; - if (d >= INT32_MIN && d <= INT32_MAX) { - t1.d = d; - t2.d = (int32_t)d; - return t1.u64 == t2.u64; + t.d = d; + u = t.u64; + + e = ((u >> 52) & 0x7FF) - 1023; + if (e > 30) { + // accept 0, INT32_MIN, reject too large, too small, nan, inf, -0 + return !u || (u == 0xc1e0000000000000); } else { - return FALSE; + // fractional if low bits are non-zero + return !(u << 12 << e); } } From 2e2afa03a99fafcc2588cfac179baefc90caca57 Mon Sep 17 00:00:00 2001 From: Charlie Gordon Date: Fri, 22 Mar 2024 21:00:53 +0100 Subject: [PATCH 5/7] Fix implementation defined behavior - use comparisons instead of `(int32_t)` casts --- quickjs.c | 3 +-- quickjs.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/quickjs.c b/quickjs.c index ec4f4aa72..5d77250b5 100644 --- a/quickjs.c +++ b/quickjs.c @@ -1305,7 +1305,7 @@ static JSValue js_uint32(uint32_t v) static JSValue js_int64(int64_t v) { - if (v == (int32_t)v) + if (v >= INT32_MIN && v <= INT32_MAX) return js_int32(v); else return js_float64(v); @@ -36177,7 +36177,6 @@ static JSValue js_function_bind(JSContext *ctx, JSValue this_val, goto exception; if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) { /* most common case */ - // XXX: potential overflow int len1 = JS_VALUE_GET_INT(len_val); if (len1 <= arg_count) len1 = 0; diff --git a/quickjs.h b/quickjs.h index 4fe7575b6..f70d4c716 100644 --- a/quickjs.h +++ b/quickjs.h @@ -487,7 +487,7 @@ static js_force_inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) static js_force_inline JSValue JS_NewInt64(JSContext *ctx, int64_t val) { JSValue v; - if (val == (int32_t)val) { + if (val >= INT32_MIN && v <= INT32_MAX) { v = JS_NewInt32(ctx, val); } else { v = JS_NewFloat64(ctx, val); From f66404546acb597e69b012eb58927fa85426d065 Mon Sep 17 00:00:00 2001 From: Charlie Gordon Date: Sat, 23 Mar 2024 08:38:38 +0100 Subject: [PATCH 6/7] unbreak JS_NewInt64 --- quickjs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickjs.h b/quickjs.h index f70d4c716..a264e73db 100644 --- a/quickjs.h +++ b/quickjs.h @@ -487,7 +487,7 @@ static js_force_inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) static js_force_inline JSValue JS_NewInt64(JSContext *ctx, int64_t val) { JSValue v; - if (val >= INT32_MIN && v <= INT32_MAX) { + if (val >= INT32_MIN && val <= INT32_MAX) { v = JS_NewInt32(ctx, val); } else { v = JS_NewFloat64(ctx, val); From 982c346524cc20c987229efa265b09a73f3f03da Mon Sep 17 00:00:00 2001 From: Charlie Gordon Date: Sat, 23 Mar 2024 09:09:03 +0100 Subject: [PATCH 7/7] added explanatory comment --- quickjs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/quickjs.c b/quickjs.c index 5d77250b5..0dfd5d094 100644 --- a/quickjs.c +++ b/quickjs.c @@ -1275,7 +1275,8 @@ static inline BOOL double_is_int32(double d) // accept 0, INT32_MIN, reject too large, too small, nan, inf, -0 return !u || (u == 0xc1e0000000000000); } else { - // fractional if low bits are non-zero + // shift out sign, exponent and whole part bits + // value is fractional if remaining low bits are non-zero return !(u << 12 << e); } }