...

Source file src/github.com/dop251/goja/builtin_math.go

Documentation: github.com/dop251/goja

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

View as plain text