2017-06-29 22:28:00 +00:00
|
|
|
// Copyright 2017 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.
|
|
|
|
|
|
|
|
#ifdef V8_INTL_SUPPORT
|
|
|
|
|
|
|
|
#include "src/builtins/builtins-intl.h"
|
2018-06-11 21:30:30 +00:00
|
|
|
#include "src/lookup.h"
|
|
|
|
#include "src/objects-inl.h"
|
2018-06-19 18:03:56 +00:00
|
|
|
#include "src/objects/intl-objects.h"
|
2017-06-29 22:28:00 +00:00
|
|
|
#include "test/cctest/cctest.h"
|
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
// This operator overloading enables CHECK_EQ to be used with
|
|
|
|
// std::vector<NumberFormatSpan>
|
|
|
|
bool operator==(const NumberFormatSpan& lhs, const NumberFormatSpan& rhs) {
|
|
|
|
return memcmp(&lhs, &rhs, sizeof(lhs)) == 0;
|
|
|
|
}
|
|
|
|
template <typename _CharT, typename _Traits>
|
|
|
|
std::basic_ostream<_CharT, _Traits>& operator<<(
|
|
|
|
std::basic_ostream<_CharT, _Traits>& self, const NumberFormatSpan& part) {
|
|
|
|
return self << "{" << part.field_id << "," << part.begin_pos << ","
|
|
|
|
<< part.end_pos << "}";
|
|
|
|
}
|
|
|
|
|
|
|
|
void test_flatten_regions_to_parts(
|
|
|
|
const std::vector<NumberFormatSpan>& regions,
|
|
|
|
const std::vector<NumberFormatSpan>& expected_parts) {
|
|
|
|
std::vector<NumberFormatSpan> mutable_regions = regions;
|
|
|
|
std::vector<NumberFormatSpan> parts = FlattenRegionsToParts(&mutable_regions);
|
|
|
|
CHECK_EQ(expected_parts, parts);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(FlattenRegionsToParts) {
|
|
|
|
test_flatten_regions_to_parts(
|
|
|
|
std::vector<NumberFormatSpan>{
|
|
|
|
NumberFormatSpan(-1, 0, 10), NumberFormatSpan(1, 2, 8),
|
|
|
|
NumberFormatSpan(2, 2, 4), NumberFormatSpan(3, 6, 8),
|
|
|
|
},
|
|
|
|
std::vector<NumberFormatSpan>{
|
|
|
|
NumberFormatSpan(-1, 0, 2), NumberFormatSpan(2, 2, 4),
|
|
|
|
NumberFormatSpan(1, 4, 6), NumberFormatSpan(3, 6, 8),
|
|
|
|
NumberFormatSpan(-1, 8, 10),
|
|
|
|
});
|
|
|
|
test_flatten_regions_to_parts(
|
|
|
|
std::vector<NumberFormatSpan>{
|
|
|
|
NumberFormatSpan(0, 0, 1),
|
|
|
|
},
|
|
|
|
std::vector<NumberFormatSpan>{
|
|
|
|
NumberFormatSpan(0, 0, 1),
|
|
|
|
});
|
|
|
|
test_flatten_regions_to_parts(
|
|
|
|
std::vector<NumberFormatSpan>{
|
|
|
|
NumberFormatSpan(-1, 0, 1), NumberFormatSpan(0, 0, 1),
|
|
|
|
},
|
|
|
|
std::vector<NumberFormatSpan>{
|
|
|
|
NumberFormatSpan(0, 0, 1),
|
|
|
|
});
|
|
|
|
test_flatten_regions_to_parts(
|
|
|
|
std::vector<NumberFormatSpan>{
|
|
|
|
NumberFormatSpan(0, 0, 1), NumberFormatSpan(-1, 0, 1),
|
|
|
|
},
|
|
|
|
std::vector<NumberFormatSpan>{
|
|
|
|
NumberFormatSpan(0, 0, 1),
|
|
|
|
});
|
|
|
|
test_flatten_regions_to_parts(
|
|
|
|
std::vector<NumberFormatSpan>{
|
|
|
|
NumberFormatSpan(-1, 0, 10), NumberFormatSpan(1, 0, 1),
|
|
|
|
NumberFormatSpan(2, 0, 2), NumberFormatSpan(3, 0, 3),
|
|
|
|
NumberFormatSpan(4, 0, 4), NumberFormatSpan(5, 0, 5),
|
|
|
|
NumberFormatSpan(15, 5, 10), NumberFormatSpan(16, 6, 10),
|
|
|
|
NumberFormatSpan(17, 7, 10), NumberFormatSpan(18, 8, 10),
|
|
|
|
NumberFormatSpan(19, 9, 10),
|
|
|
|
},
|
|
|
|
std::vector<NumberFormatSpan>{
|
|
|
|
NumberFormatSpan(1, 0, 1), NumberFormatSpan(2, 1, 2),
|
|
|
|
NumberFormatSpan(3, 2, 3), NumberFormatSpan(4, 3, 4),
|
|
|
|
NumberFormatSpan(5, 4, 5), NumberFormatSpan(15, 5, 6),
|
|
|
|
NumberFormatSpan(16, 6, 7), NumberFormatSpan(17, 7, 8),
|
|
|
|
NumberFormatSpan(18, 8, 9), NumberFormatSpan(19, 9, 10),
|
|
|
|
});
|
|
|
|
|
|
|
|
// : 4
|
|
|
|
// : 22 33 3
|
|
|
|
// : 11111 22
|
|
|
|
// input regions: 0000000 111
|
|
|
|
// : ------------
|
|
|
|
// output parts: 0221340--231
|
|
|
|
test_flatten_regions_to_parts(
|
|
|
|
std::vector<NumberFormatSpan>{
|
|
|
|
NumberFormatSpan(-1, 0, 12), NumberFormatSpan(0, 0, 7),
|
|
|
|
NumberFormatSpan(1, 9, 12), NumberFormatSpan(1, 1, 6),
|
|
|
|
NumberFormatSpan(2, 9, 11), NumberFormatSpan(2, 1, 3),
|
|
|
|
NumberFormatSpan(3, 10, 11), NumberFormatSpan(3, 4, 6),
|
|
|
|
NumberFormatSpan(4, 5, 6),
|
|
|
|
},
|
|
|
|
std::vector<NumberFormatSpan>{
|
|
|
|
NumberFormatSpan(0, 0, 1), NumberFormatSpan(2, 1, 3),
|
|
|
|
NumberFormatSpan(1, 3, 4), NumberFormatSpan(3, 4, 5),
|
|
|
|
NumberFormatSpan(4, 5, 6), NumberFormatSpan(0, 6, 7),
|
|
|
|
NumberFormatSpan(-1, 7, 9), NumberFormatSpan(2, 9, 10),
|
|
|
|
NumberFormatSpan(3, 10, 11), NumberFormatSpan(1, 11, 12),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-07-05 12:35:08 +00:00
|
|
|
TEST(GetStringOption) {
|
2018-06-11 21:30:30 +00:00
|
|
|
LocalContext env;
|
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
v8::Isolate* v8_isolate = env->GetIsolate();
|
|
|
|
v8::HandleScope handle_scope(v8_isolate);
|
|
|
|
|
|
|
|
Handle<JSObject> options = isolate->factory()->NewJSObjectWithNullProto();
|
2018-07-05 12:35:08 +00:00
|
|
|
{
|
|
|
|
// No value found
|
|
|
|
std::unique_ptr<char[]> result = nullptr;
|
|
|
|
Maybe<bool> found =
|
|
|
|
Intl::GetStringOption(isolate, options, "foo",
|
|
|
|
std::vector<const char*>{}, "service", &result);
|
|
|
|
CHECK(!found.FromJust());
|
|
|
|
CHECK_NULL(result);
|
|
|
|
}
|
|
|
|
|
2018-06-11 21:30:30 +00:00
|
|
|
Handle<String> key = isolate->factory()->NewStringFromAsciiChecked("foo");
|
|
|
|
v8::internal::LookupIterator it(options, key);
|
|
|
|
CHECK(Object::SetProperty(&it, Handle<Smi>(Smi::FromInt(42), isolate),
|
|
|
|
LanguageMode::kStrict,
|
|
|
|
AllocationMemento::MAY_BE_STORE_FROM_KEYED)
|
|
|
|
.FromJust());
|
2018-07-05 12:35:08 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
// Value found
|
|
|
|
std::unique_ptr<char[]> result = nullptr;
|
|
|
|
Maybe<bool> found =
|
|
|
|
Intl::GetStringOption(isolate, options, "foo",
|
|
|
|
std::vector<const char*>{}, "service", &result);
|
|
|
|
CHECK(found.FromJust());
|
|
|
|
CHECK_NOT_NULL(result);
|
|
|
|
CHECK_EQ(0, strcmp("42", result.get()));
|
|
|
|
}
|
|
|
|
|
2018-06-11 21:30:30 +00:00
|
|
|
{
|
2018-07-05 12:35:08 +00:00
|
|
|
// No expected value in values array
|
|
|
|
std::unique_ptr<char[]> result = nullptr;
|
|
|
|
Maybe<bool> found = Intl::GetStringOption(isolate, options, "foo",
|
|
|
|
std::vector<const char*>{"bar"},
|
|
|
|
"service", &result);
|
2018-06-11 21:30:30 +00:00
|
|
|
CHECK(isolate->has_pending_exception());
|
2018-07-05 12:35:08 +00:00
|
|
|
CHECK(found.IsNothing());
|
|
|
|
CHECK_NULL(result);
|
2018-06-11 21:30:30 +00:00
|
|
|
isolate->clear_pending_exception();
|
|
|
|
}
|
|
|
|
|
2018-07-05 12:35:08 +00:00
|
|
|
{
|
|
|
|
// Expected value in values array
|
|
|
|
std::unique_ptr<char[]> result = nullptr;
|
|
|
|
Maybe<bool> found = Intl::GetStringOption(isolate, options, "foo",
|
|
|
|
std::vector<const char*>{"42"},
|
|
|
|
"service", &result);
|
|
|
|
CHECK(found.FromJust());
|
|
|
|
CHECK_NOT_NULL(result);
|
|
|
|
CHECK_EQ(0, strcmp("42", result.get()));
|
|
|
|
}
|
|
|
|
}
|
2018-06-11 21:30:30 +00:00
|
|
|
|
2018-07-05 12:35:08 +00:00
|
|
|
TEST(GetBoolOption) {
|
|
|
|
LocalContext env;
|
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
v8::Isolate* v8_isolate = env->GetIsolate();
|
|
|
|
v8::HandleScope handle_scope(v8_isolate);
|
|
|
|
|
|
|
|
Handle<JSObject> options = isolate->factory()->NewJSObjectWithNullProto();
|
|
|
|
{
|
|
|
|
bool result = false;
|
|
|
|
Maybe<bool> found =
|
|
|
|
Intl::GetBoolOption(isolate, options, "foo", "service", &result);
|
|
|
|
CHECK(!found.FromJust());
|
|
|
|
CHECK(!result);
|
|
|
|
}
|
|
|
|
|
|
|
|
Handle<String> key = isolate->factory()->NewStringFromAsciiChecked("foo");
|
|
|
|
{
|
|
|
|
v8::internal::LookupIterator it(options, key);
|
|
|
|
Handle<Object> false_value =
|
|
|
|
handle(i::ReadOnlyRoots(isolate).false_value(), isolate);
|
|
|
|
Object::SetProperty(options, key, false_value, LanguageMode::kStrict)
|
|
|
|
.Assert();
|
|
|
|
bool result = false;
|
|
|
|
Maybe<bool> found =
|
|
|
|
Intl::GetBoolOption(isolate, options, "foo", "service", &result);
|
|
|
|
CHECK(found.FromJust());
|
|
|
|
CHECK(!result);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::internal::LookupIterator it(options, key);
|
|
|
|
Handle<Object> true_value =
|
|
|
|
handle(i::ReadOnlyRoots(isolate).true_value(), isolate);
|
|
|
|
Object::SetProperty(options, key, true_value, LanguageMode::kStrict)
|
|
|
|
.Assert();
|
|
|
|
bool result = false;
|
|
|
|
Maybe<bool> found =
|
|
|
|
Intl::GetBoolOption(isolate, options, "foo", "service", &result);
|
|
|
|
CHECK(found.FromJust());
|
|
|
|
CHECK(result);
|
|
|
|
}
|
2018-06-11 21:30:30 +00:00
|
|
|
}
|
|
|
|
|
2018-06-19 18:03:56 +00:00
|
|
|
bool ScriptTagWasRemoved(std::string locale, std::string expected) {
|
|
|
|
std::string without_script_tag;
|
2018-07-02 14:02:35 +00:00
|
|
|
bool didShorten = Intl::RemoveLocaleScriptTag(locale, &without_script_tag);
|
2018-06-19 18:03:56 +00:00
|
|
|
return didShorten && expected == without_script_tag;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScriptTagWasNotRemoved(std::string locale) {
|
|
|
|
std::string without_script_tag;
|
2018-07-02 14:02:35 +00:00
|
|
|
bool didShorten = Intl::RemoveLocaleScriptTag(locale, &without_script_tag);
|
2018-06-19 18:03:56 +00:00
|
|
|
return !didShorten && without_script_tag.empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(RemoveLocaleScriptTag) {
|
|
|
|
CHECK(ScriptTagWasRemoved("aa_Bbbb_CC", "aa_CC"));
|
|
|
|
CHECK(ScriptTagWasRemoved("aaa_Bbbb_CC", "aaa_CC"));
|
|
|
|
|
|
|
|
CHECK(ScriptTagWasNotRemoved("aa"));
|
|
|
|
CHECK(ScriptTagWasNotRemoved("aaa"));
|
|
|
|
CHECK(ScriptTagWasNotRemoved("aa_CC"));
|
|
|
|
CHECK(ScriptTagWasNotRemoved("aa_Bbb_CC"));
|
|
|
|
CHECK(ScriptTagWasNotRemoved("aa_1bbb_CC"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GetAvailableLocales) {
|
|
|
|
std::set<std::string> locales;
|
|
|
|
|
2018-07-02 14:02:35 +00:00
|
|
|
locales = Intl::GetAvailableLocales(IcuService::kBreakIterator);
|
2018-06-19 18:03:56 +00:00
|
|
|
CHECK(locales.count("en-US"));
|
|
|
|
CHECK(!locales.count("abcdefg"));
|
|
|
|
|
2018-07-02 14:02:35 +00:00
|
|
|
locales = Intl::GetAvailableLocales(IcuService::kCollator);
|
2018-06-19 18:03:56 +00:00
|
|
|
CHECK(locales.count("en-US"));
|
|
|
|
|
2018-07-02 14:02:35 +00:00
|
|
|
locales = Intl::GetAvailableLocales(IcuService::kDateFormat);
|
2018-06-19 18:03:56 +00:00
|
|
|
CHECK(locales.count("en-US"));
|
|
|
|
|
2018-07-02 14:02:35 +00:00
|
|
|
locales = Intl::GetAvailableLocales(IcuService::kNumberFormat);
|
2018-06-19 18:03:56 +00:00
|
|
|
CHECK(locales.count("en-US"));
|
|
|
|
|
2018-07-02 14:02:35 +00:00
|
|
|
locales = Intl::GetAvailableLocales(IcuService::kPluralRules);
|
2018-06-19 18:03:56 +00:00
|
|
|
CHECK(locales.count("en-US"));
|
2018-07-10 19:29:55 +00:00
|
|
|
|
|
|
|
locales = Intl::GetAvailableLocales(IcuService::kResourceBundle);
|
|
|
|
CHECK(locales.count("en-US"));
|
|
|
|
|
|
|
|
locales = Intl::GetAvailableLocales(IcuService::kRelativeDateTimeFormatter);
|
|
|
|
CHECK(locales.count("en-US"));
|
2018-06-19 18:03:56 +00:00
|
|
|
}
|
|
|
|
|
2018-07-02 14:02:35 +00:00
|
|
|
TEST(IsObjectOfType) {
|
|
|
|
LocalContext env;
|
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
v8::Isolate* v8_isolate = env->GetIsolate();
|
|
|
|
v8::HandleScope handle_scope(v8_isolate);
|
|
|
|
|
|
|
|
Handle<JSObject> obj = isolate->factory()->NewJSObjectWithNullProto();
|
|
|
|
Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
|
|
|
|
|
|
|
|
STATIC_ASSERT(Intl::Type::kNumberFormat == 0);
|
|
|
|
Intl::Type types[] = {Intl::Type::kNumberFormat, Intl::Type::kCollator,
|
|
|
|
Intl::Type::kDateTimeFormat, Intl::Type::kPluralRules,
|
|
|
|
Intl::Type::kBreakIterator, Intl::Type::kLocale};
|
|
|
|
|
|
|
|
for (auto type : types) {
|
|
|
|
Handle<Smi> tag =
|
|
|
|
Handle<Smi>(Smi::FromInt(static_cast<int>(type)), isolate);
|
|
|
|
JSObject::SetProperty(obj, marker, tag, LanguageMode::kStrict).Assert();
|
|
|
|
|
|
|
|
CHECK(Intl::IsObjectOfType(isolate, obj, type));
|
|
|
|
}
|
|
|
|
|
|
|
|
Handle<Object> tag = isolate->factory()->NewStringFromAsciiChecked("foo");
|
|
|
|
JSObject::SetProperty(obj, marker, tag, LanguageMode::kStrict).Assert();
|
|
|
|
CHECK(!Intl::IsObjectOfType(isolate, obj, types[0]));
|
|
|
|
|
|
|
|
CHECK(!Intl::IsObjectOfType(isolate, tag, types[0]));
|
|
|
|
CHECK(!Intl::IsObjectOfType(isolate, Handle<Smi>(Smi::FromInt(0), isolate),
|
|
|
|
types[0]));
|
|
|
|
|
|
|
|
// Proxy with target as an initialized object should fail.
|
|
|
|
tag = Handle<Smi>(Smi::FromInt(static_cast<int>(types[0])), isolate);
|
|
|
|
JSObject::SetProperty(obj, marker, tag, LanguageMode::kStrict).Assert();
|
|
|
|
Handle<JSReceiver> proxy = isolate->factory()->NewJSProxy(
|
|
|
|
obj, isolate->factory()->NewJSObjectWithNullProto());
|
|
|
|
CHECK(!Intl::IsObjectOfType(isolate, proxy, types[0]));
|
|
|
|
}
|
|
|
|
|
2017-06-29 22:28:00 +00:00
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|
|
|
|
|
|
|
|
#endif // V8_INTL_SUPPORT
|