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