Drop support for "use math"

Ref: https://github.com/quickjs-ng/quickjs/issues/20
This commit is contained in:
Saúl Ibarra Corretgé 2023-11-06 23:07:31 +01:00 committed by GitHub
parent 9b3b3084fa
commit 1fb9a5010f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 49 additions and 3380 deletions

2
.gitignore vendored
View file

@ -13,8 +13,6 @@ qjs
qjs-debug
qjs32
qjsc
qjscalc
qjscalc.c
repl.c
run-test262
run-test262-32

View file

@ -154,9 +154,6 @@ else
QJSC_CC=$(CC)
QJSC=./qjsc$(EXE)
endif
ifndef CONFIG_WIN32
PROGS+=qjscalc
endif
ifdef CONFIG_M32
PROGS+=qjs32 qjs32_s
endif
@ -186,7 +183,6 @@ QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(
QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS)
ifdef CONFIG_BIGNUM
QJS_LIB_OBJS+=$(OBJDIR)/libbf.o
QJS_OBJS+=$(OBJDIR)/qjscalc.o
endif
HOST_LIBS=-lm -ldl -lpthread
@ -232,9 +228,6 @@ qjs32_s: $(patsubst %.o, %.m32s.o, $(QJS_OBJS))
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS)
@size $@
qjscalc: qjs
ln -sf $< $@
ifdef CONFIG_LTO
LTOEXT=.lto
else
@ -252,9 +245,6 @@ endif # CONFIG_LTO
repl.c: $(QJSC) repl.js
$(QJSC) -c -o $@ -m repl.js
qjscalc.c: $(QJSC) qjscalc.js
$(QJSC) -fbignum -c -o $@ qjscalc.js
ifneq ($(wildcard unicode/UnicodeData.txt),)
$(OBJDIR)/libunicode.o $(OBJDIR)/libunicode.m32.o $(OBJDIR)/libunicode.m32s.o \
$(OBJDIR)/libunicode.nolto.o: libunicode-table.h
@ -305,7 +295,7 @@ unicode_gen: $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o libunicode.c u
$(HOST_CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o
clean:
rm -f repl.c qjscalc.c out.c
rm -f repl.c out.c
rm -f *.a *.o *.d *~ unicode_gen regexp_test $(PROGS)
rm -f hello.c test_fib.c
rm -f examples/*.so tests/*.so
@ -316,7 +306,6 @@ install: all
mkdir -p "$(DESTDIR)$(prefix)/bin"
$(STRIP) qjs qjsc
install -m755 qjs qjsc "$(DESTDIR)$(prefix)/bin"
ln -sf qjs "$(DESTDIR)$(prefix)/bin/qjscalc"
mkdir -p "$(DESTDIR)$(prefix)/lib/quickjs"
install -m644 libquickjs.a "$(DESTDIR)$(prefix)/lib/quickjs"
ifdef CONFIG_LTO
@ -417,7 +406,6 @@ endif
ifdef CONFIG_BIGNUM
./qjs --bignum tests/test_op_overloading.js
./qjs --bignum tests/test_bignum.js
./qjs --qjscalc tests/test_qjscalc.js
endif
ifdef CONFIG_M32
./qjs32 tests/test_closure.js
@ -429,7 +417,6 @@ ifdef CONFIG_M32
ifdef CONFIG_BIGNUM
./qjs32 --bignum tests/test_op_overloading.js
./qjs32 --bignum tests/test_bignum.js
./qjs32 --qjscalc tests/test_qjscalc.js
endif
endif

34
qjs.c
View file

@ -44,8 +44,6 @@
extern const uint8_t qjsc_repl[];
extern const uint32_t qjsc_repl_size;
#ifdef CONFIG_BIGNUM
extern const uint8_t qjsc_qjscalc[];
extern const uint32_t qjsc_qjscalc_size;
static int bignum_ext;
#endif
@ -293,7 +291,6 @@ void help(void)
" --std make 'std' and 'os' available to the loaded script\n"
#ifdef CONFIG_BIGNUM
" --bignum enable the bignum extensions (BigFloat, BigDecimal)\n"
" --qjscalc load the QJSCalc runtime (default if invoked as qjscalc)\n"
#endif
"-T --trace trace memory allocation\n"
"-d --dump dump the memory usage stats\n"
@ -321,23 +318,8 @@ int main(int argc, char **argv)
size_t memory_limit = 0;
char *include_list[32];
int i, include_count = 0;
#ifdef CONFIG_BIGNUM
int load_jscalc;
#endif
size_t stack_size = 0;
#ifdef CONFIG_BIGNUM
/* load jscalc runtime if invoked as 'qjscalc' */
{
const char *p, *exename;
exename = argv[0];
p = strrchr(exename, '/');
if (p)
exename = p + 1;
load_jscalc = !strcmp(exename, "qjscalc");
}
#endif
/* cannot use getopt because we want to pass the command line to
the script */
optind = 1;
@ -420,10 +402,6 @@ int main(int argc, char **argv)
bignum_ext = 1;
continue;
}
if (!strcmp(longopt, "qjscalc")) {
load_jscalc = 1;
continue;
}
#endif
if (opt == 'q' || !strcmp(longopt, "quit")) {
empty_run++;
@ -454,11 +432,6 @@ int main(int argc, char **argv)
}
}
#ifdef CONFIG_BIGNUM
if (load_jscalc)
bignum_ext = 1;
#endif
if (trace_memory) {
js_trace_malloc_init(&trace_data);
rt = JS_NewRuntime2(&trace_mf, &trace_data);
@ -490,11 +463,6 @@ int main(int argc, char **argv)
}
if (!empty_run) {
#ifdef CONFIG_BIGNUM
if (load_jscalc) {
js_std_eval_binary(ctx, qjsc_qjscalc, qjsc_qjscalc_size, 0);
}
#endif
js_std_add_helpers(ctx, argc - optind, argv + optind);
/* make 'std' and 'os' visible to non module code */

2657
qjscalc.js

File diff suppressed because it is too large Load diff

View file

@ -258,7 +258,6 @@ DEF( or, 1, 2, 1, none)
DEF(is_undefined_or_null, 1, 1, 1, none)
#ifdef CONFIG_BIGNUM
DEF( mul_pow10, 1, 2, 1, none)
DEF( math_mod, 1, 2, 1, none)
#endif
/* must be the last non short and non temporary opcode */
DEF( nop, 1, 0, 0, none)

464
quickjs.c
View file

@ -314,7 +314,6 @@ struct JSClass {
#define JS_MODE_STRICT (1 << 0)
#define JS_MODE_STRIP (1 << 1)
#define JS_MODE_MATH (1 << 2)
typedef struct JSStackFrame {
struct JSStackFrame *prev_frame; /* NULL if first stack frame */
@ -325,7 +324,7 @@ typedef struct JSStackFrame {
const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
instruction after the call */
int arg_count;
int js_mode; /* 0 or JS_MODE_MATH for C functions */
int js_mode;
/* only used in generators. Current stack pointer value. NULL if
the function is running. */
JSValue *cur_sp;
@ -1146,8 +1145,7 @@ static inline bf_t *JS_GetBigInt(JSValueConst val)
JSBigFloat *p = JS_VALUE_GET_PTR(val);
return &p->num;
}
static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
BOOL convert_to_safe_integer);
static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val);
static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val);
static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);
static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val);
@ -2378,14 +2376,6 @@ static inline BOOL is_strict_mode(JSContext *ctx)
return (sf && (sf->js_mode & JS_MODE_STRICT));
}
#ifdef CONFIG_BIGNUM
static inline BOOL is_math_mode(JSContext *ctx)
{
JSStackFrame *sf = ctx->rt->current_stack_frame;
return (sf && (sf->js_mode & JS_MODE_MATH));
}
#endif
/* JSAtom support */
#define JS_ATOM_TAG_INT (1U << 31)
@ -10086,9 +10076,6 @@ static double js_strtod(const char *p, int radix, BOOL is_float)
#define ATOD_TYPE_BIG_INT (1 << 7)
#define ATOD_TYPE_BIG_FLOAT (2 << 7)
#define ATOD_TYPE_BIG_DECIMAL (3 << 7)
/* assume bigint mode: floats are parsed as integers if no decimal
point nor exponent */
#define ATOD_MODE_BIGINT (1 << 9)
/* accept -0x1 */
#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10)
@ -10108,7 +10095,7 @@ static JSValue js_string_to_bigint(JSContext *ctx, const char *buf,
JS_FreeValue(ctx, val);
return JS_ThrowOutOfMemory(ctx);
}
val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0);
val = JS_CompactBigInt1(ctx, val);
return val;
}
@ -10324,29 +10311,11 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
} else if (*p == 'm') {
p++;
atod_type = ATOD_TYPE_BIG_DECIMAL;
} else {
if (flags & ATOD_MODE_BIGINT) {
if (!is_float)
atod_type = ATOD_TYPE_BIG_INT;
if (has_legacy_octal)
goto fail;
} else {
if (is_float && radix != 10)
goto fail;
}
}
} else {
if (atod_type == ATOD_TYPE_FLOAT64) {
if (flags & ATOD_MODE_BIGINT) {
if (!is_float)
atod_type = ATOD_TYPE_BIG_INT;
if (has_legacy_octal)
goto fail;
} else {
if (is_float && radix != 10)
goto fail;
}
} else if (is_float && radix != 10) {
goto fail;
}
} else if ((atod_type == ATOD_TYPE_FLOAT64) && is_float && radix != 10) {
goto fail;
}
switch(atod_type) {
@ -12018,30 +11987,22 @@ JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v)
JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
{
if (is_math_mode(ctx) &&
v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
return JS_NewInt64(ctx, v);
} else {
return JS_NewBigInt64_1(ctx, v);
}
return JS_NewBigInt64_1(ctx, v);
}
JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
{
JSValue val;
if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) {
val = JS_NewInt64(ctx, v);
} else {
bf_t *a;
val = JS_NewBigInt(ctx);
if (JS_IsException(val))
return val;
a = JS_GetBigInt(val);
if (bf_set_ui(a, v)) {
JS_FreeValue(ctx, val);
return JS_ThrowOutOfMemory(ctx);
}
bf_t *a;
val = JS_NewBigInt(ctx);
if (JS_IsException(val))
return val;
a = JS_GetBigInt(val);
if (bf_set_ui(a, v)) {
JS_FreeValue(ctx, val);
return JS_ThrowOutOfMemory(ctx);
}
return val;
}
@ -12126,8 +12087,6 @@ static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)
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)) {
@ -12163,43 +12122,18 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)
case JS_TAG_INT:
case JS_TAG_NULL:
case JS_TAG_UNDEFINED:
if (!is_math_mode(ctx))
goto fail;
/* fall tru */
case JS_TAG_FLOAT64:
case JS_TAG_BIG_FLOAT:
goto fail;
case JS_TAG_BOOL:
r = buf;
bf_init(ctx->bf_ctx, r);
bf_set_si(r, JS_VALUE_GET_INT(val));
break;
case JS_TAG_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))
@ -12328,20 +12262,12 @@ static JSValue JS_NewBigInt(JSContext *ctx)
return JS_MKPTR(JS_TAG_BIG_INT, p);
}
static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
BOOL convert_to_safe_integer)
static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val)
{
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) {
bf_t *a = JS_GetBigInt(val);
if (a->expn == BF_EXP_ZERO && a->sign) {
JSBigFloat *p = JS_VALUE_GET_PTR(val);
assert(p->header.ref_count == 1);
a->sign = 0;
@ -12349,13 +12275,12 @@ static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
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
/* Nnormalize the zero representation. Could also be used to convert the bigint
to a short bigint value. The reference count of the value must be
1. Cannot fail */
static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val)
{
return JS_CompactBigInt1(ctx, val, is_math_mode(ctx));
return JS_CompactBigInt1(ctx, val);
}
/* must be kept in sync with JSOverloadableOperatorEnum */
@ -12394,7 +12319,6 @@ static int get_ovop_from_opcode(OPCodeEnum op)
case OP_div:
return JS_OVOP_DIV;
case OP_mod:
case OP_math_mod:
return JS_OVOP_MOD;
case OP_pow:
return JS_OVOP_POW;
@ -12575,63 +12499,6 @@ static __exception int js_call_binary_op_fallback(JSContext *ctx,
return -1;
}
/* 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;
}
/* return -1 if exception, 0 if no operator overloading, 1 if
overloaded operator called */
static __exception int js_call_unary_op_fallback(JSContext *ctx,
@ -12703,7 +12570,7 @@ static int js_unary_arith_bigint(JSContext *ctx,
int ret, v;
JSValue res;
if (op == OP_plus && !is_math_mode(ctx)) {
if (op == OP_plus) {
JS_ThrowTypeError(ctx, "bigint argument with unary +");
JS_FreeValue(ctx, op1);
return -1;
@ -12755,7 +12622,7 @@ static int js_unary_arith_bigfloat(JSContext *ctx,
int ret, v;
JSValue res;
if (op == OP_plus && !is_math_mode(ctx)) {
if (op == OP_plus) {
JS_ThrowTypeError(ctx, "bigfloat argument with unary +");
JS_FreeValue(ctx, op1);
return -1;
@ -12804,7 +12671,7 @@ static int js_unary_arith_bigdecimal(JSContext *ctx,
int ret, v;
JSValue res;
if (op == OP_plus && !is_math_mode(ctx)) {
if (op == OP_plus) {
JS_ThrowTypeError(ctx, "bigdecimal argument with unary +");
JS_FreeValue(ctx, op1);
return -1;
@ -12899,7 +12766,6 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
}
break;
case JS_TAG_BIG_INT:
handle_bigint:
if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1))
goto exception;
break;
@ -12914,10 +12780,7 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
default:
handle_float64:
{
double d;
if (is_math_mode(ctx))
goto handle_bigint;
d = JS_VALUE_GET_FLOAT64(op1);
double d = JS_VALUE_GET_FLOAT64(op1);
switch(op) {
case OP_inc:
case OP_dec:
@ -12979,7 +12842,7 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
op1 = JS_ToNumericFree(ctx, op1);
if (JS_IsException(op1))
goto exception;
if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {
if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {
if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1))
goto exception;
} else {
@ -13024,11 +12887,6 @@ static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op,
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);
@ -13086,72 +12944,20 @@ static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op,
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);
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;
}
ret = BF_ST_INVALID_OP;
} else {
ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS);
}
@ -13268,10 +13074,6 @@ static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,
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;
@ -13358,32 +13160,14 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
break;
case OP_mul:
v = (int64_t)v1 * (int64_t)v2;
if (is_math_mode(ctx) &&
(v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER))
goto handle_bigint;
if (v == 0 && (v1 | v2) < 0) {
sp[-2] = __JS_NewFloat64(ctx, -0.0);
return 0;
}
break;
case OP_div:
if (is_math_mode(ctx))
goto handle_bigint;
sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2);
return 0;
case OP_math_mod:
if (unlikely(v2 == 0)) {
throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO);
goto exception;
}
v = (int64_t)v1 % (int64_t)v2;
if (v < 0) {
if (v2 < 0)
v -= v2;
else
v += v2;
}
break;
case OP_mod:
if (v1 < 0 || v2 <= 0) {
sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2));
@ -13393,13 +13177,8 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
}
break;
case OP_pow:
if (!is_math_mode(ctx)) {
sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));
return 0;
} else {
goto handle_bigint;
}
break;
sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));
return 0;
default:
abort();
}
@ -13411,7 +13190,6 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2))
goto exception;
} else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
handle_bigint:
if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
goto exception;
} else {
@ -13424,8 +13202,6 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
if (JS_ToFloat64Free(ctx, &d2, op2))
goto exception;
handle_float64:
if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))
goto handle_bigint;
switch(op) {
case OP_sub:
dr = d1 - d2;
@ -13439,13 +13215,6 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
case OP_mod:
dr = fmod(d1, d2);
break;
case OP_math_mod:
d2 = fabs(d2);
dr = fmod(d1, d2);
/* XXX: loss of accuracy if dr < 0 */
if (dr < 0)
dr += d2;
break;
case OP_pow:
dr = js_pow(d1, d2);
break;
@ -13552,7 +13321,6 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
if (ctx->rt->bigfloat_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
goto exception;
} else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
handle_bigint:
if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
goto exception;
} else {
@ -13564,8 +13332,6 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
}
if (JS_ToFloat64Free(ctx, &d2, op2))
goto exception;
if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))
goto handle_bigint;
sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
}
return 0;
@ -13618,9 +13384,6 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
goto exception;
}
if (is_math_mode(ctx))
goto bigint_op;
tag1 = JS_VALUE_GET_TAG(op1);
tag2 = JS_VALUE_GET_TAG(op2);
if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
@ -13629,10 +13392,8 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
JS_FreeValue(ctx, op2);
JS_ThrowTypeError(ctx, "both operands must be bigint");
goto exception;
} else {
bigint_op:
if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
goto exception;
} else if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) {
goto exception;
}
} else {
if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {
@ -13832,8 +13593,7 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
goto float64_compare;
} else {
if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) ||
(tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) &&
!is_math_mode(ctx)) {
(tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING))) {
if (tag1 == JS_TAG_STRING) {
op1 = JS_StringToBigInt(ctx, op1);
if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
@ -13993,8 +13753,7 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
} else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) ||
(tag2 == JS_TAG_STRING && tag_is_number(tag1))) {
if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) &&
!is_math_mode(ctx)) {
if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT)) {
if (tag1 == JS_TAG_STRING) {
op1 = JS_StringToBigInt(ctx, op1);
if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
@ -14099,9 +13858,8 @@ static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)
JS_FreeValue(ctx, op1);
goto exception;
}
/* XXX: could forbid >>> in bignum mode */
if (!is_math_mode(ctx) &&
(JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||
if ((JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||
JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) {
JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>");
JS_FreeValue(ctx, op1);
@ -16044,16 +15802,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
rt->current_stack_frame = sf;
ctx = p->u.cfunc.realm; /* change the current realm */
#ifdef CONFIG_BIGNUM
/* we only propagate the bignum mode as some runtime functions
test it */
if (prev_sf)
sf->js_mode = prev_sf->js_mode & JS_MODE_MATH;
else
sf->js_mode = 0;
#else
sf->js_mode = 0;
#endif
sf->cur_func = (JSValue)func_obj;
sf->arg_count = argc;
arg_buf = argv;
@ -18022,11 +17771,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
v2 = JS_VALUE_GET_INT(op2);
r = (int64_t)v1 * v2;
if (unlikely((int)r != r)) {
#ifdef CONFIG_BIGNUM
if (unlikely(sf->js_mode & JS_MODE_MATH) &&
(r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER))
goto binary_arith_slow;
#endif
d = (double)r;
goto mul_fp_res;
}
@ -18038,10 +17782,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
sp[-2] = JS_NewInt32(ctx, r);
sp--;
} else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
#ifdef CONFIG_BIGNUM
if (unlikely(sf->js_mode & JS_MODE_MATH))
goto binary_arith_slow;
#endif
d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2);
mul_fp_res:
sp[-2] = __JS_NewFloat64(ctx, d);
@ -18058,8 +17798,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
op2 = sp[-1];
if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
int v1, v2;
if (unlikely(sf->js_mode & JS_MODE_MATH))
goto binary_arith_slow;
v1 = JS_VALUE_GET_INT(op1);
v2 = JS_VALUE_GET_INT(op2);
sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);
@ -18070,9 +17808,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
}
BREAK;
CASE(OP_mod):
#ifdef CONFIG_BIGNUM
CASE(OP_math_mod):
#endif
{
JSValue op1, op2;
op1 = sp[-2];
@ -18254,29 +17989,10 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
uint32_t v1, v2;
v1 = JS_VALUE_GET_INT(op1);
v2 = JS_VALUE_GET_INT(op2);
#ifdef CONFIG_BIGNUM
{
int64_t r;
if (unlikely(sf->js_mode & JS_MODE_MATH)) {
if (v2 > 0x1f)
goto shl_slow;
r = (int64_t)v1 << v2;
if ((int)r != r)
goto shl_slow;
} else {
v2 &= 0x1f;
}
}
#else
v2 &= 0x1f;
#endif
v2 = JS_VALUE_GET_INT(op2) & 0x1f;
sp[-2] = JS_NewInt32(ctx, v1 << v2);
sp--;
} else {
#ifdef CONFIG_BIGNUM
shl_slow:
#endif
if (js_binary_logic_slow(ctx, sp, opcode))
goto exception;
sp--;
@ -18314,10 +18030,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
v2 = JS_VALUE_GET_INT(op2);
#ifdef CONFIG_BIGNUM
if (unlikely(v2 > 0x1f)) {
if (unlikely(sf->js_mode & JS_MODE_MATH))
goto sar_slow;
else
v2 &= 0x1f;
v2 &= 0x1f;
}
#else
v2 &= 0x1f;
@ -18326,9 +18039,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
(int)JS_VALUE_GET_INT(op1) >> v2);
sp--;
} else {
#ifdef CONFIG_BIGNUM
sar_slow:
#endif
if (js_binary_logic_slow(ctx, sp, opcode))
goto exception;
sp--;
@ -19797,9 +19507,6 @@ enum {
TOK_AND_ASSIGN,
TOK_XOR_ASSIGN,
TOK_OR_ASSIGN,
#ifdef CONFIG_BIGNUM
TOK_MATH_POW_ASSIGN,
#endif
TOK_POW_ASSIGN,
TOK_LAND_ASSIGN,
TOK_LOR_ASSIGN,
@ -19819,9 +19526,6 @@ enum {
TOK_STRICT_NEQ,
TOK_LAND,
TOK_LOR,
#ifdef CONFIG_BIGNUM
TOK_MATH_POW,
#endif
TOK_POW,
TOK_ARROW,
TOK_ELLIPSIS,
@ -20906,11 +20610,6 @@ static __exception int next_token(JSParseState *s)
ATOD_ACCEPT_UNDERSCORES;
#ifdef CONFIG_BIGNUM
flags |= ATOD_ACCEPT_SUFFIX;
if (s->cur_func->js_mode & JS_MODE_MATH) {
flags |= ATOD_MODE_BIGINT;
if (s->cur_func->js_mode & JS_MODE_MATH)
flags |= ATOD_TYPE_BIG_FLOAT;
}
#endif
radix = 0;
#ifdef CONFIG_BIGNUM
@ -21076,33 +20775,6 @@ static __exception int next_token(JSParseState *s)
goto def_token;
}
break;
#ifdef CONFIG_BIGNUM
/* in math mode, '^' is the power operator. '^^' is always the
xor operator and '**' is always the power operator */
case '^':
if (p[1] == '=') {
p += 2;
if (s->cur_func->js_mode & JS_MODE_MATH)
s->token.val = TOK_MATH_POW_ASSIGN;
else
s->token.val = TOK_XOR_ASSIGN;
} else if (p[1] == '^') {
if (p[2] == '=') {
p += 3;
s->token.val = TOK_XOR_ASSIGN;
} else {
p += 2;
s->token.val = '^';
}
} else {
p++;
if (s->cur_func->js_mode & JS_MODE_MATH)
s->token.val = TOK_MATH_POW;
else
s->token.val = '^';
}
break;
#else
case '^':
if (p[1] == '=') {
p += 2;
@ -21111,7 +20783,6 @@ static __exception int next_token(JSParseState *s)
goto def_token;
}
break;
#endif
case '|':
if (p[1] == '=') {
p += 2;
@ -25143,24 +24814,6 @@ static __exception int js_parse_unary(JSParseState *s, int parse_flags)
break;
}
if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) {
#ifdef CONFIG_BIGNUM
if (s->token.val == TOK_POW || s->token.val == TOK_MATH_POW) {
/* Extended exponentiation syntax rules: we extend the ES7
grammar in order to have more intuitive semantics:
-2**2 evaluates to -4. */
if (!(s->cur_func->js_mode & JS_MODE_MATH)) {
if (parse_flags & PF_POW_FORBIDDEN) {
JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");
return -1;
}
}
if (next_token(s))
return -1;
if (js_parse_unary(s, PF_POW_ALLOWED))
return -1;
emit_op(s, OP_pow);
}
#else
if (s->token.val == TOK_POW) {
/* Strict ES7 exponentiation syntax rules: To solve
conficting semantics between different implementations
@ -25177,7 +24830,6 @@ static __exception int js_parse_unary(JSParseState *s, int parse_flags)
return -1;
emit_op(s, OP_pow);
}
#endif
}
return 0;
}
@ -25206,12 +24858,7 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level,
opcode = OP_div;
break;
case '%':
#ifdef CONFIG_BIGNUM
if (s->cur_func->js_mode & JS_MODE_MATH)
opcode = OP_math_mod;
else
#endif
opcode = OP_mod;
opcode = OP_mod;
break;
default:
return 0;
@ -25610,12 +25257,6 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
OP_pow,
};
op = assign_opcodes[op - TOK_MUL_ASSIGN];
#ifdef CONFIG_BIGNUM
if (s->cur_func->js_mode & JS_MODE_MATH) {
if (op == OP_mod)
op = OP_math_mod;
}
#endif
emit_op(s, op);
}
put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE);
@ -29312,10 +28953,7 @@ static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionB
printf(" mode:");
if (b->js_mode & JS_MODE_STRICT)
printf(" strict");
#ifdef CONFIG_BIGNUM
if (b->js_mode & JS_MODE_MATH)
printf(" math");
#endif
printf("\n");
}
if (b->arg_count && b->vardefs) {
@ -32897,11 +32535,6 @@ static __exception int js_parse_directives(JSParseState *s)
else if (!strcmp(str, "use strip")) {
s->cur_func->js_mode |= JS_MODE_STRIP;
}
#endif
#ifdef CONFIG_BIGNUM
else if (s->ctx->bignum_ext && !strcmp(str, "use math")) {
s->cur_func->js_mode |= JS_MODE_MATH;
}
#endif
}
return js_parse_seek_token(s, &pos);
@ -52539,15 +52172,12 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
break;
#ifdef CONFIG_BIGNUM
case JS_CLASS_BIG_INT64_ARRAY:
if (is_bigint || (is_math_mode(ctx) && is_int &&
v64 >= -MAX_SAFE_INTEGER &&
v64 <= MAX_SAFE_INTEGER)) {
if (is_bigint) {
goto scan64;
}
break;
case JS_CLASS_BIG_UINT64_ARRAY:
if (is_bigint || (is_math_mode(ctx) && is_int &&
v64 >= 0 && v64 <= MAX_SAFE_INTEGER)) {
if (is_bigint) {
const uint64_t *pv;
uint64_t v;
scan64:

View file

@ -1,256 +0,0 @@
"use math";
"use strict";
function assert(actual, expected, message) {
if (arguments.length == 1)
expected = true;
if (actual === expected)
return;
if (actual !== null && expected !== null
&& typeof actual == 'object' && typeof expected == 'object'
&& actual.toString() === expected.toString())
return;
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
function assertThrows(err, func)
{
var ex;
ex = false;
try {
func();
} catch(e) {
ex = true;
assert(e instanceof err);
}
assert(ex, true, "exception expected");
}
// load more elaborate version of assert if available
try { __loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
function pow(a, n)
{
var r, i;
r = 1;
for(i = 0; i < n; i++)
r *= a;
return r;
}
function test_integer()
{
var a, r;
a = pow(3, 100);
assert((a - 1) != a);
assert(a == 515377520732011331036461129765621272702107522001);
assert(a == 0x5a4653ca673768565b41f775d6947d55cf3813d1);
assert(Integer.isInteger(1) === true);
assert(Integer.isInteger(1.0) === false);
assert(Integer.floorLog2(0) === -1);
assert(Integer.floorLog2(7) === 2);
r = 1 << 31;
assert(r, 2147483648, "1 << 31 === 2147483648");
r = 1 << 32;
assert(r, 4294967296, "1 << 32 === 4294967296");
r = (1 << 31) < 0;
assert(r, false, "(1 << 31) < 0 === false");
assert(typeof 1 === "number");
assert(typeof 9007199254740991 === "number");
assert(typeof 9007199254740992 === "bigint");
}
function test_float()
{
assert(typeof 1.0 === "bigfloat");
assert(1 == 1.0);
assert(1 !== 1.0);
}
/* jscalc tests */
function test_modulo()
{
var i, p, a, b;
/* Euclidian modulo operator */
assert((-3) % 2 == 1);
assert(3 % (-2) == 1);
p = 101;
for(i = 1; i < p; i++) {
a = Integer.invmod(i, p);
assert(a >= 0 && a < p);
assert((i * a) % p == 1);
}
assert(Integer.isPrime(2^107-1));
assert(!Integer.isPrime((2^107-1) * (2^89-1)));
a = Integer.factor((2^89-1)*2^3*11*13^2*1009);
assert(a == [ 2,2,2,11,13,13,1009,618970019642690137449562111 ]);
}
function test_fraction()
{
assert((1/3 + 1).toString(), "4/3")
assert((2/3)^30, 1073741824/205891132094649);
assert(1/3 < 2/3);
assert(1/3 < 1);
assert(1/3 == 1.0/3);
assert(1.0/3 < 2/3);
}
function test_mod()
{
var a, b, p;
a = Mod(3, 101);
b = Mod(-1, 101);
assert((a + b) == Mod(2, 101));
assert(a ^ 100 == Mod(1, 101));
p = 2 ^ 607 - 1; /* mersenne prime */
a = Mod(3, p) ^ (p - 1);
assert(a == Mod(1, p));
}
function test_polynomial()
{
var a, b, q, r, t, i;
a = (1 + X) ^ 4;
assert(a == X^4+4*X^3+6*X^2+4*X+1);
r = (1 + X);
q = (1+X+X^2);
b = (1 - X^2);
a = q * b + r;
t = Polynomial.divrem(a, b);
assert(t[0] == q);
assert(t[1] == r);
a = 1 + 2*X + 3*X^2;
assert(a.apply(0.1) == 1.23);
a = 1-2*X^2+2*X^3;
assert(deriv(a) == (6*X^2-4*X));
assert(deriv(integ(a)) == a);
a = (X-1)*(X-2)*(X-3)*(X-4)*(X-0.1);
r = polroots(a);
for(i = 0; i < r.length; i++) {
b = abs(a.apply(r[i]));
assert(b <= 1e-13);
}
}
function test_poly_mod()
{
var a, p;
/* modulo using polynomials */
p = X^2 + X + 1;
a = PolyMod(3+X, p) ^ 10;
assert(a == PolyMod(-3725*X-18357, p));
a = PolyMod(1/X, 1+X^2);
assert(a == PolyMod(-X, X^2+1));
}
function test_rfunc()
{
var a;
a = (X+1)/((X+1)*(X-1));
assert(a == 1/(X-1));
a = (X + 2) / (X - 2);
assert(a.apply(1/3) == -7/5);
assert(deriv((X^2-X+1)/(X-1)) == (X^2-2*X)/(X^2-2*X+1));
}
function test_series()
{
var a, b;
a = 1+X+O(X^5);
b = a.inverse();
assert(b == 1-X+X^2-X^3+X^4+O(X^5));
assert(deriv(b) == -1+2*X-3*X^2+4*X^3+O(X^4));
assert(deriv(integ(b)) == b);
a = Series(1/(1-X), 5);
assert(a == 1+X+X^2+X^3+X^4+O(X^5));
b = a.apply(0.1);
assert(b == 1.1111);
assert(exp(3*X^2+O(X^10)) == 1+3*X^2+9/2*X^4+9/2*X^6+27/8*X^8+O(X^10));
assert(sin(X+O(X^6)) == X-1/6*X^3+1/120*X^5+O(X^6));
assert(cos(X+O(X^6)) == 1-1/2*X^2+1/24*X^4+O(X^6));
assert(tan(X+O(X^8)) == X+1/3*X^3+2/15*X^5+17/315*X^7+O(X^8));
assert((1+X+O(X^6))^(2+X) == 1+2*X+2*X^2+3/2*X^3+5/6*X^4+5/12*X^5+O(X^6));
}
function test_matrix()
{
var a, b, r;
a = [[1, 2],[3, 4]];
b = [3, 4];
r = a * b;
assert(r == [11, 25]);
r = (a^-1) * 2;
assert(r == [[-4, 2],[3, -1]]);
assert(norm2([1,2,3]) == 14);
assert(diag([1,2,3]) == [ [ 1, 0, 0 ], [ 0, 2, 0 ], [ 0, 0, 3 ] ]);
assert(trans(a) == [ [ 1, 3 ], [ 2, 4 ] ]);
assert(trans([1,2,3]) == [[1,2,3]]);
assert(trace(a) == 5);
assert(charpoly(Matrix.hilbert(4)) == X^4-176/105*X^3+3341/12600*X^2-41/23625*X+1/6048000);
assert(det(Matrix.hilbert(4)) == 1/6048000);
a = [[1,2,1],[-2,-3,1],[3,5,0]];
assert(rank(a) == 2);
assert(ker(a) == [ [ 5 ], [ -3 ], [ 1 ] ]);
assert(dp([1, 2, 3], [3, -4, -7]) === -26);
assert(cp([1, 2, 3], [3, -4, -7]) == [ -2, 16, -10 ]);
}
function assert_eq(a, ref)
{
assert(abs(a / ref - 1.0) <= 1e-15);
}
function test_trig()
{
assert_eq(sin(1/2), 0.479425538604203);
assert_eq(sin(2+3*I), 9.154499146911428-4.168906959966565*I);
assert_eq(cos(2+3*I), -4.189625690968807-9.109227893755337*I);
assert_eq((2+0.5*I)^(1.1-0.5*I), 2.494363021357619-0.23076804554558092*I);
assert_eq(sqrt(2*I), 1 + I);
}
test_integer();
test_float();
test_modulo();
test_fraction();
test_mod();
test_polynomial();
test_poly_mod();
test_rfunc();
test_series();
test_matrix();
test_trig();