• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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 #include "numberLiteral.h"
17 
18 #include "checker/TSchecker.h"
19 #include "compiler/core/ETSGen.h"
20 #include "compiler/core/pandagen.h"
21 #include "util/dtoa_helper.h"
22 
23 #include <cstdlib>
24 
25 namespace ark::es2panda::ir {
26 
27 inline constexpr size_t BUF_SIZE = 128;
28 inline constexpr int TWO = 2;
29 inline constexpr int SIX = 6;
30 inline constexpr int TWENTY_ONE = 21;
31 inline constexpr size_t TEN = 10;
32 
TransformChildren(const NodeTransformer & cb,std::string_view const transformationName)33 void NumberLiteral::TransformChildren([[maybe_unused]] const NodeTransformer &cb,
34                                       [[maybe_unused]] std::string_view const transformationName)
35 {
36 }
37 
Iterate(const NodeTraverser & cb) const38 void NumberLiteral::Iterate([[maybe_unused]] const NodeTraverser &cb) const {}
39 
Dump(ir::AstDumper * dumper) const40 void NumberLiteral::Dump(ir::AstDumper *dumper) const
41 {
42     dumper->Add({{"type", "NumberLiteral"}, {"value", number_}});
43 }
44 
Dump(ir::SrcDumper * dumper) const45 void NumberLiteral::Dump(ir::SrcDumper *dumper) const
46 {
47     if (std::string(number_.Str()).empty() || (parent_ != nullptr && parent_->IsTSEnumMember())) {
48         if (number_.IsInt()) {
49             dumper->Add(number_.GetInt());
50             return;
51         }
52 
53         if (number_.IsLong()) {
54             dumper->Add(number_.GetLong());
55             return;
56         }
57 
58         if (number_.IsFloat()) {
59             dumper->Add(number_.GetFloat());
60             return;
61         }
62 
63         if (number_.IsDouble()) {
64             dumper->Add(number_.GetDouble());
65             return;
66         }
67     }
68     dumper->Add(std::string(number_.Str()));
69 }
70 
Compile(compiler::PandaGen * pg) const71 void NumberLiteral::Compile(compiler::PandaGen *pg) const
72 {
73     pg->GetAstCompiler()->Compile(this);
74 }
75 
Compile(compiler::ETSGen * etsg) const76 void NumberLiteral::Compile(compiler::ETSGen *etsg) const
77 {
78     etsg->GetAstCompiler()->Compile(this);
79 }
80 
Check(checker::TSChecker * checker)81 checker::Type *NumberLiteral::Check(checker::TSChecker *checker)
82 {
83     return checker->GetAnalyzer()->Check(this);
84 }
85 
Check(checker::ETSChecker * checker)86 checker::VerifiedType NumberLiteral::Check(checker::ETSChecker *checker)
87 {
88     return {this, checker->GetAnalyzer()->Check(this)};
89 }
90 
Clone(ArenaAllocator * const allocator,AstNode * const parent)91 NumberLiteral *NumberLiteral::Clone(ArenaAllocator *const allocator, AstNode *const parent)
92 {
93     auto *const clone = allocator->New<NumberLiteral>(number_);
94     ES2PANDA_ASSERT(clone != nullptr);
95     if (parent != nullptr) {
96         clone->SetParent(parent);
97     }
98     clone->SetRange(Range());
99     return clone;
100 }
101 
102 // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
103 template <typename FpType, std::enable_if_t<std::is_floating_point_v<FpType>, bool> = true>
StrToFp(char * str,char ** strEnd)104 static FpType StrToFp(char *str, char **strEnd)
105 {
106     if constexpr (std::is_same_v<FpType, double>) {
107         return std::strtod(str, strEnd);
108     } else {
109         return std::strtof(str, strEnd);
110     }
111 }
112 
ParseExponent(const char * from,const char * end)113 static int ParseExponent(const char *from, const char *end)
114 {
115     int result = 0;
116     bool negative = false;
117 
118     if (*from == '-') {
119         negative = true;
120         ++from;
121     } else if (*from == '+') {
122         ++from;
123     }
124 
125     // Parse the digits of the exponent
126     while (from != end && (std::isdigit(*from) != 0)) {
127         result = result * TEN + (*from - '0');
128         ++from;
129     }
130 
131     if (negative) {
132         result = -result;
133     }
134 
135     return result;
136 }
137 
138 template <typename FpType, std::enable_if_t<std::is_floating_point_v<FpType>, bool> = true>
GetBase(FpType d,int digits,int * decpt,Span<char> buf)139 void GetBase(FpType d, int digits, int *decpt, Span<char> buf)
140 {
141     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
142     auto ret = snprintf_s(buf.begin(), buf.size(), buf.size() - 1, "%.*e", digits - 1, d);
143     if (ret == -1) {
144         ES2PANDA_UNREACHABLE();
145     }
146     char *end = buf.begin() + ret;
147     ES2PANDA_ASSERT(*end == 0);
148     const size_t positive = (digits > 1) ? 1 : 0;
149     char *ePos = buf.begin() + digits + positive;
150     ES2PANDA_ASSERT(*ePos == 'e');
151     char *signPos = ePos + 1;
152     char *from = signPos + 1;
153     // exponent
154     *decpt = ParseExponent(from, end);
155 
156     if (*signPos == '-') {
157         *decpt *= -1;
158     }
159     ++*decpt;
160 }
161 
162 template <typename FpType>
GetBaseBinarySearch(FpType d,int * decpt,Span<char> buf)163 static int GetBaseBinarySearch(FpType d, int *decpt, Span<char> buf)
164 {
165     // find the minimum amount of digits
166     int minDigits = 1;
167     int maxDigits = std::is_same_v<FpType, double> ? ark::es2panda::util::DOUBLE_MAX_PRECISION
168                                                    : ark::es2panda::util::FLOAT_MAX_PRECISION;
169     int digits;
170 
171     while (minDigits < maxDigits) {
172         digits = (minDigits + maxDigits) / TWO;
173         GetBase(d, digits, decpt, buf);
174 
175         bool same = StrToFp<FpType>(buf.begin(), nullptr) == d;
176         if (same) {
177             // no need to keep the trailing zeros
178             while (digits >= 2 && buf[digits] == '0') {  // 2 means ignore the integer and point
179                 digits--;
180             }
181             maxDigits = digits;
182         } else {
183             minDigits = digits + 1;
184         }
185     }
186     digits = maxDigits;
187     GetBase(d, digits, decpt, buf);
188     return digits;
189 }
190 
191 template <typename IntegerType>
IntegerToString(IntegerType number,std::string & resStr)192 static void IntegerToString(IntegerType number, std::string &resStr)
193 {
194     std::ostringstream oss;
195     oss << number;
196     resStr = oss.str();
197 }
198 
199 template <typename FpType>
RecheckGetMinimumDigits(FpType d,Span<char> buf)200 [[maybe_unused]] static bool RecheckGetMinimumDigits(FpType d, Span<char> buf)
201 {
202     ES2PANDA_ASSERT(StrToFp<FpType>(buf.begin(), nullptr) == d);
203     std::string str(buf.begin());
204     auto pos = str.find('e');
205     std::copy(str.begin() + pos, str.end(), str.begin() + pos - 1);
206     str[str.size() - 1] = '\0';
207     return StrToFp<FpType>(str.data(), nullptr) != d;
208 }
209 
210 // result is written starting with buf[1]
211 template <typename FpType>
GetMinimumDigits(FpType d,int * decpt,Span<char> buf)212 static int GetMinimumDigits(FpType d, int *decpt, Span<char> buf)
213 {
214     if (std::is_same_v<FpType, double>) {
215         util::DtoaHelper helper(buf.begin() + 1);
216         helper.Dtoa(d);
217         *decpt = helper.GetPoint();
218         return helper.GetDigits();
219     }
220     // Note(daizihan): Can use GetBaseFast as in runtime implementation, but need charconv supported by compiler.
221     int digits = GetBaseBinarySearch(d, decpt, buf);
222     ES2PANDA_ASSERT(RecheckGetMinimumDigits(d, buf));
223     ES2PANDA_ASSERT(digits == 1 || buf[1] == '.');
224     buf[1] = buf[0];
225     return digits;
226 }
227 
228 template <typename FpType>
FpToStringDecimalRadixMainCase(FpType number,bool negative,Span<char> buffer)229 static Span<char> FpToStringDecimalRadixMainCase(FpType number, bool negative, Span<char> buffer)
230 {
231     auto bufferStart = buffer.begin() + 2U;
232     ES2PANDA_ASSERT(number > 0);
233     int n = 0;
234     int k = GetMinimumDigits(number, &n, buffer.SubSpan(1));
235     auto bufferEnd = bufferStart + k;
236 
237     if (0 < n && n <= TWENTY_ONE) {  // NOLINT(readability-magic-numbers)
238         if (k <= n) {
239             // 6. If k ≤ n ≤ 21, return the String consisting of the code units of the k digits of the decimal
240             // representation of s (in order, with no leading zeroes), followed by n−k occurrences of the code unit
241             // 0x0030 (DIGIT ZERO).
242             std::fill_n(bufferEnd, n - k, '0');
243             bufferEnd += n - k;
244         } else {
245             // 7. If 0 < n ≤ 21, return the String consisting of the code units of the most significant n digits of the
246             // decimal representation of s, followed by the code unit 0x002E (FULL STOP), followed by the code units of
247             // the remaining k−n digits of the decimal representation of s.
248             auto fracStart = bufferStart + n;
249             if (memmove_s(fracStart + 1, buffer.end() - (fracStart + 1), fracStart, k - n) != EOK) {
250                 ES2PANDA_UNREACHABLE();
251             }
252             *fracStart = '.';
253             bufferEnd++;
254         }
255     } else if (-SIX < n && n <= 0) {  // NOLINT(readability-magic-numbers)
256         // 8. If −6 < n ≤ 0, return the String consisting of the code unit 0x0030 (DIGIT ZERO), followed by the code
257         // unit 0x002E (FULL STOP), followed by −n occurrences of the code unit 0x0030 (DIGIT ZERO), followed by the
258         // code units of the k digits of the decimal representation of s.
259         int length = -n + 2U;
260         auto fracStart = bufferStart + length;
261         if (memmove_s(fracStart, buffer.end() - fracStart, bufferStart, k) != EOK) {
262             ES2PANDA_UNREACHABLE();
263         }
264         std::fill_n(bufferStart, length, '0');
265         bufferStart[1] = '.';
266         bufferEnd += length;
267     } else {
268         if (k == 1) {
269             // 9. Otherwise, if k = 1, return the String consisting of the code unit of the single digit of s
270             ES2PANDA_ASSERT(bufferEnd == bufferStart + 1);
271         } else {
272             *(bufferStart - 1) = *bufferStart;
273             *(bufferStart--) = '.';
274         }
275         // followed by code unit 0x0065 (LATIN SMALL LETTER E), followed by the code unit 0x002B (PLUS SIGN) or the code
276         // unit 0x002D (HYPHEN-MINUS) according to whether n−1 is positive or negative, followed by the code units of
277         // the decimal representation of the integer abs(n−1) (with no leading zeroes).
278         *(bufferEnd++) = 'e';
279         if (n >= 1) {
280             *(bufferEnd++) = '+';
281         }
282         std::string result = std::to_string(n - 1);
283         if (bufferEnd + result.size() <= buffer.end()) {
284             bufferEnd = std::copy(result.begin(), result.end(), bufferEnd);
285         } else {
286             ES2PANDA_UNREACHABLE();
287         }
288     }
289     if (negative) {
290         *--bufferStart = '-';
291     }
292     return {bufferStart, bufferEnd};
293 }
294 
295 template <typename FpType>
SmallFpToString(FpType number,bool negative,char * buffer)296 static char *SmallFpToString(FpType number, bool negative, char *buffer)
297 {
298     using SignedInt = typename ark::helpers::TypeHelperT<sizeof(FpType) * CHAR_BIT, true>;
299     if (negative) {
300         *(buffer++) = '-';
301     }
302     *(buffer++) = '0';
303     *(buffer++) = '.';
304     SignedInt power = TEN;
305     SignedInt s = 0;
306     int maxDigits = std::is_same_v<FpType, double> ? ark::es2panda::util::DOUBLE_MAX_PRECISION
307                                                    : ark::es2panda::util::FLOAT_MAX_PRECISION;
308     int digits = maxDigits;
309     for (int k = 1; k <= maxDigits; ++k) {
310         s = static_cast<SignedInt>(number * power);
311         if (k == maxDigits || s / static_cast<FpType>(power) == number) {  // s * (10 ** -k)
312             digits = k;
313             break;
314         }
315         power *= TEN;
316     }
317     for (int k = digits - 1; k >= 0; k--) {
318         auto digit = std::abs(s) % TEN;
319         s /= TEN;
320         *(buffer + k) = '0' + digit;
321     }
322     return buffer + digits;
323 }
324 
325 template <typename FpType>
FpNonFiniteToString(FpType number)326 static const char *FpNonFiniteToString(FpType number)
327 {
328     ES2PANDA_ASSERT(std::isnan(number) || !std::isfinite(number));
329     if (std::isnan(number)) {
330         return "NaN";
331     }
332     return std::signbit(number) ? "-Infinity" : "Infinity";
333 }
334 
335 template <typename FpType>
FpToString(FpType number,std::string & resStr)336 static void FpToString(FpType number, std::string &resStr)
337 {
338     static constexpr FpType MIN_BOUND = 0.1;
339 
340     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
341     std::array<char, BUF_SIZE + 2U> buffer;
342     if (INT_MIN < number && number < static_cast<FpType>(INT_MAX)) {
343         if (auto intVal = static_cast<int32_t>(number); number == static_cast<double>(intVal)) {
344             IntegerToString(intVal, resStr);
345             return;
346         }
347     }
348 
349     // isfinite checks if the number is normal, subnormal or zero, but not infinite or NaN.
350     if (!std::isfinite(number)) {
351         auto *str = FpNonFiniteToString(number);
352         resStr.assign(str);
353         return;
354     }
355 
356     bool negative = false;
357     if (number < 0) {
358         negative = true;
359         number = -number;
360     }
361 
362     if (!std::is_same_v<FpType, double> && MIN_BOUND <= number && number < 1) {
363         // Fast path. In this case, n==0, just need to calculate k and s.
364         auto bufferEnd = SmallFpToString(number, negative, buffer.begin());
365         resStr.assign(buffer.data(), bufferEnd - buffer.data());
366         return;
367     }
368 
369     auto newBuffer = FpToStringDecimalRadixMainCase(number, negative, Span(buffer));
370     resStr.assign(newBuffer.begin(), newBuffer.size());
371 }
372 // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
373 
ToString() const374 std::string NumberLiteral::ToString() const
375 {
376     std::string result {};
377     if (number_.IsInt()) {
378         IntegerToString(number_.GetInt(), result);
379     }
380 
381     if (number_.IsLong()) {
382         IntegerToString(number_.GetLong(), result);
383     }
384 
385     if (number_.IsDouble()) {
386         FpToString(number_.GetDouble(), result);
387     }
388 
389     if (number_.IsFloat()) {
390         FpToString(number_.GetFloat(), result);
391     }
392 
393     ES2PANDA_ASSERT(!result.empty());
394     return result;
395 }
396 }  // namespace ark::es2panda::ir
397