123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- 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
- }
|