cosmopolitan/third_party/quickjs/bigint.c
Justine Tunney fa20edc44d
Reduce header complexity
- Remove most __ASSEMBLER__ __LINKER__ ifdefs
- Rename libc/intrin/bits.h to libc/serialize.h
- Block pthread cancelation in fchmodat() polyfill
- Remove `clang-format off` statements in third_party
2023-11-28 14:39:42 -08:00

851 lines
26 KiB
C

/*
* QuickJS Javascript Engine
*
* Copyright (c) 2017-2021 Fabrice Bellard
* Copyright (c) 2017-2021 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/runtime/runtime.h"
#include "third_party/quickjs/internal.h"
asm(".ident\t\"\\n\\n\
QuickJS (MIT License)\\n\
Copyright (c) 2017-2021 Fabrice Bellard\\n\
Copyright (c) 2017-2021 Charlie Gordon\"");
asm(".include \"libc/disclaimer.inc\"");
JSValue JS_NewBigInt(JSContext *ctx)
{
JSBigFloat *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,
BOOL convert_to_safe_integer)
{
int64_t v;
bf_t *a;
if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT)
return val; /* fail safe */
a = JS_GetBigInt(val);
if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 &&
v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
JS_FreeValue(ctx, val);
return JS_NewInt64(ctx, v);
} else if (a->expn == BF_EXP_ZERO && a->sign) {
JSBigFloat *p = JS_VALUE_GET_PTR(val);
(void)p;
assert(p->header.ref_count == 1);
a->sign = 0;
}
return val;
}
/* Convert the big int to a safe integer if in math mode. normalize
the zero representation. Could also be used to convert the bigint
to a short bigint value. The reference count of the value must be
1. Cannot fail */
JSValue JS_CompactBigInt(JSContext *ctx, JSValue val)
{
return JS_CompactBigInt1(ctx, val, is_math_mode(ctx));
}
/* return NaN if bad bigint literal */
JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)
{
const char *str, *p;
size_t len;
int flags;
str = JS_ToCStringLen(ctx, &len, val);
JS_FreeValue(ctx, val);
if (!str)
return JS_EXCEPTION;
p = str;
p += skip_spaces(p);
if ((p - str) == len) {
val = JS_NewBigInt64(ctx, 0);
} else {
flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;
if (is_math_mode(ctx))
flags |= ATOD_MODE_BIGINT;
val = js_atof(ctx, p, &p, 0, flags);
p += skip_spaces(p);
if (!JS_IsException(val)) {
if ((p - str) != len) {
JS_FreeValue(ctx, val);
val = JS_NAN;
}
}
}
JS_FreeCString(ctx, str);
return val;
}
static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val)
{
val = JS_StringToBigInt(ctx, val);
if (JS_VALUE_IS_NAN(val))
return JS_ThrowSyntaxError(ctx, "invalid bigint literal");
return val;
}
static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)
{
uint32_t tag;
redo:
tag = JS_VALUE_GET_NORM_TAG(val);
switch(tag) {
case JS_TAG_INT:
case JS_TAG_BOOL:
val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));
break;
case JS_TAG_BIG_INT:
break;
case JS_TAG_FLOAT64:
case JS_TAG_BIG_FLOAT:
{
bf_t *a, a_s;
a = JS_ToBigFloat(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);
val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer");
} else {
val = JS_CompactBigInt(ctx, val1);
}
}
if (a == &a_s)
bf_delete(a);
}
break;
case JS_TAG_BIG_DECIMAL:
val = JS_ToStringFree(ctx, val);
if (JS_IsException(val))
break;
goto redo;
case JS_TAG_STRING:
val = JS_StringToBigIntErr(ctx, val);
break;
case JS_TAG_OBJECT:
val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
if (JS_IsException(val))
break;
goto redo;
case JS_TAG_NULL:
case JS_TAG_UNDEFINED:
default:
JS_FreeValue(ctx, val);
return JS_ThrowTypeError(ctx, "cannot convert to bigint");
}
return val;
}
static JSValue js_bigint_constructor(JSContext *ctx,
JSValueConst new_target,
int argc, JSValueConst *argv)
{
if (!JS_IsUndefined(new_target))
return JS_ThrowTypeError(ctx, "not a constructor");
return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));
}
static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)
{
if (JS_IsBigInt(ctx, this_val))
return JS_DupValue(ctx, this_val);
if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
JSObject *p = JS_VALUE_GET_OBJ(this_val);
if (p->class_id == JS_CLASS_BIG_INT) {
if (JS_IsBigInt(ctx, p->u.object_data))
return JS_DupValue(ctx, p->u.object_data);
}
}
return JS_ThrowTypeError(ctx, "not a bigint");
}
static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValue val;
int base;
JSValue ret;
val = js_thisBigIntValue(ctx, this_val);
if (JS_IsException(val))
return val;
if (argc == 0 || JS_IsUndefined(argv[0])) {
base = 10;
} else {
base = js_get_radix(ctx, argv[0]);
if (base < 0)
goto fail;
}
ret = js_bigint_to_string1(ctx, val, base);
JS_FreeValue(ctx, val);
return ret;
fail:
JS_FreeValue(ctx, val);
return JS_EXCEPTION;
}
static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return js_thisBigIntValue(ctx, this_val);
}
static JSValue js_bigint_div(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
bf_t a_s, b_s, *a, *b, *r, *q;
int status;
JSValue q_val, r_val;
q_val = JS_NewBigInt(ctx);
if (JS_IsException(q_val))
return JS_EXCEPTION;
r_val = JS_NewBigInt(ctx);
if (JS_IsException(r_val))
goto fail;
b = NULL;
a = JS_ToBigInt(ctx, &a_s, argv[0]);
if (!a)
goto fail;
b = JS_ToBigInt(ctx, &b_s, argv[1]);
if (!b) {
JS_FreeBigInt(ctx, a, &a_s);
goto fail;
}
q = JS_GetBigInt(q_val);
r = JS_GetBigInt(r_val);
status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf);
JS_FreeBigInt(ctx, a, &a_s);
JS_FreeBigInt(ctx, b, &b_s);
if (UNLIKELY(status)) {
throw_bf_exception(ctx, status);
goto fail;
}
q_val = JS_CompactBigInt(ctx, q_val);
if (magic & 0x10) {
JSValue ret;
ret = JS_NewArray(ctx);
if (JS_IsException(ret))
goto fail;
JS_SetPropertyUint32(ctx, ret, 0, q_val);
JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val));
return ret;
} else {
JS_FreeValue(ctx, r_val);
return q_val;
}
fail:
JS_FreeValue(ctx, q_val);
JS_FreeValue(ctx, r_val);
return JS_EXCEPTION;
}
static JSValue js_bigint_sqrt(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
bf_t a_s, *a, *r, *rem;
int status;
JSValue r_val, rem_val;
r_val = JS_NewBigInt(ctx);
if (JS_IsException(r_val))
return JS_EXCEPTION;
rem_val = JS_NewBigInt(ctx);
if (JS_IsException(rem_val))
return JS_EXCEPTION;
r = JS_GetBigInt(r_val);
rem = JS_GetBigInt(rem_val);
a = JS_ToBigInt(ctx, &a_s, argv[0]);
if (!a)
goto fail;
status = bf_sqrtrem(r, rem, a);
JS_FreeBigInt(ctx, a, &a_s);
if (UNLIKELY(status & ~BF_ST_INEXACT)) {
throw_bf_exception(ctx, status);
goto fail;
}
r_val = JS_CompactBigInt(ctx, r_val);
if (magic) {
JSValue ret;
ret = JS_NewArray(ctx);
if (JS_IsException(ret))
goto fail;
JS_SetPropertyUint32(ctx, ret, 0, r_val);
JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val));
return ret;
} else {
JS_FreeValue(ctx, rem_val);
return r_val;
}
fail:
JS_FreeValue(ctx, r_val);
JS_FreeValue(ctx, rem_val);
return JS_EXCEPTION;
}
static JSValue js_bigint_op1(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv,
int magic)
{
bf_t a_s, *a;
int64_t res;
a = JS_ToBigInt(ctx, &a_s, argv[0]);
if (!a)
return JS_EXCEPTION;
switch(magic) {
case 0: /* floorLog2 */
if (a->sign || a->expn <= 0) {
res = -1;
} else {
res = a->expn - 1;
}
break;
case 1: /* ctz */
if (bf_is_zero(a)) {
res = -1;
} else {
res = bf_get_exp_min(a);
}
break;
default:
abort();
}
JS_FreeBigInt(ctx, a, &a_s);
return JS_NewBigInt64(ctx, res);
}
static JSValue js_bigint_asUintN(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv, int asIntN)
{
uint64_t bits;
bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s;
JSValue res;
if (JS_ToIndex(ctx, &bits, argv[0]))
return JS_EXCEPTION;
res = JS_NewBigInt(ctx);
if (JS_IsException(res))
return JS_EXCEPTION;
r = JS_GetBigInt(res);
a = JS_ToBigInt(ctx, &a_s, argv[1]);
if (!a) {
JS_FreeValue(ctx, res);
return JS_EXCEPTION;
}
/* 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);
}
static const JSCFunctionListEntry js_bigint_funcs[] = {
JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ),
JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ),
/* QuickJS extensions */
JS_CFUNC_MAGIC_DEF("tdiv", 2, js_bigint_div, BF_RNDZ ),
JS_CFUNC_MAGIC_DEF("fdiv", 2, js_bigint_div, BF_RNDD ),
JS_CFUNC_MAGIC_DEF("cdiv", 2, js_bigint_div, BF_RNDU ),
JS_CFUNC_MAGIC_DEF("ediv", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN ),
JS_CFUNC_MAGIC_DEF("tdivrem", 2, js_bigint_div, BF_RNDZ | 0x10 ),
JS_CFUNC_MAGIC_DEF("fdivrem", 2, js_bigint_div, BF_RNDD | 0x10 ),
JS_CFUNC_MAGIC_DEF("cdivrem", 2, js_bigint_div, BF_RNDU | 0x10 ),
JS_CFUNC_MAGIC_DEF("edivrem", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN | 0x10 ),
JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigint_sqrt, 0 ),
JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_bigint_sqrt, 1 ),
JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_bigint_op1, 0 ),
JS_CFUNC_MAGIC_DEF("ctz", 1, js_bigint_op1, 1 ),
};
static const JSCFunctionListEntry js_bigint_proto_funcs[] = {
JS_CFUNC_DEF("toString", 0, js_bigint_toString ),
JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ),
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ),
};
static JSValue js_string_to_bigint(JSContext *ctx, const char *buf,
int radix, int flags, slimb_t *pexponent)
{
bf_t a_s, *a = &a_s;
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);
}
val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0);
return val;
}
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 && !is_math_mode(ctx)) {
JS_ThrowTypeError(ctx, "bigint argument with unary +");
JS_FreeValue(ctx, op1);
return -1;
}
res = JS_NewBigInt(ctx);
if (JS_IsException(res)) {
JS_FreeValue(ctx, op1);
return -1;
}
r = JS_GetBigInt(res);
a = JS_ToBigInt(ctx, &a_s, op1);
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);
JS_FreeValue(ctx, op1);
if (UNLIKELY(ret)) {
JS_FreeValue(ctx, res);
throw_bf_exception(ctx, ret);
return -1;
}
res = JS_CompactBigInt(ctx, res);
*pres = res;
return 0;
}
/* try to call the operation on the operatorSet field of 'obj'. Only
used for "/" and "**" on the BigInt prototype in math mode */
static __exception int js_call_binary_op_simple(JSContext *ctx,
JSValue *pret,
JSValueConst obj,
JSValueConst op1,
JSValueConst op2,
OPCodeEnum op)
{
JSValue opset1_obj, method, ret, new_op1, new_op2;
JSOperatorSetData *opset1;
JSOverloadableOperatorEnum ovop;
JSObject *p;
JSValueConst args[2];
opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);
if (JS_IsException(opset1_obj))
goto exception;
if (JS_IsUndefined(opset1_obj))
return 0;
opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
if (!opset1)
goto exception;
ovop = get_ovop_from_opcode(op);
p = opset1->self_ops[ovop];
if (!p) {
JS_FreeValue(ctx, opset1_obj);
return 0;
}
new_op1 = JS_ToNumeric(ctx, op1);
if (JS_IsException(new_op1))
goto exception;
new_op2 = JS_ToNumeric(ctx, op2);
if (JS_IsException(new_op2)) {
JS_FreeValue(ctx, new_op1);
goto exception;
}
method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
args[0] = new_op1;
args[1] = new_op2;
ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);
JS_FreeValue(ctx, new_op1);
JS_FreeValue(ctx, new_op2);
if (JS_IsException(ret))
goto exception;
JS_FreeValue(ctx, opset1_obj);
*pret = ret;
return 1;
exception:
JS_FreeValue(ctx, opset1_obj);
*pret = JS_UNDEFINED;
return -1;
}
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;
res = JS_NewBigInt(ctx);
if (JS_IsException(res))
goto fail;
a = JS_ToBigInt(ctx, &a_s, op1);
if (!a)
goto fail;
b = JS_ToBigInt(ctx, &b_s, op2);
if (!b) {
JS_FreeBigInt(ctx, a, &a_s);
goto fail;
}
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:
if (!is_math_mode(ctx)) {
bf_t rem_s, *rem = &rem_s;
bf_init(ctx->bf_ctx, rem);
ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ,
BF_RNDZ);
bf_delete(rem);
} else {
goto math_mode_div_pow;
}
break;
case OP_math_mod:
/* Euclidian remainder */
ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP;
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) {
if (!is_math_mode(ctx)) {
ret = BF_ST_INVALID_OP;
} else {
math_mode_div_pow:
JS_FreeValue(ctx, res);
ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op);
if (ret != 0) {
JS_FreeBigInt(ctx, a, &a_s);
JS_FreeBigInt(ctx, b, &b_s);
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
if (ret < 0) {
return -1;
} else {
*pres = res;
return 0;
}
}
/* if no BigInt power operator defined, return a
bigfloat */
res = JS_NewBigFloat(ctx);
if (JS_IsException(res)) {
JS_FreeBigInt(ctx, a, &a_s);
JS_FreeBigInt(ctx, b, &b_s);
goto fail;
}
r = JS_GetBigFloat(res);
if (op == OP_div) {
ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR;
} else {
ret = bf_pow(r, a, b, ctx->fp_env.prec,
ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR;
}
JS_FreeBigInt(ctx, a, &a_s);
JS_FreeBigInt(ctx, b, &b_s);
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
if (UNLIKELY(ret)) {
JS_FreeValue(ctx, res);
throw_bf_exception(ctx, ret);
return -1;
}
*pres = res;
return 0;
}
} 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);
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
if (UNLIKELY(ret)) {
JS_FreeValue(ctx, res);
throw_bf_exception(ctx, ret);
return -1;
}
*pres = JS_CompactBigInt(ctx, res);
return 0;
fail:
JS_FreeValue(ctx, res);
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
return -1;
}
void JS_AddIntrinsicBigInt(JSContext *ctx)
{
JSRuntime *rt = ctx->rt;
JSValueConst obj1;
rt->bigint_ops.to_string = js_bigint_to_string;
rt->bigint_ops.from_string = js_string_to_bigint;
rt->bigint_ops.unary_arith = js_unary_arith_bigint;
rt->bigint_ops.binary_arith = js_binary_arith_bigint;
rt->bigint_ops.compare = js_compare_bigfloat;
ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
js_bigint_proto_funcs,
countof(js_bigint_proto_funcs));
obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1,
ctx->class_proto[JS_CLASS_BIG_INT]);
JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs,
countof(js_bigint_funcs));
}
/* if the returned bigfloat is allocated it is equal to
'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */
static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)
{
uint32_t tag;
bf_t *r;
JSBigFloat *p;
redo:
tag = JS_VALUE_GET_NORM_TAG(val);
switch(tag) {
case JS_TAG_INT:
case JS_TAG_NULL:
case JS_TAG_UNDEFINED:
if (!is_math_mode(ctx))
goto fail;
/* fall tru */
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_FLOAT64:
{
double d = JS_VALUE_GET_FLOAT64(val);
if (!is_math_mode(ctx))
goto fail;
if (!isfinite(d))
goto fail;
r = buf;
bf_init(ctx->bf_ctx, r);
d = trunc(d);
bf_set_float64(r, d);
}
break;
case JS_TAG_BIG_INT:
p = JS_VALUE_GET_PTR(val);
r = &p->num;
break;
case JS_TAG_BIG_FLOAT:
if (!is_math_mode(ctx))
goto fail;
p = JS_VALUE_GET_PTR(val);
if (!bf_is_finite(&p->num))
goto fail;
r = buf;
bf_init(ctx->bf_ctx, r);
bf_set(r, &p->num);
bf_rint(r, BF_RNDZ);
JS_FreeValue(ctx, val);
break;
case JS_TAG_STRING:
val = JS_StringToBigIntErr(ctx, val);
if (JS_IsException(val))
return NULL;
goto redo;
case JS_TAG_OBJECT:
val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
if (JS_IsException(val))
return NULL;
goto redo;
default:
fail:
JS_FreeValue(ctx, val);
JS_ThrowTypeError(ctx, "cannot convert to bigint");
return NULL;
}
return r;
}
bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val)
{
return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, 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);
}
}
/* free the bf_t allocated by JS_ToBigInt */
void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf)
{
if (a == buf) {
bf_delete(a);
} else {
JSBigFloat *p = (JSBigFloat *)((uint8_t *)a -
offsetof(JSBigFloat, num));
JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_FLOAT, p));
}
}
/* XXX: merge with JS_ToInt64Free with a specific flag */
int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
{
bf_t a_s, *a;
a = JS_ToBigIntFree(ctx, &a_s, val);
if (!a) {
*pres = 0;
return -1;
}
bf_get_int64(pres, a, BF_GET_INT_MOD);
JS_FreeBigInt(ctx, a, &a_s);
return 0;
}
int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
{
return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val));
}