• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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