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

[Intl] mv code to objects/js-date-time-format.*

Bug: v8:5751
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng;luci.chromium.try:linux_chromium_rel_ng
Change-Id: Idf89e9d79e9b063c7d01e2b133826b9127910f4e
Reviewed-on: https://chromium-review.googlesource.com/1205835
Commit-Queue: Frank Tang <ftang@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55785}
parent 0d1b00b8
......@@ -30,6 +30,7 @@
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-break-iterator.h"
#include "src/objects/js-collator.h"
#include "src/objects/js-date-time-format.h"
#include "src/objects/js-list-format.h"
#include "src/objects/js-locale.h"
#include "src/objects/js-number-format.h"
......@@ -2901,8 +2902,15 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
{
Handle<JSFunction> date_time_format_constructor = InstallFunction(
isolate_, intl, "DateTimeFormat", JS_OBJECT_TYPE, DateFormat::kSize,
0, factory->the_hole_value(), Builtins::kIllegal);
isolate_, intl, "DateTimeFormat", JS_INTL_DATE_TIME_FORMAT_TYPE,
JSDateTimeFormat::kSize, 0, factory->the_hole_value(),
Builtins::kDateTimeFormatConstructor);
date_time_format_constructor->shared()->set_length(0);
date_time_format_constructor->shared()->DontAdaptArguments();
InstallWithIntrinsicDefaultProto(
isolate_, date_time_format_constructor,
Context::INTL_DATE_TIME_FORMAT_FUNCTION_INDEX);
native_context()->set_intl_date_time_format_function(
*date_time_format_constructor);
......
......@@ -844,45 +844,45 @@ BUILTIN(DatePrototypeToTimeString) {
BUILTIN(DatePrototypeToLocaleDateString) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSDate, date, "Date.prototype.toLocaleDateString");
RETURN_RESULT_OR_FAILURE(isolate,
JSDateTimeFormat::ToLocaleDateTime(
isolate,
date, // date
args.atOrUndefined(isolate, 1), // locales
args.atOrUndefined(isolate, 2), // options
"date", // required
"date", // defaults
"dateformatdate")); // service
RETURN_RESULT_OR_FAILURE(
isolate, JSDateTimeFormat::ToLocaleDateTime(
isolate,
date, // date
args.atOrUndefined(isolate, 1), // locales
args.atOrUndefined(isolate, 2), // options
JSDateTimeFormat::RequiredOption::kDate, // required
JSDateTimeFormat::DefaultsOption::kDate, // defaults
"dateformatdate")); // service
}
// ecma402 #sup-date.prototype.tolocalestring
BUILTIN(DatePrototypeToLocaleString) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSDate, date, "Date.prototype.toLocaleString");
RETURN_RESULT_OR_FAILURE(isolate,
JSDateTimeFormat::ToLocaleDateTime(
isolate,
date, // date
args.atOrUndefined(isolate, 1), // locales
args.atOrUndefined(isolate, 2), // options
"any", // required
"all", // defaults
"dateformatall")); // service
RETURN_RESULT_OR_FAILURE(
isolate, JSDateTimeFormat::ToLocaleDateTime(
isolate,
date, // date
args.atOrUndefined(isolate, 1), // locales
args.atOrUndefined(isolate, 2), // options
JSDateTimeFormat::RequiredOption::kAny, // required
JSDateTimeFormat::DefaultsOption::kAll, // defaults
"dateformatall")); // service
}
// ecma402 #sup-date.prototype.tolocaletimestring
BUILTIN(DatePrototypeToLocaleTimeString) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSDate, date, "Date.prototype.toLocaleTimeString");
RETURN_RESULT_OR_FAILURE(isolate,
JSDateTimeFormat::ToLocaleDateTime(
isolate,
date, // date
args.atOrUndefined(isolate, 1), // locales
args.atOrUndefined(isolate, 2), // options
"time", // required
"time", // defaults
"dateformattime")); // service
RETURN_RESULT_OR_FAILURE(
isolate, JSDateTimeFormat::ToLocaleDateTime(
isolate,
date, // date
args.atOrUndefined(isolate, 1), // locales
args.atOrUndefined(isolate, 2), // options
JSDateTimeFormat::RequiredOption::kTime, // required
JSDateTimeFormat::DefaultsOption::kTime, // defaults
"dateformattime")); // service
}
#endif // V8_INTL_SUPPORT
......
......@@ -1345,6 +1345,8 @@ namespace internal {
CPP(DatePrototypeToLocaleString) \
/* ecma402 #sup-date.prototype.tolocaletimestring */ \
CPP(DatePrototypeToLocaleTimeString) \
/* ecma402 #sec-intl.datetimeformat */ \
CPP(DateTimeFormatConstructor) \
/* ecma402 #sec-datetime-format-functions */ \
CPP(DateTimeFormatInternalFormat) \
/* ecma402 #sec-intl.datetimeformat.prototype.format */ \
......
......@@ -131,110 +131,6 @@ BUILTIN(StringPrototypeNormalizeIntl) {
namespace {
// The list comes from third_party/icu/source/i18n/unicode/udat.h.
// They're mapped to DateTimeFormat components listed at
// https://tc39.github.io/ecma402/#sec-datetimeformat-abstracts .
Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) {
switch (field_id) {
case -1:
return isolate->factory()->literal_string();
case UDAT_YEAR_FIELD:
case UDAT_EXTENDED_YEAR_FIELD:
case UDAT_YEAR_NAME_FIELD:
return isolate->factory()->year_string();
case UDAT_MONTH_FIELD:
case UDAT_STANDALONE_MONTH_FIELD:
return isolate->factory()->month_string();
case UDAT_DATE_FIELD:
return isolate->factory()->day_string();
case UDAT_HOUR_OF_DAY1_FIELD:
case UDAT_HOUR_OF_DAY0_FIELD:
case UDAT_HOUR1_FIELD:
case UDAT_HOUR0_FIELD:
return isolate->factory()->hour_string();
case UDAT_MINUTE_FIELD:
return isolate->factory()->minute_string();
case UDAT_SECOND_FIELD:
return isolate->factory()->second_string();
case UDAT_DAY_OF_WEEK_FIELD:
case UDAT_DOW_LOCAL_FIELD:
case UDAT_STANDALONE_DAY_FIELD:
return isolate->factory()->weekday_string();
case UDAT_AM_PM_FIELD:
return isolate->factory()->dayperiod_string();
case UDAT_TIMEZONE_FIELD:
case UDAT_TIMEZONE_RFC_FIELD:
case UDAT_TIMEZONE_GENERIC_FIELD:
case UDAT_TIMEZONE_SPECIAL_FIELD:
case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
case UDAT_TIMEZONE_ISO_FIELD:
case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
return isolate->factory()->timeZoneName_string();
case UDAT_ERA_FIELD:
return isolate->factory()->era_string();
default:
// Other UDAT_*_FIELD's cannot show up because there is no way to specify
// them via options of Intl.DateTimeFormat.
UNREACHABLE();
// To prevent MSVC from issuing C4715 warning.
return Handle<String>();
}
}
MaybeHandle<Object> FormatDateToParts(Isolate* isolate, icu::DateFormat* format,
double date_value) {
Factory* factory = isolate->factory();
icu::UnicodeString formatted;
icu::FieldPositionIterator fp_iter;
icu::FieldPosition fp;
UErrorCode status = U_ZERO_ERROR;
format->format(date_value, formatted, &fp_iter, status);
if (U_FAILURE(status)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object);
}
Handle<JSArray> result = factory->NewJSArray(0);
int32_t length = formatted.length();
if (length == 0) return result;
int index = 0;
int32_t previous_end_pos = 0;
Handle<String> substring;
while (fp_iter.next(fp)) {
int32_t begin_pos = fp.getBeginIndex();
int32_t end_pos = fp.getEndIndex();
if (previous_end_pos < begin_pos) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring,
Intl::ToString(isolate, formatted, previous_end_pos, begin_pos),
Object);
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(-1, isolate), substring);
++index;
}
ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring,
Intl::ToString(isolate, formatted, begin_pos, end_pos), Object);
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(fp.getField(), isolate),
substring);
previous_end_pos = end_pos;
++index;
}
if (previous_end_pos < length) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring,
Intl::ToString(isolate, formatted, previous_end_pos, length), Object);
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(-1, isolate), substring);
}
JSObject::ValidateElements(*result);
return result;
}
MaybeHandle<JSObject> SupportedLocalesOfCommon(Isolate* isolate,
const char* service_in,
BuiltinArguments args) {
......@@ -302,13 +198,14 @@ BUILTIN(DateTimeFormatPrototypeFormatToParts) {
CHECK_RECEIVER(JSObject, date_format_holder, method);
Factory* factory = isolate->factory();
if (!Intl::IsObjectOfType(isolate, date_format_holder,
Intl::Type::kDateTimeFormat)) {
if (!date_format_holder->IsJSDateTimeFormat()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
factory->NewStringFromAsciiChecked(method),
date_format_holder));
}
Handle<JSDateTimeFormat> dtf =
Handle<JSDateTimeFormat>::cast(date_format_holder);
Handle<Object> x = args.atOrUndefined(isolate, 1);
if (x->IsUndefined(isolate)) {
......@@ -324,12 +221,8 @@ BUILTIN(DateTimeFormatPrototypeFormatToParts) {
isolate, NewRangeError(MessageTemplate::kInvalidTimeValue));
}
icu::SimpleDateFormat* date_format =
DateFormat::UnpackDateFormat(date_format_holder);
CHECK_NOT_NULL(date_format);
RETURN_RESULT_OR_FAILURE(isolate,
FormatDateToParts(isolate, date_format, date_value));
RETURN_RESULT_OR_FAILURE(
isolate, JSDateTimeFormat::FormatToParts(isolate, dtf, date_value));
}
namespace {
......@@ -357,9 +250,14 @@ Handle<JSFunction> CreateBoundFunction(Isolate* isolate,
isolate->factory()->NewFunctionFromSharedFunctionInfo(map, info, context);
return new_bound_function;
}
} // namespace
BUILTIN(NumberFormatConstructor) {
HandleScope scope(isolate);
/**
* Common code shared between DateTimeFormatConstructor and
* NumberFormatConstrutor
*/
template <class T>
Object* FormatConstructor(BuiltinArguments args, Isolate* isolate,
Handle<Object> constructor, const char* method) {
Handle<JSReceiver> new_target;
// 1. If NewTarget is undefined, let newTarget be the active
// function object, else let newTarget be NewTarget.
......@@ -375,41 +273,29 @@ BUILTIN(NumberFormatConstructor) {
Handle<Object> locales = args.atOrUndefined(isolate, 1);
Handle<Object> options = args.atOrUndefined(isolate, 2);
// 2. Let numberFormat be ? OrdinaryCreateFromConstructor(newTarget,
// "%NumberFormatPrototype%", « [[InitializedNumberFormat]], [[Locale]],
// [[NumberingSystem]], [[Style]], [[Currency]], [[CurrencyDisplay]],
// [[MinimumIntegerDigits]], [[MinimumFractionDigits]],
// [[MaximumFractionDigits]], [[MinimumSignificantDigits]],
// [[MaximumSignificantDigits]], [[UseGrouping]], [[PositivePattern]],
// [[NegativePattern]], [[BoundFormat]] »).
// 2. Let format be ? OrdinaryCreateFromConstructor(newTarget,
// "%<T>Prototype%", ...).
Handle<JSObject> number_format_obj;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_format_obj,
Handle<JSObject> format_obj;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, format_obj,
JSObject::New(target, new_target));
Handle<JSNumberFormat> number_format =
Handle<JSNumberFormat>::cast(number_format_obj);
number_format->set_flags(0);
Handle<T> format = Handle<T>::cast(format_obj);
// 3. Perform ? InitializeNumberFormat(numberFormat, locales, options).
// 3. Perform ? Initialize<T>(Format, locales, options).
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, number_format,
JSNumberFormat::InitializeNumberFormat(isolate, number_format, locales,
options));
isolate, format, T::Initialize(isolate, format, locales, options));
// 4. Let this be the this value.
Handle<Object> receiver = args.receiver();
// 5. If NewTarget is undefined and ? InstanceofOperator(this, %NumberFormat%)
// 5. If NewTarget is undefined and ? InstanceofOperator(this, %<T>%)
// is true, then
//
// Look up the intrinsic value that has been stored on the context.
Handle<Object> number_format_constructor =
isolate->intl_number_format_function();
// Call the instanceof function
Handle<Object> is_instance_of_obj;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, is_instance_of_obj,
Object::InstanceOf(isolate, receiver, number_format_constructor));
Object::InstanceOf(isolate, receiver, constructor));
// Get the boolean value of the result
bool is_instance_of = is_instance_of_obj->BooleanValue(isolate);
......@@ -417,17 +303,17 @@ BUILTIN(NumberFormatConstructor) {
if (args.new_target()->IsUndefined(isolate) && is_instance_of) {
if (!receiver->IsJSReceiver()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
isolate->factory()->NewStringFromStaticChars(
"Intl.NumberFormat"),
receiver));
isolate,
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
isolate->factory()->NewStringFromAsciiChecked(method),
receiver));
}
Handle<JSReceiver> rec = Handle<JSReceiver>::cast(receiver);
// a. Perform ? DefinePropertyOrThrow(this,
// %Intl%.[[FallbackSymbol]], PropertyDescriptor{ [[Value]]: numberFormat,
// %Intl%.[[FallbackSymbol]], PropertyDescriptor{ [[Value]]: format,
// [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }).
PropertyDescriptor desc;
desc.set_value(number_format);
desc.set_value(format);
desc.set_writable(false);
desc.set_enumerable(false);
desc.set_configurable(false);
......@@ -439,9 +325,17 @@ BUILTIN(NumberFormatConstructor) {
// b. b. Return this.
return *receiver;
}
// 6. Return format.
return *format;
}
// 6. Return numberFormat.
return *number_format;
} // namespace
BUILTIN(NumberFormatConstructor) {
HandleScope scope(isolate);
return FormatConstructor<JSNumberFormat>(
args, isolate, isolate->intl_number_format_function(),
"Intl.NumberFormat");
}
BUILTIN(NumberFormatPrototypeFormatNumber) {
......@@ -508,6 +402,13 @@ BUILTIN(NumberFormatInternalFormatNumber) {
isolate, JSNumberFormat::FormatNumber(isolate, number_format, number));
}
BUILTIN(DateTimeFormatConstructor) {
HandleScope scope(isolate);
return FormatConstructor<JSDateTimeFormat>(
args, isolate, isolate->intl_date_time_format_function(),
"Intl.DateTimeFormat");
}
BUILTIN(DateTimeFormatPrototypeFormat) {
const char* const method = "get Intl.DateTimeFormat.prototype.format";
HandleScope scope(isolate);
......@@ -517,16 +418,12 @@ BUILTIN(DateTimeFormatPrototypeFormat) {
CHECK_RECEIVER(JSReceiver, receiver, method);
// 3. Let dtf be ? UnwrapDateTimeFormat(dtf).
Handle<JSObject> date_format_holder;
Handle<JSDateTimeFormat> format;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, date_format_holder,
JSDateTimeFormat::Unwrap(isolate, receiver, method));
DCHECK(Intl::IsObjectOfType(isolate, date_format_holder,
Intl::Type::kDateTimeFormat));
isolate, format,
JSDateTimeFormat::UnwrapDateTimeFormat(isolate, receiver));
Handle<Object> bound_format = Handle<Object>(
date_format_holder->GetEmbedderField(DateFormat::kBoundFormatIndex),
isolate);
Handle<Object> bound_format = Handle<Object>(format->bound_format(), isolate);
// 4. If dtf.[[BoundFormat]] is undefined, then
if (!bound_format->IsUndefined(isolate)) {
......@@ -536,11 +433,10 @@ BUILTIN(DateTimeFormatPrototypeFormat) {
}
Handle<JSFunction> new_bound_format_function = CreateBoundFunction(
isolate, date_format_holder, Builtins::kDateTimeFormatInternalFormat, 1);
isolate, format, Builtins::kDateTimeFormatInternalFormat, 1);
// 4.c. Set dtf.[[BoundFormat]] to F.
date_format_holder->SetEmbedderField(DateFormat::kBoundFormatIndex,
*new_bound_format_function);
format->set_bound_format(*new_bound_format_function);
// 5. Return dtf.[[BoundFormat]].
return *new_bound_format_function;
......@@ -551,15 +447,12 @@ BUILTIN(DateTimeFormatInternalFormat) {
Handle<Context> context = Handle<Context>(isolate->context(), isolate);
// 1. Let dtf be F.[[DateTimeFormat]].
Handle<JSObject> date_format_holder = Handle<JSObject>(
JSObject::cast(context->get(
static_cast<int>(Intl::BoundFunctionContextSlot::kBoundFunction))),
isolate);
// 2. Assert: Type(dtf) is Object and dtf has an [[InitializedDateTimeFormat]]
// internal slot.
DCHECK(Intl::IsObjectOfType(isolate, date_format_holder,
Intl::Type::kDateTimeFormat));
Handle<JSDateTimeFormat> date_format_holder = Handle<JSDateTimeFormat>(
JSDateTimeFormat::cast(context->get(
static_cast<int>(Intl::BoundFunctionContextSlot::kBoundFunction))),
isolate);
Handle<Object> date = args.atOrUndefined(isolate, 1);
......
......@@ -43,35 +43,6 @@ utils.Import(function(from) {
ArrayPush = from.ArrayPush;
});
// Utilities for definitions
macro NUMBER_IS_NAN(arg)
(%IS_VAR(arg) !== arg)
endmacro
// To avoid ES2015 Function name inference.
macro ANONYMOUS_FUNCTION(fn)
(0, (fn))
endmacro
function IntlConstruct(receiver, constructor, create, newTarget, args,
compat) {
var locales = args[0];
var options = args[1];
var instance = create(locales, options);
if (compat && IS_UNDEFINED(newTarget) && receiver instanceof constructor) {
%object_define_property(receiver, IntlFallbackSymbol, { value: instance });
return receiver;
}
return instance;
}
// -------------------------------------------------------------------
/**
......@@ -299,69 +270,6 @@ function bestFitMatcher(service, requestedLocales) {
return lookupMatcher(service, requestedLocales);
}
/**
* Populates internalOptions object with boolean key-value pairs
* from extensionMap and options.
* Returns filtered extension (number and date format constructors use
* Unicode extensions for passing parameters to ICU).
* It's used for extension-option pairs only, e.g. kn-normalization, but not
* for 'sensitivity' since it doesn't have extension equivalent.
* Extensions like nu and ca don't have options equivalent, so we place
* undefined in the map.property to denote that.
*/
function setOptions(inOptions, extensionMap, keyValues, getOption, outOptions) {
var extension = '';
var updateExtension = function updateExtension(key, value) {
return '-' + key + '-' + TO_STRING(value);
}
var updateProperty = function updateProperty(property, type, value) {
if (type === 'boolean' && (typeof value === 'string')) {
value = (value === 'true') ? true : false;
}
if (!IS_UNDEFINED(property)) {
%DefineWEProperty(outOptions, property, value);
}
}
for (var key in keyValues) {
if (HAS_OWN_PROPERTY(keyValues, key)) {
var value = UNDEFINED;
var map = keyValues[key];
if (!IS_UNDEFINED(map.property)) {
// This may return true if user specifies numeric: 'false', since
// Boolean('nonempty') === true.
value = getOption(map.property, map.type, map.values);
}
if (!IS_UNDEFINED(value)) {
updateProperty(map.property, map.type, value);
extension += updateExtension(key, value);
continue;
}
// User options didn't have it, check Unicode extension.
// Here we want to convert strings 'true', 'false' into proper Boolean
// values (not a user error).
if (HAS_OWN_PROPERTY(extensionMap, key)) {
value = extensionMap[key];
if (!IS_UNDEFINED(value)) {
updateProperty(map.property, map.type, value);
extension += updateExtension(key, value);
} else if (map.type === 'boolean') {
// Boolean keys are allowed not to have values in Unicode extension.
// Those default to true.
updateProperty(map.property, map.type, true);
extension += updateExtension(key, true);
}
}
}
}
return extension === ''? '' : '-u' + extension;
}
/**
* Given an array-like, outputs an Array with the numbered
* properties copied over and defined
......@@ -422,16 +330,6 @@ function getAvailableLocalesOf(service) {
return available;
}
/**
* Defines a property and sets writable, enumerable and configurable to true.
*/
function defineWECProperty(object, property, value) {
%object_define_property(object, property, {value: value,
writable: true,
enumerable: true,
configurable: true});
}
/**
* Returns an InternalArray where all locales are canonicalized and duplicates
* removed.
......@@ -503,73 +401,6 @@ DEFINE_METHOD(
}
);
/**
* Initializes the given object so it's a valid DateTimeFormat instance.
* Useful for subclassing.
*/
function CreateDateTimeFormat(locales, options) {
if (IS_UNDEFINED(options)) {
options = {__proto__: null};
}
var locale = resolveLocale('dateformat', locales, options);
options = %ToDateTimeOptions(options, 'any', 'date');
var getOption = getGetOption(options, 'dateformat');
// We implement only best fit algorithm, but still need to check
// if the formatMatcher values are in range.
var matcher = getOption('formatMatcher', 'string',
['basic', 'best fit'], 'best fit');
// ICU prefers options to be passed using -u- extension key/values, so
// we need to build that.
var internalOptions = {__proto__: null};
var extensionMap = %ParseExtension(locale.extension);
/**
* Map of Unicode extensions to option properties, and their values and types,
* for a date/time format.
*/
var DATETIME_FORMAT_KEY_MAP = {
__proto__: null,
'ca': {__proto__: null, 'property': UNDEFINED, 'type': 'string'},
'nu': {__proto__: null, 'property': UNDEFINED, 'type': 'string'}
};
var extension = setOptions(options, extensionMap, DATETIME_FORMAT_KEY_MAP,
getOption, internalOptions);
var requestedLocale = locale.locale + extension;
// Still need to store locale and numberingSystem till we move the storage
// to JSDateTimeFormat
var resolved = {__proto__: null};
var dateFormat = %CreateDateTimeFormat(requestedLocale, options, resolved);
%MarkAsInitializedIntlObjectOfType(dateFormat, DATE_TIME_FORMAT_TYPE);
// Still need to store locale and numberingSystem till we move the storage
// to JSDateTimeFormat
dateFormat[resolvedSymbol] = resolved;
return dateFormat;
}
/**
* Constructs Intl.DateTimeFormat object given optional locales and options
* parameters.
*
* @constructor
*/
function DateTimeFormatConstructor() {
return IntlConstruct(this, GlobalIntlDateTimeFormat, CreateDateTimeFormat,
new.target, arguments, true);
}
%SetCode(GlobalIntlDateTimeFormat, DateTimeFormatConstructor);
/**
* DateTimeFormat resolvedOptions method.
*/
......
......@@ -1910,6 +1910,10 @@ void JSCollator::JSCollatorVerify(Isolate* isolate) {
void JSDateTimeFormat::JSDateTimeFormatVerify(Isolate* isolate) {
JSObjectVerify(isolate);
VerifyObjectField(isolate, kLocaleOffset);
VerifyObjectField(isolate, kNumberingSystemOffset);
VerifyObjectField(isolate, kICUSimpleDateFormatOffset);
VerifyObjectField(isolate, kBoundFormatOffset);
}
void JSListFormat::JSListFormatVerify(Isolate* isolate) {
......
......@@ -1991,6 +1991,11 @@ void JSCollator::JSCollatorPrint(std::ostream& os) { // NOLINT
void JSDateTimeFormat::JSDateTimeFormatPrint(std::ostream& os) { // NOLINT
JSObjectPrintHeader(os, this, "JSDateTimeFormat");
os << "\n - locale: " << Brief(locale());
os << "\n - numbering system: " << Brief(numbering_system());
os << "\n - icu simple date format: " << Brief(icu_simple_date_format());
os << "\n - bound format: " << Brief(bound_format());
os << "\n";
}
void JSListFormat::JSListFormatPrint(std::ostream& os) { // NOLINT
......
......@@ -26,11 +26,8 @@
#include "src/objects/string.h"
#include "src/property-descriptor.h"
#include "unicode/brkiter.h"
#include "unicode/calendar.h"
#include "unicode/coll.h"
#include "unicode/decimfmt.h"
#include "unicode/dtptngen.h"
#include "unicode/gregocal.h"
#include "unicode/locid.h"
#include "unicode/numfmt.h"
#include "unicode/numsys.h"
......@@ -63,138 +60,6 @@ std::string Intl::GetNumberingSystem(const icu::Locale& icu_locale) {
return value;
}
namespace {
// ecma-402/#sec-isvalidtimezonename
bool IsValidTimeZoneName(const icu::TimeZone& tz) {
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString id;
tz.getID(id);
icu::UnicodeString canonical;
icu::TimeZone::getCanonicalID(id, canonical, status);
return U_SUCCESS(status) &&
canonical != icu::UnicodeString("Etc/Unknown", -1, US_INV);
}
std::unique_ptr<icu::TimeZone> CreateTimeZone(Isolate* isolate,
const char* timezone) {
// Create time zone as specified by the user. We have to re-create time zone
// since calendar takes ownership.
if (timezone == nullptr) {
return std::unique_ptr<icu::TimeZone>(icu::TimeZone::createDefault());
}
std::string canonicalized =
JSDateTimeFormat::CanonicalizeTimeZoneID(isolate, timezone);
if (canonicalized.empty()) return std::unique_ptr<icu::TimeZone>();
std::unique_ptr<icu::TimeZone> tz(
icu::TimeZone::createTimeZone(canonicalized.c_str()));
if (!IsValidTimeZoneName(*tz)) return std::unique_ptr<icu::TimeZone>();
return tz;
}
std::unique_ptr<icu::Calendar> CreateCalendar(Isolate* isolate,
const icu::Locale& icu_locale,
const char* timezone) {
std::unique_ptr<icu::TimeZone> tz = CreateTimeZone(isolate, timezone);
if (tz.get() == nullptr) return std::unique_ptr<icu::Calendar>();
// Create a calendar using locale, and apply time zone to it.
UErrorCode status = U_ZERO_ERROR;
std::unique_ptr<icu::Calendar> calendar(
icu::Calendar::createInstance(tz.release(), icu_locale, status));
CHECK(U_SUCCESS(status));
CHECK_NOT_NULL(calendar.get());
if (calendar->getDynamicClassID() ==
icu::GregorianCalendar::getStaticClassID()) {
icu::GregorianCalendar* gc =
static_cast<icu::GregorianCalendar*>(calendar.get());
UErrorCode status = U_ZERO_ERROR;
// The beginning of ECMAScript time, namely -(2**53)
const double start_of_time = -9007199254740992;
gc->setGregorianChange(start_of_time, status);
DCHECK(U_SUCCESS(status));
}
return calendar;
}
std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormat(
Isolate* isolate, const icu::Locale& icu_locale,
const std::string skeleton) {
// See https://github.com/tc39/ecma402/issues/225 . The best pattern
// generation needs to be done in the base locale according to the
// current spec however odd it may be. See also crbug.com/826549 .
// This is a temporary work-around to get v8's external behavior to match
// the current spec, but does not follow the spec provisions mentioned
// in the above Ecma 402 issue.
// TODO(jshin): The spec may need to be revised because using the base
// locale for the pattern match is not quite right. Moreover, what to
// do with 'related year' part when 'chinese/dangi' calendar is specified
// has to be discussed. Revisit once the spec is clarified/revised.
icu::Locale no_extension_locale(icu_locale.getBaseName());
UErrorCode status = U_ZERO_ERROR;
std::unique_ptr<icu::DateTimePatternGenerator> generator(
icu::DateTimePatternGenerator::createInstance(no_extension_locale,
status));
icu::UnicodeString pattern;
if (U_SUCCESS(status)) {
pattern =
generator->getBestPattern(icu::UnicodeString(skeleton.c_str()), status);
}
// Make formatter from skeleton. Calendar and numbering system are added
// to the locale as Unicode extension (if they were specified at all).
std::unique_ptr<icu::SimpleDateFormat> date_format(
new icu::SimpleDateFormat(pattern, icu_locale, status));
if (U_FAILURE(status)) return std::unique_ptr<icu::SimpleDateFormat>();
CHECK_NOT_NULL(date_format.get());
return date_format;
}
void SetResolvedDateSettings(Isolate* isolate, const icu::Locale& icu_locale,
icu::SimpleDateFormat* date_format,
Handle<JSObject> resolved) {
Factory* factory = isolate->factory();
UErrorCode status = U_ZERO_ERROR;
// Ugly hack. ICU doesn't expose numbering system in any way, so we have
// to assume that for given locale NumberingSystem constructor produces the
// same digits as NumberFormat/Calendar would.
icu::NumberingSystem* numbering_system =
icu::NumberingSystem::createInstance(icu_locale, status);
if (U_SUCCESS(status)) {
const char* ns = numbering_system->getName();
JSObject::SetProperty(
isolate, resolved, factory->NewStringFromStaticChars("numberingSystem"),
factory->NewStringFromAsciiChecked(ns), LanguageMode::kSloppy)
.Assert();
} else {
JSObject::SetProperty(isolate, resolved,
factory->NewStringFromStaticChars("numberingSystem"),
factory->undefined_value(), LanguageMode::kSloppy)
.Assert();
}
delete numbering_system;
// Set the locale
char result[ULOC_FULLNAME_CAPACITY];
status = U_ZERO_ERROR;
uloc_toLanguageTag(icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY,
FALSE, &status);
if (U_SUCCESS(status)) {
JSObject::SetProperty(
isolate, resolved, factory->NewStringFromStaticChars("locale"),
factory->NewStringFromAsciiChecked(result), LanguageMode::kSloppy)
.Assert();
} else {
// This would never happen, since we got the locale from ICU.
JSObject::SetProperty(
isolate, resolved, factory->NewStringFromStaticChars("locale"),
factory->NewStringFromStaticChars("und"), LanguageMode::kSloppy)
.Assert();
}
}
} // namespace
MaybeHandle<JSObject> Intl::CachedOrNewService(
Isolate* isolate, Handle<String> service, Handle<Object> locales,
Handle<Object> options, Handle<Object> internal_options) {
......@@ -240,59 +105,6 @@ icu::Locale Intl::CreateICULocale(Isolate* isolate,
}
// static
Maybe<icu::SimpleDateFormat*> DateFormat::InitializeDateTimeFormat(
Isolate* isolate, Handle<String> locale, Handle<JSObject> options,
Handle<JSObject> resolved) {
icu::Locale icu_locale = Intl::CreateICULocale(isolate, locale);
DCHECK(!icu_locale.isBogus());
static std::vector<const char*> empty_values = {};
std::unique_ptr<char[]> timezone = nullptr;
Maybe<bool> maybe_timezone =
Intl::GetStringOption(isolate, options, "timeZone", empty_values,
"Intl.DateTimeFormat", &timezone);
MAYBE_RETURN(maybe_timezone, Nothing<icu::SimpleDateFormat*>());
Maybe<std::string> maybe_skeleton =
JSDateTimeFormat::OptionsToSkeleton(isolate, options);
MAYBE_RETURN(maybe_skeleton, Nothing<icu::SimpleDateFormat*>());
CHECK(!maybe_skeleton.FromJust().empty());
std::string skeleton = maybe_skeleton.FromJust();
std::unique_ptr<icu::Calendar> calendar(
CreateCalendar(isolate, icu_locale, timezone.get()));
if (calendar.get() == nullptr) {
THROW_NEW_ERROR_RETURN_VALUE(
isolate,
NewRangeError(
MessageTemplate::kInvalidTimeZone,
isolate->factory()->NewStringFromAsciiChecked(timezone.get())),
Nothing<icu::SimpleDateFormat*>());
}
std::unique_ptr<icu::SimpleDateFormat> date_format(
CreateICUDateFormat(isolate, icu_locale, skeleton));
if (date_format.get() == nullptr) {
// Remove extensions and try again.
icu_locale = icu::Locale(icu_locale.getBaseName());
date_format = CreateICUDateFormat(isolate, icu_locale, skeleton);
if (date_format.get() == nullptr) {
FATAL("Failed to create ICU date format, are ICU data files missing?");
}
}
date_format->adoptCalendar(calendar.release());
SetResolvedDateSettings(isolate, icu_locale, date_format.get(), resolved);
return Just(date_format.release());
}
icu::SimpleDateFormat* DateFormat::UnpackDateFormat(Handle<JSObject> obj) {
return reinterpret_cast<icu::SimpleDateFormat*>(
obj->GetEmbedderField(DateFormat::kSimpleDateFormatIndex));
}
void DateFormat::DeleteDateFormat(const v8::WeakCallbackInfo<void>& data) {
delete reinterpret_cast<icu::SimpleDateFormat*>(data.GetInternalField(0));
GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter()));
}
MaybeHandle<String> Intl::ToString(Isolate* isolate,
const icu::UnicodeString& string) {
......@@ -564,77 +376,6 @@ MaybeHandle<Object> Intl::LegacyUnwrapReceiver(Isolate* isolate,
return receiver;
}
MaybeHandle<JSObject> Intl::UnwrapReceiver(Isolate* isolate,
Handle<JSReceiver> receiver,
Handle<JSFunction> constructor,
Intl::Type type,
Handle<String> method_name,
bool check_legacy_constructor) {
DCHECK(type == Intl::Type::kCollator || type == Intl::Type::kNumberFormat ||
type == Intl::Type::kDateTimeFormat ||
type == Intl::Type::kBreakIterator);
Handle<Object> new_receiver = receiver;
if (check_legacy_constructor) {
bool has_initialized_slot = Intl::IsObjectOfType(isolate, receiver, type);
ASSIGN_RETURN_ON_EXCEPTION(
isolate, new_receiver,
LegacyUnwrapReceiver(isolate, receiver, constructor,
has_initialized_slot),
JSObject);
}
// Collator and NumberFormat has been ported to use regular instance types. We
// shouldn't be using Intl::IsObjectOfType anymore.
if (type == Intl::Type::kCollator) {
if (!receiver->IsJSCollator()) {
// 3. a. Throw a TypeError exception.
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
method_name, receiver),
JSObject);
}
return Handle<JSCollator>::cast(receiver);
} else if (type == Intl::Type::kNumberFormat) {
if (!receiver->IsJSNumberFormat()) {
// 3. a. Throw a TypeError exception.
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
method_name, receiver),
JSObject);
}
return Handle<JSNumberFormat>::cast(receiver);
}
DCHECK_NE(type, Intl::Type::kCollator);
DCHECK_NE(type, Intl::Type::kNumberFormat);
// 3. If Type(new_receiver) is not Object or nf does not have an
// [[Initialized...]] internal slot, then
if (!Intl::IsObjectOfType(isolate, new_receiver, type)) {
// 3. a. Throw a TypeError exception.
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
method_name, receiver),
JSObject);
}
// The above IsObjectOfType returns true only for JSObjects, which
// makes this cast safe.
return Handle<JSObject>::cast(new_receiver);
}
void Intl::DefineWEProperty(Isolate* isolate, Handle<JSReceiver> target,
Handle<Name> key, Handle<Object> value) {
PropertyDescriptor desc;
desc.set_writable(true);
desc.set_enumerable(true);
desc.set_value(value);
Maybe<bool> success =
JSReceiver::DefineOwnProperty(isolate, target, key, &desc, kDontThrow);
DCHECK(success.IsJust() && success.FromJust());
USE(success);
}
namespace {
// Define general regexp macros.
......@@ -1162,50 +903,6 @@ Maybe<std::vector<std::string>> Intl::CanonicalizeLocaleList(
return Just(seen);
}
/**
* Parses Unicode extension into key - value map.
* Returns empty object if the extension string is invalid.
* We are not concerned with the validity of the values at this point.
* 'attribute' in RFC 6047 is not supported. Keys without explicit
* values are assigned UNDEFINED.
* TODO(jshin): Fix the handling of 'attribute' (in RFC 6047, but none
* has been defined so that it's not used) and boolean keys without
* an explicit value.
*/
void Intl::ParseExtension(Isolate* isolate, const std::string& extension,
std::map<std::string, std::string>& out) {
if (extension.compare(0, 3, "-u-") != 0) return;
// Key is {2}alphanum, value is {3,8}alphanum.
// Some keys may not have explicit values (booleans).
std::string key;
std::string value;
// Skip the "-u-".
size_t start = 3;
size_t end;
do {
end = extension.find('-', start);
size_t length =
(end == std::string::npos) ? extension.length() - start : end - start;
std::string element = extension.substr(start, length);
// Key is {2}alphanum
if (length == 2) {
if (!key.empty()) {
out.insert(std::pair<std::string, std::string>(key, value));
value.clear();
}
key = element;
// value is {3,8}alphanum.
} else if (length >= 3 && length <= 8 && !key.empty()) {
value = value.empty() ? element : (value + "-" + element);
} else {
return;
}
start = end + 1;
} while (end != std::string::npos);
if (!key.empty()) out.insert(std::pair<std::string, std::string>(key, value));
}
// ecma402 #sup-string.prototype.tolocalelowercase
// ecma402 #sup-string.prototype.tolocaleuppercase
MaybeHandle<String> Intl::StringLocaleConvertCase(Isolate* isolate,
......
......@@ -31,41 +31,6 @@ namespace internal {
template <typename T>
class Handle;
class DateFormat {
public:
// Create a formatter for the specificied locale and options. Returns the
// resolved settings for the locale / options.
static Maybe<icu::SimpleDateFormat*> InitializeDateTimeFormat(
Isolate* isolate, Handle<String> locale, Handle<JSObject> options,
Handle<JSObject> resolved);
// Unpacks date format object from corresponding JavaScript object.
static icu::SimpleDateFormat* UnpackDateFormat(Handle<JSObject> obj);
// Release memory we allocated for the DateFormat once the JS object that
// holds the pointer gets garbage collected.
static void DeleteDateFormat(const v8::WeakCallbackInfo<void>& data);
// Layout description.
#define DATE_FORMAT_FIELDS(V) \
V(kSimpleDateFormat, kPointerSize) \
V(kBoundFormat, kPointerSize) \
V(kSize, 0)
DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, DATE_FORMAT_FIELDS)
#undef DATE_FORMAT_FIELDS
// TODO(ryzokuken): Remove this and use regular accessors once DateFormat is a
// subclass of JSObject
//
// This needs to be consistent with the above Layout Description
static const int kSimpleDateFormatIndex = 0;
static const int kBoundFormatIndex = 1;
private:
DateFormat();
};
class Intl {
public:
enum Type {
......@@ -119,23 +84,11 @@ class Intl {
static std::string DefaultLocale(Isolate* isolate);
static void DefineWEProperty(Isolate* isolate, Handle<JSReceiver> target,
Handle<Name> key, Handle<Object> value);
// 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,
std::string* locale_less_script);
// Returns the underlying Intl receiver for various methods which
// implement ECMA-402 v1 semantics for supporting initializing
// existing Intl objects.
V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> UnwrapReceiver(
Isolate* isolate, Handle<JSReceiver> receiver,
Handle<JSFunction> constructor, Intl::Type type,
Handle<String> method_name /* TODO(gsathya): Make this char const* */,
bool check_legacy_constructor = false);
// The ResolveLocale abstract operation compares a BCP 47 language
// priority list requestedLocales against the locales in
// availableLocales and determines the best available language to
......@@ -213,10 +166,6 @@ class Intl {
Isolate* isolate, Handle<Object> locales,
bool only_return_one_result = false);
// TODO(ftang): Remove this and use ICU to the conversion in the future
static void ParseExtension(Isolate* isolate, const std::string& extension,
std::map<std::string, std::string>& out);
V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> CreateNumberFormat(
Isolate* isolate, Handle<String> locale, Handle<JSObject> options,
Handle<JSObject> resolved);
......
......@@ -18,6 +18,12 @@
namespace v8 {
namespace internal {
ACCESSORS(JSDateTimeFormat, locale, String, kLocaleOffset);
ACCESSORS(JSDateTimeFormat, numbering_system, String, kNumberingSystemOffset);
ACCESSORS(JSDateTimeFormat, icu_simple_date_format,
Managed<icu::SimpleDateFormat>, kICUSimpleDateFormatOffset)
ACCESSORS(JSDateTimeFormat, bound_format, Object, kBoundFormatOffset);
CAST_ACCESSOR(JSDateTimeFormat);
} // namespace internal
......
......@@ -18,6 +18,9 @@
#include "src/objects/js-date-time-format-inl.h"
#include "unicode/calendar.h"
#include "unicode/dtptngen.h"
#include "unicode/gregocal.h"
#include "unicode/numsys.h"
#include "unicode/smpdtfmt.h"
#include "unicode/unistr.h"
......@@ -290,20 +293,11 @@ std::string JSDateTimeFormat::CanonicalizeTimeZoneID(Isolate* isolate,
MaybeHandle<JSObject> JSDateTimeFormat::ResolvedOptions(
Isolate* isolate, Handle<JSReceiver> format_holder) {
Factory* factory = isolate->factory();
// 3. Let dtf be ? UnwrapDateTimeFormat(dtf).
if (!Intl::IsObjectOfType(isolate, format_holder,
Intl::Type::kDateTimeFormat)) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
factory->NewStringFromStaticChars(
"Intl.DateTimeFormat.resolvedOptions"),
format_holder),
JSObject);
}
CHECK(format_holder->IsJSObject());
icu::SimpleDateFormat* icu_simple_date_format =
DateFormat::UnpackDateFormat(Handle<JSObject>::cast(format_holder));
Handle<JSDateTimeFormat> format;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, format,
JSDateTimeFormat::UnwrapDateTimeFormat(isolate, format_holder), JSObject);
// 4. Let options be ! ObjectCreate(%ObjectPrototype%).
Handle<JSObject> options = factory->NewJSObject(isolate->object_function());
......@@ -312,53 +306,13 @@ MaybeHandle<JSObject> JSDateTimeFormat::ResolvedOptions(
// a. Let p be the Property value of the current row.
Handle<Object> resolved_obj;
// After we move all the data to JSDateTimeFormat, we should just get locale
// and numberingSystem from the member data. This is here until we move
// everything.
ASSIGN_RETURN_ON_EXCEPTION(
isolate, resolved_obj,
JSReceiver::GetProperty(isolate, format_holder,
factory->intl_resolved_symbol()),
JSObject);
CHECK(resolved_obj->IsJSObject());
Handle<JSObject> resolved = Handle<JSObject>::cast(resolved_obj);
// locale
Handle<Object> locale_obj;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, locale_obj,
JSReceiver::GetProperty(isolate, resolved, factory->locale_string()),
JSObject);
CHECK(locale_obj->IsString());
Handle<String> locale = Handle<String>::cast(locale_obj);
Handle<String> locale(format->locale(), isolate);
CHECK(JSReceiver::CreateDataProperty(
isolate, options, factory->locale_string(), locale, kDontThrow)
.FromJust());
// numberingSystem
// replace to factory->numberingSystem_string(), after +/1168518 landed.
Handle<String> numberingSystem_string =
factory->NewStringFromStaticChars("numberingSystem");
Handle<Object> numbering_system_obj;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, numbering_system_obj,
JSReceiver::GetProperty(isolate, resolved, numberingSystem_string),
JSObject);
if (numbering_system_obj->IsString()) {
Handle<String> numbering_system =
Handle<String>::cast(numbering_system_obj);
CHECK(JSReceiver::CreateDataProperty(isolate, options,
numberingSystem_string,
numbering_system, kDontThrow)
.FromJust());
}
icu::UnicodeString pattern_unicode;
icu_simple_date_format->toPattern(pattern_unicode);
std::string pattern;
pattern_unicode.toUTF8String(pattern);
SetPropertyFromPattern(isolate, pattern, options);
icu::SimpleDateFormat* icu_simple_date_format = UnpackDateFormat(format);
// calendar
const icu::Calendar* calendar = icu_simple_date_format->getCalendar();
// getType() returns legacy calendar type name instead of LDML/BCP47 calendar
......@@ -381,6 +335,15 @@ MaybeHandle<JSObject> JSDateTimeFormat::ResolvedOptions(
kDontThrow)
.FromJust());
// numberingSystem
if (format->numbering_system()->IsString()) {
Handle<String> numbering_system(format->numbering_system(), isolate);
CHECK(JSReceiver::CreateDataProperty(isolate, options,
factory->numberingSystem_string(),
numbering_system, kDontThrow)
.FromJust());
}
// timezone
const icu::TimeZone& tz = calendar->getTimeZone();
icu::UnicodeString time_zone;
......@@ -395,18 +358,14 @@ MaybeHandle<JSObject> JSDateTimeFormat::ResolvedOptions(
// timezone. We have Etc/UTC because 'UTC', 'Etc/Universal',
// 'Etc/Zulu' and others are turned to 'Etc/UTC' by ICU. Etc/GMT comes
// from Etc/GMT0, Etc/GMT+0, Etc/GMT-0, Etc/Greenwich.
// ecma402##sec-canonicalizetimezonename step 3
// ecma402#sec-canonicalizetimezonename step 3
if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/UTC") ||
canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) {
timezone_value = factory->NewStringFromAsciiChecked("UTC");
timezone_value = factory->NewStringFromStaticChars("UTC");
} else {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, timezone_value,
factory->NewStringFromTwoByte(
Vector<const uint16_t>(reinterpret_cast<const uint16_t*>(
canonical_time_zone.getBuffer()),
canonical_time_zone.length())),
JSObject);
ASSIGN_RETURN_ON_EXCEPTION(isolate, timezone_value,
Intl::ToString(isolate, canonical_time_zone),
JSObject);
}
CHECK(JSReceiver::CreateDataProperty(
isolate, options, factory->NewStringFromStaticChars("timeZone"),
......@@ -419,6 +378,13 @@ MaybeHandle<JSObject> JSDateTimeFormat::ResolvedOptions(
factory->undefined_value(), kDontThrow)
.FromJust());
}
icu::UnicodeString pattern_unicode;
icu_simple_date_format->toPattern(pattern_unicode);
std::string pattern;
pattern_unicode.toUTF8String(pattern);
SetPropertyFromPattern(isolate, pattern, options);
return options;
}
......@@ -453,7 +419,7 @@ namespace {
// ecma402/#sec-formatdatetime
// FormatDateTime( dateTimeFormat, x )
MaybeHandle<String> FormatDateTime(Isolate* isolate,
Handle<JSObject> date_time_format_holder,
Handle<JSDateTimeFormat> date_time_format,
double x) {
double date_value = DateCache::TimeClip(x);
if (std::isnan(date_value)) {
......@@ -461,17 +427,14 @@ MaybeHandle<String> FormatDateTime(Isolate* isolate,
String);
}
CHECK(Intl::IsObjectOfType(isolate, date_time_format_holder,
Intl::Type::kDateTimeFormat));
icu::SimpleDateFormat* date_format =
DateFormat::UnpackDateFormat(date_time_format_holder);
JSDateTimeFormat::UnpackDateFormat(date_time_format);
CHECK_NOT_NULL(date_format);
icu::UnicodeString result;
date_format->format(date_value, result);
return isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()));
return Intl::ToString(isolate, result);
}
} // namespace
......@@ -479,12 +442,10 @@ MaybeHandle<String> FormatDateTime(Isolate* isolate,
// ecma402/#sec-datetime-format-functions
// DateTime Format Functions
MaybeHandle<String> JSDateTimeFormat::DateTimeFormat(
Isolate* isolate, Handle<JSObject> date_time_format_holder,
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
Handle<Object> date) {
// 2. Assert: Type(dtf) is Object and dtf has an [[InitializedDateTimeFormat]]
// internal slot.
DCHECK(Intl::IsObjectOfType(isolate, date_time_format_holder,
Intl::Type::kDateTimeFormat));
// 3. If date is not provided or is undefined, then
double x;
......@@ -500,12 +461,12 @@ MaybeHandle<String> JSDateTimeFormat::DateTimeFormat(
x = date->Number();
}
// 5. Return FormatDateTime(dtf, x).
return FormatDateTime(isolate, date_time_format_holder, x);
return FormatDateTime(isolate, date_time_format, x);
}
MaybeHandle<String> JSDateTimeFormat::ToLocaleDateTime(
Isolate* isolate, Handle<Object> date, Handle<Object> locales,
Handle<Object> options, const char* required, const char* defaults,
Handle<Object> options, RequiredOption required, DefaultsOption defaults,
const char* service) {
Factory* factory = isolate->factory();
// 1. Let x be ? thisTimeValue(this value);
......@@ -529,16 +490,19 @@ MaybeHandle<String> JSDateTimeFormat::ToLocaleDateTime(
ToDateTimeOptions(isolate, options, required, defaults), String);
// 4. Let dateFormat be ? Construct(%DateTimeFormat%, « locales, options »).
Handle<JSObject> date_format;
Handle<JSObject> object;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, date_format,
isolate, object,
Intl::CachedOrNewService(isolate,
factory->NewStringFromAsciiChecked(service),
locales, options, internal_options),
String);
CHECK(object->IsJSDateTimeFormat());
Handle<JSDateTimeFormat> date_time_format =
Handle<JSDateTimeFormat>::cast(object);
// 5. Return FormatDateTime(dateFormat, x).
return FormatDateTime(isolate, date_format, x);
return FormatDateTime(isolate, date_time_format, x);
}
namespace {
......@@ -592,8 +556,8 @@ Maybe<bool> CreateDefault(Isolate* isolate, Handle<JSObject> options,
// ecma-402/#sec-todatetimeoptions
MaybeHandle<JSObject> JSDateTimeFormat::ToDateTimeOptions(
Isolate* isolate, Handle<Object> input_options, const char* required,
const char* defaults) {
Isolate* isolate, Handle<Object> input_options, RequiredOption required,
DefaultsOption defaults) {
Factory* factory = isolate->factory();
// 1. If options is undefined, let options be null; otherwise let options be ?
// ToObject(options).
......@@ -614,9 +578,8 @@ MaybeHandle<JSObject> JSDateTimeFormat::ToDateTimeOptions(
// 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)) {
if (required == RequiredOption::kAny || required == RequiredOption::kDate) {
// a. For each of the property names "weekday", "year", "month", "day", do
const std::vector<std::string> list({"weekday", "year", "month", "day"});
Maybe<bool> maybe_needs_default = NeedsDefault(isolate, options, list);
......@@ -625,7 +588,7 @@ MaybeHandle<JSObject> JSDateTimeFormat::ToDateTimeOptions(
}
// 5. If required is "time" or "any", then
if (required_is_any || (strcmp(required, "time") == 0)) {
if (required == RequiredOption::kAny || required == RequiredOption::kTime) {
// a. For each of the property names "hour", "minute", "second", do
const std::vector<std::string> list({"hour", "minute", "second"});
Maybe<bool> maybe_needs_default = NeedsDefault(isolate, options, list);
......@@ -635,14 +598,13 @@ MaybeHandle<JSObject> JSDateTimeFormat::ToDateTimeOptions(
// 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)) {
if (defaults == DefaultsOption::kAll || defaults == DefaultsOption::kDate) {
// a. For each of the property names "year", "month", "day", do)
const std::vector<std::string> list({"year", "month", "day"});
MAYBE_RETURN(CreateDefault(isolate, options, list), Handle<JSObject>());
}
// 7. If needDefaults is true and defaults is either "time" or "all", then
if (default_is_all || (strcmp(defaults, "time") == 0)) {
if (defaults == DefaultsOption::kAll || defaults == DefaultsOption::kTime) {
// a. For each of the property names "hour", "minute", "second", do
const std::vector<std::string> list({"hour", "minute", "second"});
MAYBE_RETURN(CreateDefault(isolate, options, list), Handle<JSObject>());
......@@ -652,21 +614,375 @@ MaybeHandle<JSObject> JSDateTimeFormat::ToDateTimeOptions(
return options;
}
MaybeHandle<JSObject> JSDateTimeFormat::Unwrap(Isolate* isolate,
Handle<JSReceiver> receiver,
const char* method_name) {
MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::UnwrapDateTimeFormat(
Isolate* isolate, Handle<JSReceiver> format_holder) {
Handle<Context> native_context =
Handle<Context>(isolate->context()->native_context(), isolate);
Handle<JSFunction> constructor = Handle<JSFunction>(
JSFunction::cast(native_context->intl_date_time_format_function()),
isolate);
Handle<String> method_name_str =
isolate->factory()->NewStringFromAsciiChecked(method_name);
Handle<Object> dtf;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, dtf,
Intl::LegacyUnwrapReceiver(isolate, format_holder, constructor,
format_holder->IsJSDateTimeFormat()),
JSDateTimeFormat);
// 2. If Type(dtf) is not Object or dtf does not have an
// [[InitializedDateTimeFormat]] internal slot, then
if (!dtf->IsJSDateTimeFormat()) {
// a. Throw a TypeError exception.
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
isolate->factory()->NewStringFromAsciiChecked(
"UnwrapDateTimeFormat"),
format_holder),
JSDateTimeFormat);
}
// 3. Return dtf.
return Handle<JSDateTimeFormat>::cast(dtf);
}
namespace {
// ecma-402/#sec-isvalidtimezonename
bool IsValidTimeZoneName(const icu::TimeZone& tz) {
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString id;
tz.getID(id);
icu::UnicodeString canonical;
icu::TimeZone::getCanonicalID(id, canonical, status);
return U_SUCCESS(status) &&
canonical != icu::UnicodeString("Etc/Unknown", -1, US_INV);
}
std::unique_ptr<icu::TimeZone> CreateTimeZone(Isolate* isolate,
const char* timezone) {
// Create time zone as specified by the user. We have to re-create time zone
// since calendar takes ownership.
if (timezone == nullptr) {
// 19.a. Else / Let timeZone be DefaultTimeZone().
return std::unique_ptr<icu::TimeZone>(icu::TimeZone::createDefault());
}
std::string canonicalized =
JSDateTimeFormat::CanonicalizeTimeZoneID(isolate, timezone);
if (canonicalized.empty()) return std::unique_ptr<icu::TimeZone>();
std::unique_ptr<icu::TimeZone> tz(
icu::TimeZone::createTimeZone(canonicalized.c_str()));
// 18.b If the result of IsValidTimeZoneName(timeZone) is false, then
// i. Throw a RangeError exception.
if (!IsValidTimeZoneName(*tz)) return std::unique_ptr<icu::TimeZone>();
return tz;
}
std::unique_ptr<icu::Calendar> CreateCalendar(Isolate* isolate,
const icu::Locale& icu_locale,
const char* timezone) {
std::unique_ptr<icu::TimeZone> tz = CreateTimeZone(isolate, timezone);
if (tz.get() == nullptr) return std::unique_ptr<icu::Calendar>();
// Create a calendar using locale, and apply time zone to it.
UErrorCode status = U_ZERO_ERROR;
std::unique_ptr<icu::Calendar> calendar(
icu::Calendar::createInstance(tz.release(), icu_locale, status));
CHECK(U_SUCCESS(status));
CHECK_NOT_NULL(calendar.get());
if (calendar->getDynamicClassID() ==
icu::GregorianCalendar::getStaticClassID()) {
icu::GregorianCalendar* gc =
static_cast<icu::GregorianCalendar*>(calendar.get());
UErrorCode status = U_ZERO_ERROR;
// The beginning of ECMAScript time, namely -(2**53)
const double start_of_time = -9007199254740992;
gc->setGregorianChange(start_of_time, status);
DCHECK(U_SUCCESS(status));
}
return calendar;
}
std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormat(
Isolate* isolate, const icu::Locale& icu_locale,
const std::string skeleton) {
// See https://github.com/tc39/ecma402/issues/225 . The best pattern
// generation needs to be done in the base locale according to the
// current spec however odd it may be. See also crbug.com/826549 .
// This is a temporary work-around to get v8's external behavior to match
// the current spec, but does not follow the spec provisions mentioned
// in the above Ecma 402 issue.
// TODO(jshin): The spec may need to be revised because using the base
// locale for the pattern match is not quite right. Moreover, what to
// do with 'related year' part when 'chinese/dangi' calendar is specified
// has to be discussed. Revisit once the spec is clarified/revised.
icu::Locale no_extension_locale(icu_locale.getBaseName());
UErrorCode status = U_ZERO_ERROR;
std::unique_ptr<icu::DateTimePatternGenerator> generator(
icu::DateTimePatternGenerator::createInstance(no_extension_locale,
status));
icu::UnicodeString pattern;
if (U_SUCCESS(status)) {
pattern =
generator->getBestPattern(icu::UnicodeString(skeleton.c_str()), status);
}
// Make formatter from skeleton. Calendar and numbering system are added
// to the locale as Unicode extension (if they were specified at all).
status = U_ZERO_ERROR;
std::unique_ptr<icu::SimpleDateFormat> date_format(
new icu::SimpleDateFormat(pattern, icu_locale, status));
if (U_FAILURE(status)) return std::unique_ptr<icu::SimpleDateFormat>();
CHECK_NOT_NULL(date_format.get());
return date_format;
}
void SetResolvedDateSettings(Isolate* isolate, const icu::Locale& icu_locale,
icu::SimpleDateFormat* date_format,
Handle<JSDateTimeFormat> date_time_format) {
Factory* factory = isolate->factory();
// Set the locale
// 12. Set dateTimeFormat.[[Locale]] to r.[[locale]].
UErrorCode status = U_ZERO_ERROR;
char result[ULOC_FULLNAME_CAPACITY];
uloc_toLanguageTag(icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY,
FALSE, &status);
Handle<String> locale_str = U_SUCCESS(status)
? factory->NewStringFromAsciiChecked(result)
: factory->NewStringFromStaticChars("und");
date_time_format->set_locale(*locale_str);
// TODO(ftang): try to avoid storing numbering_system into JSDateTimeFormat
// later.
// Ugly hack. ICU doesn't expose numbering system in any way, so we have
// to assume that for given locale NumberingSystem constructor produces the
// same digits as NumberFormat/Calendar would.
// 15. Set dateTimeFormat.[[NumberingSystem]] to r.[[nu]].
status = U_ZERO_ERROR;
std::unique_ptr<icu::NumberingSystem> numbering_system(
icu::NumberingSystem::createInstance(icu_locale, status));
if (U_SUCCESS(status)) {
const char* ns = numbering_system->getName();
Handle<String> numbering_system_str =
factory->NewStringFromAsciiChecked(ns);
date_time_format->set_numbering_system(*numbering_system_str);
}
}
} // namespace
enum FormatMatcherOption { kBestFit, kBasic };
// ecma402/#sec-initializedatetimeformat
MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
Handle<Object> requested_locales, Handle<Object> input_options) {
// 2. Let options be ? ToDateTimeOptions(options, "any", "date").
Handle<JSObject> options;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, options,
JSDateTimeFormat::ToDateTimeOptions(
isolate, input_options, RequiredOption::kAny, DefaultsOption::kDate),
JSDateTimeFormat);
// 11. Let r be ResolveLocale( %DateTimeFormat%.[[AvailableLocales]],
// requestedLocales, opt, %DateTimeFormat%.[[RelevantExtensionKeys]],
// localeData).
const char* kService = "Intl.DateTimeFormat";
Handle<JSObject> r;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, r,
Intl::ResolveLocale(isolate, "dateformat", requested_locales, options),
JSDateTimeFormat);
Handle<String> locale_with_extension_str =
isolate->factory()->NewStringFromStaticChars("localeWithExtension");
Handle<Object> locale_with_extension_obj =
JSObject::GetDataProperty(r, locale_with_extension_str);
// The locale_with_extension has to be a string. Either a user
// provided canonicalized string or the default locale.
CHECK(locale_with_extension_obj->IsString());
Handle<String> locale_with_extension =
Handle<String>::cast(locale_with_extension_obj);
icu::Locale icu_locale =
Intl::CreateICULocale(isolate, locale_with_extension);
DCHECK(!icu_locale.isBogus());
// We implement only best fit algorithm, but still need to check
// if the formatMatcher values are in range.
// 25. Let matcher be ? GetOption(options, "formatMatcher", "string",
// « "basic", "best fit" », "best fit").
Handle<JSReceiver> options_obj;
ASSIGN_RETURN_ON_EXCEPTION(isolate, options_obj,
Object::ToObject(isolate, options),
JSDateTimeFormat);
std::unique_ptr<char[]> matcher_str = nullptr;
std::vector<const char*> matcher_values = {"basic", "best fit"};
Maybe<bool> maybe_found_matcher =
Intl::GetStringOption(isolate, options_obj, "formatMatcher",
matcher_values, kService, &matcher_str);
MAYBE_RETURN(maybe_found_matcher, Handle<JSDateTimeFormat>());
// 17. Let timeZone be ? Get(options, "timeZone").
static std::vector<const char*> empty_values = {};
std::unique_ptr<char[]> timezone = nullptr;
Maybe<bool> maybe_timezone = Intl::GetStringOption(
isolate, options, "timeZone", empty_values, kService, &timezone);
MAYBE_RETURN(maybe_timezone, Handle<JSDateTimeFormat>());
Maybe<std::string> maybe_skeleton =
JSDateTimeFormat::OptionsToSkeleton(isolate, options);
MAYBE_RETURN(maybe_skeleton, Handle<JSDateTimeFormat>());
CHECK(!maybe_skeleton.FromJust().empty());
std::string skeleton = maybe_skeleton.FromJust();
// 13. Set dateTimeFormat.[[Calendar]] to r.[[ca]].
std::unique_ptr<icu::Calendar> calendar(
CreateCalendar(isolate, icu_locale, timezone.get()));
// 18.b If the result of IsValidTimeZoneName(timeZone) is false, then
// i. Throw a RangeError exception.
if (calendar.get() == nullptr) {
THROW_NEW_ERROR(isolate,
NewRangeError(MessageTemplate::kInvalidTimeZone,
isolate->factory()->NewStringFromAsciiChecked(
timezone.get())),
JSDateTimeFormat);
}
std::unique_ptr<icu::SimpleDateFormat> date_format(
CreateICUDateFormat(isolate, icu_locale, skeleton));
if (date_format.get() == nullptr) {
// Remove extensions and try again.
icu_locale = icu::Locale(icu_locale.getBaseName());
date_format = CreateICUDateFormat(isolate, icu_locale, skeleton);
if (date_format.get() == nullptr) {
FATAL("Failed to create ICU date format, are ICU data files missing?");
}
}
date_format->adoptCalendar(calendar.release());
SetResolvedDateSettings(isolate, icu_locale, date_format.get(),
date_time_format);
Handle<Managed<icu::SimpleDateFormat>> managed_format =
Managed<icu::SimpleDateFormat>::FromUniquePtr(isolate, 0,
std::move(date_format));
date_time_format->set_icu_simple_date_format(*managed_format);
return date_time_format;
}
return Intl::UnwrapReceiver(isolate, receiver, constructor,
Intl::Type::kDateTimeFormat, method_name_str,
true);
icu::SimpleDateFormat* JSDateTimeFormat::UnpackDateFormat(
Handle<JSDateTimeFormat> date_time_format) {
return date_time_format->icu_simple_date_format()->raw();
}
namespace {
// The list comes from third_party/icu/source/i18n/unicode/udat.h.
// They're mapped to DateTimeFormat components listed at
// https://tc39.github.io/ecma402/#sec-datetimeformat-abstracts .
Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) {
switch (field_id) {
case -1:
return isolate->factory()->literal_string();
case UDAT_YEAR_FIELD:
case UDAT_EXTENDED_YEAR_FIELD:
case UDAT_YEAR_NAME_FIELD:
return isolate->factory()->year_string();
case UDAT_MONTH_FIELD:
case UDAT_STANDALONE_MONTH_FIELD:
return isolate->factory()->month_string();
case UDAT_DATE_FIELD:
return isolate->factory()->day_string();
case UDAT_HOUR_OF_DAY1_FIELD:
case UDAT_HOUR_OF_DAY0_FIELD:
case UDAT_HOUR1_FIELD:
case UDAT_HOUR0_FIELD:
return isolate->factory()->hour_string();
case UDAT_MINUTE_FIELD:
return isolate->factory()->minute_string();
case UDAT_SECOND_FIELD:
return isolate->factory()->second_string();
case UDAT_DAY_OF_WEEK_FIELD:
case UDAT_DOW_LOCAL_FIELD:
case UDAT_STANDALONE_DAY_FIELD:
return isolate->factory()->weekday_string();
case UDAT_AM_PM_FIELD:
return isolate->factory()->dayperiod_string();
case UDAT_TIMEZONE_FIELD:
case UDAT_TIMEZONE_RFC_FIELD:
case UDAT_TIMEZONE_GENERIC_FIELD:
case UDAT_TIMEZONE_SPECIAL_FIELD:
case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
case UDAT_TIMEZONE_ISO_FIELD:
case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
return isolate->factory()->timeZoneName_string();
case UDAT_ERA_FIELD:
return isolate->factory()->era_string();
default:
// Other UDAT_*_FIELD's cannot show up because there is no way to specify
// them via options of Intl.DateTimeFormat.
UNREACHABLE();
// To prevent MSVC from issuing C4715 warning.
return Handle<String>();
}
}
} // namespace
MaybeHandle<Object> JSDateTimeFormat::FormatToParts(
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
double date_value) {
Factory* factory = isolate->factory();
icu::SimpleDateFormat* format =
JSDateTimeFormat::UnpackDateFormat(date_time_format);
CHECK_NOT_NULL(format);
icu::UnicodeString formatted;
icu::FieldPositionIterator fp_iter;
icu::FieldPosition fp;
UErrorCode status = U_ZERO_ERROR;
format->format(date_value, formatted, &fp_iter, status);
if (U_FAILURE(status)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object);
}
Handle<JSArray> result = factory->NewJSArray(0);
int32_t length = formatted.length();
if (length == 0) return result;
int index = 0;
int32_t previous_end_pos = 0;
Handle<String> substring;
while (fp_iter.next(fp)) {
int32_t begin_pos = fp.getBeginIndex();
int32_t end_pos = fp.getEndIndex();
if (previous_end_pos < begin_pos) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring,
Intl::ToString(isolate, formatted, previous_end_pos, begin_pos),
Object);
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(-1, isolate), substring);
++index;
}
ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring,
Intl::ToString(isolate, formatted, begin_pos, end_pos), Object);
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(fp.getField(), isolate),
substring);
previous_end_pos = end_pos;
++index;
}
if (previous_end_pos < length) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring,
Intl::ToString(isolate, formatted, previous_end_pos, length), Object);
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(-1, isolate), substring);
}
JSObject::ValidateElements(*result);
return result;
}
} // namespace internal
} // namespace v8
......@@ -10,6 +10,7 @@
#define V8_OBJECTS_JS_DATE_TIME_FORMAT_H_
#include "src/isolate.h"
#include "src/objects/managed.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
......@@ -23,16 +24,19 @@ namespace internal {
class JSDateTimeFormat : public JSObject {
public:
V8_WARN_UNUSED_RESULT static MaybeHandle<JSDateTimeFormat> Initialize(
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
Handle<Object> locales, Handle<Object> options);
static icu::SimpleDateFormat* UnpackDateFormat(
Handle<JSDateTimeFormat> date_time_format);
V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> ResolvedOptions(
Isolate* isolate, Handle<JSReceiver> date_time_holder);
Isolate* isolate, Handle<JSReceiver> format_holder);
// The UnwrapDateTimeFormat abstract operation gets the underlying
// DateTimeFormat operation for various methods which implement ECMA-402 v1
// semantics for supporting initializing existing Intl objects.
//
// ecma402/#sec-unwrapdatetimeformat
V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> Unwrap(
Isolate* isolate, Handle<JSReceiver> receiver, const char* method_name);
V8_WARN_UNUSED_RESULT static MaybeHandle<JSDateTimeFormat>
UnwrapDateTimeFormat(Isolate* isolate, Handle<JSReceiver> format_holder);
// Convert the options to ICU DateTimePatternGenerator skeleton.
static Maybe<std::string> OptionsToSkeleton(Isolate* isolate,
......@@ -46,30 +50,47 @@ class JSDateTimeFormat : public JSObject {
// ecma402/#sec-datetime-format-functions
// DateTime Format Functions
V8_WARN_UNUSED_RESULT static MaybeHandle<String> DateTimeFormat(
Isolate* isolate, Handle<JSObject> date_time_format_holder,
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
Handle<Object> date);
V8_WARN_UNUSED_RESULT static MaybeHandle<Object> FormatToParts(
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
double date_value);
// ecma-402/#sec-todatetimeoptions
enum class RequiredOption { kDate, kTime, kAny };
enum class DefaultsOption { kDate, kTime, kAll };
V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> ToDateTimeOptions(
Isolate* isolate, Handle<Object> input_options, const char* required,
const char* defaults);
Isolate* isolate, Handle<Object> input_options, RequiredOption required,
DefaultsOption 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,
Handle<Object> options, RequiredOption required, DefaultsOption defaults,
const char* service);
DECL_CAST(JSDateTimeFormat)
// TODO(ftang): try to remove numbering_system from JSDateTimeFormat and
// directly read from icu to save some bytes later.
// Layout description.
#define JS_DATE_TIME_FORMAT_FIELDS(V) \
/* Total size. */ \
#define JS_DATE_TIME_FORMAT_FIELDS(V) \
V(kLocaleOffset, kPointerSize) \
V(kNumberingSystemOffset, kPointerSize) \
V(kICUSimpleDateFormatOffset, kPointerSize) \
V(kBoundFormatOffset, kPointerSize) \
/* Total size. */ \
V(kSize, 0)
DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize,
JS_DATE_TIME_FORMAT_FIELDS)
#undef JS_DATE_TIME_FORMAT_FIELDS
DECL_ACCESSORS(locale, String)
DECL_ACCESSORS(numbering_system, String)
DECL_ACCESSORS(icu_simple_date_format, Managed<icu::SimpleDateFormat>)
DECL_ACCESSORS(bound_format, Object)
DECL_PRINTER(JSDateTimeFormat)
DECL_VERIFIER(JSDateTimeFormat)
......
......@@ -182,9 +182,11 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::UnwrapNumberFormat(
}
// static
MaybeHandle<JSNumberFormat> JSNumberFormat::InitializeNumberFormat(
MaybeHandle<JSNumberFormat> JSNumberFormat::Initialize(
Isolate* isolate, Handle<JSNumberFormat> number_format,
Handle<Object> locales, Handle<Object> options_obj) {
// set the flags to 0 ASAP.
number_format->set_flags(0);
Factory* factory = isolate->factory();
// 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
Handle<JSObject> requested_locales;
......
......@@ -28,9 +28,9 @@ namespace internal {
class JSNumberFormat : public JSObject {
public:
// ecma402/#sec-initializenumberformat
V8_WARN_UNUSED_RESULT static MaybeHandle<JSNumberFormat>
InitializeNumberFormat(Isolate* isolate, Handle<JSNumberFormat> number_format,
Handle<Object> locales, Handle<Object> options);
V8_WARN_UNUSED_RESULT static MaybeHandle<JSNumberFormat> Initialize(
Isolate* isolate, Handle<JSNumberFormat> number_format,
Handle<Object> locales, Handle<Object> options);
// ecma402/#sec-unwrapnumberformat
V8_WARN_UNUSED_RESULT static MaybeHandle<JSNumberFormat> UnwrapNumberFormat(
......
......@@ -110,92 +110,13 @@ RUNTIME_FUNCTION(Runtime_GetDefaultICULocale) {
Intl::DefaultLocale(isolate).c_str());
}
RUNTIME_FUNCTION(Runtime_DefineWEProperty) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSObject, target, 0);
CONVERT_ARG_HANDLE_CHECKED(Name, key, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
Intl::DefineWEProperty(isolate, target, key, value);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_IsInitializedIntlObjectOfType) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, input, 0);
CONVERT_SMI_ARG_CHECKED(expected_type_int, 1);
Intl::Type expected_type = Intl::TypeFromInt(expected_type_int);
return isolate->heap()->ToBoolean(
Intl::IsObjectOfType(isolate, input, expected_type));
}
RUNTIME_FUNCTION(Runtime_MarkAsInitializedIntlObjectOfType) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSObject, input, 0);
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(isolate, input, marker, type, LanguageMode::kStrict)
.Assert();
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_CreateDateTimeFormat) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
Handle<JSFunction> constructor(
isolate->native_context()->intl_date_time_format_function(), isolate);
Handle<JSObject> local_object;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, local_object,
JSObject::New(constructor, constructor));
// Set date time formatter as embedder field of the resulting JS object.
Maybe<icu::SimpleDateFormat*> maybe_date_format =
DateFormat::InitializeDateTimeFormat(isolate, locale, options, resolved);
MAYBE_RETURN(maybe_date_format, ReadOnlyRoots(isolate).exception());
icu::SimpleDateFormat* date_format = maybe_date_format.FromJust();
CHECK_NOT_NULL(date_format);
local_object->SetEmbedderField(DateFormat::kSimpleDateFormatIndex,
reinterpret_cast<Smi*>(date_format));
// Make object handle weak so we can delete the data format once GC kicks in.
Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
DateFormat::DeleteDateFormat,
WeakCallbackType::kInternalFields);
return *local_object;
}
// ecma402/#sec-intl.datetimeformat.prototype.resolvedoptions
RUNTIME_FUNCTION(Runtime_DateTimeFormatResolvedOptions) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
// 1. Let dtf be this value.
CONVERT_ARG_HANDLE_CHECKED(Object, dtf, 0);
// 2. If Type(dtf) is not Object, throw a TypeError exception.
if (!dtf->IsJSReceiver()) {
Handle<String> method_str = isolate->factory()->NewStringFromStaticChars(
......@@ -204,9 +125,10 @@ RUNTIME_FUNCTION(Runtime_DateTimeFormatResolvedOptions) {
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
method_str, dtf));
}
Handle<JSReceiver> date_format_holder = Handle<JSReceiver>::cast(dtf);
Handle<JSReceiver> format_holder = Handle<JSReceiver>::cast(dtf);
RETURN_RESULT_OR_FAILURE(
isolate, JSDateTimeFormat::ResolvedOptions(isolate, date_format_holder));
isolate, JSDateTimeFormat::ResolvedOptions(isolate, format_holder));
}
RUNTIME_FUNCTION(Runtime_NumberFormatResolvedOptions) {
......@@ -236,25 +158,6 @@ RUNTIME_FUNCTION(Runtime_NumberFormatResolvedOptions) {
return *JSNumberFormat::ResolvedOptions(isolate, number_format);
}
RUNTIME_FUNCTION(Runtime_ParseExtension) {
Factory* factory = isolate->factory();
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(String, extension, 0);
std::map<std::string, std::string> map;
Intl::ParseExtension(isolate, std::string(extension->ToCString().get()), map);
Handle<JSObject> extension_map =
isolate->factory()->NewJSObjectWithNullProto();
for (std::map<std::string, std::string>::iterator it = map.begin();
it != map.end(); it++) {
JSObject::AddProperty(
isolate, extension_map,
factory->NewStringFromAsciiChecked(it->first.c_str()),
factory->NewStringFromAsciiChecked(it->second.c_str()), NONE);
}
return *extension_map;
}
RUNTIME_FUNCTION(Runtime_PluralRulesSelect) {
HandleScope scope(isolate);
......@@ -281,18 +184,6 @@ RUNTIME_FUNCTION(Runtime_PluralRulesSelect) {
isolate, JSPluralRules::ResolvePlural(isolate, plural_rules, number));
}
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,
JSDateTimeFormat::ToDateTimeOptions(
isolate, options, required->ToCString().get(),
defaults->ToCString().get()));
}
RUNTIME_FUNCTION(Runtime_StringToLowerCaseIntl) {
HandleScope scope(isolate);
DCHECK_EQ(args.length(), 1);
......@@ -327,20 +218,5 @@ RUNTIME_FUNCTION(Runtime_DateCacheVersion) {
return date_cache_version->get(0);
}
RUNTIME_FUNCTION(Runtime_IntlUnwrapReceiver) {
HandleScope scope(isolate);
DCHECK_EQ(5, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0);
CONVERT_SMI_ARG_CHECKED(type_int, 1);
CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 2);
CONVERT_ARG_HANDLE_CHECKED(String, method, 3);
CONVERT_BOOLEAN_ARG_CHECKED(check_legacy_constructor, 4);
RETURN_RESULT_OR_FAILURE(
isolate, Intl::UnwrapReceiver(isolate, receiver, constructor,
Intl::TypeFromInt(type_int), method,
check_legacy_constructor));
}
} // namespace internal
} // namespace v8
......@@ -202,20 +202,13 @@ namespace internal {
#define FOR_EACH_INTRINSIC_INTL(F) \
F(AvailableLocalesOf, 1, 1) \
F(CanonicalizeLanguageTag, 1, 1) \
F(CreateDateTimeFormat, 3, 1) \
F(DateCacheVersion, 0, 1) \
F(DateTimeFormatResolvedOptions, 1, 1) \
F(DefineWEProperty, 3, 1) \
F(FormatList, 2, 1) \
F(FormatListToParts, 2, 1) \
F(GetDefaultICULocale, 0, 1) \
F(IntlUnwrapReceiver, 5, 1) \
F(IsInitializedIntlObjectOfType, 2, 1) \
F(MarkAsInitializedIntlObjectOfType, 2, 1) \
F(ParseExtension, 1, 1) \
F(NumberFormatResolvedOptions, 1, 1) \
F(PluralRulesSelect, 2, 1) \
F(ToDateTimeOptions, 3, 1) \
F(StringToLowerCaseIntl, 1, 1) \
F(StringToUpperCaseIntl, 1, 1) // End of macro.
#else
......
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