Commit f03a754c authored by Sathya Gunasekaran's avatar Sathya Gunasekaran Committed by Commit Bot

[intl] Refactor instance type checks

Adds Intl::IsObjectOfType method to do type checks. This will make it
easier to port the methods using the runtime type check calls as we
won't have to create a v8::string for type checks.

Bug: v8:5751
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: I0babdc8709564be693ce808e2ef3ffef7b24ceec
Reviewed-on: https://chromium-review.googlesource.com/1121943
Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54143}
parent 7da34b96
......@@ -2122,6 +2122,7 @@ v8_source_set("v8_base") {
"src/objects/frame-array.h",
"src/objects/hash-table-inl.h",
"src/objects/hash-table.h",
"src/objects/intl-objects-inl.h",
"src/objects/intl-objects.cc",
"src/objects/intl-objects.h",
"src/objects/js-array-inl.h",
......@@ -2804,6 +2805,7 @@ v8_source_set("v8_base") {
"src/char-predicates.cc",
"src/intl.cc",
"src/intl.h",
"src/objects/intl-objects-inl.h",
"src/objects/intl-objects.cc",
"src/objects/intl-objects.h",
"src/objects/js-locale-inl.h",
......
......@@ -443,12 +443,8 @@ BUILTIN(NumberFormatPrototypeFormatToParts) {
HandleScope handle_scope(isolate);
CHECK_RECEIVER(JSObject, number_format_holder, method);
Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
Handle<Object> tag =
JSReceiver::GetDataProperty(number_format_holder, marker);
Handle<String> expected_tag =
isolate->factory()->NewStringFromStaticChars("numberformat");
if (!(tag->IsString() && String::cast(*tag)->Equals(*expected_tag))) {
if (!Intl::IsObjectOfType(isolate, number_format_holder,
Intl::Type::kNumberFormat)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
......@@ -478,10 +474,8 @@ BUILTIN(DateTimeFormatPrototypeFormatToParts) {
CHECK_RECEIVER(JSObject, date_format_holder, method);
Factory* factory = isolate->factory();
Handle<Symbol> marker = factory->intl_initialized_marker_symbol();
Handle<Object> tag = JSReceiver::GetDataProperty(date_format_holder, marker);
Handle<String> expected_tag = factory->NewStringFromStaticChars("dateformat");
if (!(tag->IsString() && String::cast(*tag)->Equals(*expected_tag))) {
if (!Intl::IsObjectOfType(isolate, date_format_holder,
Intl::Type::kDateTimeFormat)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
factory->NewStringFromAsciiChecked(method),
......
......@@ -67,7 +67,7 @@ endmacro
/**
* Adds bound method to the prototype of the given object.
*/
function AddBoundMethod(obj, methodName, implementation, length, typename,
function AddBoundMethod(obj, methodName, implementation, length, type,
compat) {
%CheckIsBootstrapping();
var internalName = %CreatePrivateSymbol(methodName);
......@@ -75,7 +75,7 @@ function AddBoundMethod(obj, methodName, implementation, length, typename,
DEFINE_METHOD(
obj.prototype,
get [methodName]() {
var receiver = Unwrap(this, typename, obj, methodName, compat);
var receiver = Unwrap(this, type, obj, methodName, compat);
if (IS_UNDEFINED(receiver[internalName])) {
var boundMethod;
if (IS_UNDEFINED(length) || length === 2) {
......@@ -120,11 +120,11 @@ function IntlConstruct(receiver, constructor, create, newTarget, args,
function Unwrap(receiver, typename, constructor, method, compat) {
if (!%IsInitializedIntlObjectOfType(receiver, typename)) {
function Unwrap(receiver, type, constructor, method, compat) {
if (!%IsInitializedIntlObjectOfType(receiver, type)) {
if (compat && receiver instanceof constructor) {
let fallback = receiver[IntlFallbackSymbol];
if (%IsInitializedIntlObjectOfType(fallback, typename)) {
if (%IsInitializedIntlObjectOfType(fallback, type)) {
return fallback;
}
}
......@@ -1053,7 +1053,7 @@ function CreateCollator(locales, options) {
var collator = %CreateCollator(requestedLocale, internalOptions, resolved);
%MarkAsInitializedIntlObjectOfType(collator, 'collator');
%MarkAsInitializedIntlObjectOfType(collator, COLLATOR_TYPE);
collator[resolvedSymbol] = resolved;
return collator;
......@@ -1079,8 +1079,8 @@ function CollatorConstructor() {
DEFINE_METHOD(
GlobalIntlCollator.prototype,
resolvedOptions() {
var coll = Unwrap(this, 'collator', GlobalIntlCollator, 'resolvedOptions',
false);
var coll = Unwrap(this, COLLATOR_TYPE, GlobalIntlCollator,
'resolvedOptions', false);
return {
locale: coll[resolvedSymbol].locale,
usage: coll[resolvedSymbol].usage,
......@@ -1123,7 +1123,7 @@ function compare(collator, x, y) {
};
AddBoundMethod(GlobalIntlCollator, 'compare', compare, 2, 'collator', false);
AddBoundMethod(GlobalIntlCollator, 'compare', compare, 2, COLLATOR_TYPE, false);
function PluralRulesConstructor() {
if (IS_UNDEFINED(new.target)) {
......@@ -1166,7 +1166,7 @@ function PluralRulesConstructor() {
var pluralRules = %CreatePluralRules(requestedLocale, internalOptions,
resolved);
%MarkAsInitializedIntlObjectOfType(pluralRules, 'pluralrules');
%MarkAsInitializedIntlObjectOfType(pluralRules, PLURAL_RULES_TYPE);
pluralRules[resolvedSymbol] = resolved;
return pluralRules;
......@@ -1176,7 +1176,7 @@ function PluralRulesConstructor() {
DEFINE_METHOD(
GlobalIntlPluralRules.prototype,
resolvedOptions() {
if (!%IsInitializedIntlObjectOfType(this, 'pluralrules')) {
if (!%IsInitializedIntlObjectOfType(this, PLURAL_RULES_TYPE)) {
throw %make_type_error(kIncompatibleMethodReceiver,
'Intl.PluralRules.prototype.resolvedOptions',
this);
......@@ -1217,7 +1217,7 @@ DEFINE_METHOD(
DEFINE_METHOD(
GlobalIntlPluralRules.prototype,
select(value) {
if (!%IsInitializedIntlObjectOfType(this, 'pluralrules')) {
if (!%IsInitializedIntlObjectOfType(this, PLURAL_RULES_TYPE)) {
throw %make_type_error(kIncompatibleMethodReceiver,
'Intl.PluralRules.prototype.select',
this);
......@@ -1378,7 +1378,7 @@ function CreateNumberFormat(locales, options) {
{value: currencyDisplay, writable: true});
}
%MarkAsInitializedIntlObjectOfType(numberFormat, 'numberformat');
%MarkAsInitializedIntlObjectOfType(numberFormat, NUMBER_FORMAT_TYPE);
numberFormat[resolvedSymbol] = resolved;
return numberFormat;
......@@ -1404,7 +1404,7 @@ function NumberFormatConstructor() {
DEFINE_METHOD(
GlobalIntlNumberFormat.prototype,
resolvedOptions() {
var format = Unwrap(this, 'numberformat', GlobalIntlNumberFormat,
var format = Unwrap(this, NUMBER_FORMAT_TYPE, GlobalIntlNumberFormat,
'resolvedOptions', true);
var result = {
locale: format[resolvedSymbol].locale,
......@@ -1465,7 +1465,7 @@ function formatNumber(formatter, value) {
AddBoundMethod(GlobalIntlNumberFormat, 'format', formatNumber, 1,
'numberformat', true);
NUMBER_FORMAT_TYPE, true);
/**
* Returns a string that matches LDML representation of the options object.
......@@ -1740,7 +1740,7 @@ function CreateDateTimeFormat(locales, options) {
throw %make_range_error(kUnsupportedTimeZone, tz);
}
%MarkAsInitializedIntlObjectOfType(dateFormat, 'dateformat');
%MarkAsInitializedIntlObjectOfType(dateFormat, DATE_TIME_FORMAT_TYPE);
dateFormat[resolvedSymbol] = resolved;
return dateFormat;
......@@ -1766,7 +1766,7 @@ function DateTimeFormatConstructor() {
DEFINE_METHOD(
GlobalIntlDateTimeFormat.prototype,
resolvedOptions() {
var format = Unwrap(this, 'dateformat', GlobalIntlDateTimeFormat,
var format = Unwrap(this, DATE_TIME_FORMAT_TYPE, GlobalIntlDateTimeFormat,
'resolvedOptions', true);
/**
......@@ -1841,7 +1841,7 @@ function formatDate(formatter, dateValue) {
}
// Length is 1 as specified in ECMA 402 v2+
AddBoundMethod(GlobalIntlDateTimeFormat, 'format', formatDate, 1, 'dateformat',
AddBoundMethod(GlobalIntlDateTimeFormat, 'format', formatDate, 1, DATE_TIME_FORMAT_TYPE,
true);
......@@ -1911,7 +1911,7 @@ function CreateBreakIterator(locales, options) {
var iterator = %CreateBreakIterator(locale.locale, internalOptions, resolved);
%MarkAsInitializedIntlObjectOfType(iterator, 'breakiterator');
%MarkAsInitializedIntlObjectOfType(iterator, BREAK_ITERATOR_TYPE);
iterator[resolvedSymbol] = resolved;
return iterator;
......@@ -1941,7 +1941,7 @@ DEFINE_METHOD(
throw %make_type_error(kOrdinaryFunctionCalledAsConstructor);
}
var segmenter = Unwrap(this, 'breakiterator', GlobalIntlv8BreakIterator,
var segmenter = Unwrap(this, BREAK_ITERATOR_TYPE, GlobalIntlv8BreakIterator,
'resolvedOptions', false);
return {
......@@ -2012,13 +2012,15 @@ function breakType(iterator) {
AddBoundMethod(GlobalIntlv8BreakIterator, 'adoptText', adoptText, 1,
'breakiterator');
AddBoundMethod(GlobalIntlv8BreakIterator, 'first', first, 0, 'breakiterator');
AddBoundMethod(GlobalIntlv8BreakIterator, 'next', next, 0, 'breakiterator');
BREAK_ITERATOR_TYPE);
AddBoundMethod(GlobalIntlv8BreakIterator, 'first', first, 0,
BREAK_ITERATOR_TYPE);
AddBoundMethod(GlobalIntlv8BreakIterator, 'next', next, 0,
BREAK_ITERATOR_TYPE);
AddBoundMethod(GlobalIntlv8BreakIterator, 'current', current, 0,
'breakiterator');
BREAK_ITERATOR_TYPE);
AddBoundMethod(GlobalIntlv8BreakIterator, 'breakType', breakType, 0,
'breakiterator');
BREAK_ITERATOR_TYPE);
// Save references to Intl objects and methods we use, for added security.
var savedObjects = {
......
......@@ -83,3 +83,11 @@ macro DEFINE_METHOD(obj, method_def) = DEFINE_METHOD_LEN(obj, method_def, -1);
# Constants. The compiler constant folds them.
define INFINITY = (1/0);
define UNDEFINED = (void 0);
# This should be kept consistent with Intl::Type.
define NUMBER_FORMAT_TYPE = 0;
define COLLATOR_TYPE = 1;
define DATE_TIME_FORMAT_TYPE = 2;
define PLURAL_RULES_TYPE = 3;
define BREAK_ITERATOR_TYPE = 4;
define LOCALE_TYPE = 5;
// 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.
#ifndef V8_OBJECTS_INTL_OBJECTS_INL_H_
#define V8_OBJECTS_INTL_OBJECTS_INL_H_
#include "src/objects/intl-objects.h"
namespace v8 {
namespace internal {
inline Intl::Type Intl::TypeFromInt(int type_int) {
STATIC_ASSERT(Intl::Type::kNumberFormat == 0);
DCHECK_LE(Intl::Type::kNumberFormat, type_int);
DCHECK_GT(Intl::Type::kTypeCount, type_int);
return static_cast<Intl::Type>(type_int);
}
inline Intl::Type Intl::TypeFromSmi(Smi* type) {
return TypeFromInt(Smi::ToInt(type));
}
} // namespace internal
} // namespace v8
#endif // V8_OBJECTS_INTL_OBJECTS_INL_H_
......@@ -7,6 +7,7 @@
#endif // V8_INTL_SUPPORT
#include "src/objects/intl-objects.h"
#include "src/objects/intl-objects-inl.h"
#include <memory>
......@@ -1080,8 +1081,8 @@ void V8BreakIterator::DeleteBreakIterator(
}
// Build the shortened locale; eg, convert xx_Yyyy_ZZ to xx_ZZ.
bool IntlUtil::RemoveLocaleScriptTag(const std::string& icu_locale,
std::string* locale_less_script) {
bool Intl::RemoveLocaleScriptTag(const std::string& icu_locale,
std::string* locale_less_script) {
icu::Locale new_locale = icu::Locale::createCanonical(icu_locale.c_str());
const char* icu_script = new_locale.getScript();
if (icu_script == NULL || strlen(icu_script) == 0) {
......@@ -1097,7 +1098,7 @@ bool IntlUtil::RemoveLocaleScriptTag(const std::string& icu_locale,
return true;
}
std::set<std::string> IntlUtil::GetAvailableLocales(const IcuService& service) {
std::set<std::string> Intl::GetAvailableLocales(const IcuService& service) {
const icu::Locale* icu_available_locales = nullptr;
int32_t count = 0;
......@@ -1142,7 +1143,7 @@ std::set<std::string> IntlUtil::GetAvailableLocales(const IcuService& service) {
locales.insert(locale);
std::string shortened_locale;
if (IntlUtil::RemoveLocaleScriptTag(icu_name, &shortened_locale)) {
if (Intl::RemoveLocaleScriptTag(icu_name, &shortened_locale)) {
std::replace(shortened_locale.begin(), shortened_locale.end(), '_', '-');
locales.insert(shortened_locale);
}
......@@ -1151,5 +1152,19 @@ std::set<std::string> IntlUtil::GetAvailableLocales(const IcuService& service) {
return locales;
}
bool Intl::IsObjectOfType(Isolate* isolate, Handle<Object> input,
Intl::Type expected_type) {
if (!input->IsJSObject()) return false;
Handle<JSObject> obj = Handle<JSObject>::cast(input);
Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
Handle<Object> tag = JSReceiver::GetDataProperty(obj, marker);
if (!tag->IsSmi()) return false;
Intl::Type type = Intl::TypeFromSmi(Smi::cast(*tag));
return type == expected_type;
}
} // namespace internal
} // namespace v8
......@@ -163,13 +163,36 @@ class V8BreakIterator {
V8BreakIterator();
};
class IntlUtil {
class Intl {
public:
enum Type {
kNumberFormat = 0,
kCollator,
kDateTimeFormat,
kPluralRules,
kBreakIterator,
kLocale,
kTypeCount
};
inline static Intl::Type TypeFromInt(int type);
inline static Intl::Type TypeFromSmi(Smi* type);
// Checks if the given object has the expected_type based by looking
// up a private symbol on the object.
//
// TODO(gsathya): This should just be an instance type check once we
// move all the Intl objects to C++.
static bool IsObjectOfType(Isolate* isolate, Handle<Object> object,
Intl::Type expected_type);
// Gets the ICU locales for a given service. If there is a locale with a
// script tag then the locales also include a locale without the script; eg,
// pa_Guru_IN (language=Panjabi, script=Gurmukhi, country-India) would include
// pa_IN.
static std::set<std::string> GetAvailableLocales(const IcuService& service);
// If locale has a script tag then return true and the locale without the
// script else return false and an empty string
static bool RemoveLocaleScriptTag(const std::string& icu_locale,
......
......@@ -20,6 +20,7 @@
#include "src/intl.h"
#include "src/isolate-inl.h"
#include "src/messages.h"
#include "src/objects/intl-objects-inl.h"
#include "src/objects/intl-objects.h"
#include "src/utils.h"
......@@ -172,36 +173,18 @@ RUNTIME_FUNCTION(Runtime_GetDefaultICULocale) {
return *factory->NewStringFromStaticChars("und");
}
RUNTIME_FUNCTION(Runtime_IsInitializedIntlObject) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, input, 0);
if (!input->IsJSObject()) return isolate->heap()->false_value();
Handle<JSObject> obj = Handle<JSObject>::cast(input);
Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
Handle<Object> tag = JSReceiver::GetDataProperty(obj, marker);
return isolate->heap()->ToBoolean(!tag->IsUndefined(isolate));
}
RUNTIME_FUNCTION(Runtime_IsInitializedIntlObjectOfType) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, input, 0);
CONVERT_ARG_HANDLE_CHECKED(String, expected_type, 1);
CONVERT_SMI_ARG_CHECKED(expected_type_int, 1);
if (!input->IsJSObject()) return isolate->heap()->false_value();
Handle<JSObject> obj = Handle<JSObject>::cast(input);
Intl::Type expected_type = Intl::TypeFromInt(expected_type_int);
Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
Handle<Object> tag = JSReceiver::GetDataProperty(obj, marker);
return isolate->heap()->ToBoolean(tag->IsString() &&
String::cast(*tag)->Equals(*expected_type));
return isolate->heap()->ToBoolean(
Intl::IsObjectOfType(isolate, input, expected_type));
}
RUNTIME_FUNCTION(Runtime_MarkAsInitializedIntlObjectOfType) {
......@@ -210,7 +193,13 @@ RUNTIME_FUNCTION(Runtime_MarkAsInitializedIntlObjectOfType) {
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSObject, input, 0);
CONVERT_ARG_HANDLE_CHECKED(String, type, 1);
CONVERT_ARG_HANDLE_CHECKED(Smi, type, 1);
#ifdef DEBUG
// TypeFromSmi does correctness checks.
Intl::Type type_intl = Intl::TypeFromSmi(*type);
USE(type_intl);
#endif
Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
JSObject::SetProperty(input, marker, type, LanguageMode::kStrict).Assert();
......
......@@ -225,7 +225,6 @@ namespace internal {
F(InternalCompare, 3, 1) \
F(InternalDateFormat, 2, 1) \
F(InternalNumberFormat, 2, 1) \
F(IsInitializedIntlObject, 1, 1) \
F(IsInitializedIntlObjectOfType, 2, 1) \
F(MarkAsInitializedIntlObjectOfType, 2, 1) \
F(PluralRulesSelect, 2, 1) \
......
......@@ -183,15 +183,13 @@ TEST(GetOptions) {
bool ScriptTagWasRemoved(std::string locale, std::string expected) {
std::string without_script_tag;
bool didShorten =
IntlUtil::RemoveLocaleScriptTag(locale, &without_script_tag);
bool didShorten = Intl::RemoveLocaleScriptTag(locale, &without_script_tag);
return didShorten && expected == without_script_tag;
}
bool ScriptTagWasNotRemoved(std::string locale) {
std::string without_script_tag;
bool didShorten =
IntlUtil::RemoveLocaleScriptTag(locale, &without_script_tag);
bool didShorten = Intl::RemoveLocaleScriptTag(locale, &without_script_tag);
return !didShorten && without_script_tag.empty();
}
......@@ -209,23 +207,61 @@ TEST(RemoveLocaleScriptTag) {
TEST(GetAvailableLocales) {
std::set<std::string> locales;
locales = IntlUtil::GetAvailableLocales(IcuService::kBreakIterator);
locales = Intl::GetAvailableLocales(IcuService::kBreakIterator);
CHECK(locales.count("en-US"));
CHECK(!locales.count("abcdefg"));
locales = IntlUtil::GetAvailableLocales(IcuService::kCollator);
locales = Intl::GetAvailableLocales(IcuService::kCollator);
CHECK(locales.count("en-US"));
locales = IntlUtil::GetAvailableLocales(IcuService::kDateFormat);
locales = Intl::GetAvailableLocales(IcuService::kDateFormat);
CHECK(locales.count("en-US"));
locales = IntlUtil::GetAvailableLocales(IcuService::kNumberFormat);
locales = Intl::GetAvailableLocales(IcuService::kNumberFormat);
CHECK(locales.count("en-US"));
locales = IntlUtil::GetAvailableLocales(IcuService::kPluralRules);
locales = Intl::GetAvailableLocales(IcuService::kPluralRules);
CHECK(locales.count("en-US"));
}
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]));
}
} // namespace internal
} // namespace v8
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment