vendor tsgo

This commit is contained in:
2026-07-09 16:50:43 -04:00
parent c06ea2e5a4
commit 98978e4930
5804 changed files with 1556156 additions and 101 deletions

View File

@@ -0,0 +1,177 @@
// Package jsnum provides JS-like number handling.
package jsnum
import (
"math"
"math/big"
)
const (
MaxSafeInteger Number = 1<<53 - 1
MinSafeInteger Number = -MaxSafeInteger
)
// Number represents a JS-like number.
//
// All operations that can be performed directly on this type
// (e.g., conversion, arithmetic, etc.) behave as they would in JavaScript,
// but any other operation should use this type's methods,
// not the "math" package and conversions.
type Number float64
func NaN() Number {
return Number(math.NaN())
}
func (n Number) IsNaN() bool {
return math.IsNaN(float64(n))
}
func Inf(sign int) Number {
return Number(math.Inf(sign))
}
func (n Number) IsInf() bool {
return math.IsInf(float64(n), 0)
}
func isNonFinite(x float64) bool {
// This is equivalent to checking `math.IsNaN(x) || math.IsInf(x, 0)` in one operation.
const mask = 0x7FF0000000000000
return math.Float64bits(x)&mask == mask
}
// https://tc39.es/ecma262/2024/multipage/abstract-operations.html#sec-touint32
func (x Number) toUint32() uint32 {
// The only difference between ToUint32 and ToInt32 is the interpretation of the bits.
return uint32(x.toInt32())
}
// https://tc39.es/ecma262/2024/multipage/abstract-operations.html#sec-toint32
func (n Number) toInt32() int32 {
x := float64(n)
// Fast path: if the number is in the range (-2^31, 2^32), i.e. an SMI,
// then we don't need to do any special mapping.
if smi := int32(x); float64(smi) == x {
return smi
}
// 2. If number is not finite or number is either +0𝔽 or -0𝔽, return +0𝔽.
// Zero was covered by the test above.
if isNonFinite(x) {
return 0
}
// Let int be truncate((number)).
x = math.Trunc(x)
// Let int32bit be int modulo 2**32.
x = math.Mod(x, 1<<32)
// If int32bit ≥ 2**31, return 𝔽(int32bit - 2**32); otherwise return 𝔽(int32bit).
return int32(int64(x))
}
func (x Number) toShiftCount() uint32 {
return x.toUint32() & 31
}
// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-signedRightShift
func (x Number) SignedRightShift(y Number) Number {
return Number(x.toInt32() >> y.toShiftCount())
}
// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-unsignedRightShift
func (x Number) UnsignedRightShift(y Number) Number {
return Number(x.toUint32() >> y.toShiftCount())
}
// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-leftShift
func (x Number) LeftShift(y Number) Number {
return Number(x.toInt32() << y.toShiftCount())
}
// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-bitwiseNOT
func (x Number) BitwiseNOT() Number {
return Number(^x.toInt32())
}
// The below are implemented by https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numberbitwiseop.
// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-bitwiseOR
func (x Number) BitwiseOR(y Number) Number {
return Number(x.toInt32() | y.toInt32())
}
// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-bitwiseAND
func (x Number) BitwiseAND(y Number) Number {
return Number(x.toInt32() & y.toInt32())
}
// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-bitwiseXOR
func (x Number) BitwiseXOR(y Number) Number {
return Number(x.toInt32() ^ y.toInt32())
}
func (x Number) trunc() Number {
return Number(math.Trunc(float64(x)))
}
func (x Number) Floor() Number {
return Number(math.Floor(float64(x)))
}
func (x Number) Abs() Number {
return Number(math.Abs(float64(x)))
}
var negativeZero = Number(math.Copysign(0, -1))
// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-remainder
func (n Number) Remainder(d Number) Number {
switch {
case n.IsNaN() || d.IsNaN():
return NaN()
case n.IsInf():
return NaN()
case d.IsInf():
return n
case d == 0:
return NaN()
case n == 0:
return n
}
return Number(math.Mod(float64(n), float64(d)))
}
// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-exponentiate
func (base Number) Exponentiate(exponent Number) Number {
switch {
case (base == 1 || base == -1) && exponent.IsInf():
return NaN()
case base == 1 && exponent.IsNaN():
return NaN()
}
b := float64(base)
e := float64(exponent)
// For integer base ** integer exponent where the result exceeds 53 bits,
// math.Pow can be off by multiple ULPs vs JS engines. Use exact big.Int
// arithmetic and IEEE 754 round-to-nearest-even conversion instead.
// The ES spec (§6.1.6.1.3) says exponentiate returns an
// "implementation-approximated" value, so engines are allowed to differ.
// This won't exactly match every engine (V8's fdlibm-compiled pow can
// round halfway ties differently), but will always be within 1 ULP
// (unit in the last place, i.e. the least significant bit of the result).
if b >= math.MinInt64 && b <= math.MaxInt64 && b == math.Trunc(b) &&
e >= 0 && e <= math.MaxInt64 && e == math.Trunc(e) && !math.IsInf(e, 0) {
magnitude := e * math.Log2(math.Abs(b))
if magnitude > 53 && magnitude <= math.Log2(math.MaxFloat64) {
ri := new(big.Int).Exp(big.NewInt(int64(b)), big.NewInt(int64(e)), nil)
result, _ := new(big.Float).SetPrec(256).SetInt(ri).Float64()
return Number(result)
}
}
return Number(math.Pow(b, e))
}