1 /**
2 * Copyright (c) 2021-2024 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
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 if (UNLIKELY(radix != helpers::DECIMAL)) {
55 return helpers::FpToString(number, radix);
56 }
57 auto *cache = PandaEtsVM::GetCurrent()->GetDoubleToStringCache();
58 ASSERT(cache != nullptr);
59 return cache->GetOrCache(EtsCoroutine::GetCurrent(), number);
60 }
61
StdCoreDoubleToLocaleString(ObjectHeader * obj,EtsString * locale)62 EtsString *StdCoreDoubleToLocaleString(ObjectHeader *obj, EtsString *locale)
63 {
64 ASSERT(obj != nullptr && locale != nullptr);
65 icu::Locale loc;
66 UErrorCode status = U_ZERO_ERROR;
67 PandaVector<uint8_t> buf;
68 std::string_view locTag = locale->ConvertToStringView(&buf);
69 icu::StringPiece sp {locTag.data(), static_cast<int32_t>(locTag.size())};
70 loc = icu::Locale::forLanguageTag(sp, status);
71
72 if (UNLIKELY(U_FAILURE(status))) {
73 std::string message = "Language tag '" + std::string(locTag) + "' is invalid or not supported";
74 ThrowEtsException(EtsCoroutine::GetCurrent(), panda_file_items::class_descriptors::RANGE_ERROR, message);
75 return nullptr;
76 }
77
78 double objValue = helpers::GetStdDoubleArgument(obj);
79 if (std::isnan(objValue)) {
80 return EtsString::CreateFromMUtf8("NaN");
81 }
82
83 icu::number::LocalizedNumberFormatter locNumFmt = icu::number::NumberFormatter::withLocale(loc);
84 icu::number::FormattedNumber fmtNum = locNumFmt.formatDouble(objValue, status);
85
86 if (UNLIKELY(U_FAILURE(status))) {
87 std::string message = "Unable to convert " + std::to_string(objValue) + " to locale " + std::string(locTag);
88 ThrowEtsException(EtsCoroutine::GetCurrent(), panda_file_items::class_descriptors::RUNTIME_EXCEPTION, message);
89 return nullptr;
90 }
91
92 icu::UnicodeString uniStr = fmtNum.toString(status);
93 return EtsString::CreateFromUtf16(reinterpret_cast<const uint16_t *>(uniStr.getBuffer()), uniStr.length());
94 }
95
StdCoreDoubleParseFloat(EtsString * s)96 double StdCoreDoubleParseFloat(EtsString *s)
97 {
98 return ParseFloat(s, helpers::flags::IGNORE_TRAILING);
99 }
100
StdCoreDoubleParseInt(EtsString * s,int32_t radix)101 double StdCoreDoubleParseInt(EtsString *s, int32_t radix)
102 {
103 bool isUtf16 = s->IsUtf16();
104 size_t startIndex = 0;
105 size_t length = s->GetLength();
106 const int baseDec = 10;
107 const int baseHex = 16;
108
109 Span<uint16_t> utf16Span {};
110 Span<uint8_t> mutf8Span {};
111 if (isUtf16) {
112 utf16Span = {s->GetDataUtf16(), s->GetUtf16Length()};
113 } else {
114 mutf8Span = {s->GetDataMUtf8(), s->GetMUtf8Length()};
115 }
116
117 if (length >= 1) {
118 char firstChar = isUtf16 ? utf16Span[0] : mutf8Span[0];
119 if (firstChar == '-') {
120 startIndex = 1;
121 }
122 }
123
124 if (radix == -1 || radix == 0) {
125 radix = baseDec;
126 if (length >= 2U + startIndex) {
127 auto first = isUtf16 ? utf16Span[startIndex] : mutf8Span[startIndex];
128 auto second = isUtf16 ? utf16Span[startIndex + 1] : mutf8Span[startIndex + 1];
129 if (first == '0' && (second == 'x' || second == 'X')) {
130 radix = baseHex;
131 }
132 }
133 }
134
135 if (isUtf16) {
136 size_t utf16Length = s->GetUtf16Length();
137 size_t utf8Size = utf::Utf16ToUtf8Size(s->GetDataUtf16(), utf16Length) - 1;
138 PandaVector<uint8_t> buf(utf8Size);
139 size_t convertedSize = utf::ConvertRegionUtf16ToUtf8(s->GetDataUtf16(), buf.data(), length, utf8Size, 0);
140 Span<uint8_t> str = Span<uint8_t>(buf.data(), convertedSize);
141 return std::trunc(helpers::StringToDoubleWithRadix(str.begin(), str.end(), radix));
142 }
143 Span<uint8_t> str = Span<uint8_t>(s->GetDataMUtf8(), s->GetMUtf8Length() - 1);
144 return std::trunc(helpers::StringToDoubleWithRadix(str.begin(), str.end(), radix));
145 }
146
StdCoreDoubleToExponential(ObjectHeader * obj,double d)147 EtsString *StdCoreDoubleToExponential(ObjectHeader *obj, double d)
148 {
149 double objValue = helpers::GetStdDoubleArgument(obj);
150 // If x is NaN, return the String "NaN".
151 if (std::isnan(objValue)) {
152 return EtsString::CreateFromMUtf8("NaN");
153 }
154 // If x < 0, then
155 // a. Let s be "-".
156 // b. Let x = –x.
157 // If x = +infinity, then
158 // a. Return the concatenation of the Strings s and "Infinity".
159 if (!std::isfinite(objValue)) {
160 if (objValue < 0) {
161 return EtsString::CreateFromMUtf8("-Infinity");
162 }
163 return EtsString::CreateFromMUtf8("Infinity");
164 }
165
166 // truncate the arg val
167 double digitAbs = std::isnan(d) ? 0 : d;
168 digitAbs = std::abs((digitAbs >= 0) ? std::floor(digitAbs) : std::ceil(digitAbs));
169 // Check range
170 if (UNLIKELY(digitAbs > helpers::MAX_FRACTION || digitAbs < helpers::MIN_FRACTION)) {
171 ThrowEtsException(EtsCoroutine::GetCurrent(),
172 panda_file_items::class_descriptors::ARGUMENT_OUT_OF_RANGE_EXCEPTION,
173 "toExponential argument must be between 0 and 100");
174 return nullptr;
175 }
176
177 return helpers::DoubleToExponential(objValue, static_cast<int>(digitAbs));
178 }
179
StdCoreDoubleToPrecision(ObjectHeader * obj,double d)180 EtsString *StdCoreDoubleToPrecision(ObjectHeader *obj, double d)
181 {
182 double objValue = helpers::GetStdDoubleArgument(obj);
183 // If x is NaN, return the String "NaN".
184 if (std::isnan(objValue)) {
185 return EtsString::CreateFromMUtf8("NaN");
186 }
187 // If x < 0, then
188 // a. Let s be "-".
189 // b. Let x = –x.
190 // If x = +infinity, then
191 // a. Return the concatenation of the Strings s and "Infinity".
192 if (!std::isfinite(objValue)) {
193 if (objValue < 0) {
194 return EtsString::CreateFromMUtf8("-Infinity");
195 }
196 return EtsString::CreateFromMUtf8("Infinity");
197 }
198
199 // truncate the arg val
200 double digitAbs = std::isnan(d) ? 0 : d;
201 digitAbs = std::abs((digitAbs >= 0) ? std::floor(digitAbs) : std::ceil(digitAbs));
202 // Check range
203 if (UNLIKELY(digitAbs > helpers::MAX_FRACTION || digitAbs < helpers::MIN_FRACTION + 1)) {
204 ThrowEtsException(EtsCoroutine::GetCurrent(),
205 panda_file_items::class_descriptors::ARGUMENT_OUT_OF_RANGE_EXCEPTION,
206 "toPrecision argument must be between 1 and 100");
207 return nullptr;
208 }
209
210 return helpers::DoubleToPrecision(objValue, static_cast<int>(digitAbs));
211 }
212
StdCoreDoubleToFixed(ObjectHeader * obj,double d)213 EtsString *StdCoreDoubleToFixed(ObjectHeader *obj, double d)
214 {
215 double objValue = helpers::GetStdDoubleArgument(obj);
216 // If x is NaN, return the String "NaN".
217 if (std::isnan(objValue)) {
218 return EtsString::CreateFromMUtf8("NaN");
219 }
220 // If x < 0, then
221 // a. Let s be "-".
222 // b. Let x = –x.
223 // If x = +infinity, then
224 // a. Return the concatenation of the Strings s and "Infinity".
225 if (!std::isfinite(objValue)) {
226 if (objValue < 0) {
227 return EtsString::CreateFromMUtf8("-Infinity");
228 }
229 return EtsString::CreateFromMUtf8("Infinity");
230 }
231
232 // truncate the arg val
233 double digitAbs = std::isnan(d) ? 0 : d;
234 digitAbs = std::abs((digitAbs >= 0) ? std::floor(digitAbs) : std::ceil(digitAbs));
235 // Check range
236 if (UNLIKELY(digitAbs > helpers::MAX_FRACTION || digitAbs < helpers::MIN_FRACTION)) {
237 ThrowEtsException(EtsCoroutine::GetCurrent(),
238 panda_file_items::class_descriptors::ARGUMENT_OUT_OF_RANGE_EXCEPTION,
239 "toFixed argument must be between 0 and 100");
240 return nullptr;
241 }
242
243 return helpers::DoubleToFixed(objValue, static_cast<int>(digitAbs));
244 }
245
StdCoreDoubleIsNan(double v)246 extern "C" EtsBoolean StdCoreDoubleIsNan(double v)
247 {
248 return ToEtsBoolean(v != v);
249 }
250
StdCoreDoubleIsFinite(double v)251 extern "C" EtsBoolean StdCoreDoubleIsFinite(double v)
252 {
253 static const double POSITIVE_INFINITY = 1.0 / 0.0;
254 static const double NEGATIVE_INFINITY = -1.0 / 0.0;
255
256 return ToEtsBoolean(v == v && v != POSITIVE_INFINITY && v != NEGATIVE_INFINITY);
257 }
258
StdCoreDoubleBitCastFromLong(EtsLong i)259 extern "C" EtsDouble StdCoreDoubleBitCastFromLong(EtsLong i)
260 {
261 return bit_cast<EtsDouble>(i);
262 }
263
StdCoreDoubleBitCastToLong(EtsDouble f)264 extern "C" EtsLong StdCoreDoubleBitCastToLong(EtsDouble f)
265 {
266 return bit_cast<EtsLong>(f);
267 }
268
IsInteger(double v)269 static inline bool IsInteger(double v)
270 {
271 return std::isfinite(v) && (std::fabs(v - std::trunc(v)) <= std::numeric_limits<double>::epsilon());
272 }
273
StdCoreDoubleIsInteger(double v)274 extern "C" EtsBoolean StdCoreDoubleIsInteger(double v)
275 {
276 return ToEtsBoolean(IsInteger(v));
277 }
278
279 /*
280 * In ETS Double.isSafeInteger returns (Double.isInteger(v) && (abs(v) <= Double.MAX_SAFE_INTEGER)).
281 * MAX_SAFE_INTEGER is a max integer value that can be used as a double without losing precision.
282 */
StdCoreDoubleIsSafeInteger(double v)283 extern "C" EtsBoolean StdCoreDoubleIsSafeInteger(double v)
284 {
285 return ToEtsBoolean(IsInteger(v) && (std::fabs(v) <= helpers::MaxSafeInteger<double>()));
286 }
287
StdCoreDoubleNumberFromString(EtsString * s)288 double StdCoreDoubleNumberFromString(EtsString *s)
289 {
290 uint32_t flags = 0;
291 flags |= helpers::flags::ALLOW_BINARY;
292 flags |= helpers::flags::ALLOW_OCTAL;
293 flags |= helpers::flags::ALLOW_HEX;
294 flags |= helpers::flags::EMPTY_IS_ZERO;
295 flags |= helpers::flags::ERROR_IN_EXPONENT_IS_NAN;
296 return ParseFloat(s, flags);
297 }
298
299 } // namespace ark::ets::intrinsics
300