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