...

Source file src/github.com/dop251/goja/builtin_number.go

Documentation: github.com/dop251/goja

     1  package goja
     2  
     3  import (
     4  	"math"
     5  	"sync"
     6  
     7  	"github.com/dop251/goja/ftoa"
     8  )
     9  
    10  func (r *Runtime) toNumber(v Value) Value {
    11  	switch t := v.(type) {
    12  	case valueFloat, valueInt:
    13  		return v
    14  	case *Object:
    15  		switch t := t.self.(type) {
    16  		case *primitiveValueObject:
    17  			return r.toNumber(t.pValue)
    18  		case *objectGoReflect:
    19  			if t.class == classNumber && t.valueOf != nil {
    20  				return t.valueOf()
    21  			}
    22  		}
    23  		if t == r.global.NumberPrototype {
    24  			return _positiveZero
    25  		}
    26  	}
    27  	panic(r.NewTypeError("Value is not a number: %s", v))
    28  }
    29  
    30  func (r *Runtime) numberproto_valueOf(call FunctionCall) Value {
    31  	return r.toNumber(call.This)
    32  }
    33  
    34  func (r *Runtime) numberproto_toString(call FunctionCall) Value {
    35  	var numVal Value
    36  	switch t := call.This.(type) {
    37  	case valueFloat, valueInt:
    38  		numVal = t
    39  	case *Object:
    40  		switch t := t.self.(type) {
    41  		case *primitiveValueObject:
    42  			numVal = r.toNumber(t.pValue)
    43  		case *objectGoReflect:
    44  			if t.class == classNumber {
    45  				if t.toString != nil {
    46  					return t.toString()
    47  				}
    48  				if t.valueOf != nil {
    49  					numVal = t.valueOf()
    50  				}
    51  			}
    52  		}
    53  		if t == r.global.NumberPrototype {
    54  			return asciiString("0")
    55  		}
    56  	}
    57  	if numVal == nil {
    58  		panic(r.NewTypeError("Value is not a number"))
    59  	}
    60  	var radix int
    61  	if arg := call.Argument(0); arg != _undefined {
    62  		radix = int(arg.ToInteger())
    63  	} else {
    64  		radix = 10
    65  	}
    66  
    67  	if radix < 2 || radix > 36 {
    68  		panic(r.newError(r.getRangeError(), "toString() radix argument must be between 2 and 36"))
    69  	}
    70  
    71  	num := numVal.ToFloat()
    72  
    73  	if math.IsNaN(num) {
    74  		return stringNaN
    75  	}
    76  
    77  	if math.IsInf(num, 1) {
    78  		return stringInfinity
    79  	}
    80  
    81  	if math.IsInf(num, -1) {
    82  		return stringNegInfinity
    83  	}
    84  
    85  	if radix == 10 {
    86  		return asciiString(fToStr(num, ftoa.ModeStandard, 0))
    87  	}
    88  
    89  	return asciiString(ftoa.FToBaseStr(num, radix))
    90  }
    91  
    92  func (r *Runtime) numberproto_toFixed(call FunctionCall) Value {
    93  	num := r.toNumber(call.This).ToFloat()
    94  	prec := call.Argument(0).ToInteger()
    95  
    96  	if prec < 0 || prec > 100 {
    97  		panic(r.newError(r.getRangeError(), "toFixed() precision must be between 0 and 100"))
    98  	}
    99  	if math.IsNaN(num) {
   100  		return stringNaN
   101  	}
   102  	return asciiString(fToStr(num, ftoa.ModeFixed, int(prec)))
   103  }
   104  
   105  func (r *Runtime) numberproto_toExponential(call FunctionCall) Value {
   106  	num := r.toNumber(call.This).ToFloat()
   107  	precVal := call.Argument(0)
   108  	var prec int64
   109  	if precVal == _undefined {
   110  		return asciiString(fToStr(num, ftoa.ModeStandardExponential, 0))
   111  	} else {
   112  		prec = precVal.ToInteger()
   113  	}
   114  
   115  	if math.IsNaN(num) {
   116  		return stringNaN
   117  	}
   118  	if math.IsInf(num, 1) {
   119  		return stringInfinity
   120  	}
   121  	if math.IsInf(num, -1) {
   122  		return stringNegInfinity
   123  	}
   124  
   125  	if prec < 0 || prec > 100 {
   126  		panic(r.newError(r.getRangeError(), "toExponential() precision must be between 0 and 100"))
   127  	}
   128  
   129  	return asciiString(fToStr(num, ftoa.ModeExponential, int(prec+1)))
   130  }
   131  
   132  func (r *Runtime) numberproto_toPrecision(call FunctionCall) Value {
   133  	numVal := r.toNumber(call.This)
   134  	precVal := call.Argument(0)
   135  	if precVal == _undefined {
   136  		return numVal.toString()
   137  	}
   138  	num := numVal.ToFloat()
   139  	prec := precVal.ToInteger()
   140  
   141  	if math.IsNaN(num) {
   142  		return stringNaN
   143  	}
   144  	if math.IsInf(num, 1) {
   145  		return stringInfinity
   146  	}
   147  	if math.IsInf(num, -1) {
   148  		return stringNegInfinity
   149  	}
   150  	if prec < 1 || prec > 100 {
   151  		panic(r.newError(r.getRangeError(), "toPrecision() precision must be between 1 and 100"))
   152  	}
   153  
   154  	return asciiString(fToStr(num, ftoa.ModePrecision, int(prec)))
   155  }
   156  
   157  func (r *Runtime) number_isFinite(call FunctionCall) Value {
   158  	switch arg := call.Argument(0).(type) {
   159  	case valueInt:
   160  		return valueTrue
   161  	case valueFloat:
   162  		f := float64(arg)
   163  		return r.toBoolean(!math.IsInf(f, 0) && !math.IsNaN(f))
   164  	default:
   165  		return valueFalse
   166  	}
   167  }
   168  
   169  func (r *Runtime) number_isInteger(call FunctionCall) Value {
   170  	switch arg := call.Argument(0).(type) {
   171  	case valueInt:
   172  		return valueTrue
   173  	case valueFloat:
   174  		f := float64(arg)
   175  		return r.toBoolean(!math.IsNaN(f) && !math.IsInf(f, 0) && math.Floor(f) == f)
   176  	default:
   177  		return valueFalse
   178  	}
   179  }
   180  
   181  func (r *Runtime) number_isNaN(call FunctionCall) Value {
   182  	if f, ok := call.Argument(0).(valueFloat); ok && math.IsNaN(float64(f)) {
   183  		return valueTrue
   184  	}
   185  	return valueFalse
   186  }
   187  
   188  func (r *Runtime) number_isSafeInteger(call FunctionCall) Value {
   189  	arg := call.Argument(0)
   190  	if i, ok := arg.(valueInt); ok && i >= -(maxInt-1) && i <= maxInt-1 {
   191  		return valueTrue
   192  	}
   193  	if arg == _negativeZero {
   194  		return valueTrue
   195  	}
   196  	return valueFalse
   197  }
   198  
   199  func createNumberProtoTemplate() *objectTemplate {
   200  	t := newObjectTemplate()
   201  	t.protoFactory = func(r *Runtime) *Object {
   202  		return r.global.ObjectPrototype
   203  	}
   204  
   205  	t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getNumber(), true, false, true) })
   206  
   207  	t.putStr("toExponential", func(r *Runtime) Value { return r.methodProp(r.numberproto_toExponential, "toExponential", 1) })
   208  	t.putStr("toFixed", func(r *Runtime) Value { return r.methodProp(r.numberproto_toFixed, "toFixed", 1) })
   209  	t.putStr("toLocaleString", func(r *Runtime) Value { return r.methodProp(r.numberproto_toString, "toLocaleString", 0) })
   210  	t.putStr("toPrecision", func(r *Runtime) Value { return r.methodProp(r.numberproto_toPrecision, "toPrecision", 1) })
   211  	t.putStr("toString", func(r *Runtime) Value { return r.methodProp(r.numberproto_toString, "toString", 1) })
   212  	t.putStr("valueOf", func(r *Runtime) Value { return r.methodProp(r.numberproto_valueOf, "valueOf", 0) })
   213  
   214  	return t
   215  }
   216  
   217  var numberProtoTemplate *objectTemplate
   218  var numberProtoTemplateOnce sync.Once
   219  
   220  func getNumberProtoTemplate() *objectTemplate {
   221  	numberProtoTemplateOnce.Do(func() {
   222  		numberProtoTemplate = createNumberProtoTemplate()
   223  	})
   224  	return numberProtoTemplate
   225  }
   226  
   227  func (r *Runtime) getNumberPrototype() *Object {
   228  	ret := r.global.NumberPrototype
   229  	if ret == nil {
   230  		ret = &Object{runtime: r}
   231  		r.global.NumberPrototype = ret
   232  		o := r.newTemplatedObject(getNumberProtoTemplate(), ret)
   233  		o.class = classNumber
   234  	}
   235  	return ret
   236  }
   237  
   238  func (r *Runtime) getParseFloat() *Object {
   239  	ret := r.global.parseFloat
   240  	if ret == nil {
   241  		ret = r.newNativeFunc(r.builtin_parseFloat, "parseFloat", 1)
   242  		r.global.parseFloat = ret
   243  	}
   244  	return ret
   245  }
   246  
   247  func (r *Runtime) getParseInt() *Object {
   248  	ret := r.global.parseInt
   249  	if ret == nil {
   250  		ret = r.newNativeFunc(r.builtin_parseInt, "parseInt", 2)
   251  		r.global.parseInt = ret
   252  	}
   253  	return ret
   254  }
   255  
   256  func createNumberTemplate() *objectTemplate {
   257  	t := newObjectTemplate()
   258  	t.protoFactory = func(r *Runtime) *Object {
   259  		return r.getFunctionPrototype()
   260  	}
   261  	t.putStr("length", func(r *Runtime) Value { return valueProp(intToValue(1), false, false, true) })
   262  	t.putStr("name", func(r *Runtime) Value { return valueProp(asciiString("Number"), false, false, true) })
   263  
   264  	t.putStr("prototype", func(r *Runtime) Value { return valueProp(r.getNumberPrototype(), false, false, false) })
   265  
   266  	t.putStr("EPSILON", func(r *Runtime) Value { return valueProp(_epsilon, false, false, false) })
   267  	t.putStr("isFinite", func(r *Runtime) Value { return r.methodProp(r.number_isFinite, "isFinite", 1) })
   268  	t.putStr("isInteger", func(r *Runtime) Value { return r.methodProp(r.number_isInteger, "isInteger", 1) })
   269  	t.putStr("isNaN", func(r *Runtime) Value { return r.methodProp(r.number_isNaN, "isNaN", 1) })
   270  	t.putStr("isSafeInteger", func(r *Runtime) Value { return r.methodProp(r.number_isSafeInteger, "isSafeInteger", 1) })
   271  	t.putStr("MAX_SAFE_INTEGER", func(r *Runtime) Value { return valueProp(valueInt(maxInt-1), false, false, false) })
   272  	t.putStr("MIN_SAFE_INTEGER", func(r *Runtime) Value { return valueProp(valueInt(-(maxInt - 1)), false, false, false) })
   273  	t.putStr("MIN_VALUE", func(r *Runtime) Value { return valueProp(valueFloat(math.SmallestNonzeroFloat64), false, false, false) })
   274  	t.putStr("MAX_VALUE", func(r *Runtime) Value { return valueProp(valueFloat(math.MaxFloat64), false, false, false) })
   275  	t.putStr("NaN", func(r *Runtime) Value { return valueProp(_NaN, false, false, false) })
   276  	t.putStr("NEGATIVE_INFINITY", func(r *Runtime) Value { return valueProp(_negativeInf, false, false, false) })
   277  	t.putStr("parseFloat", func(r *Runtime) Value { return valueProp(r.getParseFloat(), true, false, true) })
   278  	t.putStr("parseInt", func(r *Runtime) Value { return valueProp(r.getParseInt(), true, false, true) })
   279  	t.putStr("POSITIVE_INFINITY", func(r *Runtime) Value { return valueProp(_positiveInf, false, false, false) })
   280  
   281  	return t
   282  }
   283  
   284  var numberTemplate *objectTemplate
   285  var numberTemplateOnce sync.Once
   286  
   287  func getNumberTemplate() *objectTemplate {
   288  	numberTemplateOnce.Do(func() {
   289  		numberTemplate = createNumberTemplate()
   290  	})
   291  	return numberTemplate
   292  }
   293  
   294  func (r *Runtime) getNumber() *Object {
   295  	ret := r.global.Number
   296  	if ret == nil {
   297  		ret = &Object{runtime: r}
   298  		r.global.Number = ret
   299  		r.newTemplatedFuncObject(getNumberTemplate(), ret, r.builtin_Number,
   300  			r.wrapNativeConstruct(r.builtin_newNumber, ret, r.getNumberPrototype()))
   301  	}
   302  	return ret
   303  }
   304  

View as plain text