github.com/dop251/goja@v0.0.0-20240220182346-e401ed450204/builtin_math.go (about) 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 }