github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/builtin_math.go (about)

     1  package goja
     2  
     3  import (
     4  	"math"
     5  	"math/bits"
     6  )
     7  
     8  func (r *Runtime) math_abs(call FunctionCall) Value {
     9  	return floatToValue(math.Abs(call.Argument(0).ToFloat()))
    10  }
    11  
    12  func (r *Runtime) math_acos(call FunctionCall) Value {
    13  	return floatToValue(math.Acos(call.Argument(0).ToFloat()))
    14  }
    15  
    16  func (r *Runtime) math_acosh(call FunctionCall) Value {
    17  	return floatToValue(math.Acosh(call.Argument(0).ToFloat()))
    18  }
    19  
    20  func (r *Runtime) math_asin(call FunctionCall) Value {
    21  	return floatToValue(math.Asin(call.Argument(0).ToFloat()))
    22  }
    23  
    24  func (r *Runtime) math_asinh(call FunctionCall) Value {
    25  	return floatToValue(math.Asinh(call.Argument(0).ToFloat()))
    26  }
    27  
    28  func (r *Runtime) math_atan(call FunctionCall) Value {
    29  	return floatToValue(math.Atan(call.Argument(0).ToFloat()))
    30  }
    31  
    32  func (r *Runtime) math_atanh(call FunctionCall) Value {
    33  	return floatToValue(math.Atanh(call.Argument(0).ToFloat()))
    34  }
    35  
    36  func (r *Runtime) math_atan2(call FunctionCall) Value {
    37  	y := call.Argument(0).ToFloat()
    38  	x := call.Argument(1).ToFloat()
    39  
    40  	return floatToValue(math.Atan2(y, x))
    41  }
    42  
    43  func (r *Runtime) math_cbrt(call FunctionCall) Value {
    44  	return floatToValue(math.Cbrt(call.Argument(0).ToFloat()))
    45  }
    46  
    47  func (r *Runtime) math_ceil(call FunctionCall) Value {
    48  	return floatToValue(math.Ceil(call.Argument(0).ToFloat()))
    49  }
    50  
    51  func (r *Runtime) math_clz32(call FunctionCall) Value {
    52  	return intToValue(int64(bits.LeadingZeros32(toUint32(call.Argument(0)))))
    53  }
    54  
    55  func (r *Runtime) math_cos(call FunctionCall) Value {
    56  	return floatToValue(math.Cos(call.Argument(0).ToFloat()))
    57  }
    58  
    59  func (r *Runtime) math_cosh(call FunctionCall) Value {
    60  	return floatToValue(math.Cosh(call.Argument(0).ToFloat()))
    61  }
    62  
    63  func (r *Runtime) math_exp(call FunctionCall) Value {
    64  	return floatToValue(math.Exp(call.Argument(0).ToFloat()))
    65  }
    66  
    67  func (r *Runtime) math_expm1(call FunctionCall) Value {
    68  	return floatToValue(math.Expm1(call.Argument(0).ToFloat()))
    69  }
    70  
    71  func (r *Runtime) math_floor(call FunctionCall) Value {
    72  	return floatToValue(math.Floor(call.Argument(0).ToFloat()))
    73  }
    74  
    75  func (r *Runtime) math_fround(call FunctionCall) Value {
    76  	return floatToValue(float64(float32(call.Argument(0).ToFloat())))
    77  }
    78  
    79  func (r *Runtime) math_hypot(call FunctionCall) Value {
    80  	var max float64
    81  	var hasNaN bool
    82  	absValues := make([]float64, 0, len(call.Arguments))
    83  	for _, v := range call.Arguments {
    84  		arg := nilSafe(v).ToFloat()
    85  		if math.IsNaN(arg) {
    86  			hasNaN = true
    87  		} else {
    88  			abs := math.Abs(arg)
    89  			if abs > max {
    90  				max = abs
    91  			}
    92  			absValues = append(absValues, abs)
    93  		}
    94  	}
    95  	if math.IsInf(max, 1) {
    96  		return _positiveInf
    97  	}
    98  	if hasNaN {
    99  		return _NaN
   100  	}
   101  	if max == 0 {
   102  		return _positiveZero
   103  	}
   104  
   105  	// Kahan summation to avoid rounding errors.
   106  	// Normalize the numbers to the largest one to avoid overflow.
   107  	var sum, compensation float64
   108  	for _, n := range absValues {
   109  		n /= max
   110  		summand := n*n - compensation
   111  		preliminary := sum + summand
   112  		compensation = (preliminary - sum) - summand
   113  		sum = preliminary
   114  	}
   115  	return floatToValue(math.Sqrt(sum) * max)
   116  }
   117  
   118  func (r *Runtime) math_imul(call FunctionCall) Value {
   119  	x := toUint32(call.Argument(0))
   120  	y := toUint32(call.Argument(1))
   121  	return intToValue(int64(int32(x * y)))
   122  }
   123  
   124  func (r *Runtime) math_log(call FunctionCall) Value {
   125  	return floatToValue(math.Log(call.Argument(0).ToFloat()))
   126  }
   127  
   128  func (r *Runtime) math_log1p(call FunctionCall) Value {
   129  	return floatToValue(math.Log1p(call.Argument(0).ToFloat()))
   130  }
   131  
   132  func (r *Runtime) math_log10(call FunctionCall) Value {
   133  	return floatToValue(math.Log10(call.Argument(0).ToFloat()))
   134  }
   135  
   136  func (r *Runtime) math_log2(call FunctionCall) Value {
   137  	return floatToValue(math.Log2(call.Argument(0).ToFloat()))
   138  }
   139  
   140  func (r *Runtime) math_max(call FunctionCall) Value {
   141  	result := math.Inf(-1)
   142  	args := call.Arguments
   143  	for i, arg := range args {
   144  		n := nilSafe(arg).ToFloat()
   145  		if math.IsNaN(n) {
   146  			args = args[i+1:]
   147  			goto NaNLoop
   148  		}
   149  		result = math.Max(result, n)
   150  	}
   151  
   152  	return floatToValue(result)
   153  
   154  NaNLoop:
   155  	// All arguments still need to be coerced to number according to the specs.
   156  	for _, arg := range args {
   157  		nilSafe(arg).ToFloat()
   158  	}
   159  	return _NaN
   160  }
   161  
   162  func (r *Runtime) math_min(call FunctionCall) Value {
   163  	result := math.Inf(1)
   164  	args := call.Arguments
   165  	for i, arg := range args {
   166  		n := nilSafe(arg).ToFloat()
   167  		if math.IsNaN(n) {
   168  			args = args[i+1:]
   169  			goto NaNLoop
   170  		}
   171  		result = math.Min(result, n)
   172  	}
   173  
   174  	return floatToValue(result)
   175  
   176  NaNLoop:
   177  	// All arguments still need to be coerced to number according to the specs.
   178  	for _, arg := range args {
   179  		nilSafe(arg).ToFloat()
   180  	}
   181  	return _NaN
   182  }
   183  
   184  func pow(x, y Value) Value {
   185  	if x, ok := x.(valueInt); ok {
   186  		if y, ok := y.(valueInt); ok && y >= 0 {
   187  			if y == 0 {
   188  				return intToValue(1)
   189  			}
   190  			if x == 0 {
   191  				return intToValue(0)
   192  			}
   193  			ip := ipow(int64(x), int64(y))
   194  			if ip != 0 {
   195  				return intToValue(ip)
   196  			}
   197  		}
   198  	}
   199  	xf := x.ToFloat()
   200  	yf := y.ToFloat()
   201  	if math.Abs(xf) == 1 && math.IsInf(yf, 0) {
   202  		return _NaN
   203  	}
   204  	if xf == 1 && math.IsNaN(yf) {
   205  		return _NaN
   206  	}
   207  	return floatToValue(math.Pow(xf, yf))
   208  }
   209  
   210  func (r *Runtime) math_pow(call FunctionCall) Value {
   211  	return pow(call.Argument(0), call.Argument(1))
   212  }
   213  
   214  func (r *Runtime) math_random(call FunctionCall) Value {
   215  	return floatToValue(r.rand())
   216  }
   217  
   218  func (r *Runtime) math_round(call FunctionCall) Value {
   219  	f := call.Argument(0).ToFloat()
   220  	if math.IsNaN(f) {
   221  		return _NaN
   222  	}
   223  
   224  	if f == 0 && math.Signbit(f) {
   225  		return _negativeZero
   226  	}
   227  
   228  	t := math.Trunc(f)
   229  
   230  	if f >= 0 {
   231  		if f-t >= 0.5 {
   232  			return floatToValue(t + 1)
   233  		}
   234  	} else {
   235  		if t-f > 0.5 {
   236  			return floatToValue(t - 1)
   237  		}
   238  	}
   239  
   240  	return floatToValue(t)
   241  }
   242  
   243  func (r *Runtime) math_sign(call FunctionCall) Value {
   244  	arg := call.Argument(0)
   245  	num := arg.ToFloat()
   246  	if math.IsNaN(num) || num == 0 { // this will match -0 too
   247  		return arg
   248  	}
   249  	if num > 0 {
   250  		return intToValue(1)
   251  	}
   252  	return intToValue(-1)
   253  }
   254  
   255  func (r *Runtime) math_sin(call FunctionCall) Value {
   256  	return floatToValue(math.Sin(call.Argument(0).ToFloat()))
   257  }
   258  
   259  func (r *Runtime) math_sinh(call FunctionCall) Value {
   260  	return floatToValue(math.Sinh(call.Argument(0).ToFloat()))
   261  }
   262  
   263  func (r *Runtime) math_sqrt(call FunctionCall) Value {
   264  	return floatToValue(math.Sqrt(call.Argument(0).ToFloat()))
   265  }
   266  
   267  func (r *Runtime) math_tan(call FunctionCall) Value {
   268  	return floatToValue(math.Tan(call.Argument(0).ToFloat()))
   269  }
   270  
   271  func (r *Runtime) math_tanh(call FunctionCall) Value {
   272  	return floatToValue(math.Tanh(call.Argument(0).ToFloat()))
   273  }
   274  
   275  func (r *Runtime) math_trunc(call FunctionCall) Value {
   276  	arg := call.Argument(0)
   277  	if i, ok := arg.(valueInt); ok {
   278  		return i
   279  	}
   280  	return floatToValue(math.Trunc(arg.ToFloat()))
   281  }
   282  
   283  func (r *Runtime) createMath(val *Object) objectImpl {
   284  	m := &baseObject{
   285  		class:      classObject,
   286  		val:        val,
   287  		extensible: true,
   288  		prototype:  r.global.ObjectPrototype,
   289  	}
   290  	m.init()
   291  
   292  	m._putProp("E", valueFloat(math.E), false, false, false)
   293  	m._putProp("LN10", valueFloat(math.Ln10), false, false, false)
   294  	m._putProp("LN2", valueFloat(math.Ln2), false, false, false)
   295  	m._putProp("LOG10E", valueFloat(math.Log10E), false, false, false)
   296  	m._putProp("LOG2E", valueFloat(math.Log2E), false, false, false)
   297  	m._putProp("PI", valueFloat(math.Pi), false, false, false)
   298  	m._putProp("SQRT1_2", valueFloat(sqrt1_2), false, false, false)
   299  	m._putProp("SQRT2", valueFloat(math.Sqrt2), false, false, false)
   300  	m._putSym(SymToStringTag, valueProp(asciiString(classMath), false, false, true))
   301  
   302  	m._putProp("abs", r.newNativeFunc(r.math_abs, nil, "abs", nil, 1), true, false, true)
   303  	m._putProp("acos", r.newNativeFunc(r.math_acos, nil, "acos", nil, 1), true, false, true)
   304  	m._putProp("acosh", r.newNativeFunc(r.math_acosh, nil, "acosh", nil, 1), true, false, true)
   305  	m._putProp("asin", r.newNativeFunc(r.math_asin, nil, "asin", nil, 1), true, false, true)
   306  	m._putProp("asinh", r.newNativeFunc(r.math_asinh, nil, "asinh", nil, 1), true, false, true)
   307  	m._putProp("atan", r.newNativeFunc(r.math_atan, nil, "atan", nil, 1), true, false, true)
   308  	m._putProp("atanh", r.newNativeFunc(r.math_atanh, nil, "atanh", nil, 1), true, false, true)
   309  	m._putProp("atan2", r.newNativeFunc(r.math_atan2, nil, "atan2", nil, 2), true, false, true)
   310  	m._putProp("cbrt", r.newNativeFunc(r.math_cbrt, nil, "cbrt", nil, 1), true, false, true)
   311  	m._putProp("ceil", r.newNativeFunc(r.math_ceil, nil, "ceil", nil, 1), true, false, true)
   312  	m._putProp("clz32", r.newNativeFunc(r.math_clz32, nil, "clz32", nil, 1), true, false, true)
   313  	m._putProp("cos", r.newNativeFunc(r.math_cos, nil, "cos", nil, 1), true, false, true)
   314  	m._putProp("cosh", r.newNativeFunc(r.math_cosh, nil, "cosh", nil, 1), true, false, true)
   315  	m._putProp("exp", r.newNativeFunc(r.math_exp, nil, "exp", nil, 1), true, false, true)
   316  	m._putProp("expm1", r.newNativeFunc(r.math_expm1, nil, "expm1", nil, 1), true, false, true)
   317  	m._putProp("floor", r.newNativeFunc(r.math_floor, nil, "floor", nil, 1), true, false, true)
   318  	m._putProp("fround", r.newNativeFunc(r.math_fround, nil, "fround", nil, 1), true, false, true)
   319  	m._putProp("hypot", r.newNativeFunc(r.math_hypot, nil, "hypot", nil, 2), true, false, true)
   320  	m._putProp("imul", r.newNativeFunc(r.math_imul, nil, "imul", nil, 2), true, false, true)
   321  	m._putProp("log", r.newNativeFunc(r.math_log, nil, "log", nil, 1), true, false, true)
   322  	m._putProp("log1p", r.newNativeFunc(r.math_log1p, nil, "log1p", nil, 1), true, false, true)
   323  	m._putProp("log10", r.newNativeFunc(r.math_log10, nil, "log10", nil, 1), true, false, true)
   324  	m._putProp("log2", r.newNativeFunc(r.math_log2, nil, "log2", nil, 1), true, false, true)
   325  	m._putProp("max", r.newNativeFunc(r.math_max, nil, "max", nil, 2), true, false, true)
   326  	m._putProp("min", r.newNativeFunc(r.math_min, nil, "min", nil, 2), true, false, true)
   327  	m._putProp("pow", r.newNativeFunc(r.math_pow, nil, "pow", nil, 2), true, false, true)
   328  	m._putProp("random", r.newNativeFunc(r.math_random, nil, "random", nil, 0), true, false, true)
   329  	m._putProp("round", r.newNativeFunc(r.math_round, nil, "round", nil, 1), true, false, true)
   330  	m._putProp("sign", r.newNativeFunc(r.math_sign, nil, "sign", nil, 1), true, false, true)
   331  	m._putProp("sin", r.newNativeFunc(r.math_sin, nil, "sin", nil, 1), true, false, true)
   332  	m._putProp("sinh", r.newNativeFunc(r.math_sinh, nil, "sinh", nil, 1), true, false, true)
   333  	m._putProp("sqrt", r.newNativeFunc(r.math_sqrt, nil, "sqrt", nil, 1), true, false, true)
   334  	m._putProp("tan", r.newNativeFunc(r.math_tan, nil, "tan", nil, 1), true, false, true)
   335  	m._putProp("tanh", r.newNativeFunc(r.math_tanh, nil, "tanh", nil, 1), true, false, true)
   336  	m._putProp("trunc", r.newNativeFunc(r.math_trunc, nil, "trunc", nil, 1), true, false, true)
   337  
   338  	return m
   339  }
   340  
   341  func (r *Runtime) initMath() {
   342  	r.addToGlobal("Math", r.newLazyObject(r.createMath))
   343  }