1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef ECMASCRIPT_BUILTINS_BUILTINS_NUMBER_H
17#define ECMASCRIPT_BUILTINS_BUILTINS_NUMBER_H
18
19#include "ecmascript/base/builtins_base.h"
20#include "ecmascript/js_tagged_value.h"
21
22// List of constants in Number, excluding '@@' internal properties.
23#define BUILTIN_NUMBER_CONSTANTS(V)                             \
24    V(EPSILON)           /* Number.EPSILON */                   \
25    V(MAX_SAFE_INTEGER)  /* Number.MAX_SAFE_INTEGER */          \
26    V(MAX_VALUE)         /* Number.MAX_VALUE */                 \
27    V(MIN_SAFE_INTEGER)  /* Number.MIN_SAFE_INTEGER */          \
28    V(MIN_VALUE)         /* Number.MIN_VALUE */                 \
29    V(NEGATIVE_INFINITY) /* Number.NEGATIVE_INFINITY */         \
30    V(NaN)               /* Number.NaN */                       \
31    V(POSITIVE_INFINITY) /* Number.POSITIVE_INFINITY */
32
33// List of functions in Number.
34// V(name, func, length, stubIndex)
35// where BuiltinsNumber::func refers to the native implementation of Number[name].
36//       kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available.
37#define BUILTIN_NUMBER_NON_GLOBAL_FUNCTIONS(V)                                                           \
38    V("isFinite",      IsFinite,      1, NumberIsFinite)          /* Number.isFinite ( number ) */       \
39    V("isInteger",     IsInteger,     1, NumberIsInteger)         /* Number.isInteger ( number ) */      \
40    V("isNaN",         IsNaN,         1, NumberIsNaN)             /* Number.isNaN ( number ) */          \
41    V("isSafeInteger", IsSafeInteger, 1, NumberIsSafeInteger)     /* Number.isSafeInteger ( number ) */
42
43// List of functions in Number that can be accessed via globalThis.
44// V(name, func, length, stubIndex)
45// where BuiltinsNumber::func refers to the native implementation of Number[name].
46#define BUILTIN_NUMBER_GLOBAL_FUNCTIONS(V)                                              \
47    V("parseFloat", ParseFloat, 1, NumberParseFloat) /* Number.parseFloat ( string ) */ \
48    V("parseInt",   ParseInt,   2, NumberParseInt) /* Number.parseInt ( string, radix ) */
49
50#define BUILTIN_NUMBER_FUNCTIONS(V)         \
51    BUILTIN_NUMBER_NON_GLOBAL_FUNCTIONS(V)  \
52    BUILTIN_NUMBER_GLOBAL_FUNCTIONS(V)
53
54// List of functions in Number.prototype, excluding the constructor and '@@' properties.
55// V(name, func, length, stubIndex)
56// where BuiltinsNumber::func refers to the native implementation of Number.prototype[name].
57#define BUILTIN_NUMBER_PROTOTYPE_FUNCTIONS(V)                               \
58    /* Number.prototype.toExponential ( fractionDigits ) */                 \
59    V("toExponential",  ToExponential,  1, INVALID)                         \
60    /* Number.prototype.toFixed ( fractionDigits ) */                       \
61    V("toFixed",        ToFixed,        1, INVALID)                         \
62    /* Number.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] ) */ \
63    V("toLocaleString", ToLocaleString, 0, INVALID)                         \
64    /* Number.prototype.toPrecision ( precision ) */                        \
65    V("toPrecision",    ToPrecision,    1, INVALID)                         \
66    /* Number.prototype.toString ( [ radix ] ) */                           \
67    V("toString",       ToString,       1, NumberToString)                  \
68    /* Number.prototype.valueOf ( ) */                                      \
69    V("valueOf",        ValueOf,        0, INVALID)
70
71namespace panda::ecmascript::builtins {
72class BuiltinsNumber : public base::BuiltinsBase {
73public:
74    // 21.1.2.1 Number.EPSILON
75    static constexpr double EPSILON = std::numeric_limits<double>::epsilon();
76    // 21.1.2.6 Number.MAX_SAFE_INTEGER (which is 2**53 - 1 = 9007199254740991)
77    static constexpr int64_t MAX_SAFE_INTEGER = (1LL << 53) - 1;
78    // 21.1.2.8 Number.MIN_SAFE_INTEGER (which is -(2**53 - 1) = -9007199254740991)
79    static constexpr int64_t MIN_SAFE_INTEGER = -((1LL << 53) - 1);
80    // 21.1.2.7 Number.MAX_VALUE
81    static constexpr double MAX_VALUE = std::numeric_limits<double>::max();
82    // 21.1.2.9 Number.MIN_VALUE
83    static constexpr double MIN_VALUE = std::numeric_limits<double>::denorm_min();
84    // 21.1.2.14 Number.POSITIVE_INFINITY
85    static constexpr double POSITIVE_INFINITY = std::numeric_limits<double>::infinity();
86    // 21.1.2.11 Number.NEGATIVE_INFINITY
87    static constexpr double NEGATIVE_INFINITY = -POSITIVE_INFINITY;
88    // 21.1.2.10 Number.NaN
89    static constexpr double NaN = NAN;
90
91    // 20.1.1.1
92    static JSTaggedValue NumberConstructor(EcmaRuntimeCallInfo *argv);
93
94    // 20.1.2.2
95    static JSTaggedValue IsFinite(EcmaRuntimeCallInfo *argv);
96    // 20.1.2.3
97    static JSTaggedValue IsInteger(EcmaRuntimeCallInfo *argv);
98    // 20.1.2.4
99    static JSTaggedValue IsNaN(EcmaRuntimeCallInfo *argv);
100    // 20.1.2.5
101    static JSTaggedValue IsSafeInteger(EcmaRuntimeCallInfo *argv);
102    // 20.1.2.12
103    static JSTaggedValue ParseFloat(EcmaRuntimeCallInfo *argv);
104    // 20.1.2.13
105    static JSTaggedValue ParseInt(EcmaRuntimeCallInfo *argv);
106
107    // prototype
108    // 20.1.3.2
109    static JSTaggedValue ToExponential(EcmaRuntimeCallInfo *argv);
110    // 20.1.3.3
111    static JSTaggedValue ToFixed(EcmaRuntimeCallInfo *argv);
112    // 20.1.3.4
113    static JSTaggedValue ToLocaleString(EcmaRuntimeCallInfo *argv);
114    // 20.1.3.5
115    static JSTaggedValue ToPrecision(EcmaRuntimeCallInfo *argv);
116    // 20.1.3.6
117    static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv);
118    // 20.1.3.7
119    static JSTaggedValue ValueOf(EcmaRuntimeCallInfo *argv);
120
121    // Excluding the '@@' internal properties.
122    static Span<const base::BuiltinConstantEntry> GetNumberConstants()
123    {
124        return Span<const base::BuiltinConstantEntry>(NUMBER_CONSTANTS);
125    }
126
127    // Excluding the '@@' internal properties.
128    static Span<const base::BuiltinFunctionEntry> GetNumberNonGlobalFunctions()
129    {
130        return Span<const base::BuiltinFunctionEntry>(NUMBER_NON_GLOBAL_FUNCTIONS);
131    }
132
133    // Excluding the '@@' internal properties.
134    static Span<const base::BuiltinFunctionEntry> GetNumberGlobalFunctions()
135    {
136        return Span<const base::BuiltinFunctionEntry>(NUMBER_GLOBAL_FUNCTIONS);
137    }
138
139    // Excluding the constructor and '@@' internal properties.
140    static Span<const base::BuiltinFunctionEntry> GetNumberPrototypeFunctions()
141    {
142        return Span<const base::BuiltinFunctionEntry>(NUMBER_PROTOTYPE_FUNCTIONS);
143    }
144
145private:
146#define BUILTIN_NUMBER_CONSTANT_ENTRY(name) \
147    base::BuiltinConstantEntry::Create(#name, JSTaggedValue(BuiltinsNumber::name)),
148
149    static inline std::array NUMBER_CONSTANTS = {
150        BUILTIN_NUMBER_CONSTANTS(BUILTIN_NUMBER_CONSTANT_ENTRY)
151    };
152#undef BUILTIN_NUMBER_CONSTANT_ENTRY
153
154#define BUILTIN_NUMBER_FUNCTION_ENTRY(name, func, length, id) \
155    base::BuiltinFunctionEntry::Create(name, BuiltinsNumber::func, length, kungfu::BuiltinsStubCSigns::id),
156
157    static constexpr std::array NUMBER_NON_GLOBAL_FUNCTIONS = {
158        BUILTIN_NUMBER_NON_GLOBAL_FUNCTIONS(BUILTIN_NUMBER_FUNCTION_ENTRY)
159    };
160    static constexpr std::array NUMBER_GLOBAL_FUNCTIONS = {
161        BUILTIN_NUMBER_GLOBAL_FUNCTIONS(BUILTIN_NUMBER_FUNCTION_ENTRY)
162    };
163    static constexpr std::array NUMBER_PROTOTYPE_FUNCTIONS = {
164        BUILTIN_NUMBER_PROTOTYPE_FUNCTIONS(BUILTIN_NUMBER_FUNCTION_ENTRY)
165    };
166#undef BUILTIN_NUMBER_FUNCTION_ENTRY
167
168    static JSTaggedNumber ThisNumberValue(JSThread *thread, EcmaRuntimeCallInfo *argv);
169};
170
171class NumberToStringResultCache : public TaggedArray {
172public:
173    static NumberToStringResultCache *Cast(TaggedObject *object)
174    {
175        return reinterpret_cast<NumberToStringResultCache*>(object);
176    }
177    static JSTaggedValue CreateCacheTable(const JSThread *thread);
178    JSTaggedValue FindCachedResult(int entry, JSTaggedValue &target);
179    void SetCachedResult(const JSThread *thread, int entry, JSTaggedValue &number, JSHandle<EcmaString> &result);
180    int GetNumberHash(JSTaggedValue &number)
181    {
182        unsigned int mask = INITIAL_CACHE_NUMBER - 1;
183        unsigned int value = 0;
184        if (number.IsInt()) {
185            value = static_cast<unsigned int>(number.GetInt());
186        } else {
187            int64_t bits = base::bit_cast<int64_t>(number.GetDouble());
188            value = static_cast<unsigned int>(bits) ^ static_cast<unsigned int>(bits >> 32); // 32: hight 32 bit
189        }
190        return value & mask;
191    }
192
193private:
194    static constexpr int INITIAL_CACHE_NUMBER = 256;
195    static constexpr int NUMBER_INDEX = 0;
196    static constexpr int RESULT_INDEX = 1;
197    static constexpr int ENTRY_SIZE = 2;
198};
199}  // namespace panda::ecmascript::builtins
200#endif  // ECMASCRIPT_BUILTINS_BUILTINS_NUBMER_H
201