go.ketch.com/lib/goja@v0.0.1/builtin_number.go (about)

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