ai_old/goja/builtin_bigint.go

370 lines
9.5 KiB
Go
Raw Normal View History

2024-09-20 16:50:35 +08:00
package goja
import (
"fmt"
"hash/maphash"
"math"
"math/big"
"reflect"
"strconv"
"sync"
"github.com/dop251/goja/unistring"
)
type valueBigInt big.Int
func (v *valueBigInt) ToInteger() int64 {
v.ToNumber()
return 0
}
func (v *valueBigInt) toString() String {
return asciiString((*big.Int)(v).String())
}
func (v *valueBigInt) string() unistring.String {
return unistring.String(v.String())
}
func (v *valueBigInt) ToString() Value {
return v
}
func (v *valueBigInt) String() string {
return (*big.Int)(v).String()
}
func (v *valueBigInt) ToFloat() float64 {
v.ToNumber()
return 0
}
func (v *valueBigInt) ToNumber() Value {
panic(typeError("Cannot convert a BigInt value to a number"))
}
func (v *valueBigInt) ToBoolean() bool {
return (*big.Int)(v).Sign() != 0
}
func (v *valueBigInt) ToObject(r *Runtime) *Object {
return r.newPrimitiveObject(v, r.getBigIntPrototype(), classObject)
}
func (v *valueBigInt) SameAs(other Value) bool {
if o, ok := other.(*valueBigInt); ok {
return (*big.Int)(v).Cmp((*big.Int)(o)) == 0
}
return false
}
func (v *valueBigInt) Equals(other Value) bool {
switch o := other.(type) {
case *valueBigInt:
return (*big.Int)(v).Cmp((*big.Int)(o)) == 0
case valueInt:
return (*big.Int)(v).Cmp(big.NewInt(int64(o))) == 0
case valueFloat:
if IsInfinity(o) || math.IsNaN(float64(o)) {
return false
}
if f := big.NewFloat(float64(o)); f.IsInt() {
i, _ := f.Int(nil)
return (*big.Int)(v).Cmp(i) == 0
}
return false
case String:
bigInt, err := stringToBigInt(o.toTrimmedUTF8())
if err != nil {
return false
}
return bigInt.Cmp((*big.Int)(v)) == 0
case valueBool:
return (*big.Int)(v).Int64() == o.ToInteger()
case *Object:
return v.Equals(o.toPrimitiveNumber())
}
return false
}
func (v *valueBigInt) StrictEquals(other Value) bool {
o, ok := other.(*valueBigInt)
if ok {
return (*big.Int)(v).Cmp((*big.Int)(o)) == 0
}
return false
}
func (v *valueBigInt) Export() interface{} {
return new(big.Int).Set((*big.Int)(v))
}
func (v *valueBigInt) ExportType() reflect.Type {
return typeBigInt
}
func (v *valueBigInt) baseObject(rt *Runtime) *Object {
return rt.getBigIntPrototype()
}
func (v *valueBigInt) hash(hash *maphash.Hash) uint64 {
var sign byte
if (*big.Int)(v).Sign() < 0 {
sign = 0x01
} else {
sign = 0x00
}
_ = hash.WriteByte(sign)
_, _ = hash.Write((*big.Int)(v).Bytes())
h := hash.Sum64()
hash.Reset()
return h
}
func toBigInt(value Value) *valueBigInt {
// Undefined Throw a TypeError exception.
// Null Throw a TypeError exception.
// Boolean Return 1n if prim is true and 0n if prim is false.
// BigInt Return prim.
// Number Throw a TypeError exception.
// String 1. Let n be StringToBigInt(prim).
// 2. If n is undefined, throw a SyntaxError exception.
// 3. Return n.
// Symbol Throw a TypeError exception.
switch prim := value.(type) {
case *valueBigInt:
return prim
case String:
bigInt, err := stringToBigInt(prim.toTrimmedUTF8())
if err != nil {
panic(syntaxError(fmt.Sprintf("Cannot convert %s to a BigInt", prim)))
}
return (*valueBigInt)(bigInt)
case valueBool:
return (*valueBigInt)(big.NewInt(prim.ToInteger()))
case *Symbol:
panic(typeError("Cannot convert Symbol to a BigInt"))
case *Object:
return toBigInt(prim.toPrimitiveNumber())
default:
panic(typeError(fmt.Sprintf("Cannot convert %s to a BigInt", prim)))
}
}
func numberToBigInt(v Value) *valueBigInt {
switch v := toNumeric(v).(type) {
case *valueBigInt:
return v
case valueInt:
return (*valueBigInt)(big.NewInt(v.ToInteger()))
case valueFloat:
if IsInfinity(v) || math.IsNaN(float64(v)) {
panic(rangeError(fmt.Sprintf("Cannot convert %s to a BigInt", v)))
}
if f := big.NewFloat(float64(v)); f.IsInt() {
n, _ := f.Int(nil)
return (*valueBigInt)(n)
}
panic(rangeError(fmt.Sprintf("Cannot convert %s to a BigInt", v)))
case *Object:
prim := v.toPrimitiveNumber()
switch prim.(type) {
case valueInt, valueFloat:
return numberToBigInt(prim)
default:
return toBigInt(prim)
}
default:
panic(newTypeError("Cannot convert %s to a BigInt", v))
}
}
func stringToBigInt(str string) (*big.Int, error) {
var bigint big.Int
n, err := stringToInt(str)
if err != nil {
switch {
case isRangeErr(err):
bigint.SetString(str, 0)
case err == strconv.ErrSyntax:
default:
return nil, strconv.ErrSyntax
}
} else {
bigint.SetInt64(n)
}
return &bigint, nil
}
func (r *Runtime) thisBigIntValue(value Value) Value {
switch t := value.(type) {
case *valueBigInt:
return t
case *Object:
switch t := t.self.(type) {
case *primitiveValueObject:
return r.thisBigIntValue(t.pValue)
case *objectGoReflect:
if t.exportType() == typeBigInt && t.valueOf != nil {
return t.valueOf()
}
}
}
panic(r.NewTypeError("requires that 'this' be a BigInt"))
}
func (r *Runtime) bigintproto_valueOf(call FunctionCall) Value {
return r.thisBigIntValue(call.This)
}
func (r *Runtime) bigintproto_toString(call FunctionCall) Value {
x := (*big.Int)(r.thisBigIntValue(call.This).(*valueBigInt))
radix := call.Argument(0)
var radixMV int
if radix == _undefined {
radixMV = 10
} else {
radixMV = int(radix.ToInteger())
if radixMV < 2 || radixMV > 36 {
panic(r.newError(r.getRangeError(), "radix must be an integer between 2 and 36"))
}
}
return asciiString(x.Text(radixMV))
}
func (r *Runtime) bigint_asIntN(call FunctionCall) Value {
if len(call.Arguments) < 2 {
panic(r.NewTypeError("Cannot convert undefined to a BigInt"))
}
bits := r.toIndex(call.Argument(0).ToNumber())
if bits < 0 {
panic(r.NewTypeError("Invalid value: not (convertible to) a safe integer"))
}
bigint := toBigInt(call.Argument(1))
twoToBits := new(big.Int).Lsh(big.NewInt(1), uint(bits))
mod := new(big.Int).Mod((*big.Int)(bigint), twoToBits)
if bits > 0 && mod.Cmp(new(big.Int).Lsh(big.NewInt(1), uint(bits-1))) >= 0 {
return (*valueBigInt)(mod.Sub(mod, twoToBits))
} else {
return (*valueBigInt)(mod)
}
}
func (r *Runtime) bigint_asUintN(call FunctionCall) Value {
if len(call.Arguments) < 2 {
panic(r.NewTypeError("Cannot convert undefined to a BigInt"))
}
bits := r.toIndex(call.Argument(0).ToNumber())
if bits < 0 {
panic(r.NewTypeError("Invalid value: not (convertible to) a safe integer"))
}
bigint := (*big.Int)(toBigInt(call.Argument(1)))
ret := new(big.Int).Mod(bigint, new(big.Int).Lsh(big.NewInt(1), uint(bits)))
return (*valueBigInt)(ret)
}
var bigintTemplate *objectTemplate
var bigintTemplateOnce sync.Once
func getBigIntTemplate() *objectTemplate {
bigintTemplateOnce.Do(func() {
bigintTemplate = createBigIntTemplate()
})
return bigintTemplate
}
func createBigIntTemplate() *objectTemplate {
t := newObjectTemplate()
t.protoFactory = func(r *Runtime) *Object {
return r.getFunctionPrototype()
}
t.putStr("name", func(r *Runtime) Value { return valueProp(asciiString("BigInt"), false, false, true) })
t.putStr("length", func(r *Runtime) Value { return valueProp(intToValue(1), false, false, true) })
t.putStr("prototype", func(r *Runtime) Value { return valueProp(r.getBigIntPrototype(), false, false, false) })
t.putStr("asIntN", func(r *Runtime) Value { return r.methodProp(r.bigint_asIntN, "asIntN", 2) })
t.putStr("asUintN", func(r *Runtime) Value { return r.methodProp(r.bigint_asUintN, "asUintN", 2) })
return t
}
func (r *Runtime) builtin_BigInt(call FunctionCall) Value {
if len(call.Arguments) > 0 {
switch v := call.Argument(0).(type) {
case *valueBigInt, valueInt, valueFloat, *Object:
return numberToBigInt(v)
default:
return toBigInt(v)
}
}
return (*valueBigInt)(big.NewInt(0))
}
func (r *Runtime) builtin_newBigInt(args []Value, newTarget *Object) *Object {
if newTarget != nil {
panic(r.NewTypeError("BigInt is not a constructor"))
}
var v Value
if len(args) > 0 {
v = numberToBigInt(args[0])
} else {
v = (*valueBigInt)(big.NewInt(0))
}
return r.newPrimitiveObject(v, newTarget, classObject)
}
func (r *Runtime) getBigInt() *Object {
ret := r.global.BigInt
if ret == nil {
ret = &Object{runtime: r}
r.global.BigInt = ret
r.newTemplatedFuncObject(getBigIntTemplate(), ret, r.builtin_BigInt,
r.wrapNativeConstruct(r.builtin_newBigInt, ret, r.getBigIntPrototype()))
}
return ret
}
func createBigIntProtoTemplate() *objectTemplate {
t := newObjectTemplate()
t.protoFactory = func(r *Runtime) *Object {
return r.global.ObjectPrototype
}
t.putStr("length", func(r *Runtime) Value { return valueProp(intToValue(0), false, false, true) })
t.putStr("name", func(r *Runtime) Value { return valueProp(asciiString("BigInt"), false, false, true) })
t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getBigInt(), true, false, true) })
t.putStr("toLocaleString", func(r *Runtime) Value { return r.methodProp(r.bigintproto_toString, "toLocaleString", 0) })
t.putStr("toString", func(r *Runtime) Value { return r.methodProp(r.bigintproto_toString, "toString", 0) })
t.putStr("valueOf", func(r *Runtime) Value { return r.methodProp(r.bigintproto_valueOf, "valueOf", 0) })
t.putSym(SymToStringTag, func(r *Runtime) Value { return valueProp(asciiString("BigInt"), false, false, true) })
return t
}
var bigintProtoTemplate *objectTemplate
var bigintProtoTemplateOnce sync.Once
func getBigIntProtoTemplate() *objectTemplate {
bigintProtoTemplateOnce.Do(func() {
bigintProtoTemplate = createBigIntProtoTemplate()
})
return bigintProtoTemplate
}
func (r *Runtime) getBigIntPrototype() *Object {
ret := r.global.BigIntPrototype
if ret == nil {
ret = &Object{runtime: r}
r.global.BigIntPrototype = ret
o := r.newTemplatedObject(getBigIntProtoTemplate(), ret)
o.class = classObject
}
return ret
}