* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecmascript/builtins/builtins_bigint.h"
#include "ecmascript/js_primitive_ref.h"
#ifdef ARK_SUPPORT_INTL
#include "ecmascript/js_number_format.h"
#else
#ifndef ARK_NOT_SUPPORT_INTL_GLOBAL
#include "ecmascript/intl/global_intl_helper.h"
#endif
#endif
namespace panda::ecmascript::builtins {
JSTaggedValue BuiltinsBigInt::BigIntConstructor(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, BigInt, Constructor);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
if (!newTarget->IsUndefined()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "BigInt is not a constructor", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
return BigIntConstructorInternal(thread, value);
}
JSTaggedValue BuiltinsBigInt::BigIntConstructorInternal(JSThread *thread, JSHandle<JSTaggedValue> value)
{
BUILTINS_API_TRACE(thread, BigInt, Constructor);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> Primitive(thread, JSTaggedValue::ToPrimitive(thread, value));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (Primitive->IsNumber()) {
return BigInt::NumberToBigInt(thread, Primitive);
}
return JSTaggedValue::ToBigInt(thread, value);
}
JSTaggedValue BuiltinsBigInt::AsUintN(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, BigInt, AsUintN);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> bits = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> bigint = GetCallArg(argv, 1);
JSTaggedNumber index = JSTaggedValue::ToIndex(thread, bits);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue jsBigint = JSTaggedValue::ToBigInt(thread, bigint);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<BigInt> jsBigintVal(thread, jsBigint);
return BigInt::AsUintN(thread, index, jsBigintVal);
}
JSTaggedValue BuiltinsBigInt::AsIntN(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, BigInt, AsIntN);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> bits = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> bigint = GetCallArg(argv, 1);
JSTaggedNumber index = JSTaggedValue::ToIndex(thread, bits);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue jsBigint = JSTaggedValue::ToBigInt(thread, bigint);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<BigInt> jsBigintVal(thread, jsBigint);
return BigInt::AsintN(thread, index, jsBigintVal);
}
JSTaggedValue BuiltinsBigInt::ToLocaleString(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, BigInt, ToLocaleString);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSTaggedValue value = ThisBigIntValue(argv);
[[maybe_unused]] JSHandle<JSTaggedValue> x(thread, value);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> options = GetCallArg(argv, 1);
[[maybe_unused]] bool cacheable = (locales->IsUndefined() || locales->IsString()) && options->IsUndefined();
#ifdef ARK_SUPPORT_INTL
if (cacheable) {
auto numberFormatter = JSNumberFormat::GetCachedIcuNumberFormatter(thread, locales);
if (numberFormatter != nullptr) {
JSHandle<JSTaggedValue> result = JSNumberFormat::FormatNumeric(thread, numberFormatter, x.GetTaggedValue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return result.GetTaggedValue();
}
}
EcmaVM *ecmaVm = thread->GetEcmaVM();
JSHandle<JSFunction> ctor(ecmaVm->GetGlobalEnv()->GetNumberFormatFunction());
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(ctor);
JSHandle<JSNumberFormat> numberFormat = JSHandle<JSNumberFormat>::Cast(obj);
JSNumberFormat::InitializeNumberFormat(thread, numberFormat, locales, options, cacheable);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (cacheable) {
auto numberFormatter = JSNumberFormat::GetCachedIcuNumberFormatter(thread, locales);
ASSERT(numberFormatter != nullptr);
JSHandle<JSTaggedValue> result = JSNumberFormat::FormatNumeric(thread, numberFormatter, x.GetTaggedValue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return result.GetTaggedValue();
}
JSHandle<JSTaggedValue> result = JSNumberFormat::FormatNumeric(thread, numberFormat, x.GetTaggedValue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return result.GetTaggedValue();
#else
#ifdef ARK_NOT_SUPPORT_INTL_GLOBAL
ARK_SUPPORT_INTL_RETURN_JSVALUE(thread, "LocaleCompare");
#else
intl::GlobalIntlHelper gh(thread, intl::GlobalFormatterType::NumberFormatter);
auto numberFormatter = gh.GetGlobalObject<intl::GlobalNumberFormat>(thread,
locales, options, intl::GlobalFormatterType::NumberFormatter, cacheable);
if (numberFormatter == nullptr) {
LOG_ECMA(ERROR) << "BuiltinsBigInt:numberFormatter is nullptr";
}
ASSERT(numberFormatter != nullptr);
std::string result = numberFormatter->Format(x->GetDouble());
EcmaVM *ecmaVm = thread->GetEcmaVM();
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle returnValue = factory->NewFromStdString(result);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return returnValue.GetTaggedValue();
#endif
#endif
}
JSTaggedValue BuiltinsBigInt::ToString(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, BigInt, ToString);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSTaggedValue value = ThisBigIntValue(argv);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<BigInt> thisBigint(thread, value);
double radix = base::DECIMAL;
JSHandle<JSTaggedValue> radixValue = GetCallArg(argv, 0);
if (!radixValue->IsUndefined()) {
JSTaggedNumber radixNumber = JSTaggedValue::ToInteger(thread, radixValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
radix = radixNumber.GetNumber();
}
if (radix < base::MIN_RADIX || radix > base::MAX_RADIX) {
THROW_RANGE_ERROR_AND_RETURN(thread, "toString() radix argument must be between 2 and 36",
JSTaggedValue::Exception());
}
if (radix == base::DECIMAL) {
return BigInt::ToString(thread, thisBigint).GetTaggedValue();
}
return BigInt::ToString(thread, thisBigint, static_cast<int>(radix)).GetTaggedValue();
}
JSTaggedValue BuiltinsBigInt::ValueOf(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, BigInt, ValueOf);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
return ThisBigIntValue(argv);
}
JSTaggedValue BuiltinsBigInt::ThisBigIntValue(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, BigInt, ThisBigIntValue);
JSHandle<JSTaggedValue> value = GetThis(argv);
if (value->IsBigInt()) {
return value.GetTaggedValue();
}
if (value->IsJSPrimitiveRef()) {
JSTaggedValue primitive = JSPrimitiveRef::Cast(value->GetTaggedObject())->GetValue(thread);
if (primitive.IsBigInt()) {
return primitive;
}
}
THROW_TYPE_ERROR_AND_RETURN(thread, "not BigInt type", JSTaggedValue::Exception());
}
}