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
27namespace panda::ecmascript::builtins {
28JSTaggedValue 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
43JSTaggedValue 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
59JSTaggedValue 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
78JSTaggedValue 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
98JSTaggedValue 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
162JSTaggedValue 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
195JSTaggedValue 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
205JSTaggedValue 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