1/* 2 * Copyright (c) 2021-2024 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_number_format.h" 17 18#include "ecmascript/js_function.h" 19#include "ecmascript/js_number_format.h" 20 21namespace panda::ecmascript::builtins { 22// 13.2.1 Intl.NumberFormat ( [ locales [ , options ] ] ) 23JSTaggedValue BuiltinsNumberFormat::NumberFormatConstructor(EcmaRuntimeCallInfo *argv) 24{ 25 JSThread *thread = argv->GetThread(); 26 BUILTINS_API_TRACE(thread, NumberFormat, Constructor); 27 [[maybe_unused]] EcmaHandleScope scope(thread); 28 EcmaVM *ecmaVm = thread->GetEcmaVM(); 29 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv(); 30 ObjectFactory *factory = ecmaVm->GetFactory(); 31 32 // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. 33 JSHandle<JSTaggedValue> constructor = GetConstructor(argv); 34 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv); 35 if (newTarget->IsUndefined()) { 36 newTarget = constructor; 37 } 38 39 // Let numberFormat be ? OrdinaryCreateFromConstructor(newTarget, "%NumberFormatPrototype%", 40 // « [[InitializedNumberFormat]], [[Locale]], [[DataLocale]], [[NumberingSystem]], [[Style]], [[Unit]], 41 // [[UnitDisplay]], [[Currency]], [[CurrencyDisplay]], [[CurrencySign]], [[MinimumIntegerDigits]], 42 // [[MinimumFractionDigits]], [[MaximumFractionDigits]], [[MinimumSignificantDigits]], [[MaximumSignificantDigits]], 43 // [[RoundingType]], [[Notation]], [[CompactDisplay]], [[UseGrouping]], [[SignDisplay]], [[BoundFormat]] »). 44 JSHandle<JSObject> newObject = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget); 45 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 46 JSHandle<JSNumberFormat> numberFormat = JSHandle<JSNumberFormat>::Cast(newObject); 47 48 // 3. Perform ? InitializeNumberFormat(numberFormat, locales, options). 49 JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0); 50 JSHandle<JSTaggedValue> options = GetCallArg(argv, 1); 51 JSNumberFormat::InitializeNumberFormat(thread, numberFormat, locales, options); 52 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 53 54 // 4. Let this be the this value. 55 JSHandle<JSTaggedValue> thisValue = GetThis(argv); 56 57 // 5. If NewTarget is undefined and Type(this) is Object and ? InstanceofOperator(this, %NumberFormat%) is true, 58 // then 59 // a. Perform ? DefinePropertyOrThrow(this, %Intl%.[[FallbackSymbol]], PropertyDescriptor{ 60 // [[Value]]: numberFormat, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }). 61 // b. Return this. 62 if (GetNewTarget(argv)->IsUndefined() && thisValue->IsJSObject()) { 63 bool isInstanceOf = JSFunction::OrdinaryHasInstance(thread, env->GetNumberFormatFunction(), thisValue); 64 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 65 if (isInstanceOf) { 66 PropertyDescriptor descriptor(thread, JSHandle<JSTaggedValue>::Cast(numberFormat), false, false, false); 67 JSHandle<JSTaggedValue> key(thread, JSHandle<JSIntl>::Cast(env->GetIntlFunction())->GetFallbackSymbol()); 68 JSTaggedValue::DefinePropertyOrThrow(thread, thisValue, key, descriptor); 69 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 70 return thisValue.GetTaggedValue(); 71 } 72 } 73 74 // 6. Return numberFormat. 75 return numberFormat.GetTaggedValue(); 76} 77 78// 13.3.2 Intl.NumberFormat.supportedLocalesOf ( locales [ , options ] ) 79JSTaggedValue BuiltinsNumberFormat::SupportedLocalesOf(EcmaRuntimeCallInfo *argv) 80{ 81 JSThread *thread = argv->GetThread(); 82 BUILTINS_API_TRACE(thread, NumberFormat, SupportedLocalesOf); 83 [[maybe_unused]] EcmaHandleScope scope(thread); 84 // 1. Let availableLocales be %NumberFormat%.[[AvailableLocales]]. 85 JSHandle<TaggedArray> availableLocales = JSNumberFormat::GetAvailableLocales(thread); 86 87 // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales). 88 JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0); 89 JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales); 90 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 91 92 // 3. Return ? SupportedLocales(availableLocales, requestedLocales, options). 93 JSHandle<JSTaggedValue> options = GetCallArg(argv, 1); 94 JSHandle<JSArray> result = JSLocale::SupportedLocales(thread, availableLocales, requestedLocales, options); 95 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 96 return result.GetTaggedValue(); 97} 98 99// 13.4.3 get Intl.NumberFormat.prototype.format 100JSTaggedValue BuiltinsNumberFormat::Format(EcmaRuntimeCallInfo *argv) 101{ 102 JSThread *thread = argv->GetThread(); 103 BUILTINS_API_TRACE(thread, NumberFormat, Format); 104 [[maybe_unused]] EcmaHandleScope scope(thread); 105 106 // 1. Let nf be this value. 107 JSHandle<JSTaggedValue> thisValue = GetThis(argv); 108 // 2. If Type(nf) is not Object, throw a TypeError exception. 109 if (!thisValue->IsJSObject()) { 110 THROW_TYPE_ERROR_AND_RETURN(thread, "nf is not object", JSTaggedValue::Exception()); 111 } 112 // 3. Let nf be ? UnwrapNumberFormat(nf). 113 JSHandle<JSTaggedValue> nf = JSNumberFormat::UnwrapNumberFormat(thread, thisValue); 114 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 115 if (nf->IsUndefined()) { 116 THROW_TYPE_ERROR_AND_RETURN(thread, "nf is not object", JSTaggedValue::Exception()); 117 } 118 119 JSHandle<JSNumberFormat> typpedNf = JSHandle<JSNumberFormat>::Cast(nf); 120 JSHandle<JSTaggedValue> boundFunc(thread, typpedNf->GetBoundFormat()); 121 // 4. If nf.[[BoundFormat]] is undefined, then 122 // a. Let F be a new built-in function object as defined in Number Format Functions (12.1.4). 123 // b. Set F.[[NumberFormat]] to nf. 124 // c. Set nf.[[BoundFormat]] to F. 125 if (boundFunc->IsUndefined()) { 126 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 127 JSHandle<JSIntlBoundFunction> intlBoundFunc = factory->NewJSIntlBoundFunction( 128 MethodIndex::BUILTINS_NUMBER_FORMAT_NUMBER_FORMAT_INTERNAL_FORMAT_NUMBER); 129 intlBoundFunc->SetNumberFormat(thread, typpedNf); 130 typpedNf->SetBoundFormat(thread, intlBoundFunc); 131 } 132 return typpedNf->GetBoundFormat(); 133} 134 135// 13.4.4 Intl.NumberFormat.prototype.formatToParts ( date ) 136JSTaggedValue BuiltinsNumberFormat::FormatToParts(EcmaRuntimeCallInfo *argv) 137{ 138 JSThread *thread = argv->GetThread(); 139 BUILTINS_API_TRACE(thread, NumberFormat, FormatToParts); 140 [[maybe_unused]] EcmaHandleScope scope(thread); 141 // 1. Let nf be the this value. 142 JSHandle<JSTaggedValue> nf = GetThis(argv); 143 // 2. Perform ? RequireInternalSlot(nf, [[InitializedNumberFormat]]). 144 if (!nf->IsJSNumberFormat()) { 145 THROW_TYPE_ERROR_AND_RETURN(thread, "Is not JSNumberFormat", JSTaggedValue::Exception()); 146 } 147 // 3. Let x be ? ToNumeric(value). 148 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); 149 JSHandle<JSTaggedValue> x = JSTaggedValue::ToNumeric(thread, value); 150 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 151 JSHandle<JSArray> result = 152 JSNumberFormat::FormatNumericToParts(thread, JSHandle<JSNumberFormat>::Cast(nf), x.GetTaggedValue()); 153 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 154 return result.GetTaggedValue(); 155} 156// 13.4.5 Intl.NumberFormat.prototype.resolvedOptions () 157JSTaggedValue BuiltinsNumberFormat::ResolvedOptions(EcmaRuntimeCallInfo *argv) 158{ 159 JSThread *thread = argv->GetThread(); 160 BUILTINS_API_TRACE(thread, NumberFormat, ResolvedOptions); 161 [[maybe_unused]] EcmaHandleScope scope(thread); 162 // 1. Let nf be this value. 163 JSHandle<JSTaggedValue> thisValue = GetThis(argv); 164 // 2. If Type(nf) is not Object, throw a TypeError exception. 165 if (!thisValue->IsJSObject()) { 166 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not object", JSTaggedValue::Exception()); 167 } 168 // 3. Let nf be ? UnwrapNumberFormat(nf). 169 JSHandle<JSTaggedValue> nf = JSNumberFormat::UnwrapNumberFormat(thread, thisValue); 170 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 171 // Perform ? RequireInternalSlot(nf, [[InitializedNumberFormat]]). 172 if (!nf->IsJSNumberFormat()) { 173 THROW_TYPE_ERROR_AND_RETURN(thread, "nf is not JSNumberFormat", JSTaggedValue::Exception()); 174 } 175 // 4. Let options be ! ObjectCreate(%ObjectPrototype%). 176 auto ecmaVm = thread->GetEcmaVM(); 177 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv(); 178 ObjectFactory *factory = ecmaVm->GetFactory(); 179 JSHandle<JSFunction> ctor(env->GetObjectFunction()); 180 JSHandle<JSObject> options(factory->NewJSObjectByConstructor(ctor)); 181 182 // 5. For each row of Table 5, except the header row, in table order, do 183 // Let p be the Property value of the current row. 184 // Let v be the value of nf's internal slot whose name is the Internal Slot value of the current row. 185 // If v is not undefined, then 186 // Perform ! CreateDataPropertyOrThrow(options, p, v). 187 JSNumberFormat::ResolvedOptions(thread, JSHandle<JSNumberFormat>::Cast(nf), options); 188 return options.GetTaggedValue(); 189} 190 191JSTaggedValue BuiltinsNumberFormat::NumberFormatInternalFormatNumber(EcmaRuntimeCallInfo *argv) 192{ 193 JSThread *thread = argv->GetThread(); 194 [[maybe_unused]] EcmaHandleScope scope(thread); 195 JSHandle<JSIntlBoundFunction> intlBoundFunc = JSHandle<JSIntlBoundFunction>::Cast(GetConstructor(argv)); 196 197 // 1. Let nf be F.[[NumberFormat]]. 198 JSHandle<JSTaggedValue> nf(thread, intlBoundFunc->GetNumberFormat()); 199 // 2. Assert: Type(nf) is Object and nf has an [[InitializedNumberFormat]] internal slot. 200 ASSERT(nf->IsJSObject() && nf->IsJSNumberFormat()); 201 // 3. If value is not provided, let value be undefined. 202 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); 203 // 4 Let x be ? ToNumeric(value). 204 JSHandle<JSTaggedValue> x = JSTaggedValue::ToNumeric(thread, value); 205 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 206 // 5 Return ? FormatNumeric(nf, x). 207 JSHandle<JSTaggedValue> result = 208 JSNumberFormat::FormatNumeric(thread, JSHandle<JSNumberFormat>::Cast(nf), x.GetTaggedValue()); 209 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 210 return result.GetTaggedValue(); 211} 212} // namespace panda::ecmascript::builtins