view release on metacpan or search on metacpan
quickjs/quickjs.c view on Meta::CPAN
typedef struct JSStackFrame {
struct JSStackFrame *prev_frame; /* NULL if first stack frame */
JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */
JSValue *arg_buf; /* arguments */
JSValue *var_buf; /* variables */
struct list_head var_ref_list; /* list of JSVarRef.var_ref_link */
const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
instruction after the call */
int arg_count;
int js_mode; /* for C functions, only JS_MODE_MATH may be set */
/* only used in generators. Current stack pointer value. NULL if
the function is running. */
JSValue *cur_sp;
} JSStackFrame;
typedef enum {
JS_GC_OBJ_TYPE_JS_OBJECT,
JS_GC_OBJ_TYPE_FUNCTION_BYTECODE,
JS_GC_OBJ_TYPE_SHAPE,
JS_GC_OBJ_TYPE_VAR_REF,
JS_GC_OBJ_TYPE_ASYNC_FUNCTION,
JS_GC_OBJ_TYPE_JS_CONTEXT,
} JSGCObjectTypeEnum;
/* header for GC objects. GC objects are C data structures with a
reference count that can reference other GC objects. JS Objects are
a particular type of GC object. */
struct JSGCObjectHeader {
int ref_count; /* must come first, 32-bit */
JSGCObjectTypeEnum gc_obj_type : 4;
uint8_t mark : 4; /* used by the GC */
uint8_t dummy1; /* not used by the GC */
uint16_t dummy2; /* not used by the GC */
struct list_head link;
};
typedef struct JSVarRef {
union {
JSGCObjectHeader header; /* must come first */
struct {
int __gc_ref_count; /* corresponds to header.ref_count */
uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
uint8_t is_detached : 1;
uint8_t is_arg : 1;
uint16_t var_idx; /* index of the corresponding function variable on
the stack */
};
};
JSValue *pvalue; /* pointer to the value, either on the stack or
to 'value' */
union {
JSValue value; /* used when is_detached = TRUE */
struct {
struct list_head var_ref_link; /* JSStackFrame.var_ref_list list */
struct JSAsyncFunctionState *async_func; /* != NULL if async stack frame */
}; /* used when is_detached = FALSE */
};
} JSVarRef;
/* the same structure is used for big integers and big floats. Big
integers are never infinite or NaNs */
typedef struct JSBigFloat {
JSRefCountHeader header; /* must come first, 32-bit */
bf_t num;
} JSBigFloat;
#ifdef CONFIG_BIGNUM
typedef struct JSFloatEnv {
limb_t prec;
bf_flags_t flags;
unsigned int status;
} JSFloatEnv;
typedef struct JSBigDecimal {
JSRefCountHeader header; /* must come first, 32-bit */
bfdec_t num;
} JSBigDecimal;
#endif
typedef enum {
JS_AUTOINIT_ID_PROTOTYPE,
JS_AUTOINIT_ID_MODULE_NS,
JS_AUTOINIT_ID_PROP,
} JSAutoInitIDEnum;
/* must be large enough to have a negligible runtime cost and small
enough to call the interrupt callback often. */
#define JS_INTERRUPT_COUNTER_INIT 10000
struct JSContext {
JSGCObjectHeader header; /* must come first */
JSRuntime *rt;
struct list_head link;
uint16_t binary_object_count;
int binary_object_size;
JSShape *array_shape; /* initial shape for Array objects */
JSValue *class_proto;
JSValue function_proto;
JSValue function_ctor;
JSValue array_ctor;
JSValue regexp_ctor;
JSValue promise_ctor;
JSValue native_error_proto[JS_NATIVE_ERROR_COUNT];
JSValue iterator_proto;
JSValue async_iterator_proto;
JSValue array_proto_values;
JSValue throw_type_error;
JSValue eval_obj;
JSValue global_obj; /* global object */
JSValue global_var_obj; /* contains the global let/const definitions */
uint64_t random_state;
bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */
#ifdef CONFIG_BIGNUM
JSFloatEnv fp_env; /* global FP environment */
BOOL bignum_ext : 8; /* enable math mode */
BOOL allow_operator_overloading : 8;
quickjs/quickjs.c view on Meta::CPAN
}
/* return TRUE if the atom is an array index (i.e. 0 <= index <=
2^32-2 and return its value */
static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom)
{
if (__JS_AtomIsTaggedInt(atom)) {
*pval = __JS_AtomToUInt32(atom);
return TRUE;
} else {
JSRuntime *rt = ctx->rt;
JSAtomStruct *p;
uint32_t val;
assert(atom < rt->atom_size);
p = rt->atom_array[atom];
if (p->atom_type == JS_ATOM_TYPE_STRING &&
is_num_string(&val, p) && val != -1) {
*pval = val;
return TRUE;
} else {
*pval = 0;
return FALSE;
}
}
}
/* This test must be fast if atom is not a numeric index (e.g. a
method name). Return JS_UNDEFINED if not a numeric
index. JS_EXCEPTION can also be returned. */
static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)
{
JSRuntime *rt = ctx->rt;
JSAtomStruct *p1;
JSString *p;
int c, len, ret;
JSValue num, str;
if (__JS_AtomIsTaggedInt(atom))
return JS_NewInt32(ctx, __JS_AtomToUInt32(atom));
assert(atom < rt->atom_size);
p1 = rt->atom_array[atom];
if (p1->atom_type != JS_ATOM_TYPE_STRING)
return JS_UNDEFINED;
p = p1;
len = p->len;
if (p->is_wide_char) {
const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len;
if (r >= r_end)
return JS_UNDEFINED;
c = *r;
if (c == '-') {
if (r >= r_end)
return JS_UNDEFINED;
r++;
c = *r;
/* -0 case is specific */
if (c == '0' && len == 2)
goto minus_zero;
}
/* XXX: should test NaN, but the tests do not check it */
if (!is_num(c)) {
/* XXX: String should be normalized, therefore 8-bit only */
const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' };
if (!(c =='I' && (r_end - r) == 8 &&
!memcmp(r + 1, nfinity16, sizeof(nfinity16))))
return JS_UNDEFINED;
}
} else {
const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len;
if (r >= r_end)
return JS_UNDEFINED;
c = *r;
if (c == '-') {
if (r >= r_end)
return JS_UNDEFINED;
r++;
c = *r;
/* -0 case is specific */
if (c == '0' && len == 2) {
minus_zero:
return __JS_NewFloat64(ctx, -0.0);
}
}
if (!is_num(c)) {
if (!(c =='I' && (r_end - r) == 8 &&
!memcmp(r + 1, "nfinity", 7)))
return JS_UNDEFINED;
}
}
/* XXX: bignum: would be better to only accept integer to avoid
relying on current floating point precision */
/* this is ECMA CanonicalNumericIndexString primitive */
num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p));
if (JS_IsException(num))
return num;
str = JS_ToString(ctx, num);
if (JS_IsException(str)) {
JS_FreeValue(ctx, num);
return str;
}
ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str));
JS_FreeValue(ctx, str);
if (ret == 0) {
return num;
} else {
JS_FreeValue(ctx, num);
return JS_UNDEFINED;
}
}
/* return -1 if exception or TRUE/FALSE */
static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom)
{
JSValue num;
num = JS_AtomIsNumericIndex1(ctx, atom);
if (likely(JS_IsUndefined(num)))
return FALSE;
if (JS_IsException(num))
return -1;
JS_FreeValue(ctx, num);
quickjs/quickjs.c view on Meta::CPAN
val = JS_ToNumberFree(ctx, val);
if (JS_IsException(val)) {
*pres = JS_FLOAT64_NAN;
return -1;
}
tag = JS_VALUE_GET_NORM_TAG(val);
switch(tag) {
case JS_TAG_INT:
d = JS_VALUE_GET_INT(val);
break;
case JS_TAG_FLOAT64:
d = JS_VALUE_GET_FLOAT64(val);
break;
case JS_TAG_BIG_INT:
#ifdef CONFIG_BIGNUM
case JS_TAG_BIG_FLOAT:
#endif
{
JSBigFloat *p = JS_VALUE_GET_PTR(val);
/* XXX: there can be a double rounding issue with some
primitives (such as JS_ToUint8ClampFree()), but it is
not critical to fix it. */
bf_get_float64(&p->num, &d, BF_RNDN);
JS_FreeValue(ctx, val);
}
break;
default:
abort();
}
*pres = d;
return 0;
}
static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)
{
uint32_t tag;
tag = JS_VALUE_GET_TAG(val);
if (tag <= JS_TAG_NULL) {
*pres = JS_VALUE_GET_INT(val);
return 0;
} else if (JS_TAG_IS_FLOAT64(tag)) {
*pres = JS_VALUE_GET_FLOAT64(val);
return 0;
} else {
return __JS_ToFloat64Free(ctx, pres, val);
}
}
int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val)
{
return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val));
}
static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
{
return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));
}
/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */
static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
{
uint32_t tag;
JSValue ret;
redo:
tag = JS_VALUE_GET_NORM_TAG(val);
switch(tag) {
case JS_TAG_INT:
case JS_TAG_BOOL:
case JS_TAG_NULL:
case JS_TAG_UNDEFINED:
ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
break;
case JS_TAG_FLOAT64:
{
double d = JS_VALUE_GET_FLOAT64(val);
if (isnan(d)) {
ret = JS_NewInt32(ctx, 0);
} else {
/* convert -0 to +0 */
d = trunc(d) + 0.0;
ret = JS_NewFloat64(ctx, d);
}
}
break;
#ifdef CONFIG_BIGNUM
case JS_TAG_BIG_FLOAT:
{
bf_t a_s, *a, r_s, *r = &r_s;
BOOL is_nan;
a = JS_ToBigFloat(ctx, &a_s, val);
if (!a) {
JS_FreeValue(ctx, val);
return JS_EXCEPTION;
}
if (!bf_is_finite(a)) {
is_nan = bf_is_nan(a);
if (is_nan)
ret = JS_NewInt32(ctx, 0);
else
ret = JS_DupValue(ctx, val);
} else {
ret = JS_NewBigInt(ctx);
if (!JS_IsException(ret)) {
r = JS_GetBigInt(ret);
bf_set(r, a);
bf_rint(r, BF_RNDZ);
ret = JS_CompactBigInt(ctx, ret);
}
}
if (a == &a_s)
bf_delete(a);
JS_FreeValue(ctx, val);
}
break;
#endif
default:
val = JS_ToNumberFree(ctx, val);
quickjs/quickjs.c view on Meta::CPAN
}
}
int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val)
{
return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
}
int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val,
int64_t min, int64_t max, int64_t neg_offset)
{
int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
if (res == 0) {
if (*pres < 0)
*pres += neg_offset;
if (*pres < min)
*pres = min;
else if (*pres > max)
*pres = max;
}
return res;
}
/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0)
in case of exception */
static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
{
uint32_t tag;
int64_t ret;
redo:
tag = JS_VALUE_GET_NORM_TAG(val);
switch(tag) {
case JS_TAG_INT:
case JS_TAG_BOOL:
case JS_TAG_NULL:
case JS_TAG_UNDEFINED:
ret = JS_VALUE_GET_INT(val);
break;
case JS_TAG_FLOAT64:
{
JSFloat64Union u;
double d;
int e;
d = JS_VALUE_GET_FLOAT64(val);
u.d = d;
/* we avoid doing fmod(x, 2^64) */
e = (u.u64 >> 52) & 0x7ff;
if (likely(e <= (1023 + 62))) {
/* fast case */
ret = (int64_t)d;
} else if (e <= (1023 + 62 + 53)) {
uint64_t v;
/* remainder modulo 2^64 */
v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
ret = v << ((e - 1023) - 52);
/* take the sign into account */
if (u.u64 >> 63)
ret = -ret;
} else {
ret = 0; /* also handles NaN and +inf */
}
}
break;
#ifdef CONFIG_BIGNUM
case JS_TAG_BIG_FLOAT:
{
JSBigFloat *p = JS_VALUE_GET_PTR(val);
bf_get_int64(&ret, &p->num, BF_GET_INT_MOD);
JS_FreeValue(ctx, val);
}
break;
#endif
default:
val = JS_ToNumberFree(ctx, val);
if (JS_IsException(val)) {
*pres = 0;
return -1;
}
goto redo;
}
*pres = ret;
return 0;
}
int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
{
return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val));
}
int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val)
{
if (JS_IsBigInt(ctx, val))
return JS_ToBigInt64(ctx, pres, val);
else
return JS_ToInt64(ctx, pres, val);
}
/* return (<0, 0) in case of exception */
static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)
{
uint32_t tag;
int32_t ret;
redo:
tag = JS_VALUE_GET_NORM_TAG(val);
switch(tag) {
case JS_TAG_INT:
case JS_TAG_BOOL:
case JS_TAG_NULL:
case JS_TAG_UNDEFINED:
ret = JS_VALUE_GET_INT(val);
break;
case JS_TAG_FLOAT64:
{
JSFloat64Union u;
double d;
int e;
d = JS_VALUE_GET_FLOAT64(val);
u.d = d;
/* we avoid doing fmod(x, 2^32) */
e = (u.u64 >> 52) & 0x7ff;
if (likely(e <= (1023 + 30))) {
/* fast case */
ret = (int32_t)d;
} else if (e <= (1023 + 30 + 53)) {
uint64_t v;
/* remainder modulo 2^32 */
v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
v = v << ((e - 1023) - 52 + 32);
ret = v >> 32;
/* take the sign into account */
if (u.u64 >> 63)
ret = -ret;
} else {
ret = 0; /* also handles NaN and +inf */
}
}
break;
#ifdef CONFIG_BIGNUM
case JS_TAG_BIG_FLOAT:
{
JSBigFloat *p = JS_VALUE_GET_PTR(val);
bf_get_int32(&ret, &p->num, BF_GET_INT_MOD);
JS_FreeValue(ctx, val);
}
break;
#endif
default:
val = JS_ToNumberFree(ctx, val);
if (JS_IsException(val)) {
*pres = 0;
return -1;
}
goto redo;
}
*pres = ret;
return 0;
}
int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val)
{
return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val));
}
static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val)
{
return JS_ToInt32Free(ctx, (int32_t *)pres, val);
}
static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)
{
uint32_t tag;
int res;
redo:
tag = JS_VALUE_GET_NORM_TAG(val);
switch(tag) {
case JS_TAG_INT:
case JS_TAG_BOOL:
case JS_TAG_NULL:
case JS_TAG_UNDEFINED:
res = JS_VALUE_GET_INT(val);
#ifdef CONFIG_BIGNUM
int_clamp:
#endif
res = max_int(0, min_int(255, res));
break;
case JS_TAG_FLOAT64:
{
double d = JS_VALUE_GET_FLOAT64(val);
if (isnan(d)) {
res = 0;
} else {
if (d < 0)
res = 0;
quickjs/quickjs.c view on Meta::CPAN
fesetround(rounding_mode);
n = snprintf(buf, buf_size, "%.*f", n_digits, d);
if (rounding_mode != FE_TONEAREST)
fesetround(FE_TONEAREST);
assert(n < buf_size);
return n;
}
static void js_fcvt(char *buf, int buf_size, double d, int n_digits)
{
int rounding_mode;
rounding_mode = FE_TONEAREST;
#ifdef CONFIG_PRINTF_RNDN
{
int n1, n2;
char buf1[JS_DTOA_BUF_SIZE];
char buf2[JS_DTOA_BUF_SIZE];
/* The JS rounding is specified as round to nearest ties away from
zero (RNDNA), but in printf the "ties" case is not specified
(for example it is RNDN for glibc, RNDNA for Windows), so we
must round manually. */
n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_TONEAREST);
rounding_mode = FE_TONEAREST;
/* XXX: could use 2 digits to reduce the average running time */
if (buf1[n1 - 1] == '5') {
n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_DOWNWARD);
n2 = js_fcvt1(buf2, sizeof(buf2), d, n_digits + 1, FE_UPWARD);
if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) {
/* exact result: round away from zero */
if (buf1[0] == '-')
rounding_mode = FE_DOWNWARD;
else
rounding_mode = FE_UPWARD;
}
}
}
#endif /* CONFIG_PRINTF_RNDN */
js_fcvt1(buf, buf_size, d, n_digits, rounding_mode);
}
/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */
/* use as many digits as necessary */
#define JS_DTOA_VAR_FORMAT (0 << 0)
/* use n_digits significant digits (1 <= n_digits <= 101) */
#define JS_DTOA_FIXED_FORMAT (1 << 0)
/* force fractional format: [-]dd.dd with n_digits fractional digits */
#define JS_DTOA_FRAC_FORMAT (2 << 0)
/* force exponential notation either in fixed or variable format */
#define JS_DTOA_FORCE_EXP (1 << 2)
/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough.
XXX: radix != 10 is only supported for small integers
*/
static void js_dtoa1(char *buf, double d, int radix, int n_digits, int flags)
{
char *q;
if (!isfinite(d)) {
if (isnan(d)) {
strcpy(buf, "NaN");
} else {
q = buf;
if (d < 0)
*q++ = '-';
strcpy(q, "Infinity");
}
} else if (flags == JS_DTOA_VAR_FORMAT) {
int64_t i64;
char buf1[70], *ptr;
i64 = (int64_t)d;
if (d != i64 || i64 > MAX_SAFE_INTEGER || i64 < -MAX_SAFE_INTEGER)
goto generic_conv;
/* fast path for integers */
ptr = i64toa(buf1 + sizeof(buf1), i64, radix);
strcpy(buf, ptr);
} else {
if (d == 0.0)
d = 0.0; /* convert -0 to 0 */
if (flags == JS_DTOA_FRAC_FORMAT) {
js_fcvt(buf, JS_DTOA_BUF_SIZE, d, n_digits);
} else {
char buf1[JS_DTOA_BUF_SIZE];
int sign, decpt, k, n, i, p, n_max;
BOOL is_fixed;
generic_conv:
is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT);
if (is_fixed) {
n_max = n_digits;
} else {
n_max = 21;
}
/* the number has k digits (k >= 1) */
k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed);
n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */
q = buf;
if (sign)
*q++ = '-';
if (flags & JS_DTOA_FORCE_EXP)
goto force_exp;
if (n >= 1 && n <= n_max) {
if (k <= n) {
memcpy(q, buf1, k);
q += k;
for(i = 0; i < (n - k); i++)
*q++ = '0';
*q = '\0';
} else {
/* k > n */
memcpy(q, buf1, n);
q += n;
*q++ = '.';
for(i = 0; i < (k - n); i++)
*q++ = buf1[n + i];
*q = '\0';
}
} else if (n >= -5 && n <= 0) {
*q++ = '0';
*q++ = '.';
for(i = 0; i < -n; i++)
*q++ = '0';
quickjs/quickjs.c view on Meta::CPAN
return p->class_id == JS_CLASS_ARRAY;
} else {
return FALSE;
}
}
static double js_pow(double a, double b)
{
if (unlikely(!isfinite(b)) && fabs(a) == 1) {
/* not compatible with IEEE 754 */
return JS_FLOAT64_NAN;
} else {
return pow(a, b);
}
}
JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v)
{
JSValue val;
bf_t *a;
val = JS_NewBigInt(ctx);
if (JS_IsException(val))
return val;
a = JS_GetBigInt(val);
if (bf_set_si(a, v)) {
JS_FreeValue(ctx, val);
return JS_ThrowOutOfMemory(ctx);
}
return val;
}
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);
}
}
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);
}
}
return val;
}
/* return NaN if bad bigint literal */
static 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;
#ifdef CONFIG_BIGNUM
if (is_math_mode(ctx))
flags |= ATOD_MODE_BIGINT;
#endif
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;
}
/* 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;
quickjs/quickjs.c view on Meta::CPAN
bigint_op:
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))) {
JS_FreeValue(ctx, op2);
goto exception;
}
if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))
goto exception;
switch(op) {
case OP_shl:
r = v1 << (v2 & 0x1f);
break;
case OP_sar:
r = (int)v1 >> (v2 & 0x1f);
break;
case OP_and:
r = v1 & v2;
break;
case OP_or:
r = v1 | v2;
break;
case OP_xor:
r = v1 ^ v2;
break;
default:
abort();
}
sp[-2] = JS_NewInt32(ctx, r);
}
return 0;
exception:
sp[-2] = JS_UNDEFINED;
sp[-1] = JS_UNDEFINED;
return -1;
}
/* Note: also used for bigint */
static 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;
}
#ifdef CONFIG_BIGNUM
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); /* cannot fail */
b = JS_ToBigDecimal(ctx, op2); /* cannot fail */
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;
}
#endif /* !CONFIG_BIGNUM */
static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
OPCodeEnum op)
{
JSValue op1, op2;
int res;
uint32_t tag1, tag2;
op1 = sp[-2];
op2 = sp[-1];
tag1 = JS_VALUE_GET_NORM_TAG(op1);
tag2 = JS_VALUE_GET_NORM_TAG(op2);
#ifdef CONFIG_BIGNUM
/* try to call an overloaded operator */
if ((tag1 == JS_TAG_OBJECT &&
(tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
(tag2 == JS_TAG_OBJECT &&
(tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
JSValue ret;
res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op,
FALSE, HINT_NUMBER);
if (res != 0) {
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
if (res < 0) {
goto exception;
} else {
sp[-2] = ret;
return 0;
}
}
}
#endif
op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);
if (JS_IsException(op1)) {
JS_FreeValue(ctx, op2);
goto exception;
}
op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);
if (JS_IsException(op2)) {
JS_FreeValue(ctx, op1);
goto exception;
}
tag1 = JS_VALUE_GET_NORM_TAG(op1);
tag2 = JS_VALUE_GET_NORM_TAG(op2);
if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) {
JSString *p1, *p2;
p1 = JS_VALUE_GET_STRING(op1);
p2 = JS_VALUE_GET_STRING(op2);
res = js_string_compare(ctx, p1, p2);
quickjs/quickjs.c view on Meta::CPAN
goto invalid_bigint_string;
}
if (tag2 == JS_TAG_STRING) {
op2 = JS_StringToBigInt(ctx, op2);
if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {
invalid_bigint_string:
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
res = FALSE;
goto done;
}
}
} else {
op1 = JS_ToNumericFree(ctx, op1);
if (JS_IsException(op1)) {
JS_FreeValue(ctx, op2);
goto exception;
}
op2 = JS_ToNumericFree(ctx, op2);
if (JS_IsException(op2)) {
JS_FreeValue(ctx, op1);
goto exception;
}
}
tag1 = JS_VALUE_GET_NORM_TAG(op1);
tag2 = JS_VALUE_GET_NORM_TAG(op2);
#ifdef CONFIG_BIGNUM
if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2);
if (res < 0)
goto exception;
} else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2);
if (res < 0)
goto exception;
} else
#endif
if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2);
if (res < 0)
goto exception;
} else {
double d1, d2;
float64_compare:
/* can use floating point comparison */
if (tag1 == JS_TAG_FLOAT64) {
d1 = JS_VALUE_GET_FLOAT64(op1);
} else {
d1 = JS_VALUE_GET_INT(op1);
}
if (tag2 == JS_TAG_FLOAT64) {
d2 = JS_VALUE_GET_FLOAT64(op2);
} else {
d2 = JS_VALUE_GET_INT(op2);
}
switch(op) {
case OP_lt:
res = (d1 < d2); /* if NaN return false */
break;
case OP_lte:
res = (d1 <= d2); /* if NaN return false */
break;
case OP_gt:
res = (d1 > d2); /* if NaN return false */
break;
default:
case OP_gte:
res = (d1 >= d2); /* if NaN return false */
break;
}
}
}
done:
sp[-2] = JS_NewBool(ctx, res);
return 0;
exception:
sp[-2] = JS_UNDEFINED;
sp[-1] = JS_UNDEFINED;
return -1;
}
static BOOL tag_is_number(uint32_t tag)
{
return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT ||
tag == JS_TAG_FLOAT64
#ifdef CONFIG_BIGNUM
|| tag == JS_TAG_BIG_FLOAT || tag == JS_TAG_BIG_DECIMAL
#endif
);
}
static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
BOOL is_neq)
{
JSValue op1, op2;
#ifdef CONFIG_BIGNUM
JSValue ret;
#endif
int res;
uint32_t tag1, tag2;
op1 = sp[-2];
op2 = sp[-1];
redo:
tag1 = JS_VALUE_GET_NORM_TAG(op1);
tag2 = JS_VALUE_GET_NORM_TAG(op2);
if (tag_is_number(tag1) && tag_is_number(tag2)) {
if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
} else if ((tag1 == JS_TAG_FLOAT64 &&
(tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) ||
(tag2 == JS_TAG_FLOAT64 &&
(tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) {
double d1, d2;
if (tag1 == JS_TAG_FLOAT64) {
d1 = JS_VALUE_GET_FLOAT64(op1);
} else {
d1 = JS_VALUE_GET_INT(op1);
}
if (tag2 == JS_TAG_FLOAT64) {
d2 = JS_VALUE_GET_FLOAT64(op2);
} else {
d2 = JS_VALUE_GET_INT(op2);
}
res = (d1 == d2);
} else
#ifdef CONFIG_BIGNUM
if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
quickjs/quickjs.c view on Meta::CPAN
break;
case JS_TAG_NULL:
case JS_TAG_UNDEFINED:
res = (tag1 == tag2);
break;
case JS_TAG_STRING:
{
JSString *p1, *p2;
if (tag1 != tag2) {
res = FALSE;
} else {
p1 = JS_VALUE_GET_STRING(op1);
p2 = JS_VALUE_GET_STRING(op2);
res = (js_string_compare(ctx, p1, p2) == 0);
}
}
break;
case JS_TAG_SYMBOL:
{
JSAtomStruct *p1, *p2;
if (tag1 != tag2) {
res = FALSE;
} else {
p1 = JS_VALUE_GET_PTR(op1);
p2 = JS_VALUE_GET_PTR(op2);
res = (p1 == p2);
}
}
break;
case JS_TAG_OBJECT:
if (tag1 != tag2)
res = FALSE;
else
res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2);
break;
case JS_TAG_INT:
d1 = JS_VALUE_GET_INT(op1);
if (tag2 == JS_TAG_INT) {
d2 = JS_VALUE_GET_INT(op2);
goto number_test;
} else if (tag2 == JS_TAG_FLOAT64) {
d2 = JS_VALUE_GET_FLOAT64(op2);
goto number_test;
} else {
res = FALSE;
}
break;
case JS_TAG_FLOAT64:
d1 = JS_VALUE_GET_FLOAT64(op1);
if (tag2 == JS_TAG_FLOAT64) {
d2 = JS_VALUE_GET_FLOAT64(op2);
} else if (tag2 == JS_TAG_INT) {
d2 = JS_VALUE_GET_INT(op2);
} else {
res = FALSE;
break;
}
number_test:
if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {
JSFloat64Union u1, u2;
/* NaN is not always normalized, so this test is necessary */
if (isnan(d1) || isnan(d2)) {
res = isnan(d1) == isnan(d2);
} else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) {
res = (d1 == d2); /* +0 == -0 */
} else {
u1.d = d1;
u2.d = d2;
res = (u1.u64 == u2.u64); /* +0 != -0 */
}
} else {
res = (d1 == d2); /* if NaN return false and +0 == -0 */
}
goto done_no_free;
case JS_TAG_BIG_INT:
{
bf_t a_s, *a, b_s, *b;
if (tag1 != tag2) {
res = FALSE;
break;
}
a = JS_ToBigFloat(ctx, &a_s, op1); /* cannot fail */
b = JS_ToBigFloat(ctx, &b_s, op2); /* cannot fail */
res = bf_cmp_eq(a, b);
if (a == &a_s)
bf_delete(a);
if (b == &b_s)
bf_delete(b);
}
break;
#ifdef CONFIG_BIGNUM
case JS_TAG_BIG_FLOAT:
{
JSBigFloat *p1, *p2;
const bf_t *a, *b;
if (tag1 != tag2) {
res = FALSE;
break;
}
p1 = JS_VALUE_GET_PTR(op1);
p2 = JS_VALUE_GET_PTR(op2);
a = &p1->num;
b = &p2->num;
if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {
if (eq_mode == JS_EQ_SAME_VALUE_ZERO &&
a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) {
res = TRUE;
} else {
res = (bf_cmp_full(a, b) == 0);
}
} else {
res = bf_cmp_eq(a, b);
}
}
break;
case JS_TAG_BIG_DECIMAL:
{
JSBigDecimal *p1, *p2;
const bfdec_t *a, *b;
if (tag1 != tag2) {
res = FALSE;
break;
}
p1 = JS_VALUE_GET_PTR(op1);
p2 = JS_VALUE_GET_PTR(op2);
a = &p1->num;
b = &p2->num;
res = bfdec_cmp_eq(a, b);
}
break;
#endif
default:
quickjs/quickjs.c view on Meta::CPAN
/* Note: 'func_obj' is not necessarily a constructor */
static void JS_SetConstructor2(JSContext *ctx,
JSValueConst func_obj,
JSValueConst proto,
int proto_flags, int ctor_flags)
{
JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype,
JS_DupValue(ctx, proto), proto_flags);
JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,
JS_DupValue(ctx, func_obj),
ctor_flags);
set_cycle_flag(ctx, func_obj);
set_cycle_flag(ctx, proto);
}
void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj,
JSValueConst proto)
{
JS_SetConstructor2(ctx, func_obj, proto,
0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
}
static void JS_NewGlobalCConstructor2(JSContext *ctx,
JSValue func_obj,
const char *name,
JSValueConst proto)
{
JS_DefinePropertyValueStr(ctx, ctx->global_obj, name,
JS_DupValue(ctx, func_obj),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_SetConstructor(ctx, func_obj, proto);
JS_FreeValue(ctx, func_obj);
}
static JSValueConst JS_NewGlobalCConstructor(JSContext *ctx, const char *name,
JSCFunction *func, int length,
JSValueConst proto)
{
JSValue func_obj;
func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor_or_func, 0);
JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);
return func_obj;
}
static JSValueConst JS_NewGlobalCConstructorOnly(JSContext *ctx, const char *name,
JSCFunction *func, int length,
JSValueConst proto)
{
JSValue func_obj;
func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor, 0);
JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);
return func_obj;
}
static JSValue js_global_eval(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_EvalObject(ctx, ctx->global_obj, argv[0], JS_EVAL_TYPE_INDIRECT, -1);
}
static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
double d;
/* XXX: does this work for bigfloat? */
if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
return JS_EXCEPTION;
return JS_NewBool(ctx, isnan(d));
}
static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
BOOL res;
double d;
if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
return JS_EXCEPTION;
res = isfinite(d);
return JS_NewBool(ctx, res);
}
/* Object class */
static JSValue JS_ToObject(JSContext *ctx, JSValueConst val)
{
int tag = JS_VALUE_GET_NORM_TAG(val);
JSValue obj;
switch(tag) {
default:
case JS_TAG_NULL:
case JS_TAG_UNDEFINED:
return JS_ThrowTypeError(ctx, "cannot convert to object");
case JS_TAG_OBJECT:
case JS_TAG_EXCEPTION:
return JS_DupValue(ctx, val);
case JS_TAG_BIG_INT:
obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT);
goto set_value;
#ifdef CONFIG_BIGNUM
case JS_TAG_BIG_FLOAT:
obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT);
goto set_value;
case JS_TAG_BIG_DECIMAL:
obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL);
goto set_value;
#endif
case JS_TAG_INT:
case JS_TAG_FLOAT64:
obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);
goto set_value;
case JS_TAG_STRING:
/* XXX: should call the string constructor */
{
JSString *p1 = JS_VALUE_GET_STRING(val);
obj = JS_NewObjectClass(ctx, JS_CLASS_STRING);
JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);
}
goto set_value;
case JS_TAG_BOOL:
quickjs/quickjs.c view on Meta::CPAN
if (argc == 0) {
val = JS_NewInt32(ctx, 0);
} else {
val = JS_ToNumeric(ctx, argv[0]);
if (JS_IsException(val))
return val;
switch(JS_VALUE_GET_TAG(val)) {
case JS_TAG_BIG_INT:
#ifdef CONFIG_BIGNUM
case JS_TAG_BIG_FLOAT:
#endif
{
JSBigFloat *p = JS_VALUE_GET_PTR(val);
double d;
bf_get_float64(&p->num, &d, BF_RNDN);
JS_FreeValue(ctx, val);
val = __JS_NewFloat64(ctx, d);
}
break;
#ifdef CONFIG_BIGNUM
case JS_TAG_BIG_DECIMAL:
val = JS_ToStringFree(ctx, val);
if (JS_IsException(val))
return val;
val = JS_ToNumberFree(ctx, val);
if (JS_IsException(val))
return val;
break;
#endif
default:
break;
}
}
if (!JS_IsUndefined(new_target)) {
obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER);
if (!JS_IsException(obj))
JS_SetObjectData(ctx, obj, val);
return obj;
} else {
return val;
}
}
#if 0
static JSValue js_number___toInteger(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[0]));
}
static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int64_t v;
if (JS_ToLengthFree(ctx, &v, JS_DupValue(ctx, argv[0])))
return JS_EXCEPTION;
return JS_NewInt64(ctx, v);
}
#endif
static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
if (!JS_IsNumber(argv[0]))
return JS_FALSE;
return js_global_isNaN(ctx, this_val, argc, argv);
}
static JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
if (!JS_IsNumber(argv[0]))
return JS_FALSE;
return js_global_isFinite(ctx, this_val, argc, argv);
}
static JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int ret;
ret = JS_NumberIsInteger(ctx, argv[0]);
if (ret < 0)
return JS_EXCEPTION;
else
return JS_NewBool(ctx, ret);
}
static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
double d;
if (!JS_IsNumber(argv[0]))
return JS_FALSE;
if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
return JS_EXCEPTION;
return JS_NewBool(ctx, is_safe_integer(d));
}
static const JSCFunctionListEntry js_number_funcs[] = {
/* global ParseInt and parseFloat should be defined already or delayed */
JS_ALIAS_BASE_DEF("parseInt", "parseInt", 0 ),
JS_ALIAS_BASE_DEF("parseFloat", "parseFloat", 0 ),
JS_CFUNC_DEF("isNaN", 1, js_number_isNaN ),
JS_CFUNC_DEF("isFinite", 1, js_number_isFinite ),
JS_CFUNC_DEF("isInteger", 1, js_number_isInteger ),
JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ),
JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ),
JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ),
JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ),
JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ),
JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */
JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */
JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */
//JS_CFUNC_DEF("__toInteger", 1, js_number___toInteger ),
//JS_CFUNC_DEF("__toLength", 1, js_number___toLength ),
};
static JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val)
{
if (JS_IsNumber(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_NUMBER) {
if (JS_IsNumber(p->u.object_data))
return JS_DupValue(ctx, p->u.object_data);
}
}
return JS_ThrowTypeError(ctx, "not a number");
}
static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return js_thisNumberValue(ctx, this_val);
}
static int js_get_radix(JSContext *ctx, JSValueConst val)
{
int radix;
if (JS_ToInt32Sat(ctx, &radix, val))
return -1;
if (radix < 2 || radix > 36) {
JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
return -1;
}
return radix;
}
static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
JSValue val;
int base;
double d;
val = js_thisNumberValue(ctx, this_val);
if (JS_IsException(val))
return val;
if (magic || JS_IsUndefined(argv[0])) {
base = 10;
} else {
base = js_get_radix(ctx, argv[0]);
if (base < 0)
goto fail;
}
if (JS_ToFloat64Free(ctx, &d, val))
quickjs/quickjs.c view on Meta::CPAN
JS_CFUNC_DEF("split", 2, js_string_split ),
JS_CFUNC_DEF("substring", 2, js_string_substring ),
JS_CFUNC_DEF("substr", 2, js_string_substr ),
JS_CFUNC_DEF("slice", 2, js_string_slice ),
JS_CFUNC_DEF("repeat", 1, js_string_repeat ),
JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ),
JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ),
JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ),
JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ),
JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ),
JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ),
JS_ALIAS_DEF("trimRight", "trimEnd" ),
JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ),
JS_ALIAS_DEF("trimLeft", "trimStart" ),
JS_CFUNC_DEF("toString", 0, js_string_toString ),
JS_CFUNC_DEF("valueOf", 0, js_string_toString ),
JS_CFUNC_DEF("__quote", 1, js_string___quote ),
JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ),
JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ),
JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ),
JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ),
JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ),
JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ),
/* ES6 Annex B 2.3.2 etc. */
JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ),
JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ),
JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ),
JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ),
JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ),
JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ),
JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ),
JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ),
JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ),
JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ),
JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ),
JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ),
JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ),
};
static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = {
JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ),
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ),
};
#ifdef CONFIG_ALL_UNICODE
static const JSCFunctionListEntry js_string_proto_normalize[] = {
JS_CFUNC_DEF("normalize", 0, js_string_normalize ),
};
#endif
void JS_AddIntrinsicStringNormalize(JSContext *ctx)
{
#ifdef CONFIG_ALL_UNICODE
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize,
countof(js_string_proto_normalize));
#endif
}
/* Math */
/* precondition: a and b are not NaN */
static double js_fmin(double a, double b)
{
if (a == 0 && b == 0) {
JSFloat64Union a1, b1;
a1.d = a;
b1.d = b;
a1.u64 |= b1.u64;
return a1.d;
} else {
return fmin(a, b);
}
}
/* precondition: a and b are not NaN */
static double js_fmax(double a, double b)
{
if (a == 0 && b == 0) {
JSFloat64Union a1, b1;
a1.d = a;
b1.d = b;
a1.u64 &= b1.u64;
return a1.d;
} else {
return fmax(a, b);
}
}
static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
BOOL is_max = magic;
double r, a;
int i;
uint32_t tag;
if (unlikely(argc == 0)) {
return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0);
}
tag = JS_VALUE_GET_TAG(argv[0]);
if (tag == JS_TAG_INT) {
int a1, r1 = JS_VALUE_GET_INT(argv[0]);
for(i = 1; i < argc; i++) {
tag = JS_VALUE_GET_TAG(argv[i]);
if (tag != JS_TAG_INT) {
r = r1;
goto generic_case;
}
a1 = JS_VALUE_GET_INT(argv[i]);
if (is_max)
r1 = max_int(r1, a1);
else
r1 = min_int(r1, a1);
}
return JS_NewInt32(ctx, r1);
} else {
if (JS_ToFloat64(ctx, &r, argv[0]))
return JS_EXCEPTION;
i = 1;
generic_case:
while (i < argc) {
if (JS_ToFloat64(ctx, &a, argv[i]))
return JS_EXCEPTION;
if (!isnan(r)) {
if (isnan(a)) {
r = a;
} else {
if (is_max)
r = js_fmax(r, a);
else
r = js_fmin(r, a);
}
}
i++;
}
return JS_NewFloat64(ctx, r);
}
}
static double js_math_sign(double a)
{
if (isnan(a) || a == 0.0)
return a;
if (a < 0)
return -1;
else
return 1;
}
static double js_math_round(double a)
{
JSFloat64Union u;
uint64_t frac_mask, one;
unsigned int e, s;
u.d = a;
e = (u.u64 >> 52) & 0x7ff;
if (e < 1023) {
/* abs(a) < 1 */
if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) {
/* abs(a) > 0.5 or a = 0.5: return +/-1.0 */
u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52);
} else {
/* return +/-0.0 */
u.u64 &= (uint64_t)1 << 63;
}
} else if (e < (1023 + 52)) {
s = u.u64 >> 63;
one = (uint64_t)1 << (52 - (e - 1023));
frac_mask = one - 1;
u.u64 += (one >> 1) - s;
u.u64 &= ~frac_mask; /* truncate to an integer */
}
/* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */
return u.d;
}
static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
double r, a;
int i;
r = 0;
if (argc > 0) {
if (JS_ToFloat64(ctx, &r, argv[0]))
return JS_EXCEPTION;
if (argc == 1) {
r = fabs(r);
} else {
/* use the built-in function to minimize precision loss */
for (i = 1; i < argc; i++) {
if (JS_ToFloat64(ctx, &a, argv[i]))
return JS_EXCEPTION;
r = hypot(r, a);
}
}
}
return JS_NewFloat64(ctx, r);
}
static double js_math_fround(double a)
{
return (float)a;
}
static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int a, b;
if (JS_ToInt32(ctx, &a, argv[0]))
return JS_EXCEPTION;
if (JS_ToInt32(ctx, &b, argv[1]))
return JS_EXCEPTION;
/* purposely ignoring overflow */
return JS_NewInt32(ctx, a * b);
}
static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
uint32_t a, r;
if (JS_ToUint32(ctx, &a, argv[0]))
return JS_EXCEPTION;
if (a == 0)
r = 32;
else
r = clz32(a);
return JS_NewInt32(ctx, r);
}
/* xorshift* random number generator by Marsaglia */
quickjs/quickjs.c view on Meta::CPAN
goto fail;
}
JS_FreeValue(ctx, key);
JS_FreeValue(ctx, value);
}
JS_FreeValue(ctx, ret);
JS_FreeValue(ctx, item);
}
JS_FreeValue(ctx, next_method);
JS_FreeValue(ctx, iter);
JS_FreeValue(ctx, adder);
}
return obj;
fail:
if (JS_IsObject(iter)) {
/* close the iterator object, preserving pending exception */
JS_IteratorClose(ctx, iter, TRUE);
}
JS_FreeValue(ctx, next_method);
JS_FreeValue(ctx, iter);
JS_FreeValue(ctx, adder);
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
/* XXX: could normalize strings to speed up comparison */
static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key)
{
uint32_t tag = JS_VALUE_GET_TAG(key);
/* convert -0.0 to +0.0 */
if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) {
key = JS_NewInt32(ctx, 0);
}
return key;
}
/* XXX: better hash ? */
static uint32_t map_hash_key(JSContext *ctx, JSValueConst key)
{
uint32_t tag = JS_VALUE_GET_NORM_TAG(key);
uint32_t h;
double d;
JSFloat64Union u;
switch(tag) {
case JS_TAG_BOOL:
h = JS_VALUE_GET_INT(key);
break;
case JS_TAG_STRING:
h = hash_string(JS_VALUE_GET_STRING(key), 0);
break;
case JS_TAG_OBJECT:
case JS_TAG_SYMBOL:
h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163;
break;
case JS_TAG_INT:
d = JS_VALUE_GET_INT(key) * 3163;
goto hash_float64;
case JS_TAG_FLOAT64:
d = JS_VALUE_GET_FLOAT64(key);
/* normalize the NaN */
if (isnan(d))
d = JS_FLOAT64_NAN;
hash_float64:
u.d = d;
h = (u.u32[0] ^ u.u32[1]) * 3163;
break;
default:
h = 0; /* XXX: bignum support */
break;
}
h ^= tag;
return h;
}
static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,
JSValueConst key)
{
struct list_head *el;
JSMapRecord *mr;
uint32_t h;
h = map_hash_key(ctx, key) & (s->hash_size - 1);
list_for_each(el, &s->hash_table[h]) {
mr = list_entry(el, JSMapRecord, hash_link);
if (js_same_value_zero(ctx, mr->key, key))
return mr;
}
return NULL;
}
static void map_hash_resize(JSContext *ctx, JSMapState *s)
{
uint32_t new_hash_size, i, h;
size_t slack;
struct list_head *new_hash_table, *el;
JSMapRecord *mr;
/* XXX: no reporting of memory allocation failure */
if (s->hash_size == 1)
new_hash_size = 4;
else
new_hash_size = s->hash_size * 2;
new_hash_table = js_realloc2(ctx, s->hash_table,
sizeof(new_hash_table[0]) * new_hash_size, &slack);
if (!new_hash_table)
return;
new_hash_size += slack / sizeof(*new_hash_table);
for(i = 0; i < new_hash_size; i++)
init_list_head(&new_hash_table[i]);
list_for_each(el, &s->records) {
mr = list_entry(el, JSMapRecord, link);
if (!mr->empty) {
h = map_hash_key(ctx, mr->key) & (new_hash_size - 1);
list_add_tail(&mr->hash_link, &new_hash_table[h]);
}
}
s->hash_table = new_hash_table;
s->hash_size = new_hash_size;
s->record_count_threshold = new_hash_size * 2;
quickjs/quickjs.c view on Meta::CPAN
int i, len, c;
str = JS_ToString(ctx, argv[0]);
if (JS_IsException(str))
return str;
p = JS_VALUE_GET_STRING(str);
string_buffer_init(ctx, b, p->len);
for (i = 0, len = p->len; i < len; i++) {
c = string_get(p, i);
if (isUnescaped(c)) {
string_buffer_putc16(b, c);
} else {
encodeURI_hex(b, c);
}
}
JS_FreeValue(ctx, str);
return string_buffer_end(b);
}
static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValue str;
StringBuffer b_s, *b = &b_s;
JSString *p;
int i, len, c, n;
str = JS_ToString(ctx, argv[0]);
if (JS_IsException(str))
return str;
string_buffer_init(ctx, b, 0);
p = JS_VALUE_GET_STRING(str);
for (i = 0, len = p->len; i < len; i++) {
c = string_get(p, i);
if (c == '%') {
if (i + 6 <= len
&& string_get(p, i + 1) == 'u'
&& (n = string_get_hex(p, i + 2, 4)) >= 0) {
c = n;
i += 6 - 1;
} else
if (i + 3 <= len
&& (n = string_get_hex(p, i + 1, 2)) >= 0) {
c = n;
i += 3 - 1;
}
}
string_buffer_putc16(b, c);
}
JS_FreeValue(ctx, str);
return string_buffer_end(b);
}
/* global object */
static const JSCFunctionListEntry js_global_funcs[] = {
JS_CFUNC_DEF("parseInt", 2, js_parseInt ),
JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ),
JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),
JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),
JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ),
JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ),
JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ),
JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ),
JS_CFUNC_DEF("escape", 1, js_global_escape ),
JS_CFUNC_DEF("unescape", 1, js_global_unescape ),
JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
JS_PROP_UNDEFINED_DEF("undefined", 0 ),
};
/* Date */
static int64_t math_mod(int64_t a, int64_t b) {
/* return positive modulo */
int64_t m = a % b;
return m + (m < 0) * b;
}
static int64_t floor_div(int64_t a, int64_t b) {
/* integer division rounding toward -Infinity */
int64_t m = a % b;
return (a - (m + (m < 0) * b)) / b;
}
static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv);
static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst 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_DATE && JS_IsNumber(p->u.object_data))
return JS_ToFloat64(ctx, valp, p->u.object_data);
}
JS_ThrowTypeError(ctx, "not a Date object");
return -1;
}
static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v)
{
if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
JSObject *p = JS_VALUE_GET_OBJ(this_val);
if (p->class_id == JS_CLASS_DATE) {
JS_FreeValue(ctx, p->u.object_data);
p->u.object_data = JS_NewFloat64(ctx, v);
return JS_DupValue(ctx, p->u.object_data);
}
}
return JS_ThrowTypeError(ctx, "not a Date object");
}
static int64_t days_from_year(int64_t y) {
return 365 * (y - 1970) + floor_div(y - 1969, 4) -
floor_div(y - 1901, 100) + floor_div(y - 1601, 400);
}
static int64_t days_in_year(int64_t y) {
return 365 + !(y % 4) - !(y % 100) + !(y % 400);
}
/* return the year, update days */
static int64_t year_from_days(int64_t *days) {
int64_t y, d1, nd, d = *days;
y = floor_div(d * 10000, 3652425) + 1970;
/* the initial approximation is very good, so only a few
iterations are necessary */
for(;;) {
d1 = d - days_from_year(y);
if (d1 < 0) {
y--;
d1 += days_in_year(y);
} else {
nd = days_in_year(y);
if (d1 < nd)
break;
d1 -= nd;
y++;
}
}
*days = d1;
return y;
}
static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
static char const day_names[] = "SunMonTueWedThuFriSat";
static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
double fields[9], int is_local, int force)
{
double dval;
int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;
if (JS_ThisTimeValue(ctx, &dval, obj))
return -1;
if (isnan(dval)) {
if (!force)
return FALSE; /* NaN */
d = 0; /* initialize all fields to 0 */
} else {
d = dval;
if (is_local) {
tz = -getTimezoneOffset(d);
d += tz * 60000;
}
}
/* result is >= 0, we can use % */
h = math_mod(d, 86400000);
days = (d - h) / 86400000;
ms = h % 1000;
h = (h - ms) / 1000;
s = h % 60;
h = (h - s) / 60;
m = h % 60;
h = (h - m) / 60;
wd = math_mod(days + 4, 7); /* week day */
y = year_from_days(&days);
for(i = 0; i < 11; i++) {
md = month_days[i];
if (i == 1)
md += days_in_year(y) - 365;
if (days < md)
break;
days -= md;
}
fields[0] = y;
fields[1] = i;
fields[2] = days + 1;
fields[3] = h;
fields[4] = m;
fields[5] = s;
fields[6] = ms;
fields[7] = wd;
fields[8] = tz;
return TRUE;
}
static double time_clip(double t) {
if (t >= -8.64e15 && t <= 8.64e15)
return trunc(t) + 0.0; /* convert -0 to +0 */
else
return NAN;
}
/* The spec mandates the use of 'double' and it fixes the order
of the operations */
static double set_date_fields(double fields[], int is_local) {
int64_t y;
double days, d, h, m1;
int i, m, md;
m1 = fields[1];
m = fmod(m1, 12);
if (m < 0)
m += 12;
y = (int64_t)(fields[0] + floor(m1 / 12));
quickjs/quickjs.c view on Meta::CPAN
return JS_NewFloat64(ctx, fields[n]);
}
static JSValue set_date_field(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
// _field(obj, first_field, end_field, args, is_local)
double fields[9];
int res, first_field, end_field, is_local, i, n;
double d, a;
d = NAN;
first_field = (magic >> 8) & 0x0F;
end_field = (magic >> 4) & 0x0F;
is_local = magic & 0x0F;
res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0);
if (res < 0)
return JS_EXCEPTION;
// Argument coercion is observable and must be done unconditionally.
n = min_int(argc, end_field - first_field);
for(i = 0; i < n; i++) {
if (JS_ToFloat64(ctx, &a, argv[i]))
return JS_EXCEPTION;
if (!isfinite(a))
res = FALSE;
fields[first_field + i] = trunc(a);
}
if (res && argc > 0) {
d = set_date_fields(fields, is_local);
}
return JS_SetThisTimeValue(ctx, this_val, d);
}
/* fmt:
0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT"
1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)"
2: toISOString: "2018-01-02T23:02:56.927Z"
3: toLocaleString: "1/2/2018, 11:40:40 PM"
part: 1=date, 2=time 3=all
XXX: should use a variant of strftime().
*/
static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
// _string(obj, fmt, part)
char buf[64];
double fields[9];
int res, fmt, part, pos;
int y, mon, d, h, m, s, ms, wd, tz;
fmt = (magic >> 4) & 0x0F;
part = magic & 0x0F;
res = get_date_fields(ctx, this_val, fields, fmt & 1, 0);
if (res < 0)
return JS_EXCEPTION;
if (!res) {
if (fmt == 2)
return JS_ThrowRangeError(ctx, "Date value is NaN");
else
return JS_NewString(ctx, "Invalid Date");
}
y = fields[0];
mon = fields[1];
d = fields[2];
h = fields[3];
m = fields[4];
s = fields[5];
ms = fields[6];
wd = fields[7];
tz = fields[8];
pos = 0;
if (part & 1) { /* date part */
switch(fmt) {
case 0:
pos += snprintf(buf + pos, sizeof(buf) - pos,
"%.3s, %02d %.3s %0*d ",
day_names + wd * 3, d,
month_names + mon * 3, 4 + (y < 0), y);
break;
case 1:
pos += snprintf(buf + pos, sizeof(buf) - pos,
"%.3s %.3s %02d %0*d",
day_names + wd * 3,
month_names + mon * 3, d, 4 + (y < 0), y);
if (part == 3) {
buf[pos++] = ' ';
}
break;
case 2:
if (y >= 0 && y <= 9999) {
pos += snprintf(buf + pos, sizeof(buf) - pos,
"%04d", y);
} else {
pos += snprintf(buf + pos, sizeof(buf) - pos,
"%+07d", y);
}
pos += snprintf(buf + pos, sizeof(buf) - pos,
"-%02d-%02dT", mon + 1, d);
break;
case 3:
pos += snprintf(buf + pos, sizeof(buf) - pos,
"%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y);
if (part == 3) {
buf[pos++] = ',';
buf[pos++] = ' ';
}
break;
}
}
if (part & 2) { /* time part */
switch(fmt) {
case 0:
pos += snprintf(buf + pos, sizeof(buf) - pos,
"%02d:%02d:%02d GMT", h, m, s);
break;
quickjs/quickjs.c view on Meta::CPAN
return JS_EXCEPTION;
}
static const JSCFunctionListEntry js_operators_funcs[] = {
JS_CFUNC_DEF("create", 1, js_operators_create ),
JS_CFUNC_DEF("updateBigIntOperators", 2, js_operators_updateBigIntOperators ),
};
/* must be called after all overloadable base types are initialized */
void JS_AddIntrinsicOperators(JSContext *ctx)
{
JSValue obj;
ctx->allow_operator_overloading = TRUE;
obj = JS_NewCFunction(ctx, js_global_operators, "Operators", 1);
JS_SetPropertyFunctionList(ctx, obj,
js_operators_funcs,
countof(js_operators_funcs));
JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_Operators,
obj,
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
/* add default operatorSets */
js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BOOLEAN]);
js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_NUMBER]);
js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_STRING]);
js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_INT]);
js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]);
js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
}
#endif /* CONFIG_BIGNUM */
/* BigInt */
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:
#ifdef CONFIG_BIGNUM
case JS_TAG_BIG_FLOAT:
#endif
{
bf_t *a, a_s;
a = JS_ToBigFloat(ctx, &a_s, val);
if (!a) {
JS_FreeValue(ctx, val);
return JS_EXCEPTION;
}
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;
#ifdef CONFIG_BIGNUM
case JS_TAG_BIG_DECIMAL:
val = JS_ToStringFree(ctx, val);
if (JS_IsException(val))
break;
goto redo;
#endif
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)
quickjs/quickjs.c view on Meta::CPAN
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));
}
enum {
MATH_OP_ABS,
MATH_OP_FLOOR,
MATH_OP_CEIL,
MATH_OP_ROUND,
MATH_OP_TRUNC,
MATH_OP_SQRT,
MATH_OP_FPROUND,
MATH_OP_ACOS,
MATH_OP_ASIN,
MATH_OP_ATAN,
MATH_OP_ATAN2,
MATH_OP_COS,
MATH_OP_EXP,
MATH_OP_LOG,
MATH_OP_POW,
MATH_OP_SIN,
MATH_OP_TAN,
MATH_OP_FMOD,
MATH_OP_REM,
MATH_OP_SIGN,
MATH_OP_ADD,
MATH_OP_SUB,
MATH_OP_MUL,
MATH_OP_DIV,
};
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);
if (!a) {
JS_FreeValue(ctx, op1);
return JS_EXCEPTION;
}
fe = &ctx->fp_env;
if (argc > 1) {
fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV);
if (!fe)
goto fail;
quickjs/quickjs.c view on Meta::CPAN
}
res = JS_NewBigFloat(ctx);
if (JS_IsException(res)) {
fail:
if (b == &b_s)
bf_delete(b);
fail2:
if (a == &a_s)
bf_delete(a);
fail1:
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 ),
};
/* FloatEnv */
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;
quickjs/quickjs.c view on Meta::CPAN
scan8:
pv = p->u.array.u.uint8_ptr;
v = v64;
if (inc > 0) {
pp = memchr(pv + k, v, len - k);
if (pp)
res = pp - pv;
} else {
for (; k != stop; k += inc) {
if (pv[k] == v) {
res = k;
break;
}
}
}
}
break;
case JS_CLASS_INT16_ARRAY:
if (is_int && (int16_t)v64 == v64)
goto scan16;
break;
case JS_CLASS_UINT16_ARRAY:
if (is_int && (uint16_t)v64 == v64) {
const uint16_t *pv;
uint16_t v;
scan16:
pv = p->u.array.u.uint16_ptr;
v = v64;
for (; k != stop; k += inc) {
if (pv[k] == v) {
res = k;
break;
}
}
}
break;
case JS_CLASS_INT32_ARRAY:
if (is_int && (int32_t)v64 == v64)
goto scan32;
break;
case JS_CLASS_UINT32_ARRAY:
if (is_int && (uint32_t)v64 == v64) {
const uint32_t *pv;
uint32_t v;
scan32:
pv = p->u.array.u.uint32_ptr;
v = v64;
for (; k != stop; k += inc) {
if (pv[k] == v) {
res = k;
break;
}
}
}
break;
case JS_CLASS_FLOAT32_ARRAY:
if (is_bigint)
break;
if (isnan(d)) {
const float *pv = p->u.array.u.float_ptr;
/* special case: indexOf returns -1, includes finds NaN */
if (special != special_includes)
goto done;
for (; k != stop; k += inc) {
if (isnan(pv[k])) {
res = k;
break;
}
}
} else if ((f = (float)d) == d) {
const float *pv = p->u.array.u.float_ptr;
for (; k != stop; k += inc) {
if (pv[k] == f) {
res = k;
break;
}
}
}
break;
case JS_CLASS_FLOAT64_ARRAY:
if (is_bigint)
break;
if (isnan(d)) {
const double *pv = p->u.array.u.double_ptr;
/* special case: indexOf returns -1, includes finds NaN */
if (special != special_includes)
goto done;
for (; k != stop; k += inc) {
if (isnan(pv[k])) {
res = k;
break;
}
}
} else {
const double *pv = p->u.array.u.double_ptr;
for (; k != stop; k += inc) {
if (pv[k] == d) {
res = k;
break;
}
}
}
break;
case JS_CLASS_BIG_INT64_ARRAY:
if (is_bigint || (is_math_mode(ctx) && is_int &&
v64 >= -MAX_SAFE_INTEGER &&
v64 <= MAX_SAFE_INTEGER)) {
goto scan64;
}
break;
case JS_CLASS_BIG_UINT64_ARRAY:
if (is_bigint || (is_math_mode(ctx) && is_int &&
v64 >= 0 && v64 <= MAX_SAFE_INTEGER)) {
const uint64_t *pv;
uint64_t v;
scan64:
pv = p->u.array.u.uint64_ptr;
v = v64;
for (; k != stop; k += inc) {
if (pv[k] == v) {
res = k;
break;
}
}
}
break;
}
done:
if (special == special_includes)
return JS_NewBool(ctx, res >= 0);
else
return JS_NewInt32(ctx, res);
exception:
return JS_EXCEPTION;
}
static JSValue js_typed_array_join(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int toLocaleString)
{
JSValue sep = JS_UNDEFINED, el;
StringBuffer b_s, *b = &b_s;
JSString *p = NULL;
int i, n;