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