mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-12 09:17:53 +00:00
- 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
725 lines
21 KiB
C
725 lines
21 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 "libc/str/str.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_NewBigDecimal(JSContext *ctx)
|
|
{
|
|
JSBigDecimal *p;
|
|
p = js_malloc(ctx, sizeof(*p));
|
|
if (!p)
|
|
return JS_EXCEPTION;
|
|
p->header.ref_count = 1;
|
|
bfdec_init(ctx->bf_ctx, &p->num);
|
|
return JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
|
|
}
|
|
|
|
/* return NULL if invalid type */
|
|
bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val)
|
|
{
|
|
uint32_t tag;
|
|
JSBigDecimal *p;
|
|
bfdec_t *r;
|
|
tag = JS_VALUE_GET_NORM_TAG(val);
|
|
switch(tag) {
|
|
case JS_TAG_BIG_DECIMAL:
|
|
p = JS_VALUE_GET_PTR(val);
|
|
r = &p->num;
|
|
break;
|
|
default:
|
|
JS_ThrowTypeError(ctx, "bigdecimal expected");
|
|
r = NULL;
|
|
break;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
|
|
BOOL allow_null_or_undefined)
|
|
{
|
|
redo:
|
|
switch(JS_VALUE_GET_NORM_TAG(val)) {
|
|
case JS_TAG_BIG_DECIMAL:
|
|
break;
|
|
case JS_TAG_NULL:
|
|
if (!allow_null_or_undefined)
|
|
goto fail;
|
|
/* fall thru */
|
|
case JS_TAG_BOOL:
|
|
case JS_TAG_INT:
|
|
{
|
|
bfdec_t *r;
|
|
int32_t v = JS_VALUE_GET_INT(val);
|
|
val = JS_NewBigDecimal(ctx);
|
|
if (JS_IsException(val))
|
|
break;
|
|
r = JS_GetBigDecimal(val);
|
|
if (bfdec_set_si(r, v)) {
|
|
JS_FreeValue(ctx, val);
|
|
val = JS_EXCEPTION;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case JS_TAG_FLOAT64:
|
|
case JS_TAG_BIG_INT:
|
|
case JS_TAG_BIG_FLOAT:
|
|
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) {
|
|
bfdec_t *r;
|
|
val = JS_NewBigDecimal(ctx);
|
|
if (JS_IsException(val))
|
|
break;
|
|
r = JS_GetBigDecimal(val);
|
|
bfdec_set_zero(r, 0);
|
|
err = 0;
|
|
} else {
|
|
val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL);
|
|
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 bigdecimal literal");
|
|
}
|
|
}
|
|
break;
|
|
case JS_TAG_OBJECT:
|
|
val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
|
|
if (JS_IsException(val))
|
|
break;
|
|
goto redo;
|
|
case JS_TAG_UNDEFINED:
|
|
{
|
|
bfdec_t *r;
|
|
if (!allow_null_or_undefined)
|
|
goto fail;
|
|
val = JS_NewBigDecimal(ctx);
|
|
if (JS_IsException(val))
|
|
break;
|
|
r = JS_GetBigDecimal(val);
|
|
bfdec_set_nan(r);
|
|
}
|
|
break;
|
|
default:
|
|
fail:
|
|
JS_FreeValue(ctx, val);
|
|
return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal");
|
|
}
|
|
return val;
|
|
}
|
|
|
|
static JSValue js_bigdecimal_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) {
|
|
bfdec_t *r;
|
|
val = JS_NewBigDecimal(ctx);
|
|
if (JS_IsException(val))
|
|
return val;
|
|
r = JS_GetBigDecimal(val);
|
|
bfdec_set_zero(r, 0);
|
|
} else {
|
|
val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val)
|
|
{
|
|
if (JS_IsBigDecimal(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_DECIMAL) {
|
|
if (JS_IsBigDecimal(p->u.object_data))
|
|
return JS_DupValue(ctx, p->u.object_data);
|
|
}
|
|
}
|
|
return JS_ThrowTypeError(ctx, "not a bigdecimal");
|
|
}
|
|
|
|
static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue val;
|
|
|
|
val = js_thisBigDecimalValue(ctx, this_val);
|
|
if (JS_IsException(val))
|
|
return val;
|
|
return JS_ToStringFree(ctx, val);
|
|
}
|
|
|
|
static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
return js_thisBigDecimalValue(ctx, this_val);
|
|
}
|
|
|
|
static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj)
|
|
{
|
|
const char *str;
|
|
size_t size;
|
|
int rnd_mode;
|
|
|
|
str = JS_ToCStringLen(ctx, &size, obj);
|
|
if (!str)
|
|
return -1;
|
|
if (strlen(str) != size)
|
|
goto invalid_rounding_mode;
|
|
if (!strcmp(str, "floor")) {
|
|
rnd_mode = BF_RNDD;
|
|
} else if (!strcmp(str, "ceiling")) {
|
|
rnd_mode = BF_RNDU;
|
|
} else if (!strcmp(str, "down")) {
|
|
rnd_mode = BF_RNDZ;
|
|
} else if (!strcmp(str, "up")) {
|
|
rnd_mode = BF_RNDA;
|
|
} else if (!strcmp(str, "half-even")) {
|
|
rnd_mode = BF_RNDN;
|
|
} else if (!strcmp(str, "half-up")) {
|
|
rnd_mode = BF_RNDNA;
|
|
} else {
|
|
invalid_rounding_mode:
|
|
JS_FreeCString(ctx, str);
|
|
JS_ThrowTypeError(ctx, "invalid rounding mode");
|
|
return -1;
|
|
}
|
|
JS_FreeCString(ctx, str);
|
|
return rnd_mode;
|
|
}
|
|
|
|
typedef struct {
|
|
int64_t prec;
|
|
bf_flags_t flags;
|
|
} BigDecimalEnv;
|
|
|
|
static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe,
|
|
JSValueConst obj)
|
|
{
|
|
JSValue prop;
|
|
int64_t val;
|
|
BOOL has_prec;
|
|
int rnd_mode;
|
|
if (!JS_IsObject(obj)) {
|
|
JS_ThrowTypeErrorNotAnObject(ctx);
|
|
return -1;
|
|
}
|
|
prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode);
|
|
if (JS_IsException(prop))
|
|
return -1;
|
|
rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop);
|
|
JS_FreeValue(ctx, prop);
|
|
if (rnd_mode < 0)
|
|
return -1;
|
|
fe->flags = rnd_mode;
|
|
prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits);
|
|
if (JS_IsException(prop))
|
|
return -1;
|
|
has_prec = FALSE;
|
|
if (!JS_IsUndefined(prop)) {
|
|
if (JS_ToInt64SatFree(ctx, &val, prop))
|
|
return -1;
|
|
if (val < 1 || val > BF_PREC_MAX)
|
|
goto invalid_precision;
|
|
fe->prec = val;
|
|
has_prec = TRUE;
|
|
}
|
|
prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumFractionDigits);
|
|
if (JS_IsException(prop))
|
|
return -1;
|
|
if (!JS_IsUndefined(prop)) {
|
|
if (has_prec) {
|
|
JS_FreeValue(ctx, prop);
|
|
JS_ThrowTypeError(ctx, "cannot provide both maximumSignificantDigits and maximumFractionDigits");
|
|
return -1;
|
|
}
|
|
if (JS_ToInt64SatFree(ctx, &val, prop))
|
|
return -1;
|
|
if (val < 0 || val > BF_PREC_MAX) {
|
|
invalid_precision:
|
|
JS_ThrowTypeError(ctx, "invalid precision");
|
|
return -1;
|
|
}
|
|
fe->prec = val;
|
|
fe->flags |= BF_FLAG_RADPNT_PREC;
|
|
has_prec = TRUE;
|
|
}
|
|
if (!has_prec) {
|
|
JS_ThrowTypeError(ctx, "precision must be present");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int magic)
|
|
{
|
|
bfdec_t *a, *b, r_s, *r = &r_s;
|
|
JSValue op1, op2, res;
|
|
BigDecimalEnv fe_s, *fe = &fe_s;
|
|
int op_count, ret;
|
|
if (magic == MATH_OP_SQRT ||
|
|
magic == MATH_OP_ROUND)
|
|
op_count = 1;
|
|
else
|
|
op_count = 2;
|
|
op1 = JS_ToNumeric(ctx, argv[0]);
|
|
if (JS_IsException(op1))
|
|
return op1;
|
|
a = JS_ToBigDecimal(ctx, op1);
|
|
if (!a) {
|
|
JS_FreeValue(ctx, op1);
|
|
return JS_EXCEPTION;
|
|
}
|
|
if (op_count >= 2) {
|
|
op2 = JS_ToNumeric(ctx, argv[1]);
|
|
if (JS_IsException(op2)) {
|
|
JS_FreeValue(ctx, op1);
|
|
return op2;
|
|
}
|
|
b = JS_ToBigDecimal(ctx, op2);
|
|
if (!b)
|
|
goto fail;
|
|
} else {
|
|
op2 = JS_UNDEFINED;
|
|
b = NULL;
|
|
}
|
|
fe->flags = BF_RNDZ;
|
|
fe->prec = BF_PREC_INF;
|
|
if (op_count < argc) {
|
|
if (js_bigdecimal_get_env(ctx, fe, argv[op_count]))
|
|
goto fail;
|
|
}
|
|
res = JS_NewBigDecimal(ctx);
|
|
if (JS_IsException(res)) {
|
|
fail:
|
|
JS_FreeValue(ctx, op1);
|
|
JS_FreeValue(ctx, op2);
|
|
return JS_EXCEPTION;
|
|
}
|
|
r = JS_GetBigDecimal(res);
|
|
switch (magic) {
|
|
case MATH_OP_ADD:
|
|
ret = bfdec_add(r, a, b, fe->prec, fe->flags);
|
|
break;
|
|
case MATH_OP_SUB:
|
|
ret = bfdec_sub(r, a, b, fe->prec, fe->flags);
|
|
break;
|
|
case MATH_OP_MUL:
|
|
ret = bfdec_mul(r, a, b, fe->prec, fe->flags);
|
|
break;
|
|
case MATH_OP_DIV:
|
|
ret = bfdec_div(r, a, b, fe->prec, fe->flags);
|
|
break;
|
|
case MATH_OP_FMOD:
|
|
ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
|
|
break;
|
|
case MATH_OP_SQRT:
|
|
ret = bfdec_sqrt(r, a, fe->prec, fe->flags);
|
|
break;
|
|
case MATH_OP_ROUND:
|
|
ret = bfdec_set(r, a);
|
|
if (!(ret & BF_ST_MEM_ERROR))
|
|
ret = bfdec_round(r, fe->prec, fe->flags);
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
JS_FreeValue(ctx, op1);
|
|
JS_FreeValue(ctx, op2);
|
|
ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP |
|
|
BF_ST_OVERFLOW;
|
|
if (ret != 0) {
|
|
JS_FreeValue(ctx, res);
|
|
return throw_bf_exception(ctx, ret);
|
|
} else {
|
|
return res;
|
|
}
|
|
}
|
|
|
|
static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue val, ret;
|
|
int64_t f;
|
|
int rnd_mode;
|
|
val = js_thisBigDecimalValue(ctx, this_val);
|
|
if (JS_IsException(val))
|
|
return val;
|
|
if (JS_ToInt64Sat(ctx, &f, argv[0]))
|
|
goto fail;
|
|
if (f < 0 || f > BF_PREC_MAX) {
|
|
JS_ThrowRangeError(ctx, "invalid number of digits");
|
|
goto fail;
|
|
}
|
|
rnd_mode = BF_RNDNA;
|
|
if (argc > 1) {
|
|
rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
|
|
if (rnd_mode < 0)
|
|
goto fail;
|
|
}
|
|
ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
|
|
JS_FreeValue(ctx, val);
|
|
return ret;
|
|
fail:
|
|
JS_FreeValue(ctx, val);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue val, ret;
|
|
int64_t f;
|
|
int rnd_mode;
|
|
val = js_thisBigDecimalValue(ctx, this_val);
|
|
if (JS_IsException(val))
|
|
return val;
|
|
if (JS_ToInt64Sat(ctx, &f, argv[0]))
|
|
goto fail;
|
|
if (JS_IsUndefined(argv[0])) {
|
|
ret = js_bigdecimal_to_string1(ctx, val, 0,
|
|
BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
|
|
} else {
|
|
if (f < 0 || f > BF_PREC_MAX) {
|
|
JS_ThrowRangeError(ctx, "invalid number of digits");
|
|
goto fail;
|
|
}
|
|
rnd_mode = BF_RNDNA;
|
|
if (argc > 1) {
|
|
rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
|
|
if (rnd_mode < 0)
|
|
goto fail;
|
|
}
|
|
ret = js_bigdecimal_to_string1(ctx, val, f + 1,
|
|
rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
|
|
}
|
|
JS_FreeValue(ctx, val);
|
|
return ret;
|
|
fail:
|
|
JS_FreeValue(ctx, val);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue val, ret;
|
|
int64_t p;
|
|
int rnd_mode;
|
|
val = js_thisBigDecimalValue(ctx, this_val);
|
|
if (JS_IsException(val))
|
|
return val;
|
|
if (JS_IsUndefined(argv[0])) {
|
|
return JS_ToStringFree(ctx, val);
|
|
}
|
|
if (JS_ToInt64Sat(ctx, &p, argv[0]))
|
|
goto fail;
|
|
if (p < 1 || p > BF_PREC_MAX) {
|
|
JS_ThrowRangeError(ctx, "invalid number of digits");
|
|
goto fail;
|
|
}
|
|
rnd_mode = BF_RNDNA;
|
|
if (argc > 1) {
|
|
rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
|
|
if (rnd_mode < 0)
|
|
goto fail;
|
|
}
|
|
ret = js_bigdecimal_to_string1(ctx, val, p,
|
|
rnd_mode | BF_FTOA_FORMAT_FIXED);
|
|
JS_FreeValue(ctx, val);
|
|
return ret;
|
|
fail:
|
|
JS_FreeValue(ctx, val);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = {
|
|
JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ),
|
|
JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ),
|
|
JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ),
|
|
JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ),
|
|
JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ),
|
|
};
|
|
|
|
static const JSCFunctionListEntry js_bigdecimal_funcs[] = {
|
|
JS_CFUNC_MAGIC_DEF("add", 2, js_bigdecimal_fop, MATH_OP_ADD ),
|
|
JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ),
|
|
JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ),
|
|
JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ),
|
|
JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ),
|
|
JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ),
|
|
JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ),
|
|
};
|
|
|
|
static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf,
|
|
int radix, int flags, slimb_t *pexponent)
|
|
{
|
|
bfdec_t *a;
|
|
int ret;
|
|
JSValue val;
|
|
val = JS_NewBigDecimal(ctx);
|
|
if (JS_IsException(val))
|
|
return val;
|
|
a = JS_GetBigDecimal(val);
|
|
ret = bfdec_atof(a, buf, NULL, BF_PREC_INF,
|
|
BF_RNDZ | BF_ATOF_NO_NAN_INF);
|
|
if (ret & BF_ST_MEM_ERROR) {
|
|
JS_FreeValue(ctx, val);
|
|
return JS_ThrowOutOfMemory(ctx);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
static int js_unary_arith_bigdecimal(JSContext *ctx,
|
|
JSValue *pres, OPCodeEnum op, JSValue op1)
|
|
{
|
|
bfdec_t *r, *a;
|
|
int ret, v;
|
|
JSValue res;
|
|
if (op == OP_plus && !is_math_mode(ctx)) {
|
|
JS_ThrowTypeError(ctx, "bigdecimal argument with unary +");
|
|
JS_FreeValue(ctx, op1);
|
|
return -1;
|
|
}
|
|
res = JS_NewBigDecimal(ctx);
|
|
if (JS_IsException(res)) {
|
|
JS_FreeValue(ctx, op1);
|
|
return -1;
|
|
}
|
|
r = JS_GetBigDecimal(res);
|
|
a = JS_ToBigDecimal(ctx, op1);
|
|
ret = 0;
|
|
switch(op) {
|
|
case OP_inc:
|
|
case OP_dec:
|
|
v = 2 * (op - OP_dec) - 1;
|
|
ret = bfdec_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
|
|
break;
|
|
case OP_plus:
|
|
ret = bfdec_set(r, a);
|
|
break;
|
|
case OP_neg:
|
|
ret = bfdec_set(r, a);
|
|
bfdec_neg(r);
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
JS_FreeValue(ctx, op1);
|
|
if (UNLIKELY(ret)) {
|
|
JS_FreeValue(ctx, res);
|
|
throw_bf_exception(ctx, ret);
|
|
return -1;
|
|
}
|
|
*pres = res;
|
|
return 0;
|
|
}
|
|
|
|
/* b must be a positive integer */
|
|
static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b)
|
|
{
|
|
bfdec_t b1;
|
|
int32_t b2;
|
|
int ret;
|
|
bfdec_init(b->ctx, &b1);
|
|
ret = bfdec_set(&b1, b);
|
|
if (ret) {
|
|
bfdec_delete(&b1);
|
|
return ret;
|
|
}
|
|
ret = bfdec_rint(&b1, BF_RNDZ);
|
|
if (ret) {
|
|
bfdec_delete(&b1);
|
|
return BF_ST_INVALID_OP; /* must be an integer */
|
|
}
|
|
ret = bfdec_get_int32(&b2, &b1);
|
|
bfdec_delete(&b1);
|
|
if (ret)
|
|
return ret; /* overflow */
|
|
if (b2 < 0)
|
|
return BF_ST_INVALID_OP; /* must be positive */
|
|
return bfdec_pow_ui(r, a, b2);
|
|
}
|
|
|
|
static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op,
|
|
JSValue op1, JSValue op2)
|
|
{
|
|
bfdec_t *a, *b;
|
|
int res;
|
|
/* Note: binary floats are converted to bigdecimal with
|
|
toString(). It is not mathematically correct but is consistent
|
|
with the BigDecimal() constructor behavior */
|
|
op1 = JS_ToBigDecimalFree(ctx, op1, TRUE);
|
|
if (JS_IsException(op1)) {
|
|
JS_FreeValue(ctx, op2);
|
|
return -1;
|
|
}
|
|
op2 = JS_ToBigDecimalFree(ctx, op2, TRUE);
|
|
if (JS_IsException(op2)) {
|
|
JS_FreeValue(ctx, op1);
|
|
return -1;
|
|
}
|
|
a = JS_ToBigDecimal(ctx, op1);
|
|
b = JS_ToBigDecimal(ctx, op2);
|
|
switch(op) {
|
|
case OP_lt:
|
|
res = bfdec_cmp_lt(a, b); /* if NaN return false */
|
|
break;
|
|
case OP_lte:
|
|
res = bfdec_cmp_le(a, b); /* if NaN return false */
|
|
break;
|
|
case OP_gt:
|
|
res = bfdec_cmp_lt(b, a); /* if NaN return false */
|
|
break;
|
|
case OP_gte:
|
|
res = bfdec_cmp_le(b, a); /* if NaN return false */
|
|
break;
|
|
case OP_eq:
|
|
res = bfdec_cmp_eq(a, b); /* if NaN return false */
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
JS_FreeValue(ctx, op1);
|
|
JS_FreeValue(ctx, op2);
|
|
return res;
|
|
}
|
|
|
|
static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,
|
|
JSValue *pres, JSValue op1, JSValue op2)
|
|
{
|
|
bfdec_t *r, *a, *b;
|
|
int ret;
|
|
JSValue res;
|
|
res = JS_NewBigDecimal(ctx);
|
|
if (JS_IsException(res))
|
|
goto fail;
|
|
r = JS_GetBigDecimal(res);
|
|
a = JS_ToBigDecimal(ctx, op1);
|
|
if (!a)
|
|
goto fail;
|
|
b = JS_ToBigDecimal(ctx, op2);
|
|
if (!b)
|
|
goto fail;
|
|
switch(op) {
|
|
case OP_add:
|
|
ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ);
|
|
break;
|
|
case OP_sub:
|
|
ret = bfdec_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
|
|
break;
|
|
case OP_mul:
|
|
ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
|
|
break;
|
|
case OP_div:
|
|
ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ);
|
|
break;
|
|
case OP_math_mod:
|
|
/* Euclidian remainder */
|
|
ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN);
|
|
break;
|
|
case OP_mod:
|
|
ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ);
|
|
break;
|
|
case OP_pow:
|
|
ret = js_bfdec_pow(r, a, b);
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
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;
|
|
fail:
|
|
JS_FreeValue(ctx, res);
|
|
JS_FreeValue(ctx, op1);
|
|
JS_FreeValue(ctx, op2);
|
|
return -1;
|
|
}
|
|
|
|
void JS_AddIntrinsicBigDecimal(JSContext *ctx)
|
|
{
|
|
JSRuntime *rt = ctx->rt;
|
|
JSValueConst obj1;
|
|
rt->bigdecimal_ops.to_string = js_bigdecimal_to_string;
|
|
rt->bigdecimal_ops.from_string = js_string_to_bigdecimal;
|
|
rt->bigdecimal_ops.unary_arith = js_unary_arith_bigdecimal;
|
|
rt->bigdecimal_ops.binary_arith = js_binary_arith_bigdecimal;
|
|
rt->bigdecimal_ops.compare = js_compare_bigdecimal;
|
|
ctx->class_proto[JS_CLASS_BIG_DECIMAL] = JS_NewObject(ctx);
|
|
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL],
|
|
js_bigdecimal_proto_funcs,
|
|
countof(js_bigdecimal_proto_funcs));
|
|
obj1 = JS_NewGlobalCConstructor(ctx, "BigDecimal",
|
|
js_bigdecimal_constructor, 1,
|
|
ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
|
|
JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs,
|
|
countof(js_bigdecimal_funcs));
|
|
}
|
|
|
|
void JS_EnableBignumExt(JSContext *ctx, BOOL enable)
|
|
{
|
|
ctx->bignum_ext = enable;
|
|
}
|