mirror of
https://github.com/roytam1/basilisk55.git
synced 2026-05-26 15:02:46 +00:00
ported from UXP: Issue #1969 - Implement Intl.RelativeTimeFormat. (86f540b6)
This commit is contained in:
@@ -87,12 +87,14 @@ included_inclnames_to_ignore = set([
|
||||
'unicode/ucol.h', # ICU
|
||||
'unicode/udat.h', # ICU
|
||||
'unicode/udatpg.h', # ICU
|
||||
'unicode/udisplaycontext.h',# ICU
|
||||
'unicode/uenum.h', # ICU
|
||||
'unicode/uniset.h', # ICU
|
||||
'unicode/unorm2.h', # ICU
|
||||
'unicode/unum.h', # ICU
|
||||
'unicode/unumsys.h', # ICU
|
||||
'unicode/upluralrules.h', # ICU
|
||||
'unicode/ureldatefmt.h', # ICU
|
||||
'unicode/ustring.h', # ICU
|
||||
'unicode/utypes.h', # ICU
|
||||
'vtune/VTuneWrapper.h' # VTune
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
Workaround for https://unicode-org.atlassian.net/browse/ICU-20253
|
||||
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1504656
|
||||
|
||||
diff --git a/intl/icu/source/i18n/reldatefmt.cpp b/intl/icu/source/i18n/reldatefmt.cpp
|
||||
--- a/intl/icu/source/i18n/reldatefmt.cpp
|
||||
+++ b/intl/icu/source/i18n/reldatefmt.cpp
|
||||
@@ -157,24 +157,30 @@ const UnicodeString& RelativeDateTimeCac
|
||||
}
|
||||
|
||||
// Use fallback cache for SimpleFormatter relativeUnits.
|
||||
const SimpleFormatter* RelativeDateTimeCacheData::getRelativeDateTimeUnitFormatter(
|
||||
int32_t fStyle,
|
||||
URelativeDateTimeUnit unit,
|
||||
int32_t pastFutureIndex,
|
||||
int32_t pluralUnit) const {
|
||||
- int32_t style = fStyle;
|
||||
- do {
|
||||
- if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != nullptr) {
|
||||
- return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit];
|
||||
+ while (true) {
|
||||
+ int32_t style = fStyle;
|
||||
+ do {
|
||||
+ if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != nullptr) {
|
||||
+ return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit];
|
||||
+ }
|
||||
+ style = fallBackCache[style];
|
||||
+ } while (style != -1);
|
||||
+
|
||||
+ if (pluralUnit == StandardPlural::OTHER) {
|
||||
+ return nullptr; // No formatter found.
|
||||
}
|
||||
- style = fallBackCache[style];
|
||||
- } while (style != -1);
|
||||
- return nullptr; // No formatter found.
|
||||
+ pluralUnit = StandardPlural::OTHER;
|
||||
+ }
|
||||
}
|
||||
|
||||
static UBool getStringWithFallback(
|
||||
const UResourceBundle *resource,
|
||||
const char *key,
|
||||
UnicodeString &result,
|
||||
UErrorCode &status) {
|
||||
int32_t len = 0;
|
||||
+3
-2
@@ -7,11 +7,11 @@ set -e
|
||||
|
||||
# Update to an ICU release:
|
||||
# Usage: update-icu.sh <URL of ICU GIT> <release tag name>
|
||||
# E.g., for ICU 63.2: update-icu.sh https://github.com/unicode-org/icu.git release-63-2
|
||||
# E.g., for ICU 62.2: update-icu.sh https://github.com/unicode-org/icu.git release-62-2
|
||||
#
|
||||
# Update to an ICU maintenance branch:
|
||||
# Usage: update-icu.sh <URL of ICU GIT> <maintenance name>
|
||||
# E.g., for ICU 63.2: update-icu.sh https://github.com/unicode-org/icu.git maint/maint-63
|
||||
# E.g., for ICU 62.2: update-icu.sh https://github.com/unicode-org/icu.git maint/maint-62
|
||||
|
||||
if [ $# -lt 2 ]; then
|
||||
echo "Usage: update-icu.sh <URL of ICU GIT> <release tag name>"
|
||||
@@ -86,6 +86,7 @@ for patch in \
|
||||
suppress-warnings.diff \
|
||||
bug-1172609-timezone-recreateDefault.diff \
|
||||
bug-1198952-workaround-make-3.82-bug.diff \
|
||||
bug-1504656-relativetimeformat-plural-other-fallback.diff \
|
||||
; do
|
||||
echo "Applying local patch $patch"
|
||||
patch -d ${icu_dir}/../../ -p1 --no-backup-if-mismatch < ${icu_dir}/../icu-patches/$patch
|
||||
|
||||
+1
-1
@@ -852,7 +852,7 @@ struct JSClass {
|
||||
// application.
|
||||
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
|
||||
#define JSCLASS_GLOBAL_SLOT_COUNT \
|
||||
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 45)
|
||||
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 46)
|
||||
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
||||
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
||||
#define JSCLASS_GLOBAL_FLAGS \
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "builtin/Intl.h"
|
||||
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/Range.h"
|
||||
|
||||
@@ -31,10 +32,12 @@
|
||||
#include "unicode/ucol.h"
|
||||
#include "unicode/udat.h"
|
||||
#include "unicode/udatpg.h"
|
||||
#include "unicode/udisplaycontext.h"
|
||||
#include "unicode/uenum.h"
|
||||
#include "unicode/unum.h"
|
||||
#include "unicode/unumsys.h"
|
||||
#include "unicode/upluralrules.h"
|
||||
#include "unicode/ureldatefmt.h"
|
||||
#include "unicode/ustring.h"
|
||||
#include "vm/DateTime.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
@@ -3192,6 +3195,303 @@ js::intl_GetPluralCategories(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**************** RelativeTimeFormat *****************/
|
||||
|
||||
static void relativeTimeFormat_finalize(FreeOp* fop, JSObject* obj);
|
||||
|
||||
static const uint32_t URELATIVE_TIME_FORMAT_SLOT = 0;
|
||||
static const uint32_t RELATIVE_TIME_FORMAT_SLOTS_COUNT = 1;
|
||||
|
||||
static const ClassOps RelativeTimeFormatClassOps = {
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
nullptr, /* getProperty */
|
||||
nullptr, /* enumerate */
|
||||
nullptr, /* newEnumerate */
|
||||
nullptr, /* resolve */
|
||||
nullptr, /* mayResolve */
|
||||
relativeTimeFormat_finalize
|
||||
};
|
||||
|
||||
static const Class RelativeTimeFormatClass = {
|
||||
js_Object_str,
|
||||
JSCLASS_HAS_RESERVED_SLOTS(RELATIVE_TIME_FORMAT_SLOTS_COUNT) |
|
||||
JSCLASS_FOREGROUND_FINALIZE,
|
||||
&RelativeTimeFormatClassOps
|
||||
};
|
||||
|
||||
#if JS_HAS_TOSOURCE
|
||||
static bool
|
||||
relativeTimeFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setString(cx->names().RelativeTimeFormat);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const JSFunctionSpec relativeTimeFormat_static_methods[] = {
|
||||
JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_RelativeTimeFormat_supportedLocalesOf", 1, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static const JSFunctionSpec relativeTimeFormat_methods[] = {
|
||||
JS_SELF_HOSTED_FN("resolvedOptions", "Intl_RelativeTimeFormat_resolvedOptions", 0, 0),
|
||||
JS_SELF_HOSTED_FN("format", "Intl_RelativeTimeFormat_format", 2, 0),
|
||||
#if JS_HAS_TOSOURCE
|
||||
JS_FN(js_toSource_str, relativeTimeFormat_toSource, 0, 0),
|
||||
#endif
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static const JSPropertySpec relativeTimeFormat_properties[] = {
|
||||
JS_STRING_SYM_PS(toStringTag, "Intl.RelativeTimeFormat", JSPROP_READONLY),
|
||||
JS_PS_END};
|
||||
|
||||
/**
|
||||
* RelativeTimeFormat constructor.
|
||||
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.1
|
||||
*/
|
||||
static bool
|
||||
RelativeTimeFormat(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
// Step 1.
|
||||
if (!ThrowIfNotConstructing(cx, args, "Intl.RelativeTimeFormat"))
|
||||
return false;
|
||||
|
||||
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||
RootedObject proto(cx);
|
||||
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreateRelativeTimeFormatPrototype(cx, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject relativeTimeFormat(cx);
|
||||
relativeTimeFormat = NewObjectWithGivenProto(cx, &RelativeTimeFormatClass, proto);
|
||||
if (!relativeTimeFormat)
|
||||
return false;
|
||||
|
||||
relativeTimeFormat->as<NativeObject>().setReservedSlot(URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
|
||||
|
||||
RootedValue locales(cx, args.get(0));
|
||||
RootedValue options(cx, args.get(1));
|
||||
|
||||
// Step 3.
|
||||
if (!IntlInitialize(cx, relativeTimeFormat, cx->names().InitializeRelativeTimeFormat, locales, options))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*relativeTimeFormat);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
relativeTimeFormat_finalize(FreeOp* fop, JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(fop->onMainThread());
|
||||
|
||||
// This is-undefined check shouldn't be necessary, but for internal
|
||||
// brokenness in object allocation code. For the moment, hack around it by
|
||||
// explicitly guarding against the possibility of the reserved slot not
|
||||
// containing a private. See bug 949220.
|
||||
const Value& slot = obj->as<NativeObject>().getReservedSlot(URELATIVE_TIME_FORMAT_SLOT);
|
||||
if (!slot.isUndefined()) {
|
||||
if (URelativeDateTimeFormatter* rtf = static_cast<URelativeDateTimeFormatter*>(slot.toPrivate()))
|
||||
ureldatefmt_close(rtf);
|
||||
}
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
CreateRelativeTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
|
||||
{
|
||||
RootedFunction ctor(cx);
|
||||
ctor = global->createConstructor(cx, &RelativeTimeFormat, cx->names().RelativeTimeFormat, 0);
|
||||
if (!ctor)
|
||||
return nullptr;
|
||||
|
||||
RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &RelativeTimeFormatClass));
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
proto->setReservedSlot(URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||
return nullptr;
|
||||
|
||||
if (!JS_DefineFunctions(cx, ctor, relativeTimeFormat_static_methods))
|
||||
return nullptr;
|
||||
|
||||
if (!JS_DefineFunctions(cx, proto, relativeTimeFormat_methods))
|
||||
return nullptr;
|
||||
|
||||
if (!JS_DefineProperties(cx, proto, relativeTimeFormat_properties))
|
||||
return nullptr;
|
||||
|
||||
RootedValue options(cx);
|
||||
if (!CreateDefaultOptions(cx, &options))
|
||||
return nullptr;
|
||||
|
||||
if (!IntlInitialize(cx, proto, cx->names().InitializeRelativeTimeFormat, UndefinedHandleValue,
|
||||
options))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||
if (!DefineProperty(cx, Intl, cx->names().RelativeTimeFormat, ctorValue, nullptr, nullptr, 0)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_RelativeTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 0);
|
||||
|
||||
RootedValue result(cx);
|
||||
// We're going to use ULocale availableLocales as per ICU recommendation:
|
||||
// https://ssl.icu-project.org/trac/ticket/12756
|
||||
if (!intl_availableLocales(cx, uloc_countAvailable, uloc_getAvailable, &result))
|
||||
return false;
|
||||
args.rval().set(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
enum class RelativeTimeNumeric
|
||||
{
|
||||
/**
|
||||
* Only strings with numeric components like `1 day ago`.
|
||||
*/
|
||||
Always,
|
||||
/**
|
||||
* Natural-language strings like `yesterday` when possible,
|
||||
* otherwise strings with numeric components as in `7 months ago`.
|
||||
*/
|
||||
Auto,
|
||||
};
|
||||
|
||||
bool
|
||||
js::intl_FormatRelativeTime(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 3);
|
||||
|
||||
RootedObject relativeTimeFormat(cx, &args[0].toObject());
|
||||
|
||||
RootedObject internals(cx, GetInternals(cx, relativeTimeFormat));
|
||||
if (!internals)
|
||||
return false;
|
||||
|
||||
RootedValue value(cx);
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
|
||||
return false;
|
||||
JSAutoByteString locale(cx, value.toString());
|
||||
if (!locale)
|
||||
return false;
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().style, &value))
|
||||
return false;
|
||||
RootedLinearString style(cx, value.toString()->ensureLinear(cx));
|
||||
if (!style)
|
||||
return false;
|
||||
|
||||
double t = args[1].toNumber();
|
||||
|
||||
UDateRelativeDateTimeFormatterStyle relDateTimeStyle;
|
||||
|
||||
if (StringEqualsAscii(style, "short")) {
|
||||
relDateTimeStyle = UDAT_STYLE_SHORT;
|
||||
} else if (StringEqualsAscii(style, "narrow")) {
|
||||
relDateTimeStyle = UDAT_STYLE_NARROW;
|
||||
} else {
|
||||
MOZ_ASSERT(StringEqualsAscii(style, "long"));
|
||||
relDateTimeStyle = UDAT_STYLE_LONG;
|
||||
}
|
||||
|
||||
URelativeDateTimeUnit relDateTimeUnit;
|
||||
{
|
||||
JSLinearString* unit = args[2].toString()->ensureLinear(cx);
|
||||
if (!unit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StringEqualsAscii(unit, "second") || StringEqualsAscii(unit, "seconds")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_SECOND;
|
||||
} else if (StringEqualsAscii(unit, "minute") || StringEqualsAscii(unit, "minutes")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_MINUTE;
|
||||
} else if (StringEqualsAscii(unit, "hour") || StringEqualsAscii(unit, "hours")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_HOUR;
|
||||
} else if (StringEqualsAscii(unit, "day") || StringEqualsAscii(unit, "days")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_DAY;
|
||||
} else if (StringEqualsAscii(unit, "week") || StringEqualsAscii(unit, "weeks")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_WEEK;
|
||||
} else if (StringEqualsAscii(unit, "month") || StringEqualsAscii(unit, "months")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_MONTH;
|
||||
} else if (StringEqualsAscii(unit, "quarter") || StringEqualsAscii(unit, "quarters")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_QUARTER;
|
||||
} else {
|
||||
MOZ_ASSERT(StringEqualsAscii(unit, "year") || StringEqualsAscii(unit, "years"));
|
||||
relDateTimeUnit = UDAT_REL_UNIT_YEAR;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().numeric, &value))
|
||||
return false;
|
||||
RootedLinearString numeric(cx, value.toString()->ensureLinear(cx));
|
||||
if (!numeric)
|
||||
return false;
|
||||
|
||||
RelativeTimeNumeric relDateTimeNumeric;
|
||||
|
||||
if (StringEqualsAscii(numeric, "auto")) {
|
||||
relDateTimeNumeric = RelativeTimeNumeric::Auto;
|
||||
} else {
|
||||
MOZ_ASSERT(StringEqualsAscii(numeric, "always"));
|
||||
relDateTimeNumeric = RelativeTimeNumeric::Always;
|
||||
}
|
||||
|
||||
Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
|
||||
if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
|
||||
return false;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
URelativeDateTimeFormatter* rtf =
|
||||
ureldatefmt_open(icuLocale(locale.ptr()), nullptr, relDateTimeStyle,
|
||||
UDISPCTX_CAPITALIZATION_FOR_STANDALONE, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t size;
|
||||
|
||||
if (relDateTimeNumeric == RelativeTimeNumeric::Auto) {
|
||||
size = ureldatefmt_format(rtf, t, relDateTimeUnit, Char16ToUChar(chars.begin()),
|
||||
INITIAL_CHAR_BUFFER_SIZE, &status);
|
||||
} else {
|
||||
MOZ_ASSERT(relDateTimeNumeric == RelativeTimeNumeric::Always);
|
||||
size = ureldatefmt_formatNumeric(rtf, t, relDateTimeUnit, Char16ToUChar(chars.begin()),
|
||||
INITIAL_CHAR_BUFFER_SIZE, &status);
|
||||
}
|
||||
|
||||
ScopedICUObject<URelativeDateTimeFormatter, ureldatefmt_close> closeRelativeTimeFormat(rtf);
|
||||
|
||||
JSString* str = NewStringCopyN<CanGC>(cx, chars.begin(), size);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
args.rval().setString(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
@@ -3682,6 +3982,10 @@ GlobalObject::initIntlObject(JSContext* cx, Handle<GlobalObject*> global)
|
||||
RootedObject numberFormatProto(cx, CreateNumberFormatPrototype(cx, intl, global));
|
||||
if (!numberFormatProto)
|
||||
return false;
|
||||
RootedObject relativeTimeFmtProto(cx, CreateRelativeTimeFormatPrototype(cx, intl, global));
|
||||
if (!relativeTimeFmtProto) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The |Intl| object is fully set up now, so define the global property.
|
||||
RootedValue intlValue(cx, ObjectValue(*intl));
|
||||
@@ -3703,6 +4007,7 @@ GlobalObject::initIntlObject(JSContext* cx, Handle<GlobalObject*> global)
|
||||
global->setReservedSlot(COLLATOR_PROTO, ObjectValue(*collatorProto));
|
||||
global->setReservedSlot(DATE_TIME_FORMAT_PROTO, ObjectValue(*dateTimeFormatProto));
|
||||
global->setReservedSlot(NUMBER_FORMAT_PROTO, ObjectValue(*numberFormatProto));
|
||||
global->setReservedSlot(RELATIVE_TIME_FORMAT_PROTO, ObjectValue(*relativeTimeFmtProto));
|
||||
|
||||
// Also cache |Intl| to implement spec language that conditions behavior
|
||||
// based on values being equal to "the standard built-in |Intl| object".
|
||||
|
||||
@@ -399,6 +399,31 @@ intl_SelectPluralRule(JSContext* cx, unsigned argc, Value* vp);
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_GetPluralCategories(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/******************** RelativeTimeFormat ********************/
|
||||
|
||||
/**
|
||||
* Returns an object indicating the supported locales for relative time format
|
||||
* by having a true-valued property for each such locale with the
|
||||
* canonicalized language tag as the property name. The object has no
|
||||
* prototype.
|
||||
*
|
||||
* Usage: availableLocales = intl_RelativeTimeFormat_availableLocales()
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_RelativeTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Returns a relative time as a string formatted according to the effective
|
||||
* locale and the formatting options of the given RelativeTimeFormat.
|
||||
*
|
||||
* t should be a number representing a number to be formatted.
|
||||
* unit should be "second", "minute", "hour", "day", "week", "month", "quarter", or "year".
|
||||
*
|
||||
* Usage: formatted = intl_FormatRelativeTime(relativeTimeFormat, t, unit)
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_FormatRelativeTime(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Returns a plain object with calendar information for a single valid locale
|
||||
* (callers must perform this validation). The object will have these
|
||||
|
||||
+260
-9
@@ -23,6 +23,7 @@
|
||||
intl_FormatDateTime: false,
|
||||
intl_SelectPluralRule: false,
|
||||
intl_GetPluralCategories: false,
|
||||
intl_FormatRelativeTime: false,
|
||||
intl_GetCalendarInfo: false,
|
||||
*/
|
||||
|
||||
@@ -1279,7 +1280,8 @@ function setLazyData(internals, type, lazyData)
|
||||
{
|
||||
assert(internals.type === "partial", "can't set lazy data for anything but a newborn");
|
||||
assert(type === "Collator" || type === "DateTimeFormat" ||
|
||||
type == "NumberFormat" || type === "PluralRules",
|
||||
type === "NumberFormat" || type === "PluralRules" ||
|
||||
type === "RelativeTimeFormat",
|
||||
"bad type");
|
||||
assert(IsObject(lazyData), "non-object lazy data");
|
||||
|
||||
@@ -1331,7 +1333,8 @@ function isInitializedIntlObject(obj) {
|
||||
assert(callFunction(std_Object_hasOwnProperty, internals, "type"), "missing type");
|
||||
var type = internals.type;
|
||||
assert(type === "partial" || type === "Collator" ||
|
||||
type === "DateTimeFormat" || type === "NumberFormat" || type === "PluralRules",
|
||||
type === "DateTimeFormat" || type === "NumberFormat" ||
|
||||
type === "PluralRules" || type === "RelativeTimeFormat",
|
||||
"unexpected type");
|
||||
assert(callFunction(std_Object_hasOwnProperty, internals, "lazyData"), "missing lazyData");
|
||||
assert(callFunction(std_Object_hasOwnProperty, internals, "internalProps"), "missing internalProps");
|
||||
@@ -1385,14 +1388,24 @@ function getInternals(obj)
|
||||
|
||||
var internalProps;
|
||||
var type = internals.type;
|
||||
if (type === "Collator")
|
||||
internalProps = resolveCollatorInternals(lazyData)
|
||||
else if (type === "DateTimeFormat")
|
||||
internalProps = resolveDateTimeFormatInternals(lazyData)
|
||||
else if (type === "PluralRules")
|
||||
internalProps = resolvePluralRulesInternals(lazyData)
|
||||
else
|
||||
|
||||
switch (type) {
|
||||
case "Collator":
|
||||
internalProps = resolveCollatorInternals(lazyData);
|
||||
break;
|
||||
case "DateTimeFormat":
|
||||
internalProps = resolveDateTimeFormatInternals(lazyData);
|
||||
break;
|
||||
case "PluralRules":
|
||||
internalProps = resolvePluralRulesInternals(lazyData);
|
||||
break;
|
||||
case "RelativeTimeFormat":
|
||||
internalProps = resolveRelativeTimeFormatInternals(lazyData);
|
||||
break;
|
||||
default: // type === "NumberFormat"
|
||||
internalProps = resolveNumberFormatInternals(lazyData);
|
||||
break;
|
||||
}
|
||||
setInternalProperties(internals, internalProps);
|
||||
return internalProps;
|
||||
}
|
||||
@@ -3206,6 +3219,244 @@ function Intl_PluralRules_resolvedOptions() {
|
||||
}
|
||||
|
||||
|
||||
/********** Intl.RelativeTimeFormat **********/
|
||||
|
||||
/**
|
||||
* RelativeTimeFormat internal properties.
|
||||
*
|
||||
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.3.3.
|
||||
*/
|
||||
var relativeTimeFormatInternalProperties = {
|
||||
localeData: relativeTimeFormatLocaleData,
|
||||
_availableLocales: null,
|
||||
availableLocales: function() // eslint-disable-line object-shorthand
|
||||
{
|
||||
var locales = this._availableLocales;
|
||||
if (locales)
|
||||
return locales;
|
||||
|
||||
locales = intl_RelativeTimeFormat_availableLocales();
|
||||
addSpecialMissingLanguageTags(locales);
|
||||
return (this._availableLocales = locales);
|
||||
},
|
||||
relevantExtensionKeys: [],
|
||||
};
|
||||
|
||||
function relativeTimeFormatLocaleData() {
|
||||
// RelativeTimeFormat doesn't support any extension keys.
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute an internal properties object from |lazyRelativeTimeFormatData|.
|
||||
*/
|
||||
function resolveRelativeTimeFormatInternals(lazyRelativeTimeFormatData) {
|
||||
assert(IsObject(lazyRelativeTimeFormatData), "lazy data not an object?");
|
||||
|
||||
var internalProps = std_Object_create(null);
|
||||
|
||||
var RelativeTimeFormat = relativeTimeFormatInternalProperties;
|
||||
|
||||
// Steps 7-8.
|
||||
const r = ResolveLocale(callFunction(RelativeTimeFormat.availableLocales, RelativeTimeFormat),
|
||||
lazyRelativeTimeFormatData.requestedLocales,
|
||||
lazyRelativeTimeFormatData.opt,
|
||||
RelativeTimeFormat.relevantExtensionKeys,
|
||||
RelativeTimeFormat.localeData);
|
||||
|
||||
// Step 9-10.
|
||||
internalProps.locale = r.locale;
|
||||
|
||||
// Step 11.
|
||||
assert(r.locale === r.dataLocale,
|
||||
"resolved locale matches the resolved data-locale when no extension-keys are present");
|
||||
|
||||
// Step 13.
|
||||
internalProps.style = lazyRelativeTimeFormatData.style;
|
||||
|
||||
// Step 15.
|
||||
internalProps.numeric = lazyRelativeTimeFormatData.numeric;
|
||||
|
||||
// Steps 16-20 (Not relevant in our implementation).
|
||||
|
||||
return internalProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object containing the RelativeTimeFormat internal properties of |obj|,
|
||||
* or throws a TypeError if |obj| isn't RelativeTimeFormat-initialized.
|
||||
*/
|
||||
function getRelativeTimeFormatInternals(obj, methodName) {
|
||||
var internals = getIntlObjectInternals(obj, "RelativeTimeFormat", methodName);
|
||||
assert(internals.type === "RelativeTimeFormat", "bad type escaped getIntlObjectInternals");
|
||||
|
||||
var internalProps = maybeInternalProperties(internals);
|
||||
if (internalProps)
|
||||
return internalProps;
|
||||
|
||||
internalProps = resolveRelativeTimeFormatInternals(internals.lazyData);
|
||||
setInternalProperties(internals, internalProps);
|
||||
return internalProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes an object as a RelativeTimeFormat.
|
||||
*
|
||||
* This method is complicated a moderate bit by its implementing initialization
|
||||
* as a *lazy* concept. Everything that must happen now, does -- but we defer
|
||||
* all the work we can until the object is actually used as a RelativeTimeFormat.
|
||||
* This later work occurs in |resolveRelativeTimeFormatInternals|; steps not noted
|
||||
* here occur there.
|
||||
*
|
||||
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.1.1.
|
||||
*/
|
||||
function InitializeRelativeTimeFormat(relativeTimeFormat, locales, options) {
|
||||
assert(IsObject(relativeTimeFormat), "InitializeRelativeTimeFormat");
|
||||
|
||||
if (isInitializedIntlObject(relativeTimeFormat))
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
|
||||
|
||||
let internals = initializeIntlObject(relativeTimeFormat);
|
||||
|
||||
// Lazy RelativeTimeFormat data has the following structure:
|
||||
//
|
||||
// {
|
||||
// requestedLocales: List of locales,
|
||||
// style: "long" / "short" / "narrow",
|
||||
// numeric: "always" / "auto",
|
||||
//
|
||||
// opt: // opt object computed in InitializeRelativeTimeFormat
|
||||
// {
|
||||
// localeMatcher: "lookup" / "best fit",
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Note that lazy data is only installed as a final step of initialization,
|
||||
// so every RelativeTimeFormat lazy data object has *all* these properties, never a
|
||||
// subset of them.
|
||||
const lazyRelativeTimeFormatData = std_Object_create(null);
|
||||
|
||||
// Step 1.
|
||||
let requestedLocales = CanonicalizeLocaleList(locales);
|
||||
lazyRelativeTimeFormatData.requestedLocales = requestedLocales;
|
||||
|
||||
// Steps 2-3.
|
||||
if (options === undefined)
|
||||
options = std_Object_create(null);
|
||||
else
|
||||
options = ToObject(options);
|
||||
|
||||
// Step 4.
|
||||
let opt = new Record();
|
||||
|
||||
// Steps 5-6.
|
||||
let matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
|
||||
opt.localeMatcher = matcher;
|
||||
|
||||
lazyRelativeTimeFormatData.opt = opt;
|
||||
|
||||
// Steps 12-13.
|
||||
const style = GetOption(options, "style", "string", ["long", "short", "narrow"], "long");
|
||||
lazyRelativeTimeFormatData.style = style;
|
||||
|
||||
// Steps 14-15.
|
||||
const numeric = GetOption(options, "numeric", "string", ["always", "auto"], "always");
|
||||
lazyRelativeTimeFormatData.numeric = numeric;
|
||||
|
||||
setLazyData(internals, "RelativeTimeFormat", lazyRelativeTimeFormatData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subset of the given locale list for which this locale list has a
|
||||
* matching (possibly fallback) locale. Locales appear in the same order in the
|
||||
* returned list as in the input list.
|
||||
*
|
||||
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.3.2.
|
||||
*/
|
||||
function Intl_RelativeTimeFormat_supportedLocalesOf(locales /*, options*/) {
|
||||
var options = arguments.length > 1 ? arguments[1] : undefined;
|
||||
|
||||
// Step 1.
|
||||
var availableLocales = callFunction(relativeTimeFormatInternalProperties.availableLocales,
|
||||
relativeTimeFormatInternalProperties);
|
||||
// Step 2.
|
||||
let requestedLocales = CanonicalizeLocaleList(locales);
|
||||
|
||||
// Step 3.
|
||||
return SupportedLocales(availableLocales, requestedLocales, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String value representing the written form of a relative date
|
||||
* formatted according to the effective locale and the formatting options
|
||||
* of this RelativeTimeFormat object.
|
||||
*
|
||||
* Spec: ECMAScript 402 API, RelativeTImeFormat, 1.4.3.
|
||||
*/
|
||||
function Intl_RelativeTimeFormat_format(value, unit) {
|
||||
// Step 1.
|
||||
let relativeTimeFormat = this;
|
||||
|
||||
// Step 2.
|
||||
let internals = getRelativeTimeFormatInternals(relativeTimeFormat, "format");
|
||||
|
||||
// Step 3.
|
||||
let t = ToNumber(value);
|
||||
|
||||
// Step 4.
|
||||
let u = ToString(unit);
|
||||
|
||||
// PartitionRelativeTimePattern, step 4.
|
||||
if (!Number_isFinite(t)) {
|
||||
ThrowRangeError(JSMSG_DATE_NOT_FINITE, "RelativeTimeFormat");
|
||||
}
|
||||
|
||||
// PartitionRelativeTimePattern, step 5.
|
||||
switch (u) {
|
||||
case "second":
|
||||
case "seconds":
|
||||
case "minute":
|
||||
case "minutes":
|
||||
case "hour":
|
||||
case "hours":
|
||||
case "day":
|
||||
case "days":
|
||||
case "week":
|
||||
case "weeks":
|
||||
case "month":
|
||||
case "months":
|
||||
case "quarter":
|
||||
case "quarters":
|
||||
case "year":
|
||||
case "years":
|
||||
break;
|
||||
default:
|
||||
ThrowRangeError(JSMSG_INVALID_OPTION_VALUE, "unit", u);
|
||||
}
|
||||
|
||||
// Step 5.
|
||||
return intl_FormatRelativeTime(relativeTimeFormat, t, u, internals.numeric);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resolved options for a PluralRules object.
|
||||
*
|
||||
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.4.4.
|
||||
*/
|
||||
function Intl_RelativeTimeFormat_resolvedOptions() {
|
||||
var internals = getRelativeTimeFormatInternals(this, "resolvedOptions");
|
||||
|
||||
// Steps 4-5.
|
||||
var result = {
|
||||
locale: internals.locale,
|
||||
style: internals.style,
|
||||
numeric: internals.numeric,
|
||||
};
|
||||
|
||||
// Step 6.
|
||||
return result;
|
||||
}
|
||||
|
||||
function Intl_getCanonicalLocales(locales) {
|
||||
let codes = CanonicalizeLocaleList(locales);
|
||||
let result = [];
|
||||
|
||||
@@ -182,6 +182,7 @@
|
||||
macro(InitializeDateTimeFormat, InitializeDateTimeFormat, "InitializeDateTimeFormat") \
|
||||
macro(InitializeNumberFormat, InitializeNumberFormat, "InitializeNumberFormat") \
|
||||
macro(InitializePluralRules, InitializePluralRules, "InitializePluralRules") \
|
||||
macro(InitializeRelativeTimeFormat, InitializeRelativeTimeFormat, "InitializeRelativeTimeFormat") \
|
||||
macro(innermost, innermost, "innermost") \
|
||||
macro(inNursery, inNursery, "inNursery") \
|
||||
macro(input, input, "input") \
|
||||
@@ -296,6 +297,8 @@
|
||||
macro(Reify, Reify, "Reify") \
|
||||
macro(reject, reject, "reject") \
|
||||
macro(rejected, rejected, "rejected") \
|
||||
macro(RelativeTimeFormat, RelativeTimeFormat, "RelativeTimeFormat") \
|
||||
macro(RelativeTimeFormatFormat, RelativeTimeFormatFormat, "Intl_RelativeTimeFormat_Format") \
|
||||
macro(RequireObjectCoercible, RequireObjectCoercible, "RequireObjectCoercible") \
|
||||
macro(resolve, resolve, "resolve") \
|
||||
macro(resumeGenerator, resumeGenerator, "resumeGenerator") \
|
||||
@@ -363,6 +366,7 @@
|
||||
macro(unescape, unescape, "unescape") \
|
||||
macro(uneval, uneval, "uneval") \
|
||||
macro(unicode, unicode, "unicode") \
|
||||
macro(unit, unit, "unit") \
|
||||
macro(uninitialized, uninitialized, "uninitialized") \
|
||||
macro(unsized, unsized, "unsized") \
|
||||
macro(unwatch, unwatch, "unwatch") \
|
||||
|
||||
@@ -110,6 +110,7 @@ class GlobalObject : public NativeObject
|
||||
NUMBER_FORMAT_PROTO,
|
||||
DATE_TIME_FORMAT_PROTO,
|
||||
PLURAL_RULES_PROTO,
|
||||
RELATIVE_TIME_FORMAT_PROTO,
|
||||
MODULE_PROTO,
|
||||
IMPORT_ENTRY_PROTO,
|
||||
EXPORT_ENTRY_PROTO,
|
||||
@@ -506,6 +507,11 @@ class GlobalObject : public NativeObject
|
||||
return getOrCreateObject(cx, global, PLURAL_RULES_PROTO, initIntlObject);
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
getOrCreateRelativeTimeFormatPrototype(JSContext* cx, Handle<GlobalObject*> global) {
|
||||
return getOrCreateObject(cx, global, RELATIVE_TIME_FORMAT_PROTO, initIntlObject);
|
||||
}
|
||||
|
||||
static bool ensureModulePrototypesCreated(JSContext *cx, Handle<GlobalObject*> global);
|
||||
|
||||
JSObject* maybeGetModulePrototype() {
|
||||
|
||||
@@ -2697,6 +2697,8 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_FN("intl_PluralRules_availableLocales", intl_PluralRules_availableLocales, 0,0),
|
||||
JS_FN("intl_GetPluralCategories", intl_GetPluralCategories, 2, 0),
|
||||
JS_FN("intl_SelectPluralRule", intl_SelectPluralRule, 2,0),
|
||||
JS_FN("intl_RelativeTimeFormat_availableLocales", intl_RelativeTimeFormat_availableLocales, 0,0),
|
||||
JS_FN("intl_FormatRelativeTime", intl_FormatRelativeTime, 3,0),
|
||||
|
||||
JS_INLINABLE_FN("IsRegExpObject",
|
||||
intrinsic_IsInstanceOfBuiltin<RegExpObject>, 1,0,
|
||||
|
||||
Reference in New Issue
Block a user