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