• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
23 namespace panda::ecmascript {
24 
SetIcuBreakIterator(JSThread * thread,const JSHandle<JSSegmenter> & segmenter,icu::BreakIterator * icuBreakIterator,const NativePointerCallback & callback)25 void 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 
GetAvailableLocales(JSThread * thread)42 JSHandle<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 
InitializeIcuBreakIterator(JSThread * thread,const JSHandle<JSSegmenter> & segmenter,const icu::Locale & icuLocale,GranularityOption granularity)49 void 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 
InitializeSegmenter(JSThread * thread,const JSHandle<JSSegmenter> & segmenter,const JSHandle<JSTaggedValue> & locales,const JSHandle<JSTaggedValue> & options)79 JSHandle<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 
GranularityOptionToEcmaString(JSThread * thread,GranularityOption granularity)147 JSHandle<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 
ResolvedOptions(JSThread * thread,const JSHandle<JSSegmenter> & segmenter,const JSHandle<JSObject> & options)168 void 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