[Intl] Create a JSDateTimeFormat and move ResolvedOptions under it.

Bug: v8:8066

Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: I5511b6b9272804ebbb61bf2127a2ad51bfc70e28
Reviewed-on: https://chromium-review.googlesource.com/1179319
Reviewed-by: Georg Neis <neis@chromium.org>
Reviewed-by: Adam Klein <adamk@chromium.org>
Commit-Queue: Frank Tang <ftang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55481}
This commit is contained in:
Frank Tang 2018-08-28 21:40:53 -07:00 committed by Commit Bot
parent 38cbc26a75
commit a279e23ff8
18 changed files with 499 additions and 283 deletions

View File

@ -2185,6 +2185,9 @@ v8_source_set("v8_base") {
"src/objects/js-collator.h",
"src/objects/js-collection-inl.h",
"src/objects/js-collection.h",
"src/objects/js-date-time-format-inl.h",
"src/objects/js-date-time-format.cc",
"src/objects/js-date-time-format.h",
"src/objects/js-generator-inl.h",
"src/objects/js-generator.h",
"src/objects/js-list-format-inl.h",
@ -2895,6 +2898,9 @@ v8_source_set("v8_base") {
"src/objects/js-collator-inl.h",
"src/objects/js-collator.cc",
"src/objects/js-collator.h",
"src/objects/js-date-time-format-inl.h",
"src/objects/js-date-time-format.cc",
"src/objects/js-date-time-format.h",
"src/objects/js-list-format-inl.h",
"src/objects/js-list-format.cc",
"src/objects/js-list-format.h",

View File

@ -208,6 +208,7 @@ Type::bitset BitsetType::Lub(HeapObjectType const& type) {
case JS_DATE_TYPE:
#ifdef V8_INTL_SUPPORT
case JS_INTL_COLLATOR_TYPE:
case JS_INTL_DATE_TIME_FORMAT_TYPE:
case JS_INTL_LIST_FORMAT_TYPE:
case JS_INTL_LOCALE_TYPE:
case JS_INTL_PLURAL_RULES_TYPE:

View File

@ -258,7 +258,6 @@
V(generic_symbol) \
V(home_object_symbol) \
V(intl_initialized_marker_symbol) \
V(intl_pattern_symbol) \
V(intl_resolved_symbol) \
V(interpreter_trampoline_symbol) \
V(megamorphic_symbol) \

View File

@ -34,7 +34,6 @@ var InternalArray = utils.InternalArray;
var MathMax = global.Math.max;
var ObjectHasOwnProperty = global.Object.prototype.hasOwnProperty;
var ObjectKeys = global.Object.keys;
var patternSymbol = utils.ImportNow("intl_pattern_symbol");
var resolvedSymbol = utils.ImportNow("intl_resolved_symbol");
var StringSubstr = GlobalString.prototype.substr;
var StringSubstring = GlobalString.prototype.substring;
@ -155,18 +154,6 @@ function GetAnyExtensionRE() {
return ANY_EXTENSION_RE;
}
/**
* Replace quoted text (single quote, anything but the quote and quote again).
*/
var QUOTED_STRING_RE = UNDEFINED;
function GetQuotedStringRE() {
if (IS_UNDEFINED(QUOTED_STRING_RE)) {
QUOTED_STRING_RE = new GlobalRegExp("'[^']+'", 'g');
}
return QUOTED_STRING_RE;
}
/**
* Matches valid service name.
*/
@ -513,7 +500,6 @@ function getAvailableLocalesOf(service) {
return available;
}
/**
* Defines a property and sets writable, enumerable and configurable to true.
*/
@ -524,18 +510,6 @@ function defineWECProperty(object, property, value) {
configurable: true});
}
/**
* Adds property to an object if the value is not undefined.
* Sets all descriptors to true.
*/
function addWECPropertyIfDefined(object, property, value) {
if (!IS_UNDEFINED(value)) {
defineWECProperty(object, property, value);
}
}
/**
* Returns titlecased word, aMeRricA -> America.
*/
@ -898,85 +872,6 @@ function appendToLDMLString(option, pairs) {
}
}
/**
* Returns object that matches LDML representation of the date.
*/
function fromLDMLString(ldmlString) {
// First remove '' quoted text, so we lose 'Uhr' strings.
ldmlString = %RegExpInternalReplace(GetQuotedStringRE(), ldmlString, '');
var options = {__proto__: null};
var match = %regexp_internal_match(/E{3,5}/, ldmlString);
options = appendToDateTimeObject(
options, 'weekday', match, {EEEEE: 'narrow', EEE: 'short', EEEE: 'long'});
match = %regexp_internal_match(/G{3,5}/, ldmlString);
options = appendToDateTimeObject(
options, 'era', match, {GGGGG: 'narrow', GGG: 'short', GGGG: 'long'});
match = %regexp_internal_match(/y{1,2}/, ldmlString);
options = appendToDateTimeObject(
options, 'year', match, {y: 'numeric', yy: '2-digit'});
match = %regexp_internal_match(/M{1,5}/, ldmlString);
options = appendToDateTimeObject(options, 'month', match, {MM: '2-digit',
M: 'numeric', MMMMM: 'narrow', MMM: 'short', MMMM: 'long'});
// Sometimes we get L instead of M for month - standalone name.
match = %regexp_internal_match(/L{1,5}/, ldmlString);
options = appendToDateTimeObject(options, 'month', match, {LL: '2-digit',
L: 'numeric', LLLLL: 'narrow', LLL: 'short', LLLL: 'long'});
match = %regexp_internal_match(/d{1,2}/, ldmlString);
options = appendToDateTimeObject(
options, 'day', match, {d: 'numeric', dd: '2-digit'});
match = %regexp_internal_match(/h{1,2}/, ldmlString);
if (match !== null) {
options['hour12'] = true;
}
options = appendToDateTimeObject(
options, 'hour', match, {h: 'numeric', hh: '2-digit'});
match = %regexp_internal_match(/H{1,2}/, ldmlString);
if (match !== null) {
options['hour12'] = false;
}
options = appendToDateTimeObject(
options, 'hour', match, {H: 'numeric', HH: '2-digit'});
match = %regexp_internal_match(/m{1,2}/, ldmlString);
options = appendToDateTimeObject(
options, 'minute', match, {m: 'numeric', mm: '2-digit'});
match = %regexp_internal_match(/s{1,2}/, ldmlString);
options = appendToDateTimeObject(
options, 'second', match, {s: 'numeric', ss: '2-digit'});
match = %regexp_internal_match(/z|zzzz/, ldmlString);
options = appendToDateTimeObject(
options, 'timeZoneName', match, {z: 'short', zzzz: 'long'});
return options;
}
function appendToDateTimeObject(options, option, match, pairs) {
if (IS_NULL(match)) {
if (!HAS_OWN_PROPERTY(options, option)) {
%DefineWEProperty(options, option, UNDEFINED);
}
return options;
}
var property = match[0];
%DefineWEProperty(options, option, pairs[property]);
return options;
}
/**
* Returns options with at least default values in it.
*/
@ -1083,35 +978,18 @@ function CreateDateTimeFormat(locales, options) {
getOption, internalOptions);
var requestedLocale = locale.locale + extension;
var resolved = %object_define_properties({__proto__: null}, {
calendar: {writable: true},
day: {writable: true},
era: {writable: true},
hour12: {writable: true},
hour: {writable: true},
locale: {writable: true},
minute: {writable: true},
month: {writable: true},
numberingSystem: {writable: true},
[patternSymbol]: {writable: true},
requestedLocale: {value: requestedLocale, writable: true},
second: {writable: true},
timeZone: {writable: true},
timeZoneName: {writable: true},
tz: {value: tz, writable: true},
weekday: {writable: true},
year: {writable: true}
});
// Still need to store locale and numberingSystem till we move the storage
// to JSDateTimeFormat
var resolved = {__proto__: null};
var dateFormat = %CreateDateTimeFormat(
requestedLocale,
{__proto__: null, skeleton: ldmlString, timeZone: tz}, resolved);
if (resolved.timeZone === "Etc/Unknown") {
throw %make_range_error(kInvalidTimeZone, tz);
}
%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;
@ -1130,59 +1008,13 @@ function DateTimeFormatConstructor() {
}
%SetCode(GlobalIntlDateTimeFormat, DateTimeFormatConstructor);
/**
* DateTimeFormat resolvedOptions method.
*/
DEFINE_METHOD(
GlobalIntlDateTimeFormat.prototype,
resolvedOptions() {
var methodName = 'resolvedOptions';
if(!IS_RECEIVER(this)) {
throw %make_type_error(kIncompatibleMethodReceiver, methodName, this);
}
var format = %IntlUnwrapReceiver(this, DATE_TIME_FORMAT_TYPE,
GlobalIntlDateTimeFormat,
methodName, true);
/**
* Maps ICU calendar names to LDML/BCP47 types for key 'ca'.
* See typeMap section in third_party/icu/source/data/misc/keyTypeData.txt
* and
* http://www.unicode.org/repos/cldr/tags/latest/common/bcp47/calendar.xml
*/
var ICU_CALENDAR_MAP = {
__proto__: null,
'gregorian': 'gregory',
'ethiopic-amete-alem': 'ethioaa'
};
var fromPattern = fromLDMLString(format[resolvedSymbol][patternSymbol]);
var userCalendar = ICU_CALENDAR_MAP[format[resolvedSymbol].calendar];
if (IS_UNDEFINED(userCalendar)) {
// No match means that ICU's legacy name is identical to LDML/BCP type.
userCalendar = format[resolvedSymbol].calendar;
}
var result = {
locale: format[resolvedSymbol].locale,
numberingSystem: format[resolvedSymbol].numberingSystem,
calendar: userCalendar,
timeZone: format[resolvedSymbol].timeZone
};
addWECPropertyIfDefined(result, 'timeZoneName', fromPattern.timeZoneName);
addWECPropertyIfDefined(result, 'era', fromPattern.era);
addWECPropertyIfDefined(result, 'year', fromPattern.year);
addWECPropertyIfDefined(result, 'month', fromPattern.month);
addWECPropertyIfDefined(result, 'day', fromPattern.day);
addWECPropertyIfDefined(result, 'weekday', fromPattern.weekday);
addWECPropertyIfDefined(result, 'hour12', fromPattern.hour12);
addWECPropertyIfDefined(result, 'hour', fromPattern.hour);
addWECPropertyIfDefined(result, 'minute', fromPattern.minute);
addWECPropertyIfDefined(result, 'second', fromPattern.second);
return result;
return %DateTimeFormatResolvedOptions(this);
}
);

View File

@ -727,6 +727,7 @@ ReturnType BodyDescriptorApply(InstanceType type, T1 p1, T2 p2, T3 p3, T4 p4) {
case JS_BOUND_FUNCTION_TYPE:
#ifdef V8_INTL_SUPPORT
case JS_INTL_COLLATOR_TYPE:
case JS_INTL_DATE_TIME_FORMAT_TYPE:
case JS_INTL_LIST_FORMAT_TYPE:
case JS_INTL_LOCALE_TYPE:
case JS_INTL_PLURAL_RULES_TYPE:

View File

@ -15,16 +15,18 @@
#include "src/objects-inl.h"
#include "src/objects/arguments-inl.h"
#include "src/objects/bigint.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-collator-inl.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/data-handler-inl.h"
#include "src/objects/debug-objects-inl.h"
#include "src/objects/hash-table-inl.h"
#include "src/objects/js-array-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-collator-inl.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/js-collection-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-date-time-format-inl.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/js-generator-inl.h"
#include "src/objects/literal-objects-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-list-format-inl.h"
#include "src/objects/js-locale-inl.h"
@ -35,6 +37,7 @@
#include "src/objects/js-plural-rules-inl.h"
#include "src/objects/js-relative-time-format-inl.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/literal-objects-inl.h"
#include "src/objects/maybe-object.h"
#include "src/objects/microtask-inl.h"
#include "src/objects/module-inl.h"
@ -359,6 +362,9 @@ void HeapObject::HeapObjectVerify(Isolate* isolate) {
case JS_INTL_COLLATOR_TYPE:
JSCollator::cast(this)->JSCollatorVerify(isolate);
break;
case JS_INTL_DATE_TIME_FORMAT_TYPE:
JSDateTimeFormat::cast(this)->JSDateTimeFormatVerify(isolate);
break;
case JS_INTL_LIST_FORMAT_TYPE:
JSListFormat::cast(this)->JSListFormatVerify(isolate);
break;
@ -1878,6 +1884,10 @@ void JSCollator::JSCollatorVerify(Isolate* isolate) {
VerifyObjectField(isolate, kBoundCompareOffset);
}
void JSDateTimeFormat::JSDateTimeFormatVerify(Isolate* isolate) {
JSObjectVerify(isolate);
}
void JSListFormat::JSListFormatVerify(Isolate* isolate) {
JSObjectVerify(isolate);
VerifyObjectField(isolate, kLocaleOffset);

View File

@ -217,6 +217,7 @@ namespace internal {
#define INSTANCE_TYPE_LIST(V) \
INSTANCE_TYPE_LIST_BEFORE_INTL(V) \
V(JS_INTL_COLLATOR_TYPE) \
V(JS_INTL_DATE_TIME_FORMAT_TYPE) \
V(JS_INTL_LIST_FORMAT_TYPE) \
V(JS_INTL_LOCALE_TYPE) \
V(JS_INTL_PLURAL_RULES_TYPE) \

View File

@ -14,15 +14,18 @@
#include "src/interpreter/bytecodes.h"
#include "src/objects-inl.h"
#include "src/objects/arguments-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-collator-inl.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/data-handler-inl.h"
#include "src/objects/debug-objects-inl.h"
#include "src/objects/hash-table-inl.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/js-array-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-collator-inl.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/js-collection-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-date-time-format-inl.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/js-generator-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-list-format-inl.h"
@ -312,6 +315,9 @@ void HeapObject::HeapObjectPrint(std::ostream& os) { // NOLINT
case JS_INTL_COLLATOR_TYPE:
JSCollator::cast(this)->JSCollatorPrint(os);
break;
case JS_INTL_DATE_TIME_FORMAT_TYPE:
JSDateTimeFormat::cast(this)->JSDateTimeFormatPrint(os);
break;
case JS_INTL_LIST_FORMAT_TYPE:
JSListFormat::cast(this)->JSListFormatPrint(os);
break;
@ -1960,6 +1966,10 @@ void JSCollator::JSCollatorPrint(std::ostream& os) { // NOLINT
os << "\n";
}
void JSDateTimeFormat::JSDateTimeFormatPrint(std::ostream& os) { // NOLINT
JSObjectPrintHeader(os, this, "JSDateTimeFormat");
}
void JSListFormat::JSListFormatPrint(std::ostream& os) { // NOLINT
JSObjectPrintHeader(os, this, "JSListFormat");
os << "\n - locale: " << Brief(locale());

View File

@ -64,6 +64,9 @@
#include "src/objects/js-collator.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/js-collection-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-date-time-format.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/js-generator-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-list-format.h"
@ -1446,6 +1449,8 @@ int JSObject::GetHeaderSize(InstanceType type,
#ifdef V8_INTL_SUPPORT
case JS_INTL_COLLATOR_TYPE:
return JSCollator::kSize;
case JS_INTL_DATE_TIME_FORMAT_TYPE:
return JSDateTimeFormat::kSize;
case JS_INTL_LIST_FORMAT_TYPE:
return JSListFormat::kSize;
case JS_INTL_LOCALE_TYPE:
@ -3190,6 +3195,7 @@ VisitorId Map::GetVisitorId(Map* map) {
case JS_REGEXP_STRING_ITERATOR_TYPE:
#ifdef V8_INTL_SUPPORT
case JS_INTL_COLLATOR_TYPE:
case JS_INTL_DATE_TIME_FORMAT_TYPE:
case JS_INTL_LIST_FORMAT_TYPE:
case JS_INTL_LOCALE_TYPE:
case JS_INTL_PLURAL_RULES_TYPE:
@ -13130,6 +13136,7 @@ bool CanSubclassHaveInobjectProperties(InstanceType instance_type) {
case JS_GENERATOR_OBJECT_TYPE:
#ifdef V8_INTL_SUPPORT
case JS_INTL_COLLATOR_TYPE:
case JS_INTL_DATE_TIME_FORMAT_TYPE:
case JS_INTL_LIST_FORMAT_TYPE:
case JS_INTL_PLURAL_RULES_TYPE:
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:

View File

@ -76,6 +76,7 @@
// - JSMessageObject
// - JSModuleNamespace
// - JSCollator // If V8_INTL_SUPPORT enabled.
// - JSDateTimeFormat // If V8_INTL_SUPPORT enabled.
// - JSListFormat // If V8_INTL_SUPPORT enabled.
// - JSLocale // If V8_INTL_SUPPORT enabled.
// - JSPluralRules // If V8_INTL_SUPPORT enabled.
@ -583,6 +584,7 @@ enum InstanceType : uint16_t {
#ifdef V8_INTL_SUPPORT
JS_INTL_COLLATOR_TYPE,
JS_INTL_DATE_TIME_FORMAT_TYPE,
JS_INTL_LIST_FORMAT_TYPE,
JS_INTL_LOCALE_TYPE,
JS_INTL_PLURAL_RULES_TYPE,
@ -701,6 +703,7 @@ class JSGlobalObject;
class JSGlobalProxy;
#ifdef V8_INTL_SUPPORT
class JSCollator;
class JSDateTimeFormat;
class JSListFormat;
class JSLocale;
class JSPluralRules;
@ -912,6 +915,7 @@ class ZoneForwardList;
#define HEAP_OBJECT_ORDINARY_TYPE_LIST(V) \
HEAP_OBJECT_ORDINARY_TYPE_LIST_BASE(V) \
V(JSCollator) \
V(JSDateTimeFormat) \
V(JSListFormat) \
V(JSLocale) \
V(JSPluralRules) \
@ -1029,12 +1033,13 @@ class ZoneForwardList;
V(WeakArrayList, WEAK_ARRAY_LIST_TYPE)
#ifdef V8_INTL_SUPPORT
#define INSTANCE_TYPE_CHECKERS_SINGLE(V) \
INSTANCE_TYPE_CHECKERS_SINGLE_BASE(V) \
V(JSCollator, JS_INTL_COLLATOR_TYPE) \
V(JSListFormat, JS_INTL_LIST_FORMAT_TYPE) \
V(JSLocale, JS_INTL_LOCALE_TYPE) \
V(JSPluralRules, JS_INTL_PLURAL_RULES_TYPE) \
#define INSTANCE_TYPE_CHECKERS_SINGLE(V) \
INSTANCE_TYPE_CHECKERS_SINGLE_BASE(V) \
V(JSCollator, JS_INTL_COLLATOR_TYPE) \
V(JSDateTimeFormat, JS_INTL_DATE_TIME_FORMAT_TYPE) \
V(JSListFormat, JS_INTL_LIST_FORMAT_TYPE) \
V(JSLocale, JS_INTL_LOCALE_TYPE) \
V(JSPluralRules, JS_INTL_PLURAL_RULES_TYPE) \
V(JSRelativeTimeFormat, JS_INTL_RELATIVE_TIME_FORMAT_TYPE)
#else

View File

@ -169,66 +169,9 @@ void SetResolvedDateSettings(Isolate* isolate, const icu::Locale& icu_locale,
Handle<JSObject> resolved) {
Factory* factory = isolate->factory();
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString pattern;
date_format->toPattern(pattern);
JSObject::SetProperty(
isolate, resolved, factory->intl_pattern_symbol(),
factory
->NewStringFromTwoByte(Vector<const uint16_t>(
reinterpret_cast<const uint16_t*>(pattern.getBuffer()),
pattern.length()))
.ToHandleChecked(),
LanguageMode::kSloppy)
.Assert();
// Set time zone and calendar.
const icu::Calendar* calendar = date_format->getCalendar();
// getType() returns legacy calendar type name instead of LDML/BCP47 calendar
// key values. intl.js maps them to BCP47 values for key "ca".
// TODO(jshin): Consider doing it here, instead.
const char* calendar_name = calendar->getType();
JSObject::SetProperty(
isolate, resolved, factory->NewStringFromStaticChars("calendar"),
factory->NewStringFromAsciiChecked(calendar_name), LanguageMode::kSloppy)
.Assert();
const icu::TimeZone& tz = calendar->getTimeZone();
icu::UnicodeString time_zone;
tz.getID(time_zone);
icu::UnicodeString canonical_time_zone;
icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status);
if (U_SUCCESS(status)) {
// In CLDR (http://unicode.org/cldr/trac/ticket/9943), Etc/UTC is made
// a separate timezone ID from Etc/GMT even though they're still the same
// 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
if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/UTC") ||
canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) {
JSObject::SetProperty(
isolate, resolved, factory->NewStringFromStaticChars("timeZone"),
factory->NewStringFromStaticChars("UTC"), LanguageMode::kSloppy)
.Assert();
} else {
JSObject::SetProperty(isolate, resolved,
factory->NewStringFromStaticChars("timeZone"),
factory
->NewStringFromTwoByte(Vector<const uint16_t>(
reinterpret_cast<const uint16_t*>(
canonical_time_zone.getBuffer()),
canonical_time_zone.length()))
.ToHandleChecked(),
LanguageMode::kSloppy)
.Assert();
}
}
// 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.
status = U_ZERO_ERROR;
icu::NumberingSystem* numbering_system =
icu::NumberingSystem::createInstance(icu_locale, status);
if (U_SUCCESS(status)) {
@ -600,6 +543,21 @@ icu::Locale Intl::CreateICULocale(Isolate* isolate,
return icu_locale;
}
bool DateFormat::IsValidTimeZone(icu::SimpleDateFormat* date_format) {
UErrorCode status = U_ZERO_ERROR;
// Set time zone and calendar.
const icu::Calendar* calendar = date_format->getCalendar();
const icu::TimeZone& tz = calendar->getTimeZone();
icu::UnicodeString time_zone;
tz.getID(time_zone);
icu::UnicodeString canonical_time_zone;
icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status);
std::string timezone_str;
canonical_time_zone.toUTF8String(timezone_str);
if (U_SUCCESS(status)) return timezone_str != "Etc/Unknown";
return true;
}
// static
icu::SimpleDateFormat* DateFormat::InitializeDateTimeFormat(
Isolate* isolate, Handle<String> locale, Handle<JSObject> options,

View File

@ -45,6 +45,9 @@ class DateFormat {
// Unpacks date format object from corresponding JavaScript object.
static icu::SimpleDateFormat* UnpackDateFormat(Handle<JSObject> obj);
// Determine the TimeZone is valid.
static bool IsValidTimeZone(icu::SimpleDateFormat* date_format);
// 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);

View File

@ -0,0 +1,28 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_INTL_SUPPORT
#error Internationalization is expected to be enabled.
#endif // V8_INTL_SUPPORT
#ifndef V8_OBJECTS_JS_DATE_TIME_FORMAT_INL_H_
#define V8_OBJECTS_JS_DATE_TIME_FORMAT_INL_H_
#include "src/objects-inl.h"
#include "src/objects/js-date-time-format.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
CAST_ACCESSOR(JSDateTimeFormat);
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"
#endif // V8_OBJECTS_JS_DATE_TIME_FORMAT_INL_H_

View File

@ -0,0 +1,274 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_INTL_SUPPORT
#error Internationalization is expected to be enabled.
#endif // V8_INTL_SUPPORT
#include "src/objects/js-date-time-format.h"
#include <memory>
#include <string>
#include <vector>
#include "src/heap/factory.h"
#include "src/isolate.h"
#include "src/objects/intl-objects.h"
#include "src/objects/js-date-time-format-inl.h"
#include "unicode/calendar.h"
#include "unicode/smpdtfmt.h"
#include "unicode/unistr.h"
namespace v8 {
namespace internal {
namespace {
class PatternMap {
public:
PatternMap(std::string pattern, std::string value)
: pattern(pattern), value(value) {}
virtual ~PatternMap() {}
std::string pattern;
std::string value;
};
class PatternItem {
public:
PatternItem(const std::string property, std::vector<PatternMap> pairs,
std::vector<const char*>* allowed_values)
: property(property), pairs(pairs), allowed_values(allowed_values) {}
virtual ~PatternItem() {}
const std::string property;
// It is important for the pattern in the pairs from longer one to shorter one
// if the longer one contains substring of an shorter one.
std::vector<PatternMap> pairs;
std::vector<const char*>* allowed_values;
};
static const std::vector<PatternItem>& GetPatternItems() {
static std::vector<const char*> kLongShort = {"long", "short"};
static std::vector<const char*> kNarrowLongShort = {"narrow", "long",
"short"};
static std::vector<const char*> k2DigitNumeric = {"2-digit", "numeric"};
static std::vector<const char*> kNarrowLongShort2DigitNumeric = {
"narrow", "long", "short", "2-digit", "numeric"};
static std::vector<PatternItem> kPatternItems = {
PatternItem("weekday",
{{"EEEEE", "narrow"}, {"EEEE", "long"}, {"EEE", "short"}},
&kNarrowLongShort),
PatternItem("era",
{{"GGGGG", "narrow"}, {"GGGG", "long"}, {"GGG", "short"}},
&kNarrowLongShort),
PatternItem("year", {{"yy", "2-digit"}, {"y", "numeric"}},
&k2DigitNumeric),
// Sometimes we get L instead of M for month - standalone name.
PatternItem("month",
{{"MMMMM", "narrow"},
{"MMMM", "long"},
{"MMM", "short"},
{"MM", "2-digit"},
{"M", "numeric"},
{"LLLLL", "narrow"},
{"LLLL", "long"},
{"LLL", "short"},
{"LL", "2-digit"},
{"L", "numeric"}},
&kNarrowLongShort2DigitNumeric),
PatternItem("day", {{"dd", "2-digit"}, {"d", "numeric"}},
&k2DigitNumeric),
PatternItem("hour",
{{"HH", "2-digit"},
{"H", "numeric"},
{"hh", "2-digit"},
{"h", "numeric"}},
&k2DigitNumeric),
PatternItem("minute", {{"mm", "2-digit"}, {"m", "numeric"}},
&k2DigitNumeric),
PatternItem("second", {{"ss", "2-digit"}, {"s", "numeric"}},
&k2DigitNumeric),
PatternItem("timeZoneName", {{"zzzz", "long"}, {"z", "short"}},
&kLongShort)};
return kPatternItems;
}
void SetPropertyFromPattern(Isolate* isolate, const std::string& pattern,
Handle<JSObject> options) {
Factory* factory = isolate->factory();
const std::vector<PatternItem>& items = GetPatternItems();
for (auto item = items.cbegin(); item != items.cend(); ++item) {
for (auto pair = item->pairs.cbegin(); pair != item->pairs.cend(); ++pair) {
if (pattern.find(pair->pattern) != std::string::npos) {
// After we find the first pair in the item which matching the pattern,
// we set the property and look for the next item in kPatternItems.
CHECK(JSReceiver::CreateDataProperty(
isolate, options,
factory->NewStringFromAsciiChecked(item->property.c_str()),
factory->NewStringFromAsciiChecked(pair->value.c_str()),
kDontThrow)
.FromJust());
break;
}
}
}
// hour12
// b. If p is "hour12", then
// i. Let hc be dtf.[[HourCycle]].
// ii. If hc is "h11" or "h12", let v be true.
// iii. Else if, hc is "h23" or "h24", let v be false.
// iv. Else, let v be undefined.
if (pattern.find("h") != std::string::npos) {
CHECK(JSReceiver::CreateDataProperty(
isolate, options, factory->NewStringFromStaticChars("hour12"),
factory->true_value(), kDontThrow)
.FromJust());
} else if (pattern.find("H") != std::string::npos) {
CHECK(JSReceiver::CreateDataProperty(
isolate, options, factory->NewStringFromStaticChars("hour12"),
factory->false_value(), kDontThrow)
.FromJust());
}
}
} // namespace
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));
// 4. Let options be ! ObjectCreate(%ObjectPrototype%).
Handle<JSObject> options = factory->NewJSObject(isolate->object_function());
// 5. For each row of Table 6, except the header row, in any order, do
// 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);
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);
// calendar
const icu::Calendar* calendar = icu_simple_date_format->getCalendar();
// getType() returns legacy calendar type name instead of LDML/BCP47 calendar
// key values. intl.js maps them to BCP47 values for key "ca".
// TODO(jshin): Consider doing it here, instead.
std::string calendar_str = calendar->getType();
// Maps ICU calendar names to LDML/BCP47 types for key 'ca'.
// See typeMap section in third_party/icu/source/data/misc/keyTypeData.txt
// and
// http://www.unicode.org/repos/cldr/tags/latest/common/bcp47/calendar.xml
if (calendar_str == "gregorian") {
calendar_str = "gregory";
} else if (calendar_str == "ethiopic-amete-alem") {
calendar_str = "ethioaa";
}
CHECK(JSReceiver::CreateDataProperty(
isolate, options, factory->NewStringFromStaticChars("calendar"),
factory->NewStringFromAsciiChecked(calendar_str.c_str()),
kDontThrow)
.FromJust());
// timezone
const icu::TimeZone& tz = calendar->getTimeZone();
icu::UnicodeString time_zone;
tz.getID(time_zone);
UErrorCode error = U_ZERO_ERROR;
icu::UnicodeString canonical_time_zone;
icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, error);
if (U_SUCCESS(error)) {
Handle<String> timezone_value;
// In CLDR (http://unicode.org/cldr/trac/ticket/9943), Etc/UTC is made
// a separate timezone ID from Etc/GMT even though they're still the same
// 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
if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/UTC") ||
canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) {
timezone_value = factory->NewStringFromAsciiChecked("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);
}
CHECK(JSReceiver::CreateDataProperty(
isolate, options, factory->NewStringFromStaticChars("timeZone"),
timezone_value, kDontThrow)
.FromJust());
} else {
// Somehow on Windows we will reach here.
CHECK(JSReceiver::CreateDataProperty(
isolate, options, factory->NewStringFromStaticChars("timeZone"),
factory->undefined_value(), kDontThrow)
.FromJust());
}
return options;
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,52 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_INTL_SUPPORT
#error Internationalization is expected to be enabled.
#endif // V8_INTL_SUPPORT
#ifndef V8_OBJECTS_JS_DATE_TIME_FORMAT_H_
#define V8_OBJECTS_JS_DATE_TIME_FORMAT_H_
#include "src/isolate.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace U_ICU_NAMESPACE {
class SimpleDateFormat;
}
namespace v8 {
namespace internal {
class JSDateTimeFormat : public JSObject {
public:
V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> ResolvedOptions(
Isolate* isolate, Handle<JSReceiver> date_time_holder);
DECL_CAST(JSDateTimeFormat)
// Layout description.
#define JS_DATE_TIME_FORMAT_FIELDS(V) \
/* Total size. */ \
V(kSize, 0)
DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize,
JS_DATE_TIME_FORMAT_FIELDS)
#undef JS_DATE_TIME_FORMAT_FIELDS
DECL_PRINTER(JSDateTimeFormat)
DECL_VERIFIER(JSDateTimeFormat)
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSDateTimeFormat);
};
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"
#endif // V8_OBJECTS_JS_DATE_TIME_FORMAT_H_

View File

@ -22,6 +22,7 @@
#include "src/objects/intl-objects.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-collator-inl.h"
#include "src/objects/js-date-time-format-inl.h"
#include "src/objects/js-list-format-inl.h"
#include "src/objects/js-list-format.h"
#include "src/objects/js-plural-rules-inl.h"
@ -217,6 +218,13 @@ RUNTIME_FUNCTION(Runtime_CreateDateTimeFormat) {
icu::SimpleDateFormat* date_format =
DateFormat::InitializeDateTimeFormat(isolate, locale, options, resolved);
CHECK_NOT_NULL(date_format);
if (!DateFormat::IsValidTimeZone(date_format)) {
delete date_format;
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewRangeError(MessageTemplate::kInvalidTimeZone,
isolate->factory()->NewStringFromStaticChars("Etc/GMT")));
}
local_object->SetEmbedderField(DateFormat::kSimpleDateFormatIndex,
reinterpret_cast<Smi*>(date_format));
@ -229,6 +237,25 @@ RUNTIME_FUNCTION(Runtime_CreateDateTimeFormat) {
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(
"Intl.DateTimeFormat.prototype.resolvedOptions");
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
method_str, dtf));
}
Handle<JSReceiver> date_format_holder = Handle<JSReceiver>::cast(dtf);
RETURN_RESULT_OR_FAILURE(
isolate, JSDateTimeFormat::ResolvedOptions(isolate, date_format_holder));
}
RUNTIME_FUNCTION(Runtime_CreateNumberFormat) {
HandleScope scope(isolate);

View File

@ -208,6 +208,7 @@ namespace internal {
F(CreateNumberFormat, 3, 1) \
F(CurrencyDigits, 1, 1) \
F(DateCacheVersion, 0, 1) \
F(DateTimeFormatResolvedOptions, 1, 1) \
F(DefaultNumberOption, 5, 1) \
F(DefineWEProperty, 3, 1) \
F(FormatList, 2, 1) \

View File

@ -159,17 +159,18 @@ INSTANCE_TYPES = {
1082: "JS_TYPED_ARRAY_TYPE",
1083: "JS_DATA_VIEW_TYPE",
1084: "JS_INTL_COLLATOR_TYPE",
1085: "JS_INTL_LIST_FORMAT_TYPE",
1086: "JS_INTL_LOCALE_TYPE",
1087: "JS_INTL_PLURAL_RULES_TYPE",
1088: "JS_INTL_RELATIVE_TIME_FORMAT_TYPE",
1089: "WASM_GLOBAL_TYPE",
1090: "WASM_INSTANCE_TYPE",
1091: "WASM_MEMORY_TYPE",
1092: "WASM_MODULE_TYPE",
1093: "WASM_TABLE_TYPE",
1094: "JS_BOUND_FUNCTION_TYPE",
1095: "JS_FUNCTION_TYPE",
1085: "JS_INTL_DATE_TIME_FORMAT_TYPE",
1086: "JS_INTL_LIST_FORMAT_TYPE",
1087: "JS_INTL_LOCALE_TYPE",
1088: "JS_INTL_PLURAL_RULES_TYPE",
1089: "JS_INTL_RELATIVE_TIME_FORMAT_TYPE",
1090: "WASM_GLOBAL_TYPE",
1091: "WASM_INSTANCE_TYPE",
1092: "WASM_MEMORY_TYPE",
1093: "WASM_MODULE_TYPE",
1094: "WASM_TABLE_TYPE",
1095: "JS_BOUND_FUNCTION_TYPE",
1096: "JS_FUNCTION_TYPE",
}
# List of known V8 maps.
@ -284,32 +285,32 @@ KNOWN_MAPS = {
("RO_SPACE", 0x04811): (173, "ArrayBoilerplateDescriptionMap"),
("RO_SPACE", 0x04b01): (161, "InterceptorInfoMap"),
("RO_SPACE", 0x04bf9): (169, "ScriptMap"),
("RO_SPACE", 0x09aa1): (154, "AccessorInfoMap"),
("RO_SPACE", 0x09af1): (153, "AccessCheckInfoMap"),
("RO_SPACE", 0x09b41): (155, "AccessorPairMap"),
("RO_SPACE", 0x09b91): (156, "AliasedArgumentsEntryMap"),
("RO_SPACE", 0x09be1): (157, "AllocationMementoMap"),
("RO_SPACE", 0x09c31): (158, "AsyncGeneratorRequestMap"),
("RO_SPACE", 0x09c81): (159, "DebugInfoMap"),
("RO_SPACE", 0x09cd1): (160, "FunctionTemplateInfoMap"),
("RO_SPACE", 0x09d21): (162, "InterpreterDataMap"),
("RO_SPACE", 0x09d71): (163, "ModuleInfoEntryMap"),
("RO_SPACE", 0x09dc1): (164, "ModuleMap"),
("RO_SPACE", 0x09e11): (165, "ObjectTemplateInfoMap"),
("RO_SPACE", 0x09e61): (166, "PromiseCapabilityMap"),
("RO_SPACE", 0x09eb1): (167, "PromiseReactionMap"),
("RO_SPACE", 0x09f01): (168, "PrototypeInfoMap"),
("RO_SPACE", 0x09f51): (170, "StackFrameInfoMap"),
("RO_SPACE", 0x09fa1): (172, "Tuple3Map"),
("RO_SPACE", 0x09ff1): (174, "WasmDebugInfoMap"),
("RO_SPACE", 0x0a041): (175, "WasmExportedFunctionDataMap"),
("RO_SPACE", 0x0a091): (176, "CallableTaskMap"),
("RO_SPACE", 0x0a0e1): (177, "CallbackTaskMap"),
("RO_SPACE", 0x0a131): (178, "PromiseFulfillReactionJobTaskMap"),
("RO_SPACE", 0x0a181): (179, "PromiseRejectReactionJobTaskMap"),
("RO_SPACE", 0x0a1d1): (180, "PromiseResolveThenableJobTaskMap"),
("RO_SPACE", 0x0a221): (181, "AllocationSiteMap"),
("RO_SPACE", 0x0a271): (181, "AllocationSiteMap"),
("RO_SPACE", 0x09a81): (154, "AccessorInfoMap"),
("RO_SPACE", 0x09ad1): (153, "AccessCheckInfoMap"),
("RO_SPACE", 0x09b21): (155, "AccessorPairMap"),
("RO_SPACE", 0x09b71): (156, "AliasedArgumentsEntryMap"),
("RO_SPACE", 0x09bc1): (157, "AllocationMementoMap"),
("RO_SPACE", 0x09c11): (158, "AsyncGeneratorRequestMap"),
("RO_SPACE", 0x09c61): (159, "DebugInfoMap"),
("RO_SPACE", 0x09cb1): (160, "FunctionTemplateInfoMap"),
("RO_SPACE", 0x09d01): (162, "InterpreterDataMap"),
("RO_SPACE", 0x09d51): (163, "ModuleInfoEntryMap"),
("RO_SPACE", 0x09da1): (164, "ModuleMap"),
("RO_SPACE", 0x09df1): (165, "ObjectTemplateInfoMap"),
("RO_SPACE", 0x09e41): (166, "PromiseCapabilityMap"),
("RO_SPACE", 0x09e91): (167, "PromiseReactionMap"),
("RO_SPACE", 0x09ee1): (168, "PrototypeInfoMap"),
("RO_SPACE", 0x09f31): (170, "StackFrameInfoMap"),
("RO_SPACE", 0x09f81): (172, "Tuple3Map"),
("RO_SPACE", 0x09fd1): (174, "WasmDebugInfoMap"),
("RO_SPACE", 0x0a021): (175, "WasmExportedFunctionDataMap"),
("RO_SPACE", 0x0a071): (176, "CallableTaskMap"),
("RO_SPACE", 0x0a0c1): (177, "CallbackTaskMap"),
("RO_SPACE", 0x0a111): (178, "PromiseFulfillReactionJobTaskMap"),
("RO_SPACE", 0x0a161): (179, "PromiseRejectReactionJobTaskMap"),
("RO_SPACE", 0x0a1b1): (180, "PromiseResolveThenableJobTaskMap"),
("RO_SPACE", 0x0a201): (181, "AllocationSiteMap"),
("RO_SPACE", 0x0a251): (181, "AllocationSiteMap"),
("MAP_SPACE", 0x02201): (1057, "ExternalMap"),
("MAP_SPACE", 0x02251): (1072, "JSMessageObjectMap"),
}