blob: 32f31ad003f3e4c847f93063a33e8b35afe96426 [file] [log] [blame]
// 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-break-iterator.h"
#include "src/objects/intl-objects.h"
#include "src/objects/js-break-iterator-inl.h"
#include "unicode/brkiter.h"
namespace v8 {
namespace internal {
namespace {
enum class Type { CHARACTER, WORD, SENTENCE, LINE };
} // anonymous namespace
MaybeHandle<JSV8BreakIterator> JSV8BreakIterator::New(
Isolate* isolate, Handle<Map> map, Handle<Object> locales,
Handle<Object> options_obj, const char* service) {
Factory* factory = isolate->factory();
// 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
Maybe<std::vector<std::string>> maybe_requested_locales =
Intl::CanonicalizeLocaleList(isolate, locales);
MAYBE_RETURN(maybe_requested_locales, MaybeHandle<JSV8BreakIterator>());
std::vector<std::string> requested_locales =
maybe_requested_locales.FromJust();
Handle<JSReceiver> options;
if (options_obj->IsUndefined(isolate)) {
options = factory->NewJSObjectWithNullProto();
} else {
ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
Object::ToObject(isolate, options_obj, service),
JSV8BreakIterator);
}
// Extract locale string
Maybe<Intl::MatcherOption> maybe_locale_matcher =
Intl::GetLocaleMatcher(isolate, options, service);
MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSV8BreakIterator>());
Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
Maybe<Intl::ResolvedLocale> maybe_resolve_locale =
Intl::ResolveLocale(isolate, JSV8BreakIterator::GetAvailableLocales(),
requested_locales, matcher, {});
if (maybe_resolve_locale.IsNothing()) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),
JSV8BreakIterator);
}
Intl::ResolvedLocale r = maybe_resolve_locale.FromJust();
// Extract type from options
Maybe<Type> maybe_type = Intl::GetStringOption<Type>(
isolate, options, "type", service,
{"word", "character", "sentence", "line"},
{Type::WORD, Type::CHARACTER, Type::SENTENCE, Type::LINE}, Type::WORD);
MAYBE_RETURN(maybe_type, MaybeHandle<JSV8BreakIterator>());
Type type_enum = maybe_type.FromJust();
icu::Locale icu_locale = r.icu_locale;
DCHECK(!icu_locale.isBogus());
// Construct break_iterator using icu_locale and type
UErrorCode status = U_ZERO_ERROR;
std::unique_ptr<icu::BreakIterator> break_iterator = nullptr;
switch (type_enum) {
case Type::CHARACTER:
break_iterator.reset(
icu::BreakIterator::createCharacterInstance(icu_locale, status));
break;
case Type::SENTENCE:
break_iterator.reset(
icu::BreakIterator::createSentenceInstance(icu_locale, status));
break;
case Type::LINE:
isolate->CountUsage(
v8::Isolate::UseCounterFeature::kBreakIteratorTypeLine);
break_iterator.reset(
icu::BreakIterator::createLineInstance(icu_locale, status));
break;
default:
isolate->CountUsage(
v8::Isolate::UseCounterFeature::kBreakIteratorTypeWord);
break_iterator.reset(
icu::BreakIterator::createWordInstance(icu_locale, status));
break;
}
// Error handling for break_iterator
if (U_FAILURE(status) || break_iterator.get() == nullptr) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),
JSV8BreakIterator);
}
isolate->CountUsage(v8::Isolate::UseCounterFeature::kBreakIterator);
// Construct managed objects from pointers
Handle<Managed<icu::BreakIterator>> managed_break_iterator =
Managed<icu::BreakIterator>::FromUniquePtr(isolate, 0,
std::move(break_iterator));
Handle<Managed<icu::UnicodeString>> managed_unicode_string =
Managed<icu::UnicodeString>::FromRawPtr(isolate, 0, nullptr);
Handle<String> locale_str =
isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
// Now all properties are ready, so we can allocate the result object.
Handle<JSV8BreakIterator> break_iterator_holder =
Handle<JSV8BreakIterator>::cast(
isolate->factory()->NewFastOrSlowJSObjectFromMap(map));
DisallowHeapAllocation no_gc;
break_iterator_holder->set_locale(*locale_str);
break_iterator_holder->set_break_iterator(*managed_break_iterator);
break_iterator_holder->set_unicode_string(*managed_unicode_string);
// Return break_iterator_holder
return break_iterator_holder;
}
namespace {
Type GetType(icu::BreakIterator* break_iterator) {
// Since the developer calling the Intl.v8BreakIterator already know the type,
// we usually do not need to know the type unless the resolvedOptions() is
// called, we use the following trick to figure out the type instead of
// storing it with the JSV8BreakIterator object to save memory.
// This routine is not fast but should be seldomly used only.
// We need to clone a copy of break iteator because we need to setText to it.
std::unique_ptr<icu::BreakIterator> cloned_break_iterator(
break_iterator->clone());
// Use a magic string "He is." to call next().
// character type: will return 1 for "H"
// word type: will return 2 for "He"
// line type: will return 3 for "He "
// sentence type: will return 6 for "He is."
icu::UnicodeString data("He is.");
cloned_break_iterator->setText(data);
switch (cloned_break_iterator->next()) {
case 1: // After "H"
return Type::CHARACTER;
case 2: // After "He"
return Type::WORD;
case 3: // After "He "
return Type::LINE;
case 6: // After "He is."
return Type::SENTENCE;
default:
UNREACHABLE();
}
}
Handle<String> TypeAsString(Isolate* isolate, Type type) {
switch (type) {
case Type::CHARACTER:
return ReadOnlyRoots(isolate).character_string_handle();
case Type::WORD:
return ReadOnlyRoots(isolate).word_string_handle();
case Type::SENTENCE:
return ReadOnlyRoots(isolate).sentence_string_handle();
case Type::LINE:
return ReadOnlyRoots(isolate).line_string_handle();
}
UNREACHABLE();
}
} // anonymous namespace
Handle<JSObject> JSV8BreakIterator::ResolvedOptions(
Isolate* isolate, Handle<JSV8BreakIterator> break_iterator) {
Factory* factory = isolate->factory();
Type type = GetType(break_iterator->break_iterator().raw());
Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
Handle<String> locale(break_iterator->locale(), isolate);
JSObject::AddProperty(isolate, result, factory->locale_string(), locale,
NONE);
JSObject::AddProperty(isolate, result, factory->type_string(),
TypeAsString(isolate, type), NONE);
return result;
}
void JSV8BreakIterator::AdoptText(
Isolate* isolate, Handle<JSV8BreakIterator> break_iterator_holder,
Handle<String> text) {
icu::BreakIterator* break_iterator =
break_iterator_holder->break_iterator().raw();
DCHECK_NOT_NULL(break_iterator);
Handle<Managed<icu::UnicodeString>> unicode_string =
Intl::SetTextToBreakIterator(isolate, text, break_iterator);
break_iterator_holder->set_unicode_string(*unicode_string);
}
Handle<Object> JSV8BreakIterator::Current(
Isolate* isolate, Handle<JSV8BreakIterator> break_iterator) {
return isolate->factory()->NewNumberFromInt(
break_iterator->break_iterator().raw()->current());
}
Handle<Object> JSV8BreakIterator::First(
Isolate* isolate, Handle<JSV8BreakIterator> break_iterator) {
return isolate->factory()->NewNumberFromInt(
break_iterator->break_iterator().raw()->first());
}
Handle<Object> JSV8BreakIterator::Next(
Isolate* isolate, Handle<JSV8BreakIterator> break_iterator) {
return isolate->factory()->NewNumberFromInt(
break_iterator->break_iterator().raw()->next());
}
String JSV8BreakIterator::BreakType(Isolate* isolate,
Handle<JSV8BreakIterator> break_iterator) {
int32_t status = break_iterator->break_iterator().raw()->getRuleStatus();
// Keep return values in sync with JavaScript BreakType enum.
if (status >= UBRK_WORD_NONE && status < UBRK_WORD_NONE_LIMIT) {
return ReadOnlyRoots(isolate).none_string();
}
if (status >= UBRK_WORD_NUMBER && status < UBRK_WORD_NUMBER_LIMIT) {
return ReadOnlyRoots(isolate).number_string();
}
if (status >= UBRK_WORD_LETTER && status < UBRK_WORD_LETTER_LIMIT) {
return ReadOnlyRoots(isolate).letter_string();
}
if (status >= UBRK_WORD_KANA && status < UBRK_WORD_KANA_LIMIT) {
return ReadOnlyRoots(isolate).kana_string();
}
if (status >= UBRK_WORD_IDEO && status < UBRK_WORD_IDEO_LIMIT) {
return ReadOnlyRoots(isolate).ideo_string();
}
return ReadOnlyRoots(isolate).unknown_string();
}
const std::set<std::string>& JSV8BreakIterator::GetAvailableLocales() {
return Intl::GetAvailableLocales();
}
} // namespace internal
} // namespace v8