mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
305 lines
9.4 KiB
C
305 lines
9.4 KiB
C
|
/*
|
||
|
* QuickJS Javascript Engine
|
||
|
*
|
||
|
* Copyright (c) 2017-2021 Fabrice Bellard
|
||
|
* Copyright (c) 2017-2021 Charlie Gordon
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
* of this software and associated documentation files (the "Software"), to deal
|
||
|
* in the Software without restriction, including without limitation the rights
|
||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
* copies of the Software, and to permit persons to whom the Software is
|
||
|
* furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in
|
||
|
* all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
* THE SOFTWARE.
|
||
|
*/
|
||
|
#include "libc/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));
|
||
|
}
|