Commit a9e2b2ce authored by Jungshik Shin's avatar Jungshik Shin Committed by Commit Bot

Move DateTimeFormat.formatToParts to CPP from JS

Bug: None
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: Ie8a0db70a2f29567718fbacfd33fcd412109d069
Reviewed-on: https://chromium-review.googlesource.com/1034282
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52929}
parent 37693e0a
......@@ -2733,6 +2733,10 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
JSObject::AddProperty(
prototype, factory->to_string_tag_symbol(), factory->Object_string(),
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY));
SimpleInstallFunction(prototype, "formatToParts",
Builtins::kDateTimeFormatPrototypeFormatToParts, 1,
false);
}
{
......@@ -2750,13 +2754,9 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
prototype, factory->to_string_tag_symbol(), factory->Object_string(),
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY));
Handle<String> name = factory->InternalizeUtf8String("formatToParts");
InstallFunction(
prototype,
SimpleCreateFunction(isolate, name,
Builtins::kNumberFormatPrototypeFormatToParts, 1,
false),
name);
SimpleInstallFunction(prototype, "formatToParts",
Builtins::kNumberFormatPrototypeFormatToParts, 1,
false);
}
{
......
......@@ -1260,19 +1260,21 @@ namespace internal {
ASM(MathPowInternal)
#ifdef V8_INTL_SUPPORT
#define BUILTIN_LIST(CPP, API, TFJ, TFC, TFS, TFH, ASM) \
BUILTIN_LIST_BASE(CPP, API, TFJ, TFC, TFS, TFH, ASM) \
BUILTIN_LIST_FROM_DSL(CPP, API, TFJ, TFC, TFS, TFH, ASM) \
\
TFS(StringToLowerCaseIntl, kString) \
/* ES #sec-string.prototype.tolowercase */ \
TFJ(StringPrototypeToLowerCaseIntl, 0) \
/* ES #sec-string.prototype.touppercase */ \
CPP(StringPrototypeToUpperCaseIntl) \
/* ES #sec-string.prototype.normalize */ \
CPP(StringPrototypeNormalizeIntl) \
/* ecma402 #sec-intl.numberformat.prototype.formattoparts */ \
CPP(NumberFormatPrototypeFormatToParts)
#define BUILTIN_LIST(CPP, API, TFJ, TFC, TFS, TFH, ASM) \
BUILTIN_LIST_BASE(CPP, API, TFJ, TFC, TFS, TFH, ASM) \
BUILTIN_LIST_FROM_DSL(CPP, API, TFJ, TFC, TFS, TFH, ASM) \
\
TFS(StringToLowerCaseIntl, kString) \
/* ES #sec-string.prototype.tolowercase */ \
TFJ(StringPrototypeToLowerCaseIntl, 0) \
/* ES #sec-string.prototype.touppercase */ \
CPP(StringPrototypeToUpperCaseIntl) \
/* ES #sec-string.prototype.normalize */ \
CPP(StringPrototypeNormalizeIntl) \
/* ecma402 #sec-intl.numberformat.prototype.formattoparts */ \
CPP(NumberFormatPrototypeFormatToParts) \
/* ecma402 #sec-intl.datetimeformat.prototype.formattoparts */ \
CPP(DateTimeFormatPrototypeFormatToParts)
#else
#define BUILTIN_LIST(CPP, API, TFJ, TFC, TFS, TFH, ASM) \
BUILTIN_LIST_BASE(CPP, API, TFJ, TFC, TFS, TFH, ASM) \
......
......@@ -9,15 +9,19 @@
#include "src/builtins/builtins-intl.h"
#include "src/builtins/builtins-utils.h"
#include "src/builtins/builtins.h"
#include "src/date.h"
#include "src/intl.h"
#include "src/objects-inl.h"
#include "src/objects/intl-objects.h"
#include "unicode/datefmt.h"
#include "unicode/decimfmt.h"
#include "unicode/fieldpos.h"
#include "unicode/fpositer.h"
#include "unicode/normalizer2.h"
#include "unicode/numfmt.h"
#include "unicode/smpdtfmt.h"
#include "unicode/udat.h"
#include "unicode/ufieldpositer.h"
#include "unicode/unistr.h"
#include "unicode/ustring.h"
......@@ -152,6 +156,57 @@ Handle<String> IcuNumberFieldIdToNumberType(int32_t field_id, double number,
}
}
// 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>();
}
}
bool AddElement(Handle<JSArray> array, int index,
Handle<String> field_type_string,
const icu::UnicodeString& formatted, int32_t begin, int32_t end,
......@@ -240,6 +295,53 @@ Object* FormatNumberToParts(Isolate* isolate, icu::NumberFormat* fmt,
return *result;
}
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)) return isolate->heap()->undefined_value();
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;
while (fp_iter.next(fp)) {
int32_t begin_pos = fp.getBeginIndex();
int32_t end_pos = fp.getEndIndex();
if (previous_end_pos < begin_pos) {
if (!AddElement(result, index, IcuDateFieldIdToDateType(-1, isolate),
formatted, previous_end_pos, begin_pos, isolate)) {
return isolate->heap()->undefined_value();
}
++index;
}
if (!AddElement(result, index,
IcuDateFieldIdToDateType(fp.getField(), isolate), formatted,
begin_pos, end_pos, isolate)) {
return isolate->heap()->undefined_value();
}
previous_end_pos = end_pos;
++index;
}
if (previous_end_pos < length) {
if (!AddElement(result, index, IcuDateFieldIdToDateType(-1, isolate),
formatted, previous_end_pos, length, isolate)) {
return isolate->heap()->undefined_value();
}
}
JSObject::ValidateElements(*result);
return *result;
}
} // namespace
// Flattens a list of possibly-overlapping "regions" to a list of
......@@ -366,5 +468,42 @@ BUILTIN(NumberFormatPrototypeFormatToParts) {
return result;
}
BUILTIN(DateTimeFormatPrototypeFormatToParts) {
const char* const method = "Intl.DateTimeFormat.prototype.formatToParts";
HandleScope handle_scope(isolate);
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))) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
factory->NewStringFromAsciiChecked(method),
date_format_holder));
}
Handle<Object> x = args.atOrUndefined(isolate, 1);
if (x->IsUndefined(isolate)) {
x = factory->NewNumber(JSDate::CurrentTimeValue(isolate));
} else {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x,
Object::ToNumber(args.at(1)));
}
double date_value = DateCache::TimeClip(x->Number());
if (std::isnan(date_value)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidTimeValue));
}
icu::SimpleDateFormat* date_format =
DateFormat::UnpackDateFormat(isolate, date_format_holder);
CHECK_NOT_NULL(date_format);
return FormatDateToParts(isolate, date_format, date_value);
}
} // namespace internal
} // namespace v8
......@@ -1022,6 +1022,7 @@ bool Builtins::IsIsolateIndependent(int index) {
case kWeakSetPrototypeAdd:
case kWeakSetPrototypeDelete:
#ifdef V8_INTL_SUPPORT
case kDateTimeFormatPrototypeFormatToParts:
case kNumberFormatPrototypeFormatToParts:
case kStringPrototypeNormalizeIntl:
case kStringPrototypeToLowerCaseIntl:
......
......@@ -1840,30 +1840,6 @@ function formatDate(formatter, dateValue) {
return %InternalDateFormat(formatter, dateMs);
}
DEFINE_METHOD(
GlobalIntlDateTimeFormat.prototype,
formatToParts(dateValue) {
REQUIRE_OBJECT_COERCIBLE(this, "Intl.DateTimeFormat.prototype.formatToParts");
if (!IS_OBJECT(this)) {
throw %make_type_error(kCalledOnNonObject, this);
}
if (!%IsInitializedIntlObjectOfType(this, 'dateformat')) {
throw %make_type_error(kIncompatibleMethodReceiver,
'Intl.DateTimeFormat.prototype.formatToParts',
this);
}
var dateMs;
if (IS_UNDEFINED(dateValue)) {
dateMs = %DateCurrentTime();
} else {
dateMs = TO_NUMBER(dateValue);
}
return %InternalDateFormatToParts(this, dateMs);
}
);
// Length is 1 as specified in ECMA 402 v2+
AddBoundMethod(GlobalIntlDateTimeFormat, 'format', formatDate, 1, 'dateformat',
true);
......
......@@ -32,8 +32,6 @@
#include "unicode/decimfmt.h"
#include "unicode/dtfmtsym.h"
#include "unicode/dtptngen.h"
#include "unicode/fieldpos.h"
#include "unicode/fpositer.h"
#include "unicode/locid.h"
#include "unicode/numfmt.h"
#include "unicode/numsys.h"
......@@ -41,7 +39,6 @@
#include "unicode/rbbi.h"
#include "unicode/smpdtfmt.h"
#include "unicode/timezone.h"
#include "unicode/translit.h"
#include "unicode/uchar.h"
#include "unicode/ucol.h"
#include "unicode/ucurr.h"
......@@ -280,143 +277,6 @@ RUNTIME_FUNCTION(Runtime_InternalDateFormat) {
result.length())));
}
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>();
}
}
bool AddElement(Handle<JSArray> array, int index, int32_t field_id,
const icu::UnicodeString& formatted, int32_t begin, int32_t end,
Isolate* isolate) {
HandleScope scope(isolate);
Factory* factory = isolate->factory();
Handle<JSObject> element = factory->NewJSObject(isolate->object_function());
Handle<String> value = IcuDateFieldIdToDateType(field_id, isolate);
JSObject::AddProperty(element, factory->type_string(), value, NONE);
icu::UnicodeString field(formatted.tempSubStringBetween(begin, end));
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, value,
factory->NewStringFromTwoByte(Vector<const uint16_t>(
reinterpret_cast<const uint16_t*>(field.getBuffer()),
field.length())),
false);
JSObject::AddProperty(element, factory->value_string(), value, NONE);
RETURN_ON_EXCEPTION_VALUE(
isolate, JSObject::AddDataElement(array, index, element, NONE), false);
return true;
}
} // namespace
RUNTIME_FUNCTION(Runtime_InternalDateFormatToParts) {
HandleScope scope(isolate);
Factory* factory = isolate->factory();
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSObject, date_format_holder, 0);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(date, 1);
double date_value = DateCache::TimeClip(date->Number());
if (std::isnan(date_value)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidTimeValue));
}
icu::SimpleDateFormat* date_format =
DateFormat::UnpackDateFormat(isolate, date_format_holder);
CHECK_NOT_NULL(date_format);
icu::UnicodeString formatted;
icu::FieldPositionIterator fp_iter;
icu::FieldPosition fp;
UErrorCode status = U_ZERO_ERROR;
date_format->format(date_value, formatted, &fp_iter, status);
if (U_FAILURE(status)) return isolate->heap()->undefined_value();
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;
while (fp_iter.next(fp)) {
int32_t begin_pos = fp.getBeginIndex();
int32_t end_pos = fp.getEndIndex();
if (previous_end_pos < begin_pos) {
if (!AddElement(result, index, -1, formatted, previous_end_pos, begin_pos,
isolate)) {
return isolate->heap()->undefined_value();
}
++index;
}
if (!AddElement(result, index, fp.getField(), formatted, begin_pos, end_pos,
isolate)) {
return isolate->heap()->undefined_value();
}
previous_end_pos = end_pos;
++index;
}
if (previous_end_pos < length) {
if (!AddElement(result, index, -1, formatted, previous_end_pos, length,
isolate)) {
return isolate->heap()->undefined_value();
}
}
JSObject::ValidateElements(*result);
return *result;
}
RUNTIME_FUNCTION(Runtime_CreateNumberFormat) {
HandleScope scope(isolate);
......
......@@ -259,7 +259,6 @@ namespace internal {
F(GetDefaultICULocale, 0, 1) \
F(InternalCompare, 3, 1) \
F(InternalDateFormat, 2, 1) \
F(InternalDateFormatToParts, 2, 1) \
F(InternalNumberFormat, 2, 1) \
F(IsInitializedIntlObject, 1, 1) \
F(IsInitializedIntlObjectOfType, 2, 1) \
......
......@@ -301,9 +301,6 @@
'language/expressions/arrow-function/dflt-params-duplicates': [FAIL],
'language/expressions/async-arrow-function/dflt-params-duplicates': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=5244
'intl402/NumberFormat/prototype/formatToParts/*': ['--harmony-number-format-to-parts'],
# https://bugs.chromium.org/p/v8/issues/detail?id=5327
'built-ins/TypedArrays/internals/Set/key-is-minus-zero': [FAIL],
'built-ins/TypedArrays/internals/Set/BigInt/key-is-minus-zero': [FAIL],
......
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