1 // Copyright 2018 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef V8_INTL_SUPPORT
6 #error Internationalization is expected to be enabled.
7 #endif // V8_INTL_SUPPORT
8
9 #include "src/objects/js-segmenter.h"
10
11 #include <map>
12 #include <memory>
13 #include <string>
14
15 #include "src/execution/isolate.h"
16 #include "src/heap/factory.h"
17 #include "src/objects/intl-objects.h"
18 #include "src/objects/js-segmenter-inl.h"
19 #include "src/objects/managed-inl.h"
20 #include "src/objects/objects-inl.h"
21 #include "src/objects/option-utils.h"
22 #include "unicode/brkiter.h"
23
24 namespace v8 {
25 namespace internal {
26
New(Isolate * isolate,Handle<Map> map,Handle<Object> locales,Handle<Object> input_options)27 MaybeHandle<JSSegmenter> JSSegmenter::New(Isolate* isolate, Handle<Map> map,
28 Handle<Object> locales,
29 Handle<Object> input_options) {
30 // 4. Let requestedLocales be ? CanonicalizeLocaleList(locales).
31 Maybe<std::vector<std::string>> maybe_requested_locales =
32 Intl::CanonicalizeLocaleList(isolate, locales);
33 MAYBE_RETURN(maybe_requested_locales, Handle<JSSegmenter>());
34 std::vector<std::string> requested_locales =
35 maybe_requested_locales.FromJust();
36
37 Handle<JSReceiver> options;
38 const char* service = "Intl.Segmenter";
39 // 5. Let options be GetOptionsObject(_options_).
40 ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
41 GetOptionsObject(isolate, input_options, service),
42 JSSegmenter);
43
44 // 7. Let opt be a new Record.
45 // 8. Let matcher be ? GetOption(options, "localeMatcher", "string",
46 // « "lookup", "best fit" », "best fit").
47 // 9. Set opt.[[localeMatcher]] to matcher.
48 Maybe<Intl::MatcherOption> maybe_locale_matcher =
49 Intl::GetLocaleMatcher(isolate, options, service);
50 MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSSegmenter>());
51 Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
52
53 // 10. Let localeData be %Segmenter%.[[LocaleData]].
54
55 // 11. Let r be ResolveLocale(%Segmenter%.[[AvailableLocales]],
56 // requestedLocales, opt, %Segmenter%.[[RelevantExtensionKeys]]).
57 Maybe<Intl::ResolvedLocale> maybe_resolve_locale =
58 Intl::ResolveLocale(isolate, JSSegmenter::GetAvailableLocales(),
59 requested_locales, matcher, {});
60 if (maybe_resolve_locale.IsNothing()) {
61 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),
62 JSSegmenter);
63 }
64 Intl::ResolvedLocale r = maybe_resolve_locale.FromJust();
65
66 // 12. Set segmenter.[[Locale]] to the value of r.[[locale]].
67 Handle<String> locale_str =
68 isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
69
70 // 13. Let granularity be ? GetOption(options, "granularity", "string", «
71 // "grapheme", "word", "sentence" », "grapheme").
72 Maybe<Granularity> maybe_granularity = GetStringOption<Granularity>(
73 isolate, options, "granularity", service,
74 {"grapheme", "word", "sentence"},
75 {Granularity::GRAPHEME, Granularity::WORD, Granularity::SENTENCE},
76 Granularity::GRAPHEME);
77 MAYBE_RETURN(maybe_granularity, MaybeHandle<JSSegmenter>());
78 Granularity granularity_enum = maybe_granularity.FromJust();
79
80 icu::Locale icu_locale = r.icu_locale;
81 DCHECK(!icu_locale.isBogus());
82
83 UErrorCode status = U_ZERO_ERROR;
84 std::unique_ptr<icu::BreakIterator> icu_break_iterator;
85
86 switch (granularity_enum) {
87 case Granularity::GRAPHEME:
88 icu_break_iterator.reset(
89 icu::BreakIterator::createCharacterInstance(icu_locale, status));
90 break;
91 case Granularity::WORD:
92 icu_break_iterator.reset(
93 icu::BreakIterator::createWordInstance(icu_locale, status));
94 break;
95 case Granularity::SENTENCE:
96 icu_break_iterator.reset(
97 icu::BreakIterator::createSentenceInstance(icu_locale, status));
98 break;
99 }
100
101 DCHECK(U_SUCCESS(status));
102 DCHECK_NOT_NULL(icu_break_iterator.get());
103
104 Handle<Managed<icu::BreakIterator>> managed_break_iterator =
105 Managed<icu::BreakIterator>::FromUniquePtr(isolate, 0,
106 std::move(icu_break_iterator));
107
108 // Now all properties are ready, so we can allocate the result object.
109 Handle<JSSegmenter> segmenter = Handle<JSSegmenter>::cast(
110 isolate->factory()->NewFastOrSlowJSObjectFromMap(map));
111 DisallowGarbageCollection no_gc;
112 segmenter->set_flags(0);
113
114 // 12. Set segmenter.[[Locale]] to the value of r.[[Locale]].
115 segmenter->set_locale(*locale_str);
116
117 // 14. Set segmenter.[[SegmenterGranularity]] to granularity.
118 segmenter->set_granularity(granularity_enum);
119
120 segmenter->set_icu_break_iterator(*managed_break_iterator);
121
122 // 15. Return segmenter.
123 return segmenter;
124 }
125
126 // ecma402 #sec-Intl.Segmenter.prototype.resolvedOptions
ResolvedOptions(Isolate * isolate,Handle<JSSegmenter> segmenter)127 Handle<JSObject> JSSegmenter::ResolvedOptions(Isolate* isolate,
128 Handle<JSSegmenter> segmenter) {
129 Factory* factory = isolate->factory();
130 // 3. Let options be ! ObjectCreate(%ObjectPrototype%).
131 Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
132 // 4. For each row of Table 1, except the header row, do
133 // a. Let p be the Property value of the current row.
134 // b. Let v be the value of pr's internal slot whose name is the Internal Slot
135 // value of the current row.
136 //
137 // c. If v is not undefined, then
138 // i. Perform ! CreateDataPropertyOrThrow(options, p, v).
139 // Table 1: Resolved Options of Segmenter Instances
140 // Internal Slot Property
141 // [[Locale]] "locale"
142 // [[SegmenterGranularity]] "granularity"
143
144 Handle<String> locale(segmenter->locale(), isolate);
145 JSObject::AddProperty(isolate, result, factory->locale_string(), locale,
146 NONE);
147 JSObject::AddProperty(isolate, result, factory->granularity_string(),
148 segmenter->GranularityAsString(isolate), NONE);
149 // 5. Return options.
150 return result;
151 }
152
GranularityAsString(Isolate * isolate) const153 Handle<String> JSSegmenter::GranularityAsString(Isolate* isolate) const {
154 return GetGranularityString(isolate, granularity());
155 }
156
GetGranularityString(Isolate * isolate,Granularity granularity)157 Handle<String> JSSegmenter::GetGranularityString(Isolate* isolate,
158 Granularity granularity) {
159 Factory* factory = isolate->factory();
160 switch (granularity) {
161 case Granularity::GRAPHEME:
162 return factory->grapheme_string();
163 case Granularity::WORD:
164 return factory->word_string();
165 case Granularity::SENTENCE:
166 return factory->sentence_string();
167 }
168 UNREACHABLE();
169 }
170
GetAvailableLocales()171 const std::set<std::string>& JSSegmenter::GetAvailableLocales() {
172 return Intl::GetAvailableLocales();
173 }
174
175 } // namespace internal
176 } // namespace v8
177