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 {
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
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .