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 "builtins_collator.h" 17 18#include "ecmascript/intl/locale_helper.h" 19#include "ecmascript/js_collator.h" 20#include "ecmascript/js_function.h" 21 22namespace panda::ecmascript::builtins { 23constexpr uint32_t FUNCTION_LENGTH_TWO = 2; 24 25// 11.1.2 Intl.Collator ( [ locales [ , options ] ] ) 26JSTaggedValue BuiltinsCollator::CollatorConstructor(EcmaRuntimeCallInfo *argv) 27{ 28 JSThread *thread = argv->GetThread(); 29 BUILTINS_API_TRACE(thread, Collator, CollatorConstructor); 30 [[maybe_unused]] EcmaHandleScope scope(thread); 31 EcmaVM *ecmaVm = thread->GetEcmaVM(); 32 ObjectFactory *factory = ecmaVm->GetFactory(); 33 34 // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. 35 JSHandle<JSTaggedValue> constructor = GetConstructor(argv); 36 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv); 37 if (newTarget->IsUndefined()) { 38 newTarget = constructor; 39 } 40 // 2. Let internalSlotsList be « [[InitializedCollator]], [[Locale]], [[Usage]], [[Sensitivity]], 41 // [[IgnorePunctuation]], [[Collation]], [[BoundCompare]] ». 42 // 3. If %Collator%.[[RelevantExtensionKeys]] contains "kn", then 43 // a. Append [[Numeric]] as the last element of internalSlotsList. 44 // 4. If %Collator%.[[RelevantExtensionKeys]] contains "kf", then 45 // a. Append [[CaseFirst]] as the last element of internalSlotsList. 46 47 // 5. Let collator be ? OrdinaryCreateFromConstructor(newTarget, "%CollatorPrototype%", internalSlotsList). 48 JSHandle<JSObject> newObject = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget); 49 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 50 JSHandle<JSCollator> collator = JSHandle<JSCollator>::Cast(newObject); 51 52 // 6. Return ? InitializeCollator(collator, locales, options). 53 JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0); 54 JSHandle<JSTaggedValue> options = GetCallArg(argv, 1); 55 JSHandle<JSCollator> result = JSCollator::InitializeCollator(thread, collator, locales, options); 56 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 57 return result.GetTaggedValue(); 58} 59 60// 11.2.2 Intl.Collator.supportedLocalesOf ( locales [ , options ] ) 61JSTaggedValue BuiltinsCollator::SupportedLocalesOf(EcmaRuntimeCallInfo *argv) 62{ 63 JSThread *thread = argv->GetThread(); 64 BUILTINS_API_TRACE(thread, Collator, SupportedLocalesOf); 65 [[maybe_unused]] EcmaHandleScope scope(thread); 66 // 1. Let availableLocales be %Collator%.[[AvailableLocales]]. 67 JSHandle<TaggedArray> availableLocales = JSCollator::GetAvailableLocales(thread); 68 69 // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales). 70 JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0); 71 JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales); 72 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 73 74 // 3. Return ? SupportedLocales(availableLocales, requestedLocales, options). 75 JSHandle<JSTaggedValue> options = GetCallArg(argv, 1); 76 JSHandle<JSArray> result = JSLocale::SupportedLocales(thread, availableLocales, requestedLocales, options); 77 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 78 return result.GetTaggedValue(); 79} 80 81// 11.3.3 get Intl.Collator.prototype.compare 82JSTaggedValue BuiltinsCollator::Compare(EcmaRuntimeCallInfo *argv) 83{ 84 JSThread *thread = argv->GetThread(); 85 BUILTINS_API_TRACE(thread, Collator, Compare); 86 [[maybe_unused]] EcmaHandleScope scope(thread); 87 // 1. Let collator be this value. 88 JSHandle<JSTaggedValue> thisValue = GetThis(argv); 89 90 // 2. Perform ? RequireInternalSlot(collator, [[InitializedCollator]]). 91 if (!thisValue->IsJSCollator()) { 92 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not collator", JSTaggedValue::Exception()); 93 } 94 // 3. If collator.[[BoundCompare]] is undefined, then 95 // a. Let F be a new built-in function object as defined in 11.3.3.1. 96 // b. Set F.[[Collator]] to collator. 97 // c. Set collator.[[BoundCompare]] to F. 98 // 4. Return collator.[[BoundCompare]]. 99 JSHandle<JSCollator> collator = JSHandle<JSCollator>::Cast(thisValue); 100 JSHandle<JSTaggedValue> boundCompare(thread, collator->GetBoundCompare()); 101 if (boundCompare->IsUndefined()) { 102 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 103 JSHandle<JSIntlBoundFunction> intlBoundFunc = factory->NewJSIntlBoundFunction( 104 MethodIndex::BUILTINS_COLLATOR_ANONYMOUS_COLLATOR, FUNCTION_LENGTH_TWO); 105 intlBoundFunc->SetCollator(thread, collator); 106 collator->SetBoundCompare(thread, intlBoundFunc); 107 } 108 return collator->GetBoundCompare(); 109} 110 111// 11.3.3.1 Collator Compare Functions 112JSTaggedValue BuiltinsCollator::AnonymousCollator(EcmaRuntimeCallInfo *argv) 113{ 114 // A Collator compare function is an anonymous built-in function that has a [[Collator]] internal slot. 115 // When a Collator compare function F is called with arguments x and y, the following steps are taken: 116 JSThread *thread = argv->GetThread(); 117 BUILTINS_API_TRACE(thread, Collator, AnonymousCollator); 118 [[maybe_unused]] EcmaHandleScope scope(thread); 119 JSHandle<JSIntlBoundFunction> intlBoundFunc = JSHandle<JSIntlBoundFunction>::Cast(GetConstructor(argv)); 120 121 // 1. Let collator be F.[[Collator]]. 122 JSHandle<JSTaggedValue> collator(thread, intlBoundFunc->GetCollator()); 123 124 // 2. Assert: Type(collator) is Object and collator has an [[InitializedCollator]] internal slot. 125 ASSERT_PRINT(collator->IsJSObject() && collator->IsJSCollator(), "collator is not object or JSCollator"); 126 127 // 3. If x is not provided, let x be undefined. 128 JSHandle<JSTaggedValue> x = GetCallArg(argv, 0); 129 130 // 4. If y is not provided, let y be undefined. 131 JSHandle<JSTaggedValue> y = GetCallArg(argv, 1); 132 133 // 5. Let X be ? ToString(x). 134 JSHandle<EcmaString> xValue = JSTaggedValue::ToString(thread, x); 135 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Undefined()); 136 // 6. Let Y be ? ToString(y). 137 JSHandle<EcmaString> yValue = JSTaggedValue::ToString(thread, y); 138 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Undefined()); 139 // 7. Return CompareStrings(collator, X, Y). 140 icu::Collator *icuCollator = (JSHandle<JSCollator>::Cast(collator))->GetIcuCollator(); 141 return JSCollator::CompareStrings(thread, icuCollator, xValue, yValue); 142} 143 144// 11.3.4 Intl.Collator.prototype.resolvedOptions () 145JSTaggedValue BuiltinsCollator::ResolvedOptions(EcmaRuntimeCallInfo *argv) 146{ 147 JSThread *thread = argv->GetThread(); 148 BUILTINS_API_TRACE(thread, Collator, ResolvedOptions); 149 [[maybe_unused]] EcmaHandleScope scope(thread); 150 JSHandle<JSTaggedValue> thisValue = GetThis(argv); 151 if (!thisValue->IsJSCollator()) { 152 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Collator object", JSTaggedValue::Exception()); 153 } 154 JSHandle<JSObject> options = JSCollator::ResolvedOptions(thread, JSHandle<JSCollator>::Cast(thisValue)); 155 return options.GetTaggedValue(); 156} 157} // namespace panda::ecmascript::builtins