diff options
Diffstat (limited to 'lib/monoucha0/monoucha/qjs/quickjs.c')
-rw-r--r-- | lib/monoucha0/monoucha/qjs/quickjs.c | 3338 |
1 files changed, 2522 insertions, 816 deletions
diff --git a/lib/monoucha0/monoucha/qjs/quickjs.c b/lib/monoucha0/monoucha/qjs/quickjs.c index 8909a934..80c492bb 100644 --- a/lib/monoucha0/monoucha/qjs/quickjs.c +++ b/lib/monoucha0/monoucha/qjs/quickjs.c @@ -1,8 +1,8 @@ /* * QuickJS Javascript Engine * - * Copyright (c) 2017-2024 Fabrice Bellard - * Copyright (c) 2017-2024 Charlie Gordon + * Copyright (c) 2017-2025 Fabrice Bellard + * Copyright (c) 2017-2025 Charlie Gordon * Copyright (c) 2023-2025 Ben Noordhuis * Copyright (c) 2023-2025 Saúl Ibarra Corretgé * @@ -47,7 +47,7 @@ #include "list.h" #include "quickjs.h" #include "libregexp.h" -#include "libbf.h" +#include "xsum.h" #if defined(EMSCRIPTEN) || defined(_MSC_VER) #define DIRECT_DISPATCH 0 @@ -237,6 +237,11 @@ typedef struct JSRuntimeFinalizerState { void *arg; } JSRuntimeFinalizerState; +typedef struct JSValueLink { + struct JSValueLink *next; + JSValueConst value; +} JSValueLink; + struct JSRuntime { JSMallocFunctions mf; JSMallocState malloc_state; @@ -286,6 +291,12 @@ struct JSRuntime { JSInterruptHandler *interrupt_handler; void *interrupt_opaque; + JSPromiseHook *promise_hook; + void *promise_hook_opaque; + // for smuggling the parent promise from js_promise_then + // to js_promise_constructor + JSValueLink *parent_promise; + JSHostPromiseRejectionTracker *host_promise_rejection_tracker; void *host_promise_rejection_tracker_opaque; @@ -308,7 +319,6 @@ struct JSRuntime { int shape_hash_size; int shape_hash_count; /* number of hashed shapes */ JSShape **shape_hash; - bf_context_t bf_ctx; void *user_opaque; void *libc_opaque; JSRuntimeFinalizerState *finalizers; @@ -369,15 +379,7 @@ typedef struct JSVarRef { struct { int __gc_ref_count; /* corresponds to header.ref_count */ uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ - - /* 0 : the JSVarRef is on the stack. header.link is an element - of JSStackFrame.var_ref_list. - 1 : the JSVarRef is detached. header.link has the normal meanning - */ - uint8_t is_detached : 1; - uint8_t is_arg : 1; - uint16_t var_idx; /* index of the corresponding function variable on - the stack */ + bool is_detached; }; }; JSValue *pvalue; /* pointer to the value, either on the stack or @@ -389,19 +391,48 @@ typedef struct JSRefCountHeader { int ref_count; } JSRefCountHeader; -/* the same structure is used for big integers. - Big integers are never infinite or NaNs */ +/* bigint */ +typedef int32_t js_slimb_t; +typedef uint32_t js_limb_t; +typedef int64_t js_sdlimb_t; +typedef uint64_t js_dlimb_t; + +#define JS_LIMB_DIGITS 9 + +/* Must match the size of short_big_int in JSValueUnion */ +#define JS_LIMB_BITS 32 +#define JS_SHORT_BIG_INT_BITS JS_LIMB_BITS +#define JS_BIGINT_MAX_SIZE ((1024 * 1024) / JS_LIMB_BITS) /* in limbs */ +#define JS_SHORT_BIG_INT_MIN INT32_MIN +#define JS_SHORT_BIG_INT_MAX INT32_MAX + + typedef struct JSBigInt { JSRefCountHeader header; /* must come first, 32-bit */ - bf_t num; + uint32_t len; /* number of limbs, >= 1 */ + js_limb_t tab[]; /* two's complement representation, always + normalized so that 'len' is the minimum + possible length >= 1 */ } JSBigInt; +/* this bigint structure can hold a 64 bit integer */ +typedef struct { + js_limb_t big_int_buf[sizeof(JSBigInt) / sizeof(js_limb_t)]; /* for JSBigInt */ + /* must come just after */ + js_limb_t tab[(64 + JS_LIMB_BITS - 1) / JS_LIMB_BITS]; +} JSBigIntBuf; + typedef enum { JS_AUTOINIT_ID_PROTOTYPE, JS_AUTOINIT_ID_MODULE_NS, JS_AUTOINIT_ID_PROP, + JS_AUTOINIT_ID_BYTECODE, } JSAutoInitIDEnum; +enum { + JS_BUILTIN_ARRAY_FROMASYNC = 1, +}; + /* must be large enough to have a negligible runtime cost and small enough to call the interrupt callback often. */ #define JS_INTERRUPT_COUNTER_INIT 10000 @@ -440,7 +471,7 @@ struct JSContext { double time_origin; uint64_t random_state; - bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */ + /* when the counter reaches zero, JSRutime.interrupt_handler is called */ int interrupt_counter; @@ -478,6 +509,26 @@ typedef struct JSWeakRefRecord { } u; } JSWeakRefRecord; +typedef struct JSMapRecord { + int ref_count; /* used during enumeration to avoid freeing the record */ + bool empty; /* true if the record is deleted */ + struct JSMapState *map; + struct list_head link; + struct list_head hash_link; + JSValue key; + JSValue value; +} JSMapRecord; + +typedef struct JSMapState { + bool is_weak; /* true if WeakSet/WeakMap */ + struct list_head records; /* list of JSMapRecord.link */ + uint32_t record_count; + struct list_head *hash_table; + uint32_t hash_size; /* must be a power of two */ + uint32_t record_count_threshold; /* count at which a hash table + resize is needed */ +} JSMapState; + enum { JS_ATOM_TYPE_STRING = 1, JS_ATOM_TYPE_GLOBAL_SYMBOL, @@ -1122,6 +1173,12 @@ static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def, JSAtom name); static JSValue js_array_push(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int unshift); +static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv); +static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv, int magic); +static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); typedef enum JSStrictEqModeEnum { JS_EQ_STRICT, @@ -1137,18 +1194,7 @@ static bool js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val); static JSProperty *add_property(JSContext *ctx, JSObject *p, JSAtom prop, int prop_flags); -static JSValue JS_NewBigInt(JSContext *ctx); -static inline bf_t *JS_GetBigInt(JSValueConst val) -{ - JSBigInt *p = JS_VALUE_GET_PTR(val); - return &p->num; -} -static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val); -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 bf_t *JS_ToBigInt1(JSContext *ctx, bf_t *buf, JSValueConst val); -static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf); JSValue JS_ThrowOutOfMemory(JSContext *ctx); static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx); static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); @@ -1505,13 +1551,6 @@ void *js_mallocz_rt(JSRuntime *rt, size_t size) return js_calloc_rt(rt, 1, size); } -/* called by libbf */ -static void *js_bf_realloc(void *opaque, void *ptr, size_t size) -{ - JSRuntime *rt = opaque; - return js_realloc_rt(rt, ptr, size); -} - /* Throw out of memory in case of error */ void *js_calloc(JSContext *ctx, size_t count, size_t size) { @@ -1691,8 +1730,8 @@ static JSClassShortDef const js_std_class_def[] = { { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */ { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */ { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */ - { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */ - { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKSET */ + { JS_ATOM_WeakMap, js_map_finalizer, NULL }, /* JS_CLASS_WEAKMAP */ + { JS_ATOM_WeakSet, js_map_finalizer, NULL }, /* JS_CLASS_WEAKSET */ { JS_ATOM_Iterator, NULL, NULL }, /* JS_CLASS_ITERATOR */ { JS_ATOM_IteratorHelper, js_iterator_helper_finalizer, js_iterator_helper_mark }, /* JS_CLASS_ITERATOR_HELPER */ { JS_ATOM_IteratorWrap, js_iterator_wrap_finalizer, js_iterator_wrap_mark }, /* JS_CLASS_ITERATOR_WRAP */ @@ -1770,8 +1809,6 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) rt->malloc_state = ms; rt->malloc_gc_threshold = 256 * 1024; - bf_context_init(&rt->bf_ctx, js_bf_realloc, rt); - init_list_head(&rt->context_list); init_list_head(&rt->gc_obj_list); init_list_head(&rt->gc_zero_ref_count_list); @@ -2127,8 +2164,6 @@ void JS_FreeRuntime(JSRuntime *rt) } js_free_rt(rt, rt->class_array); - bf_context_end(&rt->bf_ctx); - #ifdef ENABLE_DUMPS // JS_DUMP_ATOM_LEAKS /* only the atoms defined in JS_InitAtoms() should be left */ if (check_dump_flag(rt, JS_DUMP_ATOM_LEAKS)) { @@ -2275,7 +2310,6 @@ JSContext *JS_NewContextRaw(JSRuntime *rt) } ctx->rt = rt; list_add_tail(&ctx->link, &rt->context_list); - ctx->bf_ctx = &rt->bf_ctx; for(i = 0; i < rt->class_count; i++) ctx->class_proto[i] = JS_NULL; ctx->array_ctor = JS_NULL; @@ -3445,6 +3479,8 @@ const char *JS_AtomToCString(JSContext *ctx, JSAtom atom) return cstr; } +#ifndef QJS_DISABLE_PARSER + /* return a string atom containing name concatenated with str1 */ /* `str1` may be pure ASCII or UTF-8 encoded */ // TODO(chqrlie): use string concatenation instead of UTF-8 conversion @@ -3487,6 +3523,8 @@ static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n) return js_atom_concat_str(ctx, name, buf); } +#endif // QJS_DISABLE_PARSER + static inline bool JS_IsEmptyString(JSValueConst v) { return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0; @@ -4041,6 +4079,19 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) return JS_MKPTR(JS_TAG_STRING, str); } +JSValue JS_NewTwoByteString(JSContext *ctx, const uint16_t *buf, size_t len) +{ + JSString *str; + + if (!len) + return JS_AtomToString(ctx, JS_ATOM_empty_string); + str = js_alloc_string(ctx, len, 1); + if (!str) + return JS_EXCEPTION; + memcpy(str16(str), buf, len * sizeof(*buf)); + return JS_MKPTR(JS_TAG_STRING, str); +} + static JSValue JS_ConcatString3(JSContext *ctx, const char *str1, JSValue str2, const char *str3) { @@ -5778,9 +5829,8 @@ static void js_free_value_rt(JSRuntime *rt, JSValue v) break; case JS_TAG_BIG_INT: { - JSBigInt *bf = JS_VALUE_GET_PTR(v); - bf_delete(&bf->num); - js_free_rt(rt, bf); + JSBigInt *p = JS_VALUE_GET_PTR(v); + js_free_rt(rt, p); } break; case JS_TAG_SYMBOL: @@ -5839,6 +5889,22 @@ void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) } } +static void mark_weak_map_value(JSRuntime *rt, JSWeakRefRecord *first_weak_ref, JS_MarkFunc *mark_func) { + JSWeakRefRecord *wr; + JSMapRecord *mr; + JSMapState *s; + + for (wr = first_weak_ref; wr != NULL; wr = wr->next_weak_ref) { + if (wr->kind == JS_WEAK_REF_KIND_MAP) { + mr = wr->u.map_record; + s = mr->map; + assert(s->is_weak); + assert(!mr->empty); /* no iterator on WeakMap/WeakSet */ + JS_MarkValue(rt, mr->value, mark_func); + } + } +} + static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, JS_MarkFunc *mark_func) { @@ -5878,6 +5944,10 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, prs++; } + if (unlikely(p->first_weak_ref)) { + mark_weak_map_value(rt, p->first_weak_ref, mark_func); + } + if (p->class_id != JS_CLASS_OBJECT) { JSClassGCMark *gc_mark; gc_mark = rt->class_array[p->class_id].gc_mark; @@ -7219,15 +7289,19 @@ static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); } +static void JS_ThrowInterrupted(JSContext *ctx) +{ + JS_ThrowInternalError(ctx, "interrupted"); + JS_SetUncatchableError(ctx, ctx->rt->current_exception); +} + static no_inline __exception int __js_poll_interrupts(JSContext *ctx) { JSRuntime *rt = ctx->rt; ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; if (rt->interrupt_handler) { if (rt->interrupt_handler(rt, rt->interrupt_opaque)) { - /* XXX: should set a specific flag to avoid catching */ - JS_ThrowInternalError(ctx, "interrupted"); - js_set_uncatchable_error(ctx, ctx->rt->current_exception, true); + JS_ThrowInterrupted(ctx); return -1; } } @@ -7331,6 +7405,7 @@ static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val) { JSValue ret; switch(JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: ret = ctx->class_proto[JS_CLASS_BIG_INT]; break; @@ -7494,13 +7569,55 @@ int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj) return JS_OrdinaryIsInstanceOf(ctx, val, obj); } +#include "builtin-array-fromasync.h" + +static JSValue js_bytecode_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, + void *opaque) +{ + switch ((uintptr_t)opaque) { + default: + abort(); + case JS_BUILTIN_ARRAY_FROMASYNC: + { + JSValue obj = JS_ReadObject(ctx, qjsc_builtin_array_fromasync, + sizeof(qjsc_builtin_array_fromasync), + JS_READ_OBJ_BYTECODE); + if (JS_IsException(obj)) + return JS_EXCEPTION; + JSValue fun = JS_EvalFunction(ctx, obj); + if (JS_IsException(fun)) + return JS_EXCEPTION; + assert(JS_IsFunction(ctx, fun)); + JSValue args[] = { + JS_NewCFunction(ctx, js_array_constructor, "Array", 0), + JS_NewCFunctionMagic(ctx, js_error_constructor, "TypeError", 1, + JS_CFUNC_constructor_or_func_magic, + JS_TYPE_ERROR), + JS_AtomToValue(ctx, JS_ATOM_Symbol_asyncIterator), + JS_NewCFunctionMagic(ctx, js_object_defineProperty, + "Object.defineProperty", 3, + JS_CFUNC_generic_magic, 0), + JS_AtomToValue(ctx, JS_ATOM_Symbol_iterator), + }; + JSValue result = JS_Call(ctx, fun, JS_UNDEFINED, + countof(args), vc(args)); + for (size_t i = 0; i < countof(args); i++) + JS_FreeValue(ctx, args[i]); + JS_FreeValue(ctx, fun); + return result; + } + } + return JS_UNDEFINED; +} + /* return the value associated to the autoinit property or an exception */ typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); -static JSAutoInitFunc *js_autoinit_func_table[] = { +static JSAutoInitFunc *const js_autoinit_func_table[] = { js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */ js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */ JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */ + js_bytecode_autoinit, /* JS_AUTOINIT_ID_BYTECODE */ }; /* warning: 'prs' is reallocated after it */ @@ -7534,9 +7651,8 @@ static JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, JSObject *p; JSProperty *pr; JSShapeProperty *prs; - uint32_t tag, proto_depth; + uint32_t tag; - proto_depth = 0; tag = JS_VALUE_GET_TAG(obj); if (unlikely(tag != JS_TAG_OBJECT)) { switch(tag) { @@ -7658,7 +7774,6 @@ static JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, } } } - proto_depth++; p = p->shape->proto; if (!p) break; @@ -8988,6 +9103,8 @@ retry: goto retry2; } else if (!(prs->flags & JS_PROP_WRITABLE)) { goto read_only_prop; + } else { + break; } } } @@ -10442,11 +10559,24 @@ static int JS_ToBoolFree(JSContext *ctx, JSValue val) JS_FreeValue(ctx, val); return ret; } + case JS_TAG_SHORT_BIG_INT: + return JS_VALUE_GET_SHORT_BIG_INT(val) != 0; case JS_TAG_BIG_INT: { JSBigInt *p = JS_VALUE_GET_PTR(val); bool ret; - ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN; + int i; + + /* fail safe: we assume it is not necessarily + normalized. Beginning from the MSB ensures that the + test is fast. */ + ret = false; + for(i = p->len - 1; i >= 0; i--) { + if (p->tab[i] != 0) { + ret = true; + break; + } + } JS_FreeValue(ctx, val); return ret; } @@ -10509,6 +10639,1392 @@ static inline int to_digit(int c) return 36; } +/* bigint support */ + +#define ADDC(res, carry_out, op1, op2, carry_in) \ +do { \ + js_limb_t __v, __a, __k, __k1; \ + __v = (op1); \ + __a = __v + (op2); \ + __k1 = __a < __v; \ + __k = (carry_in); \ + __a = __a + __k; \ + carry_out = (__a < __k) | __k1; \ + res = __a; \ +} while (0) + +/* a != 0 */ +static inline js_limb_t js_limb_clz(js_limb_t a) +{ + return clz32(a); +} + +static js_limb_t mp_add(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2, + js_limb_t n, js_limb_t carry) +{ + int i; + for(i = 0;i < n; i++) { + ADDC(res[i], carry, op1[i], op2[i], carry); + } + return carry; +} + +static js_limb_t mp_sub(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2, + int n, js_limb_t carry) +{ + int i; + js_limb_t k, a, v, k1; + + k = carry; + for(i=0;i<n;i++) { + v = op1[i]; + a = v - op2[i]; + k1 = a > v; + v = a - k; + k = (v > a) | k1; + res[i] = v; + } + return k; +} + +/* compute 0 - op2. carry = 0 or 1. */ +static js_limb_t mp_neg(js_limb_t *res, const js_limb_t *op2, int n) +{ + int i; + js_limb_t v, carry; + + carry = 1; + for(i=0;i<n;i++) { + v = ~op2[i] + carry; + carry = v < carry; + res[i] = v; + } + return carry; +} + +/* tabr[] = taba[] * b + l. Return the high carry */ +static js_limb_t mp_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b, js_limb_t l) +{ + js_limb_t i; + js_dlimb_t t; + + for(i = 0; i < n; i++) { + t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l; + tabr[i] = t; + l = t >> JS_LIMB_BITS; + } + return l; +} + +static js_limb_t mp_div1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b, js_limb_t r) +{ + js_slimb_t i; + js_dlimb_t a1; + for(i = n - 1; i >= 0; i--) { + a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i]; + tabr[i] = a1 / b; + r = a1 % b; + } + return r; +} + +/* tabr[] += taba[] * b, return the high word. */ +static js_limb_t mp_add_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b) +{ + js_limb_t i, l; + js_dlimb_t t; + + l = 0; + for(i = 0; i < n; i++) { + t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l + tabr[i]; + tabr[i] = t; + l = t >> JS_LIMB_BITS; + } + return l; +} + +/* size of the result : op1_size + op2_size. */ +static void mp_mul_basecase(js_limb_t *result, + const js_limb_t *op1, js_limb_t op1_size, + const js_limb_t *op2, js_limb_t op2_size) +{ + int i; + js_limb_t r; + + result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0); + for(i=1;i<op2_size;i++) { + r = mp_add_mul1(result + i, op1, op1_size, op2[i]); + result[i + op1_size] = r; + } +} + +/* tabr[] -= taba[] * b. Return the value to substract to the high + word. */ +static js_limb_t mp_sub_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b) +{ + js_limb_t i, l; + js_dlimb_t t; + + l = 0; + for(i = 0; i < n; i++) { + t = tabr[i] - (js_dlimb_t)taba[i] * (js_dlimb_t)b - l; + tabr[i] = t; + l = -(t >> JS_LIMB_BITS); + } + return l; +} + +/* WARNING: d must be >= 2^(JS_LIMB_BITS-1) */ +static inline js_limb_t udiv1norm_init(js_limb_t d) +{ + js_limb_t a0, a1; + a1 = -d - 1; + a0 = -1; + return (((js_dlimb_t)a1 << JS_LIMB_BITS) | a0) / d; +} + +/* return the quotient and the remainder in '*pr'of 'a1*2^JS_LIMB_BITS+a0 + / d' with 0 <= a1 < d. */ +static inline js_limb_t udiv1norm(js_limb_t *pr, js_limb_t a1, js_limb_t a0, + js_limb_t d, js_limb_t d_inv) +{ + js_limb_t n1m, n_adj, q, r, ah; + js_dlimb_t a; + n1m = ((js_slimb_t)a0 >> (JS_LIMB_BITS - 1)); + n_adj = a0 + (n1m & d); + a = (js_dlimb_t)d_inv * (a1 - n1m) + n_adj; + q = (a >> JS_LIMB_BITS) + a1; + /* compute a - q * r and update q so that the remainder is\ + between 0 and d - 1 */ + a = ((js_dlimb_t)a1 << JS_LIMB_BITS) | a0; + a = a - (js_dlimb_t)q * d - d; + ah = a >> JS_LIMB_BITS; + q += 1 + ah; + r = (js_limb_t)a + (ah & d); + *pr = r; + return q; +} + +#define UDIV1NORM_THRESHOLD 3 + +/* b must be >= 1 << (JS_LIMB_BITS - 1) */ +static js_limb_t mp_div1norm(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b, js_limb_t r) +{ + js_slimb_t i; + + if (n >= UDIV1NORM_THRESHOLD) { + js_limb_t b_inv; + b_inv = udiv1norm_init(b); + for(i = n - 1; i >= 0; i--) { + tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv); + } + } else { + js_dlimb_t a1; + for(i = n - 1; i >= 0; i--) { + a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i]; + tabr[i] = a1 / b; + r = a1 % b; + } + } + return r; +} + +/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb + - 1] must be >= 1 << (JS_LIMB_BITS - 1). na - nb must be >= 0. 'taba' + is modified and contains the remainder (nb limbs). tabq[0..na-nb] + contains the quotient with tabq[na - nb] <= 1. */ +static void mp_divnorm(js_limb_t *tabq, js_limb_t *taba, js_limb_t na, + const js_limb_t *tabb, js_limb_t nb) +{ + js_limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r; + int i, j; + + b1 = tabb[nb - 1]; + if (nb == 1) { + taba[0] = mp_div1norm(tabq, taba, na, b1, 0); + return; + } + n = na - nb; + + if (n >= UDIV1NORM_THRESHOLD) + b1_inv = udiv1norm_init(b1); + else + b1_inv = 0; + + /* first iteration: the quotient is only 0 or 1 */ + q = 1; + for(j = nb - 1; j >= 0; j--) { + if (taba[n + j] != tabb[j]) { + if (taba[n + j] < tabb[j]) + q = 0; + break; + } + } + tabq[n] = q; + if (q) { + mp_sub(taba + n, taba + n, tabb, nb, 0); + } + + for(i = n - 1; i >= 0; i--) { + if (unlikely(taba[i + nb] >= b1)) { + q = -1; + } else if (b1_inv) { + q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv); + } else { + js_dlimb_t al; + al = ((js_dlimb_t)taba[i + nb] << JS_LIMB_BITS) | taba[i + nb - 1]; + q = al / b1; + r = al % b1; + } + r = mp_sub_mul1(taba + i, tabb, nb, q); + + v = taba[i + nb]; + a = v - r; + c = (a > v); + taba[i + nb] = a; + + if (c != 0) { + /* negative result */ + for(;;) { + q--; + c = mp_add(taba + i, taba + i, tabb, nb, 0); + /* propagate carry and test if positive result */ + if (c != 0) { + if (++taba[i + nb] == 0) { + break; + } + } + } + } + tabq[i] = q; + } +} + +/* 1 <= shift <= JS_LIMB_BITS - 1 */ +static js_limb_t mp_shl(js_limb_t *tabr, const js_limb_t *taba, int n, + int shift) +{ + int i; + js_limb_t l, v; + l = 0; + for(i = 0; i < n; i++) { + v = taba[i]; + tabr[i] = (v << shift) | l; + l = v >> (JS_LIMB_BITS - shift); + } + return l; +} + +/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). + 1 <= shift <= LIMB_BITS - 1 */ +static js_limb_t mp_shr(js_limb_t *tab_r, const js_limb_t *tab, int n, + int shift, js_limb_t high) +{ + int i; + js_limb_t l, a; + + l = high; + for(i = n - 1; i >= 0; i--) { + a = tab[i]; + tab_r[i] = (a >> shift) | (l << (JS_LIMB_BITS - shift)); + l = a; + } + return l & (((js_limb_t)1 << shift) - 1); +} + +static JSBigInt *js_bigint_new(JSContext *ctx, int len) +{ + JSBigInt *r; + if (len > JS_BIGINT_MAX_SIZE) { + JS_ThrowRangeError(ctx, "BigInt is too large to allocate"); + return NULL; + } + r = js_malloc(ctx, sizeof(JSBigInt) + len * sizeof(js_limb_t)); + if (!r) + return NULL; + r->header.ref_count = 1; + r->len = len; + return r; +} + +static JSBigInt *js_bigint_set_si(JSBigIntBuf *buf, js_slimb_t a) +{ + JSBigInt *r = (JSBigInt *)buf->big_int_buf; + r->header.ref_count = 0; /* fail safe */ + r->len = 1; + r->tab[0] = a; + return r; +} + +static JSBigInt *js_bigint_set_si64(JSBigIntBuf *buf, int64_t a) +{ + JSBigInt *r = (JSBigInt *)buf->big_int_buf; + r->header.ref_count = 0; /* fail safe */ + if (a >= INT32_MIN && a <= INT32_MAX) { + r->len = 1; + r->tab[0] = a; + } else { + r->len = 2; + r->tab[0] = a; + r->tab[1] = a >> JS_LIMB_BITS; + } + return r; +} + +/* val must be a short big int */ +static JSBigInt *js_bigint_set_short(JSBigIntBuf *buf, JSValueConst val) +{ + return js_bigint_set_si(buf, JS_VALUE_GET_SHORT_BIG_INT(val)); +} + +static __maybe_unused void js_bigint_dump1(JSContext *ctx, const char *str, + const js_limb_t *tab, int len) +{ + int i; + printf("%s: ", str); + for(i = len - 1; i >= 0; i--) { + printf(" %08x", tab[i]); + } + printf("\n"); +} + +static __maybe_unused void js_bigint_dump(JSContext *ctx, const char *str, + const JSBigInt *p) +{ + js_bigint_dump1(ctx, str, p->tab, p->len); +} + +static JSBigInt *js_bigint_new_si(JSContext *ctx, js_slimb_t a) +{ + JSBigInt *r; + r = js_bigint_new(ctx, 1); + if (!r) + return NULL; + r->tab[0] = a; + return r; +} + +static JSBigInt *js_bigint_new_si64(JSContext *ctx, int64_t a) +{ + if (a >= INT32_MIN && a <= INT32_MAX) { + return js_bigint_new_si(ctx, a); + } else { + JSBigInt *r; + r = js_bigint_new(ctx, 2); + if (!r) + return NULL; + r->tab[0] = a; + r->tab[1] = a >> 32; + return r; + } +} + +static JSBigInt *js_bigint_new_ui64(JSContext *ctx, uint64_t a) +{ + if (a <= INT64_MAX) { + return js_bigint_new_si64(ctx, a); + } else { + JSBigInt *r; + r = js_bigint_new(ctx, (65 + JS_LIMB_BITS - 1) / JS_LIMB_BITS); + if (!r) + return NULL; + r->tab[0] = a; + r->tab[1] = a >> 32; + r->tab[2] = 0; + return r; + } +} + +static JSBigInt *js_bigint_new_di(JSContext *ctx, js_sdlimb_t a) +{ + JSBigInt *r; + if (a == (js_slimb_t)a) { + r = js_bigint_new(ctx, 1); + if (!r) + return NULL; + r->tab[0] = a; + } else { + r = js_bigint_new(ctx, 2); + if (!r) + return NULL; + r->tab[0] = a; + r->tab[1] = a >> JS_LIMB_BITS; + } + return r; +} + +/* Remove redundant high order limbs. Warning: 'a' may be + reallocated. Can never fail. +*/ +static JSBigInt *js_bigint_normalize1(JSContext *ctx, JSBigInt *a, int l) +{ + js_limb_t v; + + assert(a->header.ref_count == 1); + while (l > 1) { + v = a->tab[l - 1]; + if ((v != 0 && v != -1) || + (v & 1) != (a->tab[l - 2] >> (JS_LIMB_BITS - 1))) { + break; + } + l--; + } + if (l != a->len) { + JSBigInt *a1; + /* realloc to reduce the size */ + a->len = l; + a1 = js_realloc(ctx, a, sizeof(JSBigInt) + l * sizeof(js_limb_t)); + if (a1) + a = a1; + } + return a; +} + +static JSBigInt *js_bigint_normalize(JSContext *ctx, JSBigInt *a) +{ + return js_bigint_normalize1(ctx, a, a->len); +} + +/* return 0 or 1 depending on the sign */ +static inline int js_bigint_sign(const JSBigInt *a) +{ + return a->tab[a->len - 1] >> (JS_LIMB_BITS - 1); +} + +static js_slimb_t js_bigint_get_si_sat(const JSBigInt *a) +{ + if (a->len == 1) { + return a->tab[0]; + } else { + if (js_bigint_sign(a)) + return INT32_MIN; + else + return INT32_MAX; + } +} + +/* add the op1 limb */ +static JSBigInt *js_bigint_extend(JSContext *ctx, JSBigInt *r, + js_limb_t op1) +{ + int n2 = r->len; + if ((op1 != 0 && op1 != -1) || + (op1 & 1) != r->tab[n2 - 1] >> (JS_LIMB_BITS - 1)) { + JSBigInt *r1; + r1 = js_realloc(ctx, r, + sizeof(JSBigInt) + (n2 + 1) * sizeof(js_limb_t)); + if (!r1) { + js_free(ctx, r); + return NULL; + } + r = r1; + r->len = n2 + 1; + r->tab[n2] = op1; + } else { + /* otherwise still need to normalize the result */ + r = js_bigint_normalize(ctx, r); + } + return r; +} + +/* return NULL in case of error. Compute a + b (b_neg = 0) or a - b + (b_neg = 1) */ +/* XXX: optimize */ +static JSBigInt *js_bigint_add(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b, int b_neg) +{ + JSBigInt *r; + int n1, n2, i; + js_limb_t carry, op1, op2, a_sign, b_sign; + + n2 = max_int(a->len, b->len); + n1 = min_int(a->len, b->len); + r = js_bigint_new(ctx, n2); + if (!r) + return NULL; + /* XXX: optimize */ + /* common part */ + carry = b_neg; + for(i = 0; i < n1; i++) { + op1 = a->tab[i]; + op2 = b->tab[i] ^ (-b_neg); + ADDC(r->tab[i], carry, op1, op2, carry); + } + a_sign = -js_bigint_sign(a); + b_sign = (-js_bigint_sign(b)) ^ (-b_neg); + /* part with sign extension of one operand */ + if (a->len > b->len) { + for(i = n1; i < n2; i++) { + op1 = a->tab[i]; + ADDC(r->tab[i], carry, op1, b_sign, carry); + } + } else if (a->len < b->len) { + for(i = n1; i < n2; i++) { + op2 = b->tab[i] ^ (-b_neg); + ADDC(r->tab[i], carry, a_sign, op2, carry); + } + } + + /* part with sign extension for both operands. Extend the result + if necessary */ + return js_bigint_extend(ctx, r, a_sign + b_sign + carry); +} + +/* XXX: optimize */ +static JSBigInt *js_bigint_neg(JSContext *ctx, const JSBigInt *a) +{ + JSBigIntBuf buf; + JSBigInt *b; + b = js_bigint_set_si(&buf, 0); + return js_bigint_add(ctx, b, a, 1); +} + +static JSBigInt *js_bigint_mul(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b) +{ + JSBigInt *r; + + r = js_bigint_new(ctx, a->len + b->len); + if (!r) + return NULL; + mp_mul_basecase(r->tab, a->tab, a->len, b->tab, b->len); + /* correct the result if negative operands (no overflow is + possible) */ + if (js_bigint_sign(a)) + mp_sub(r->tab + a->len, r->tab + a->len, b->tab, b->len, 0); + if (js_bigint_sign(b)) + mp_sub(r->tab + b->len, r->tab + b->len, a->tab, a->len, 0); + return js_bigint_normalize(ctx, r); +} + +/* return the division or the remainder. 'b' must be != 0. return NULL + in case of exception (division by zero or memory error) */ +static JSBigInt *js_bigint_divrem(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b, bool is_rem) +{ + JSBigInt *r, *q; + js_limb_t *tabb, h; + int na, nb, a_sign, b_sign, shift; + + if (b->len == 1 && b->tab[0] == 0) { + JS_ThrowRangeError(ctx, "BigInt division by zero"); + return NULL; + } + + a_sign = js_bigint_sign(a); + b_sign = js_bigint_sign(b); + na = a->len; + nb = b->len; + + r = js_bigint_new(ctx, na + 2); + if (!r) + return NULL; + if (a_sign) { + mp_neg(r->tab, a->tab, na); + } else { + memcpy(r->tab, a->tab, na * sizeof(a->tab[0])); + } + /* normalize */ + while (na > 1 && r->tab[na - 1] == 0) + na--; + + tabb = js_malloc(ctx, nb * sizeof(tabb[0])); + if (!tabb) { + js_free(ctx, r); + return NULL; + } + if (b_sign) { + mp_neg(tabb, b->tab, nb); + } else { + memcpy(tabb, b->tab, nb * sizeof(tabb[0])); + } + /* normalize */ + while (nb > 1 && tabb[nb - 1] == 0) + nb--; + + /* trivial case if 'a' is small */ + if (na < nb) { + js_free(ctx, r); + js_free(ctx, tabb); + if (is_rem) { + /* r = a */ + r = js_bigint_new(ctx, a->len); + if (!r) + return NULL; + memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0])); + return r; + } else { + /* q = 0 */ + return js_bigint_new_si(ctx, 0); + } + } + + /* normalize 'b' */ + shift = js_limb_clz(tabb[nb - 1]); + if (shift != 0) { + mp_shl(tabb, tabb, nb, shift); + h = mp_shl(r->tab, r->tab, na, shift); + if (h != 0) + r->tab[na++] = h; + } + + q = js_bigint_new(ctx, na - nb + 2); /* one more limb for the sign */ + if (!q) { + js_free(ctx, r); + js_free(ctx, tabb); + return NULL; + } + + // js_bigint_dump1(ctx, "a", r->tab, na); + // js_bigint_dump1(ctx, "b", tabb, nb); + mp_divnorm(q->tab, r->tab, na, tabb, nb); + js_free(ctx, tabb); + + if (is_rem) { + js_free(ctx, q); + if (shift != 0) + mp_shr(r->tab, r->tab, nb, shift, 0); + r->tab[nb++] = 0; + if (a_sign) + mp_neg(r->tab, r->tab, nb); + r = js_bigint_normalize1(ctx, r, nb); + return r; + } else { + js_free(ctx, r); + q->tab[na - nb + 1] = 0; + if (a_sign ^ b_sign) { + mp_neg(q->tab, q->tab, q->len); + } + q = js_bigint_normalize(ctx, q); + return q; + } +} + +/* and, or, xor */ +static JSBigInt *js_bigint_logic(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b, OPCodeEnum op) +{ + JSBigInt *r; + js_limb_t b_sign; + int a_len, b_len, i; + + if (a->len < b->len) { + const JSBigInt *tmp; + tmp = a; + a = b; + b = tmp; + } + /* a_len >= b_len */ + a_len = a->len; + b_len = b->len; + b_sign = -js_bigint_sign(b); + + r = js_bigint_new(ctx, a_len); + if (!r) + return NULL; + switch(op) { + case OP_or: + for(i = 0; i < b_len; i++) { + r->tab[i] = a->tab[i] | b->tab[i]; + } + for(i = b_len; i < a_len; i++) { + r->tab[i] = a->tab[i] | b_sign; + } + break; + case OP_and: + for(i = 0; i < b_len; i++) { + r->tab[i] = a->tab[i] & b->tab[i]; + } + for(i = b_len; i < a_len; i++) { + r->tab[i] = a->tab[i] & b_sign; + } + break; + case OP_xor: + for(i = 0; i < b_len; i++) { + r->tab[i] = a->tab[i] ^ b->tab[i]; + } + for(i = b_len; i < a_len; i++) { + r->tab[i] = a->tab[i] ^ b_sign; + } + break; + default: + abort(); + } + return js_bigint_normalize(ctx, r); +} + +static JSBigInt *js_bigint_not(JSContext *ctx, const JSBigInt *a) +{ + JSBigInt *r; + int i; + + r = js_bigint_new(ctx, a->len); + if (!r) + return NULL; + for(i = 0; i < a->len; i++) { + r->tab[i] = ~a->tab[i]; + } + /* no normalization is needed */ + return r; +} + +static JSBigInt *js_bigint_shl(JSContext *ctx, const JSBigInt *a, + unsigned int shift1) +{ + int d, i, shift; + JSBigInt *r; + js_limb_t l; + + if (a->len == 1 && a->tab[0] == 0) + return js_bigint_new_si(ctx, 0); /* zero case */ + d = shift1 / JS_LIMB_BITS; + shift = shift1 % JS_LIMB_BITS; + r = js_bigint_new(ctx, a->len + d); + if (!r) + return NULL; + for(i = 0; i < d; i++) + r->tab[i] = 0; + if (shift == 0) { + for(i = 0; i < a->len; i++) { + r->tab[i + d] = a->tab[i]; + } + } else { + l = mp_shl(r->tab + d, a->tab, a->len, shift); + if (js_bigint_sign(a)) + l |= (js_limb_t)(-1) << shift; + r = js_bigint_extend(ctx, r, l); + } + return r; +} + +static JSBigInt *js_bigint_shr(JSContext *ctx, const JSBigInt *a, + unsigned int shift1) +{ + int d, i, shift, a_sign, n1; + JSBigInt *r; + + d = shift1 / JS_LIMB_BITS; + shift = shift1 % JS_LIMB_BITS; + a_sign = js_bigint_sign(a); + if (d >= a->len) + return js_bigint_new_si(ctx, -a_sign); + n1 = a->len - d; + r = js_bigint_new(ctx, n1); + if (!r) + return NULL; + if (shift == 0) { + for(i = 0; i < n1; i++) { + r->tab[i] = a->tab[i + d]; + } + /* no normalization is needed */ + } else { + mp_shr(r->tab, a->tab + d, n1, shift, -a_sign); + r = js_bigint_normalize(ctx, r); + } + return r; +} + +static JSBigInt *js_bigint_pow(JSContext *ctx, const JSBigInt *a, JSBigInt *b) +{ + uint32_t e; + int n_bits, i; + JSBigInt *r, *r1; + + /* b must be >= 0 */ + if (js_bigint_sign(b)) { + JS_ThrowRangeError(ctx, "BigInt negative exponent"); + return NULL; + } + if (b->len == 1 && b->tab[0] == 0) { + /* a^0 = 1 */ + return js_bigint_new_si(ctx, 1); + } else if (a->len == 1) { + js_limb_t v; + bool is_neg; + + v = a->tab[0]; + if (v <= 1) + return js_bigint_new_si(ctx, v); + else if (v == -1) + return js_bigint_new_si(ctx, 1 - 2 * (b->tab[0] & 1)); + is_neg = (js_slimb_t)v < 0; + if (is_neg) + v = -v; + if ((v & (v - 1)) == 0) { + uint64_t e1; + int n; + /* v = 2^n */ + n = JS_LIMB_BITS - 1 - js_limb_clz(v); + if (b->len > 1) + goto overflow; + if (b->tab[0] > INT32_MAX) + goto overflow; + e = b->tab[0]; + e1 = (uint64_t)e * n; + if (e1 > JS_BIGINT_MAX_SIZE * JS_LIMB_BITS) + goto overflow; + e = e1; + if (is_neg) + is_neg = b->tab[0] & 1; + r = js_bigint_new(ctx, + (e + JS_LIMB_BITS + 1 - is_neg) / JS_LIMB_BITS); + if (!r) + return NULL; + memset(r->tab, 0, sizeof(r->tab[0]) * r->len); + r->tab[e / JS_LIMB_BITS] = + (js_limb_t)(1 - 2 * is_neg) << (e % JS_LIMB_BITS); + return r; + } + } + if (b->len > 1) + goto overflow; + if (b->tab[0] > INT32_MAX) + goto overflow; + e = b->tab[0]; + n_bits = 32 - clz32(e); + + r = js_bigint_new(ctx, a->len); + if (!r) + return NULL; + memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0])); + for(i = n_bits - 2; i >= 0; i--) { + r1 = js_bigint_mul(ctx, r, r); + if (!r1) + return NULL; + js_free(ctx, r); + r = r1; + if ((e >> i) & 1) { + r1 = js_bigint_mul(ctx, r, a); + if (!r1) + return NULL; + js_free(ctx, r); + r = r1; + } + } + return r; + overflow: + JS_ThrowRangeError(ctx, "BigInt is too large"); + return NULL; +} + +/* return (mant, exp) so that abs(a) ~ mant*2^(exp - (limb_bits - + 1). a must be != 0. */ +static uint64_t js_bigint_get_mant_exp(JSContext *ctx, + int *pexp, const JSBigInt *a) +{ + js_limb_t t[4 - JS_LIMB_BITS / 32], carry, v, low_bits; + int n1, n2, sgn, shift, i, j, e; + uint64_t a1, a0; + + n2 = 4 - JS_LIMB_BITS / 32; + n1 = a->len - n2; + sgn = js_bigint_sign(a); + + /* low_bits != 0 if there are a non zero low bit in abs(a) */ + low_bits = 0; + carry = sgn; + for(i = 0; i < n1; i++) { + v = (a->tab[i] ^ (-sgn)) + carry; + carry = v < carry; + low_bits |= v; + } + /* get the n2 high limbs of abs(a) */ + for(j = 0; j < n2; j++) { + i = j + n1; + if (i < 0) { + v = 0; + } else { + v = (a->tab[i] ^ (-sgn)) + carry; + carry = v < carry; + } + t[j] = v; + } + + a1 = ((uint64_t)t[2] << 32) | t[1]; + a0 = (uint64_t)t[0] << 32; + a0 |= (low_bits != 0); + /* normalize */ + { + shift = clz64(a1); + if (shift != 0) { + a1 = (a1 << shift) | (a0 >> (64 - shift)); + a0 <<= shift; + } + } + a1 |= (a0 != 0); /* keep the bits for the final rounding */ + /* compute the exponent */ + e = a->len * JS_LIMB_BITS - shift - 1; + *pexp = e; + return a1; +} + +/* shift left with round to nearest, ties to even. n >= 1 */ +static uint64_t shr_rndn(uint64_t a, int n) +{ + uint64_t addend = ((a >> n) & 1) + ((1 << (n - 1)) - 1); + return (a + addend) >> n; +} + +/* convert to float64 with round to nearest, ties to even. Return + +/-infinity if too large. */ +static double js_bigint_to_float64(JSContext *ctx, const JSBigInt *a) +{ + int sgn, e; + uint64_t mant; + + if (a->len == 1) { + /* fast case, including zero */ + return (double)(js_slimb_t)a->tab[0]; + } + + sgn = js_bigint_sign(a); + mant = js_bigint_get_mant_exp(ctx, &e, a); + if (e > 1023) { + /* overflow: return infinity */ + mant = 0; + e = 1024; + } else { + mant = (mant >> 1) | (mant & 1); /* avoid overflow in rounding */ + mant = shr_rndn(mant, 10); + /* rounding can cause an overflow */ + if (mant >= ((uint64_t)1 << 53)) { + mant >>= 1; + e++; + } + mant &= (((uint64_t)1 << 52) - 1); + } + return uint64_as_float64(((uint64_t)sgn << 63) | + ((uint64_t)(e + 1023) << 52) | + mant); +} + +/* return (1, NULL) if not an integer, (2, NULL) if NaN or Infinity, + (0, n) if an integer, (0, NULL) in case of memory error */ +static JSBigInt *js_bigint_from_float64(JSContext *ctx, int *pres, double a1) +{ + uint64_t a = float64_as_uint64(a1); + int sgn, e, shift; + uint64_t mant; + JSBigIntBuf buf; + JSBigInt *r; + + sgn = a >> 63; + e = (a >> 52) & ((1 << 11) - 1); + mant = a & (((uint64_t)1 << 52) - 1); + if (e == 2047) { + /* NaN, Infinity */ + *pres = 2; + return NULL; + } + if (e == 0 && mant == 0) { + /* zero */ + *pres = 0; + return js_bigint_new_si(ctx, 0); + } + e -= 1023; + /* 0 < a < 1 : not an integer */ + if (e < 0) + goto not_an_integer; + mant |= (uint64_t)1 << 52; + if (e < 52) { + shift = 52 - e; + /* check that there is no fractional part */ + if (mant & (((uint64_t)1 << shift) - 1)) { + not_an_integer: + *pres = 1; + return NULL; + } + mant >>= shift; + e = 0; + } else { + e -= 52; + } + if (sgn) + mant = -mant; + /* the integer is mant*2^e */ + r = js_bigint_set_si64(&buf, (int64_t)mant); + *pres = 0; + return js_bigint_shl(ctx, r, e); +} + +/* return -1, 0, 1 or (2) (unordered) */ +static int js_bigint_float64_cmp(JSContext *ctx, const JSBigInt *a, + double b) +{ + int b_sign, a_sign, e, f; + uint64_t mant, b1, a_mant; + + b1 = float64_as_uint64(b); + b_sign = b1 >> 63; + e = (b1 >> 52) & ((1 << 11) - 1); + mant = b1 & (((uint64_t)1 << 52) - 1); + a_sign = js_bigint_sign(a); + if (e == 2047) { + if (mant != 0) { + /* NaN */ + return 2; + } else { + /* +/- infinity */ + return 2 * b_sign - 1; + } + } else if (e == 0 && mant == 0) { + /* b = +/-0 */ + if (a->len == 1 && a->tab[0] == 0) + return 0; + else + return 1 - 2 * a_sign; + } else if (a->len == 1 && a->tab[0] == 0) { + /* a = 0, b != 0 */ + return 2 * b_sign - 1; + } else if (a_sign != b_sign) { + return 1 - 2 * a_sign; + } else { + e -= 1023; + /* Note: handling denormals is not necessary because we + compare to integers hence f >= 0 */ + /* compute f so that 2^f <= abs(a) < 2^(f+1) */ + a_mant = js_bigint_get_mant_exp(ctx, &f, a); + if (f != e) { + if (f < e) + return -1; + else + return 1; + } else { + mant = (mant | ((uint64_t)1 << 52)) << 11; /* align to a_mant */ + if (a_mant < mant) + return 2 * a_sign - 1; + else if (a_mant > mant) + return 1 - 2 * a_sign; + else + return 0; + } + } +} + +/* return -1, 0 or 1 */ +static int js_bigint_cmp(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b) +{ + int a_sign, b_sign, res, i; + a_sign = js_bigint_sign(a); + b_sign = js_bigint_sign(b); + if (a_sign != b_sign) { + res = 1 - 2 * a_sign; + } else { + /* we assume the numbers are normalized */ + if (a->len != b->len) { + if (a->len < b->len) + res = 2 * a_sign - 1; + else + res = 1 - 2 * a_sign; + } else { + res = 0; + for(i = a->len -1; i >= 0; i--) { + if (a->tab[i] != b->tab[i]) { + if (a->tab[i] < b->tab[i]) + res = -1; + else + res = 1; + break; + } + } + } + } + return res; +} + +/* contains 10^i */ +static const js_limb_t js_pow_dec[JS_LIMB_DIGITS + 1] = { + 1U, + 10U, + 100U, + 1000U, + 10000U, + 100000U, + 1000000U, + 10000000U, + 100000000U, + 1000000000U, +}; + +/* syntax: [-]digits in base radix. Return NULL if memory error. radix + = 10, 2, 8 or 16. */ +static JSBigInt *js_bigint_from_string(JSContext *ctx, + const char *str, int radix) +{ + const char *p = str; + int is_neg, n_digits, n_limbs, len, log2_radix, n_bits, i; + JSBigInt *r; + js_limb_t v, c, h; + + is_neg = 0; + if (*p == '-') { + is_neg = 1; + p++; + } + while (*p == '0') + p++; + n_digits = strlen(p); + log2_radix = 32 - clz32(radix - 1); /* ceil(log2(radix)) */ + /* compute the maximum number of limbs */ + /* XXX: overflow */ + if (radix == 10) { + n_bits = (n_digits * 27 + 7) / 8; /* >= ceil(n_digits * log2(10)) */ + } else { + n_bits = n_digits * log2_radix; + } + /* we add one extra bit for the sign */ + n_limbs = max_int(1, n_bits / JS_LIMB_BITS + 1); + r = js_bigint_new(ctx, n_limbs); + if (!r) + return NULL; + if (radix == 10) { + int digits_per_limb = JS_LIMB_DIGITS; + len = 1; + r->tab[0] = 0; + for(;;) { + /* XXX: slow */ + v = 0; + for(i = 0; i < digits_per_limb; i++) { + c = to_digit(*p); + if (c >= radix) + break; + p++; + v = v * 10 + c; + } + if (i == 0) + break; + if (len == 1 && r->tab[0] == 0) { + r->tab[0] = v; + } else { + h = mp_mul1(r->tab, r->tab, len, js_pow_dec[i], v); + if (h != 0) { + r->tab[len++] = h; + } + } + } + /* add one extra limb to have the correct sign*/ + if ((r->tab[len - 1] >> (JS_LIMB_BITS - 1)) != 0) + r->tab[len++] = 0; + r->len = len; + } else { + unsigned int bit_pos, shift, pos; + + /* power of two base: no multiplication is needed */ + r->len = n_limbs; + memset(r->tab, 0, sizeof(r->tab[0]) * n_limbs); + for(i = 0; i < n_digits; i++) { + c = to_digit(p[n_digits - 1 - i]); + assert(c < radix); + bit_pos = i * log2_radix; + shift = bit_pos & (JS_LIMB_BITS - 1); + pos = bit_pos / JS_LIMB_BITS; + r->tab[pos] |= c << shift; + /* if log2_radix does not divide JS_LIMB_BITS, needed an + additional op */ + if (shift + log2_radix > JS_LIMB_BITS) { + r->tab[pos + 1] |= c >> (JS_LIMB_BITS - shift); + } + } + } + r = js_bigint_normalize(ctx, r); + /* XXX: could do it in place */ + if (is_neg) { + JSBigInt *r1; + r1 = js_bigint_neg(ctx, r); + js_free(ctx, r); + r = r1; + } + return r; +} + +/* 2 <= base <= 36 */ +static char const digits[36] = { + '0','1','2','3','4','5','6','7','8','9', + 'a','b','c','d','e','f','g','h','i','j', + 'k','l','m','n','o','p','q','r','s','t', + 'u','v','w','x','y','z' +}; + +/* special version going backwards */ +/* XXX: use dtoa.c */ +static char *js_u64toa(char *q, int64_t n, unsigned int base) +{ + int digit; + if (base == 10) { + /* division by known base uses multiplication */ + do { + digit = (uint64_t)n % 10; + n = (uint64_t)n / 10; + *--q = '0' + digit; + } while (n != 0); + } else { + do { + digit = (uint64_t)n % base; + n = (uint64_t)n / base; + *--q = digits[digit]; + } while (n != 0); + } + return q; +} + +/* len >= 1. 2 <= radix <= 36 */ +static char *limb_to_a(char *q, js_limb_t n, unsigned int radix, int len) +{ + int digit, i; + + if (radix == 10) { + /* specific case with constant divisor */ + /* XXX: optimize */ + for(i = 0; i < len; i++) { + digit = (js_limb_t)n % 10; + n = (js_limb_t)n / 10; + *--q = digit + '0'; + } + } else { + for(i = 0; i < len; i++) { + digit = (js_limb_t)n % radix; + n = (js_limb_t)n / radix; + *--q = digits[digit]; + } + } + return q; +} + +#define JS_RADIX_MAX 36 + +static const uint8_t digits_per_limb_table[JS_RADIX_MAX - 1] = { +32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +}; + +static const js_limb_t radix_base_table[JS_RADIX_MAX - 1] = { + 0x00000000, 0xcfd41b91, 0x00000000, 0x48c27395, + 0x81bf1000, 0x75db9c97, 0x40000000, 0xcfd41b91, + 0x3b9aca00, 0x8c8b6d2b, 0x19a10000, 0x309f1021, + 0x57f6c100, 0x98c29b81, 0x00000000, 0x18754571, + 0x247dbc80, 0x3547667b, 0x4c4b4000, 0x6b5a6e1d, + 0x94ace180, 0xcaf18367, 0x0b640000, 0x0e8d4a51, + 0x1269ae40, 0x17179149, 0x1cb91000, 0x23744899, + 0x2b73a840, 0x34e63b41, 0x40000000, 0x4cfa3cc1, + 0x5c13d840, 0x6d91b519, 0x81bf1000, +}; + +static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) { + char buf[66]; + int len; + len = i64toa_radix(buf, JS_VALUE_GET_SHORT_BIG_INT(val), radix); + return js_new_string8_len(ctx, buf, len); + } else { + JSBigInt *r, *tmp = NULL; + char *buf, *q, *buf_end; + int is_neg, n_bits, log2_radix, n_digits; + bool is_binary_radix; + JSValue res; + + assert(JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT); + r = JS_VALUE_GET_PTR(val); + if (r->len == 1 && r->tab[0] == 0) { + /* '0' case */ + return js_new_string8_len(ctx, "0", 1); + } + is_binary_radix = ((radix & (radix - 1)) == 0); + is_neg = js_bigint_sign(r); + if (is_neg) { + tmp = js_bigint_neg(ctx, r); + if (!tmp) + return JS_EXCEPTION; + r = tmp; + } else if (!is_binary_radix) { + /* need to modify 'r' */ + tmp = js_bigint_new(ctx, r->len); + if (!tmp) + return JS_EXCEPTION; + memcpy(tmp->tab, r->tab, r->len * sizeof(r->tab[0])); + r = tmp; + } + log2_radix = 31 - clz32(radix); /* floor(log2(radix)) */ + n_bits = r->len * JS_LIMB_BITS - js_limb_clz(r->tab[r->len - 1]); + /* n_digits is exact only if radix is a power of + two. Otherwise it is >= the exact number of digits */ + n_digits = (n_bits + log2_radix - 1) / log2_radix; + /* XXX: could directly build the JSString */ + buf = js_malloc(ctx, n_digits + is_neg + 1); + if (!buf) { + js_free(ctx, tmp); + return JS_EXCEPTION; + } + q = buf + n_digits + is_neg + 1; + *--q = '\0'; + buf_end = q; + if (!is_binary_radix) { + int len; + js_limb_t radix_base, v; + radix_base = radix_base_table[radix - 2]; + len = r->len; + for(;;) { + /* remove leading zero limbs */ + while (len > 1 && r->tab[len - 1] == 0) + len--; + if (len == 1 && r->tab[0] < radix_base) { + v = r->tab[0]; + if (v != 0) { + q = js_u64toa(q, v, radix); + } + break; + } else { + v = mp_div1(r->tab, r->tab, len, radix_base, 0); + q = limb_to_a(q, v, radix, digits_per_limb_table[radix - 2]); + } + } + } else { + int i, shift; + unsigned int bit_pos, pos, c; + + /* radix is a power of two */ + for(i = 0; i < n_digits; i++) { + bit_pos = i * log2_radix; + pos = bit_pos / JS_LIMB_BITS; + shift = bit_pos % JS_LIMB_BITS; + if (likely((shift + log2_radix) <= JS_LIMB_BITS)) { + c = r->tab[pos] >> shift; + } else { + c = (r->tab[pos] >> shift) | + (r->tab[pos + 1] << (JS_LIMB_BITS - shift)); + } + c &= (radix - 1); + *--q = digits[c]; + } + } + if (is_neg) + *--q = '-'; + js_free(ctx, tmp); + res = js_new_string8_len(ctx, q, buf_end - q); + js_free(ctx, buf); + return res; + } +} + +/* if possible transform a BigInt to short big and free it, otherwise + return a normal bigint */ +static JSValue JS_CompactBigInt(JSContext *ctx, JSBigInt *p) +{ + JSValue res; + if (p->len == 1) { + res = __JS_NewShortBigInt(ctx, (js_slimb_t)p->tab[0]); + js_free(ctx, p); + return res; + } else { + return JS_MKPTR(JS_TAG_BIG_INT, p); + } +} + /* XXX: remove */ static double js_strtod(const char *str, int radix, bool is_float) { @@ -10536,7 +12052,10 @@ static double js_strtod(const char *str, int radix, bool is_float) n_max = ((uint64_t)-1 - (radix - 1)) / radix; /* XXX: could be more precise */ int_exp = 0; - while ((c = to_digit(*p)) < radix) { + while (*p != '\0') { + c = to_digit((uint8_t)*p); + if (c >= radix) + break; if (n <= n_max) { n = n * radix + c; } else { @@ -10559,23 +12078,6 @@ static double js_strtod(const char *str, int radix, bool is_float) return d; } -static JSValue js_string_to_bigint(JSContext *ctx, const char *buf, int radix) -{ - bf_t *a; - int ret; - 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) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); - } - return JS_CompactBigInt1(ctx, val); -} - /* `js_atof(ctx, p, len, pp, radix, flags)` Convert the string pointed to by `p` to a number value. Return an exception in case of memory error. @@ -10718,8 +12220,15 @@ static JSValue js_atof(JSContext *ctx, const char *p, size_t len, } if (flags & ATOD_WANT_BIG_INT) { - if (!is_float) - val = js_string_to_bigint(ctx, buf, radix); + JSBigInt *r; + if (!is_float) { + r = js_bigint_from_string(ctx, buf, radix); + if (!r) { + val = JS_ThrowOutOfMemory(ctx); + goto done; + } + val = JS_CompactBigInt(ctx, r); + } } else { d = js_strtod(buf, radix, is_float); val = js_number(d); /* return int or float64 */ @@ -10755,6 +12264,7 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { case JS_TAG_BIG_INT: + case JS_TAG_SHORT_BIG_INT: if (flag != TON_FLAG_NUMERIC) { JS_FreeValue(ctx, val); return JS_ThrowTypeError(ctx, "cannot convert BigInt to number"); @@ -10788,6 +12298,7 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, JS_FreeValue(ctx, val); if (!str) return JS_EXCEPTION; + // TODO(saghul): Sync with bellard/quickjs ? flags = ATOD_TRIM_SPACES | ATOD_ACCEPT_EMPTY | ATOD_ACCEPT_FLOAT | ATOD_ACCEPT_INFINITY | ATOD_ACCEPT_HEX_PREFIX | ATOD_ACCEPT_BIN_OCT | @@ -10829,10 +12340,8 @@ static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, uint32_t tag; val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = NAN; - return -1; - } + if (JS_IsException(val)) + goto fail; tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { case JS_TAG_INT: @@ -10841,21 +12350,14 @@ static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, case JS_TAG_FLOAT64: d = JS_VALUE_GET_FLOAT64(val); break; - case JS_TAG_BIG_INT: - { - JSBigInt *p = JS_VALUE_GET_PTR(val); - /* XXX: there can be a double rounding issue with some - primitives (such as JS_ToUint8ClampFree()), but it is - not critical to fix it. */ - bf_get_float64(&p->num, &d, BF_RNDN); - JS_FreeValue(ctx, val); - } - break; default: abort(); } *pres = d; return 0; +fail: + *pres = NAN; + return -1; } static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) @@ -11239,21 +12741,6 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, len = v; } break; - case JS_TAG_BIG_INT: - { - JSBigInt *p = JS_VALUE_GET_PTR(val); - bf_t a; - bool res; - bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD); - bf_init(ctx->bf_ctx, &a); - bf_set_ui(&a, len); - res = bf_cmp_eq(&a, &p->num); - bf_delete(&a); - JS_FreeValue(ctx, val); - if (!res) - goto fail; - } - break; default: if (JS_TAG_IS_FLOAT64(tag)) { double d; @@ -11359,42 +12846,18 @@ static bool JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val) u.d = JS_VALUE_GET_FLOAT64(val); return (u.u64 >> 63); } + case JS_TAG_SHORT_BIG_INT: + return (JS_VALUE_GET_SHORT_BIG_INT(val) < 0); case JS_TAG_BIG_INT: { JSBigInt *p = JS_VALUE_GET_PTR(val); - /* Note: integer zeros are not necessarily positive */ - return p->num.sign && !bf_is_zero(&p->num); + return js_bigint_sign(p); } default: return false; } } -static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix) -{ - JSValue ret; - bf_t a_s, *a; - char *str; - int saved_sign; - size_t len; - - a = JS_ToBigInt(ctx, &a_s, val); - if (!a) - return JS_EXCEPTION; - saved_sign = a->sign; - if (a->expn == BF_EXP_ZERO) - a->sign = 0; - str = bf_ftoa(&len, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC | - BF_FTOA_JS_QUIRKS); - a->sign = saved_sign; - JS_FreeBigInt(ctx, a, &a_s); - if (!str) - return JS_ThrowOutOfMemory(ctx); - ret = js_new_string8_len(ctx, str, len); - bf_free(ctx->bf_ctx, str); - return ret; -} - static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val) { return js_bigint_to_string1(ctx, val, 10); @@ -11867,6 +13330,7 @@ JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, } case JS_TAG_FLOAT64: return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 0, JS_DTOA_TOSTRING); + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: return js_bigint_to_string(ctx, val); case JS_TAG_UNINITIALIZED: @@ -12152,14 +13616,26 @@ static __maybe_unused void JS_DumpValue(JSRuntime *rt, JSValueConst val) case JS_TAG_FLOAT64: printf("%.14g", JS_VALUE_GET_FLOAT64(val)); break; + case JS_TAG_SHORT_BIG_INT: + printf("%" PRId64 "n", (int64_t)JS_VALUE_GET_SHORT_BIG_INT(val)); + break; case JS_TAG_BIG_INT: { JSBigInt *p = JS_VALUE_GET_PTR(val); - char *str; - str = bf_ftoa(NULL, &p->num, 10, 0, - BF_RNDZ | BF_FTOA_FORMAT_FRAC); - printf("%sn", str); - bf_realloc(&rt->bf_ctx, str, 0); + int sgn, i; + /* In order to avoid allocations we just dump the limbs */ + sgn = js_bigint_sign(p); + if (sgn) + printf("BigInt.asIntN(%d,", p->len * JS_LIMB_BITS); + printf("0x"); + for(i = p->len - 1; i >= 0; i--) { + if (i != p->len - 1) + printf("_"); + printf("%08x", p->tab[i]); + } + printf("n"); + if (sgn) + printf(")"); } break; case JS_TAG_STRING: @@ -12242,76 +13718,28 @@ static double js_math_pow(double a, double b) JSValue JS_NewBigInt64(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); + if (v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX) { + return __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *p; + p = js_bigint_new_si64(ctx, v); + if (!p) + return JS_EXCEPTION; + return JS_MKPTR(JS_TAG_BIG_INT, p); } - return val; } JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) { - JSValue val; - 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 bigint is allocated it is equal to - 'buf'. Otherwise it is a pointer to the bigint in 'val'. Return - NULL in case of error. */ -// TODO(bnoordhuis) Merge with JS_ToBigInt() -static bf_t *JS_ToBigInt1(JSContext *ctx, bf_t *buf, JSValueConst val) -{ - uint32_t tag; - bf_t *r; - JSBigInt *p; - - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - r = buf; - bf_init(ctx->bf_ctx, r); - if (bf_set_si(r, JS_VALUE_GET_INT(val))) - goto fail; - break; - case JS_TAG_FLOAT64: - r = buf; - bf_init(ctx->bf_ctx, r); - if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) { - fail: - bf_delete(r); - return NULL; - } - break; - case JS_TAG_BIG_INT: - p = JS_VALUE_GET_PTR(val); - r = &p->num; - break; - case JS_TAG_UNDEFINED: - default: - r = buf; - bf_init(ctx->bf_ctx, r); - bf_set_nan(r); - break; + if (v <= JS_SHORT_BIG_INT_MAX) { + return __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *p; + p = js_bigint_new_ui64(ctx, v); + if (!p) + return JS_EXCEPTION; + return JS_MKPTR(JS_TAG_BIG_INT, p); } - return r; } /* return NaN if bad bigint literal */ @@ -12325,6 +13753,7 @@ static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) JS_FreeValue(ctx, val); if (!str) return JS_EXCEPTION; + // TODO(saghul): sync with bellard/quickjs ? flags = ATOD_WANT_BIG_INT | ATOD_TRIM_SPACES | ATOD_ACCEPT_EMPTY | ATOD_ACCEPT_HEX_PREFIX | ATOD_ACCEPT_BIN_OCT | @@ -12342,106 +13771,69 @@ static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val) return val; } -/* if the returned bigint is allocated it is equal to - 'buf'. Otherwise it is a pointer to the bigint in 'val'. */ -static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) +/* JS Numbers are not allowed */ +static JSValue JS_ToBigIntFree(JSContext *ctx, JSValue val) { uint32_t tag; - bf_t *r; - JSBigInt *p; redo: tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { + case JS_TAG_SHORT_BIG_INT: + case JS_TAG_BIG_INT: + break; case JS_TAG_INT: case JS_TAG_NULL: case JS_TAG_UNDEFINED: case JS_TAG_FLOAT64: goto fail; case JS_TAG_BOOL: - r = buf; - bf_init(ctx->bf_ctx, r); - bf_set_si(r, JS_VALUE_GET_INT(val)); - break; - case JS_TAG_BIG_INT: - p = JS_VALUE_GET_PTR(val); - r = &p->num; + val = __JS_NewShortBigInt(ctx, JS_VALUE_GET_INT(val)); break; case JS_TAG_STRING: val = JS_StringToBigIntErr(ctx, val); if (JS_IsException(val)) - return NULL; + return val; goto redo; case JS_TAG_OBJECT: val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); if (JS_IsException(val)) - return NULL; + return val; goto redo; default: fail: JS_FreeValue(ctx, val); - JS_ThrowTypeError(ctx, "cannot convert to BigInt"); - return NULL; - } - return r; -} - -static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val) -{ - return JS_ToBigIntFree(ctx, buf, js_dup(val)); -} - -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, *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) { - JS_FreeValue(ctx, res); - return JS_EXCEPTION; - } - r = JS_GetBigInt(res); - ret = bf_set(r, a); - JS_FreeBigInt(ctx, a, &a_s); - if (ret) { - JS_FreeValue(ctx, res); - return JS_ThrowOutOfMemory(ctx); - } - return JS_CompactBigInt(ctx, res); + return JS_ThrowTypeError(ctx, "cannot convert to bigint"); } + return val; } -/* free the bf_t allocated by JS_ToBigInt */ -static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf) +static JSValue JS_ToBigInt(JSContext *ctx, JSValueConst val) { - if (a == buf) { - bf_delete(a); - } else { - JSBigInt *p = (JSBigInt *)((uint8_t *)a - offsetof(JSBigInt, num)); - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_INT, p)); - } + return JS_ToBigIntFree(ctx, js_dup(val)); } /* XXX: merge with JS_ToInt64Free with a specific flag */ static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val) { - bf_t a_s, *a; + uint64_t res; - a = JS_ToBigIntFree(ctx, &a_s, val); - if (!a) { + val = JS_ToBigIntFree(ctx, val); + if (JS_IsException(val)) { *pres = 0; return -1; } - bf_get_int64(pres, a, BF_GET_INT_MOD); - JS_FreeBigInt(ctx, a, &a_s); + if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) { + res = JS_VALUE_GET_SHORT_BIG_INT(val); + } else { + JSBigInt *p = JS_VALUE_GET_PTR(val); + /* return the value mod 2^64 */ + res = p->tab[0]; + if (p->len >= 2) + res |= (uint64_t)p->tab[1] << 32; + JS_FreeValue(ctx, val); + } + *pres = res; return 0; } @@ -12455,103 +13847,6 @@ int JS_ToBigUint64(JSContext *ctx, uint64_t *pres, JSValueConst val) return JS_ToBigInt64Free(ctx, (int64_t *)pres, js_dup(val)); } -static JSValue JS_NewBigInt(JSContext *ctx) -{ - JSBigInt *p; - 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_CompactBigInt1(JSContext *ctx, JSValue val) -{ - if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT) - return val; /* fail safe */ - bf_t *a = JS_GetBigInt(val); - if (a->expn == BF_EXP_ZERO && a->sign) { - assert(((JSBigInt*)JS_VALUE_GET_PTR(val))->header.ref_count == 1); - a->sign = 0; - } - return val; -} - -/* Nnormalize 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); -} - -static JSValue throw_bf_exception(JSContext *ctx, int status) -{ - const char *str; - if (status & BF_ST_MEM_ERROR) - return JS_ThrowOutOfMemory(ctx); - if (status & BF_ST_DIVIDE_ZERO) { - str = "division by zero"; - } else if (status & BF_ST_INVALID_OP) { - str = "invalid operation"; - } else { - str = "integer overflow"; - } - return JS_ThrowRangeError(ctx, "%s", str); -} - -static int js_unary_arith_bigint(JSContext *ctx, - JSValue *pres, OPCodeEnum op, JSValue op1) -{ - bf_t a_s, *r, *a; - int ret, v; - JSValue res; - - if (op == OP_plus) { - 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_ToBigIntFree(ctx, &a_s, op1); // infallible, always a bigint - ret = 0; - switch(op) { - case OP_inc: - case OP_dec: - v = 2 * (op - OP_dec) - 1; - ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ); - break; - case OP_plus: - ret = bf_set(r, a); - break; - case OP_neg: - ret = bf_set(r, a); - bf_neg(r); - break; - case OP_not: - ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ); - bf_neg(r); - break; - default: - abort(); - } - JS_FreeBigInt(ctx, a, &a_s); - if (unlikely(ret)) { - JS_FreeValue(ctx, res); - throw_bf_exception(ctx, ret); - return -1; - } - res = JS_CompactBigInt(ctx, res); - *pres = res; - return 0; -} - static no_inline __exception int js_unary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) @@ -12559,12 +13854,13 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx, JSValue op1; int v; uint32_t tag; + JSBigIntBuf buf1; + JSBigInt *p1; op1 = sp[-1]; /* fast path for float64 */ if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) goto handle_float64; - op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) goto exception; @@ -12593,17 +13889,80 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx, default: abort(); } - sp[-1] = js_int64(v64); + sp[-1] = JS_NewInt64(ctx, v64); + } + break; + case JS_TAG_SHORT_BIG_INT: + { + int64_t v; + v = JS_VALUE_GET_SHORT_BIG_INT(op1); + switch(op) { + case OP_plus: + JS_ThrowTypeError(ctx, "bigint argument with unary +"); + goto exception; + case OP_inc: + if (v == JS_SHORT_BIG_INT_MAX) + goto bigint_slow_case; + sp[-1] = __JS_NewShortBigInt(ctx, v + 1); + break; + case OP_dec: + if (v == JS_SHORT_BIG_INT_MIN) + goto bigint_slow_case; + sp[-1] = __JS_NewShortBigInt(ctx, v - 1); + break; + case OP_neg: + v = JS_VALUE_GET_SHORT_BIG_INT(op1); + if (v == JS_SHORT_BIG_INT_MIN) { + bigint_slow_case: + p1 = js_bigint_set_short(&buf1, op1); + goto bigint_slow_case1; + } + sp[-1] = __JS_NewShortBigInt(ctx, -v); + break; + default: + abort(); + } } break; case JS_TAG_BIG_INT: - if (js_unary_arith_bigint(ctx, sp - 1, op, op1)) - goto exception; + { + JSBigInt *r; + p1 = JS_VALUE_GET_PTR(op1); + bigint_slow_case1: + switch(op) { + case OP_plus: + JS_ThrowTypeError(ctx, "bigint argument with unary +"); + JS_FreeValue(ctx, op1); + goto exception; + case OP_inc: + case OP_dec: + { + JSBigIntBuf buf2; + JSBigInt *p2; + p2 = js_bigint_set_si(&buf2, 2 * (op - OP_dec) - 1); + r = js_bigint_add(ctx, p1, p2, 0); + } + break; + case OP_neg: + r = js_bigint_neg(ctx, p1); + break; + case OP_not: + r = js_bigint_not(ctx, p1); + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + if (!r) + goto exception; + sp[-1] = JS_CompactBigInt(ctx, r); + } break; default: handle_float64: { - double d = JS_VALUE_GET_FLOAT64(op1); + double d; + d = JS_VALUE_GET_FLOAT64(op1); switch(op) { case OP_inc: case OP_dec: @@ -12649,17 +14008,24 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) { JSValue op1; - op1 = JS_ToNumericFree(ctx, sp[-1]); + op1 = sp[-1]; + op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) goto exception; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { - if (js_unary_arith_bigint(ctx, sp - 1, OP_not, op1)) + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) { + sp[-1] = __JS_NewShortBigInt(ctx, ~JS_VALUE_GET_SHORT_BIG_INT(op1)); + } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { + JSBigInt *r; + r = js_bigint_not(ctx, JS_VALUE_GET_PTR(op1)); + JS_FreeValue(ctx, op1); + if (!r) goto exception; + sp[-1] = JS_CompactBigInt(ctx, r); } else { int32_t v1; if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) goto exception; - sp[-1] = js_int32(~v1); + sp[-1] = JS_NewInt32(ctx, ~v1); } return 0; exception: @@ -12667,107 +14033,6 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) return -1; } -static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, - JSValue *pres, JSValue op1, JSValue op2) -{ - bf_t a_s, b_s, *r, *a, *b; - int ret; - JSValue res; - - a = JS_ToBigIntFree(ctx, &a_s, op1); - if (!a) { - JS_FreeValue(ctx, op2); - return -1; - } - b = JS_ToBigIntFree(ctx, &b_s, op2); - if (!b) { - JS_FreeBigInt(ctx, a, &a_s); - return -1; - } - res = JS_NewBigInt(ctx); - if (JS_IsException(res)) { - JS_FreeBigInt(ctx, a, &a_s); - JS_FreeBigInt(ctx, b, &b_s); - return -1; - } - r = JS_GetBigInt(res); - ret = 0; - switch(op) { - case OP_add: - ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ); - break; - case OP_sub: - ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ); - break; - case OP_mul: - ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ); - break; - case OP_div: - { - 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); - } - break; - case OP_mod: - ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ, - BF_RNDZ) & BF_ST_INVALID_OP; - break; - case OP_pow: - if (b->sign) { - ret = BF_ST_INVALID_OP; - } else { - ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS); - } - break; - - /* logical operations */ - case OP_shl: - case OP_sar: - { - slimb_t v2; -#if LIMB_BITS == 32 - bf_get_int32(&v2, b, 0); - if (v2 == INT32_MIN) - v2 = INT32_MIN + 1; -#else - bf_get_int64(&v2, b, 0); - if (v2 == INT64_MIN) - v2 = INT64_MIN + 1; -#endif - if (op == OP_sar) - v2 = -v2; - ret = bf_set(r, a); - ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ); - if (v2 < 0) { - ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR); - } - } - break; - case OP_and: - ret = bf_logic_and(r, a, b); - break; - case OP_or: - ret = bf_logic_or(r, a, b); - break; - case OP_xor: - ret = bf_logic_xor(r, a, b); - break; - default: - abort(); - } - JS_FreeBigInt(ctx, a, &a_s); - JS_FreeBigInt(ctx, b, &b_s); - if (unlikely(ret)) { - JS_FreeValue(ctx, res); - throw_bf_exception(ctx, ret); - return -1; - } - *pres = JS_CompactBigInt(ctx, res); - return 0; -} - static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { @@ -12785,7 +14050,50 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s d2 = JS_VALUE_GET_FLOAT64(op2); goto handle_float64; } - + /* fast path for short big int operations */ + if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { + js_slimb_t v1, v2; + js_sdlimb_t v; + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + switch(op) { + case OP_sub: + v = (js_sdlimb_t)v1 - (js_sdlimb_t)v2; + break; + case OP_mul: + v = (js_sdlimb_t)v1 * (js_sdlimb_t)v2; + break; + case OP_div: + if (v2 == 0 || + ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) && + v2 == -1)) { + goto slow_big_int; + } + sp[-2] = __JS_NewShortBigInt(ctx, v1 / v2); + return 0; + case OP_mod: + if (v2 == 0 || + ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) && + v2 == -1)) { + goto slow_big_int; + } + sp[-2] = __JS_NewShortBigInt(ctx, v1 % v2); + return 0; + case OP_pow: + goto slow_big_int; + default: + abort(); + } + if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) { + sp[-2] = __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *r = js_bigint_new_di(ctx, v); + if (!r) + goto exception; + sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); + } + return 0; + } op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); @@ -12816,7 +14124,7 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s } break; case OP_div: - sp[-2] = js_float64((double)v1 / (double)v2); + sp[-2] = js_number((double)v1 / (double)v2); return 0; case OP_mod: if (v1 < 0 || v2 <= 0) { @@ -12833,9 +14141,47 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s abort(); } sp[-2] = js_int64(v); - } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { - if (js_binary_arith_bigint(ctx, op, sp - 2, op1, op2)) + } else if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_BIG_INT) && + (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_BIG_INT)) { + JSBigInt *p1, *p2, *r; + JSBigIntBuf buf1, buf2; + slow_big_int: + /* bigint result */ + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + switch(op) { + case OP_add: + r = js_bigint_add(ctx, p1, p2, 0); + break; + case OP_sub: + r = js_bigint_add(ctx, p1, p2, 1); + break; + case OP_mul: + r = js_bigint_mul(ctx, p1, p2); + break; + case OP_div: + r = js_bigint_divrem(ctx, p1, p2, false); + break; + case OP_mod: + r = js_bigint_divrem(ctx, p1, p2, true); + break; + case OP_pow: + r = js_bigint_pow(ctx, p1, p2); + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (!r) goto exception; + sp[-2] = JS_CompactBigInt(ctx, r); } else { double dr; /* float64 result */ @@ -12892,6 +14238,23 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) sp[-2] = js_float64(d1 + d2); return 0; } + /* fast path for short bigint */ + if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { + js_slimb_t v1, v2; + js_sdlimb_t v; + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + v = (js_sdlimb_t)v1 + (js_sdlimb_t)v2; + if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) { + sp[-2] = __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *r = js_bigint_new_di(ctx, v); + if (!r) + goto exception; + sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); + } + return 0; + } if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); @@ -12935,10 +14298,26 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) v1 = JS_VALUE_GET_INT(op1); v2 = JS_VALUE_GET_INT(op2); v = (int64_t)v1 + (int64_t)v2; - sp[-2] = js_int64(v); - } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { - if (js_binary_arith_bigint(ctx, OP_add, sp - 2, op1, op2)) + sp[-2] = JS_NewInt64(ctx, v); + } else if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && + (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) { + JSBigInt *p1, *p2, *r; + JSBigIntBuf buf1, buf2; + /* bigint result */ + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + r = js_bigint_add(ctx, p1, p2, 0); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (!r) goto exception; + sp[-2] = JS_CompactBigInt(ctx, r); } else { double d1, d2; /* float64 result */ @@ -12970,6 +14349,62 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); + if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { + js_slimb_t v1, v2, v; + js_sdlimb_t vd; + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + /* bigint fast path */ + switch(op) { + case OP_and: + v = v1 & v2; + break; + case OP_or: + v = v1 | v2; + break; + case OP_xor: + v = v1 ^ v2; + break; + case OP_sar: + if (v2 > (JS_LIMB_BITS - 1)) { + goto slow_big_int; + } else if (v2 < 0) { + if (v2 < -(JS_LIMB_BITS - 1)) + goto slow_big_int; + v2 = -v2; + goto bigint_shl; + } + bigint_sar: + v = v1 >> v2; + break; + case OP_shl: + if (v2 > (JS_LIMB_BITS - 1)) { + goto slow_big_int; + } else if (v2 < 0) { + if (v2 < -(JS_LIMB_BITS - 1)) + goto slow_big_int; + v2 = -v2; + goto bigint_sar; + } + bigint_shl: + vd = (js_dlimb_t)v1 << v2; + if (likely(vd >= JS_SHORT_BIG_INT_MIN && + vd <= JS_SHORT_BIG_INT_MAX)) { + v = vd; + } else { + JSBigInt *r = js_bigint_new_di(ctx, vd); + if (!r) + goto exception; + sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); + return 0; + } + break; + default: + abort(); + } + sp[-2] = __JS_NewShortBigInt(ctx, v); + return 0; + } op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); @@ -12983,15 +14418,50 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, 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 (js_binary_arith_bigint(ctx, op, sp - 2, op1, op2)) { - goto exception; + if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && + (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) { + JSBigInt *p1, *p2, *r; + JSBigIntBuf buf1, buf2; + slow_big_int: + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + switch(op) { + case OP_and: + case OP_or: + case OP_xor: + r = js_bigint_logic(ctx, p1, p2, op); + break; + case OP_shl: + case OP_sar: + { + js_slimb_t shift; + shift = js_bigint_get_si_sat(p2); + if (shift > INT32_MAX) + shift = INT32_MAX; + else if (shift < -INT32_MAX) + shift = -INT32_MAX; + if (op == OP_sar) + shift = -shift; + if (shift >= 0) + r = js_bigint_shl(ctx, p1, shift); + else + r = js_bigint_shr(ctx, p1, -shift); + } + break; + default: + abort(); } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (!r) + goto exception; + sp[-2] = JS_CompactBigInt(ctx, r); } else { if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { JS_FreeValue(ctx, op2); @@ -13027,49 +14497,96 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, return -1; } +/* op1 must be a bigint or int. */ +static JSBigInt *JS_ToBigIntBuf(JSContext *ctx, JSBigIntBuf *buf1, + JSValue op1) +{ + JSBigInt *p1; + + switch(JS_VALUE_GET_TAG(op1)) { + case JS_TAG_INT: + p1 = js_bigint_set_si(buf1, JS_VALUE_GET_INT(op1)); + break; + case JS_TAG_SHORT_BIG_INT: + p1 = js_bigint_set_short(buf1, op1); + break; + case JS_TAG_BIG_INT: + p1 = JS_VALUE_GET_PTR(op1); + break; + default: + abort(); + } + return p1; +} + +/* op1 and op2 must be numeric types and at least one must be a + bigint. No exception is generated. */ static int js_compare_bigint(JSContext *ctx, OPCodeEnum op, JSValue op1, JSValue op2) { - bf_t a_s, b_s, *a, *b; - int res; + int res, val, tag1, tag2; + JSBigIntBuf buf1, buf2; + JSBigInt *p1, *p2; - a = JS_ToBigInt1(ctx, &a_s, op1); - if (!a) { - JS_FreeValue(ctx, op2); - return -1; - } - b = JS_ToBigInt1(ctx, &b_s, op2); - if (!b) { - if (a == &a_s) - bf_delete(a); + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_INT) && + (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_INT)) { + /* fast path */ + js_slimb_t v1, v2; + if (tag1 == JS_TAG_INT) + v1 = JS_VALUE_GET_INT(op1); + else + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + if (tag2 == JS_TAG_INT) + v2 = JS_VALUE_GET_INT(op2); + else + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + val = (v1 > v2) - (v1 < v2); + } else { + if (tag1 == JS_TAG_FLOAT64) { + p2 = JS_ToBigIntBuf(ctx, &buf2, op2); + val = js_bigint_float64_cmp(ctx, p2, JS_VALUE_GET_FLOAT64(op1)); + if (val == 2) + goto unordered; + val = -val; + } else if (tag2 == JS_TAG_FLOAT64) { + p1 = JS_ToBigIntBuf(ctx, &buf1, op1); + val = js_bigint_float64_cmp(ctx, p1, JS_VALUE_GET_FLOAT64(op2)); + if (val == 2) { + unordered: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return false; + } + } else { + p1 = JS_ToBigIntBuf(ctx, &buf1, op1); + p2 = JS_ToBigIntBuf(ctx, &buf2, op2); + val = js_bigint_cmp(ctx, p1, p2); + } JS_FreeValue(ctx, op1); - return -1; + JS_FreeValue(ctx, op2); } + switch(op) { case OP_lt: - res = bf_cmp_lt(a, b); /* if NaN return false */ + res = val < 0; break; case OP_lte: - res = bf_cmp_le(a, b); /* if NaN return false */ + res = val <= 0; break; case OP_gt: - res = bf_cmp_lt(b, a); /* if NaN return false */ + res = val > 0; break; case OP_gte: - res = bf_cmp_le(b, a); /* if NaN return false */ + res = val >= 0; break; case OP_eq: - res = bf_cmp_eq(a, b); /* if NaN return false */ + res = val == 0; 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; } @@ -13125,16 +14642,20 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, /* fast path for float64/int */ goto float64_compare; } else { - if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) || - (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING))) { + if ((((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && + tag2 == JS_TAG_STRING) || + ((tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) && + tag1 == JS_TAG_STRING))) { if (tag1 == JS_TAG_STRING) { op1 = JS_StringToBigInt(ctx, op1); - if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT) goto invalid_bigint_string; } if (tag2 == JS_TAG_STRING) { op2 = JS_StringToBigInt(ctx, op2); - if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT) { invalid_bigint_string: JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -13158,10 +14679,9 @@ 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); - if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT || + tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) { res = js_compare_bigint(ctx, op, op1, op2); - if (res < 0) - goto exception; } else { double d1, d2; @@ -13205,8 +14725,9 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, static bool tag_is_number(uint32_t tag) { - return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT || - tag == JS_TAG_FLOAT64); + return (tag == JS_TAG_INT || + tag == JS_TAG_FLOAT64 || + tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT); } static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, @@ -13253,15 +14774,18 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) || (tag2 == JS_TAG_STRING && tag_is_number(tag1))) { - if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT)) { + if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT || + tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) { if (tag1 == JS_TAG_STRING) { op1 = JS_StringToBigInt(ctx, op1); - if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT) goto invalid_bigint_string; } if (tag2 == JS_TAG_STRING) { op2 = JS_StringToBigInt(ctx, op2); - if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT ) { invalid_bigint_string: JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -13343,8 +14867,10 @@ static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) goto exception; } - if ((JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || - JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) { + if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || + JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT || + JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT || + JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) { JS_ThrowTypeError(ctx, "BigInt operands are forbidden for >>>"); JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -13453,20 +14979,27 @@ static bool js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, res = (d1 == d2); /* if NaN return false and +0 == -0 */ } goto done_no_free; + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: { - bf_t a_s, *a, b_s, *b; - if (tag1 != tag2) { + JSBigIntBuf buf1, buf2; + JSBigInt *p1, *p2; + + if (tag2 != JS_TAG_SHORT_BIG_INT && + tag2 != JS_TAG_BIG_INT) { res = false; break; } - a = JS_ToBigInt1(ctx, &a_s, op1); - b = JS_ToBigInt1(ctx, &b_s, op2); - res = bf_cmp_eq(a, b); - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); + + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + res = (js_bigint_cmp(ctx, p1, p2) == 0); } break; default: @@ -13605,6 +15138,7 @@ static __exception int js_operator_typeof(JSContext *ctx, JSValue op1) tag = JS_VALUE_GET_NORM_TAG(op1); switch(tag) { + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: atom = JS_ATOM_bigint; break; @@ -14130,9 +15664,13 @@ static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj, obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done); if (JS_IsException(obj)) goto fail; - if (done != 2) { - *pdone = done; + if (likely(done == 0)) { + *pdone = false; return obj; + } else if (done != 2) { + JS_FreeValue(ctx, obj); + *pdone = true; + return JS_UNDEFINED; } else { done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); if (JS_IsException(done_val)) @@ -14505,10 +16043,16 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, { JSVarRef *var_ref; struct list_head *el; + JSValue *pvalue; + + if (is_arg) + pvalue = &sf->arg_buf[var_idx]; + else + pvalue = &sf->var_buf[var_idx]; list_for_each(el, &sf->var_ref_list) { var_ref = list_entry(el, JSVarRef, header.link); - if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) { + if (var_ref->pvalue == pvalue) { var_ref->header.ref_count++; return var_ref; } @@ -14519,13 +16063,8 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, return NULL; var_ref->header.ref_count = 1; var_ref->is_detached = false; - var_ref->is_arg = is_arg; - var_ref->var_idx = var_idx; list_add_tail(&var_ref->header.link, &sf->var_ref_list); - if (is_arg) - var_ref->pvalue = &sf->arg_buf[var_idx]; - else - var_ref->pvalue = &sf->var_buf[var_idx]; + var_ref->pvalue = pvalue; var_ref->value = JS_UNDEFINED; return var_ref; } @@ -14754,15 +16293,10 @@ static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) { struct list_head *el, *el1; JSVarRef *var_ref; - int var_idx; list_for_each_safe(el, el1, &sf->var_ref_list) { var_ref = list_entry(el, JSVarRef, header.link); - var_idx = var_ref->var_idx; - if (var_ref->is_arg) - var_ref->value = js_dup(sf->arg_buf[var_idx]); - else - var_ref->value = js_dup(sf->var_buf[var_idx]); + var_ref->value = js_dup(*var_ref->pvalue); var_ref->pvalue = &var_ref->value; /* the reference is no longer to a local variable */ var_ref->is_detached = true; @@ -14772,13 +16306,15 @@ static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int var_idx) { + JSValue *pvalue; struct list_head *el, *el1; JSVarRef *var_ref; + pvalue = &sf->var_buf[var_idx]; list_for_each_safe(el, el1, &sf->var_ref_list) { var_ref = list_entry(el, JSVarRef, header.link); - if (var_idx == var_ref->var_idx && !var_ref->is_arg) { - var_ref->value = js_dup(sf->var_buf[var_idx]); + if (var_ref->pvalue == pvalue) { + var_ref->value = js_dup(*var_ref->pvalue); var_ref->pvalue = &var_ref->value; list_del(&var_ref->header.link); /* the reference is no longer to a local variable */ @@ -15128,6 +16664,10 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, *sp++ = js_int32(get_u32(pc)); pc += 4; BREAK; + CASE(OP_push_bigint_i32): + *sp++ = __JS_NewShortBigInt(ctx, (int)get_u32(pc)); + pc += 4; + BREAK; CASE(OP_push_const): *sp++ = js_dup(b->cpool[get_u32(pc)]); pc += 4; @@ -18978,8 +20518,6 @@ static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = { opcode_info[(op) >= OP_TEMP_START ? \ (op) + (OP_TEMP_END - OP_TEMP_START) : (op)] -static __exception int next_token(JSParseState *s); - static void free_token(JSParseState *s, JSToken *token) { switch(token->val) { @@ -19086,6 +20624,10 @@ int JS_PRINTF_FORMAT_ATTR(2, 3) js_parse_error(JSParseState *s, JS_PRINTF_FORMAT return -1; } +#ifndef QJS_DISABLE_PARSER + +static __exception int next_token(JSParseState *s); + static int js_parse_expect(JSParseState *s, int tok) { char buf[ATOM_GET_STR_BUF_SIZE]; @@ -19430,6 +20972,8 @@ static __exception int js_parse_regexp(JSParseState *s) return -1; } +#endif // QJS_DISABLE_PARSER + static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, char *static_buf) { @@ -19457,6 +21001,8 @@ static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, return 0; } +#ifndef QJS_DISABLE_PARSER + /* convert a TOK_IDENT to a keyword when needed */ static void update_token_ident(JSParseState *s) { @@ -20031,6 +21577,8 @@ static __exception int next_token(JSParseState *s) return -1; } +#endif // QJS_DISABLE_PARSER + static int json_parse_error(JSParseState *s, const uint8_t *curp, const char *msg) { const uint8_t *p, *line_start; @@ -20331,6 +21879,8 @@ static __exception int json_next_token(JSParseState *s) return -1; } +#ifndef QJS_DISABLE_PARSER + /* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is only set if TOK_IMPORT is returned */ /* XXX: handle all unicode cases */ @@ -23482,7 +25032,17 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { emit_op(s, OP_push_i32); emit_u32(s, JS_VALUE_GET_INT(val)); + } else if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) { + int64_t v; + v = JS_VALUE_GET_SHORT_BIG_INT(val); + if (v >= INT32_MIN && v <= INT32_MAX) { + emit_op(s, OP_push_bigint_i32); + emit_u32(s, v); + } else { + goto large_number; + } } else { + large_number: if (emit_push_const(s, val, 0) < 0) return -1; } @@ -26237,6 +27797,8 @@ fail: return -1; } +#endif // QJS_DISABLE_PARSER + /* 'name' is freed */ static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name) { @@ -26322,6 +27884,8 @@ static void js_free_module_def(JSContext *ctx, JSModuleDef *m) js_free(ctx, m); } +#ifndef QJS_DISABLE_PARSER + static int add_req_module_entry(JSContext *ctx, JSModuleDef *m, JSAtom module_name) { @@ -26346,6 +27910,8 @@ static int add_req_module_entry(JSContext *ctx, JSModuleDef *m, return i; } +#endif // QJS_DISABLE_PARSER + static JSExportEntry *find_export_entry(JSContext *ctx, const JSModuleDef *m, JSAtom export_name) { @@ -26390,6 +27956,8 @@ static JSExportEntry *add_export_entry2(JSContext *ctx, return me; } +#ifndef QJS_DISABLE_PARSER + static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m, JSAtom local_name, JSAtom export_name, JSExportTypeEnum export_type) @@ -26413,6 +27981,8 @@ static int add_star_export_entry(JSContext *ctx, JSModuleDef *m, return 0; } +#endif // QJS_DISABLE_PARSER + /* create a C module */ /* `name_str` may be pure ASCII or UTF-8 encoded */ JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str, @@ -28061,6 +29631,8 @@ static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m) return js_dup(m->promise); } +#ifndef QJS_DISABLE_PARSER + static __exception JSAtom js_parse_from_clause(JSParseState *s) { JSAtom module_name; @@ -28500,6 +30072,8 @@ static JSFunctionDef *js_new_function_def(JSContext *ctx, return fd; } +#endif // QJS_DISABLE_PARSER + static void free_bytecode_atoms(JSRuntime *rt, const uint8_t *bc_buf, int bc_len, bool use_short_opcodes) @@ -28533,6 +30107,8 @@ static void free_bytecode_atoms(JSRuntime *rt, } } +#ifndef QJS_DISABLE_PARSER + static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd) { int i; @@ -28595,6 +30171,8 @@ static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd) js_free(ctx, fd); } +#endif // QJS_DISABLE_PARSER + #ifdef ENABLE_DUMPS // JS_DUMP_BYTECODE_* static const char *skip_lines(const char *p, int n) { while (p && n-- > 0 && *p) { @@ -29051,6 +30629,8 @@ static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionB } #endif +#ifndef QJS_DISABLE_PARSER + static int add_closure_var(JSContext *ctx, JSFunctionDef *s, bool is_local, bool is_arg, int var_idx, JSAtom var_name, @@ -31544,6 +33124,28 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) push_short_int(&bc_out, val); break; + case OP_push_bigint_i32: + { + /* transform i32(val) neg -> i32(-val) */ + val = get_i32(bc_buf + pos + 1); + if (val != INT32_MIN + && code_match(&cc, pos_next, OP_neg, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + if (code_match(&cc, cc.pos, OP_drop, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + } else { + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, OP_push_bigint_i32); + dbuf_put_u32(&bc_out, -val); + } + pos_next = cc.pos; + break; + } + } + goto no_change; + case OP_push_const: case OP_fclosure: { @@ -32336,8 +33938,10 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) are used to compile the eval and they must be ordered by scope, so it is necessary to create the closure variables before any other variable lookup is done. */ +#ifndef QJS_DISABLE_PARSER if (fd->has_eval_call) add_eval_variables(ctx, fd); +#endif // QJS_DISABLE_PARSER /* add the module global variables in the closure */ if (fd->module) { @@ -32496,6 +34100,8 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) return JS_EXCEPTION; } +#endif // QJS_DISABLE_PARSER + static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b) { int i; @@ -32530,6 +34136,8 @@ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b) } } +#ifndef QJS_DISABLE_PARSER + static __exception int js_parse_directives(JSParseState *s) { char str[20]; @@ -33341,6 +34949,8 @@ static __exception int js_parse_program(JSParseState *s) return 0; } +#endif // QJS_DISABLE_PARSER + static void js_parse_init(JSContext *ctx, JSParseState *s, const char *input, size_t input_len, const char *filename, int line) @@ -33396,6 +35006,8 @@ JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj) return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL); } +#ifndef QJS_DISABLE_PARSER + /* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ /* `export_name` and `input` may be pure ASCII or UTF-8 encoded */ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, @@ -33512,6 +35124,8 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, return JS_EXCEPTION; } +#endif // QJS_DISABLE_PARSER + /* the indirection is needed to make 'eval' optional */ static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, const char *input, size_t input_len, @@ -33748,7 +35362,7 @@ typedef enum BCTagEnum { BC_TAG_SYMBOL, } BCTagEnum; -#define BC_VERSION 19 +#define BC_VERSION 20 typedef struct BCWriterState { JSContext *ctx; @@ -33798,6 +35412,13 @@ static const char * const bc_tag_str[] = { "Set", "Symbol", }; + +static const char *bc_tag_name(uint8_t tag) +{ + if (tag >= countof(bc_tag_str)) + return "<bad tag>"; + return bc_tag_str[tag]; +} #endif static void bc_put_u8(BCWriterState *s, uint8_t v) @@ -34018,71 +35639,44 @@ static void JS_WriteString(BCWriterState *s, JSString *p) static int JS_WriteBigInt(BCWriterState *s, JSValueConst obj) { - uint32_t tag, tag1; - int64_t e; - JSBigInt *bf = JS_VALUE_GET_PTR(obj); - bf_t *a = &bf->num; - size_t len, i, n1, j; - limb_t v; + JSBigIntBuf buf; + JSBigInt *p; + uint32_t len, i; + js_limb_t v, b; + int shift; - tag = JS_VALUE_GET_TAG(obj); - switch(tag) { - case JS_TAG_BIG_INT: - tag1 = BC_TAG_BIG_INT; - break; - default: - abort(); - } - bc_put_u8(s, tag1); + bc_put_u8(s, BC_TAG_BIG_INT); - /* sign + exponent */ - if (a->expn == BF_EXP_ZERO) - e = 0; - else if (a->expn == BF_EXP_INF) - e = 1; - else if (a->expn == BF_EXP_NAN) - e = 2; - else if (a->expn >= 0) - e = a->expn + 3; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_SHORT_BIG_INT) + p = js_bigint_set_short(&buf, obj); else - e = a->expn; - e = (e * 2) | a->sign; - if (e < INT32_MIN || e > INT32_MAX) { - JS_ThrowRangeError(s->ctx, "maximum BigInt size exceeded"); - return -1; - } - bc_put_sleb128(s, e); - - /* mantissa */ - if (a->len != 0) { - i = 0; - while (i < a->len && a->tab[i] == 0) - i++; - assert(i < a->len); - v = a->tab[i]; - n1 = sizeof(limb_t); - while ((v & 0xff) == 0) { - n1--; - v >>= 8; - } - i++; - len = (a->len - i) * sizeof(limb_t) + n1; - if (len > INT32_MAX) { - JS_ThrowRangeError(s->ctx, "maximum BigInt size exceeded"); - return -1; + p = JS_VALUE_GET_PTR(obj); + if (p->len == 1 && p->tab[0] == 0) { + /* zero case */ + len = 0; + } else { + /* compute the length of the two's complement representation + in bytes */ + len = p->len * (JS_LIMB_BITS / 8); + v = p->tab[p->len - 1]; + shift = JS_LIMB_BITS - 8; + while (shift > 0) { + b = (v >> shift) & 0xff; + if (b != 0x00 && b != 0xff) + break; + if ((b & 1) != ((v >> (shift - 1)) & 1)) + break; + shift -= 8; + len--; } - bc_put_leb128(s, len); - /* always saved in byte based little endian representation */ - for(j = 0; j < n1; j++) { - bc_put_u8(s, v >> (j * 8)); + } + bc_put_leb128(s, len); + if (len > 0) { + for(i = 0; i < (len / (JS_LIMB_BITS / 8)); i++) { + bc_put_u32(s, p->tab[i]); } - for(; i < a->len; i++) { - limb_t v = a->tab[i]; -#if LIMB_BITS == 32 - bc_put_u32(s, v); -#else - bc_put_u64(s, v); -#endif + for(i = 0; i < len % (JS_LIMB_BITS / 8); i++) { + bc_put_u8(s, (p->tab[p->len - 1] >> (i * 8)) & 0xff); } } return 0; @@ -34505,6 +36099,7 @@ static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) goto fail; } break; + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: if (JS_WriteBigInt(s, obj)) goto fail; @@ -34850,6 +36445,10 @@ static JSString *JS_ReadString(BCReaderState *s) return NULL; is_wide_char = len & 1; len >>= 1; + if (len > JS_STRING_LEN_MAX) { + JS_ThrowInternalError(s->ctx, "string too long"); + return NULL; + } p = js_alloc_string(s->ctx, len, is_wide_char); if (!p) { s->error_state = -1; @@ -34947,77 +36546,46 @@ static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b, static JSValue JS_ReadBigInt(BCReaderState *s) { - JSValue obj; + JSValue obj = JS_UNDEFINED; + uint32_t len, i, n; + JSBigInt *p; + js_limb_t v; uint8_t v8; - int32_t e; - uint32_t len; - limb_t l, i, n; - limb_t v; - bf_t *a; - obj = JS_NewBigInt(s->ctx); - if (JS_IsException(obj)) + if (bc_get_leb128(s, &len)) goto fail; - - /* sign + exponent */ - if (bc_get_sleb128(s, &e)) + bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len); + if (len == 0) { + /* zero case */ + bc_read_trace(s, "}\n"); + return __JS_NewShortBigInt(s->ctx, 0); + } + p = js_bigint_new(s->ctx, (len - 1) / (JS_LIMB_BITS / 8) + 1); + if (!p) goto fail; - - a = JS_GetBigInt(obj); - a->sign = e & 1; - e >>= 1; - if (e == 0) - a->expn = BF_EXP_ZERO; - else if (e == 1) - a->expn = BF_EXP_INF; - else if (e == 2) - a->expn = BF_EXP_NAN; - else if (e >= 3) - a->expn = e - 3; - else - a->expn = e; - - /* mantissa */ - if (a->expn != BF_EXP_ZERO && - a->expn != BF_EXP_INF && - a->expn != BF_EXP_NAN) { - if (bc_get_leb128(s, &len)) - goto fail; - bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len); - if (len == 0) { - JS_ThrowRangeError(s->ctx, "maximum BigInt size exceeded"); - goto fail; - } - l = (len + sizeof(limb_t) - 1) / sizeof(limb_t); - if (bf_resize(a, l)) { - JS_ThrowOutOfMemory(s->ctx); + for(i = 0; i < len / (JS_LIMB_BITS / 8); i++) { + if (bc_get_u32(s, &v)) goto fail; - } - n = len & (sizeof(limb_t) - 1); - if (n != 0) { - v = 0; - for(i = 0; i < n; i++) { - if (bc_get_u8(s, &v8)) - goto fail; - v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8); - } - a->tab[0] = v; - i = 1; - } else { - i = 0; - } - for(; i < l; i++) { -#if LIMB_BITS == 32 - if (bc_get_u32(s, &v)) - goto fail; -#else - if (bc_get_u64(s, &v)) + p->tab[i] = v; + } + n = len % (JS_LIMB_BITS / 8); + if (n != 0) { + int shift; + v = 0; + for(i = 0; i < n; i++) { + if (bc_get_u8(s, &v8)) goto fail; -#endif - a->tab[i] = v; + v |= (js_limb_t)v8 << (i * 8); + } + shift = JS_LIMB_BITS - n * 8; + /* extend the sign */ + if (shift != 0) { + v = (js_slimb_t)(v << shift) >> shift; } + p->tab[p->len - 1] = v; } - return obj; + bc_read_trace(s, "}\n"); + return JS_CompactBigInt(s->ctx, p); fail: JS_FreeValue(s->ctx, obj); return JS_EXCEPTION; @@ -35707,7 +37275,7 @@ static JSValue JS_ReadObjectRec(BCReaderState *s) if (bc_get_u8(s, &tag)) return JS_EXCEPTION; - bc_read_trace(s, "%s {\n", bc_tag_str[tag]); + bc_read_trace(s, "%s {\n", bc_tag_name(tag)); switch(tag) { case BC_TAG_NULL: @@ -36284,6 +37852,7 @@ JSValue JS_ToObject(JSContext *ctx, JSValueConst val) case JS_TAG_OBJECT: case JS_TAG_EXCEPTION: return js_dup(val); + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT); goto set_value; @@ -37289,10 +38858,8 @@ static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val, item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); if (JS_IsException(item)) goto fail; - if (done) { - JS_FreeValue(ctx, item); + if (done) break; - } key = JS_UNDEFINED; value = JS_UNDEFINED; @@ -41056,13 +42623,18 @@ static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target, if (JS_IsException(val)) return val; switch(JS_VALUE_GET_TAG(val)) { + case JS_TAG_SHORT_BIG_INT: + val = JS_NewInt64(ctx, JS_VALUE_GET_SHORT_BIG_INT(val)); + if (JS_IsException(val)) + return val; + break; case JS_TAG_BIG_INT: { JSBigInt *p = JS_VALUE_GET_PTR(val); double d; - bf_get_float64(&p->num, &d, BF_RNDN); + d = js_bigint_to_float64(ctx, p); JS_FreeValue(ctx, val); - val = js_float64(d); + val = JS_NewFloat64(ctx, d); } break; default: @@ -42555,7 +44127,7 @@ static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val, } if (n > JS_STRING_LEN_MAX) { JS_ThrowRangeError(ctx, "invalid string length"); - goto fail2; + goto fail3; } if (string_buffer_init(ctx, b, n)) goto fail3; @@ -43287,25 +44859,32 @@ static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val, return js_int32(r); } +typedef enum SumPreciseStateEnum { + SUM_PRECISE_STATE_MINUS_ZERO, + SUM_PRECISE_STATE_NOT_A_NUMBER, + SUM_PRECISE_STATE_MINUS_INFINITY, + SUM_PRECISE_STATE_PLUS_INFINITY, + SUM_PRECISE_STATE_FINITE, +} SumPreciseStateEnum; + static JSValue js_math_sumPrecise(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue iter, next, item, ret; - bf_t a, b; int done; double d; - int r; + xsum_small_accumulator acc; + SumPreciseStateEnum state; iter = JS_GetIterator(ctx, argv[0], /*async*/false); if (JS_IsException(iter)) return JS_EXCEPTION; - bf_init(ctx->bf_ctx, &a); - bf_init(ctx->bf_ctx, &b); ret = JS_EXCEPTION; next = JS_GetProperty(ctx, iter, JS_ATOM_next); if (JS_IsException(next)) goto fail; - bf_set_zero(&a, /*is_neg*/true); + xsum_small_init(&acc); + state = SUM_PRECISE_STATE_MINUS_ZERO; for (;;) { item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); if (JS_IsException(item)) @@ -43324,25 +44903,52 @@ static JSValue js_math_sumPrecise(JSContext *ctx, JSValueConst this_val, d = JS_VALUE_GET_FLOAT64(item); break; } - if (bf_set_float64(&b, d)) - goto oom; - // Infinity + -Infinity results in BF_ST_INVALID_OP, sets |a| to nan - if ((r = bf_add(&a, &a, &b, BF_PREC_INF, BF_RNDN))) - if (r != BF_ST_INVALID_OP) - goto oom; + + if (state != SUM_PRECISE_STATE_NOT_A_NUMBER) { + if (isnan(d)) + state = SUM_PRECISE_STATE_NOT_A_NUMBER; + else if (!isfinite(d) && d > 0.0) + if (state == SUM_PRECISE_STATE_MINUS_INFINITY) + state = SUM_PRECISE_STATE_NOT_A_NUMBER; + else + state = SUM_PRECISE_STATE_PLUS_INFINITY; + else if (!isfinite(d) && d < 0.0) + if (state == SUM_PRECISE_STATE_PLUS_INFINITY) + state = SUM_PRECISE_STATE_NOT_A_NUMBER; + else + state = SUM_PRECISE_STATE_MINUS_INFINITY; + else if (!(d == 0.0 && signbit(d)) && (state == SUM_PRECISE_STATE_MINUS_ZERO || state == SUM_PRECISE_STATE_FINITE)) { + state = SUM_PRECISE_STATE_FINITE; + xsum_small_add1(&acc, d); + } + } + } + + switch (state) { + case SUM_PRECISE_STATE_NOT_A_NUMBER: + d = NAN; + break; + case SUM_PRECISE_STATE_MINUS_INFINITY: + d = -INFINITY; + break; + case SUM_PRECISE_STATE_PLUS_INFINITY: + d = INFINITY; + break; + case SUM_PRECISE_STATE_MINUS_ZERO: + d = -0.0; + break; + case SUM_PRECISE_STATE_FINITE: + d = xsum_small_round(&acc); + break; + default: + abort(); } - bf_get_float64(&a, &d, BF_RNDN); // return value deliberately ignored ret = js_float64(d); fail: JS_IteratorClose(ctx, iter, JS_IsException(ret)); JS_FreeValue(ctx, iter); JS_FreeValue(ctx, next); - bf_delete(&a); - bf_delete(&b); return ret; -oom: - JS_ThrowOutOfMemory(ctx); - goto fail; } /* xorshift* random number generator by Marsaglia */ @@ -43965,6 +45571,14 @@ bool lre_check_stack_overflow(void *opaque, size_t alloca_size) return js_check_stack_overflow(ctx->rt, alloca_size); } +int lre_check_timeout(void *opaque) +{ + JSContext *ctx = opaque; + JSRuntime *rt = ctx->rt; + return (rt->interrupt_handler && + rt->interrupt_handler(rt, rt->interrupt_opaque)); +} + #if 0 void *lre_realloc(void *opaque, void *ptr, size_t size) { @@ -44081,7 +45695,11 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, goto fail; } } else { - JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + if (rc == LRE_RET_TIMEOUT) { + JS_ThrowInterrupted(ctx); + } else { + JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + } goto fail; } } else { @@ -44276,7 +45894,11 @@ static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValue ar goto fail; } } else { - JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + if (ret == LRE_RET_TIMEOUT) { + JS_ThrowInterrupted(ctx); + } else { + JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + } goto fail; } break; @@ -45422,6 +47044,7 @@ static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, case JS_TAG_FLOAT64: case JS_TAG_BOOL: case JS_TAG_NULL: + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: case JS_TAG_EXCEPTION: return val; @@ -45452,6 +47075,11 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, tab = JS_UNDEFINED; prop = JS_UNDEFINED; + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + goto exception; + } + if (JS_IsObject(val)) { p = JS_VALUE_GET_OBJ(val); cl = p->class_id; @@ -45599,6 +47227,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, case JS_TAG_NULL: concat_value: return string_buffer_concat_value_free(jsc->b, val); + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: JS_ThrowTypeError(ctx, "BigInt are forbidden in JSON.stringify"); goto exception; @@ -47050,26 +48679,6 @@ static const JSCFunctionListEntry js_symbol_funcs[] = { /* Set/Map/WeakSet/WeakMap */ -typedef struct JSMapRecord { - int ref_count; /* used during enumeration to avoid freeing the record */ - bool empty; /* true if the record is deleted */ - struct JSMapState *map; - struct list_head link; - struct list_head hash_link; - JSValue key; - JSValue value; -} JSMapRecord; - -typedef struct JSMapState { - bool is_weak; /* true if WeakSet/WeakMap */ - struct list_head records; /* list of JSMapRecord.link */ - uint32_t record_count; - struct list_head *hash_table; - uint32_t hash_size; /* must be a power of two */ - uint32_t record_count_threshold; /* count at which a hash table - resize is needed */ -} JSMapState; - #define MAGIC_SET (1 << 0) #define MAGIC_WEAK (1 << 1) @@ -47125,10 +48734,8 @@ static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target, item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); if (JS_IsException(item)) goto fail; - if (done) { - JS_FreeValue(ctx, item); + if (done) break; - } if (is_set) { ret = JS_Call(ctx, adder, obj, 1, vc(&item)); if (JS_IsException(ret)) { @@ -47206,7 +48813,7 @@ static uint32_t map_hash_key(JSContext *ctx, JSValueConst key) uint32_t h; double d; JSFloat64Union u; - bf_t *a; + JSBigInt *r; switch(tag) { case JS_TAG_BOOL: @@ -47222,9 +48829,12 @@ static uint32_t map_hash_key(JSContext *ctx, JSValueConst key) case JS_TAG_INT: d = JS_VALUE_GET_INT(key); goto hash_float64; + case JS_TAG_SHORT_BIG_INT: + d = JS_VALUE_GET_SHORT_BIG_INT(key); + goto hash_float64; case JS_TAG_BIG_INT: - a = JS_GetBigInt(key); - h = hash_string8((void *)a->tab, a->len * sizeof(*a->tab), 0); + r = JS_VALUE_GET_PTR(key); + h = hash_string8((void *)r->tab, r->len * sizeof(*r->tab), 0); break; case JS_TAG_FLOAT64: d = JS_VALUE_GET_FLOAT64(key); @@ -47261,7 +48871,6 @@ static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s, static void map_hash_resize(JSContext *ctx, JSMapState *s) { uint32_t new_hash_size, i, h; - size_t slack; struct list_head *new_hash_table, *el; JSMapRecord *mr; @@ -47270,11 +48879,10 @@ static void map_hash_resize(JSContext *ctx, JSMapState *s) new_hash_size = 4; else new_hash_size = s->hash_size * 2; - new_hash_table = js_realloc2(ctx, s->hash_table, - sizeof(new_hash_table[0]) * new_hash_size, &slack); + new_hash_table = js_realloc(ctx, s->hash_table, + sizeof(new_hash_table[0]) * new_hash_size); if (!new_hash_table) return; - new_hash_size += slack / sizeof(*new_hash_table); for(i = 0; i < new_hash_size; i++) init_list_head(&new_hash_table[i]); @@ -47674,10 +49282,10 @@ static void js_map_mark(JSRuntime *rt, JSValueConst val, s = p->u.map_state; if (s) { + assert(!s->is_weak); list_for_each(el, &s->records) { mr = list_entry(el, JSMapRecord, link); - if (!s->is_weak) - JS_MarkValue(rt, mr->key, mark_func); + JS_MarkValue(rt, mr->key, mark_func); JS_MarkValue(rt, mr->value, mark_func); } } @@ -48741,6 +50349,38 @@ static JSValue promise_reaction_job(JSContext *ctx, int argc, return res2; } +static JSValue promise_rejection_tracker_job(JSContext *ctx, int argc, + JSValueConst *argv) +{ + JSRuntime *rt; + JSPromiseData *s; + JSValueConst promise; + + assert(argc == 1); + + rt = ctx->rt; + promise = argv[0]; + s = JS_GetOpaque(promise, JS_CLASS_PROMISE); + + if (!s || s->promise_state != JS_PROMISE_REJECTED) + return JS_UNDEFINED; /* should never happen */ + + promise_trace(ctx, "promise_rejection_tracker_job\n"); + + // Check again in case the hook was removed. + if (rt->host_promise_rejection_tracker) + rt->host_promise_rejection_tracker( + ctx, promise, s->promise_result, s->is_handled, rt->host_promise_rejection_tracker_opaque); + + return JS_UNDEFINED; +} + +void JS_SetPromiseHook(JSRuntime *rt, JSPromiseHook promise_hook, void *opaque) +{ + rt->promise_hook = promise_hook; + rt->promise_hook_opaque = opaque; +} + void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, JSHostPromiseRejectionTracker *cb, void *opaque) @@ -48764,11 +50404,11 @@ static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise, promise_trace(ctx, "fulfill_or_reject_promise: is_reject=%d\n", is_reject); - if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { + if (s->promise_state == JS_PROMISE_FULFILLED) { JSRuntime *rt = ctx->rt; - if (rt->host_promise_rejection_tracker) { - rt->host_promise_rejection_tracker(ctx, promise, value, false, - rt->host_promise_rejection_tracker_opaque); + if (rt->promise_hook) { + rt->promise_hook(ctx, JS_PROMISE_HOOK_RESOLVE, promise, + JS_UNDEFINED, rt->promise_hook_opaque); } } @@ -48789,12 +50429,12 @@ static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise, list_del(&rd->link); promise_reaction_data_free(ctx->rt, rd); } -} -static void reject_promise(JSContext *ctx, JSValueConst promise, - JSValueConst value) -{ - fulfill_or_reject_promise(ctx, promise, value, true); + if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { + JSRuntime *rt = ctx->rt; + if (rt->host_promise_rejection_tracker) + JS_EnqueueJob(ctx, promise_rejection_tracker_job, 1, &promise); + } } static JSValue js_promise_resolve_thenable_job(JSContext *ctx, @@ -48802,6 +50442,7 @@ static JSValue js_promise_resolve_thenable_job(JSContext *ctx, { JSValueConst promise, thenable, then; JSValue args[2], res; + JSRuntime *rt; promise_trace(ctx, "js_promise_resolve_thenable_job\n"); @@ -48811,7 +50452,16 @@ static JSValue js_promise_resolve_thenable_job(JSContext *ctx, then = argv[2]; if (js_create_resolving_functions(ctx, args, promise) < 0) return JS_EXCEPTION; + rt = ctx->rt; + if (rt->promise_hook) { + rt->promise_hook(ctx, JS_PROMISE_HOOK_BEFORE, promise, JS_UNDEFINED, + rt->promise_hook_opaque); + } res = JS_Call(ctx, then, thenable, 2, vc(args)); + if (rt->promise_hook) { + rt->promise_hook(ctx, JS_PROMISE_HOOK_AFTER, promise, JS_UNDEFINED, + rt->promise_hook_opaque); + } if (JS_IsException(res)) { JSValue error = JS_GetException(ctx); res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, vc(&error)); @@ -48932,7 +50582,7 @@ static JSValue js_promise_resolve_function_call(JSContext *ctx, JSValue error; fail_reject: error = JS_GetException(ctx); - reject_promise(ctx, s->promise, error); + fulfill_or_reject_promise(ctx, s->promise, error, true); JS_FreeValue(ctx, error); } else if (!JS_IsFunction(ctx, then)) { JS_FreeValue(ctx, then); @@ -48994,6 +50644,7 @@ static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target, JSValueConst executor; JSValue obj; JSPromiseData *s; + JSRuntime *rt; JSValue args[2], ret; int i; @@ -49014,6 +50665,14 @@ static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target, JS_SetOpaqueInternal(obj, s); if (js_create_resolving_functions(ctx, args, obj)) goto fail; + rt = ctx->rt; + if (rt->promise_hook) { + JSValueConst parent_promise = JS_UNDEFINED; + if (rt->parent_promise) + parent_promise = rt->parent_promise->value; + rt->promise_hook(ctx, JS_PROMISE_HOOK_INIT, obj, parent_promise, + rt->promise_hook_opaque); + } ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, vc(args)); if (JS_IsException(ret)) { JSValue ret2, error; @@ -49071,8 +50730,7 @@ static JSValue js_new_promise_capability(JSContext *ctx, executor = js_promise_executor_new(ctx); if (JS_IsException(executor)) - return executor; - + return JS_EXCEPTION; if (JS_IsUndefined(ctor)) { result_promise = js_promise_constructor(ctx, ctor, 1, vc(&executor)); } else { @@ -49523,10 +51181,8 @@ static __exception int perform_promise_then(JSContext *ctx, JSValueConst args[5]; if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { JSRuntime *rt = ctx->rt; - if (rt->host_promise_rejection_tracker) { - rt->host_promise_rejection_tracker(ctx, promise, s->promise_result, - true, rt->host_promise_rejection_tracker_opaque); - } + if (rt->host_promise_rejection_tracker) + JS_EnqueueJob(ctx, promise_rejection_tracker_job, 1, &promise); } i = s->promise_state - JS_PROMISE_FULFILLED; rd = rd_array[i]; @@ -49547,7 +51203,10 @@ static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue ctor, result_promise, resolving_funcs[2]; + bool have_promise_hook; + JSValueLink link; JSPromiseData *s; + JSRuntime *rt; int i, ret; s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE); @@ -49557,7 +51216,16 @@ static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); if (JS_IsException(ctor)) return ctor; + rt = ctx->rt; + // always restore, even if js_new_promise_capability callee removes hook + have_promise_hook = (rt->promise_hook != NULL); + if (have_promise_hook) { + link = (JSValueLink){rt->parent_promise, this_val}; + rt->parent_promise = &link; + } result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor); + if (have_promise_hook) + rt->parent_promise = link.next; JS_FreeValue(ctx, ctor); if (JS_IsException(result_promise)) return result_promise; @@ -50804,6 +52472,9 @@ static bool string_get_digits(const uint8_t *sp, int *pp, int *pval, p_start = p; while ((c = sp[p]) >= '0' && c <= '9') { + /* arbitrary limit to 9 digits */ + if (v >= 100000000) + return false; v = v * 10 + c - '0'; p++; if (p - p_start == max_digits) @@ -50847,7 +52518,7 @@ static bool string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, bool stric sgn = sp[p++]; if (sgn == '+' || sgn == '-') { int n = p; - if (!string_get_digits(sp, &p, &hh, 1, 9)) + if (!string_get_digits(sp, &p, &hh, 1, 0)) return false; n = p - n; if (strict && n != 2 && n != 4) @@ -51041,7 +52712,7 @@ static bool js_date_parse_otherstring(const uint8_t *sp, *is_local = false; } else { p++; - if (string_get_digits(sp, &p, &val, 1, 9)) { + if (string_get_digits(sp, &p, &val, 1, 0)) { if (c == '-') { if (val == 0) return false; @@ -51052,7 +52723,7 @@ static bool js_date_parse_otherstring(const uint8_t *sp, } } } else - if (string_get_digits(sp, &p, &val, 1, 9)) { + if (string_get_digits(sp, &p, &val, 1, 0)) { if (string_skip_char(sp, &p, ':')) { /* time part */ fields[3] = val; @@ -51444,7 +53115,9 @@ void JS_AddIntrinsicDate(JSContext *ctx) void JS_AddIntrinsicEval(JSContext *ctx) { +#ifndef QJS_DISABLE_PARSER ctx->eval_internal = __JS_EvalInternal; +#endif // QJS_DISABLE_PARSER } /* BigInt */ @@ -51460,40 +53133,25 @@ static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) case JS_TAG_BOOL: val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val)); break; + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: break; case JS_TAG_FLOAT64: { - bf_t *a, a_s; - - a = JS_ToBigInt1(ctx, &a_s, val); - if (!bf_is_finite(a)) { - JS_FreeValue(ctx, val); - val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt"); - } else { - 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 (ret & BF_ST_MEM_ERROR) { - JS_FreeValue(ctx, val1); - val = JS_ThrowOutOfMemory(ctx); - } else if (ret & BF_ST_INEXACT) { - JS_FreeValue(ctx, val1); + double d = JS_VALUE_GET_FLOAT64(val); + JSBigInt *r; + int res; + r = js_bigint_from_float64(ctx, &res, d); + if (!r) { + if (res == 0) { + val = JS_EXCEPTION; + } else if (res == 1) { val = JS_ThrowRangeError(ctx, "cannot convert to BigInt: not an integer"); } else { - val = JS_CompactBigInt(ctx, val1); - } + val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt"); } + } else { + val = JS_CompactBigInt(ctx, r); } - if (a == &a_s) - bf_delete(a); } break; case JS_TAG_STRING: @@ -51573,38 +53231,62 @@ static JSValue js_bigint_asUintN(JSContext *ctx, int argc, JSValueConst *argv, int asIntN) { uint64_t bits; - bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s; - JSValue res; + JSValue res, a; if (JS_ToIndex(ctx, &bits, argv[0])) return JS_EXCEPTION; - res = JS_NewBigInt(ctx); - if (JS_IsException(res)) - return JS_EXCEPTION; - a = JS_ToBigInt(ctx, &a_s, argv[1]); - if (!a) { - JS_FreeValue(ctx, res); + a = JS_ToBigInt(ctx, argv[1]); + if (JS_IsException(a)) return JS_EXCEPTION; + if (bits == 0) { + JS_FreeValue(ctx, a); + res = __JS_NewShortBigInt(ctx, 0); + } else if (JS_VALUE_GET_TAG(a) == JS_TAG_SHORT_BIG_INT) { + /* fast case */ + if (bits >= JS_SHORT_BIG_INT_BITS) { + res = a; + } else { + uint64_t v; + int shift; + shift = 64 - bits; + v = JS_VALUE_GET_SHORT_BIG_INT(a); + v = v << shift; + if (asIntN) + v = (int64_t)v >> shift; + else + v = v >> shift; + res = __JS_NewShortBigInt(ctx, v); + } + } else { + JSBigInt *r, *p = JS_VALUE_GET_PTR(a); + if (bits >= p->len * JS_LIMB_BITS) { + res = a; + } else { + int len, shift, i; + js_limb_t v; + len = (bits + JS_LIMB_BITS - 1) / JS_LIMB_BITS; + r = js_bigint_new(ctx, len); + if (!r) { + JS_FreeValue(ctx, a); + return JS_EXCEPTION; + } + r->len = len; + for(i = 0; i < len - 1; i++) + r->tab[i] = p->tab[i]; + shift = (-bits) & (JS_LIMB_BITS - 1); + /* 0 <= shift <= JS_LIMB_BITS - 1 */ + v = p->tab[len - 1] << shift; + if (asIntN) + v = (js_slimb_t)v >> shift; + else + v = v >> shift; + r->tab[len - 1] = v; + r = js_bigint_normalize(ctx, r); + JS_FreeValue(ctx, a); + res = JS_CompactBigInt(ctx, r); + } } - /* XXX: optimize */ - 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); - bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ); - bf_logic_and(r, a, mask); - if (asIntN && bits != 0) { - bf_set_ui(mask, 1); - bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ); - if (bf_cmpu(r, mask) >= 0) { - bf_set_ui(mask, 1); - bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ); - bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ); - } - } - bf_delete(mask); - JS_FreeBigInt(ctx, a, &a_s); - return JS_CompactBigInt(ctx, res); + return res; } static const JSCFunctionListEntry js_bigint_funcs[] = { @@ -51778,6 +53460,10 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) ctx->array_ctor = js_dup(obj); JS_SetPropertyFunctionList(ctx, obj, js_array_funcs, countof(js_array_funcs)); + JS_DefineAutoInitProperty(ctx, obj, JS_ATOM_fromAsync, + JS_AUTOINIT_ID_BYTECODE, + (void *)(uintptr_t)JS_BUILTIN_ARRAY_FROMASYNC, + JS_PROP_WRITABLE|JS_PROP_CONFIGURABLE); /* XXX: create auto_initializer */ { @@ -52386,8 +54072,12 @@ static JSValue js_array_buffer_resize(JSContext *ctx, JSValueConst this_val, list_for_each(el, &abuf->array_list) { ta = list_entry(el, JSTypedArray, link); p = ta->obj; - if (p->class_id == JS_CLASS_DATAVIEW) + if (p->class_id == JS_CLASS_DATAVIEW) { + if (ta->track_rab && ta->offset < len) + ta->length = len - ta->offset; + continue; + } p->u.array.count = 0; p->u.array.u.ptr = NULL; size_log2 = typed_array_size_log2(p->class_id); @@ -53340,19 +55030,33 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, v64 = d; is_int = (v64 == d); } - } else - if (tag == JS_TAG_BIG_INT) { - JSBigInt *p1 = JS_VALUE_GET_PTR(argv[0]); + } else if (tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT) { + JSBigIntBuf buf1; + JSBigInt *p1; + int sz = (64 / JS_LIMB_BITS); + if (tag == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, argv[0]); + else + p1 = JS_VALUE_GET_PTR(argv[0]); if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) { - if (bf_get_int64(&v64, &p1->num, 0) != 0) - goto done; + if (p1->len > sz) + goto done; /* does not fit an int64 : cannot be found */ } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) { - if (bf_get_uint64((uint64_t *)&v64, &p1->num) != 0) + if (js_bigint_sign(p1)) + goto done; /* v < 0 */ + if (p1->len <= sz) { + /* OK */ + } else if (p1->len == sz + 1 && p1->tab[sz] == 0) { + /* 2^63 <= v <= 2^64-1 */ + } else { goto done; + } } else { goto done; } + if (JS_ToBigInt64(ctx, &v64, argv[0])) + goto exception; d = 0; is_bigint = 1; } else { @@ -54259,10 +55963,8 @@ static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen, val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); if (JS_IsException(val)) goto fail; - if (done) { - JS_FreeValue(ctx, val); + if (done) break; - } if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0) goto fail; k++; @@ -54776,8 +56478,7 @@ static JSValue js_dataview_setValue(JSContext *ctx, if (class_id <= JS_CLASS_UINT32_ARRAY) { if (JS_ToUint32(ctx, &v, val)) return JS_EXCEPTION; - } else - if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) { + } else if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) { if (JS_ToBigInt64(ctx, (int64_t *)&v64, val)) return JS_EXCEPTION; } else { @@ -55156,7 +56857,7 @@ static JSValue js_atomics_store(JSContext *ctx, return JS_EXCEPTION; if (size_log2 == 3) { int64_t v64; - ret = JS_ToBigIntValueFree(ctx, js_dup(argv[2])); + ret = JS_ToBigIntFree(ctx, js_dup(argv[2])); if (JS_IsException(ret)) return ret; if (JS_ToBigInt64(ctx, &v64, ret)) { @@ -55846,6 +57547,7 @@ static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref) assert(!mr->empty); /* no iterator on WeakMap/WeakSet */ list_del(&mr->hash_link); list_del(&mr->link); + s->record_count--; break; case JS_WEAK_REF_KIND_WEAK_REF: wrd = wr->u.weak_ref_data; @@ -56082,6 +57784,7 @@ static void _JS_AddIntrinsicCallSite(JSContext *ctx) bool JS_DetectModule(const char *input, size_t input_len) { +#ifndef QJS_DISABLE_PARSER JSRuntime *rt; JSContext *ctx; JSValue val; @@ -56110,6 +57813,9 @@ bool JS_DetectModule(const char *input, size_t input_len) JS_FreeContext(ctx); JS_FreeRuntime(rt); return is_module; +#else + return false; +#endif // QJS_DISABLE_PARSER } uintptr_t js_std_cmd(int cmd, ...) { |