/* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include typedef enum { NJS_MATH_ABS, NJS_MATH_ACOS, NJS_MATH_ACOSH, NJS_MATH_ASIN, NJS_MATH_ASINH, NJS_MATH_ATAN, NJS_MATH_ATAN2, NJS_MATH_ATANH, NJS_MATH_CBRT, NJS_MATH_CEIL, NJS_MATH_CLZ32, NJS_MATH_COS, NJS_MATH_COSH, NJS_MATH_EXP, NJS_MATH_EXPM1, NJS_MATH_FLOOR, NJS_MATH_FROUND, NJS_MATH_IMUL, NJS_MATH_LOG, NJS_MATH_LOG10, NJS_MATH_LOG1P, NJS_MATH_LOG2, NJS_MATH_POW, NJS_MATH_ROUND, NJS_MATH_SIGN, NJS_MATH_SIN, NJS_MATH_SINH, NJS_MATH_SQRT, NJS_MATH_TAN, NJS_MATH_TANH, NJS_MATH_TRUNC, } njs_math_func_t; static njs_int_t njs_object_math_func(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { double num, num2; uint8_t sign; uint32_t u32; uint64_t one, fraction_mask; njs_int_t ret, ep; njs_math_func_t func; njs_diyfp_conv_t conv; func = magic; ret = njs_value_to_number(vm, njs_arg(args, nargs, 1), &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } switch (func) { case NJS_MATH_ABS: num = fabs(num); break; case NJS_MATH_ACOS: #if (NJS_SOLARIS) /* On Solaris acos(x) returns 0 for x > 1. */ if (fabs(num) > 1.0) { num = NAN; } #endif num = acos(num); break; case NJS_MATH_ACOSH: num = acosh(num); break; case NJS_MATH_ASIN: #if (NJS_SOLARIS) /* On Solaris asin(x) returns 0 for x > 1. */ if (fabs(num) > 1.0) { num = NAN; } #endif num = asin(num); break; case NJS_MATH_ASINH: num = asinh(num); break; case NJS_MATH_ATAN: num = atan(num); break; case NJS_MATH_ATANH: num = atanh(num); break; case NJS_MATH_CBRT: num = cbrt(num); break; case NJS_MATH_CEIL: num = ceil(num); break; case NJS_MATH_CLZ32: u32 = njs_number_to_uint32(num); num = njs_leading_zeros(u32); break; case NJS_MATH_COS: num = cos(num); break; case NJS_MATH_COSH: num = cosh(num); break; case NJS_MATH_EXP: num = exp(num); break; case NJS_MATH_EXPM1: num = expm1(num); break; case NJS_MATH_FLOOR: num = floor(num); break; case NJS_MATH_FROUND: num = (float) num; break; case NJS_MATH_LOG: num = log(num); break; case NJS_MATH_LOG10: num = log10(num); break; case NJS_MATH_LOG1P: num = log1p(num); break; case NJS_MATH_LOG2: #if (NJS_SOLARIS) /* On Solaris 10 log(-1) returns -Infinity. */ if (num < 0) { num = NAN; } #endif num = log2(num); break; case NJS_MATH_SIGN: if (!isnan(num) && num != 0) { num = signbit(num) ? -1 : 1; } break; case NJS_MATH_SIN: num = sin(num); break; case NJS_MATH_SINH: num = sinh(num); break; case NJS_MATH_SQRT: num = sqrt(num); break; case NJS_MATH_ROUND: conv.d = num; ep = (conv.u64 & NJS_DBL_EXPONENT_MASK) >> NJS_DBL_SIGNIFICAND_SIZE; if (ep < NJS_DBL_EXPONENT_OFFSET) { /* |v| < 1. */ if (ep == (NJS_DBL_EXPONENT_OFFSET - 1) && conv.u64 != njs_uint64(0xbfe00000, 0x00000000)) { /* (|v| > 0.5 || v == 0.5) => +-1.0 */ conv.u64 = conv.u64 & NJS_DBL_SIGN_MASK; conv.u64 |= NJS_DBL_EXPONENT_OFFSET << NJS_DBL_SIGNIFICAND_SIZE; } else { /* (|v| < 0.5 || v == -0.5) => +-0. */ conv.u64 &= ((uint64_t) 1) << 63; } } else if (ep < NJS_DBL_EXPONENT_BIAS) { /* |v| <= 2^52 - 1 (largest safe integer). */ one = ((uint64_t) 1) << (NJS_DBL_EXPONENT_BIAS - ep); fraction_mask = one - 1; /* truncation. */ sign = conv.u64 >> 63; conv.u64 += (one >> 1) - sign; conv.u64 &= ~fraction_mask; } num = conv.d; break; case NJS_MATH_TAN: num = tan(num); break; case NJS_MATH_TANH: num = tanh(num); break; case NJS_MATH_TRUNC: num = trunc(num); break; default: ret = njs_value_to_number(vm, njs_arg(args, nargs, 2), &num2); if (njs_slow_path(ret != NJS_OK)) { return ret; } switch (func) { case NJS_MATH_ATAN2: num = atan2(num, num2); break; case NJS_MATH_IMUL: u32 = njs_number_to_uint32(num); num = (int32_t) (u32 * njs_number_to_uint32(num2)); break; default: /* * According to ECMA-262: * 1. If exponent is NaN, the result should be NaN; * 2. The result of Math.pow(+/-1, +/-Infinity) should be NaN. */ if (fabs(num) != 1 || (!isnan(num2) && !isinf(num2))) { num = pow(num, num2); } else { num = NAN; } } } njs_set_number(retval, num); return NJS_OK; } static njs_int_t njs_object_math_hypot(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; njs_int_t ret; njs_uint_t i; ret = njs_value_to_number(vm, njs_arg(args, nargs, 1), &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } num = (nargs > 1) ? fabs(num) : 0; for (i = 2; i < nargs; i++) { ret = njs_value_to_numeric(vm, &args[i], &args[i]); if (njs_slow_path(ret != NJS_OK)) { return ret; } num = hypot(num, njs_number(&args[i])); if (njs_slow_path(isinf(num))) { break; } } njs_set_number(retval, num); return NJS_OK; } njs_inline double njs_fmax(double x, double y) { if (x == 0 && y == 0) { return signbit(x) ? y : x; } return fmax(x, y); } njs_inline double njs_fmin(double x, double y) { if (x == 0 && y == 0) { return signbit(x) ? x : y; } return fmin(x, y); } static njs_int_t njs_object_math_min_max(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t max, njs_value_t *retval) { double num, value; njs_int_t ret; njs_uint_t i; value = max ? -INFINITY : INFINITY; for (i = 1; i < nargs; i++) { ret = njs_value_to_number(vm, &args[i], &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_slow_path(isnan(num))) { value = num; break; } value = max ? njs_fmax(value, num) : njs_fmin(value, num); } njs_set_number(retval, value); return NJS_OK; } static njs_int_t njs_object_math_random(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; num = njs_random(&vm->random) / 4294967296.0; njs_set_number(retval, num); return NJS_OK; } static const njs_object_prop_init_t njs_math_object_properties[] = { NJS_DECLARE_PROP_VALUE(SYMBOL_toStringTag, njs_ascii_strval("Math"), NJS_OBJECT_PROP_VALUE_C), NJS_DECLARE_PROP_VALUE(STRING_E, njs_value(NJS_NUMBER, 1, M_E), 0), NJS_DECLARE_PROP_VALUE(STRING_LN10, njs_value(NJS_NUMBER, 1, M_LN10), 0), NJS_DECLARE_PROP_VALUE(STRING_LN2, njs_value(NJS_NUMBER, 1, M_LN2), 0), NJS_DECLARE_PROP_VALUE(STRING_LOG10E, njs_value(NJS_NUMBER, 1, M_LOG10E), 0), NJS_DECLARE_PROP_VALUE(STRING_LOG2E, njs_value(NJS_NUMBER, 1, M_LOG2E), 0), NJS_DECLARE_PROP_VALUE(STRING_PI, njs_value(NJS_NUMBER, 1, M_PI), 0), NJS_DECLARE_PROP_VALUE(STRING_SQRT1_2, njs_value(NJS_NUMBER, 1, M_SQRT1_2), 0), NJS_DECLARE_PROP_VALUE(STRING_SQRT2, njs_value(NJS_NUMBER, 1, M_SQRT2), 0), NJS_DECLARE_PROP_NATIVE(STRING_abs, njs_object_math_func, 1, NJS_MATH_ABS), NJS_DECLARE_PROP_NATIVE(STRING_acos, njs_object_math_func, 1, NJS_MATH_ACOS), NJS_DECLARE_PROP_NATIVE(STRING_acosh, njs_object_math_func, 1, NJS_MATH_ACOSH), NJS_DECLARE_PROP_NATIVE(STRING_asin, njs_object_math_func, 1, NJS_MATH_ASIN), NJS_DECLARE_PROP_NATIVE(STRING_asinh, njs_object_math_func, 1, NJS_MATH_ASINH), NJS_DECLARE_PROP_NATIVE(STRING_atan, njs_object_math_func, 1, NJS_MATH_ATAN), NJS_DECLARE_PROP_NATIVE(STRING_atan2, njs_object_math_func, 2, NJS_MATH_ATAN2), NJS_DECLARE_PROP_NATIVE(STRING_atanh, njs_object_math_func, 1, NJS_MATH_ATANH), NJS_DECLARE_PROP_NATIVE(STRING_cbrt, njs_object_math_func, 1, NJS_MATH_CBRT), NJS_DECLARE_PROP_NATIVE(STRING_ceil, njs_object_math_func, 1, NJS_MATH_CEIL), NJS_DECLARE_PROP_NATIVE(STRING_clz32, njs_object_math_func, 1, NJS_MATH_CLZ32), NJS_DECLARE_PROP_NATIVE(STRING_cos, njs_object_math_func, 1, NJS_MATH_COS), NJS_DECLARE_PROP_NATIVE(STRING_cosh, njs_object_math_func, 1, NJS_MATH_COSH), NJS_DECLARE_PROP_NATIVE(STRING_exp, njs_object_math_func, 1, NJS_MATH_EXP), NJS_DECLARE_PROP_NATIVE(STRING_expm1, njs_object_math_func, 1, NJS_MATH_EXPM1), NJS_DECLARE_PROP_NATIVE(STRING_floor, njs_object_math_func, 1, NJS_MATH_FLOOR), NJS_DECLARE_PROP_NATIVE(STRING_fround, njs_object_math_func, 1, NJS_MATH_FROUND), NJS_DECLARE_PROP_NATIVE(STRING_hypot, njs_object_math_hypot, 2, 0), NJS_DECLARE_PROP_NATIVE(STRING_imul, njs_object_math_func, 2, NJS_MATH_IMUL), NJS_DECLARE_PROP_NATIVE(STRING_log, njs_object_math_func, 1, NJS_MATH_LOG), NJS_DECLARE_PROP_NATIVE(STRING_log10, njs_object_math_func, 1, NJS_MATH_LOG10), NJS_DECLARE_PROP_NATIVE(STRING_log1p, njs_object_math_func, 1, NJS_MATH_LOG1P), NJS_DECLARE_PROP_NATIVE(STRING_log2, njs_object_math_func, 1, NJS_MATH_LOG2), NJS_DECLARE_PROP_NATIVE(STRING_max, njs_object_math_min_max, 2, 1), NJS_DECLARE_PROP_NATIVE(STRING_min, njs_object_math_min_max, 2, 0), NJS_DECLARE_PROP_NATIVE(STRING_pow, njs_object_math_func, 2, NJS_MATH_POW), NJS_DECLARE_PROP_NATIVE(STRING_random, njs_object_math_random, 0, 0), NJS_DECLARE_PROP_NATIVE(STRING_round, njs_object_math_func, 1, NJS_MATH_ROUND), NJS_DECLARE_PROP_NATIVE(STRING_sign, njs_object_math_func, 1, NJS_MATH_SIGN), NJS_DECLARE_PROP_NATIVE(STRING_sin, njs_object_math_func, 1, NJS_MATH_SIN), NJS_DECLARE_PROP_NATIVE(STRING_sinh, njs_object_math_func, 1, NJS_MATH_SINH), NJS_DECLARE_PROP_NATIVE(STRING_sqrt, njs_object_math_func, 1, NJS_MATH_SQRT), NJS_DECLARE_PROP_NATIVE(STRING_tan, njs_object_math_func, 1, NJS_MATH_TAN), NJS_DECLARE_PROP_NATIVE(STRING_tanh, njs_object_math_func, 1, NJS_MATH_TANH), NJS_DECLARE_PROP_NATIVE(STRING_trunc, njs_object_math_func, 1, NJS_MATH_TRUNC), }; const njs_object_init_t njs_math_object_init = { njs_math_object_properties, njs_nitems(njs_math_object_properties), };