1package goja
2
3import (
4	"math"
5
6	"github.com/dop251/goja/ftoa"
7)
8
9func (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
26func 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
39func (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
75func (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
88func (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
115func (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
140func (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
152func (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
164func (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
171func (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
182func (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}
211