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