cosmopolitan/third_party/quickjs/float.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

1161 lines
36 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/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_NewBigFloat(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_FLOAT, p);
}
static JSValue js_float_env_constructor(JSContext *ctx,
JSValueConst new_target,
int argc, JSValueConst *argv)
{
JSValue obj;
JSFloatEnv *fe;
int64_t prec;
int flags, rndmode;
prec = ctx->fp_env.prec;
flags = ctx->fp_env.flags;
if (!JS_IsUndefined(argv[0])) {
if (JS_ToInt64Sat(ctx, &prec, argv[0]))
return JS_EXCEPTION;
if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
return JS_ThrowRangeError(ctx, "invalid precision");
flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */
if (argc > 1 && !JS_IsUndefined(argv[1])) {
if (JS_ToInt32Sat(ctx, &rndmode, argv[1]))
return JS_EXCEPTION;
if (rndmode < BF_RNDN || rndmode > BF_RNDF)
return JS_ThrowRangeError(ctx, "invalid rounding mode");
flags = rndmode;
}
}
obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV);
if (JS_IsException(obj))
return JS_EXCEPTION;
fe = js_malloc(ctx, sizeof(*fe));
if (!fe)
return JS_EXCEPTION;
fe->prec = prec;
fe->flags = flags;
fe->status = 0;
JS_SetOpaque(obj, fe);
return obj;
}
/* if the returned bigfloat is allocated it is equal to
'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return
NULL in case of error. */
bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val)
{
uint32_t tag;
bf_t *r;
JSBigFloat *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:
case JS_TAG_BIG_FLOAT:
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;
}
return r;
}
static JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val)
{
return JS_NewInt64(ctx, ctx->fp_env.prec);
}
static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val)
{
return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags));
}
static JSValue js_float_env_setPrec(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValueConst func;
int exp_bits, flags, saved_flags;
JSValue ret;
limb_t saved_prec;
int64_t prec;
func = argv[0];
if (JS_ToInt64Sat(ctx, &prec, argv[1]))
return JS_EXCEPTION;
if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
return JS_ThrowRangeError(ctx, "invalid precision");
exp_bits = BF_EXP_BITS_MAX;
if (argc > 2 && !JS_IsUndefined(argv[2])) {
if (JS_ToInt32Sat(ctx, &exp_bits, argv[2]))
return JS_EXCEPTION;
if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX)
return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
}
flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits);
saved_prec = ctx->fp_env.prec;
saved_flags = ctx->fp_env.flags;
ctx->fp_env.prec = prec;
ctx->fp_env.flags = flags;
ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL);
/* always restore the floating point precision */
ctx->fp_env.prec = saved_prec;
ctx->fp_env.flags = saved_flags;
return ret;
}
static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic)
{
JSFloatEnv *fe;
fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
if (!fe)
return JS_EXCEPTION;
switch(magic) {
case FE_PREC:
return JS_NewInt64(ctx, fe->prec);
case FE_EXP:
return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags));
case FE_RNDMODE:
return JS_NewInt32(ctx, fe->flags & BF_RND_MASK);
case FE_SUBNORMAL:
return JS_NewBool(ctx, (fe->flags & BF_FLAG_SUBNORMAL) != 0);
default:
return JS_NewBool(ctx, (fe->status & magic) != 0);
}
}
static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val)
{
int rnd_mode;
if (JS_ToInt32Sat(ctx, &rnd_mode, val))
return -1;
if (rnd_mode < BF_RNDN || rnd_mode > BF_RNDF) {
JS_ThrowRangeError(ctx, "invalid rounding mode");
return -1;
}
return rnd_mode;
}
static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic)
{
JSFloatEnv *fe;
int b;
int64_t prec;
fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
if (!fe)
return JS_EXCEPTION;
switch(magic) {
case FE_PREC:
if (JS_ToInt64Sat(ctx, &prec, val))
return JS_EXCEPTION;
if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
return JS_ThrowRangeError(ctx, "invalid precision");
fe->prec = prec;
break;
case FE_EXP:
if (JS_ToInt32Sat(ctx, &b, val))
return JS_EXCEPTION;
if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX)
return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) |
bf_set_exp_bits(b);
break;
case FE_RNDMODE:
b = bigfloat_get_rnd_mode(ctx, val);
if (b < 0)
return JS_EXCEPTION;
fe->flags = (fe->flags & ~BF_RND_MASK) | b;
break;
case FE_SUBNORMAL:
b = JS_ToBool(ctx, val);
fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0);
break;
default:
b = JS_ToBool(ctx, val);
fe->status = (fe->status & ~magic) & ((-b) & magic);
break;
}
return JS_UNDEFINED;
}
static JSValue js_float_env_clearStatus(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
if (!fe)
return JS_EXCEPTION;
fe->status = 0;
return JS_UNDEFINED;
}
static const JSCFunctionListEntry js_float_env_funcs[] = {
JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ),
JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ),
JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ),
JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ),
JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ),
JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ),
JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ),
JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ),
JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ),
JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ),
JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ),
JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ),
JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ),
JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ),
};
static const JSCFunctionListEntry js_float_env_proto_funcs[] = {
JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status,
js_float_env_proto_set_status, FE_PREC ),
JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status,
js_float_env_proto_set_status, FE_EXP ),
JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status,
js_float_env_proto_set_status, FE_RNDMODE ),
JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status,
js_float_env_proto_set_status, FE_SUBNORMAL ),
JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status,
js_float_env_proto_set_status, BF_ST_INVALID_OP ),
JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status,
js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ),
JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status,
js_float_env_proto_set_status, BF_ST_OVERFLOW ),
JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status,
js_float_env_proto_set_status, BF_ST_UNDERFLOW ),
JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status,
js_float_env_proto_set_status, BF_ST_INEXACT ),
JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ),
};
static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val)
{
if (JS_IsBigFloat(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_FLOAT) {
if (JS_IsBigFloat(p->u.object_data))
return JS_DupValue(ctx, p->u.object_data);
}
}
return JS_ThrowTypeError(ctx, "not a bigfloat");
}
static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValue val;
int base;
JSValue ret;
val = js_thisBigFloatValue(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_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);
JS_FreeValue(ctx, val);
return ret;
fail:
JS_FreeValue(ctx, val);
return JS_EXCEPTION;
}
static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return js_thisBigFloatValue(ctx, this_val);
}
static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValue val, ret;
int64_t f;
int rnd_mode, radix;
val = js_thisBigFloatValue(ctx, this_val);
if (JS_IsException(val))
return val;
if (JS_ToInt64Sat(ctx, &f, argv[0]))
goto fail;
if (f < 0 || f > BF_PREC_MAX) {
JS_ThrowRangeError(ctx, "invalid number of digits");
goto fail;
}
rnd_mode = BF_RNDNA;
radix = 10;
/* XXX: swap parameter order for rounding mode and radix */
if (argc > 1) {
rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
if (rnd_mode < 0)
goto fail;
}
if (argc > 2) {
radix = js_get_radix(ctx, argv[2]);
if (radix < 0)
goto fail;
}
ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
JS_FreeValue(ctx, val);
return ret;
fail:
JS_FreeValue(ctx, val);
return JS_EXCEPTION;
}
static BOOL js_bigfloat_is_finite(JSContext *ctx, JSValueConst val)
{
BOOL res;
uint32_t tag;
tag = JS_VALUE_GET_NORM_TAG(val);
switch(tag) {
case JS_TAG_BIG_FLOAT:
{
JSBigFloat *p = JS_VALUE_GET_PTR(val);
res = bf_is_finite(&p->num);
}
break;
default:
res = FALSE;
break;
}
return res;
}
static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValue val, ret;
int64_t f;
int rnd_mode, radix;
val = js_thisBigFloatValue(ctx, this_val);
if (JS_IsException(val))
return val;
if (JS_ToInt64Sat(ctx, &f, argv[0]))
goto fail;
if (!js_bigfloat_is_finite(ctx, val)) {
ret = JS_ToString(ctx, val);
} else if (JS_IsUndefined(argv[0])) {
ret = js_ftoa(ctx, val, 10, 0,
BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
} else {
if (f < 0 || f > BF_PREC_MAX) {
JS_ThrowRangeError(ctx, "invalid number of digits");
goto fail;
}
rnd_mode = BF_RNDNA;
radix = 10;
if (argc > 1) {
rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
if (rnd_mode < 0)
goto fail;
}
if (argc > 2) {
radix = js_get_radix(ctx, argv[2]);
if (radix < 0)
goto fail;
}
ret = js_ftoa(ctx, val, radix, f + 1,
rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
}
JS_FreeValue(ctx, val);
return ret;
fail:
JS_FreeValue(ctx, val);
return JS_EXCEPTION;
}
static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValue val, ret;
int64_t p;
int rnd_mode, radix;
val = js_thisBigFloatValue(ctx, this_val);
if (JS_IsException(val))
return val;
if (JS_IsUndefined(argv[0]))
goto to_string;
if (JS_ToInt64Sat(ctx, &p, argv[0]))
goto fail;
if (!js_bigfloat_is_finite(ctx, val)) {
to_string:
ret = JS_ToString(ctx, this_val);
} else {
if (p < 1 || p > BF_PREC_MAX) {
JS_ThrowRangeError(ctx, "invalid number of digits");
goto fail;
}
rnd_mode = BF_RNDNA;
radix = 10;
if (argc > 1) {
rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
if (rnd_mode < 0)
goto fail;
}
if (argc > 2) {
radix = js_get_radix(ctx, argv[2]);
if (radix < 0)
goto fail;
}
ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED);
}
JS_FreeValue(ctx, val);
return ret;
fail:
JS_FreeValue(ctx, val);
return JS_EXCEPTION;
}
static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = {
JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ),
JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ),
JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ),
JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ),
JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ),
};
static JSValue js_bigfloat_constructor(JSContext *ctx,
JSValueConst new_target,
int argc, JSValueConst *argv)
{
JSValue val;
if (!JS_IsUndefined(new_target))
return JS_ThrowTypeError(ctx, "not a constructor");
if (argc == 0) {
bf_t *r;
val = JS_NewBigFloat(ctx);
if (JS_IsException(val))
return val;
r = JS_GetBigFloat(val);
bf_set_zero(r, 0);
} else {
val = JS_DupValue(ctx, argv[0]);
redo:
switch(JS_VALUE_GET_NORM_TAG(val)) {
case JS_TAG_BIG_FLOAT:
break;
case JS_TAG_FLOAT64:
{
bf_t *r;
double d = JS_VALUE_GET_FLOAT64(val);
val = JS_NewBigFloat(ctx);
if (JS_IsException(val))
break;
r = JS_GetBigFloat(val);
if (bf_set_float64(r, d))
goto fail;
}
break;
case JS_TAG_INT:
{
bf_t *r;
int32_t v = JS_VALUE_GET_INT(val);
val = JS_NewBigFloat(ctx);
if (JS_IsException(val))
break;
r = JS_GetBigFloat(val);
if (bf_set_si(r, v))
goto fail;
}
break;
case JS_TAG_BIG_INT:
/* We keep the full precision of the integer */
{
JSBigFloat *p = JS_VALUE_GET_PTR(val);
val = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
}
break;
case JS_TAG_BIG_DECIMAL:
val = JS_ToStringFree(ctx, val);
if (JS_IsException(val))
break;
goto redo;
case JS_TAG_STRING:
{
const char *str, *p;
size_t len;
int err;
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) {
bf_t *r;
val = JS_NewBigFloat(ctx);
if (JS_IsException(val))
break;
r = JS_GetBigFloat(val);
bf_set_zero(r, 0);
err = 0;
} else {
val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT |
ATOD_TYPE_BIG_FLOAT |
ATOD_ACCEPT_PREFIX_AFTER_SIGN);
if (JS_IsException(val)) {
JS_FreeCString(ctx, str);
return JS_EXCEPTION;
}
p += skip_spaces(p);
err = ((p - str) != len);
}
JS_FreeCString(ctx, str);
if (err) {
JS_FreeValue(ctx, val);
return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal");
}
}
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 bigfloat");
}
}
return val;
fail:
JS_FreeValue(ctx, val);
return JS_EXCEPTION;
}
static JSValue js_bigfloat_get_const(JSContext *ctx,
JSValueConst this_val, int magic)
{
bf_t *r;
JSValue val;
val = JS_NewBigFloat(ctx);
if (JS_IsException(val))
return val;
r = JS_GetBigFloat(val);
switch(magic) {
case 0: /* PI */
bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags);
break;
case 1: /* LN2 */
bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags);
break;
case 2: /* MIN_VALUE */
case 3: /* MAX_VALUE */
{
slimb_t e_range, e;
e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1);
bf_set_ui(r, 1);
if (magic == 2) {
e = -e_range + 2;
if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL)
e -= ctx->fp_env.prec - 1;
bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags);
} else {
bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec,
ctx->fp_env.flags);
bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags);
bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec,
ctx->fp_env.flags);
}
}
break;
case 4: /* EPSILON */
bf_set_ui(r, 1);
bf_mul_2exp(r, 1 - ctx->fp_env.prec,
ctx->fp_env.prec, ctx->fp_env.flags);
break;
default:
abort();
}
return val;
}
static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
bf_t *a;
const char *str;
JSValue ret;
int radix;
JSFloatEnv *fe;
str = JS_ToCString(ctx, argv[0]);
if (!str)
return JS_EXCEPTION;
if (JS_ToInt32(ctx, &radix, argv[1])) {
fail:
JS_FreeCString(ctx, str);
return JS_EXCEPTION;
}
if (radix != 0 && (radix < 2 || radix > 36)) {
JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
goto fail;
}
fe = &ctx->fp_env;
if (argc > 2) {
fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
if (!fe)
goto fail;
}
ret = JS_NewBigFloat(ctx);
if (JS_IsException(ret))
goto done;
a = JS_GetBigFloat(ret);
/* XXX: use js_atof() */
bf_atof(a, str, NULL, radix, fe->prec, fe->flags);
done:
JS_FreeCString(ctx, str);
return ret;
}
static JSValue js_bigfloat_isFinite(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValueConst val = argv[0];
JSBigFloat *p;
if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)
return JS_FALSE;
p = JS_VALUE_GET_PTR(val);
return JS_NewBool(ctx, bf_is_finite(&p->num));
}
static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValueConst val = argv[0];
JSBigFloat *p;
if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)
return JS_FALSE;
p = JS_VALUE_GET_PTR(val);
return JS_NewBool(ctx, bf_is_nan(&p->num));
}
static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
bf_t a_s, *a, *r;
JSFloatEnv *fe;
int rnd_mode;
JSValue op1, res;
op1 = JS_ToNumeric(ctx, argv[0]);
if (JS_IsException(op1))
return op1;
a = JS_ToBigFloat(ctx, &a_s, op1);
fe = &ctx->fp_env;
if (argc > 1) {
fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV);
if (!fe)
goto fail;
}
res = JS_NewBigFloat(ctx);
if (JS_IsException(res)) {
fail:
if (a == &a_s)
bf_delete(a);
JS_FreeValue(ctx, op1);
return JS_EXCEPTION;
}
r = JS_GetBigFloat(res);
switch (magic) {
case MATH_OP_ABS:
bf_set(r, a);
r->sign = 0;
break;
case MATH_OP_FLOOR:
rnd_mode = BF_RNDD;
goto rint;
case MATH_OP_CEIL:
rnd_mode = BF_RNDU;
goto rint;
case MATH_OP_ROUND:
rnd_mode = BF_RNDNA;
goto rint;
case MATH_OP_TRUNC:
rnd_mode = BF_RNDZ;
rint:
bf_set(r, a);
fe->status |= bf_rint(r, rnd_mode);
break;
case MATH_OP_SQRT:
fe->status |= bf_sqrt(r, a, fe->prec, fe->flags);
break;
case MATH_OP_FPROUND:
bf_set(r, a);
fe->status |= bf_round(r, fe->prec, fe->flags);
break;
case MATH_OP_ACOS:
fe->status |= bf_acos(r, a, fe->prec, fe->flags);
break;
case MATH_OP_ASIN:
fe->status |= bf_asin(r, a, fe->prec, fe->flags);
break;
case MATH_OP_ATAN:
fe->status |= bf_atan(r, a, fe->prec, fe->flags);
break;
case MATH_OP_COS:
fe->status |= bf_cos(r, a, fe->prec, fe->flags);
break;
case MATH_OP_EXP:
fe->status |= bf_exp(r, a, fe->prec, fe->flags);
break;
case MATH_OP_LOG:
fe->status |= bf_log(r, a, fe->prec, fe->flags);
break;
case MATH_OP_SIN:
fe->status |= bf_sin(r, a, fe->prec, fe->flags);
break;
case MATH_OP_TAN:
fe->status |= bf_tan(r, a, fe->prec, fe->flags);
break;
case MATH_OP_SIGN:
if (bf_is_nan(a) || bf_is_zero(a)) {
bf_set(r, a);
} else {
bf_set_si(r, 1 - 2 * a->sign);
}
break;
default:
abort();
}
if (a == &a_s)
bf_delete(a);
JS_FreeValue(ctx, op1);
return res;
}
static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
bf_t a_s, *a, b_s, *b, r_s, *r = &r_s;
JSFloatEnv *fe;
JSValue op1, op2, res;
op1 = JS_ToNumeric(ctx, argv[0]);
if (JS_IsException(op1))
return op1;
op2 = JS_ToNumeric(ctx, argv[1]);
if (JS_IsException(op2)) {
JS_FreeValue(ctx, op1);
return op2;
}
a = JS_ToBigFloat(ctx, &a_s, op1);
b = JS_ToBigFloat(ctx, &b_s, op2);
fe = &ctx->fp_env;
if (argc > 2) {
fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
if (!fe)
goto fail;
}
res = JS_NewBigFloat(ctx);
if (JS_IsException(res)) {
fail:
if (a == &a_s)
bf_delete(a);
if (b == &b_s)
bf_delete(b);
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
return JS_EXCEPTION;
}
r = JS_GetBigFloat(res);
switch (magic) {
case MATH_OP_ATAN2:
fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags);
break;
case MATH_OP_POW:
fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS);
break;
case MATH_OP_FMOD:
fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
break;
case MATH_OP_REM:
fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN);
break;
case MATH_OP_ADD:
fe->status |= bf_add(r, a, b, fe->prec, fe->flags);
break;
case MATH_OP_SUB:
fe->status |= bf_sub(r, a, b, fe->prec, fe->flags);
break;
case MATH_OP_MUL:
fe->status |= bf_mul(r, a, b, fe->prec, fe->flags);
break;
case MATH_OP_DIV:
fe->status |= bf_div(r, a, b, fe->prec, fe->flags);
break;
default:
abort();
}
if (a == &a_s)
bf_delete(a);
if (b == &b_s)
bf_delete(b);
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
return res;
}
static const JSCFunctionListEntry js_bigfloat_funcs[] = {
JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ),
JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ),
JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ),
JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ),
JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ),
JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ),
JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ),
JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ),
JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ),
JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ),
JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ),
JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ),
JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ),
JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ),
JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ),
JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ),
JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ),
JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ),
JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ),
JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ),
JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ),
JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ),
JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ),
JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ),
JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ),
JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ),
JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ),
JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ),
JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ),
JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ),
JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ),
JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ),
};
static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf,
int radix, int flags, slimb_t *pexponent)
{
bf_t *a;
int ret;
JSValue val;
val = JS_NewBigFloat(ctx);
if (JS_IsException(val))
return val;
a = JS_GetBigFloat(val);
if (flags & ATOD_ACCEPT_SUFFIX) {
/* return the exponent to get infinite precision */
ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF,
BF_RNDZ | BF_ATOF_EXPONENT);
} else {
ret = bf_atof(a, buf, NULL, radix, ctx->fp_env.prec,
ctx->fp_env.flags);
}
if (ret & BF_ST_MEM_ERROR) {
JS_FreeValue(ctx, val);
return JS_ThrowOutOfMemory(ctx);
}
return val;
}
static int js_unary_arith_bigfloat(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, "bigfloat argument with unary +");
JS_FreeValue(ctx, op1);
return -1;
}
res = JS_NewBigFloat(ctx);
if (JS_IsException(res)) {
JS_FreeValue(ctx, op1);
return -1;
}
r = JS_GetBigFloat(res);
a = JS_ToBigFloat(ctx, &a_s, op1);
ret = 0;
switch(op) {
case OP_inc:
case OP_dec:
v = 2 * (op - OP_dec) - 1;
ret = bf_add_si(r, a, v, ctx->fp_env.prec, ctx->fp_env.flags);
break;
case OP_plus:
ret = bf_set(r, a);
break;
case OP_neg:
ret = bf_set(r, a);
bf_neg(r);
break;
default:
abort();
}
if (a == &a_s)
bf_delete(a);
JS_FreeValue(ctx, op1);
if (UNLIKELY(ret & BF_ST_MEM_ERROR)) {
JS_FreeValue(ctx, res);
throw_bf_exception(ctx, ret);
return -1;
}
*pres = res;
return 0;
}
static int js_binary_arith_bigfloat(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_NewBigFloat(ctx);
if (JS_IsException(res)) {
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
return -1;
}
r = JS_GetBigFloat(res);
a = JS_ToBigFloat(ctx, &a_s, op1);
b = JS_ToBigFloat(ctx, &b_s, op2);
bf_init(ctx->bf_ctx, r);
switch(op) {
case OP_add:
ret = bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
break;
case OP_sub:
ret = bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
break;
case OP_mul:
ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
break;
case OP_div:
ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
break;
case OP_math_mod:
/* Euclidian remainder */
ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
BF_DIVREM_EUCLIDIAN);
break;
case OP_mod:
ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
BF_RNDZ);
break;
case OP_pow:
ret = bf_pow(r, a, b, ctx->fp_env.prec,
ctx->fp_env.flags | BF_POW_JS_QUIRKS);
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);
if (UNLIKELY(ret & BF_ST_MEM_ERROR)) {
JS_FreeValue(ctx, res);
throw_bf_exception(ctx, ret);
return -1;
}
*pres = res;
return 0;
}
/* Note: also used for bigint */
int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op, JSValue op1, JSValue op2)
{
bf_t a_s, b_s, *a, *b;
int res;
a = JS_ToBigFloat(ctx, &a_s, op1);
if (!a) {
JS_FreeValue(ctx, op2);
return -1;
}
b = JS_ToBigFloat(ctx, &b_s, op2);
if (!b) {
if (a == &a_s)
bf_delete(a);
JS_FreeValue(ctx, op1);
return -1;
}
switch(op) {
case OP_lt:
res = bf_cmp_lt(a, b); /* if NaN return false */
break;
case OP_lte:
res = bf_cmp_le(a, b); /* if NaN return false */
break;
case OP_gt:
res = bf_cmp_lt(b, a); /* if NaN return false */
break;
case OP_gte:
res = bf_cmp_le(b, a); /* if NaN return false */
break;
case OP_eq:
res = bf_cmp_eq(a, b); /* if NaN return false */
break;
default:
abort();
}
if (a == &a_s)
bf_delete(a);
if (b == &b_s)
bf_delete(b);
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
return res;
}
static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
int64_t exponent)
{
bf_t r_s, *r = &r_s;
double d;
int ret;
/* always convert to Float64 */
bf_init(ctx->bf_ctx, r);
ret = bf_mul_pow_radix(r, a, 10, exponent,
53, bf_set_exp_bits(11) | BF_RNDN |
BF_FLAG_SUBNORMAL);
bf_get_float64(r, &d, BF_RNDN);
bf_delete(r);
if (ret & BF_ST_MEM_ERROR)
return JS_ThrowOutOfMemory(ctx);
else
return __JS_NewFloat64(ctx, d);
}
static int js_mul_pow10(JSContext *ctx, JSValue *sp)
{
bf_t a_s, *a, *r;
JSValue op1, op2, res;
int64_t e;
int ret;
res = JS_NewBigFloat(ctx);
if (JS_IsException(res))
return -1;
r = JS_GetBigFloat(res);
op1 = sp[-2];
op2 = sp[-1];
a = JS_ToBigFloat(ctx, &a_s, op1);
if (!a)
return -1;
if (JS_IsBigInt(ctx, op2)) {
ret = JS_ToBigInt64(ctx, &e, op2);
} else {
ret = JS_ToInt64(ctx, &e, op2);
}
if (ret) {
if (a == &a_s)
bf_delete(a);
JS_FreeValue(ctx, res);
return -1;
}
bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags);
if (a == &a_s)
bf_delete(a);
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
sp[-2] = res;
return 0;
}
void JS_AddIntrinsicBigFloat(JSContext *ctx)
{
JSRuntime *rt = ctx->rt;
JSValueConst obj1;
rt->bigfloat_ops.to_string = js_bigfloat_to_string;
rt->bigfloat_ops.from_string = js_string_to_bigfloat;
rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat;
rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat;
rt->bigfloat_ops.compare = js_compare_bigfloat;
rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64;
rt->bigfloat_ops.mul_pow10 = js_mul_pow10;
ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT],
js_bigfloat_proto_funcs,
countof(js_bigfloat_proto_funcs));
obj1 = JS_NewGlobalCConstructor(ctx, "BigFloat", js_bigfloat_constructor, 1,
ctx->class_proto[JS_CLASS_BIG_FLOAT]);
JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs,
countof(js_bigfloat_funcs));
ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV],
js_float_env_proto_funcs,
countof(js_float_env_proto_funcs));
obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv",
js_float_env_constructor, 1,
ctx->class_proto[JS_CLASS_FLOAT_ENV]);
JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs,
countof(js_float_env_funcs));
}