1 /*
2 * Copyright (c) 2021 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 "ecmascript/builtins/builtins_bigint.h"
17
18 #include "ecmascript/js_primitive_ref.h"
19 #ifdef ARK_SUPPORT_INTL
20 #include "ecmascript/js_number_format.h"
21 #else
22 #ifndef ARK_NOT_SUPPORT_INTL_GLOBAL
23 #include "ecmascript/intl/global_intl_helper.h"
24 #endif
25 #endif
26
27 namespace panda::ecmascript::builtins {
BigIntConstructor(EcmaRuntimeCallInfo * argv)28 JSTaggedValue BuiltinsBigInt::BigIntConstructor(EcmaRuntimeCallInfo *argv)
29 {
30 ASSERT(argv);
31 JSThread *thread = argv->GetThread();
32 BUILTINS_API_TRACE(thread, BigInt, Constructor);
33 [[maybe_unused]] EcmaHandleScope handleScope(thread);
34 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
35 // 1. If NewTarget is not undefined, throw a TypeError exception.
36 if (!newTarget->IsUndefined()) {
37 THROW_TYPE_ERROR_AND_RETURN(thread, "BigInt is not a constructor", JSTaggedValue::Exception());
38 }
39 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
40 return BigIntConstructorInternal(thread, value);
41 }
42
BigIntConstructorInternal(JSThread * thread,JSHandle<JSTaggedValue> value)43 JSTaggedValue BuiltinsBigInt::BigIntConstructorInternal(JSThread *thread, JSHandle<JSTaggedValue> value)
44 {
45 BUILTINS_API_TRACE(thread, BigInt, Constructor);
46 [[maybe_unused]] EcmaHandleScope handleScope(thread);
47 // 2. Let prim be ? ToPrimitive(value).
48 JSHandle<JSTaggedValue> Primitive(thread, JSTaggedValue::ToPrimitive(thread, value));
49 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
50 // 3. If Type(prim) is Number, return ? NumberToBigInt(prim).
51 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
52 if (Primitive->IsNumber()) {
53 return BigInt::NumberToBigInt(thread, Primitive);
54 }
55 // 4. Otherwise, return ? ToBigInt(value).
56 return JSTaggedValue::ToBigInt(thread, value);
57 }
58
AsUintN(EcmaRuntimeCallInfo * argv)59 JSTaggedValue BuiltinsBigInt::AsUintN(EcmaRuntimeCallInfo *argv)
60 {
61 ASSERT(argv);
62 JSThread *thread = argv->GetThread();
63 BUILTINS_API_TRACE(thread, BigInt, AsUintN);
64 [[maybe_unused]] EcmaHandleScope handleScope(thread);
65 JSHandle<JSTaggedValue> bits = GetCallArg(argv, 0);
66 JSHandle<JSTaggedValue> bigint = GetCallArg(argv, 1);
67 // 1. Let bits be ? ToIndex(bits).
68 JSTaggedNumber index = JSTaggedValue::ToIndex(thread, bits);
69 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
70 // 2. Let bigint be ? ToBigInt(bigint).
71 JSTaggedValue jsBigint = JSTaggedValue::ToBigInt(thread, bigint);
72 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
73 JSHandle<BigInt> jsBigintVal(thread, jsBigint);
74 // 3. Return a BigInt representing bigint modulo 2bits.
75 return BigInt::AsUintN(thread, index, jsBigintVal);
76 }
77
AsIntN(EcmaRuntimeCallInfo * argv)78 JSTaggedValue BuiltinsBigInt::AsIntN(EcmaRuntimeCallInfo *argv)
79 {
80 ASSERT(argv);
81 JSThread *thread = argv->GetThread();
82 BUILTINS_API_TRACE(thread, BigInt, AsIntN);
83 [[maybe_unused]] EcmaHandleScope handleScope(thread);
84 JSHandle<JSTaggedValue> bits = GetCallArg(argv, 0);
85 JSHandle<JSTaggedValue> bigint = GetCallArg(argv, 1);
86 // 1. Let bits be ? ToIndex(bits).
87 JSTaggedNumber index = JSTaggedValue::ToIndex(thread, bits);
88 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
89 // 2. Let bigint be ? ToBigInt(bigint).
90 JSTaggedValue jsBigint = JSTaggedValue::ToBigInt(thread, bigint);
91 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
92 JSHandle<BigInt> jsBigintVal(thread, jsBigint);
93 // 3. Let mod be ℝ(bigint) modulo 2bits.
94 // 4. If mod ≥ 2bits - 1, return ℤ(mod - 2bits); otherwise, return ℤ(mod).
95 return BigInt::AsintN(thread, index, jsBigintVal);
96 }
97
ToLocaleString(EcmaRuntimeCallInfo * argv)98 JSTaggedValue BuiltinsBigInt::ToLocaleString(EcmaRuntimeCallInfo *argv)
99 {
100 ASSERT(argv);
101 JSThread *thread = argv->GetThread();
102 BUILTINS_API_TRACE(thread, BigInt, ToLocaleString);
103 [[maybe_unused]] EcmaHandleScope handleScope(thread);
104 // 1. Let x be ? ThisBigIntValue(this value).
105 JSTaggedValue value = ThisBigIntValue(argv);
106 [[maybe_unused]] JSHandle<JSTaggedValue> x(thread, value);
107 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
108
109 JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
110 JSHandle<JSTaggedValue> options = GetCallArg(argv, 1);
111 [[maybe_unused]] bool cacheable = (locales->IsUndefined() || locales->IsString()) && options->IsUndefined();
112 #ifdef ARK_SUPPORT_INTL
113 if (cacheable) {
114 auto numberFormatter = JSNumberFormat::GetCachedIcuNumberFormatter(thread, locales);
115 if (numberFormatter != nullptr) {
116 JSHandle<JSTaggedValue> result = JSNumberFormat::FormatNumeric(thread, numberFormatter, x.GetTaggedValue());
117 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
118 return result.GetTaggedValue();
119 }
120 }
121 // 2. Let numberFormat be ? Construct(%NumberFormat%, « locales, options »).
122 EcmaVM *ecmaVm = thread->GetEcmaVM();
123 JSHandle<JSFunction> ctor(ecmaVm->GetGlobalEnv()->GetNumberFormatFunction());
124 ObjectFactory *factory = ecmaVm->GetFactory();
125 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(ctor);
126 JSHandle<JSNumberFormat> numberFormat = JSHandle<JSNumberFormat>::Cast(obj);
127 JSNumberFormat::InitializeNumberFormat(thread, numberFormat, locales, options, cacheable);
128 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
129 if (cacheable) {
130 auto numberFormatter = JSNumberFormat::GetCachedIcuNumberFormatter(thread, locales);
131 ASSERT(numberFormatter != nullptr);
132 JSHandle<JSTaggedValue> result = JSNumberFormat::FormatNumeric(thread, numberFormatter, x.GetTaggedValue());
133 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
134 return result.GetTaggedValue();
135 }
136
137 // Return ? FormatNumeric(numberFormat, x).
138 JSHandle<JSTaggedValue> result = JSNumberFormat::FormatNumeric(thread, numberFormat, x.GetTaggedValue());
139 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
140 return result.GetTaggedValue();
141 #else
142 #ifdef ARK_NOT_SUPPORT_INTL_GLOBAL
143 ARK_SUPPORT_INTL_RETURN_JSVALUE(thread, "LocaleCompare");
144 #else
145 intl::GlobalIntlHelper gh(thread, intl::GlobalFormatterType::NumberFormatter);
146 auto numberFormatter = gh.GetGlobalObject<intl::GlobalNumberFormat>(thread,
147 locales, options, intl::GlobalFormatterType::NumberFormatter, cacheable);
148 if (numberFormatter == nullptr) {
149 LOG_ECMA(ERROR) << "BuiltinsBigInt:numberFormatter is nullptr";
150 }
151 ASSERT(numberFormatter != nullptr);
152 std::string result = numberFormatter->Format(x->GetDouble());
153 EcmaVM *ecmaVm = thread->GetEcmaVM();
154 ObjectFactory *factory = ecmaVm->GetFactory();
155 JSHandle returnValue = factory->NewFromStdString(result);
156 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
157 return returnValue.GetTaggedValue();
158 #endif
159 #endif
160 }
161
ToString(EcmaRuntimeCallInfo * argv)162 JSTaggedValue BuiltinsBigInt::ToString(EcmaRuntimeCallInfo *argv)
163 {
164 ASSERT(argv);
165 JSThread *thread = argv->GetThread();
166 BUILTINS_API_TRACE(thread, BigInt, ToString);
167 [[maybe_unused]] EcmaHandleScope handleScope(thread);
168
169 // 1. Let x be ? thisBigIntValue(this value).
170 JSTaggedValue value = ThisBigIntValue(argv);
171 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
172 JSHandle<BigInt> thisBigint(thread, value);
173 // 2. If radix is not present, let radixNumber be 10
174 double radix = base::DECIMAL;
175 JSHandle<JSTaggedValue> radixValue = GetCallArg(argv, 0);
176 // 3. Else, let radixNumber be ? ToIntegerOrInfinity(radix).
177 if (!radixValue->IsUndefined()) {
178 JSTaggedNumber radixNumber = JSTaggedValue::ToInteger(thread, radixValue);
179 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
180 radix = radixNumber.GetNumber();
181 }
182 // 4. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception.
183 if (radix < base::MIN_RADIX || radix > base::MAX_RADIX) {
184 THROW_RANGE_ERROR_AND_RETURN(thread, "toString() radix argument must be between 2 and 36",
185 JSTaggedValue::Exception());
186 }
187 // 5. If radixNumber = 10, return ToString(x).
188 if (radix == base::DECIMAL) {
189 return BigInt::ToString(thread, thisBigint).GetTaggedValue();
190 }
191 // 6. Return the String representation of this BigInt value using the radix specified by radixNumber
192 return BigInt::ToString(thread, thisBigint, static_cast<int>(radix)).GetTaggedValue();
193 }
194
ValueOf(EcmaRuntimeCallInfo * argv)195 JSTaggedValue BuiltinsBigInt::ValueOf(EcmaRuntimeCallInfo *argv)
196 {
197 ASSERT(argv);
198 JSThread *thread = argv->GetThread();
199 BUILTINS_API_TRACE(thread, BigInt, ValueOf);
200 [[maybe_unused]] EcmaHandleScope handleScope(thread);
201 // 1. Let x be ? thisBigIntValue(this value).
202 return ThisBigIntValue(argv);
203 }
204
ThisBigIntValue(EcmaRuntimeCallInfo * argv)205 JSTaggedValue BuiltinsBigInt::ThisBigIntValue(EcmaRuntimeCallInfo *argv)
206 {
207 ASSERT(argv);
208 JSThread *thread = argv->GetThread();
209 BUILTINS_API_TRACE(thread, BigInt, ThisBigIntValue);
210 JSHandle<JSTaggedValue> value = GetThis(argv);
211 // 1. If Type(value) is BigInt, return value.
212 if (value->IsBigInt()) {
213 return value.GetTaggedValue();
214 }
215 // 2. If Type(value) is Object and value has a [[BigIntData]] internal slot, then
216 if (value->IsJSPrimitiveRef()) {
217 JSTaggedValue primitive = JSPrimitiveRef::Cast(value->GetTaggedObject())->GetValue();
218 // a. Assert: Type(value.[[BigIntData]]) is BigInt.
219 if (primitive.IsBigInt()) {
220 // b. Return value.[[BigIntData]].
221 return primitive;
222 }
223 }
224 // 3. Throw a TypeError exception.
225 THROW_TYPE_ERROR_AND_RETURN(thread, "not BigInt type", JSTaggedValue::Exception());
226 }
227 } // namespace panda::ecmascript::builtins