1/* 2 * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development 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/js_segmenter.h" 17 18#include <cstring> 19 20#include "ecmascript/intl/locale_helper.h" 21#include "ecmascript/object_factory-inl.h" 22 23namespace panda::ecmascript { 24 25void JSSegmenter::SetIcuBreakIterator(JSThread *thread, const JSHandle<JSSegmenter> &segmenter, 26 icu::BreakIterator* icuBreakIterator, const NativePointerCallback &callback) 27{ 28 EcmaVM *ecmaVm = thread->GetEcmaVM(); 29 ObjectFactory *factory = ecmaVm->GetFactory(); 30 31 ASSERT(icuBreakIterator != nullptr); 32 JSTaggedValue data = segmenter->GetIcuField(); 33 if (data.IsJSNativePointer()) { 34 JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject()); 35 native->ResetExternalPointer(thread, icuBreakIterator); 36 return; 37 } 38 JSHandle<JSNativePointer> pointer = factory->NewJSNativePointer(icuBreakIterator, callback); 39 segmenter->SetIcuField(thread, pointer.GetTaggedValue()); 40} 41 42JSHandle<TaggedArray> JSSegmenter::GetAvailableLocales(JSThread *thread) 43{ 44 std::vector<std::string> availableStringLocales = intl::LocaleHelper::GetAvailableLocales(thread, nullptr, nullptr); 45 JSHandle<TaggedArray> availableLocales = JSLocale::ConstructLocaleList(thread, availableStringLocales); 46 return availableLocales; 47} 48 49void JSSegmenter::InitializeIcuBreakIterator(JSThread *thread, const JSHandle<JSSegmenter> &segmenter, 50 const icu::Locale &icuLocale, GranularityOption granularity) 51{ 52 UErrorCode status = U_ZERO_ERROR; 53 std::unique_ptr<icu::BreakIterator> icuBreakIterator; 54 55 switch (granularity) { 56 case GranularityOption::GRAPHEME: 57 icuBreakIterator.reset(icu::BreakIterator::createCharacterInstance(icuLocale, status)); 58 break; 59 case GranularityOption::WORD: 60 icuBreakIterator.reset(icu::BreakIterator::createWordInstance(icuLocale, status)); 61 break; 62 case GranularityOption::SENTENCE: 63 icuBreakIterator.reset(icu::BreakIterator::createSentenceInstance(icuLocale, status)); 64 break; 65 default: 66 LOG_ECMA(FATAL) << "this branch is unreachable"; 67 UNREACHABLE(); 68 } 69 if (U_FAILURE(status) || icuBreakIterator == nullptr) { 70 if (status == UErrorCode::U_MISSING_RESOURCE_ERROR) { 71 THROW_ERROR(thread, ErrorType::REFERENCE_ERROR, "can not find icu data resources"); 72 } 73 THROW_ERROR(thread, ErrorType::RANGE_ERROR, "create icu::BreakIterator failed"); 74 } 75 76 SetIcuBreakIterator(thread, segmenter, icuBreakIterator.release(), JSSegmenter::FreeIcuBreakIterator); 77} 78 79JSHandle<JSSegmenter> JSSegmenter::InitializeSegmenter(JSThread *thread, 80 const JSHandle<JSSegmenter> &segmenter, 81 const JSHandle<JSTaggedValue> &locales, 82 const JSHandle<JSTaggedValue> &options) 83{ 84 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 85 auto globalConst = thread->GlobalConstants(); 86 // 4. Let requestedLocales be ? CanonicalizeLocaleList(locales). 87 JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales); 88 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSSegmenter, thread); 89 90 // 5. Let options be ? GetOptionsObject(options). 91 JSHandle<JSObject> optionsObject; 92 if (options->IsUndefined()) { 93 optionsObject = factory->CreateNullJSObject(); 94 } else if (!options->IsJSObject()) { 95 THROW_TYPE_ERROR_AND_RETURN(thread, "options is not Object", segmenter); 96 } else { 97 optionsObject = JSTaggedValue::ToObject(thread, options); 98 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSSegmenter, thread); 99 } 100 101 // 6. Let opt be a new Record. 102 // 7. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit"). 103 JSHandle<JSTaggedValue> property = globalConst->GetHandledLocaleMatcherString(); 104 auto matcher = JSLocale::GetOptionOfString<LocaleMatcherOption>( 105 thread, optionsObject, property, {LocaleMatcherOption::LOOKUP, LocaleMatcherOption::BEST_FIT}, 106 {"lookup", "best fit"}, LocaleMatcherOption::BEST_FIT); 107 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSSegmenter, thread); 108 109 // 9. Let localeData be %Segmenter%.[[LocaleData]]. 110 // 10. Let r be ResolveLocale(%Segmenter%.[[AvailableLocales]], requestedLocales, opt, 111 // %Segmenter%.[[RelevantExtensionKeys]], localeData). 112 JSHandle<TaggedArray> availableLocales; 113 if (requestedLocales->GetLength() == 0) { 114 availableLocales = factory->EmptyArray(); 115 } else { 116 availableLocales = JSSegmenter::GetAvailableLocales(thread); 117 } 118 std::set<std::string> relevantExtensionKeys {""}; 119 ResolvedLocale r = 120 JSLocale::ResolveLocale(thread, availableLocales, requestedLocales, matcher, relevantExtensionKeys); 121 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSSegmenter, thread); 122 123 // 11. Set segmenter.[[Locale]] to r.[[locale]]. 124 icu::Locale icuLocale = r.localeData; 125 JSHandle<EcmaString> localeStr = intl::LocaleHelper::ToLanguageTag(thread, icuLocale); 126 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSSegmenter, thread); 127 segmenter->SetLocale(thread, localeStr.GetTaggedValue()); 128 129 // 12. Let granularity be ? GetOption(options, "granularity", string, « "grapheme", "word", "sentence" », 130 // "grapheme"). 131 property = globalConst->GetHandledGranularityString(); 132 auto granularity = JSLocale::GetOptionOfString<GranularityOption>(thread, optionsObject, property, 133 {GranularityOption::GRAPHEME, 134 GranularityOption::WORD, 135 GranularityOption::SENTENCE}, 136 {"grapheme", "word", "sentence"}, 137 GranularityOption::GRAPHEME); 138 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSSegmenter, thread); 139 140 // 13. Set segmenter.[[SegmenterGranularity]] to granularity. 141 segmenter->SetGranularity(granularity); 142 InitializeIcuBreakIterator(thread, segmenter, icuLocale, granularity); 143 // 14. Return segmenter. 144 return segmenter; 145} 146 147JSHandle<JSTaggedValue> JSSegmenter::GranularityOptionToEcmaString(JSThread *thread, GranularityOption granularity) 148{ 149 JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined()); 150 auto globalConst = thread->GlobalConstants(); 151 switch (granularity) { 152 case GranularityOption::GRAPHEME: 153 result.Update(globalConst->GetHandledGraphemeString().GetTaggedValue()); 154 break; 155 case GranularityOption::WORD: 156 result.Update(globalConst->GetHandledWordString().GetTaggedValue()); 157 break; 158 case GranularityOption::SENTENCE: 159 result.Update(globalConst->GetHandledSentenceString().GetTaggedValue()); 160 break; 161 default: 162 LOG_ECMA(FATAL) << "this branch is unreachable"; 163 UNREACHABLE(); 164 } 165 return result; 166} 167 168void JSSegmenter::ResolvedOptions(JSThread *thread, const JSHandle<JSSegmenter> &segmenter, 169 const JSHandle<JSObject> &options) 170{ 171 auto globalConst = thread->GlobalConstants(); 172 173 // [[Locale]] 174 JSHandle<JSTaggedValue> propertyKey = globalConst->GetHandledLocaleString(); 175 JSHandle<JSTaggedValue> locale(thread, segmenter->GetLocale()); 176 JSObject::CreateDataPropertyOrThrow(thread, options, propertyKey, locale); 177 RETURN_IF_ABRUPT_COMPLETION(thread); 178 179 // [[SegmenterGranularity]] 180 GranularityOption granularity = segmenter->GetGranularity(); 181 propertyKey = globalConst->GetHandledGranularityString(); 182 JSHandle<JSTaggedValue> granularityString = GranularityOptionToEcmaString(thread, granularity); 183 JSObject::CreateDataPropertyOrThrow(thread, options, propertyKey, granularityString); 184 RETURN_IF_ABRUPT_COMPLETION(thread); 185} 186} // namespace panda::ecmascript 187