1 /**
2 * Copyright (c) 2021-2022 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 "unicode/locid.h"
23 #include "unicode/coll.h"
24 #include "unicode/numberformatter.h"
25 #include "unicode/unistr.h"
26 #include "utils/utf.h"
27
28 namespace panda::ets::intrinsics {
29
StdCoreDoubleToString(double number,int radix)30 EtsString *StdCoreDoubleToString(double number, int radix)
31 {
32 return helpers::FpToString(number, radix);
33 }
34
StdCoreDoubleToLocaleString(ObjectHeader * obj,EtsString * locale)35 EtsString *StdCoreDoubleToLocaleString(ObjectHeader *obj, EtsString *locale)
36 {
37 ASSERT(obj != nullptr && locale != nullptr);
38 icu::Locale loc;
39 UErrorCode status = U_ZERO_ERROR;
40 PandaVector<uint8_t> buf;
41 std::string_view locTag = locale->ConvertToStringView(&buf);
42 icu::StringPiece sp {locTag.data(), static_cast<int32_t>(locTag.size())};
43 loc = icu::Locale::forLanguageTag(sp, status);
44
45 if (UNLIKELY(U_FAILURE(status))) {
46 std::string message = "Language tag '" + std::string(locTag) + "' is invalid or not supported";
47 ThrowEtsException(EtsCoroutine::GetCurrent(), panda_file_items::class_descriptors::RANGE_ERROR, message);
48 return nullptr;
49 }
50
51 double objValue = helpers::GetStdDoubleArgument(obj);
52
53 icu::number::LocalizedNumberFormatter locNumFmt = icu::number::NumberFormatter::withLocale(loc);
54 icu::number::FormattedNumber fmtNum = locNumFmt.formatDouble(objValue, status);
55
56 if (UNLIKELY(U_FAILURE(status))) {
57 std::string message = "Unable to convert " + std::to_string(objValue) + " to locale " + std::string(locTag);
58 ThrowEtsException(EtsCoroutine::GetCurrent(), panda_file_items::class_descriptors::RUNTIME_EXCEPTION, message);
59 return nullptr;
60 }
61
62 icu::UnicodeString uniStr = fmtNum.toString(status);
63 return EtsString::CreateFromUtf16(reinterpret_cast<const uint16_t *>(uniStr.getBuffer()), uniStr.length());
64 }
65
StdCoreDoubleParseFloat(EtsString * s)66 double StdCoreDoubleParseFloat(EtsString *s)
67 {
68 if (UNLIKELY(s->IsUtf16())) {
69 size_t len = utf::Utf16ToUtf8Size(s->GetDataUtf16(), s->GetUtf16Length()) - 1;
70 PandaVector<uint8_t> buf(len);
71 len = utf::ConvertRegionUtf16ToUtf8(s->GetDataUtf16(), buf.data(), s->GetLength(), len, 0);
72
73 Span<uint8_t> str = Span<uint8_t>(buf.data(), len);
74 return helpers::StringToDouble(str.begin(), str.end(), 0, helpers::flags::IGNORE_TRAILING);
75 }
76
77 Span<uint8_t> str = Span<uint8_t>(s->GetDataMUtf8(), s->GetMUtf8Length() - 1);
78 return helpers::StringToDouble(str.begin(), str.end(), 0, helpers::flags::IGNORE_TRAILING);
79 }
80
StdCoreDoubleParseInt(EtsString * s,int32_t radix)81 double StdCoreDoubleParseInt(EtsString *s, int32_t radix)
82 {
83 if (UNLIKELY(s->IsUtf16())) {
84 size_t len = utf::Utf16ToUtf8Size(s->GetDataUtf16(), s->GetUtf16Length()) - 1;
85 PandaVector<uint8_t> buf(len);
86 len = utf::ConvertRegionUtf16ToUtf8(s->GetDataUtf16(), buf.data(), s->GetLength(), len, 0);
87
88 Span<uint8_t> str = Span<uint8_t>(buf.data(), len);
89 return std::abs(helpers::StringToDoubleWithRadix(str.begin(), str.end(), radix));
90 }
91
92 Span<uint8_t> str = Span<uint8_t>(s->GetDataMUtf8(), s->GetMUtf8Length() - 1);
93 return std::trunc(helpers::StringToDoubleWithRadix(str.begin(), str.end(), radix));
94 }
95
StdCoreDoubleToExponential(ObjectHeader * obj,double d)96 EtsString *StdCoreDoubleToExponential(ObjectHeader *obj, double d)
97 {
98 double objValue = helpers::GetStdDoubleArgument(obj);
99 // If x is NaN, return the String "NaN".
100 if (std::isnan(objValue)) {
101 return EtsString::CreateFromMUtf8("NaN");
102 }
103 // If x < 0, then
104 // a. Let s be "-".
105 // b. Let x = –x.
106 // If x = +infinity, then
107 // a. Return the concatenation of the Strings s and "Infinity".
108 if (!std::isfinite(objValue)) {
109 if (objValue < 0) {
110 return EtsString::CreateFromMUtf8("-Infinity");
111 }
112 return EtsString::CreateFromMUtf8("Infinity");
113 }
114
115 // truncate the arg val
116 double digitAbs = std::isnan(d) ? 0 : d;
117 digitAbs = std::abs((digitAbs >= 0) ? std::floor(digitAbs) : std::ceil(digitAbs));
118 // Check range
119 if (UNLIKELY(digitAbs > helpers::MAX_FRACTION || digitAbs < helpers::MIN_FRACTION)) {
120 ThrowEtsException(EtsCoroutine::GetCurrent(),
121 panda_file_items::class_descriptors::ARGUMENT_OUT_OF_RANGE_EXCEPTION,
122 "toExponential argument must be between 0 and 100");
123 return nullptr;
124 }
125
126 return helpers::DoubleToExponential(objValue, static_cast<int>(digitAbs));
127 }
128
StdCoreDoubleToPrecision(ObjectHeader * obj,double d)129 EtsString *StdCoreDoubleToPrecision(ObjectHeader *obj, double d)
130 {
131 double objValue = helpers::GetStdDoubleArgument(obj);
132 // If x is NaN, return the String "NaN".
133 if (std::isnan(objValue)) {
134 return EtsString::CreateFromMUtf8("NaN");
135 }
136 // If x < 0, then
137 // a. Let s be "-".
138 // b. Let x = –x.
139 // If x = +infinity, then
140 // a. Return the concatenation of the Strings s and "Infinity".
141 if (!std::isfinite(objValue)) {
142 if (objValue < 0) {
143 return EtsString::CreateFromMUtf8("-Infinity");
144 }
145 return EtsString::CreateFromMUtf8("Infinity");
146 }
147
148 // truncate the arg val
149 double digitAbs = std::isnan(d) ? 0 : d;
150 digitAbs = std::abs((digitAbs >= 0) ? std::floor(digitAbs) : std::ceil(digitAbs));
151 // Check range
152 if (UNLIKELY(digitAbs > helpers::MAX_FRACTION || digitAbs < helpers::MIN_FRACTION + 1)) {
153 ThrowEtsException(EtsCoroutine::GetCurrent(),
154 panda_file_items::class_descriptors::ARGUMENT_OUT_OF_RANGE_EXCEPTION,
155 "toPrecision argument must be between 1 and 100");
156 return nullptr;
157 }
158
159 return helpers::DoubleToPrecision(objValue, static_cast<int>(digitAbs));
160 }
161
StdCoreDoubleToFixed(ObjectHeader * obj,double d)162 EtsString *StdCoreDoubleToFixed(ObjectHeader *obj, double d)
163 {
164 double objValue = helpers::GetStdDoubleArgument(obj);
165 // If x is NaN, return the String "NaN".
166 if (std::isnan(objValue)) {
167 return EtsString::CreateFromMUtf8("NaN");
168 }
169 // If x < 0, then
170 // a. Let s be "-".
171 // b. Let x = –x.
172 // If x = +infinity, then
173 // a. Return the concatenation of the Strings s and "Infinity".
174 if (!std::isfinite(objValue)) {
175 if (objValue < 0) {
176 return EtsString::CreateFromMUtf8("-Infinity");
177 }
178 return EtsString::CreateFromMUtf8("Infinity");
179 }
180
181 // truncate the arg val
182 double digitAbs = std::isnan(d) ? 0 : d;
183 digitAbs = std::abs((digitAbs >= 0) ? std::floor(digitAbs) : std::ceil(digitAbs));
184 // Check range
185 if (UNLIKELY(digitAbs > helpers::MAX_FRACTION || digitAbs < helpers::MIN_FRACTION)) {
186 ThrowEtsException(EtsCoroutine::GetCurrent(),
187 panda_file_items::class_descriptors::ARGUMENT_OUT_OF_RANGE_EXCEPTION,
188 "toFixed argument must be between 0 and 100");
189 return nullptr;
190 }
191
192 return helpers::DoubleToFixed(objValue, static_cast<int>(digitAbs));
193 }
194
StdCoreDoubleIsNan(double v)195 extern "C" EtsBoolean StdCoreDoubleIsNan(double v)
196 {
197 return ToEtsBoolean(v != v);
198 }
199
StdCoreDoubleIsFinite(double v)200 extern "C" EtsBoolean StdCoreDoubleIsFinite(double v)
201 {
202 static const double POSITIVE_INFINITY = 1.0 / 0.0;
203 static const double NEGATIVE_INFINITY = -1.0 / 0.0;
204
205 return ToEtsBoolean(v == v && v != POSITIVE_INFINITY && v != NEGATIVE_INFINITY);
206 }
207
StdCoreDoubleBitCastFromLong(EtsLong i)208 extern "C" EtsDouble StdCoreDoubleBitCastFromLong(EtsLong i)
209 {
210 return bit_cast<EtsDouble>(i);
211 }
212
StdCoreDoubleBitCastToLong(EtsDouble f)213 extern "C" EtsLong StdCoreDoubleBitCastToLong(EtsDouble f)
214 {
215 return bit_cast<EtsLong>(f);
216 }
217
IsInteger(double v)218 static inline bool IsInteger(double v)
219 {
220 return std::isfinite(v) && (std::fabs(v - std::trunc(v)) <= std::numeric_limits<double>::epsilon());
221 }
222
StdCoreDoubleIsInteger(double v)223 extern "C" EtsBoolean StdCoreDoubleIsInteger(double v)
224 {
225 return ToEtsBoolean(IsInteger(v));
226 }
227
228 /*
229 * In ETS Double.isSafeInteger returns (Double.isInteger(v) && (abs(v) <= Double.MAX_SAFE_INTEGER)).
230 * MAX_SAFE_INTEGER is a max integer value that can be used as a double without losing precision.
231 */
StdCoreDoubleIsSafeInteger(double v)232 extern "C" EtsBoolean StdCoreDoubleIsSafeInteger(double v)
233 {
234 return ToEtsBoolean(IsInteger(v) && (std::fabs(v) <= helpers::MaxSafeInteger<double>()));
235 }
236
237 } // namespace panda::ets::intrinsics
238