github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/builtin_number.go (about)

     1  package goja
     2  
     3  import (
     4  	"math"
     5  
     6  	"github.com/nuvolaris/goja/ftoa"
     7  )
     8  
     9  func (r *Runtime) toNumber(v Value) Value {
    10  	switch t := v.(type) {
    11  	case valueFloat, valueInt:
    12  		return v
    13  	case *Object:
    14  		switch t := t.self.(type) {
    15  		case *primitiveValueObject:
    16  			return r.toNumber(t.pValue)
    17  		case *objectGoReflect:
    18  			if t.class == classNumber && t.valueOf != nil {
    19  				return t.valueOf()
    20  			}
    21  		}
    22  	}
    23  	panic(r.NewTypeError("Value is not a number: %s", v))
    24  }
    25  
    26  func (r *Runtime) numberproto_valueOf(call FunctionCall) Value {
    27  	return r.toNumber(call.This)
    28  }
    29  
    30  func (r *Runtime) numberproto_toString(call FunctionCall) Value {
    31  	var numVal Value
    32  	switch t := call.This.(type) {
    33  	case valueFloat, valueInt:
    34  		numVal = t
    35  	case *Object:
    36  		switch t := t.self.(type) {
    37  		case *primitiveValueObject:
    38  			numVal = r.toNumber(t.pValue)
    39  		case *objectGoReflect:
    40  			if t.class == classNumber {
    41  				if t.toString != nil {
    42  					return t.toString()
    43  				}
    44  				if t.valueOf != nil {
    45  					numVal = t.valueOf()
    46  				}
    47  			}
    48  		}
    49  	}
    50  	if numVal == nil {
    51  		panic(r.NewTypeError("Value is not a number"))
    52  	}
    53  	var radix int
    54  	if arg := call.Argument(0); arg != _undefined {
    55  		radix = int(arg.ToInteger())
    56  	} else {
    57  		radix = 10
    58  	}
    59  
    60  	if radix < 2 || radix > 36 {
    61  		panic(r.newError(r.global.RangeError, "toString() radix argument must be between 2 and 36"))
    62  	}
    63  
    64  	num := numVal.ToFloat()
    65  
    66  	if math.IsNaN(num) {
    67  		return stringNaN
    68  	}
    69  
    70  	if math.IsInf(num, 1) {
    71  		return stringInfinity
    72  	}
    73  
    74  	if math.IsInf(num, -1) {
    75  		return stringNegInfinity
    76  	}
    77  
    78  	if radix == 10 {
    79  		return asciiString(fToStr(num, ftoa.ModeStandard, 0))
    80  	}
    81  
    82  	return asciiString(ftoa.FToBaseStr(num, radix))
    83  }
    84  
    85  func (r *Runtime) numberproto_toFixed(call FunctionCall) Value {
    86  	num := r.toNumber(call.This).ToFloat()
    87  	prec := call.Argument(0).ToInteger()
    88  
    89  	if prec < 0 || prec > 100 {
    90  		panic(r.newError(r.global.RangeError, "toFixed() precision must be between 0 and 100"))
    91  	}
    92  	if math.IsNaN(num) {
    93  		return stringNaN
    94  	}
    95  	return asciiString(fToStr(num, ftoa.ModeFixed, int(prec)))
    96  }
    97  
    98  func (r *Runtime) numberproto_toExponential(call FunctionCall) Value {
    99  	num := r.toNumber(call.This).ToFloat()
   100  	precVal := call.Argument(0)
   101  	var prec int64
   102  	if precVal == _undefined {
   103  		return asciiString(fToStr(num, ftoa.ModeStandardExponential, 0))
   104  	} else {
   105  		prec = precVal.ToInteger()
   106  	}
   107  
   108  	if math.IsNaN(num) {
   109  		return stringNaN
   110  	}
   111  	if math.IsInf(num, 1) {
   112  		return stringInfinity
   113  	}
   114  	if math.IsInf(num, -1) {
   115  		return stringNegInfinity
   116  	}
   117  
   118  	if prec < 0 || prec > 100 {
   119  		panic(r.newError(r.global.RangeError, "toExponential() precision must be between 0 and 100"))
   120  	}
   121  
   122  	return asciiString(fToStr(num, ftoa.ModeExponential, int(prec+1)))
   123  }
   124  
   125  func (r *Runtime) numberproto_toPrecision(call FunctionCall) Value {
   126  	numVal := r.toNumber(call.This)
   127  	precVal := call.Argument(0)
   128  	if precVal == _undefined {
   129  		return numVal.toString()
   130  	}
   131  	num := numVal.ToFloat()
   132  	prec := precVal.ToInteger()
   133  
   134  	if math.IsNaN(num) {
   135  		return stringNaN
   136  	}
   137  	if math.IsInf(num, 1) {
   138  		return stringInfinity
   139  	}
   140  	if math.IsInf(num, -1) {
   141  		return stringNegInfinity
   142  	}
   143  	if prec < 1 || prec > 100 {
   144  		panic(r.newError(r.global.RangeError, "toPrecision() precision must be between 1 and 100"))
   145  	}
   146  
   147  	return asciiString(fToStr(num, ftoa.ModePrecision, int(prec)))
   148  }
   149  
   150  func (r *Runtime) number_isFinite(call FunctionCall) Value {
   151  	switch arg := call.Argument(0).(type) {
   152  	case valueInt:
   153  		return valueTrue
   154  	case valueFloat:
   155  		f := float64(arg)
   156  		return r.toBoolean(!math.IsInf(f, 0) && !math.IsNaN(f))
   157  	default:
   158  		return valueFalse
   159  	}
   160  }
   161  
   162  func (r *Runtime) number_isInteger(call FunctionCall) Value {
   163  	switch arg := call.Argument(0).(type) {
   164  	case valueInt:
   165  		return valueTrue
   166  	case valueFloat:
   167  		f := float64(arg)
   168  		return r.toBoolean(!math.IsNaN(f) && !math.IsInf(f, 0) && math.Floor(f) == f)
   169  	default:
   170  		return valueFalse
   171  	}
   172  }
   173  
   174  func (r *Runtime) number_isNaN(call FunctionCall) Value {
   175  	if f, ok := call.Argument(0).(valueFloat); ok && math.IsNaN(float64(f)) {
   176  		return valueTrue
   177  	}
   178  	return valueFalse
   179  }
   180  
   181  func (r *Runtime) number_isSafeInteger(call FunctionCall) Value {
   182  	arg := call.Argument(0)
   183  	if i, ok := arg.(valueInt); ok && i >= -(maxInt-1) && i <= maxInt-1 {
   184  		return valueTrue
   185  	}
   186  	if arg == _negativeZero {
   187  		return valueTrue
   188  	}
   189  	return valueFalse
   190  }
   191  
   192  func (r *Runtime) initNumber() {
   193  	r.global.NumberPrototype = r.newPrimitiveObject(valueInt(0), r.global.ObjectPrototype, classNumber)
   194  	o := r.global.NumberPrototype.self
   195  	o._putProp("toExponential", r.newNativeFunc(r.numberproto_toExponential, nil, "toExponential", nil, 1), true, false, true)
   196  	o._putProp("toFixed", r.newNativeFunc(r.numberproto_toFixed, nil, "toFixed", nil, 1), true, false, true)
   197  	o._putProp("toLocaleString", r.newNativeFunc(r.numberproto_toString, nil, "toLocaleString", nil, 0), true, false, true)
   198  	o._putProp("toPrecision", r.newNativeFunc(r.numberproto_toPrecision, nil, "toPrecision", nil, 1), true, false, true)
   199  	o._putProp("toString", r.newNativeFunc(r.numberproto_toString, nil, "toString", nil, 1), true, false, true)
   200  	o._putProp("valueOf", r.newNativeFunc(r.numberproto_valueOf, nil, "valueOf", nil, 0), true, false, true)
   201  
   202  	r.global.Number = r.newNativeFunc(r.builtin_Number, r.builtin_newNumber, "Number", r.global.NumberPrototype, 1)
   203  	o = r.global.Number.self
   204  	o._putProp("EPSILON", _epsilon, false, false, false)
   205  	o._putProp("isFinite", r.newNativeFunc(r.number_isFinite, nil, "isFinite", nil, 1), true, false, true)
   206  	o._putProp("isInteger", r.newNativeFunc(r.number_isInteger, nil, "isInteger", nil, 1), true, false, true)
   207  	o._putProp("isNaN", r.newNativeFunc(r.number_isNaN, nil, "isNaN", nil, 1), true, false, true)
   208  	o._putProp("isSafeInteger", r.newNativeFunc(r.number_isSafeInteger, nil, "isSafeInteger", nil, 1), true, false, true)
   209  	o._putProp("MAX_SAFE_INTEGER", valueInt(maxInt-1), false, false, false)
   210  	o._putProp("MIN_SAFE_INTEGER", valueInt(-(maxInt - 1)), false, false, false)
   211  	o._putProp("MIN_VALUE", valueFloat(math.SmallestNonzeroFloat64), false, false, false)
   212  	o._putProp("MAX_VALUE", valueFloat(math.MaxFloat64), false, false, false)
   213  	o._putProp("NaN", _NaN, false, false, false)
   214  	o._putProp("NEGATIVE_INFINITY", _negativeInf, false, false, false)
   215  	o._putProp("parseFloat", r.Get("parseFloat"), true, false, true)
   216  	o._putProp("parseInt", r.Get("parseInt"), true, false, true)
   217  	o._putProp("POSITIVE_INFINITY", _positiveInf, false, false, false)
   218  	r.addToGlobal("Number", r.global.Number)
   219  
   220  }