diff options
author | bellard <6490144+bellard@users.noreply.github.com> | 2020-09-06 18:57:11 +0200 |
---|---|---|
committer | bellard <6490144+bellard@users.noreply.github.com> | 2020-09-06 18:57:11 +0200 |
commit | 0e8fffd4de4a10f498f46cd0e99f53da6a523542 (patch) | |
tree | fc80c9b15e5816b84ae7d012a1106f7f46a70332 /quickjs.c | |
parent | 91459fb6723e29e923380cec0023af93819ae69d (diff) | |
download | quickjs-0e8fffd4de4a10f498f46cd0e99f53da6a523542.tar.gz quickjs-0e8fffd4de4a10f498f46cd0e99f53da6a523542.zip |
2020-01-19 release
Diffstat (limited to 'quickjs.c')
-rw-r--r-- | quickjs.c | 3206 |
1 files changed, 1788 insertions, 1418 deletions
@@ -42,10 +42,6 @@ #include "list.h" #include "quickjs.h" #include "libregexp.h" - -/* enable bignums */ -#define CONFIG_BIGNUM - #ifdef CONFIG_BIGNUM #include "libbf.h" #endif @@ -151,7 +147,8 @@ enum { JS_CLASS_BIG_INT, /* u.object_data */ JS_CLASS_BIG_FLOAT, /* u.object_data */ JS_CLASS_FLOAT_ENV, /* u.float_env */ - JS_CLASS_BIG_DECIMAL, /* u.object_data */ + JS_CLASS_BIG_DECIMAL, /* u.object_data */ + JS_CLASS_OPERATOR_SET, /* u.operator_set */ #endif JS_CLASS_MAP, /* u.map_state */ JS_CLASS_SET, /* u.map_state */ @@ -285,6 +282,7 @@ struct JSRuntime { JSNumericOperations bigint_ops; JSNumericOperations bigfloat_ops; JSNumericOperations bigdecimal_ops; + uint32_t operator_count; #endif }; @@ -300,8 +298,7 @@ struct JSClass { #define JS_MODE_STRICT (1 << 0) #define JS_MODE_STRIP (1 << 1) -#define JS_MODE_BIGINT (1 << 2) -#define JS_MODE_MATH (1 << 3) +#define JS_MODE_MATH (1 << 2) typedef struct JSStackFrame { struct JSStackFrame *prev_frame; /* NULL if first stack frame */ @@ -312,7 +309,7 @@ typedef struct JSStackFrame { const uint8_t *cur_pc; /* only used in bytecode functions : PC of the instruction after the call */ int arg_count; - int js_mode; /* 0 or JS_MODE_BIGINT for C functions */ + int js_mode; /* 0 or JS_MODE_MATH for C functions */ /* only used in generators. Current stack pointer value. NULL if the function is running. */ JSValue *cur_sp; @@ -419,8 +416,8 @@ struct JSContext { #ifdef CONFIG_BIGNUM bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */ JSFloatEnv fp_env; /* global FP environment */ - BOOL bignum_ext; /* enable bigint mode, math mode and operator - overloading */ + BOOL bignum_ext : 8; /* enable math mode */ + BOOL allow_operator_overloading : 8; #endif /* when the counter reaches zero, JSRutime.interrupt_handler is called */ int interrupt_counter; @@ -653,6 +650,53 @@ typedef struct JSAsyncFunctionData { JSAsyncFunctionState func_state; } JSAsyncFunctionData; +typedef enum { + /* binary operators */ + JS_OVOP_ADD, + JS_OVOP_SUB, + JS_OVOP_MUL, + JS_OVOP_DIV, + JS_OVOP_MOD, + JS_OVOP_POW, + JS_OVOP_OR, + JS_OVOP_AND, + JS_OVOP_XOR, + JS_OVOP_SHL, + JS_OVOP_SAR, + JS_OVOP_SHR, + JS_OVOP_EQ, + JS_OVOP_LESS, + + JS_OVOP_BINARY_COUNT, + /* unary operators */ + JS_OVOP_POS = JS_OVOP_BINARY_COUNT, + JS_OVOP_NEG, + JS_OVOP_INC, + JS_OVOP_DEC, + JS_OVOP_NOT, + + JS_OVOP_COUNT, +} JSOverloadableOperatorEnum; + +typedef struct { + uint32_t operator_index; + JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */ +} JSBinaryOperatorDefEntry; + +typedef struct { + int count; + JSBinaryOperatorDefEntry *tab; +} JSBinaryOperatorDef; + +typedef struct { + uint32_t operator_counter; + BOOL is_primitive; /* OperatorSet for a primitive type */ + /* NULL if no operator is defined */ + JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */ + JSBinaryOperatorDef left; + JSBinaryOperatorDef right; +} JSOperatorSetData; + typedef struct JSReqModuleEntry { JSAtom module_name; JSModuleDef *module; /* used using resolution */ @@ -808,6 +852,7 @@ struct JSObject { struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */ #ifdef CONFIG_BIGNUM struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */ + struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */ #endif struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */ @@ -991,6 +1036,11 @@ static void js_promise_mark(JSRuntime *rt, JSValueConst val, static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val); static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); +#ifdef CONFIG_BIGNUM +static void js_operator_set_finalizer(JSRuntime *rt, JSValue val); +static void js_operator_set_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +#endif static JSValue JS_ToStringFree(JSContext *ctx, JSValue val); static int JS_ToBoolFree(JSContext *ctx, JSValue val); static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val); @@ -1021,10 +1071,27 @@ static JSProperty *add_property(JSContext *ctx, JSObject *p, JSAtom prop, int prop_flags); #ifdef CONFIG_BIGNUM static void js_float_env_finalizer(JSRuntime *rt, JSValue val); -static JSValue JS_NewBigFloat(JSContext *ctx, bf_t *a); -static JSValue JS_NewBigDecimal(JSContext *ctx, bfdec_t *a); -static JSValue JS_NewBigInt2(JSContext *ctx, bf_t *a, BOOL force_bigint); -static JSValue JS_NewBigInt(JSContext *ctx, bf_t *a); +static JSValue JS_NewBigFloat(JSContext *ctx); +static inline bf_t *JS_GetBigFloat(JSValueConst val) +{ + JSBigFloat *p = JS_VALUE_GET_PTR(val); + return &p->num; +} +static JSValue JS_NewBigDecimal(JSContext *ctx); +static inline bfdec_t *JS_GetBigDecimal(JSValueConst val) +{ + JSBigDecimal *p = JS_VALUE_GET_PTR(val); + return &p->num; +} +static JSValue JS_NewBigInt(JSContext *ctx); +static inline bf_t *JS_GetBigInt(JSValueConst val) +{ + JSBigFloat *p = JS_VALUE_GET_PTR(val); + return &p->num; +} +static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, + BOOL convert_to_safe_integer); +static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val); static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val); static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val); static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf); @@ -1123,19 +1190,6 @@ static const JSClassExoticMethods js_proxy_exotic_methods; static const JSClassExoticMethods js_module_ns_exotic_methods; static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT; -JS_BOOL JS_IsNumber(JSValueConst v) -{ -#ifdef CONFIG_BIGNUM - /* XXX: check the bignum case. Should use a different function */ - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_INT || tag == JS_TAG_BIG_INT || - tag == JS_TAG_BIG_FLOAT || JS_TAG_IS_FLOAT64(tag); -#else - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag); -#endif -} - static void js_trigger_gc(JSRuntime *rt, size_t size) { BOOL force_gc; @@ -1333,6 +1387,7 @@ static JSClassShortDef const js_std_class_def[] = { { JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_FLOAT */ { JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL }, /* JS_CLASS_FLOAT_ENV */ { JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_DECIMAL */ + { JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark }, /* JS_CLASS_OPERATOR_SET */ #endif { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */ { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */ @@ -2136,10 +2191,10 @@ static inline BOOL is_strict_mode(JSContext *ctx) } #ifdef CONFIG_BIGNUM -static inline BOOL is_bigint_mode(JSContext *ctx) +static inline BOOL is_math_mode(JSContext *ctx) { JSStackFrame *sf = ctx->current_stack_frame; - return (sf && (sf->js_mode & JS_MODE_BIGINT)); + return (sf && (sf->js_mode & JS_MODE_MATH)); } #endif @@ -2148,25 +2203,12 @@ JSValue JS_NewInt64(JSContext *ctx, int64_t v) if (v == (int32_t)v) { return JS_NewInt32(ctx, v); } else { -#ifdef CONFIG_BIGNUM - if (is_bigint_mode(ctx)) { - bf_t a_s, *a = &a_s; - bf_init(ctx->bf_ctx, a); - bf_set_si(a, v); - return JS_NewBigInt(ctx, a); - } else -#endif - { - return __JS_NewFloat64(ctx, (double)v); - } + return __JS_NewFloat64(ctx, (double)v); } } static force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val) { -#ifdef CONFIG_BIGNUM - return JS_NewInt64(ctx, val); -#else JSValue v; if (val <= 0x7fffffff) { v = JS_MKVAL(JS_TAG_INT, val); @@ -2174,7 +2216,6 @@ static force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val) v = __JS_NewFloat64(ctx, val); } return v; -#endif } /* JSAtom support */ @@ -5720,6 +5761,7 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) #ifdef CONFIG_BIGNUM case JS_CLASS_BIG_INT: /* u.object_data */ case JS_CLASS_BIG_FLOAT: /* u.object_data */ + case JS_CLASS_BIG_DECIMAL: /* u.object_data */ #endif compute_value_size(p->u.object_data, hp); break; @@ -6515,28 +6557,17 @@ JSValueConst JS_GetPrototype(JSContext *ctx, JSValueConst val) case JS_TAG_BIG_INT: val = ctx->class_proto[JS_CLASS_BIG_INT]; break; - case JS_TAG_INT: - if (is_bigint_mode(ctx)) { - val = ctx->class_proto[JS_CLASS_BIG_INT]; - } else { - val = ctx->class_proto[JS_CLASS_NUMBER]; - } - break; - case JS_TAG_FLOAT64: - val = ctx->class_proto[JS_CLASS_NUMBER]; - break; case JS_TAG_BIG_FLOAT: val = ctx->class_proto[JS_CLASS_BIG_FLOAT]; break; case JS_TAG_BIG_DECIMAL: val = ctx->class_proto[JS_CLASS_BIG_DECIMAL]; break; -#else +#endif case JS_TAG_INT: case JS_TAG_FLOAT64: val = ctx->class_proto[JS_CLASS_NUMBER]; break; -#endif case JS_TAG_BOOL: val = ctx->class_proto[JS_CLASS_BOOLEAN]; break; @@ -7750,11 +7781,7 @@ static int set_array_length(JSContext *ctx, JSObject *p, JSProperty *prop, } p->u.array.count = len; } -#ifdef CONFIG_BIGNUM - set_value(ctx, &prop->u.value, JS_NewUint32(ctx, len)); -#else prop->u.value = JS_NewUint32(ctx, len); -#endif } else { /* Note: length is always a uint32 because the object is an array */ @@ -9352,9 +9379,6 @@ void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id) #define HINT_STRING 0 #define HINT_NUMBER 1 #define HINT_NONE 2 -#ifdef CONFIG_BIGNUM -#define HINT_INTEGER 3 -#endif /* don't try Symbol.toPrimitive */ #define HINT_FORCE_ORDINARY (1 << 4) @@ -9389,11 +9413,6 @@ static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint) case HINT_NONE: atom = JS_ATOM_default; break; -#ifdef CONFIG_BIGNUM - case HINT_INTEGER: - atom = JS_ATOM_integer; - break; -#endif } arg = JS_AtomToString(ctx, atom); ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg); @@ -9605,22 +9624,31 @@ static JSValue js_string_to_bigint(JSContext *ctx, const char *buf, { bf_t a_s, *a = &a_s; int ret; - bf_init(ctx->bf_ctx, a); + JSValue val; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ); if (ret & BF_ST_MEM_ERROR) { - bf_delete(a); + JS_FreeValue(ctx, val); return JS_ThrowOutOfMemory(ctx); } - return JS_NewBigInt2(ctx, a, !(flags & ATOD_MODE_BIGINT)); + val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0); + return val; } static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf, int radix, int flags, slimb_t *pexponent) { - bf_t a_s, *a = &a_s; + bf_t *a; int ret; - - bf_init(ctx->bf_ctx, a); + JSValue val; + + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigFloat(val); if (flags & ATOD_ACCEPT_SUFFIX) { /* return the exponent to get infinite precision */ ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF, @@ -9630,25 +9658,30 @@ static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf, ctx->fp_env.flags); } if (ret & BF_ST_MEM_ERROR) { - bf_delete(a); + JS_FreeValue(ctx, val); return JS_ThrowOutOfMemory(ctx); } - return JS_NewBigFloat(ctx, a); + return val; } static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf, int radix, int flags, slimb_t *pexponent) { - bfdec_t a_s, *a = &a_s; + bfdec_t *a; int ret; - bfdec_init(ctx->bf_ctx, a); + JSValue val; + + val = JS_NewBigDecimal(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigDecimal(val); ret = bfdec_atof(a, buf, NULL, BF_PREC_INF, BF_RNDZ | BF_ATOF_NO_NAN_INF); if (ret & BF_ST_MEM_ERROR) { - bfdec_delete(a); + JS_FreeValue(ctx, val); return JS_ThrowOutOfMemory(ctx); } - return JS_NewBigDecimal(ctx, a); + return val; } #endif @@ -9730,10 +9763,12 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, strstart(p, "Infinity", &p)) { #ifdef CONFIG_BIGNUM if (atod_type == ATOD_TYPE_BIG_FLOAT) { - bf_t a_s, *a = &a_s; - bf_init(ctx->bf_ctx, a); + bf_t *a; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + goto done; + a = JS_GetBigFloat(val); bf_set_inf(a, is_neg); - val = JS_NewBigFloat(ctx, a); } else #endif { @@ -9810,7 +9845,7 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, } else if (*p == 'l') { p++; atod_type = ATOD_TYPE_BIG_FLOAT; - } else if (*p == 'd') { + } else if (*p == 'm') { p++; atod_type = ATOD_TYPE_BIG_DECIMAL; } else { @@ -9901,7 +9936,6 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, typedef enum JSToNumberHintEnum { TON_FLAG_NUMBER, - TON_FLAG_INTEGER, TON_FLAG_NUMERIC, } JSToNumberHintEnum; @@ -9910,7 +9944,6 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, { uint32_t tag; JSValue ret; - int hint; redo: tag = JS_VALUE_GET_NORM_TAG(val); @@ -9924,12 +9957,19 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, ret = val; break; case JS_TAG_BIG_INT: - if (flag == TON_FLAG_NUMBER && !is_bigint_mode(ctx)) { + if (flag != TON_FLAG_NUMERIC) { JS_FreeValue(ctx, val); return JS_ThrowTypeError(ctx, "cannot convert bigint to number"); } - /* fall thru */ + ret = val; + break; case JS_TAG_BIG_FLOAT: + if (flag != TON_FLAG_NUMERIC) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number"); + } + ret = val; + break; #endif case JS_TAG_FLOAT64: case JS_TAG_INT: @@ -9944,12 +9984,7 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, ret = JS_NAN; break; case JS_TAG_OBJECT: -#ifdef CONFIG_BIGNUM - hint = flag == TON_FLAG_INTEGER ? HINT_INTEGER : HINT_NUMBER; -#else - hint = HINT_NUMBER; -#endif - val = JS_ToPrimitiveFree(ctx, val, hint); + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); if (JS_IsException(val)) return JS_EXCEPTION; goto redo; @@ -9969,10 +10004,6 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, ret = JS_NewInt32(ctx, 0); } else { int flags = ATOD_ACCEPT_BIN_OCT; -#ifdef CONFIG_BIGNUM - if (is_bigint_mode(ctx)) - flags |= ATOD_MODE_BIGINT; -#endif ret = js_atof(ctx, p, &p, 0, flags); if (!JS_IsException(ret)) { p += skip_spaces(p); @@ -10001,7 +10032,6 @@ static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val) return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER); } -#ifdef CONFIG_BIGNUM static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val) { return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC); @@ -10011,7 +10041,6 @@ static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val) { return JS_ToNumericFree(ctx, JS_DupValue(ctx, val)); } -#endif static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) @@ -10078,6 +10107,7 @@ static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val) return JS_ToNumberFree(ctx, JS_DupValue(ctx, val)); } +/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) { uint32_t tag; @@ -10106,11 +10136,6 @@ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) } break; #ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - if (!is_bigint_mode(ctx)) - goto to_number; - ret = val; - break; case JS_TAG_BIG_FLOAT: { bf_t a_s, *a, r_s, *r = &r_s; @@ -10124,10 +10149,13 @@ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) else ret = JS_DupValue(ctx, val); } else { - bf_init(ctx->bf_ctx, r); - bf_set(r, a); - bf_rint(r, BF_RNDZ); - ret = JS_NewBigInt(ctx, r); + ret = JS_NewBigInt(ctx); + if (!JS_IsException(ret)) { + r = JS_GetBigInt(ret); + bf_set(r, a); + bf_rint(r, BF_RNDZ); + ret = JS_CompactBigInt(ctx, ret); + } } if (a == &a_s) bf_delete(a); @@ -10136,9 +10164,6 @@ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) break; #endif default: -#ifdef CONFIG_BIGNUM - to_number: -#endif val = JS_ToNumberFree(ctx, val); if (JS_IsException(val)) return val; @@ -10182,17 +10207,12 @@ static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val) break; #ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: - to_bf: { JSBigFloat *p = JS_VALUE_GET_PTR(val); bf_get_int32(&ret, &p->num, 0); JS_FreeValue(ctx, val); } break; - case JS_TAG_BIG_INT: - if (is_bigint_mode(ctx)) - goto to_bf; - /* fall thru */ #endif default: val = JS_ToNumberFree(ctx, val); @@ -10261,17 +10281,12 @@ static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) return 0; #ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: - to_bf: { JSBigFloat *p = JS_VALUE_GET_PTR(val); bf_get_int64(pres, &p->num, 0); JS_FreeValue(ctx, val); } return 0; - case JS_TAG_BIG_INT: - if (is_bigint_mode(ctx)) - goto to_bf; - /* fall thru */ #endif default: val = JS_ToNumberFree(ctx, val); @@ -10346,17 +10361,12 @@ static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val) break; #ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: - to_bf: { JSBigFloat *p = JS_VALUE_GET_PTR(val); bf_get_int64(&ret, &p->num, BF_GET_INT_MOD); JS_FreeValue(ctx, val); } break; - case JS_TAG_BIG_INT: - if (is_bigint_mode(ctx)) - goto to_bf; - /* fall thru */ #endif default: val = JS_ToNumberFree(ctx, val); @@ -10418,17 +10428,12 @@ static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) break; #ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: - to_bf: { JSBigFloat *p = JS_VALUE_GET_PTR(val); bf_get_int32(&ret, &p->num, BF_GET_INT_MOD); JS_FreeValue(ctx, val); } break; - case JS_TAG_BIG_INT: - if (is_bigint_mode(ctx)) - goto to_bf; - /* fall thru */ #endif default: val = JS_ToNumberFree(ctx, val); @@ -10486,15 +10491,6 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) } break; #ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - if (!is_bigint_mode(ctx)) - goto to_number; - bf_get_int32(&res, &p->num, 0); - JS_FreeValue(ctx, val); - } - goto int_clamp; case JS_TAG_BIG_FLOAT: { JSBigFloat *p = JS_VALUE_GET_PTR(val); @@ -10509,9 +10505,6 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) goto int_clamp; #endif default: -#ifdef CONFIG_BIGNUM - to_number: -#endif val = JS_ToNumberFree(ctx, val); if (JS_IsException(val)) { *pres = 0; @@ -10584,6 +10577,12 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, #define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) +static BOOL is_safe_integer(double d) +{ + return isfinite(d) && floor(d) == d && + fabs(d) <= (double)MAX_SAFE_INTEGER; +} + int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val) { int64_t v; @@ -10609,7 +10608,6 @@ static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen, } /* Note: can return an exception */ -/* XXX: bignum case */ static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val) { double d; @@ -10753,7 +10751,8 @@ static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val) return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); } -static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val) +static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val, + limb_t prec, int flags) { JSValue ret; bfdec_t *a; @@ -10764,8 +10763,7 @@ static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val) saved_sign = a->sign; if (a->expn == BF_EXP_ZERO) a->sign = 0; - str = bfdec_ftoa(NULL, a, 0, BF_RNDZ | BF_FTOA_FORMAT_FREE | - BF_FTOA_JS_QUIRKS); + str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS); a->sign = saved_sign; if (!str) return JS_ThrowOutOfMemory(ctx); @@ -10774,6 +10772,12 @@ static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val) return ret; } +static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val) +{ + return js_bigdecimal_to_string1(ctx, val, 0, + BF_RNDZ | BF_FTOA_FORMAT_FREE); +} + #endif /* CONFIG_BIGNUM */ /* 2 <= base <= 36 */ @@ -11478,30 +11482,48 @@ static double js_pow(double a, double b) #ifdef CONFIG_BIGNUM +JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v) +{ + JSValue val; + bf_t *a; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); + if (bf_set_si(a, v)) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } + return val; +} + JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) { - BOOL is_bignum = is_bigint_mode(ctx); - if (is_bignum && v == (int32_t)v) { - return JS_NewInt32(ctx, v); + if (is_math_mode(ctx) && + v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) { + return JS_NewInt64(ctx, v); } else { - bf_t a_s, *a = &a_s; - bf_init(ctx->bf_ctx, a); - bf_set_si(a, v); - return JS_NewBigInt2(ctx, a, TRUE); + return JS_NewBigInt64_1(ctx, v); } } JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) { - BOOL is_bignum = is_bigint_mode(ctx); - if (is_bignum && v == (int32_t)v) { - return JS_NewInt32(ctx, v); + JSValue val; + if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) { + val = JS_NewInt64(ctx, v); } else { - bf_t a_s, *a = &a_s; - bf_init(ctx->bf_ctx, a); - bf_set_ui(a, v); - return JS_NewBigInt2(ctx, a, TRUE); + bf_t *a; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); + if (bf_set_ui(a, v)) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } } + return val; } /* if the returned bigfloat is allocated it is equal to @@ -11582,13 +11604,10 @@ static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) p = str; p += skip_spaces(p); if ((p - str) == len) { - bf_t a_s, *a = &a_s; - bf_init(ctx->bf_ctx, a); - bf_set_si(a, 0); - val = JS_NewBigInt(ctx, a); + val = JS_NewBigInt64(ctx, 0); } else { flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT; - if (is_bigint_mode(ctx)) + if (is_math_mode(ctx)) flags |= ATOD_MODE_BIGINT; val = js_atof(ctx, p, &p, 0, flags); p += skip_spaces(p); @@ -11625,7 +11644,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) case JS_TAG_INT: case JS_TAG_NULL: case JS_TAG_UNDEFINED: - if (!is_bigint_mode(ctx)) + if (!is_math_mode(ctx)) goto fail; /* fall tru */ case JS_TAG_BOOL: @@ -11636,7 +11655,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) case JS_TAG_FLOAT64: { double d = JS_VALUE_GET_FLOAT64(val); - if (!is_bigint_mode(ctx)) + if (!is_math_mode(ctx)) goto fail; if (!isfinite(d)) goto fail; @@ -11651,7 +11670,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) r = &p->num; break; case JS_TAG_BIG_FLOAT: - if (!is_bigint_mode(ctx)) + if (!is_math_mode(ctx)) goto fail; p = JS_VALUE_GET_PTR(val); if (!bf_is_finite(&p->num)) @@ -11668,8 +11687,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) return NULL; goto redo; case JS_TAG_OBJECT: - val = JS_ToPrimitiveFree(ctx, val, - is_bigint_mode(ctx) ? HINT_INTEGER : HINT_NUMBER); + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); if (JS_IsException(val)) return NULL; goto redo; @@ -11692,21 +11710,26 @@ static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val) if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) { return val; } else { - bf_t a_s, *a, b_s, *b; + bf_t a_s, *a, *r; int ret; - + JSValue res; + + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) + return JS_EXCEPTION; a = JS_ToBigIntFree(ctx, &a_s, val); - if (!a) + if (!a) { + JS_FreeValue(ctx, res); return JS_EXCEPTION; - b = &b_s; - bf_init(ctx->bf_ctx, b); - ret = bf_set(b, a); + } + r = JS_GetBigInt(res); + ret = bf_set(r, a); JS_FreeBigInt(ctx, a, &a_s); if (ret) { - bf_delete(b); + JS_FreeValue(ctx, res); return JS_ThrowOutOfMemory(ctx); } - return JS_NewBigInt2(ctx, b, TRUE); + return JS_CompactBigInt(ctx, res); } } @@ -11745,7 +11768,7 @@ int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) static JSBigFloat *js_new_bf(JSContext *ctx) { JSBigFloat *p; - p = js_mallocz(ctx, sizeof(*p)); + p = js_malloc(ctx, sizeof(*p)); if (!p) return NULL; p->header.ref_count = 1; @@ -11753,224 +11776,387 @@ static JSBigFloat *js_new_bf(JSContext *ctx) return p; } -/* WARNING: 'a' is freed */ -static JSValue JS_NewBigFloat(JSContext *ctx, bf_t *a) +static JSValue JS_NewBigFloat(JSContext *ctx) { - JSValue ret; JSBigFloat *p; - - p = js_new_bf(ctx); - bf_memcpy(&p->num, a); - ret = JS_MKPTR(JS_TAG_BIG_FLOAT, p); - return ret; + p = js_malloc(ctx, sizeof(*p)); + if (!p) + return JS_EXCEPTION; + p->header.ref_count = 1; + bf_init(ctx->bf_ctx, &p->num); + return JS_MKPTR(JS_TAG_BIG_FLOAT, p); } -/* WARNING: 'a' is freed */ -static JSValue JS_NewBigDecimal(JSContext *ctx, bfdec_t *a) +static JSValue JS_NewBigDecimal(JSContext *ctx) { - JSValue ret; JSBigDecimal *p; - - p = js_mallocz(ctx, sizeof(*p)); + p = js_malloc(ctx, sizeof(*p)); if (!p) return JS_EXCEPTION; p->header.ref_count = 1; bfdec_init(ctx->bf_ctx, &p->num); - bfdec_memcpy(&p->num, a); - ret = JS_MKPTR(JS_TAG_BIG_DECIMAL, p); - return ret; + return JS_MKPTR(JS_TAG_BIG_DECIMAL, p); } -/* WARNING: 'a' is freed */ -static JSValue JS_NewBigInt2(JSContext *ctx, bf_t *a, BOOL force_bigint) +static JSValue JS_NewBigInt(JSContext *ctx) { - JSValue ret; JSBigFloat *p; - int32_t v; - - if (!force_bigint && bf_get_int32(&v, a, 0) == 0) { - /* can fit in an int32 */ - ret = JS_NewInt32(ctx, v); - bf_delete(a); - } else { - p = js_new_bf(ctx); - bf_memcpy(&p->num, a); - /* normalize the zero representation */ - if (bf_is_zero(&p->num)) - p->num.sign = 0; - ret = JS_MKPTR(JS_TAG_BIG_INT, p); - } - return ret; + p = js_malloc(ctx, sizeof(*p)); + if (!p) + return JS_EXCEPTION; + p->header.ref_count = 1; + bf_init(ctx->bf_ctx, &p->num); + return JS_MKPTR(JS_TAG_BIG_INT, p); } -static JSValue JS_NewBigInt(JSContext *ctx, bf_t *a) +static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, + BOOL convert_to_safe_integer) { - return JS_NewBigInt2(ctx, a, !is_bigint_mode(ctx)); + int64_t v; + bf_t *a; + + if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT) + return val; /* fail safe */ + a = JS_GetBigInt(val); + if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 && + v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) { + JS_FreeValue(ctx, val); + return JS_NewInt64(ctx, v); + } else if (a->expn == BF_EXP_ZERO && a->sign) { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + assert(p->header.ref_count == 1); + a->sign = 0; + } + return val; } -/* return < 0 if exception, 0 if overloading method, 1 if overloading - operator called */ -static __exception int js_call_binary_op_fallback(JSContext *ctx, - JSValue *pret, - JSValueConst op1, - JSValueConst op2, - OPCodeEnum op) -{ - JSAtom op_name; - JSValue method, ret, c1, c2; - BOOL bool_result, swap_op; - JSValueConst args[2]; +/* Convert the big int to a safe integer if in math mode. normalize + the zero representation. Could also be used to convert the bigint + to a short bigint value. The reference count of the value must be + 1. Cannot fail */ +static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val) +{ + return JS_CompactBigInt1(ctx, val, is_math_mode(ctx)); +} + +/* must be kept in sync with JSOverloadableOperatorEnum */ +static const char *js_overloadable_operator_names[JS_OVOP_COUNT] = { + "+", + "-", + "*", + "/", + "%", + "**", + "|", + "&", + "^", + "<<", + ">>", + ">>>", + "==", + "<", + "pos", + "neg", + "++", + "--", + "~", +}; - bool_result = FALSE; - swap_op = FALSE; - c1 = JS_UNDEFINED; - c2 = JS_UNDEFINED; +static int get_ovop_from_opcode(OPCodeEnum op) +{ switch(op) { case OP_add: - op_name = JS_ATOM_Symbol_operatorAdd; - break; + return JS_OVOP_ADD; case OP_sub: - op_name = JS_ATOM_Symbol_operatorSub; - break; + return JS_OVOP_SUB; case OP_mul: - op_name = JS_ATOM_Symbol_operatorMul; - break; + return JS_OVOP_MUL; case OP_div: - case OP_math_div: - op_name = JS_ATOM_Symbol_operatorDiv; - break; + return JS_OVOP_DIV; case OP_mod: - op_name = JS_ATOM_Symbol_operatorMod; - break; - case OP_pow: - case OP_math_pow: - op_name = JS_ATOM_Symbol_operatorPow; - break; case OP_math_mod: - op_name = JS_ATOM_Symbol_operatorMathMod; - break; - case OP_shl: - op_name = JS_ATOM_Symbol_operatorShl; - break; - case OP_sar: - op_name = JS_ATOM_Symbol_operatorShr; - break; - case OP_and: - op_name = JS_ATOM_Symbol_operatorAnd; - break; + return JS_OVOP_MOD; + case OP_pow: + return JS_OVOP_POW; case OP_or: - op_name = JS_ATOM_Symbol_operatorOr; - break; + return JS_OVOP_OR; + case OP_and: + return JS_OVOP_AND; case OP_xor: - op_name = JS_ATOM_Symbol_operatorXor; - break; + return JS_OVOP_XOR; + case OP_shl: + return JS_OVOP_SHL; + case OP_sar: + return JS_OVOP_SAR; + case OP_shr: + return JS_OVOP_SHR; + case OP_eq: + case OP_neq: + return JS_OVOP_EQ; case OP_lt: - op_name = JS_ATOM_Symbol_operatorCmpLT; - bool_result = TRUE; - break; case OP_lte: - op_name = JS_ATOM_Symbol_operatorCmpLE; - bool_result = TRUE; - break; case OP_gt: - op_name = JS_ATOM_Symbol_operatorCmpLT; - bool_result = TRUE; - swap_op = TRUE; - break; case OP_gte: - op_name = JS_ATOM_Symbol_operatorCmpLE; - bool_result = TRUE; - swap_op = TRUE; - break; - case OP_eq: - case OP_neq: - op_name = JS_ATOM_Symbol_operatorCmpEQ; - bool_result = TRUE; - break; + return JS_OVOP_LESS; + case OP_plus: + return JS_OVOP_POS; + case OP_neg: + return JS_OVOP_NEG; + case OP_inc: + return JS_OVOP_INC; + case OP_dec: + return JS_OVOP_DEC; default: - goto fail; + abort(); } - c1 = JS_GetProperty(ctx, op1, JS_ATOM_constructor); - if (JS_IsException(c1)) +} + +/* return NULL if not present */ +static JSObject *find_binary_op(JSBinaryOperatorDef *def, + uint32_t operator_index, + JSOverloadableOperatorEnum op) +{ + JSBinaryOperatorDefEntry *ent; + int i; + for(i = 0; i < def->count; i++) { + ent = &def->tab[i]; + if (ent->operator_index == operator_index) + return ent->ops[op]; + } + return NULL; +} + +/* return -1 if exception, 0 if no operator overloading, 1 if + overloaded operator called */ +static __exception int js_call_binary_op_fallback(JSContext *ctx, + JSValue *pret, + JSValueConst op1, + JSValueConst op2, + OPCodeEnum op, + BOOL is_numeric, + int hint) +{ + JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2; + JSOperatorSetData *opset1, *opset2; + JSOverloadableOperatorEnum ovop; + JSObject *p; + JSValueConst args[2]; + + if (!ctx->allow_operator_overloading) + return 0; + + opset2_obj = JS_UNDEFINED; + opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset1_obj)) goto exception; - c2 = JS_GetProperty(ctx, op2, JS_ATOM_constructor); - if (JS_IsException(c2)) + if (JS_IsUndefined(opset1_obj)) + return 0; + opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); + if (!opset1) goto exception; - if (JS_VALUE_GET_TAG(c1) != JS_TAG_OBJECT || - JS_VALUE_GET_TAG(c2) != JS_TAG_OBJECT) - goto fail; - if (JS_VALUE_GET_OBJ(c1) == JS_VALUE_GET_OBJ(c2)) { - /* if same constructor, there is no ambiguity */ - method = JS_GetProperty(ctx, c1, op_name); + + opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset2_obj)) + goto exception; + if (JS_IsUndefined(opset2_obj)) { + JS_FreeValue(ctx, opset1_obj); + return 0; + } + opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET); + if (!opset2) + goto exception; + + if (opset1->is_primitive && opset2->is_primitive) { + JS_FreeValue(ctx, opset1_obj); + JS_FreeValue(ctx, opset2_obj); + return 0; + } + + ovop = get_ovop_from_opcode(op); + + if (opset1->operator_counter == opset2->operator_counter) { + p = opset1->self_ops[ovop]; + } else if (opset1->operator_counter > opset2->operator_counter) { + p = find_binary_op(&opset1->left, opset2->operator_counter, ovop); } else { - JSValue val; - int order1, order2; + p = find_binary_op(&opset2->right, opset1->operator_counter, ovop); + } + if (!p) { + JS_ThrowTypeError(ctx, "operator %s: no function defined", + js_overloadable_operator_names[ovop]); + goto exception; + } - /* different constructors: we use a user-defined ordering */ - val = JS_GetProperty(ctx, c1, JS_ATOM_Symbol_operatorOrder); - if (JS_IsException(val)) - goto exception; - if (JS_IsUndefined(val)) - goto undef_order; - if (JS_ToInt32Free(ctx, &order1, val)) - goto exception; - val = JS_GetProperty(ctx, c2, JS_ATOM_Symbol_operatorOrder); - if (JS_IsException(val)) - goto exception; - if (JS_IsUndefined(val)) { - undef_order: - JS_FreeValue(ctx, c1); - JS_FreeValue(ctx, c2); - *pret = JS_UNDEFINED; - return 0; + if (opset1->is_primitive) { + if (is_numeric) { + new_op1 = JS_ToNumeric(ctx, op1); + } else { + new_op1 = JS_ToPrimitive(ctx, op1, hint); } - if (JS_ToInt32Free(ctx, &order2, val)) - goto exception; - /* ambiguous priority: error */ - if (order1 == order2) { - JS_ThrowTypeError(ctx, "operator_order is identical in both constructors"); + if (JS_IsException(new_op1)) goto exception; - } - if (order1 > order2) { - val = c1; + } else { + new_op1 = JS_DupValue(ctx, op1); + } + + if (opset2->is_primitive) { + if (is_numeric) { + new_op2 = JS_ToNumeric(ctx, op2); } else { - val = c2; + new_op2 = JS_ToPrimitive(ctx, op2, hint); } - method = JS_GetProperty(ctx, val, op_name); - } - JS_FreeValue(ctx, c1); - JS_FreeValue(ctx, c2); - c1 = JS_UNDEFINED; - c2 = JS_UNDEFINED; - if (JS_IsException(method)) - goto exception; - if (JS_IsUndefined(method) || JS_IsNull(method)) { - *pret = JS_UNDEFINED; - return 0; + if (JS_IsException(new_op2)) { + JS_FreeValue(ctx, new_op1); + goto exception; + } + } else { + new_op2 = JS_DupValue(ctx, op2); } - if (swap_op) { - args[0] = op2; - args[1] = op1; + + /* XXX: could apply JS_ToPrimitive() if primitive type so that the + operator function does not get a value object */ + + method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) { + args[0] = new_op2; + args[1] = new_op1; } else { - args[0] = op1; - args[1] = op2; + args[0] = new_op1; + args[1] = new_op2; } ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args); + JS_FreeValue(ctx, new_op1); + JS_FreeValue(ctx, new_op2); if (JS_IsException(ret)) goto exception; - if (bool_result) { + if (ovop == JS_OVOP_EQ) { BOOL res = JS_ToBoolFree(ctx, ret); if (op == OP_neq) res ^= 1; ret = JS_NewBool(ctx, res); + } else if (ovop == JS_OVOP_LESS) { + if (JS_IsUndefined(ret)) { + ret = JS_FALSE; + } else { + BOOL res = JS_ToBoolFree(ctx, ret); + if (op == OP_lte || op == OP_gte) + res ^= 1; + ret = JS_NewBool(ctx, res); + } } + JS_FreeValue(ctx, opset1_obj); + JS_FreeValue(ctx, opset2_obj); *pret = ret; return 1; - fail: - JS_ThrowTypeError(ctx, "invalid types for binary operator"); exception: - JS_FreeValue(ctx, c1); - JS_FreeValue(ctx, c2); + JS_FreeValue(ctx, opset1_obj); + JS_FreeValue(ctx, opset2_obj); + *pret = JS_UNDEFINED; + return -1; +} + +/* try to call the operation on the operatorSet field of 'obj'. Only + used for "/" and "**" on the BigInt prototype in math mode */ +static __exception int js_call_binary_op_simple(JSContext *ctx, + JSValue *pret, + JSValueConst obj, + JSValueConst op1, + JSValueConst op2, + OPCodeEnum op) +{ + JSValue opset1_obj, method, ret, new_op1, new_op2; + JSOperatorSetData *opset1; + JSOverloadableOperatorEnum ovop; + JSObject *p; + JSValueConst args[2]; + + opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset1_obj)) + goto exception; + if (JS_IsUndefined(opset1_obj)) + return 0; + opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); + if (!opset1) + goto exception; + ovop = get_ovop_from_opcode(op); + + p = opset1->self_ops[ovop]; + if (!p) { + JS_FreeValue(ctx, opset1_obj); + return 0; + } + + new_op1 = JS_ToNumeric(ctx, op1); + if (JS_IsException(new_op1)) + goto exception; + new_op2 = JS_ToNumeric(ctx, op2); + if (JS_IsException(new_op2)) { + JS_FreeValue(ctx, new_op1); + goto exception; + } + + method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + args[0] = new_op1; + args[1] = new_op2; + ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args); + JS_FreeValue(ctx, new_op1); + JS_FreeValue(ctx, new_op2); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, opset1_obj); + *pret = ret; + return 1; + exception: + JS_FreeValue(ctx, opset1_obj); + *pret = JS_UNDEFINED; + return -1; +} + +/* return -1 if exception, 0 if no operator overloading, 1 if + overloaded operator called */ +static __exception int js_call_unary_op_fallback(JSContext *ctx, + JSValue *pret, + JSValueConst op1, + OPCodeEnum op) +{ + JSValue opset1_obj, method, ret; + JSOperatorSetData *opset1; + JSOverloadableOperatorEnum ovop; + JSObject *p; + + if (!ctx->allow_operator_overloading) + return 0; + + opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset1_obj)) + goto exception; + if (JS_IsUndefined(opset1_obj)) + return 0; + opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); + if (!opset1) + goto exception; + if (opset1->is_primitive) { + JS_FreeValue(ctx, opset1_obj); + return 0; + } + + ovop = get_ovop_from_opcode(op); + + p = opset1->self_ops[ovop]; + if (!p) { + JS_ThrowTypeError(ctx, "no overloaded operator %s", + js_overloadable_operator_names[ovop]); + goto exception; + } + method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + ret = JS_CallFree(ctx, method, JS_UNDEFINED, 1, &op1); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, opset1_obj); + *pret = ret; + return 1; + exception: + JS_FreeValue(ctx, opset1_obj); *pret = JS_UNDEFINED; return -1; } @@ -11993,16 +12179,22 @@ static JSValue throw_bf_exception(JSContext *ctx, int status) static int js_unary_arith_bigint(JSContext *ctx, JSValue *pres, OPCodeEnum op, JSValue op1) { - bf_t a_s, r_s, *r = &r_s, *a; + bf_t a_s, *r, *a; int ret, v; + JSValue res; - if (op == OP_plus && !is_bigint_mode(ctx)) { + if (op == OP_plus && !is_math_mode(ctx)) { JS_ThrowTypeError(ctx, "bigint argument with unary +"); JS_FreeValue(ctx, op1); return -1; } + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) { + JS_FreeValue(ctx, op1); + return -1; + } + r = JS_GetBigInt(res); a = JS_ToBigInt(ctx, &a_s, op1); - bf_init(ctx->bf_ctx, r); ret = 0; switch(op) { case OP_inc: @@ -12027,28 +12219,35 @@ static int js_unary_arith_bigint(JSContext *ctx, JS_FreeBigInt(ctx, a, &a_s); JS_FreeValue(ctx, op1); if (unlikely(ret)) { - bf_delete(r); + JS_FreeValue(ctx, res); throw_bf_exception(ctx, ret); return -1; } - *pres = JS_NewBigInt(ctx, r); + res = JS_CompactBigInt(ctx, res); + *pres = res; return 0; } static int js_unary_arith_bigfloat(JSContext *ctx, JSValue *pres, OPCodeEnum op, JSValue op1) { - bf_t a_s, r_s, *r = &r_s, *a; + bf_t a_s, *r, *a; int ret, v; + JSValue res; - if (op == OP_plus && !is_bigint_mode(ctx)) { + if (op == OP_plus && !is_math_mode(ctx)) { JS_ThrowTypeError(ctx, "bigfloat argument with unary +"); JS_FreeValue(ctx, op1); return -1; } + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + JS_FreeValue(ctx, op1); + return -1; + } + r = JS_GetBigFloat(res); a = JS_ToBigFloat(ctx, &a_s, op1); - bf_init(ctx->bf_ctx, r); ret = 0; switch(op) { case OP_inc: @@ -12070,28 +12269,34 @@ static int js_unary_arith_bigfloat(JSContext *ctx, bf_delete(a); JS_FreeValue(ctx, op1); if (unlikely(ret & BF_ST_MEM_ERROR)) { - bf_delete(r); + JS_FreeValue(ctx, res); throw_bf_exception(ctx, ret); return -1; } - *pres = JS_NewBigFloat(ctx, r); + *pres = res; return 0; } static int js_unary_arith_bigdecimal(JSContext *ctx, JSValue *pres, OPCodeEnum op, JSValue op1) { - bfdec_t r_s, *r = &r_s, *a; + bfdec_t *r, *a; int ret, v; - - if (op == OP_plus && !is_bigint_mode(ctx)) { + JSValue res; + + if (op == OP_plus && !is_math_mode(ctx)) { JS_ThrowTypeError(ctx, "bigdecimal argument with unary +"); JS_FreeValue(ctx, op1); return -1; } + res = JS_NewBigDecimal(ctx); + if (JS_IsException(res)) { + JS_FreeValue(ctx, op1); + return -1; + } + r = JS_GetBigDecimal(res); a = JS_ToBigDecimal(ctx, op1); - bfdec_init(ctx->bf_ctx, r); ret = 0; switch(op) { case OP_inc: @@ -12111,11 +12316,11 @@ static int js_unary_arith_bigdecimal(JSContext *ctx, } JS_FreeValue(ctx, op1); if (unlikely(ret)) { - bfdec_delete(r); + JS_FreeValue(ctx, res); throw_bf_exception(ctx, ret); return -1; } - *pres = JS_NewBigDecimal(ctx, r); + *pres = res; return 0; } @@ -12123,112 +12328,93 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { - JSValue op1, val, method; - BOOL is_legacy; - JSAtom op_name; - int v; + JSValue op1, val; + int v, ret; uint32_t tag; op1 = sp[-1]; /* fast path for float64 */ if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) goto handle_float64; - if (JS_IsObject(op1) && ctx->bignum_ext) { - switch(op) { - case OP_plus: - op_name = JS_ATOM_Symbol_operatorPlus; - break; - case OP_neg: - op_name = JS_ATOM_Symbol_operatorNeg; - break; - case OP_inc: - op_name = JS_ATOM_Symbol_operatorInc; - break; - case OP_dec: - op_name = JS_ATOM_Symbol_operatorDec; - break; - default: - abort(); - } - method = JS_GetProperty(ctx, op1, op_name); - if (JS_IsException(method)) - return -1; - if (JS_IsUndefined(method) || JS_IsNull(method)) - goto to_number; - val = JS_CallFree(ctx, method, op1, 0, NULL); - if (JS_IsException(val)) + if (JS_IsObject(op1)) { + ret = js_call_unary_op_fallback(ctx, &val, op1, op); + if (ret < 0) return -1; - JS_FreeValue(ctx, op1); - sp[-1] = val; - } else { - to_number: - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) - goto exception; - is_legacy = is_bigint_mode(ctx) ^ 1; - tag = JS_VALUE_GET_TAG(op1); - switch(tag) { - case JS_TAG_INT: - { - int64_t v64; - v64 = JS_VALUE_GET_INT(op1); - switch(op) { - case OP_inc: - case OP_dec: - v = 2 * (op - OP_dec) - 1; - v64 += v; - break; - case OP_plus: - break; - case OP_neg: - if (v64 == 0 && is_legacy) { - sp[-1] = __JS_NewFloat64(ctx, -0.0); - return 0; - } else { - v64 = -v64; - } - break; - default: - abort(); + if (ret) { + JS_FreeValue(ctx, op1); + sp[-1] = val; + return 0; + } + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) + goto exception; + tag = JS_VALUE_GET_TAG(op1); + switch(tag) { + case JS_TAG_INT: + { + int64_t v64; + v64 = JS_VALUE_GET_INT(op1); + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + v64 += v; + break; + case OP_plus: + break; + case OP_neg: + if (v64 == 0) { + sp[-1] = __JS_NewFloat64(ctx, -0.0); + return 0; + } else { + v64 = -v64; } - sp[-1] = JS_NewInt64(ctx, v64); + break; + default: + abort(); } - break; - case JS_TAG_BIG_INT: - if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1)) - goto exception; - break; - case JS_TAG_BIG_FLOAT: - if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1)) - goto exception; - break; - case JS_TAG_BIG_DECIMAL: - if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1)) - goto exception; - break; - default: - handle_float64: - { - double d; - d = JS_VALUE_GET_FLOAT64(op1); - switch(op) { - case OP_inc: - case OP_dec: - v = 2 * (op - OP_dec) - 1; - d += v; - break; - case OP_plus: - break; - case OP_neg: - d = -d; - break; - default: - abort(); - } - sp[-1] = __JS_NewFloat64(ctx, d); + sp[-1] = JS_NewInt64(ctx, v64); + } + break; + case JS_TAG_BIG_INT: + handle_bigint: + if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1)) + goto exception; + break; + case JS_TAG_BIG_FLOAT: + if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1)) + goto exception; + break; + case JS_TAG_BIG_DECIMAL: + if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1)) + goto exception; + break; + default: + handle_float64: + { + double d; + if (is_math_mode(ctx)) + goto handle_bigint; + d = JS_VALUE_GET_FLOAT64(op1); + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + d += v; + break; + case OP_plus: + break; + case OP_neg: + d = -d; + break; + default: + abort(); } - break; + sp[-1] = __JS_NewFloat64(ctx, d); } + break; } return 0; exception: @@ -12255,39 +12441,33 @@ static __exception int js_post_inc_slow(JSContext *ctx, static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) { - JSValue op1, method, val; - + JSValue op1, val; + int ret; + op1 = sp[-1]; if (JS_IsObject(op1)) { - if (!ctx->bignum_ext) - goto to_number; - method = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorNot); - if (JS_IsException(method)) - return -1; - if (JS_IsUndefined(method) || JS_IsNull(method)) - goto to_number; - val = JS_CallFree(ctx, method, op1, 0, NULL); - if (JS_IsException(val)) + ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not); + if (ret < 0) return -1; - JS_FreeValue(ctx, op1); - sp[-1] = val; - } else { - if (JS_IsString(op1)) { - to_number: - op1 = JS_ToNumberHintFree(ctx, op1, TON_FLAG_INTEGER); - if (JS_IsException(op1)) - goto exception; - } - if (is_bigint_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { - if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1)) - goto exception; - } else { - int32_t v1; - if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) - goto exception; - sp[-1] = JS_NewInt32(ctx, ~v1); + if (ret) { + JS_FreeValue(ctx, op1); + sp[-1] = val; + return 0; } } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) + goto exception; + if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { + if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1)) + goto exception; + } else { + int32_t v1; + if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) + goto exception; + sp[-1] = JS_NewInt32(ctx, ~v1); + } return 0; exception: sp[-1] = JS_UNDEFINED; @@ -12297,12 +12477,19 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op, JSValue *pres, JSValue op1, JSValue op2) { - bf_t a_s, b_s, r_s, *r, *a, *b; - int ret, rnd_mode; + bf_t a_s, b_s, *r, *a, *b; + int ret; + JSValue res; + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return -1; + } + r = JS_GetBigFloat(res); a = JS_ToBigFloat(ctx, &a_s, op1); b = JS_ToBigFloat(ctx, &b_s, op2); - r = &r_s; bf_init(ctx->bf_ctx, r); switch(op) { case OP_add: @@ -12314,29 +12501,21 @@ static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op, case OP_mul: ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); break; - case OP_math_div: case OP_div: ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); break; case OP_math_mod: /* Euclidian remainder */ - rnd_mode = BF_DIVREM_EUCLIDIAN; - goto do_mod; + ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, + BF_DIVREM_EUCLIDIAN); + break; case OP_mod: - rnd_mode = BF_RNDZ; - do_mod: - { - bf_t q_s, *q = &q_s; - bf_init(ctx->bf_ctx, q); - ret = bf_divrem(q, r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, - rnd_mode); - bf_delete(q); - } + ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, + BF_RNDZ); break; case OP_pow: - case OP_math_pow: ret = bf_pow(r, a, b, ctx->fp_env.prec, - ctx->fp_env.flags | BF_POW_JS_QUICKS); + ctx->fp_env.flags | BF_POW_JS_QUIRKS); break; default: abort(); @@ -12348,21 +12527,24 @@ static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op, JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); if (unlikely(ret & BF_ST_MEM_ERROR)) { - bf_delete(r); + JS_FreeValue(ctx, res); throw_bf_exception(ctx, ret); return -1; } - *pres = JS_NewBigFloat(ctx, r); + *pres = res; return 0; } static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, JSValue *pres, JSValue op1, JSValue op2) { - bf_t a_s, b_s, r_s, *r, *a, *b; - int ret, rnd_mode; + bf_t a_s, b_s, *r, *a, *b; + int ret; JSValue res; + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) + goto fail; a = JS_ToBigInt(ctx, &a_s, op1); if (!a) goto fail; @@ -12371,8 +12553,7 @@ static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, JS_FreeBigInt(ctx, a, &a_s); goto fail; } - r = &r_s; - bf_init(ctx->bf_ctx, r); + r = JS_GetBigInt(res); ret = 0; switch(op) { case OP_add: @@ -12384,73 +12565,75 @@ static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, case OP_mul: ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ); break; - case OP_math_div: - goto op_fallback; case OP_div: - if (!is_bigint_mode(ctx)) { + if (!is_math_mode(ctx)) { bf_t rem_s, *rem = &rem_s; bf_init(ctx->bf_ctx, rem); ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ); bf_delete(rem); } else { - bf_div(r, a, b, 53, bf_set_exp_bits(11) | - BF_RNDN | BF_FLAG_SUBNORMAL); - goto float64_result; + goto math_mode_div_pow; } break; case OP_math_mod: /* Euclidian remainder */ - rnd_mode = BF_DIVREM_EUCLIDIAN; - goto do_int_mod; + ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ, + BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP; + break; case OP_mod: - rnd_mode = BF_RNDZ; - do_int_mod: - { - bf_t q_s, *q = &q_s; - bf_init(ctx->bf_ctx, q); - ret = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, - rnd_mode) & BF_ST_INVALID_OP; - bf_delete(q); - } + ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ, + BF_RNDZ) & BF_ST_INVALID_OP; break; case OP_pow: - case OP_math_pow: if (b->sign) { - if (!is_bigint_mode(ctx)) { + if (!is_math_mode(ctx)) { ret = BF_ST_INVALID_OP; - } else if (op == OP_math_pow) { - op_fallback: - bf_delete(r); + } else { + math_mode_div_pow: + JS_FreeValue(ctx, res); + ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op); + if (ret != 0) { + JS_FreeBigInt(ctx, a, &a_s); + JS_FreeBigInt(ctx, b, &b_s); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (ret < 0) { + return -1; + } else { + *pres = res; + return 0; + } + } + /* if no BigInt power operator defined, return a + bigfloat */ + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + JS_FreeBigInt(ctx, a, &a_s); + JS_FreeBigInt(ctx, b, &b_s); + goto fail; + } + r = JS_GetBigFloat(res); + if (op == OP_div) { + ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR; + } else { + ret = bf_pow(r, a, b, ctx->fp_env.prec, + ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR; + } JS_FreeBigInt(ctx, a, &a_s); JS_FreeBigInt(ctx, b, &b_s); - ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op); JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); - if (ret < 0) { - return -1; - } else if (ret == 0) { - JS_ThrowTypeError(ctx, "operator must be defined for exact division or power"); + if (unlikely(ret)) { + JS_FreeValue(ctx, res); + throw_bf_exception(ctx, ret); return -1; } *pres = res; return 0; - } else { - double dr; - bf_pow(r, a, b, 53, bf_set_exp_bits(11) | - BF_RNDN | BF_FLAG_SUBNORMAL); - float64_result: - bf_get_float64(r, &dr, BF_RNDN); - bf_delete(r); - JS_FreeBigInt(ctx, a, &a_s); - JS_FreeBigInt(ctx, b, &b_s); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - *pres = __JS_NewFloat64(ctx, dr); - return 0; } } else { - ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUICKS); + ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS); } break; @@ -12494,13 +12677,14 @@ static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); if (unlikely(ret)) { - bf_delete(r); + JS_FreeValue(ctx, res); throw_bf_exception(ctx, ret); return -1; } - *pres = JS_NewBigInt(ctx, r); + *pres = JS_CompactBigInt(ctx, res); return 0; fail: + JS_FreeValue(ctx, res); JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); return -1; @@ -12536,18 +12720,21 @@ static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b) static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op, JSValue *pres, JSValue op1, JSValue op2) { - bfdec_t r_s, *r, *a, *b; - int ret, rnd_mode; - - /* big decimal result */ + bfdec_t *r, *a, *b; + int ret; + JSValue res; + + res = JS_NewBigDecimal(ctx); + if (JS_IsException(res)) + goto fail; + r = JS_GetBigDecimal(res); + a = JS_ToBigDecimal(ctx, op1); if (!a) goto fail; b = JS_ToBigDecimal(ctx, op2); if (!b) goto fail; - r = &r_s; - bfdec_init(ctx->bf_ctx, r); switch(op) { case OP_add: ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ); @@ -12558,26 +12745,17 @@ static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op, case OP_mul: ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ); break; - case OP_math_div: case OP_div: ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ); break; case OP_math_mod: /* Euclidian remainder */ - rnd_mode = BF_DIVREM_EUCLIDIAN; - goto do_mod_dec; + ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN); + break; case OP_mod: - rnd_mode = BF_RNDZ; - do_mod_dec: - { - bfdec_t q_s, *q = &q_s; - bfdec_init(ctx->bf_ctx, q); - ret = bfdec_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, rnd_mode); - bfdec_delete(q); - } + ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ); break; case OP_pow: - case OP_math_pow: ret = js_bfdec_pow(r, a, b); break; default: @@ -12586,13 +12764,14 @@ static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op, JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); if (unlikely(ret)) { - bfdec_delete(r); + JS_FreeValue(ctx, res); throw_bf_exception(ctx, ret); return -1; } - *pres = JS_NewBigDecimal(ctx, r); + *pres = res; return 0; fail: + JS_FreeValue(ctx, res); JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); return -1; @@ -12602,7 +12781,6 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s OPCodeEnum op) { JSValue op1, op2, res; - BOOL is_legacy; uint32_t tag1, tag2; int ret; double d1, d2; @@ -12619,12 +12797,11 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s } /* try to call an overloaded operator */ - if (((tag1 == JS_TAG_OBJECT && - (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || - (tag2 == JS_TAG_OBJECT && - (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) && - ctx->bignum_ext) { - ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op); + if ((tag1 == JS_TAG_OBJECT && + (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || + (tag2 == JS_TAG_OBJECT && + (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { + ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0); if (ret != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -12655,23 +12832,23 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s int64_t v; v1 = JS_VALUE_GET_INT(op1); v2 = JS_VALUE_GET_INT(op2); - is_legacy = is_bigint_mode(ctx) ^ 1; switch(op) { case OP_sub: v = (int64_t)v1 - (int64_t)v2; break; case OP_mul: v = (int64_t)v1 * (int64_t)v2; - if (is_legacy && v == 0 && (v1 | v2) < 0) { + if (is_math_mode(ctx) && + (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER)) + goto handle_bigint; + if (v == 0 && (v1 | v2) < 0) { sp[-2] = __JS_NewFloat64(ctx, -0.0); return 0; } break; - case OP_math_div: - if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) - goto exception; - return 0; case OP_div: + if (is_math_mode(ctx)) + goto handle_bigint; sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2); return 0; case OP_math_mod: @@ -12688,26 +12865,19 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s } break; case OP_mod: - if (is_legacy && (v1 < 0 || v2 <= 0)) { + if (v1 < 0 || v2 <= 0) { sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2)); return 0; } else { - if (unlikely(v2 == 0)) { - throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO); - goto exception; - } v = (int64_t)v1 % (int64_t)v2; } break; case OP_pow: - case OP_math_pow: - if (is_legacy) { + if (!is_math_mode(ctx)) { sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2)); return 0; } else { - if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) - goto exception; - return 0; + goto handle_bigint; } break; default: @@ -12720,6 +12890,10 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2)) goto exception; + } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + handle_bigint: + if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) + goto exception; } else if (tag1 == JS_TAG_FLOAT64 || tag2 == JS_TAG_FLOAT64) { double dr; /* float64 result */ @@ -12730,6 +12904,8 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s if (JS_ToFloat64Free(ctx, &d2, op2)) goto exception; handle_float64: + if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2)) + goto handle_bigint; switch(op) { case OP_sub: dr = d1 - d2; @@ -12738,7 +12914,6 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s dr = d1 * d2; break; case OP_div: - case OP_math_div: dr = d1 / d2; break; case OP_mod: @@ -12752,16 +12927,12 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s dr += d2; break; case OP_pow: - case OP_math_pow: dr = js_pow(d1, d2); break; default: abort(); } sp[-2] = __JS_NewFloat64(ctx, dr); - } else { - if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) - goto exception; } return 0; exception: @@ -12792,12 +12963,14 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { /* try to call an overloaded operator */ - if (((tag1 == JS_TAG_OBJECT && - (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || - (tag2 == JS_TAG_OBJECT && - (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) && - ctx->bignum_ext) { - ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add); + if ((tag1 == JS_TAG_OBJECT && + (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED && + tag2 != JS_TAG_STRING)) || + (tag2 == JS_TAG_OBJECT && + (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED && + tag1 != JS_TAG_STRING))) { + ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add, + FALSE, HINT_NONE); if (ret != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -12886,6 +13059,7 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, JSValue op1, op2, res; int ret; uint32_t tag1, tag2; + uint32_t v1, v2, r; op1 = sp[-2]; op2 = sp[-1]; @@ -12893,12 +13067,11 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, tag2 = JS_VALUE_GET_NORM_TAG(op2); /* try to call an overloaded operator */ - if (((tag1 == JS_TAG_OBJECT && - (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || - (tag2 == JS_TAG_OBJECT && - (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) && - ctx->bignum_ext) { - ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op); + if ((tag1 == JS_TAG_OBJECT && + (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || + (tag2 == JS_TAG_OBJECT && + (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { + ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0); if (ret != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -12911,63 +13084,60 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, } } - op1 = JS_ToNumberHintFree(ctx, op1, TON_FLAG_INTEGER); + op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); goto exception; } - op2 = JS_ToNumberHintFree(ctx, op2, TON_FLAG_INTEGER); + op2 = JS_ToNumericFree(ctx, op2); if (JS_IsException(op2)) { JS_FreeValue(ctx, op1); goto exception; } - if (!is_bigint_mode(ctx)) { - uint32_t v1, v2, r; + if (is_math_mode(ctx)) + goto bigint_op; - tag1 = JS_VALUE_GET_TAG(op1); - tag2 = JS_VALUE_GET_TAG(op2); - if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { - if (tag1 != tag2) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - JS_ThrowTypeError(ctx, "both operands must be bigint"); - goto exception; - } else { - goto bigint_op; - } + tag1 = JS_VALUE_GET_TAG(op1); + tag2 = JS_VALUE_GET_TAG(op2); + if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + if (tag1 != tag2) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + JS_ThrowTypeError(ctx, "both operands must be bigint"); + goto exception; } else { - if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { - JS_FreeValue(ctx, op2); - goto exception; - } - if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) + bigint_op: + if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) goto exception; - switch(op) { - case OP_shl: - r = v1 << (v2 & 0x1f); - break; - case OP_sar: - r = (int)v1 >> (v2 & 0x1f); - break; - case OP_and: - r = v1 & v2; - break; - case OP_or: - r = v1 | v2; - break; - case OP_xor: - r = v1 ^ v2; - break; - default: - abort(); - } - sp[-2] = JS_NewInt32(ctx, r); } } else { - bigint_op: - if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) goto exception; + switch(op) { + case OP_shl: + r = v1 << (v2 & 0x1f); + break; + case OP_sar: + r = (int)v1 >> (v2 & 0x1f); + break; + case OP_and: + r = v1 & v2; + break; + case OP_or: + r = v1 | v2; + break; + case OP_xor: + r = v1 ^ v2; + break; + default: + abort(); + } + sp[-2] = JS_NewInt32(ctx, r); } return 0; exception: @@ -13081,12 +13251,12 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); /* try to call an overloaded operator */ - if (((tag1 == JS_TAG_OBJECT && - (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || - (tag2 == JS_TAG_OBJECT && - (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) && - ctx->bignum_ext) { - res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op); + if ((tag1 == JS_TAG_OBJECT && + (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || + (tag2 == JS_TAG_OBJECT && + (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { + res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op, + FALSE, HINT_NUMBER); if (res != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -13165,7 +13335,7 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, } else { if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) || (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) && - !is_bigint_mode(ctx)) { + !is_math_mode(ctx)) { if (tag1 == JS_TAG_STRING) { op1 = JS_StringToBigInt(ctx, op1); if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) @@ -13271,10 +13441,11 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, goto exception; } } else if (tag1 == tag2) { - if (tag1 == JS_TAG_OBJECT && ctx->bignum_ext) { + if (tag1 == JS_TAG_OBJECT) { /* try the fallback operator */ res = js_call_binary_op_fallback(ctx, &ret, op1, op2, - is_neq ? OP_neq : OP_eq); + is_neq ? OP_neq : OP_eq, + FALSE, HINT_NONE); if (res != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -13294,7 +13465,7 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, (tag2 == JS_TAG_STRING && tag_is_number(tag1))) { if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) && - !is_bigint_mode(ctx)) { + !is_math_mode(ctx)) { if (tag1 == JS_TAG_STRING) { op1 = JS_StringToBigInt(ctx, op1); if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) @@ -13336,7 +13507,8 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, /* try the fallback operator */ res = js_call_binary_op_fallback(ctx, &ret, op1, op2, - is_neq ? OP_neq : OP_eq); + is_neq ? OP_neq : OP_eq, + FALSE, HINT_NONE); if (res != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -13391,7 +13563,7 @@ static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) goto exception; } /* XXX: could forbid >>> in bignum mode */ - if (!is_bigint_mode(ctx) && + if (!is_math_mode(ctx) && (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) { JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>"); @@ -13433,34 +13605,38 @@ static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp) { - bf_t a_s, *a, r_s, *r = &r_s; - JSValue op1, op2; - slimb_t e; + bf_t a_s, *a, *r; + JSValue op1, op2, res; + int64_t e; int ret; + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) + return -1; + r = JS_GetBigFloat(res); op1 = sp[-2]; op2 = sp[-1]; a = JS_ToBigFloat(ctx, &a_s, op1); if (!a) return -1; -#if LIMB_BITS == 32 - ret = JS_ToInt32(ctx, &e, op2); -#else - ret = JS_ToInt64(ctx, &e, op2); -#endif + if (JS_IsBigInt(ctx, op2)) { + ret = JS_ToBigInt64(ctx, &e, op2); + } else { + ret = JS_ToInt64(ctx, &e, op2); + } if (ret) { if (a == &a_s) bf_delete(a); + JS_FreeValue(ctx, res); return -1; } - bf_init(ctx->bf_ctx, r); bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags); if (a == &a_s) bf_delete(a); JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); - sp[-2] = JS_NewBigFloat(ctx, r); + sp[-2] = res; return 0; } @@ -13882,22 +14058,9 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, d2 = JS_VALUE_GET_INT(op2); goto number_test; } else if (tag2 == JS_TAG_FLOAT64) { -#ifdef CONFIG_BIGNUM - if (is_bigint_mode(ctx)) { - res = FALSE; - } else -#endif - { - d2 = JS_VALUE_GET_FLOAT64(op2); - goto number_test; - } - } else -#ifdef CONFIG_BIGNUM - if (tag2 == JS_TAG_BIG_INT && is_bigint_mode(ctx)) { - goto bigint_test; - } else -#endif - { + d2 = JS_VALUE_GET_FLOAT64(op2); + goto number_test; + } else { res = FALSE; } break; @@ -13905,11 +14068,7 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, d1 = JS_VALUE_GET_FLOAT64(op1); if (tag2 == JS_TAG_FLOAT64) { d2 = JS_VALUE_GET_FLOAT64(op2); - } else if (tag2 == JS_TAG_INT -#ifdef CONFIG_BIGNUM - && !is_bigint_mode(ctx) -#endif - ) { + } else if (tag2 == JS_TAG_INT) { d2 = JS_VALUE_GET_INT(op2); } else { res = FALSE; @@ -13936,15 +14095,10 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, case JS_TAG_BIG_INT: { bf_t a_s, *a, b_s, *b; - if (tag1 == tag2) { - /* OK */ - } else if (tag2 == JS_TAG_INT && is_bigint_mode(ctx)) { - /* OK */ - } else { + if (tag1 != tag2) { res = FALSE; break; } - bigint_test: a = JS_ToBigFloat(ctx, &a_s, op1); b = JS_ToBigFloat(ctx, &b_s, op2); res = bf_cmp_eq(a, b); @@ -14100,30 +14254,20 @@ static __exception int js_operator_typeof(JSContext *ctx, JSValue op1) tag = JS_VALUE_GET_NORM_TAG(op1); switch(tag) { #ifdef CONFIG_BIGNUM - case JS_TAG_INT: - if (is_bigint_mode(ctx)) - atom = JS_ATOM_bigint; - else - atom = JS_ATOM_number; - break; case JS_TAG_BIG_INT: atom = JS_ATOM_bigint; break; - case JS_TAG_FLOAT64: - atom = JS_ATOM_number; - break; case JS_TAG_BIG_FLOAT: atom = JS_ATOM_bigfloat; break; case JS_TAG_BIG_DECIMAL: atom = JS_ATOM_bigdecimal; break; -#else +#endif case JS_TAG_INT: case JS_TAG_FLOAT64: atom = JS_ATOM_number; break; -#endif case JS_TAG_UNDEFINED: atom = JS_ATOM_undefined; break; @@ -15288,7 +15432,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, /* we only propagate the bignum mode as some runtime functions test it */ if (prev_sf) - sf->js_mode = prev_sf->js_mode & JS_MODE_BIGINT; + sf->js_mode = prev_sf->js_mode & JS_MODE_MATH; else sf->js_mode = 0; #else @@ -17315,11 +17459,12 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, v1 = JS_VALUE_GET_INT(op1); v2 = JS_VALUE_GET_INT(op2); r = (int64_t)v1 * v2; -#ifdef CONFIG_BIGNUM - if (unlikely((int)r != r) || (r == 0 && !is_bigint_mode(ctx))) - goto binary_arith_slow; -#else if (unlikely((int)r != r)) { +#ifdef CONFIG_BIGNUM + if (unlikely(sf->js_mode & JS_MODE_MATH) && + (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER)) + goto binary_arith_slow; +#endif d = (double)r; goto mul_fp_res; } @@ -17328,14 +17473,15 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, d = -0.0; goto mul_fp_res; } -#endif sp[-2] = JS_NewInt32(ctx, r); sp--; } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { +#ifdef CONFIG_BIGNUM + if (unlikely(sf->js_mode & JS_MODE_MATH)) + goto binary_arith_slow; +#endif d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); -#ifndef CONFIG_BIGNUM mul_fp_res: -#endif sp[-2] = __JS_NewFloat64(ctx, d); sp--; } else { @@ -17343,11 +17489,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } } BREAK; -#ifdef CONFIG_BIGNUM - CASE(OP_math_div): - CASE(OP_div): - goto binary_arith_slow; -#else CASE(OP_div): { JSValue op1, op2; @@ -17355,6 +17496,8 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, op2 = sp[-1]; if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { int v1, v2; + if (unlikely(sf->js_mode & JS_MODE_MATH)) + goto binary_arith_slow; v1 = JS_VALUE_GET_INT(op1); v2 = JS_VALUE_GET_INT(op2); sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2); @@ -17364,7 +17507,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } } BREAK; -#endif CASE(OP_mod): #ifdef CONFIG_BIGNUM CASE(OP_math_mod): @@ -17390,9 +17532,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } BREAK; CASE(OP_pow): -#ifdef CONFIG_BIGNUM - CASE(OP_math_pow): -#endif binary_arith_slow: if (js_binary_arith_slow(ctx, sp, opcode)) goto exception; @@ -17422,11 +17561,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, tag = JS_VALUE_GET_TAG(op1); if (tag == JS_TAG_INT) { val = JS_VALUE_GET_INT(op1); -#ifdef CONFIG_BIGNUM - if (unlikely(val == INT32_MIN) || - (val == 0 && !is_bigint_mode(ctx))) - goto slow_neg; -#else /* Note: -0 cannot be expressed as integer */ if (unlikely(val == 0)) { d = -0.0; @@ -17436,18 +17570,12 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, d = -(double)val; goto neg_fp_res; } -#endif sp[-1] = JS_NewInt32(ctx, -val); } else if (JS_TAG_IS_FLOAT64(tag)) { d = -JS_VALUE_GET_FLOAT64(op1); -#ifndef CONFIG_BIGNUM neg_fp_res: -#endif sp[-1] = __JS_NewFloat64(ctx, d); } else { -#ifdef CONFIG_BIGNUM - slow_neg: -#endif if (js_unary_arith_slow(ctx, sp, opcode)) goto exception; } @@ -17560,7 +17688,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, #ifdef CONFIG_BIGNUM { int64_t r; - if (is_bigint_mode(ctx)) { + if (unlikely(sf->js_mode & JS_MODE_MATH)) { if (v2 > 0x1f) goto shl_slow; r = (int64_t)v1 << v2; @@ -17616,7 +17744,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, v2 = JS_VALUE_GET_INT(op2); #ifdef CONFIG_BIGNUM if (unlikely(v2 > 0x1f)) { - if (is_bigint_mode(ctx)) + if (unlikely(sf->js_mode & JS_MODE_MATH)) goto sar_slow; else v2 &= 0x1f; @@ -20182,7 +20310,7 @@ static __exception int next_token(JSParseState *s) ATOD_ACCEPT_UNDERSCORES; #ifdef CONFIG_BIGNUM flags |= ATOD_ACCEPT_SUFFIX; - if (s->cur_func->js_mode & JS_MODE_BIGINT) { + if (s->cur_func->js_mode & JS_MODE_MATH) { flags |= ATOD_MODE_BIGINT; if (s->cur_func->js_mode & JS_MODE_MATH) flags |= ATOD_TYPE_BIG_FLOAT; @@ -23312,7 +23440,6 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen } else #ifdef CONFIG_BIGNUM if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { - bf_t r_s, *r = &r_s; slimb_t e; int ret; @@ -23326,9 +23453,9 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen emit_op(s, OP_push_i32); emit_u32(s, e); } else { - bf_init(s->ctx->bf_ctx, r); - bf_set_si(r, e); - val = JS_NewBigInt(s->ctx, r); + val = JS_NewBigInt64_1(s->ctx, e); + if (JS_IsException(val)) + return -1; ret = emit_push_const(s, val, 0); JS_FreeValue(s->ctx, val); if (ret < 0) @@ -24105,10 +24232,7 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) return -1; if (js_parse_unary(s, 1)) return -1; - if (s->cur_func->js_mode & JS_MODE_MATH) - emit_op(s, OP_math_pow); - else - emit_op(s, OP_pow); + emit_op(s, OP_pow); } #else if (s->token.val == TOK_POW) { @@ -24151,12 +24275,7 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level, opcode = OP_mul; break; case '/': -#ifdef CONFIG_BIGNUM - if (s->cur_func->js_mode & JS_MODE_MATH) - opcode = OP_math_div; - else -#endif - opcode = OP_div; + opcode = OP_div; break; case '%': #ifdef CONFIG_BIGNUM @@ -24549,12 +24668,8 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) op = assign_opcodes[op - TOK_MUL_ASSIGN]; #ifdef CONFIG_BIGNUM if (s->cur_func->js_mode & JS_MODE_MATH) { - if (op == OP_div) - op = OP_math_div; - else if (op == OP_mod) + if (op == OP_mod) op = OP_math_mod; - else if (op == OP_pow) - op = OP_math_pow; } #endif emit_op(s, op); @@ -30149,20 +30264,6 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) len = opcode_info[op].size; pos_next = pos + len; switch(op) { - case OP_close_var_object: - { - if (s->var_object_idx >= 0) { - /* close the var object and create a new one */ - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, OP_close_loc); - dbuf_put_u16(&bc_out, s->var_object_idx); - dbuf_putc(&bc_out, OP_special_object); - dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT); - put_short_code(&bc_out, OP_put_loc, s->var_object_idx); - } - } - break; - case OP_line_num: /* line number info (for debug). We put it in a separate compressed table to reduce memory usage and get better @@ -31508,11 +31609,8 @@ static __exception int js_parse_directives(JSParseState *s) } #endif #ifdef CONFIG_BIGNUM - else if (s->ctx->bignum_ext && !strcmp(str, "use bigint")) { - s->cur_func->js_mode |= JS_MODE_BIGINT; - } else if (s->ctx->bignum_ext && !strcmp(str, "use math")) { - s->cur_func->js_mode |= JS_MODE_BIGINT | JS_MODE_MATH; + s->cur_func->js_mode |= JS_MODE_MATH; } #endif } @@ -31804,13 +31902,6 @@ static __exception int js_parse_function_decl2(JSParseState *s, } if (js_parse_destructing_element(s, TOK_VAR, 1, TRUE, -1, TRUE)) goto fail; - /* Close var object: necessary if direct eval call - occurred in the assignment expression or if any - variable was captured and a later direct eval call - may instantiate it in the var object. - The next pass will generate the capture if required. - */ - emit_op(s, OP_close_var_object); } else if (s->token.val == TOK_IDENT) { if (s->token.u.ident.is_reserved) { js_parse_error_reserved_identifier(s); @@ -31889,8 +31980,6 @@ static __exception int js_parse_function_decl2(JSParseState *s, set_object_name(s, name); emit_op(s, OP_put_arg); emit_u16(s, idx); - /* Close var object: see above comment. */ - emit_op(s, OP_close_var_object); emit_label(s, label); } } else if (!has_opt_arg) { @@ -34399,30 +34488,20 @@ static JSValue JS_ToObject(JSContext *ctx, JSValueConst val) case JS_TAG_EXCEPTION: return JS_DupValue(ctx, val); #ifdef CONFIG_BIGNUM - case JS_TAG_INT: - if (is_bigint_mode(ctx)) - obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT); - else - obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER); - goto set_value; case JS_TAG_BIG_INT: obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT); goto set_value; - case JS_TAG_FLOAT64: - obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER); - goto set_value; case JS_TAG_BIG_FLOAT: obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT); goto set_value; case JS_TAG_BIG_DECIMAL: obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL); goto set_value; -#else +#endif case JS_TAG_INT: case JS_TAG_FLOAT64: obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER); goto set_value; -#endif case JS_TAG_STRING: /* XXX: should call the string constructor */ { @@ -34995,10 +35074,13 @@ static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val ret = JS_PreventExtensions(ctx, obj); if (ret < 0) return JS_EXCEPTION; - if (reflect) + if (reflect) { return JS_NewBool(ctx, ret); - else + } else { + if (!ret) + return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); return JS_DupValue(ctx, obj); + } } static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val, @@ -35166,7 +35248,7 @@ static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val, goto exception; } js_free_prop_enum(ctx, props, len); - return JS_TRUE; + return JS_DupValue(ctx, obj); exception: js_free_prop_enum(ctx, props, len); @@ -37690,17 +37772,14 @@ static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) { JSValue val, obj; -#ifdef CONFIG_BIGNUM if (argc == 0) { - if (is_bigint_mode(ctx)) - val = __JS_NewFloat64(ctx, 0); - else - val = JS_NewInt32(ctx, 0); + val = JS_NewInt32(ctx, 0); } else { val = JS_ToNumeric(ctx, argv[0]); if (JS_IsException(val)) return val; switch(JS_VALUE_GET_TAG(val)) { +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_INT: case JS_TAG_BIG_FLOAT: { @@ -37711,25 +37790,19 @@ static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target, val = __JS_NewFloat64(ctx, d); } break; - case JS_TAG_INT: - if (is_bigint_mode(ctx)) { - /* always return a number in bignum mode */ - val = __JS_NewFloat64(ctx, JS_VALUE_GET_INT(val)); - } + case JS_TAG_BIG_DECIMAL: + val = JS_ToStringFree(ctx, val); + if (JS_IsException(val)) + return val; + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return val; break; +#endif default: break; } } -#else - if (argc == 0) { - val = JS_NewInt32(ctx, 0); - } else { - val = JS_ToNumber(ctx, argv[0]); - if (JS_IsException(val)) - return val; - } -#endif if (!JS_IsUndefined(new_target)) { obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER); if (!JS_IsException(obj)) @@ -37760,7 +37833,6 @@ static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val, static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - /* XXX: should just check for float and big_float */ if (!JS_IsNumber(argv[0])) return JS_FALSE; return js_global_isNaN(ctx, this_val, argc, argv); @@ -37793,8 +37865,7 @@ static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val, return JS_FALSE; if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) return JS_EXCEPTION; - return JS_NewBool(ctx, isfinite(d) && floor(d) == d && - fabs(d) <= (double)MAX_SAFE_INTEGER); + return JS_NewBool(ctx, is_safe_integer(d)); } static const JSCFunctionListEntry js_number_funcs[] = { @@ -37838,6 +37909,18 @@ static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val, return js_thisNumberValue(ctx, this_val); } +static int js_get_radix(JSContext *ctx, JSValueConst val) +{ + int radix; + if (JS_ToInt32Sat(ctx, &radix, val)) + return -1; + if (radix < 2 || radix > 36) { + JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); + return -1; + } + return radix; +} + static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { @@ -37851,12 +37934,9 @@ static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, if (magic || JS_IsUndefined(argv[0])) { base = 10; } else { - if (JS_ToInt32Sat(ctx, &base, argv[0])) - goto fail; - if (base < 2 || base > 36) { - JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); + base = js_get_radix(ctx, argv[0]); + if (base < 0) goto fail; - } } if (JS_ToFloat64Free(ctx, &d, val)) return JS_EXCEPTION; @@ -37972,10 +38052,6 @@ static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val, p = str; p += skip_spaces(p); flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN; -#ifdef CONFIG_BIGNUM - if (is_bigint_mode(ctx)) - flags |= ATOD_MODE_BIGINT; -#endif ret = js_atof(ctx, p, NULL, radix, flags); } JS_FreeCString(ctx, str); @@ -39738,348 +39814,6 @@ static double js_fmax(double a, double b) } } -#ifdef CONFIG_BIGNUM - -enum { - MATH_OP_ABS, - MATH_OP_FLOOR, - MATH_OP_CEIL, - MATH_OP_ROUND, - MATH_OP_TRUNC, - MATH_OP_SQRT, - MATH_OP_FPROUND, - MATH_OP_ACOS, - MATH_OP_ASIN, - MATH_OP_ATAN, - MATH_OP_ATAN2, - MATH_OP_COS, - MATH_OP_EXP, - MATH_OP_LOG, - MATH_OP_POW, - MATH_OP_SIN, - MATH_OP_TAN, - MATH_OP_FMOD, - MATH_OP_REM, - MATH_OP_SIGN, - - MATH_OP_ADD, - MATH_OP_SUB, - MATH_OP_MUL, - MATH_OP_DIV, -}; - -static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - bf_t a_s, *a, r_s, *r = &r_s; - JSFloatEnv *fe; - int rnd_mode; - JSValue op1; - - op1 = JS_ToNumber(ctx, argv[0]); - if (JS_IsException(op1)) - return op1; - a = JS_ToBigFloat(ctx, &a_s, op1); - fe = &ctx->fp_env; - if (argc > 1) { - fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV); - if (!fe) { - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, op1); - return JS_EXCEPTION; - } - } - - bf_init(ctx->bf_ctx, r); - switch (magic) { - case MATH_OP_ABS: - bf_set(r, a); - r->sign = 0; - break; - case MATH_OP_FLOOR: - rnd_mode = BF_RNDD; - goto rint; - case MATH_OP_CEIL: - rnd_mode = BF_RNDU; - goto rint; - case MATH_OP_ROUND: - rnd_mode = BF_RNDNU; - goto rint; - case MATH_OP_TRUNC: - rnd_mode = BF_RNDZ; - rint: - bf_set(r, a); - fe->status |= bf_rint(r, rnd_mode); - break; - case MATH_OP_SQRT: - fe->status |= bf_sqrt(r, a, fe->prec, fe->flags); - break; - case MATH_OP_FPROUND: - bf_set(r, a); - fe->status |= bf_round(r, fe->prec, fe->flags); - break; - case MATH_OP_ACOS: - fe->status |= bf_acos(r, a, fe->prec, fe->flags); - break; - case MATH_OP_ASIN: - fe->status |= bf_asin(r, a, fe->prec, fe->flags); - break; - case MATH_OP_ATAN: - fe->status |= bf_atan(r, a, fe->prec, fe->flags); - break; - case MATH_OP_COS: - fe->status |= bf_cos(r, a, fe->prec, fe->flags); - break; - case MATH_OP_EXP: - fe->status |= bf_exp(r, a, fe->prec, fe->flags); - break; - case MATH_OP_LOG: - fe->status |= bf_log(r, a, fe->prec, fe->flags); - break; - case MATH_OP_SIN: - fe->status |= bf_sin(r, a, fe->prec, fe->flags); - break; - case MATH_OP_TAN: - fe->status |= bf_tan(r, a, fe->prec, fe->flags); - break; - case MATH_OP_SIGN: - if (bf_is_nan(a) || bf_is_zero(a)) { - bf_set(r, a); - } else { - bf_set_si(r, 1 - 2 * a->sign); - } - break; - default: - abort(); - } - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, op1); - return JS_NewBigFloat(ctx, r); -} - -static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - bf_t a_s, *a, b_s, *b, r_s, *r = &r_s; - JSFloatEnv *fe; - JSValue op1, op2; - - op1 = JS_ToNumber(ctx, argv[0]); - if (JS_IsException(op1)) - return op1; - op2 = JS_ToNumber(ctx, argv[1]); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - return op2; - } - a = JS_ToBigFloat(ctx, &a_s, op1); - b = JS_ToBigFloat(ctx, &b_s, op2); - fe = &ctx->fp_env; - if (argc > 2) { - fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV); - if (!fe) { - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return JS_EXCEPTION; - } - } - - bf_init(ctx->bf_ctx, r); - switch (magic) { - case MATH_OP_ATAN2: - fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_POW: - fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUICKS); - break; - case MATH_OP_FMOD: - fe->status |= bf_fmod(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_REM: - fe->status |= bf_remainder(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_ADD: - fe->status |= bf_add(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_SUB: - fe->status |= bf_sub(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_MUL: - fe->status |= bf_mul(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_DIV: - fe->status |= bf_div(r, a, b, fe->prec, fe->flags); - break; - default: - abort(); - } - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return JS_NewBigFloat(ctx, r); -} - -static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - BOOL is_max = magic; - JSValue val, ret; - int i; - uint32_t tag; - - if (unlikely(argc == 0)) { - return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0); - } - - tag = JS_VALUE_GET_TAG(argv[0]); - if (tag == JS_TAG_INT) { - int a1, r1 = JS_VALUE_GET_INT(argv[0]); - for(i = 1; i < argc; i++) { - tag = JS_VALUE_GET_TAG(argv[i]); - if (tag != JS_TAG_INT) { - ret = JS_NewInt32(ctx, r1); - goto generic_case; - } - a1 = JS_VALUE_GET_INT(argv[i]); - if (is_max) - r1 = max_int(r1, a1); - else - r1 = min_int(r1, a1); - } - ret = JS_NewInt32(ctx, r1); - } else { - ret = JS_ToNumber(ctx, argv[0]); - if (JS_IsException(ret)) - return ret; - i = 1; - generic_case: - for(; i < argc; i++) { - val = JS_ToNumber(ctx, argv[i]); - if (JS_IsException(val)) { - JS_FreeValue(ctx, ret); - return val; - } - if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(ret)) && - JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(val))) { - double r, a; - r = JS_VALUE_GET_FLOAT64(ret); - a = JS_VALUE_GET_FLOAT64(val); - if (!isnan(r)) { - if (isnan(a)) { - r = a; - } else { - if (is_max) - r = js_fmax(r, a); - else - r = js_fmin(r, a); - } - ret = __JS_NewFloat64(ctx, r); - } - } else { - bf_t a_s, *a, r_s, *r; - int res; - - r = JS_ToBigFloat(ctx, &r_s, ret); - if (!bf_is_nan(r)) { - a = JS_ToBigFloat(ctx, &a_s, val); - res = bf_cmp_full(a, r); - if (is_max) - res = -res; - if (bf_is_nan(a) || res < 0) { - JS_FreeValue(ctx, ret); - ret = JS_DupValue(ctx, val); - } - if (a == &a_s) - bf_delete(a); - } - if (r == &r_s) - bf_delete(r); - JS_FreeValue(ctx, val); - } - } - } - return ret; -} - -static JSValue js_math_abs(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - uint32_t tag; - - val = JS_ToNumeric(ctx, argv[0]); - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - if (JS_VALUE_GET_INT(val) < 0) - val = JS_NewInt64(ctx, -(int64_t)JS_VALUE_GET_INT(val)); - break; - case JS_TAG_FLOAT64: - val = __JS_NewFloat64(ctx, fabs(JS_VALUE_GET_FLOAT64(val))); - break; - case JS_TAG_BIG_FLOAT: - case JS_TAG_BIG_INT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); - bf_set(r, &p->num); - r->sign = 0; - JS_FreeValue(ctx, val); - if (tag == JS_TAG_BIG_FLOAT) - val = JS_NewBigFloat(ctx, r); - else - val = JS_NewBigInt2(ctx, r, TRUE); - } - break; - default: - break; - } - return val; -} - -#if 0 -/* XXX: should give exact rounding */ -/* XXX: correct NaN/Infinity handling */ -static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - bf_t a_s, *a, r_s, *r = &r_s, r2_s, *r2 = &r2_s; - JSValue val; - int i; - BOOL is_float; - - bf_init(ctx->bf_ctx, r); - bf_set_si(r, 0); - for(i = 0; i < argc; i++) { - val = JS_ToNumber(ctx, argv[i]); - if (JS_IsException(val)) { - bf_delete(r); - return val; - } - a = JS_ToBigFloat(ctx, &is_float, &a_s, val); - bf_add(r, r, a, ctx->fp_env.prec, ctx->fp_env.flags); - if (a == &a_s) - bf_delete(a); - } - bf_init(ctx->bf_ctx, r2); - bf_sqrt(r2, r, ctx->fp_env.prec, ctx->fp_env.flags); - bf_delete(r); - return JS_NewBigFloat(ctx, r2); -} -#endif - -#else - static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { @@ -40133,8 +39867,6 @@ static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, } } -#endif /* !CONFIG_BIGNUM */ - static double js_math_sign(double a) { if (isnan(a) || a == 0.0) @@ -40267,11 +39999,7 @@ static JSValue js_math_random(JSContext *ctx, JSValueConst this_val, static const JSCFunctionListEntry js_math_funcs[] = { JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ), JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ), -#ifdef CONFIG_BIGNUM - JS_CFUNC_DEF("abs", 1, js_math_abs ), -#else JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ), -#endif JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ), JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ), JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ), @@ -41959,46 +41687,12 @@ static JSValue json_parse_value(JSParseState *s) } is_neg = 1; number: -#ifdef CONFIG_BIGNUM - val = JS_DupValue(ctx, s->token.u.num.val); - if (is_neg) { - switch(JS_VALUE_GET_NORM_TAG(val)) { - case JS_TAG_BIG_INT: - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p; - p = JS_VALUE_GET_PTR(val); - bf_neg(&p->num); - } - break; - case JS_TAG_FLOAT64: - { - double d; - d = JS_VALUE_GET_FLOAT64(val); - val = __JS_NewFloat64(ctx, -d); - } - break; - default: - case JS_TAG_INT: - { - int v; - v = JS_VALUE_GET_INT(val); - if (v == 0 && !is_bigint_mode(s->ctx)) - val = __JS_NewFloat64(ctx, -0.0); - else - val = JS_NewInt64(ctx, -(int64_t)v); - } - break; - } - } -#else val = s->token.u.num.val; if (is_neg) { double d; JS_ToFloat64(ctx, &d, val); /* no exception possible */ val = JS_NewFloat64(ctx, -d); } -#endif if (next_token(s)) goto fail; break; @@ -42168,10 +41862,6 @@ static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) { return r; } -#ifdef CONFIG_BIGNUM -static inline BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v); -#endif - static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, JSValueConst holder, JSValue val, JSValueConst key) { @@ -46764,7 +46454,8 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, double d; int p, i, c, sgn; JSString *sp; - + BOOL is_local; + rv = JS_NAN; s = JS_ToString(ctx, argv[0]); @@ -46780,11 +46471,15 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, if (string_get_signed_field(sp, &p, &fields[0])) goto done; + is_local = TRUE; for (i = 1; i < 6; i++) { if (string_get_field(sp, &p, &fields[i])) break; } - if (i == 6 && p < sp->len && string_get(sp, p) == '.') { + if (i <= 3) { + /* no time: UTC by default */ + is_local = FALSE; + } else if (i == 6 && p < sp->len && string_get(sp, p) == '.') { /* parse milliseconds as a fractional part, round to nearest */ /* XXX: the spec does not indicate which rounding should be used */ int mul = 1000, ms = 0; @@ -46802,14 +46497,20 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, /* parse the time zone offset if present: [+-]HH:mm */ tz = 0; - if (p < sp->len && ((sgn = string_get(sp, p)) == '+' || sgn == '-')) { - if (string_get_field(sp, &p, &hh)) - goto done; - if (string_get_field(sp, &p, &mm)) - goto done; - tz = hh * 60 + mm; - if (sgn == '-') - tz = -tz; + if (p < sp->len) { + sgn = string_get(sp, p); + if (sgn == '+' || sgn == '-') { + if (string_get_field(sp, &p, &hh)) + goto done; + if (string_get_field(sp, &p, &mm)) + goto done; + tz = hh * 60 + mm; + if (sgn == '-') + tz = -tz; + is_local = FALSE; + } else if (sgn == 'Z') { + is_local = FALSE; + } } } else { /* toString or toUTCString format */ @@ -46844,6 +46545,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, // XXX: parse optional milliseconds? /* parse the time zone offset if present: [+-]HHmm */ + is_local = FALSE; tz = 0; for (tz = 0; p < sp->len; p++) { sgn = string_get(sp, p); @@ -46860,7 +46562,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, } } } - d = set_date_fields(fields, 0) - tz * 60000; + d = set_date_fields(fields, is_local) - tz * 60000; rv = __JS_NewFloat64(ctx, d); done: @@ -47080,61 +46782,364 @@ void JS_AddIntrinsicEval(JSContext *ctx) #ifdef CONFIG_BIGNUM +/* Operators */ + +static void js_operator_set_finalizer(JSRuntime *rt, JSValue val) +{ + JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET); + int i, j; + JSBinaryOperatorDefEntry *ent; + + if (opset) { + for(i = 0; i < JS_OVOP_COUNT; i++) { + if (opset->self_ops[i]) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i])); + } + for(j = 0; j < opset->left.count; j++) { + ent = &opset->left.tab[j]; + for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { + if (ent->ops[i]) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i])); + } + } + js_free_rt(rt, opset->left.tab); + for(j = 0; j < opset->right.count; j++) { + ent = &opset->right.tab[j]; + for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { + if (ent->ops[i]) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i])); + } + } + js_free_rt(rt, opset->right.tab); + js_free_rt(rt, opset); + } +} + +static void js_operator_set_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET); + int i, j; + JSBinaryOperatorDefEntry *ent; + + if (opset) { + for(i = 0; i < JS_OVOP_COUNT; i++) { + if (opset->self_ops[i]) + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]), + mark_func); + } + for(j = 0; j < opset->left.count; j++) { + ent = &opset->left.tab[j]; + for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { + if (ent->ops[i]) + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]), + mark_func); + } + } + for(j = 0; j < opset->right.count; j++) { + ent = &opset->right.tab[j]; + for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { + if (ent->ops[i]) + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]), + mark_func); + } + } + } +} + + +/* create an OperatorSet object */ +static JSValue js_operators_create_internal(JSContext *ctx, + int argc, JSValueConst *argv, + BOOL is_primitive) +{ + JSValue opset_obj, prop, obj; + JSOperatorSetData *opset, *opset1; + JSBinaryOperatorDef *def; + JSValueConst arg; + int i, j; + JSBinaryOperatorDefEntry *new_tab; + JSBinaryOperatorDefEntry *ent; + uint32_t op_count; + + if (ctx->rt->operator_count == UINT32_MAX) { + return JS_ThrowTypeError(ctx, "too many operators"); + } + opset_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OPERATOR_SET); + if (JS_IsException(opset_obj)) + goto fail; + opset = js_mallocz(ctx, sizeof(*opset)); + if (!opset) + goto fail; + JS_SetOpaque(opset_obj, opset); + if (argc >= 1) { + arg = argv[0]; + /* self operators */ + for(i = 0; i < JS_OVOP_COUNT; i++) { + prop = JS_GetPropertyStr(ctx, arg, js_overloadable_operator_names[i]); + if (JS_IsException(prop)) + goto fail; + if (!JS_IsUndefined(prop)) { + if (check_function(ctx, prop)) { + JS_FreeValue(ctx, prop); + goto fail; + } + opset->self_ops[i] = JS_VALUE_GET_OBJ(prop); + } + } + } + /* left & right operators */ + for(j = 1; j < argc; j++) { + arg = argv[j]; + prop = JS_GetPropertyStr(ctx, arg, "left"); + if (JS_IsException(prop)) + goto fail; + def = &opset->right; + if (JS_IsUndefined(prop)) { + prop = JS_GetPropertyStr(ctx, arg, "right"); + if (JS_IsException(prop)) + goto fail; + if (JS_IsUndefined(prop)) { + JS_ThrowTypeError(ctx, "left or right property must be present"); + goto fail; + } + def = &opset->left; + } + /* get the operator set */ + obj = JS_GetProperty(ctx, prop, JS_ATOM_prototype); + JS_FreeValue(ctx, prop); + if (JS_IsException(obj)) + goto fail; + prop = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet); + JS_FreeValue(ctx, obj); + if (JS_IsException(prop)) + goto fail; + opset1 = JS_GetOpaque2(ctx, prop, JS_CLASS_OPERATOR_SET); + if (!opset1) { + JS_FreeValue(ctx, prop); + goto fail; + } + op_count = opset1->operator_counter; + JS_FreeValue(ctx, prop); + + /* we assume there are few entries */ + new_tab = js_realloc(ctx, def->tab, + (def->count + 1) * sizeof(def->tab[0])); + if (!new_tab) + goto fail; + def->tab = new_tab; + def->count++; + ent = def->tab + def->count - 1; + memset(ent, 0, sizeof(def->tab[0])); + ent->operator_index = op_count; + + for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { + prop = JS_GetPropertyStr(ctx, arg, + js_overloadable_operator_names[i]); + if (JS_IsException(prop)) + goto fail; + if (!JS_IsUndefined(prop)) { + if (check_function(ctx, prop)) { + JS_FreeValue(ctx, prop); + goto fail; + } + ent->ops[i] = JS_VALUE_GET_OBJ(prop); + } + } + } + opset->is_primitive = is_primitive; + opset->operator_counter = ctx->rt->operator_count++; + return opset_obj; + fail: + JS_FreeValue(ctx, opset_obj); + return JS_EXCEPTION; +} + +static JSValue js_operators_create(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_operators_create_internal(ctx, argc, argv, FALSE); +} + +static JSValue js_operators_updateBigIntOperators(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue opset_obj, prop; + JSOperatorSetData *opset; + const JSOverloadableOperatorEnum ops[2] = { JS_OVOP_DIV, JS_OVOP_POW }; + JSOverloadableOperatorEnum op; + int i; + + opset_obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_BIG_INT], + JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset_obj)) + goto fail; + opset = JS_GetOpaque2(ctx, opset_obj, JS_CLASS_OPERATOR_SET); + if (!opset) + goto fail; + for(i = 0; i < countof(ops); i++) { + op = ops[i]; + prop = JS_GetPropertyStr(ctx, argv[0], + js_overloadable_operator_names[op]); + if (JS_IsException(prop)) + goto fail; + if (!JS_IsUndefined(prop)) { + if (!JS_IsNull(prop) && check_function(ctx, prop)) { + JS_FreeValue(ctx, prop); + goto fail; + } + if (opset->self_ops[op]) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[op])); + if (JS_IsNull(prop)) { + opset->self_ops[op] = NULL; + } else { + opset->self_ops[op] = JS_VALUE_GET_PTR(prop); + } + } + } + JS_FreeValue(ctx, opset_obj); + return JS_UNDEFINED; + fail: + JS_FreeValue(ctx, opset_obj); + return JS_EXCEPTION; +} + +static int js_operators_set_default(JSContext *ctx, JSValueConst obj) +{ + JSValue opset_obj; + + if (!JS_IsObject(obj)) /* in case the prototype is not defined */ + return 0; + opset_obj = js_operators_create_internal(ctx, 0, NULL, TRUE); + if (JS_IsException(opset_obj)) + return -1; + /* cannot be modified by the user */ + JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_operatorSet, + opset_obj, 0); + return 0; +} + +static JSValue js_dummy_operators_ctor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + return js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); +} + +static JSValue js_global_operators(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue func_obj, proto, opset_obj; + + func_obj = JS_UNDEFINED; + proto = JS_NewObject(ctx); + if (JS_IsException(proto)) + return JS_EXCEPTION; + opset_obj = js_operators_create_internal(ctx, argc, argv, FALSE); + if (JS_IsException(opset_obj)) + goto fail; + JS_DefinePropertyValue(ctx, proto, JS_ATOM_Symbol_operatorSet, + opset_obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + func_obj = JS_NewCFunction2(ctx, js_dummy_operators_ctor, "Operators", + 0, JS_CFUNC_constructor, 0); + if (JS_IsException(func_obj)) + goto fail; + JS_SetConstructor2(ctx, func_obj, proto, + 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + JS_FreeValue(ctx, proto); + return func_obj; + fail: + JS_FreeValue(ctx, proto); + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +static const JSCFunctionListEntry js_operators_funcs[] = { + JS_CFUNC_DEF("create", 1, js_operators_create ), + JS_CFUNC_DEF("updateBigIntOperators", 2, js_operators_updateBigIntOperators ), +}; + +/* must be called after all overloadable base types are initialized */ +void JS_AddIntrinsicOperators(JSContext *ctx) +{ + JSValue obj; + + ctx->allow_operator_overloading = TRUE; + obj = JS_NewCFunction(ctx, js_global_operators, "Operators", 1); + JS_SetPropertyFunctionList(ctx, obj, + js_operators_funcs, + countof(js_operators_funcs)); + JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_Operators, + obj, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + /* add default operatorSets */ + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BOOLEAN]); + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_NUMBER]); + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_STRING]); + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_INT]); + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]); + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]); +} + /* BigInt */ static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) { uint32_t tag; - BOOL is_legacy; - int ret; - is_legacy = is_bigint_mode(ctx) ^ 1; redo: tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { case JS_TAG_INT: case JS_TAG_BOOL: - if (is_legacy) { - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); - bf_set_si(r, JS_VALUE_GET_INT(val)); - val = JS_NewBigInt2(ctx, r, TRUE); - } else { - val = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); - } + val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val)); break; case JS_TAG_BIG_INT: break; case JS_TAG_FLOAT64: case JS_TAG_BIG_FLOAT: { - bf_t *a, a_s, r_s, *r = &r_s; + bf_t *a, a_s; + a = JS_ToBigFloat(ctx, &a_s, val); - bf_init(ctx->bf_ctx, r); if (!bf_is_finite(a)) { JS_FreeValue(ctx, val); val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to bigint"); } else { - bf_set(r, a); - ret = bf_rint(r, BF_RNDZ); + JSValue val1 = JS_NewBigInt(ctx); + bf_t *r; + int ret; + if (JS_IsException(val1)) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + r = JS_GetBigInt(val1); + ret = bf_set(r, a); + ret |= bf_rint(r, BF_RNDZ); JS_FreeValue(ctx, val); - if (is_legacy && (ret & BF_ST_INEXACT)) { - bf_delete(r); + if (ret & BF_ST_MEM_ERROR) { + JS_FreeValue(ctx, val1); + val = JS_ThrowOutOfMemory(ctx); + } else if (ret & BF_ST_INEXACT) { + JS_FreeValue(ctx, val1); val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer"); } else { - val = JS_NewBigInt2(ctx, r, is_legacy); + val = JS_CompactBigInt(ctx, val1); } } if (a == &a_s) bf_delete(a); } break; + case JS_TAG_BIG_DECIMAL: + val = JS_ToStringFree(ctx, val); + if (JS_IsException(val)) + break; + goto redo; case JS_TAG_STRING: val = JS_StringToBigIntErr(ctx, val); break; case JS_TAG_OBJECT: - val = JS_ToPrimitiveFree(ctx, val, - is_legacy ? HINT_NUMBER : HINT_INTEGER); + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); if (JS_IsException(val)) break; goto redo; @@ -47154,17 +47159,6 @@ static JSValue js_bigint_constructor(JSContext *ctx, return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0])); } -static inline BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v) -{ - int tag = JS_VALUE_GET_TAG(v); - if (tag == JS_TAG_BIG_INT) - return TRUE; - if (is_bigint_mode(ctx)) - return tag == JS_TAG_INT; - else - return FALSE; -} - static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val) { if (JS_IsBigInt(ctx, this_val)) @@ -47173,8 +47167,6 @@ static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val) if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { JSObject *p = JS_VALUE_GET_OBJ(this_val); if (p->class_id == JS_CLASS_BIG_INT) { - /* XXX: may relax the check in case the object comes from - bignum mode */ if (JS_IsBigInt(ctx, p->u.object_data)) return JS_DupValue(ctx, p->u.object_data); } @@ -47195,12 +47187,9 @@ static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val, if (argc == 0 || JS_IsUndefined(argv[0])) { base = 10; } else { - if (JS_ToInt32Sat(ctx, &base, argv[0])) + base = js_get_radix(ctx, argv[0]); + if (base < 0) goto fail; - if (base < 2 || base > 36) { - JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); - goto fail; - } } ret = js_bigint_to_string1(ctx, val, base); JS_FreeValue(ctx, val); @@ -47220,72 +47209,96 @@ static JSValue js_bigint_div(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { - bf_t a_s, b_s, *a, *b, r_s, *r = &r_s, q_s, *q = &q_s; + bf_t a_s, b_s, *a, *b, *r, *q; int status; - + JSValue q_val, r_val; + + q_val = JS_NewBigInt(ctx); + if (JS_IsException(q_val)) + return JS_EXCEPTION; + r_val = JS_NewBigInt(ctx); + if (JS_IsException(r_val)) + goto fail; b = NULL; a = JS_ToBigInt(ctx, &a_s, argv[0]); if (!a) - return JS_EXCEPTION; + goto fail; b = JS_ToBigInt(ctx, &b_s, argv[1]); if (!b) { JS_FreeBigInt(ctx, a, &a_s); - return JS_EXCEPTION; + goto fail; } - bf_init(ctx->bf_ctx, q); - bf_init(ctx->bf_ctx, r); + q = JS_GetBigInt(q_val); + r = JS_GetBigInt(r_val); status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf); JS_FreeBigInt(ctx, a, &a_s); JS_FreeBigInt(ctx, b, &b_s); if (unlikely(status)) { - bf_delete(q); - bf_delete(r); throw_bf_exception(ctx, status); - return JS_EXCEPTION; + goto fail; } + q_val = JS_CompactBigInt(ctx, q_val); if (magic & 0x10) { JSValue ret; - /* XXX: handle exceptions */ ret = JS_NewArray(ctx); - JS_SetPropertyUint32(ctx, ret, 0, JS_NewBigInt(ctx, q)); - JS_SetPropertyUint32(ctx, ret, 1, JS_NewBigInt(ctx, r)); + if (JS_IsException(ret)) + goto fail; + JS_SetPropertyUint32(ctx, ret, 0, q_val); + JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val)); return ret; } else { - bf_delete(r); - return JS_NewBigInt(ctx, q); + JS_FreeValue(ctx, r_val); + return q_val; } + fail: + JS_FreeValue(ctx, q_val); + JS_FreeValue(ctx, r_val); + return JS_EXCEPTION; } static JSValue js_bigint_sqrt(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { - bf_t a_s, *a, r_s, *r = &r_s, rem_s, *rem = &rem_s; + bf_t a_s, *a, *r, *rem; int status; + JSValue r_val, rem_val; + + r_val = JS_NewBigInt(ctx); + if (JS_IsException(r_val)) + return JS_EXCEPTION; + rem_val = JS_NewBigInt(ctx); + if (JS_IsException(rem_val)) + return JS_EXCEPTION; + r = JS_GetBigInt(r_val); + rem = JS_GetBigInt(rem_val); a = JS_ToBigInt(ctx, &a_s, argv[0]); if (!a) - return JS_EXCEPTION; - bf_init(ctx->bf_ctx, r); - bf_init(ctx->bf_ctx, rem); + goto fail; status = bf_sqrtrem(r, rem, a); JS_FreeBigInt(ctx, a, &a_s); if (unlikely(status & ~BF_ST_INEXACT)) { - bf_delete(r); - bf_delete(rem); - return throw_bf_exception(ctx, status); + throw_bf_exception(ctx, status); + goto fail; } + r_val = JS_CompactBigInt(ctx, r_val); if (magic) { JSValue ret; - /* XXX: handle exceptions */ ret = JS_NewArray(ctx); - JS_SetPropertyUint32(ctx, ret, 0, JS_NewBigInt(ctx, r)); - JS_SetPropertyUint32(ctx, ret, 1, JS_NewBigInt(ctx, rem)); + if (JS_IsException(ret)) + goto fail; + JS_SetPropertyUint32(ctx, ret, 0, r_val); + JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val)); return ret; } else { - bf_delete(rem); - return JS_NewBigInt(ctx, r); + JS_FreeValue(ctx, rem_val); + return r_val; } + fail: + JS_FreeValue(ctx, r_val); + JS_FreeValue(ctx, rem_val); + return JS_EXCEPTION; } static JSValue js_bigint_op1(JSContext *ctx, @@ -47326,15 +47339,22 @@ static JSValue js_bigint_asUintN(JSContext *ctx, int argc, JSValueConst *argv, int asIntN) { uint64_t bits; - bf_t a_s, *a = &a_s, r_s, *r = &r_s, mask_s, *mask = &mask_s; - + bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s; + JSValue res; + if (JS_ToIndex(ctx, &bits, argv[0])) return JS_EXCEPTION; + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) + return JS_EXCEPTION; + r = JS_GetBigInt(res); a = JS_ToBigInt(ctx, &a_s, argv[1]); - if (!a) + if (!a) { + JS_FreeValue(ctx, res); return JS_EXCEPTION; + } /* XXX: optimize */ - bf_init(ctx->bf_ctx, r); + r = JS_GetBigInt(res); bf_init(ctx->bf_ctx, mask); bf_set_ui(mask, 1); bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ); @@ -47351,7 +47371,7 @@ static JSValue js_bigint_asUintN(JSContext *ctx, } bf_delete(mask); JS_FreeBigInt(ctx, a, &a_s); - return JS_NewBigInt(ctx, r); + return JS_CompactBigInt(ctx, res); } static const JSCFunctionListEntry js_bigint_funcs[] = { @@ -47430,12 +47450,9 @@ static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val, if (argc == 0 || JS_IsUndefined(argv[0])) { base = 10; } else { - if (JS_ToInt32Sat(ctx, &base, argv[0])) + base = js_get_radix(ctx, argv[0]); + if (base < 0) goto fail; - if (base < 2 || base > 36) { - JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); - goto fail; - } } ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); JS_FreeValue(ctx, val); @@ -47451,7 +47468,7 @@ static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val, return js_thisBigFloatValue(ctx, this_val); } -static int get_rnd_mode(JSContext *ctx, JSValueConst val) +static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val) { int rnd_mode; if (JS_ToInt32Sat(ctx, &rnd_mode, val)) @@ -47468,8 +47485,7 @@ static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val, { JSValue val, ret; int64_t f; - int res, rnd_mode; - bf_t a_s, *a, b; + int rnd_mode, radix; val = js_thisBigFloatValue(ctx, this_val); if (JS_IsException(val)) @@ -47481,26 +47497,19 @@ static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val, goto fail; } rnd_mode = BF_RNDNA; + radix = 10; + /* XXX: swap parameter order for rounding mode and radix */ if (argc > 1) { - rnd_mode = get_rnd_mode(ctx, argv[1]); + rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); if (rnd_mode < 0) goto fail; } - - a = JS_ToBigFloat(ctx, &a_s, val); - if (!a) - goto fail; - bf_init(ctx->bf_ctx, &b); - bf_set_float64(&b, 1e21); - res = bf_cmpu(a, &b); - bf_delete(&b); - if (a == &a_s) - bf_delete(a); - if (res >= 0) { - ret = JS_ToString(ctx, val); - } else { - ret = js_ftoa(ctx, val, 10, f, rnd_mode | BF_FTOA_FORMAT_FRAC); + if (argc > 2) { + radix = js_get_radix(ctx, argv[2]); + if (radix < 0) + goto fail; } + ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC); JS_FreeValue(ctx, val); return ret; fail: @@ -47533,7 +47542,7 @@ static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val, { JSValue val, ret; int64_t f; - int rnd_mode; + int rnd_mode, radix; val = js_thisBigFloatValue(ctx, this_val); if (JS_IsException(val)) @@ -47551,12 +47560,18 @@ static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val, goto fail; } rnd_mode = BF_RNDNA; + radix = 10; if (argc > 1) { - rnd_mode = get_rnd_mode(ctx, argv[1]); + rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); if (rnd_mode < 0) goto fail; } - ret = js_ftoa(ctx, val, 10, f + 1, + if (argc > 2) { + radix = js_get_radix(ctx, argv[2]); + if (radix < 0) + goto fail; + } + ret = js_ftoa(ctx, val, radix, f + 1, rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP); } JS_FreeValue(ctx, val); @@ -47571,7 +47586,7 @@ static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val, { JSValue val, ret; int64_t p; - int rnd_mode; + int rnd_mode, radix; val = js_thisBigFloatValue(ctx, this_val); if (JS_IsException(val)) @@ -47589,12 +47604,18 @@ static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val, goto fail; } rnd_mode = BF_RNDNA; + radix = 10; if (argc > 1) { - rnd_mode = get_rnd_mode(ctx, argv[1]); + rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); if (rnd_mode < 0) goto fail; } - ret = js_ftoa(ctx, val, 10, p, rnd_mode | BF_FTOA_FORMAT_FIXED); + if (argc > 2) { + radix = js_get_radix(ctx, argv[2]); + if (radix < 0) + goto fail; + } + ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED); } JS_FreeValue(ctx, val); return ret; @@ -47617,10 +47638,12 @@ static JSValue js_bigfloat_constructor(JSContext *ctx, { JSValue val; if (argc == 0) { - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); + bf_t *r; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + return val; + r = JS_GetBigFloat(val); bf_set_zero(r, 0); - val = JS_NewBigFloat(ctx, r); } else { val = JS_DupValue(ctx, argv[0]); redo: @@ -47629,18 +47652,26 @@ static JSValue js_bigfloat_constructor(JSContext *ctx, break; case JS_TAG_FLOAT64: { - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); - bf_set_float64(r, JS_VALUE_GET_FLOAT64(val)); - val = JS_NewBigFloat(ctx, r); + bf_t *r; + double d = JS_VALUE_GET_FLOAT64(val); + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigFloat(val); + if (bf_set_float64(r, d)) + goto fail; } break; case JS_TAG_INT: { - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); - bf_set_si(r, JS_VALUE_GET_INT(val)); - val = JS_NewBigFloat(ctx, r); + bf_t *r; + int32_t v = JS_VALUE_GET_INT(val); + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigFloat(val); + if (bf_set_si(r, v)) + goto fail; } break; case JS_TAG_BIG_INT: @@ -47650,6 +47681,11 @@ static JSValue js_bigfloat_constructor(JSContext *ctx, val = JS_MKPTR(JS_TAG_BIG_FLOAT, p); } break; + case JS_TAG_BIG_DECIMAL: + val = JS_ToStringFree(ctx, val); + if (JS_IsException(val)) + break; + goto redo; case JS_TAG_STRING: { const char *str, *p; @@ -47663,10 +47699,12 @@ static JSValue js_bigfloat_constructor(JSContext *ctx, p = str; p += skip_spaces(p); if ((p - str) == len) { - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); - bf_set_si(r, 0); - val = JS_NewBigFloat(ctx, r); + bf_t *r; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigFloat(val); + bf_set_zero(r, 0); err = 0; } else { val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT | @@ -47699,13 +47737,20 @@ static JSValue js_bigfloat_constructor(JSContext *ctx, } } return val; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; } static JSValue js_bigfloat_get_const(JSContext *ctx, - JSValueConst this_val, int magic) + JSValueConst this_val, int magic) { - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); + bf_t *r; + JSValue val; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + return val; + r = JS_GetBigFloat(val); switch(magic) { case 0: /* PI */ bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags); @@ -47741,13 +47786,13 @@ static JSValue js_bigfloat_get_const(JSContext *ctx, default: abort(); } - return JS_NewBigFloat(ctx, r); + return val; } static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - bf_t a_s, *a = &a_s; + bf_t *a; const char *str; JSValue ret; int radix; @@ -47771,10 +47816,13 @@ static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val, if (!fe) goto fail; } - bf_init(ctx->bf_ctx, a); + ret = JS_NewBigFloat(ctx); + if (JS_IsException(ret)) + goto done; + a = JS_GetBigFloat(ret); /* XXX: use js_atof() */ bf_atof(a, str, NULL, radix, fe->prec, fe->flags); - ret = JS_NewBigFloat(ctx, a); + done: JS_FreeCString(ctx, str); return ret; } @@ -47800,7 +47848,201 @@ static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val, if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT) return JS_FALSE; p = JS_VALUE_GET_PTR(val); - return JS_NewBool(ctx, bf_is_finite(&p->num)); + return JS_NewBool(ctx, bf_is_nan(&p->num)); +} + +enum { + MATH_OP_ABS, + MATH_OP_FLOOR, + MATH_OP_CEIL, + MATH_OP_ROUND, + MATH_OP_TRUNC, + MATH_OP_SQRT, + MATH_OP_FPROUND, + MATH_OP_ACOS, + MATH_OP_ASIN, + MATH_OP_ATAN, + MATH_OP_ATAN2, + MATH_OP_COS, + MATH_OP_EXP, + MATH_OP_LOG, + MATH_OP_POW, + MATH_OP_SIN, + MATH_OP_TAN, + MATH_OP_FMOD, + MATH_OP_REM, + MATH_OP_SIGN, + + MATH_OP_ADD, + MATH_OP_SUB, + MATH_OP_MUL, + MATH_OP_DIV, +}; + +static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + bf_t a_s, *a, *r; + JSFloatEnv *fe; + int rnd_mode; + JSValue op1, res; + + op1 = JS_ToNumeric(ctx, argv[0]); + if (JS_IsException(op1)) + return op1; + a = JS_ToBigFloat(ctx, &a_s, op1); + fe = &ctx->fp_env; + if (argc > 1) { + fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV); + if (!fe) + goto fail; + } + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + fail: + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + return JS_EXCEPTION; + } + r = JS_GetBigFloat(res); + switch (magic) { + case MATH_OP_ABS: + bf_set(r, a); + r->sign = 0; + break; + case MATH_OP_FLOOR: + rnd_mode = BF_RNDD; + goto rint; + case MATH_OP_CEIL: + rnd_mode = BF_RNDU; + goto rint; + case MATH_OP_ROUND: + rnd_mode = BF_RNDNA; + goto rint; + case MATH_OP_TRUNC: + rnd_mode = BF_RNDZ; + rint: + bf_set(r, a); + fe->status |= bf_rint(r, rnd_mode); + break; + case MATH_OP_SQRT: + fe->status |= bf_sqrt(r, a, fe->prec, fe->flags); + break; + case MATH_OP_FPROUND: + bf_set(r, a); + fe->status |= bf_round(r, fe->prec, fe->flags); + break; + case MATH_OP_ACOS: + fe->status |= bf_acos(r, a, fe->prec, fe->flags); + break; + case MATH_OP_ASIN: + fe->status |= bf_asin(r, a, fe->prec, fe->flags); + break; + case MATH_OP_ATAN: + fe->status |= bf_atan(r, a, fe->prec, fe->flags); + break; + case MATH_OP_COS: + fe->status |= bf_cos(r, a, fe->prec, fe->flags); + break; + case MATH_OP_EXP: + fe->status |= bf_exp(r, a, fe->prec, fe->flags); + break; + case MATH_OP_LOG: + fe->status |= bf_log(r, a, fe->prec, fe->flags); + break; + case MATH_OP_SIN: + fe->status |= bf_sin(r, a, fe->prec, fe->flags); + break; + case MATH_OP_TAN: + fe->status |= bf_tan(r, a, fe->prec, fe->flags); + break; + case MATH_OP_SIGN: + if (bf_is_nan(a) || bf_is_zero(a)) { + bf_set(r, a); + } else { + bf_set_si(r, 1 - 2 * a->sign); + } + break; + default: + abort(); + } + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + return res; +} + +static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + bf_t a_s, *a, b_s, *b, r_s, *r = &r_s; + JSFloatEnv *fe; + JSValue op1, op2, res; + + op1 = JS_ToNumeric(ctx, argv[0]); + if (JS_IsException(op1)) + return op1; + op2 = JS_ToNumeric(ctx, argv[1]); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + return op2; + } + a = JS_ToBigFloat(ctx, &a_s, op1); + b = JS_ToBigFloat(ctx, &b_s, op2); + fe = &ctx->fp_env; + if (argc > 2) { + fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV); + if (!fe) + goto fail; + } + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + fail: + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; + } + r = JS_GetBigFloat(res); + switch (magic) { + case MATH_OP_ATAN2: + fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_POW: + fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS); + break; + case MATH_OP_FMOD: + fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ); + break; + case MATH_OP_REM: + fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN); + break; + case MATH_OP_ADD: + fe->status |= bf_add(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_SUB: + fe->status |= bf_sub(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_MUL: + fe->status |= bf_mul(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_DIV: + fe->status |= bf_div(r, a, b, fe->prec, fe->flags); + break; + default: + abort(); + } + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return res; } static const JSCFunctionListEntry js_bigfloat_funcs[] = { @@ -47895,9 +48137,6 @@ static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val) return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags)); } -/* temporary fix for string conversion overflows */ -#define BF_EXP_BITS_MAX1 (BF_EXP_BITS_MAX - 1) - static JSValue js_float_env_setPrec(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) @@ -47913,18 +48152,16 @@ static JSValue js_float_env_setPrec(JSContext *ctx, return JS_EXCEPTION; if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) return JS_ThrowRangeError(ctx, "invalid precision"); - exp_bits = BF_EXP_BITS_MAX1; + exp_bits = BF_EXP_BITS_MAX; if (argc > 2 && !JS_IsUndefined(argv[2])) { if (JS_ToInt32Sat(ctx, &exp_bits, argv[2])) return JS_EXCEPTION; - if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX1) + if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX) return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); } - flags = BF_RNDN | bf_set_exp_bits(exp_bits); - if (exp_bits != BF_EXP_BITS_MAX1) - flags |= BF_FLAG_SUBNORMAL; + flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits); saved_prec = ctx->fp_env.prec; saved_flags = ctx->fp_env.flags; @@ -47984,24 +48221,20 @@ static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_v case FE_EXP: if (JS_ToInt32Sat(ctx, &b, val)) return JS_EXCEPTION; - if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX1) + if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX) return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); - if (b == BF_EXP_BITS_MAX1) - fe->flags &= ~BF_FLAG_SUBNORMAL; fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) | bf_set_exp_bits(b); break; case FE_RNDMODE: - b = get_rnd_mode(ctx, val); + b = bigfloat_get_rnd_mode(ctx, val); if (b < 0) return JS_EXCEPTION; fe->flags = (fe->flags & ~BF_RND_MASK) | b; break; case FE_SUBNORMAL: b = JS_ToBool(ctx, val); - if (bf_get_exp_bits(fe->flags) != BF_EXP_BITS_MAX1) { - fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0); - } + fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0); break; default: b = JS_ToBool(ctx, val); @@ -48031,12 +48264,12 @@ static const JSCFunctionListEntry js_float_env_funcs[] = { JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ), JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ), JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ), - JS_PROP_INT32_DEF("RNDNU", BF_RNDNU, 0 ), + JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ), JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ), JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ), JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ), JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ), - JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX1, 0 ), + JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ), }; static const JSCFunctionListEntry js_float_env_proto_funcs[] = { @@ -48112,10 +48345,18 @@ static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, case JS_TAG_BOOL: case JS_TAG_INT: { - bfdec_t r_s, *r = &r_s; - bfdec_init(ctx->bf_ctx, r); - bfdec_set_si(r, JS_VALUE_GET_INT(val)); - val = JS_NewBigDecimal(ctx, r); + bfdec_t *r; + int32_t v = JS_VALUE_GET_INT(val); + + val = JS_NewBigDecimal(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigDecimal(val); + if (bfdec_set_si(r, v)) { + JS_FreeValue(ctx, val); + val = JS_EXCEPTION; + break; + } } break; case JS_TAG_FLOAT64: @@ -48138,10 +48379,12 @@ static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, p = str; p += skip_spaces(p); if ((p - str) == len) { - bfdec_t r_s, *r = &r_s; - bfdec_init(ctx->bf_ctx, r); + bfdec_t *r; + val = JS_NewBigDecimal(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigDecimal(val); bfdec_set_zero(r, 0); - val = JS_NewBigDecimal(ctx, r); err = 0; } else { val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL); @@ -48166,12 +48409,14 @@ static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, goto redo; case JS_TAG_UNDEFINED: { - bfdec_t r_s, *r = &r_s; + bfdec_t *r; if (!allow_null_or_undefined) goto fail; - bfdec_init(ctx->bf_ctx, r); + val = JS_NewBigDecimal(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigDecimal(val); bfdec_set_nan(r); - val = JS_NewBigDecimal(ctx, r); } break; default: @@ -48188,10 +48433,12 @@ static JSValue js_bigdecimal_constructor(JSContext *ctx, { JSValue val; if (argc == 0) { - bfdec_t r_s, *r = &r_s; - bfdec_init(ctx->bf_ctx, r); + bfdec_t *r; + val = JS_NewBigDecimal(ctx); + if (JS_IsException(val)) + return val; + r = JS_GetBigDecimal(val); bfdec_set_zero(r, 0); - val = JS_NewBigDecimal(ctx, r); } else { val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE); } @@ -48230,6 +48477,39 @@ static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val, return js_thisBigDecimalValue(ctx, this_val); } +static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj) +{ + const char *str; + size_t size; + int rnd_mode; + + str = JS_ToCStringLen(ctx, &size, obj); + if (!str) + return -1; + if (strlen(str) != size) + goto invalid_rounding_mode; + if (!strcmp(str, "floor")) { + rnd_mode = BF_RNDD; + } else if (!strcmp(str, "ceiling")) { + rnd_mode = BF_RNDU; + } else if (!strcmp(str, "down")) { + rnd_mode = BF_RNDZ; + } else if (!strcmp(str, "up")) { + rnd_mode = BF_RNDA; + } else if (!strcmp(str, "half-even")) { + rnd_mode = BF_RNDN; + } else if (!strcmp(str, "half-up")) { + rnd_mode = BF_RNDNA; + } else { + invalid_rounding_mode: + JS_FreeCString(ctx, str); + JS_ThrowTypeError(ctx, "invalid rounding mode"); + return -1; + } + JS_FreeCString(ctx, str); + return rnd_mode; +} + typedef struct { int64_t prec; bf_flags_t flags; @@ -48239,10 +48519,9 @@ static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe, JSValueConst obj) { JSValue prop; - const char *str; - size_t size; int64_t val; BOOL has_prec; + int rnd_mode; if (!JS_IsObject(obj)) { JS_ThrowTypeErrorNotAnObject(ctx); @@ -48251,29 +48530,11 @@ static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe, prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode); if (JS_IsException(prop)) return -1; - str = JS_ToCStringLen(ctx, &size, prop); + rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop); JS_FreeValue(ctx, prop); - if (!str) + if (rnd_mode < 0) return -1; - if (strlen(str) != size) - goto invalid_rounding_mode; - if (!strcmp(str, "floor")) { - fe->flags = BF_RNDD; - } else if (!strcmp(str, "ceiling")) { - fe->flags = BF_RNDU; - } else if (!strcmp(str, "down")) { - fe->flags = BF_RNDZ; - } else if (!strcmp(str, "half-even")) { - fe->flags = BF_RNDN; - } else if (!strcmp(str, "half-up")) { - fe->flags = BF_RNDNA; - } else { - invalid_rounding_mode: - JS_FreeCString(ctx, str); - JS_ThrowTypeError(ctx, "invalid rounding mode"); - return -1; - } - JS_FreeCString(ctx, str); + fe->flags = rnd_mode; prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits); if (JS_IsException(prop)) @@ -48282,7 +48543,7 @@ static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe, if (!JS_IsUndefined(prop)) { if (JS_ToInt64SatFree(ctx, &val, prop)) return -1; - if (val < 0 || val > BF_PREC_MAX) + if (val < 1 || val > BF_PREC_MAX) goto invalid_precision; fe->prec = val; has_prec = TRUE; @@ -48320,7 +48581,7 @@ static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { bfdec_t *a, *b, r_s, *r = &r_s; - JSValue op1, op2; + JSValue op1, op2, res; BigDecimalEnv fe_s, *fe = &fe_s; int op_count, ret; @@ -48354,21 +48615,18 @@ static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val, fe->flags = BF_RNDZ; fe->prec = BF_PREC_INF; if (op_count < argc) { - if (js_bigdecimal_get_env(ctx, fe, argv[op_count])) { - fail: - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return JS_EXCEPTION; - } - if ((fe->flags & BF_FLAG_RADPNT_PREC) && - magic != MATH_OP_DIV && - magic != MATH_OP_ROUND) { - JS_ThrowTypeError(ctx, "maximumFractionDigits is not supported for this operation"); + if (js_bigdecimal_get_env(ctx, fe, argv[op_count])) goto fail; - } } - bfdec_init(ctx->bf_ctx, r); + res = JS_NewBigDecimal(ctx); + if (JS_IsException(res)) { + fail: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; + } + r = JS_GetBigDecimal(res); switch (magic) { case MATH_OP_ADD: ret = bfdec_add(r, a, b, fe->prec, fe->flags); @@ -48382,6 +48640,9 @@ static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val, case MATH_OP_DIV: ret = bfdec_div(r, a, b, fe->prec, fe->flags); break; + case MATH_OP_FMOD: + ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ); + break; case MATH_OP_SQRT: ret = bfdec_sqrt(r, a, fe->prec, fe->flags); break; @@ -48398,16 +48659,119 @@ static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val, ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP | BF_ST_OVERFLOW; if (ret != 0) { - bfdec_delete(r); + JS_FreeValue(ctx, res); return throw_bf_exception(ctx, ret); } else { - return JS_NewBigDecimal(ctx, r); + return res; } } +static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + int64_t f; + int rnd_mode; + + val = js_thisBigDecimalValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToInt64Sat(ctx, &f, argv[0])) + goto fail; + if (f < 0 || f > BF_PREC_MAX) { + JS_ThrowRangeError(ctx, "invalid number of digits"); + goto fail; + } + rnd_mode = BF_RNDNA; + if (argc > 1) { + rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC); + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + int64_t f; + int rnd_mode; + + val = js_thisBigDecimalValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToInt64Sat(ctx, &f, argv[0])) + goto fail; + if (JS_IsUndefined(argv[0])) { + ret = js_bigdecimal_to_string1(ctx, val, 0, + BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP); + } else { + if (f < 0 || f > BF_PREC_MAX) { + JS_ThrowRangeError(ctx, "invalid number of digits"); + goto fail; + } + rnd_mode = BF_RNDNA; + if (argc > 1) { + rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + ret = js_bigdecimal_to_string1(ctx, val, f + 1, + rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP); + } + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + int64_t p; + int rnd_mode; + + val = js_thisBigDecimalValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_IsUndefined(argv[0])) { + return JS_ToStringFree(ctx, val); + } + if (JS_ToInt64Sat(ctx, &p, argv[0])) + goto fail; + if (p < 1 || p > BF_PREC_MAX) { + JS_ThrowRangeError(ctx, "invalid number of digits"); + goto fail; + } + rnd_mode = BF_RNDNA; + if (argc > 1) { + rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + ret = js_bigdecimal_to_string1(ctx, val, p, + rnd_mode | BF_FTOA_FORMAT_FIXED); + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = { JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ), JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ), + JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ), + JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ), + JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ), }; static const JSCFunctionListEntry js_bigdecimal_funcs[] = { @@ -48415,6 +48779,7 @@ static const JSCFunctionListEntry js_bigdecimal_funcs[] = { JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ), JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ), JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ), + JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ), JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ), JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ), }; @@ -49512,6 +49877,8 @@ static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val, count = min_int(final - from, len - to); if (count > 0) { p = JS_VALUE_GET_OBJ(this_val); + if (typed_array_is_detached(ctx, p)) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); shift = typed_array_size_log2(p->class_id); memmove(p->u.array.u.uint8_ptr + (to << shift), p->u.array.u.uint8_ptr + (from << shift), @@ -49579,6 +49946,9 @@ static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; } + if (typed_array_is_detached(ctx, p)) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + shift = typed_array_size_log2(p->class_id); switch(shift) { case 0: |