• 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 <cstdint>
17 #include <limits>
18 #include "include/mem/panda_string.h"
19 #include "intrinsics.h"
20 #include "plugins/ets/runtime/types/ets_string.h"
21 #include "plugins/ets/runtime/intrinsics/helpers/ets_intrinsics_helpers.h"
22 #include "plugins/ets/runtime/intrinsics/helpers/ets_to_string_cache.h"
23 #include "unicode/locid.h"
24 #include "unicode/coll.h"
25 #include "unicode/numberformatter.h"
26 #include "unicode/unistr.h"
27 #include "utils/utf.h"
28 #include "plugins/ets/runtime/intrinsics/helpers/dtoa_helper.h"
29 #include "plugins/ets/runtime/ets_exceptions.h"
30 
31 namespace ark::ets::intrinsics {
32 
33 namespace {
34 
ParseFloat(EtsString * s,const uint32_t flags)35 double ParseFloat(EtsString *s, const uint32_t flags)
36 {
37     if (UNLIKELY(s->IsUtf16())) {
38         size_t len = utf::Utf16ToUtf8Size(s->GetDataUtf16(), s->GetUtf16Length()) - 1;
39         PandaVector<uint8_t> buf(len);
40         len = utf::ConvertRegionUtf16ToUtf8(s->GetDataUtf16(), buf.data(), s->GetLength(), len, 0);
41 
42         Span<uint8_t> str = Span<uint8_t>(buf.data(), len);
43         return helpers::StringToDouble(str.begin(), str.end(), 0, flags);
44     }
45 
46     Span<uint8_t> str = Span<uint8_t>(s->GetDataMUtf8(), s->GetMUtf8Length() - 1);
47     return helpers::StringToDouble(str.begin(), str.end(), 0, flags);
48 }
49 
50 }  // namespace
51 
StdCoreDoubleToString(double number,int radix)52 EtsString *StdCoreDoubleToString(double number, int radix)
53 {
54     auto *cache = PandaEtsVM::GetCurrent()->GetDoubleToStringCache();
55     if (UNLIKELY(radix != helpers::DECIMAL || cache == nullptr)) {
56         return helpers::FpToString(number, radix);
57     }
58     return cache->GetOrCache(EtsCoroutine::GetCurrent(), number);
59 }
60 
IsNegativeNan(double x)61 bool IsNegativeNan(double x)
62 {
63     return std::isnan(x) && std::signbit(x);
64 }
65 
StdCoreDoubleParseFloat(EtsString * s)66 double StdCoreDoubleParseFloat(EtsString *s)
67 {
68     return ParseFloat(s, helpers::flags::IGNORE_TRAILING);
69 }
70 
StdCoreDoubleParseInt(EtsString * s,int32_t radix)71 double StdCoreDoubleParseInt(EtsString *s, int32_t radix)
72 {
73     bool isUtf16 = s->IsUtf16();
74     size_t startIndex = 0;
75     size_t length = s->GetLength();
76     const int baseDec = 10;
77     const int baseHex = 16;
78 
79     Span<uint16_t> utf16Span {};
80     Span<uint8_t> mutf8Span {};
81     if (isUtf16) {
82         utf16Span = {s->GetDataUtf16(), s->GetUtf16Length()};
83     } else {
84         mutf8Span = {s->GetDataMUtf8(), s->GetMUtf8Length()};
85     }
86 
87     if (length >= 1) {
88         char firstChar = isUtf16 ? utf16Span[0] : mutf8Span[0];
89         if (firstChar == '-') {
90             startIndex = 1;
91         }
92     }
93 
94     if (radix == -1 || radix == 0) {
95         radix = baseDec;
96         if (length >= 2U + startIndex) {
97             auto first = isUtf16 ? utf16Span[startIndex] : mutf8Span[startIndex];
98             auto second = isUtf16 ? utf16Span[startIndex + 1] : mutf8Span[startIndex + 1];
99             if (first == '0' && (second == 'x' || second == 'X')) {
100                 radix = baseHex;
101             }
102         }
103     }
104 
105     if (isUtf16) {
106         size_t utf16Length = s->GetUtf16Length();
107         size_t utf8Size = utf::Utf16ToUtf8Size(s->GetDataUtf16(), utf16Length) - 1;
108         PandaVector<uint8_t> buf(utf8Size);
109         size_t convertedSize = utf::ConvertRegionUtf16ToUtf8(s->GetDataUtf16(), buf.data(), length, utf8Size, 0);
110         Span<uint8_t> str = Span<uint8_t>(buf.data(), convertedSize);
111         return std::trunc(helpers::StringToDoubleWithRadix(str.begin(), str.end(), radix));
112     }
113     Span<uint8_t> str = Span<uint8_t>(s->GetDataMUtf8(), s->GetMUtf8Length() - 1);
114     return std::trunc(helpers::StringToDoubleWithRadix(str.begin(), str.end(), radix));
115 }
116 
StdCoreDoubleToExponential(ObjectHeader * obj,double d)117 EtsString *StdCoreDoubleToExponential(ObjectHeader *obj, double d)
118 {
119     EtsDouble objValue = EtsBoxPrimitive<EtsDouble>::Unbox(EtsObject::FromCoreType(obj));
120     // If x is NaN, return the String "NaN".
121     if (std::isnan(objValue)) {
122         return EtsString::CreateFromMUtf8("NaN");
123     }
124     // If x < 0, then
125     //    a. Let s be "-".
126     //    b. Let x = –x.
127     // If x = +infinity, then
128     //    a. Return the concatenation of the Strings s and "Infinity".
129     if (!std::isfinite(objValue)) {
130         if (objValue < 0) {
131             return EtsString::CreateFromMUtf8("-Infinity");
132         }
133         return EtsString::CreateFromMUtf8("Infinity");
134     }
135 
136     // truncate the arg val
137     double digit = std::isnan(d) ? 0 : d;
138     digit = (digit >= 0) ? std::floor(digit) : std::ceil(digit);
139     // Check range
140     if (UNLIKELY(digit > helpers::MAX_FRACTION || digit < helpers::MIN_FRACTION)) {
141         ThrowEtsException(EtsCoroutine::GetCurrent(),
142                           panda_file_items::class_descriptors::ARGUMENT_OUT_OF_RANGE_EXCEPTION,
143                           "toExponential argument must be between 0 and 100");
144         return nullptr;
145     }
146 
147     return helpers::DoubleToExponential(objValue, static_cast<int>(digit));
148 }
149 
StdCoreDoubleToExponentialWithNoDigit(ObjectHeader * obj)150 EtsString *StdCoreDoubleToExponentialWithNoDigit(ObjectHeader *obj)
151 {
152     EtsDouble objValue = EtsBoxPrimitive<EtsDouble>::Unbox(EtsObject::FromCoreType(obj));
153     // If x is NaN, return the String "NaN".
154     if (std::isnan(objValue)) {
155         return EtsString::CreateFromMUtf8("NaN");
156     }
157     // If x < 0, then
158     //    a. Let s be "-".
159     //    b. Let x = –x.
160     // If x = +infinity, then
161     //    a. Return the concatenation of the Strings s and "Infinity".
162     if (!std::isfinite(objValue)) {
163         if (objValue < 0) {
164             return EtsString::CreateFromMUtf8("-Infinity");
165         }
166         return EtsString::CreateFromMUtf8("Infinity");
167     }
168 
169     if (objValue == 0.0) {
170         return EtsString::CreateFromMUtf8("0e+0");
171     }
172     std::string res;
173     if (objValue < 0) {
174         res += "-";
175         objValue = -objValue;
176     }
177 
178     char tmpbuf[helpers::BUF_SIZE] = {0};
179     helpers::DtoaHelper dtoa {tmpbuf};
180     dtoa.Dtoa(objValue);
181     int n = dtoa.GetPoint();
182     int k = dtoa.GetDigits();
183 
184     std::string base = tmpbuf;
185     base.erase(1, k - 1);
186     if (k != 1) {
187         base += std::string(".") + std::string(&tmpbuf[1]);
188     }
189     base += "e" + (n >= 1 ? std::string("+") : "") + std::to_string(n - 1);
190     res += base;
191     return EtsString::CreateFromMUtf8(res.c_str());
192 }
193 
StdCoreDoubleToPrecision(ObjectHeader * obj,double d)194 EtsString *StdCoreDoubleToPrecision(ObjectHeader *obj, double d)
195 {
196     EtsDouble objValue = EtsBoxPrimitive<EtsDouble>::Unbox(EtsObject::FromCoreType(obj));
197     // If x is NaN, return the String "NaN".
198     if (std::isnan(objValue)) {
199         return EtsString::CreateFromMUtf8("NaN");
200     }
201     // If x < 0, then
202     //    a. Let s be "-".
203     //    b. Let x = –x.
204     // If x = +infinity, then
205     //    a. Return the concatenation of the Strings s and "Infinity".
206     if (!std::isfinite(objValue)) {
207         if (objValue < 0) {
208             return EtsString::CreateFromMUtf8("-Infinity");
209         }
210         return EtsString::CreateFromMUtf8("Infinity");
211     }
212 
213     // truncate the arg val
214     double digitAbs = std::isnan(d) ? 0 : d;
215     digitAbs = std::abs((digitAbs >= 0) ? std::floor(digitAbs) : std::ceil(digitAbs));
216     // Check range
217     if (UNLIKELY(digitAbs > helpers::MAX_FRACTION || digitAbs < helpers::MIN_FRACTION + 1)) {
218         ThrowEtsException(EtsCoroutine::GetCurrent(),
219                           panda_file_items::class_descriptors::ARGUMENT_OUT_OF_RANGE_EXCEPTION,
220                           "toPrecision argument must be between 1 and 100");
221         return nullptr;
222     }
223 
224     return helpers::DoubleToPrecision(objValue, static_cast<int>(digitAbs));
225 }
226 
StdCoreDoubleToFixed(ObjectHeader * obj,double d)227 EtsString *StdCoreDoubleToFixed(ObjectHeader *obj, double d)
228 {
229     // truncate the arg val
230     double digitAbs = std::isnan(d) ? 0 : d;
231     digitAbs = std::abs((digitAbs >= 0) ? std::floor(digitAbs) : std::ceil(digitAbs));
232     // Check range
233     if (UNLIKELY(digitAbs > helpers::MAX_FRACTION || digitAbs < helpers::MIN_FRACTION)) {
234         ThrowEtsException(EtsCoroutine::GetCurrent(), panda_file_items::class_descriptors::RANGE_ERROR,
235                           "toFixed argument must be between 0 and 100");
236         return nullptr;
237     }
238 
239     EtsDouble objValue = EtsBoxPrimitive<EtsDouble>::Unbox(EtsObject::FromCoreType(obj));
240     // If x is NaN, return the String "NaN".
241     if (std::isnan(objValue)) {
242         return EtsString::CreateFromMUtf8("NaN");
243     }
244     // If x < 0, then
245     //    a. Let s be "-".
246     //    b. Let x = –x.
247     // If x = +infinity, then
248     //    a. Return the concatenation of the Strings s and "Infinity".
249     if (!std::isfinite(objValue)) {
250         if (objValue < 0) {
251             return EtsString::CreateFromMUtf8("-Infinity");
252         }
253         return EtsString::CreateFromMUtf8("Infinity");
254     }
255 
256     return helpers::DoubleToFixed(objValue, static_cast<int>(digitAbs));
257 }
258 
StdCoreDoubleIsNan(double v)259 extern "C" EtsBoolean StdCoreDoubleIsNan(double v)
260 {
261     return ToEtsBoolean(v != v);
262 }
263 
StdCoreDoubleIsFinite(double v)264 extern "C" EtsBoolean StdCoreDoubleIsFinite(double v)
265 {
266     static const double POSITIVE_INFINITY = 1.0 / 0.0;
267     static const double NEGATIVE_INFINITY = -1.0 / 0.0;
268 
269     return ToEtsBoolean(v == v && v != POSITIVE_INFINITY && v != NEGATIVE_INFINITY);
270 }
271 
StdCoreDoubleBitCastFromLong(EtsLong i)272 extern "C" EtsDouble StdCoreDoubleBitCastFromLong(EtsLong i)
273 {
274     return bit_cast<EtsDouble>(i);
275 }
276 
StdCoreDoubleBitCastToLong(EtsDouble f)277 extern "C" EtsLong StdCoreDoubleBitCastToLong(EtsDouble f)
278 {
279     return bit_cast<EtsLong>(f);
280 }
281 
IsInteger(double v)282 static inline bool IsInteger(double v)
283 {
284     return std::isfinite(v) && (std::fabs(v - std::trunc(v)) <= std::numeric_limits<double>::epsilon());
285 }
286 
StdCoreDoubleIsInteger(double v)287 extern "C" EtsBoolean StdCoreDoubleIsInteger(double v)
288 {
289     return ToEtsBoolean(IsInteger(v));
290 }
291 
292 /*
293  * In ETS Double.isSafeInteger returns (Double.isInteger(v) && (abs(v) <= Double.MAX_SAFE_INTEGER)).
294  * MAX_SAFE_INTEGER is a max integer value that can be used as a double without losing precision.
295  */
StdCoreDoubleIsSafeInteger(double v)296 extern "C" EtsBoolean StdCoreDoubleIsSafeInteger(double v)
297 {
298     return ToEtsBoolean(IsInteger(v) && (std::fabs(v) <= helpers::MaxSafeInteger<double>()));
299 }
300 
StdCoreDoubleNumberFromString(EtsString * s)301 double StdCoreDoubleNumberFromString(EtsString *s)
302 {
303     uint32_t flags = 0;
304     flags |= helpers::flags::ALLOW_BINARY;
305     flags |= helpers::flags::ALLOW_OCTAL;
306     flags |= helpers::flags::ALLOW_HEX;
307     flags |= helpers::flags::EMPTY_IS_ZERO;
308     flags |= helpers::flags::ERROR_IN_EXPONENT_IS_NAN;
309     return ParseFloat(s, flags);
310 }
311 
StdCoreDoubleToShort(EtsDouble val)312 EtsShort StdCoreDoubleToShort(EtsDouble val)
313 {
314     // CC-OFFNXT(G.NAM.03) false positive
315     int intVal = CastFloatToInt<EtsDouble, EtsInt>(val);
316     return static_cast<int16_t>(intVal);
317 }
318 
StdCoreDoubleToByte(EtsDouble val)319 EtsByte StdCoreDoubleToByte(EtsDouble val)
320 {
321     // CC-OFFNXT(G.NAM.03) false positive
322     int intVal = CastFloatToInt<EtsDouble, EtsInt>(val);
323     return static_cast<int8_t>(intVal);
324 }
325 
StdCoreDoubleToInt(EtsDouble val)326 EtsInt StdCoreDoubleToInt(EtsDouble val)
327 {
328     return CastFloatToInt<EtsDouble, EtsInt>(val);
329 }
330 
StdCoreDoubleToLong(EtsDouble val)331 EtsLong StdCoreDoubleToLong(EtsDouble val)
332 {
333     return CastFloatToInt<EtsDouble, EtsLong>(val);
334 }
335 
StdCoreDoubleToFloat(EtsDouble val)336 EtsFloat StdCoreDoubleToFloat(EtsDouble val)
337 {
338     return static_cast<float>(val);
339 }
340 
StdCoreDoubleToChar(EtsDouble val)341 EtsChar StdCoreDoubleToChar(EtsDouble val)
342 {
343     return CastFloatToInt<EtsDouble, EtsChar>(val);
344 }
345 
346 }  // namespace ark::ets::intrinsics
347