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 }