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