Commit cabcfb3a authored by Frank Tang's avatar Frank Tang Committed by Commit Bot

[Intl] Move ToDateTimeOptions/ToLocaleDateTime to C++

Bug: v8:7961
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: Ic414a51a64040f253da1d7ccf03c558ea70ad2bf
Reviewed-on: https://chromium-review.googlesource.com/1155271
Commit-Queue: Frank Tang <ftang@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55199}
parent 8c641547
......@@ -1102,7 +1102,7 @@ function CreateDateTimeFormat(locales, options) {
var locale = resolveLocale('dateformat', locales, options);
options = toDateTimeOptions(options, 'any', 'date');
options = %ToDateTimeOptions(options, 'any', 'date');
var getOption = getGetOption(options, 'dateformat');
......@@ -1524,26 +1524,6 @@ function cachedOrNewService(service, locales, options, defaults) {
"cached_or_new_service", cachedOrNewService
]);
/**
* Returns actual formatted date or fails if date parameter is invalid.
*/
function toLocaleDateTime(date, locales, options, required, defaults, service) {
if (!(date instanceof GlobalDate)) {
throw %make_type_error(kMethodInvokedOnWrongType, "Date");
}
var dateValue = TO_NUMBER(date);
if (NUMBER_IS_NAN(dateValue)) return 'Invalid Date';
var internalOptions = toDateTimeOptions(options, required, defaults);
var dateFormat =
cachedOrNewService(service, locales, options, internalOptions);
return %FormatDate(dateFormat, date);
}
/**
* Formats a Date object (this) using locale and options values.
* If locale or options are omitted, defaults are used - both date and time are
......@@ -1554,7 +1534,7 @@ DEFINE_METHOD(
toLocaleString() {
var locales = arguments[0];
var options = arguments[1];
return toLocaleDateTime(
return %ToLocaleDateTime(
this, locales, options, 'any', 'all', 'dateformatall');
}
);
......@@ -1570,7 +1550,7 @@ DEFINE_METHOD(
toLocaleDateString() {
var locales = arguments[0];
var options = arguments[1];
return toLocaleDateTime(
return %ToLocaleDateTime(
this, locales, options, 'date', 'date', 'dateformatdate');
}
);
......@@ -1586,7 +1566,7 @@ DEFINE_METHOD(
toLocaleTimeString() {
var locales = arguments[0];
var options = arguments[1];
return toLocaleDateTime(
return %ToLocaleDateTime(
this, locales, options, 'time', 'time', 'dateformattime');
}
);
......
......@@ -1342,6 +1342,27 @@ MaybeHandle<JSObject> JSObject::New(Handle<JSFunction> constructor,
return result;
}
// 9.1.12 ObjectCreate ( proto [ , internalSlotsList ] )
// Notice: This is NOT 19.1.2.2 Object.create ( O, Properties )
MaybeHandle<JSObject> JSObject::ObjectCreate(Isolate* isolate,
Handle<Object> prototype) {
// Generate the map with the specified {prototype} based on the Object
// function's initial map from the current native context.
// TODO(bmeurer): Use a dedicated cache for Object.create; think about
// slack tracking for Object.create.
Handle<Map> map =
Map::GetObjectCreateMap(isolate, Handle<HeapObject>::cast(prototype));
// Actually allocate the object.
Handle<JSObject> object;
if (map->is_dictionary_map()) {
object = isolate->factory()->NewSlowJSObjectFromMap(map);
} else {
object = isolate->factory()->NewJSObjectFromMap(map);
}
return object;
}
void JSObject::EnsureWritableFastElements(Handle<JSObject> object) {
DCHECK(object->HasSmiOrObjectElements() ||
object->HasFastStringWrapperElements());
......
......@@ -2235,6 +2235,11 @@ class JSObject: public JSReceiver {
static MaybeHandle<Context> GetFunctionRealm(Handle<JSObject> object);
// 9.1.12 ObjectCreate ( proto [ , internalSlotsList ] )
// Notice: This is NOT 19.1.2.2 Object.create ( O, Properties )
static V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> ObjectCreate(
Isolate* isolate, Handle<Object> prototype);
// [elements]: The elements (properties with names that are integers).
//
// Elements can be in two general modes: fast and slow. Each mode
......
......@@ -552,6 +552,23 @@ void SetResolvedBreakIteratorSettings(Isolate* isolate,
.Assert();
}
}
MaybeHandle<JSObject> CachedOrNewService(Isolate* isolate,
Handle<String> service,
Handle<Object> locales,
Handle<Object> options,
Handle<Object> internal_options) {
Handle<Object> result;
Handle<Object> undefined_value(ReadOnlyRoots(isolate).undefined_value(),
isolate);
Handle<Object> args[] = {service, locales, options, internal_options};
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
Execution::Call(isolate, isolate->cached_or_new_service(),
undefined_value, arraysize(args), args),
JSArray);
return Handle<JSObject>::cast(result);
}
} // namespace
icu::Locale Intl::CreateICULocale(Isolate* isolate,
......@@ -666,6 +683,44 @@ MaybeHandle<String> DateFormat::DateTimeFormat(
return DateFormat::FormatDateTime(isolate, date_time_format_holder, x);
}
MaybeHandle<String> DateFormat::ToLocaleDateTime(
Isolate* isolate, Handle<Object> date, Handle<Object> locales,
Handle<Object> options, const char* required, const char* defaults,
const char* service) {
Factory* factory = isolate->factory();
// 1. Let x be ? thisTimeValue(this value);
if (!date->IsJSDate()) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kMethodInvokedOnWrongType,
factory->NewStringFromStaticChars("date")),
String);
}
double const x = Handle<JSDate>::cast(date)->value()->Number();
// 2. If x is NaN, return "Invalid Date"
if (std::isnan(x)) {
return factory->NewStringFromStaticChars("Invalid Date");
}
// 3. Let options be ? ToDateTimeOptions(options, required, defaults).
Handle<JSObject> internal_options;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, internal_options,
DateFormat::ToDateTimeOptions(isolate, options, required, defaults),
String);
// 4. Let dateFormat be ? Construct(%DateTimeFormat%, « locales, options »).
Handle<JSObject> date_format;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, date_format,
CachedOrNewService(isolate, factory->NewStringFromAsciiChecked(service),
locales, options, internal_options),
String);
// 5. Return FormatDateTime(dateFormat, x).
return DateFormat::FormatDateTime(isolate, date_format, x);
}
icu::DecimalFormat* NumberFormat::InitializeNumberFormat(
Isolate* isolate, Handle<String> locale, Handle<JSObject> options,
Handle<JSObject> resolved) {
......@@ -817,6 +872,110 @@ bool Intl::RemoveLocaleScriptTag(const std::string& icu_locale,
return true;
}
namespace {
Maybe<bool> IsPropertyUndefined(Isolate* isolate, Handle<JSObject> options,
const char* property) {
Factory* factory = isolate->factory();
// i. Let prop be the property name.
// ii. Let value be ? Get(options, prop).
Handle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, value,
Object::GetPropertyOrElement(
isolate, options, factory->NewStringFromAsciiChecked(property)),
Nothing<bool>());
return Just(value->IsUndefined(isolate));
}
} // namespace
// ecma-402/#sec-todatetimeoptions
MaybeHandle<JSObject> DateFormat::ToDateTimeOptions(
Isolate* isolate, Handle<Object> input_options, const char* required,
const char* defaults) {
Factory* factory = isolate->factory();
// 1. If options is undefined, let options be null; otherwise let options be ?
// ToObject(options).
Handle<JSObject> options;
if (input_options->IsUndefined(isolate)) {
options = factory->NewJSObjectWithNullProto();
} else {
Handle<JSReceiver> options_obj;
ASSIGN_RETURN_ON_EXCEPTION(isolate, options_obj,
Object::ToObject(isolate, input_options),
JSObject);
// 2. Let options be ObjectCreate(options).
ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
JSObject::ObjectCreate(isolate, options_obj),
JSObject);
}
// 3. Let needDefaults be true.
bool needs_default = true;
bool required_is_any = strcmp(required, "any") == 0;
// 4. If required is "date" or "any", then
if (required_is_any || (strcmp(required, "date") == 0)) {
// a. For each of the property names "weekday", "year", "month", "day", do
for (auto& prop : {"weekday", "year", "month", "day"}) {
// i. Let prop be the property name.
// ii. Let value be ? Get(options, prop)
Maybe<bool> maybe_undefined = IsPropertyUndefined(isolate, options, prop);
MAYBE_RETURN(maybe_undefined, Handle<JSObject>());
// iii. If value is not undefined, let needDefaults be false.
if (!maybe_undefined.FromJust()) {
needs_default = false;
}
}
}
// 5. If required is "time" or "any", then
if (required_is_any || (strcmp(required, "time") == 0)) {
// a. For each of the property names "hour", "minute", "second", do
for (auto& prop : {"hour", "minute", "second"}) {
// i. Let prop be the property name.
// ii. Let value be ? Get(options, prop)
Maybe<bool> maybe_undefined = IsPropertyUndefined(isolate, options, prop);
MAYBE_RETURN(maybe_undefined, Handle<JSObject>());
// iii. If value is not undefined, let needDefaults be false.
if (!maybe_undefined.FromJust()) {
needs_default = false;
}
}
}
// 6. If needDefaults is true and defaults is either "date" or "all", then
if (needs_default) {
bool default_is_all = strcmp(defaults, "all") == 0;
if (default_is_all || (strcmp(defaults, "date") == 0)) {
// a. For each of the property names "year", "month", "day", do
// i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric").
for (auto& prop : {"year", "month", "day"}) {
MAYBE_RETURN(
JSReceiver::CreateDataProperty(
isolate, options, factory->NewStringFromAsciiChecked(prop),
factory->numeric_string(), kThrowOnError),
Handle<JSObject>());
}
}
// 7. If needDefaults is true and defaults is either "time" or "all", then
if (default_is_all || (strcmp(defaults, "time") == 0)) {
// a. For each of the property names "hour", "minute", "second", do
// i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric").
for (auto& prop : {"hour", "minute", "second"}) {
MAYBE_RETURN(
JSReceiver::CreateDataProperty(
isolate, options, factory->NewStringFromAsciiChecked(prop),
factory->numeric_string(), kThrowOnError),
Handle<JSObject>());
}
}
}
// 8. Return options.
return options;
}
std::set<std::string> Intl::GetAvailableLocales(const IcuService& service) {
const icu::Locale* icu_available_locales = nullptr;
int32_t count = 0;
......@@ -1734,28 +1893,6 @@ bool IsAToZ(char ch) {
return IsInRange(AsciiAlphaToLower(ch), 'a', 'z');
}
// The following are temporary function calling back into js code in
// src/js/intl.js to call pre-existing functions until they are all moved to C++
// under src/objects/*.
// TODO(ftang): remove these temp function after bstell move them from js into
// C++
MaybeHandle<JSObject> CachedOrNewService(Isolate* isolate,
Handle<String> service,
Handle<Object> locales,
Handle<Object> options) {
Handle<Object> result;
Handle<Object> undefined_value(ReadOnlyRoots(isolate).undefined_value(),
isolate);
Handle<Object> args[] = {service, locales, options};
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
Execution::Call(isolate, isolate->cached_or_new_service(),
undefined_value, arraysize(args), args),
JSArray);
return Handle<JSObject>::cast(result);
}
} // namespace
// Verifies that the input is a well-formed ISO 4217 currency code.
......@@ -1830,7 +1967,7 @@ MaybeHandle<Object> Intl::StringLocaleCompare(Isolate* isolate,
ASSIGN_RETURN_ON_EXCEPTION(
isolate, collator,
CachedOrNewService(isolate, factory->NewStringFromStaticChars("collator"),
locales, options),
locales, options, factory->undefined_value()),
Object);
CHECK(collator->IsJSCollator());
return Intl::CompareStrings(isolate, Handle<JSCollator>::cast(collator),
......@@ -1882,7 +2019,7 @@ MaybeHandle<String> Intl::NumberToLocaleString(Isolate* isolate,
isolate, number_format_holder,
CachedOrNewService(isolate,
factory->NewStringFromStaticChars("numberformat"),
locales, options),
locales, options, factory->undefined_value()),
String);
DCHECK(
Intl::IsObjectOfType(isolate, number_format_holder, Intl::kNumberFormat));
......
......@@ -60,6 +60,16 @@ class DateFormat {
Isolate* isolate, Handle<JSObject> date_time_format_holder,
Handle<Object> date);
// ecma-402/#sec-todatetimeoptions
V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> ToDateTimeOptions(
Isolate* isolate, Handle<Object> input_options, const char* required,
const char* defaults);
V8_WARN_UNUSED_RESULT static MaybeHandle<String> ToLocaleDateTime(
Isolate* isolate, Handle<Object> date, Handle<Object> locales,
Handle<Object> options, const char* required, const char* defaults,
const char* service);
// Layout description.
static const int kSimpleDateFormat = JSObject::kHeaderSize;
static const int kSize = kSimpleDateFormat + kPointerSize;
......
......@@ -486,6 +486,36 @@ RUNTIME_FUNCTION(Runtime_BreakIteratorBreakType) {
}
}
RUNTIME_FUNCTION(Runtime_ToLocaleDateTime) {
HandleScope scope(isolate);
DCHECK_EQ(6, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, date, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, locales, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, options, 2);
CONVERT_ARG_HANDLE_CHECKED(String, required, 3);
CONVERT_ARG_HANDLE_CHECKED(String, defaults, 4);
CONVERT_ARG_HANDLE_CHECKED(String, service, 5);
RETURN_RESULT_OR_FAILURE(
isolate, DateFormat::ToLocaleDateTime(
isolate, date, locales, options, required->ToCString().get(),
defaults->ToCString().get(), service->ToCString().get()));
}
RUNTIME_FUNCTION(Runtime_ToDateTimeOptions) {
HandleScope scope(isolate);
DCHECK_EQ(args.length(), 3);
CONVERT_ARG_HANDLE_CHECKED(Object, options, 0);
CONVERT_ARG_HANDLE_CHECKED(String, required, 1);
CONVERT_ARG_HANDLE_CHECKED(String, defaults, 2);
RETURN_RESULT_OR_FAILURE(
isolate, DateFormat::ToDateTimeOptions(isolate, options,
required->ToCString().get(),
defaults->ToCString().get()));
}
RUNTIME_FUNCTION(Runtime_StringToLowerCaseIntl) {
HandleScope scope(isolate);
DCHECK_EQ(args.length(), 1);
......
......@@ -405,34 +405,26 @@ RUNTIME_FUNCTION(Runtime_AddDictionaryProperty) {
RUNTIME_FUNCTION(Runtime_ObjectCreate) {
HandleScope scope(isolate);
Handle<Object> prototype = args.at(0);
Handle<Object> properties = args.at(1);
Handle<JSObject> obj;
// 1. If Type(O) is neither Object nor Null, throw a TypeError exception.
if (!prototype->IsNull(isolate) && !prototype->IsJSReceiver()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kProtoObjectOrNull, prototype));
}
// 2. Let obj be ObjectCreate(O).
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, obj, JSObject::ObjectCreate(isolate, prototype));
// Generate the map with the specified {prototype} based on the Object
// function's initial map from the current native context.
// TODO(bmeurer): Use a dedicated cache for Object.create; think about
// slack tracking for Object.create.
Handle<Map> map =
Map::GetObjectCreateMap(isolate, Handle<HeapObject>::cast(prototype));
// Actually allocate the object.
Handle<JSObject> object;
if (map->is_dictionary_map()) {
object = isolate->factory()->NewSlowJSObjectFromMap(map);
} else {
object = isolate->factory()->NewJSObjectFromMap(map);
}
// Define the properties if properties was specified and is not undefined.
Handle<Object> properties = args.at(1);
// 3. If Properties is not undefined, then
if (!properties->IsUndefined(isolate)) {
RETURN_FAILURE_ON_EXCEPTION(
isolate, JSReceiver::DefineProperties(isolate, object, properties));
// a. Return ? ObjectDefineProperties(obj, Properties).
// Define the properties if properties was specified and is not undefined.
RETURN_RESULT_OR_FAILURE(
isolate, JSReceiver::DefineProperties(isolate, obj, properties));
}
return *object;
// 4. Return obj.
return *obj;
}
MaybeHandle<Object> Runtime::SetObjectProperty(Isolate* isolate,
......
......@@ -229,6 +229,8 @@ namespace internal {
F(ParseExtension, 1, 1) \
F(PluralRulesResolvedOptions, 1, 1) \
F(PluralRulesSelect, 2, 1) \
F(ToDateTimeOptions, 3, 1) \
F(ToLocaleDateTime, 6, 1) \
F(StringToLowerCaseIntl, 1, 1) \
F(StringToUpperCaseIntl, 1, 1) \
F(SupportedLocalesOf, 3, 1) \
......
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