cosmopolitan/third_party/quickjs/bigint.c
Justine Tunney ae5d06dc53 Unbloat build config
- 10.5% reduction of o//depend dependency graph
- 8.8% reduction in latency of make command
- Fix issue with temporary file cleanup

There's a new -w option in compile.com that turns off the recent
Landlock output path workaround for "good commands" which do not
unlink() the output file like GNU tooling does.

Our new GNU Make unveil sandboxing appears to have zero overhead
in the grand scheme of things. Full builds are pretty fast since
the only thing that's actually slowed us down is probably libcxx

    make -j16 MODE=rel
    RL: took 85,732,063µs wall time
    RL: ballooned to 323,612kb in size
    RL: needed 828,560,521µs cpu (11% kernel)
    RL: caused 39,080,670 page faults (99% memcpy)
    RL: 350,073 context switches (72% consensual)
    RL: performed 0 reads and 11,494,960 write i/o operations

pledge() and unveil() no longer consider ENOSYS to be an error.
These functions have also been added to Python's cosmo module.

This change also removes some WIN32 APIs and System Five magnums
which we're not using and it's doubtful anyone else would be too
2022-08-10 04:43:09 -07: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\"");
/* clang-format off */
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);
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));
}