js-segmenter.cc 6.75 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_INTL_SUPPORT
#error Internationalization is expected to be enabled.
#endif  // V8_INTL_SUPPORT

#include "src/objects/js-segmenter.h"

#include <map>
#include <memory>
#include <string>

15
#include "src/execution/isolate.h"
16 17 18 19
#include "src/heap/factory.h"
#include "src/objects/intl-objects.h"
#include "src/objects/js-segmenter-inl.h"
#include "src/objects/managed.h"
20
#include "src/objects/objects-inl.h"
21 22 23 24 25
#include "unicode/brkiter.h"

namespace v8 {
namespace internal {

26 27 28
MaybeHandle<JSSegmenter> JSSegmenter::New(Isolate* isolate, Handle<Map> map,
                                          Handle<Object> locales,
                                          Handle<Object> input_options) {
29
  // 4. Let requestedLocales be ? CanonicalizeLocaleList(locales).
30 31 32 33 34
  Maybe<std::vector<std::string>> maybe_requested_locales =
      Intl::CanonicalizeLocaleList(isolate, locales);
  MAYBE_RETURN(maybe_requested_locales, Handle<JSSegmenter>());
  std::vector<std::string> requested_locales =
      maybe_requested_locales.FromJust();
35

36
  // 5. If options is undefined, then
37 38
  Handle<JSReceiver> options;
  if (input_options->IsUndefined(isolate)) {
39
    //  a. Let options be ObjectCreate(null).
40
    options = isolate->factory()->NewJSObjectWithNullProto();
41 42
  } else {  // 6. Else
    // a. Let options be ? ToObject(options).
43 44 45 46 47
    ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
                               Object::ToObject(isolate, input_options),
                               JSSegmenter);
  }

48 49
  // 7. Let opt be a new Record.
  // 8. Let matcher be ? GetOption(options, "localeMatcher", "string",
50
  // « "lookup", "best fit" », "best fit").
51
  // 9. Set opt.[[localeMatcher]] to matcher.
52 53 54 55
  Maybe<Intl::MatcherOption> maybe_locale_matcher =
      Intl::GetLocaleMatcher(isolate, options, "Intl.Segmenter");
  MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSSegmenter>());
  Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
56

57 58 59
  // 10. Let localeData be %Segmenter%.[[LocaleData]].

  // 11. Let r be ResolveLocale(%Segmenter%.[[AvailableLocales]],
60
  // requestedLocales, opt, %Segmenter%.[[RelevantExtensionKeys]]).
61
  Maybe<Intl::ResolvedLocale> maybe_resolve_locale =
62
      Intl::ResolveLocale(isolate, JSSegmenter::GetAvailableLocales(),
63
                          requested_locales, matcher, {});
64 65 66 67 68 69
  if (maybe_resolve_locale.IsNothing()) {
    THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),
                    JSSegmenter);
  }
  Intl::ResolvedLocale r = maybe_resolve_locale.FromJust();

70
  // 12. Set segmenter.[[Locale]] to the value of r.[[locale]].
71 72
  Handle<String> locale_str =
      isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
73 74

  // 13. Let granularity be ? GetOption(options, "granularity", "string", «
75
  // "grapheme", "word", "sentence" », "grapheme").
76 77
  Maybe<Granularity> maybe_granularity = Intl::GetStringOption<Granularity>(
      isolate, options, "granularity", "Intl.Segmenter",
78 79
      {"grapheme", "word", "sentence"},
      {Granularity::GRAPHEME, Granularity::WORD, Granularity::SENTENCE},
80 81 82
      Granularity::GRAPHEME);
  MAYBE_RETURN(maybe_granularity, MaybeHandle<JSSegmenter>());
  Granularity granularity_enum = maybe_granularity.FromJust();
83

84
  icu::Locale icu_locale = r.icu_locale;
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
  DCHECK(!icu_locale.isBogus());

  UErrorCode status = U_ZERO_ERROR;
  std::unique_ptr<icu::BreakIterator> icu_break_iterator;

  switch (granularity_enum) {
    case Granularity::GRAPHEME:
      icu_break_iterator.reset(
          icu::BreakIterator::createCharacterInstance(icu_locale, status));
      break;
    case Granularity::WORD:
      icu_break_iterator.reset(
          icu::BreakIterator::createWordInstance(icu_locale, status));
      break;
    case Granularity::SENTENCE:
      icu_break_iterator.reset(
          icu::BreakIterator::createSentenceInstance(icu_locale, status));
      break;
  }

105 106
  DCHECK(U_SUCCESS(status));
  DCHECK_NOT_NULL(icu_break_iterator.get());
107 108 109 110 111

  Handle<Managed<icu::BreakIterator>> managed_break_iterator =
      Managed<icu::BreakIterator>::FromUniquePtr(isolate, 0,
                                                 std::move(icu_break_iterator));

112
  // Now all properties are ready, so we can allocate the result object.
113
  Handle<JSSegmenter> segmenter = Handle<JSSegmenter>::cast(
114
      isolate->factory()->NewFastOrSlowJSObjectFromMap(map));
115
  DisallowGarbageCollection no_gc;
116
  segmenter->set_flags(0);
117

118 119
  // 12. Set segmenter.[[Locale]] to the value of r.[[Locale]].
  segmenter->set_locale(*locale_str);
120 121

  // 14. Set segmenter.[[SegmenterGranularity]] to granularity.
122 123 124
  segmenter->set_granularity(granularity_enum);

  segmenter->set_icu_break_iterator(*managed_break_iterator);
125

126 127
  // 15. Return segmenter.
  return segmenter;
128 129
}

130
// ecma402 #sec-Intl.Segmenter.prototype.resolvedOptions
131 132
Handle<JSObject> JSSegmenter::ResolvedOptions(Isolate* isolate,
                                              Handle<JSSegmenter> segmenter) {
133
  Factory* factory = isolate->factory();
134
  // 3. Let options be ! ObjectCreate(%ObjectPrototype%).
135
  Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
136 137 138 139 140 141 142 143 144 145 146 147
  // 4. For each row of Table 1, except the header row, do
  // a. Let p be the Property value of the current row.
  // b. Let v be the value of pr's internal slot whose name is the Internal Slot
  //    value of the current row.
  //
  // c. If v is not undefined, then
  //  i. Perform ! CreateDataPropertyOrThrow(options, p, v).
  //    Table 1: Resolved Options of Segmenter Instances
  //     Internal Slot                 Property
  //     [[Locale]]                    "locale"
  //     [[SegmenterGranularity]]      "granularity"

148
  Handle<String> locale(segmenter->locale(), isolate);
149 150
  JSObject::AddProperty(isolate, result, factory->locale_string(), locale,
                        NONE);
151
  JSObject::AddProperty(isolate, result, factory->granularity_string(),
152
                        segmenter->GranularityAsString(isolate), NONE);
153
  // 5. Return options.
154 155 156
  return result;
}

157 158 159 160 161 162 163 164
Handle<String> JSSegmenter::GranularityAsString(Isolate* isolate) const {
  return GetGranularityString(isolate, granularity());
}

Handle<String> JSSegmenter::GetGranularityString(Isolate* isolate,
                                                 Granularity granularity) {
  Factory* factory = isolate->factory();
  switch (granularity) {
165
    case Granularity::GRAPHEME:
166
      return factory->grapheme_string();
167
    case Granularity::WORD:
168
      return factory->word_string();
169
    case Granularity::SENTENCE:
170
      return factory->sentence_string();
171
  }
172
  UNREACHABLE();
173 174
}

175
const std::set<std::string>& JSSegmenter::GetAvailableLocales() {
176
  return Intl::GetAvailableLocales();
177 178
}

179 180
}  // namespace internal
}  // namespace v8