/* * QuickJS Javascript Engine * * Copyright (c) 2017-2021 Fabrice Bellard * Copyright (c) 2017-2021 Charlie Gordon * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "libc/calls/struct/timeval.h" #include "libc/time/time.h" #include "third_party/quickjs/internal.h" asm(".ident\t\"\\n\\n\ QuickJS (MIT License)\\n\ Copyright (c) 2017-2021 Fabrice Bellard\\n\ Copyright (c) 2017-2021 Charlie Gordon\""); asm(".include \"libc/disclaimer.inc\""); /* clang-format off */ /* 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 */ static uint64_t xorshift64star(uint64_t *pstate) { uint64_t x; x = *pstate; x ^= x >> 12; x ^= x << 25; x ^= x >> 27; *pstate = x; return x * 0x2545F4914F6CDD1D; } static void js_random_init(JSContext *ctx) { struct timeval tv; gettimeofday(&tv, NULL); ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec; /* the state must be non zero */ if (ctx->random_state == 0) ctx->random_state = 1; } static JSValue js_math_random(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSFloat64Union u; uint64_t v; v = xorshift64star(&ctx->random_state); /* 1.0 <= u.d < 2 */ u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12); return __JS_NewFloat64(ctx, u.d - 1.0); } 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); } } static const JSCFunctionListEntry js_math_funcs[] = { JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ), JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ), JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ), JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ), JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ), JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ), JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ), JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ), JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ), JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ), JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ), JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ), JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ), JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ), JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ), JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ), JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ), /* ES6 */ JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ), JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ), JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ), JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ), JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ), JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ), JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ), JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ), JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ), JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ), JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ), JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ), JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ), JS_CFUNC_DEF("hypot", 2, js_math_hypot ), JS_CFUNC_DEF("random", 0, js_math_random ), JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ), JS_CFUNC_DEF("imul", 2, js_math_imul ), JS_CFUNC_DEF("clz32", 1, js_math_clz32 ), JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ), JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ), JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ), JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ), JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ), JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ), JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ), JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ), JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ), }; static const JSCFunctionListEntry js_math_obj[] = { JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), }; void JS_AddIntrinsicMath(JSContext *ctx) { /* Math: create as autoinit object */ js_random_init(ctx); JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj)); }