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
24namespace v8 {
25namespace internal {
26
27MaybeHandle<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
127Handle<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
153Handle<String> JSSegmenter::GranularityAsString(Isolate* isolate) const {
154  return GetGranularityString(isolate, granularity());
155}
156
157Handle<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
171const std::set<std::string>& JSSegmenter::GetAvailableLocales() {
172  return Intl::GetAvailableLocales();
173}
174
175}  // namespace internal
176}  // namespace v8
177