Implement Intl.PluralRules API.
parent
3c917141dd
commit
98180dd541
|
@ -79,6 +79,7 @@ included_inclnames_to_ignore = set([
|
||||||
'prtypes.h', # NSPR
|
'prtypes.h', # NSPR
|
||||||
'selfhosted.out.h', # generated in $OBJDIR
|
'selfhosted.out.h', # generated in $OBJDIR
|
||||||
'shellmoduleloader.out.h', # generated in $OBJDIR
|
'shellmoduleloader.out.h', # generated in $OBJDIR
|
||||||
|
'unicode/plurrule.h', # ICU
|
||||||
'unicode/timezone.h', # ICU
|
'unicode/timezone.h', # ICU
|
||||||
'unicode/ucal.h', # ICU
|
'unicode/ucal.h', # ICU
|
||||||
'unicode/uclean.h', # ICU
|
'unicode/uclean.h', # ICU
|
||||||
|
@ -90,6 +91,7 @@ included_inclnames_to_ignore = set([
|
||||||
'unicode/unorm.h', # ICU
|
'unicode/unorm.h', # ICU
|
||||||
'unicode/unum.h', # ICU
|
'unicode/unum.h', # ICU
|
||||||
'unicode/unumsys.h', # ICU
|
'unicode/unumsys.h', # ICU
|
||||||
|
'unicode/upluralrules.h', # ICU
|
||||||
'unicode/ustring.h', # ICU
|
'unicode/ustring.h', # ICU
|
||||||
'unicode/utypes.h', # ICU
|
'unicode/utypes.h', # ICU
|
||||||
'vtune/VTuneWrapper.h' # VTune
|
'vtune/VTuneWrapper.h' # VTune
|
||||||
|
|
|
@ -778,7 +778,7 @@ struct JSClass {
|
||||||
// application.
|
// application.
|
||||||
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
|
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
|
||||||
#define JSCLASS_GLOBAL_SLOT_COUNT \
|
#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) \
|
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
||||||
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
||||||
#define JSCLASS_GLOBAL_FLAGS \
|
#define JSCLASS_GLOBAL_FLAGS \
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "builtin/Intl.h"
|
#include "builtin/Intl.h"
|
||||||
|
|
||||||
|
#include "mozilla/Casting.h"
|
||||||
#include "mozilla/PodOperations.h"
|
#include "mozilla/PodOperations.h"
|
||||||
#include "mozilla/Range.h"
|
#include "mozilla/Range.h"
|
||||||
#include "mozilla/ScopeExit.h"
|
#include "mozilla/ScopeExit.h"
|
||||||
|
@ -22,6 +23,7 @@
|
||||||
#include "jsobj.h"
|
#include "jsobj.h"
|
||||||
|
|
||||||
#include "builtin/IntlTimeZoneData.h"
|
#include "builtin/IntlTimeZoneData.h"
|
||||||
|
#include "unicode/plurrule.h"
|
||||||
#include "unicode/ucal.h"
|
#include "unicode/ucal.h"
|
||||||
#include "unicode/ucol.h"
|
#include "unicode/ucol.h"
|
||||||
#include "unicode/udat.h"
|
#include "unicode/udat.h"
|
||||||
|
@ -29,6 +31,7 @@
|
||||||
#include "unicode/uenum.h"
|
#include "unicode/uenum.h"
|
||||||
#include "unicode/unum.h"
|
#include "unicode/unum.h"
|
||||||
#include "unicode/unumsys.h"
|
#include "unicode/unumsys.h"
|
||||||
|
#include "unicode/upluralrules.h"
|
||||||
#include "unicode/ustring.h"
|
#include "unicode/ustring.h"
|
||||||
#include "vm/DateTime.h"
|
#include "vm/DateTime.h"
|
||||||
#include "vm/GlobalObject.h"
|
#include "vm/GlobalObject.h"
|
||||||
|
@ -43,6 +46,7 @@
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
|
|
||||||
|
using mozilla::AssertedCast;
|
||||||
using mozilla::IsFinite;
|
using mozilla::IsFinite;
|
||||||
using mozilla::IsNegativeZero;
|
using mozilla::IsNegativeZero;
|
||||||
using mozilla::MakeScopeExit;
|
using mozilla::MakeScopeExit;
|
||||||
|
@ -962,6 +966,89 @@ js::intl_numberingSystem(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This creates new UNumberFormat with calculated digit formatting
|
||||||
|
* properties for PluralRules.
|
||||||
|
*
|
||||||
|
* This is similar to NewUNumberFormat but doesn't allow for currency or
|
||||||
|
* percent types.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static UNumberFormat*
|
||||||
|
NewUNumberFormatForPluralRules(JSContext* cx, HandleObject pluralRules)
|
||||||
|
{
|
||||||
|
RootedObject internals(cx, GetInternals(cx, pluralRules));
|
||||||
|
if (!internals)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
RootedValue value(cx);
|
||||||
|
|
||||||
|
if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
|
||||||
|
return nullptr;
|
||||||
|
JSAutoByteString locale(cx, value.toString());
|
||||||
|
if (!locale)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
uint32_t uMinimumIntegerDigits = 1;
|
||||||
|
uint32_t uMinimumFractionDigits = 0;
|
||||||
|
uint32_t uMaximumFractionDigits = 3;
|
||||||
|
int32_t uMinimumSignificantDigits = -1;
|
||||||
|
int32_t uMaximumSignificantDigits = -1;
|
||||||
|
|
||||||
|
RootedId id(cx, NameToId(cx->names().minimumSignificantDigits));
|
||||||
|
bool hasP;
|
||||||
|
if (!HasProperty(cx, internals, id, &hasP))
|
||||||
|
return nullptr;
|
||||||
|
if (hasP) {
|
||||||
|
if (!GetProperty(cx, internals, internals, cx->names().minimumSignificantDigits,
|
||||||
|
&value))
|
||||||
|
return nullptr;
|
||||||
|
uMinimumSignificantDigits = value.toInt32();
|
||||||
|
|
||||||
|
if (!GetProperty(cx, internals, internals, cx->names().maximumSignificantDigits,
|
||||||
|
&value))
|
||||||
|
return nullptr;
|
||||||
|
uMaximumSignificantDigits = value.toInt32();
|
||||||
|
} else {
|
||||||
|
if (!GetProperty(cx, internals, internals, cx->names().minimumIntegerDigits,
|
||||||
|
&value))
|
||||||
|
return nullptr;
|
||||||
|
uMinimumIntegerDigits = AssertedCast<uint32_t>(value.toInt32());
|
||||||
|
|
||||||
|
if (!GetProperty(cx, internals, internals, cx->names().minimumFractionDigits,
|
||||||
|
&value))
|
||||||
|
return nullptr;
|
||||||
|
uMinimumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
|
||||||
|
|
||||||
|
if (!GetProperty(cx, internals, internals, cx->names().maximumFractionDigits,
|
||||||
|
&value))
|
||||||
|
return nullptr;
|
||||||
|
uMaximumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
|
||||||
|
}
|
||||||
|
|
||||||
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
|
UNumberFormat* nf = unum_open(UNUM_DECIMAL, nullptr, 0, icuLocale(locale.ptr()), nullptr, &status);
|
||||||
|
if (U_FAILURE(status)) {
|
||||||
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
ScopedICUObject<UNumberFormat, unum_close> toClose(nf);
|
||||||
|
|
||||||
|
if (uMinimumSignificantDigits != -1) {
|
||||||
|
unum_setAttribute(nf, UNUM_SIGNIFICANT_DIGITS_USED, true);
|
||||||
|
unum_setAttribute(nf, UNUM_MIN_SIGNIFICANT_DIGITS, uMinimumSignificantDigits);
|
||||||
|
unum_setAttribute(nf, UNUM_MAX_SIGNIFICANT_DIGITS, uMaximumSignificantDigits);
|
||||||
|
} else {
|
||||||
|
unum_setAttribute(nf, UNUM_MIN_INTEGER_DIGITS, uMinimumIntegerDigits);
|
||||||
|
unum_setAttribute(nf, UNUM_MIN_FRACTION_DIGITS, uMinimumFractionDigits);
|
||||||
|
unum_setAttribute(nf, UNUM_MAX_FRACTION_DIGITS, uMaximumFractionDigits);
|
||||||
|
}
|
||||||
|
|
||||||
|
return toClose.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new UNumberFormat with the locale and number formatting options
|
* Returns a new UNumberFormat with the locale and number formatting options
|
||||||
* of the given NumberFormat.
|
* of the given NumberFormat.
|
||||||
|
@ -1044,35 +1131,25 @@ NewUNumberFormat(JSContext* cx, HandleObject numberFormat)
|
||||||
if (hasP) {
|
if (hasP) {
|
||||||
if (!GetProperty(cx, internals, internals, cx->names().minimumSignificantDigits,
|
if (!GetProperty(cx, internals, internals, cx->names().minimumSignificantDigits,
|
||||||
&value))
|
&value))
|
||||||
{
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
uMinimumSignificantDigits = value.toInt32();
|
||||||
uMinimumSignificantDigits = int32_t(value.toNumber());
|
|
||||||
if (!GetProperty(cx, internals, internals, cx->names().maximumSignificantDigits,
|
if (!GetProperty(cx, internals, internals, cx->names().maximumSignificantDigits,
|
||||||
&value))
|
&value))
|
||||||
{
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
uMaximumSignificantDigits = value.toInt32();
|
||||||
uMaximumSignificantDigits = int32_t(value.toNumber());
|
|
||||||
} else {
|
} else {
|
||||||
if (!GetProperty(cx, internals, internals, cx->names().minimumIntegerDigits,
|
if (!GetProperty(cx, internals, internals, cx->names().minimumIntegerDigits,
|
||||||
&value))
|
&value))
|
||||||
{
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
uMinimumIntegerDigits = AssertedCast<uint32_t>(value.toInt32());
|
||||||
uMinimumIntegerDigits = int32_t(value.toNumber());
|
|
||||||
if (!GetProperty(cx, internals, internals, cx->names().minimumFractionDigits,
|
if (!GetProperty(cx, internals, internals, cx->names().minimumFractionDigits,
|
||||||
&value))
|
&value))
|
||||||
{
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
uMinimumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
|
||||||
uMinimumFractionDigits = int32_t(value.toNumber());
|
|
||||||
if (!GetProperty(cx, internals, internals, cx->names().maximumFractionDigits,
|
if (!GetProperty(cx, internals, internals, cx->names().maximumFractionDigits,
|
||||||
&value))
|
&value))
|
||||||
{
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
uMaximumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
|
||||||
uMaximumFractionDigits = int32_t(value.toNumber());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!GetProperty(cx, internals, internals, cx->names().useGrouping, &value))
|
if (!GetProperty(cx, internals, internals, cx->names().useGrouping, &value))
|
||||||
|
@ -2323,6 +2400,381 @@ js::intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**************** PluralRules *****************/
|
||||||
|
|
||||||
|
static void pluralRules_finalize(FreeOp* fop, JSObject* obj);
|
||||||
|
|
||||||
|
static const uint32_t UPLURAL_RULES_SLOT = 0;
|
||||||
|
static const uint32_t PLURAL_RULES_SLOTS_COUNT = 1;
|
||||||
|
|
||||||
|
static const ClassOps PluralRulesClassOps = {
|
||||||
|
nullptr, /* addProperty */
|
||||||
|
nullptr, /* delProperty */
|
||||||
|
nullptr, /* getProperty */
|
||||||
|
nullptr, /* setProperty */
|
||||||
|
nullptr, /* enumerate */
|
||||||
|
nullptr, /* resolve */
|
||||||
|
nullptr, /* mayResolve */
|
||||||
|
pluralRules_finalize
|
||||||
|
};
|
||||||
|
|
||||||
|
static const Class PluralRulesClass = {
|
||||||
|
js_Object_str,
|
||||||
|
JSCLASS_HAS_RESERVED_SLOTS(PLURAL_RULES_SLOTS_COUNT) |
|
||||||
|
JSCLASS_FOREGROUND_FINALIZE,
|
||||||
|
&PluralRulesClassOps
|
||||||
|
};
|
||||||
|
|
||||||
|
#if JS_HAS_TOSOURCE
|
||||||
|
static bool
|
||||||
|
pluralRules_toSource(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
{
|
||||||
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
args.rval().setString(cx->names().PluralRules);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const JSFunctionSpec pluralRules_static_methods[] = {
|
||||||
|
JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_PluralRules_supportedLocalesOf", 1, 0),
|
||||||
|
JS_FS_END
|
||||||
|
};
|
||||||
|
|
||||||
|
static const JSFunctionSpec pluralRules_methods[] = {
|
||||||
|
JS_SELF_HOSTED_FN("resolvedOptions", "Intl_PluralRules_resolvedOptions", 0, 0),
|
||||||
|
JS_SELF_HOSTED_FN("select", "Intl_PluralRules_select", 1, 0),
|
||||||
|
#if JS_HAS_TOSOURCE
|
||||||
|
JS_FN(js_toSource_str, pluralRules_toSource, 0, 0),
|
||||||
|
#endif
|
||||||
|
JS_FS_END
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PluralRules constructor.
|
||||||
|
* Spec: ECMAScript 402 API, PluralRules, 1.1
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
PluralRules(JSContext* cx, const CallArgs& args, bool construct)
|
||||||
|
{
|
||||||
|
RootedObject obj(cx);
|
||||||
|
|
||||||
|
if (!construct) {
|
||||||
|
JSObject* intl = GlobalObject::getOrCreateIntlObject(cx, cx->global());
|
||||||
|
if (!intl)
|
||||||
|
return false;
|
||||||
|
RootedValue self(cx, args.thisv());
|
||||||
|
if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
|
||||||
|
obj = ToObject(cx, self);
|
||||||
|
if (!obj)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool extensible;
|
||||||
|
if (!IsExtensible(cx, obj, &extensible))
|
||||||
|
return false;
|
||||||
|
if (!extensible)
|
||||||
|
return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
|
||||||
|
} else {
|
||||||
|
construct = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (construct) {
|
||||||
|
RootedObject proto(cx, GlobalObject::getOrCreatePluralRulesPrototype(cx, cx->global()));
|
||||||
|
if (!proto)
|
||||||
|
return false;
|
||||||
|
obj = NewObjectWithGivenProto(cx, &PluralRulesClass, proto);
|
||||||
|
if (!obj)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
obj->as<NativeObject>().setReservedSlot(UPLURAL_RULES_SLOT, PrivateValue(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
RootedValue locales(cx, args.get(0));
|
||||||
|
RootedValue options(cx, args.get(1));
|
||||||
|
|
||||||
|
if (!IntlInitialize(cx, obj, cx->names().InitializePluralRules, locales, options))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
args.rval().setObject(*obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
PluralRules(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
{
|
||||||
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
return PluralRules(cx, args, args.isConstructing());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
js::intl_PluralRules(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
{
|
||||||
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
MOZ_ASSERT(args.length() == 2);
|
||||||
|
return PluralRules(cx, args, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pluralRules_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(UPLURAL_RULES_SLOT);
|
||||||
|
if (!slot.isUndefined()) {
|
||||||
|
if (UPluralRules* pr = static_cast<UPluralRules*>(slot.toPrivate()))
|
||||||
|
uplrules_close(pr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSObject*
|
||||||
|
CreatePluralRulesPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
|
||||||
|
{
|
||||||
|
RootedFunction ctor(cx);
|
||||||
|
ctor = global->createConstructor(cx, &PluralRules, cx->names().PluralRules, 0);
|
||||||
|
if (!ctor)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &PluralRulesClass));
|
||||||
|
if (!proto)
|
||||||
|
return nullptr;
|
||||||
|
proto->setReservedSlot(UPLURAL_RULES_SLOT, PrivateValue(nullptr));
|
||||||
|
|
||||||
|
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!JS_DefineFunctions(cx, ctor, pluralRules_static_methods))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!JS_DefineFunctions(cx, proto, pluralRules_methods))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
RootedValue options(cx);
|
||||||
|
if (!CreateDefaultOptions(cx, &options))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!IntlInitialize(cx, proto, cx->names().InitializePluralRules, UndefinedHandleValue,
|
||||||
|
options))
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||||
|
if (!DefineProperty(cx, Intl, cx->names().PluralRules, ctorValue, nullptr, nullptr, 0))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
js::intl_PluralRules_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
js::intl_SelectPluralRule(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
{
|
||||||
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
|
||||||
|
RootedObject pluralRules(cx, &args[0].toObject());
|
||||||
|
|
||||||
|
UNumberFormat* nf = NewUNumberFormatForPluralRules(cx, pluralRules);
|
||||||
|
if (!nf)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ScopedICUObject<UNumberFormat, unum_close> closeNumberFormat(nf);
|
||||||
|
|
||||||
|
RootedObject internals(cx, GetInternals(cx, pluralRules));
|
||||||
|
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().type, &value))
|
||||||
|
return false;
|
||||||
|
JSAutoByteString type(cx, value.toString());
|
||||||
|
if (!type)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
double x = args[1].toNumber();
|
||||||
|
|
||||||
|
// We need a NumberFormat in order to format the number
|
||||||
|
// using the number formatting options (minimum/maximum*Digits)
|
||||||
|
// before we push the result to PluralRules
|
||||||
|
//
|
||||||
|
// This should be fixed in ICU 59 and we'll be able to switch to that
|
||||||
|
// API: http://bugs.icu-project.org/trac/ticket/12763
|
||||||
|
//
|
||||||
|
RootedValue fmtNumValue(cx);
|
||||||
|
if (!intl_FormatNumber(cx, nf, x, &fmtNumValue))
|
||||||
|
return false;
|
||||||
|
RootedString fmtNumValueString(cx, fmtNumValue.toString());
|
||||||
|
AutoStableStringChars stableChars(cx);
|
||||||
|
if (!stableChars.initTwoByte(cx, fmtNumValueString))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const UChar* uFmtNumValue = Char16ToUChar(stableChars.twoByteRange().begin().get());
|
||||||
|
|
||||||
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
|
|
||||||
|
UFormattable* fmt = unum_parseToUFormattable(nf, nullptr, uFmtNumValue,
|
||||||
|
stableChars.twoByteRange().length(), 0, &status);
|
||||||
|
if (U_FAILURE(status)) {
|
||||||
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedICUObject<UFormattable, ufmt_close> closeUFormattable(fmt);
|
||||||
|
|
||||||
|
double y = ufmt_getDouble(fmt, &status);
|
||||||
|
if (U_FAILURE(status)) {
|
||||||
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UPluralType category;
|
||||||
|
|
||||||
|
if (equal(type, "cardinal")) {
|
||||||
|
category = UPLURAL_TYPE_CARDINAL;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(equal(type, "ordinal"));
|
||||||
|
category = UPLURAL_TYPE_ORDINAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
UPluralRules* pr = uplrules_openForType(icuLocale(locale.ptr()), category, &status);
|
||||||
|
if (U_FAILURE(status)) {
|
||||||
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedICUObject<UPluralRules, uplrules_close> closePluralRules(pr);
|
||||||
|
|
||||||
|
Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
|
||||||
|
if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int size = uplrules_select(pr, y, Char16ToUChar(chars.begin()), INITIAL_CHAR_BUFFER_SIZE, &status);
|
||||||
|
if (status == U_BUFFER_OVERFLOW_ERROR) {
|
||||||
|
if (!chars.resize(size))
|
||||||
|
return false;
|
||||||
|
status = U_ZERO_ERROR;
|
||||||
|
uplrules_select(pr, y, Char16ToUChar(chars.begin()), size, &status);
|
||||||
|
}
|
||||||
|
if (U_FAILURE(status)) {
|
||||||
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSString* str = NewStringCopyN<CanGC>(cx, chars.begin(), size);
|
||||||
|
if (!str)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
args.rval().setString(str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
js::intl_GetPluralCategories(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
{
|
||||||
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
MOZ_ASSERT(args.length() == 2);
|
||||||
|
|
||||||
|
JSAutoByteString locale(cx, args[0].toString());
|
||||||
|
if (!locale)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
JSAutoByteString type(cx, args[1].toString());
|
||||||
|
if (!type)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
|
|
||||||
|
UPluralType category;
|
||||||
|
|
||||||
|
if (equal(type, "cardinal")) {
|
||||||
|
category = UPLURAL_TYPE_CARDINAL;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(equal(type, "ordinal"));
|
||||||
|
category = UPLURAL_TYPE_ORDINAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
UPluralRules* pr = uplrules_openForType(
|
||||||
|
icuLocale(locale.ptr()),
|
||||||
|
category,
|
||||||
|
&status
|
||||||
|
);
|
||||||
|
if (U_FAILURE(status)) {
|
||||||
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedICUObject<UPluralRules, uplrules_close> closePluralRules(pr);
|
||||||
|
|
||||||
|
// We should get a C API for that in ICU 59 and switch to it
|
||||||
|
// https://ssl.icu-project.org/trac/ticket/12772
|
||||||
|
//
|
||||||
|
icu::StringEnumeration* kwenum =
|
||||||
|
reinterpret_cast<icu::PluralRules*>(pr)->getKeywords(status);
|
||||||
|
UEnumeration* ue = uenum_openFromStringEnumeration(kwenum, &status);
|
||||||
|
|
||||||
|
if (U_FAILURE(status)) {
|
||||||
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedICUObject<UEnumeration, uenum_close> closeEnum(ue);
|
||||||
|
|
||||||
|
RootedObject res(cx, NewDenseEmptyArray(cx));
|
||||||
|
if (!res)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RootedValue element(cx);
|
||||||
|
uint32_t i = 0;
|
||||||
|
int32_t catSize;
|
||||||
|
const char* cat;
|
||||||
|
|
||||||
|
do {
|
||||||
|
cat = uenum_next(ue, &catSize, &status);
|
||||||
|
if (U_FAILURE(status)) {
|
||||||
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cat)
|
||||||
|
break;
|
||||||
|
|
||||||
|
JSString* str = NewStringCopyN<CanGC>(cx, cat, catSize);
|
||||||
|
if (!str)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
element.setString(str);
|
||||||
|
if (!DefineElement(cx, res, i, element))
|
||||||
|
return false;
|
||||||
|
i++;
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
args.rval().setObject(*res);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
|
js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
|
@ -2761,6 +3213,9 @@ GlobalObject::initIntlObject(JSContext* cx, Handle<GlobalObject*> global)
|
||||||
RootedObject numberFormatProto(cx, CreateNumberFormatPrototype(cx, intl, global));
|
RootedObject numberFormatProto(cx, CreateNumberFormatPrototype(cx, intl, global));
|
||||||
if (!numberFormatProto)
|
if (!numberFormatProto)
|
||||||
return false;
|
return false;
|
||||||
|
RootedObject pluralRulesProto(cx, CreatePluralRulesPrototype(cx, intl, global));
|
||||||
|
if (!pluralRulesProto)
|
||||||
|
return false;
|
||||||
|
|
||||||
// The |Intl| object is fully set up now, so define the global property.
|
// The |Intl| object is fully set up now, so define the global property.
|
||||||
RootedValue intlValue(cx, ObjectValue(*intl));
|
RootedValue intlValue(cx, ObjectValue(*intl));
|
||||||
|
@ -2782,6 +3237,7 @@ GlobalObject::initIntlObject(JSContext* cx, Handle<GlobalObject*> global)
|
||||||
global->setReservedSlot(COLLATOR_PROTO, ObjectValue(*collatorProto));
|
global->setReservedSlot(COLLATOR_PROTO, ObjectValue(*collatorProto));
|
||||||
global->setReservedSlot(DATE_TIME_FORMAT_PROTO, ObjectValue(*dateTimeFormatProto));
|
global->setReservedSlot(DATE_TIME_FORMAT_PROTO, ObjectValue(*dateTimeFormatProto));
|
||||||
global->setReservedSlot(NUMBER_FORMAT_PROTO, ObjectValue(*numberFormatProto));
|
global->setReservedSlot(NUMBER_FORMAT_PROTO, ObjectValue(*numberFormatProto));
|
||||||
|
global->setReservedSlot(PLURAL_RULES_PROTO, ObjectValue(*pluralRulesProto));
|
||||||
|
|
||||||
// Also cache |Intl| to implement spec language that conditions behavior
|
// Also cache |Intl| to implement spec language that conditions behavior
|
||||||
// based on values being equal to "the standard built-in |Intl| object".
|
// based on values being equal to "the standard built-in |Intl| object".
|
||||||
|
|
|
@ -358,6 +358,54 @@ intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp);
|
||||||
extern MOZ_MUST_USE bool
|
extern MOZ_MUST_USE bool
|
||||||
intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp);
|
intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
|
/******************** PluralRules ********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new PluralRules instance.
|
||||||
|
* Self-hosted code cannot cache this constructor (as it does for others in
|
||||||
|
* Utilities.js) because it is initialized after self-hosted code is compiled.
|
||||||
|
*
|
||||||
|
* Usage: pluralRules = intl_PluralRules(locales, options)
|
||||||
|
*/
|
||||||
|
extern MOZ_MUST_USE bool
|
||||||
|
intl_PluralRules(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object indicating the supported locales for plural rules
|
||||||
|
* 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_PluralRules_availableLocales()
|
||||||
|
*/
|
||||||
|
extern MOZ_MUST_USE bool
|
||||||
|
intl_PluralRules_availableLocales(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a plural rule for the number x according to the effective
|
||||||
|
* locale and the formatting options of the given PluralRules.
|
||||||
|
*
|
||||||
|
* A plural rule is a grammatical category that expresses count distinctions
|
||||||
|
* (such as "one", "two", "few" etc.).
|
||||||
|
*
|
||||||
|
* Usage: rule = intl_SelectPluralRule(pluralRules, x)
|
||||||
|
*/
|
||||||
|
extern MOZ_MUST_USE bool
|
||||||
|
intl_SelectPluralRule(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of plural rules categories for a given
|
||||||
|
* locale and type.
|
||||||
|
*
|
||||||
|
* Usage: categories = intl_GetPluralCategories(locale, type)
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* intl_getPluralCategories('pl', 'cardinal'); // ['one', 'few', 'many', 'other']
|
||||||
|
*/
|
||||||
|
extern MOZ_MUST_USE bool
|
||||||
|
intl_GetPluralCategories(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a plain object with calendar information for a single valid locale
|
* Returns a plain object with calendar information for a single valid locale
|
||||||
* (callers must perform this validation). The object will have these
|
* (callers must perform this validation). The object will have these
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
intl_availableCalendars: false,
|
intl_availableCalendars: false,
|
||||||
intl_patternForSkeleton: false,
|
intl_patternForSkeleton: false,
|
||||||
intl_FormatDateTime: false,
|
intl_FormatDateTime: false,
|
||||||
|
intl_SelectPluralRule: false,
|
||||||
|
intl_GetPluralCategories: false,
|
||||||
|
intl_GetCalendarInfo: false,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -857,6 +860,7 @@ function BestAvailableLocaleIgnoringDefault(availableLocales, locale) {
|
||||||
return BestAvailableLocaleHelper(availableLocales, locale, false);
|
return BestAvailableLocaleHelper(availableLocales, locale, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var noRelevantExtensionKeys = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares a BCP 47 language priority list against the set of locales in
|
* Compares a BCP 47 language priority list against the set of locales in
|
||||||
|
@ -1270,7 +1274,9 @@ function initializeIntlObject(obj) {
|
||||||
function setLazyData(internals, type, lazyData)
|
function setLazyData(internals, type, lazyData)
|
||||||
{
|
{
|
||||||
assert(internals.type === "partial", "can't set lazy data for anything but a newborn");
|
assert(internals.type === "partial", "can't set lazy data for anything but a newborn");
|
||||||
assert(type === "Collator" || type === "DateTimeFormat" || type == "NumberFormat", "bad type");
|
assert(type === "Collator" || type === "DateTimeFormat" ||
|
||||||
|
type == "NumberFormat" || type === "PluralRules",
|
||||||
|
"bad type");
|
||||||
assert(IsObject(lazyData), "non-object lazy data");
|
assert(IsObject(lazyData), "non-object lazy data");
|
||||||
|
|
||||||
// Set in reverse order so that the .type change is a barrier.
|
// Set in reverse order so that the .type change is a barrier.
|
||||||
|
@ -1320,7 +1326,9 @@ function isInitializedIntlObject(obj) {
|
||||||
if (IsObject(internals)) {
|
if (IsObject(internals)) {
|
||||||
assert(callFunction(std_Object_hasOwnProperty, internals, "type"), "missing type");
|
assert(callFunction(std_Object_hasOwnProperty, internals, "type"), "missing type");
|
||||||
var type = internals.type;
|
var type = internals.type;
|
||||||
assert(type === "partial" || type === "Collator" || type === "DateTimeFormat" || type === "NumberFormat", "unexpected type");
|
assert(type === "partial" || type === "Collator" ||
|
||||||
|
type === "DateTimeFormat" || type === "NumberFormat" || type === "PluralRules",
|
||||||
|
"unexpected type");
|
||||||
assert(callFunction(std_Object_hasOwnProperty, internals, "lazyData"), "missing lazyData");
|
assert(callFunction(std_Object_hasOwnProperty, internals, "lazyData"), "missing lazyData");
|
||||||
assert(callFunction(std_Object_hasOwnProperty, internals, "internalProps"), "missing internalProps");
|
assert(callFunction(std_Object_hasOwnProperty, internals, "internalProps"), "missing internalProps");
|
||||||
} else {
|
} else {
|
||||||
|
@ -1377,6 +1385,8 @@ function getInternals(obj)
|
||||||
internalProps = resolveCollatorInternals(lazyData)
|
internalProps = resolveCollatorInternals(lazyData)
|
||||||
else if (type === "DateTimeFormat")
|
else if (type === "DateTimeFormat")
|
||||||
internalProps = resolveDateTimeFormatInternals(lazyData)
|
internalProps = resolveDateTimeFormatInternals(lazyData)
|
||||||
|
else if (type === "PluralRules")
|
||||||
|
internalProps = resolvePluralRulesInternals(lazyData)
|
||||||
else
|
else
|
||||||
internalProps = resolveNumberFormatInternals(lazyData);
|
internalProps = resolveNumberFormatInternals(lazyData);
|
||||||
setInternalProperties(internals, internalProps);
|
setInternalProperties(internals, internalProps);
|
||||||
|
@ -1776,45 +1786,39 @@ function resolveNumberFormatInternals(lazyNumberFormatData) {
|
||||||
// Step 6.
|
// Step 6.
|
||||||
var opt = lazyNumberFormatData.opt;
|
var opt = lazyNumberFormatData.opt;
|
||||||
|
|
||||||
// Compute effective locale.
|
|
||||||
// Step 9.
|
|
||||||
var NumberFormat = numberFormatInternalProperties;
|
var NumberFormat = numberFormatInternalProperties;
|
||||||
|
|
||||||
// Step 10.
|
// Step 9.
|
||||||
var localeData = NumberFormat.localeData;
|
var localeData = NumberFormat.localeData;
|
||||||
|
|
||||||
// Step 11.
|
// Step 10.
|
||||||
var r = ResolveLocale(callFunction(NumberFormat.availableLocales, NumberFormat),
|
var r = ResolveLocale(callFunction(NumberFormat.availableLocales, NumberFormat),
|
||||||
lazyNumberFormatData.requestedLocales,
|
lazyNumberFormatData.requestedLocales,
|
||||||
lazyNumberFormatData.opt,
|
lazyNumberFormatData.opt,
|
||||||
NumberFormat.relevantExtensionKeys,
|
NumberFormat.relevantExtensionKeys,
|
||||||
localeData);
|
localeData);
|
||||||
|
|
||||||
// Steps 12-13. (Step 14 is not relevant to our implementation.)
|
// Steps 11-12. (Step 13 is not relevant to our implementation.)
|
||||||
internalProps.locale = r.locale;
|
internalProps.locale = r.locale;
|
||||||
internalProps.numberingSystem = r.nu;
|
internalProps.numberingSystem = r.nu;
|
||||||
|
|
||||||
// Compute formatting options.
|
// Compute formatting options.
|
||||||
// Step 16.
|
// Step 15.
|
||||||
var s = lazyNumberFormatData.style;
|
var s = lazyNumberFormatData.style;
|
||||||
internalProps.style = s;
|
internalProps.style = s;
|
||||||
|
|
||||||
// Steps 20, 22.
|
// Steps 19, 21.
|
||||||
if (s === "currency") {
|
if (s === "currency") {
|
||||||
internalProps.currency = lazyNumberFormatData.currency;
|
internalProps.currency = lazyNumberFormatData.currency;
|
||||||
internalProps.currencyDisplay = lazyNumberFormatData.currencyDisplay;
|
internalProps.currencyDisplay = lazyNumberFormatData.currencyDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 24.
|
|
||||||
internalProps.minimumIntegerDigits = lazyNumberFormatData.minimumIntegerDigits;
|
internalProps.minimumIntegerDigits = lazyNumberFormatData.minimumIntegerDigits;
|
||||||
|
|
||||||
// Steps 27.
|
|
||||||
internalProps.minimumFractionDigits = lazyNumberFormatData.minimumFractionDigits;
|
internalProps.minimumFractionDigits = lazyNumberFormatData.minimumFractionDigits;
|
||||||
|
|
||||||
// Step 30.
|
|
||||||
internalProps.maximumFractionDigits = lazyNumberFormatData.maximumFractionDigits;
|
internalProps.maximumFractionDigits = lazyNumberFormatData.maximumFractionDigits;
|
||||||
|
|
||||||
// Step 33.
|
|
||||||
if ("minimumSignificantDigits" in lazyNumberFormatData) {
|
if ("minimumSignificantDigits" in lazyNumberFormatData) {
|
||||||
// Note: Intl.NumberFormat.prototype.resolvedOptions() exposes the
|
// Note: Intl.NumberFormat.prototype.resolvedOptions() exposes the
|
||||||
// actual presence (versus undefined-ness) of these properties.
|
// actual presence (versus undefined-ness) of these properties.
|
||||||
|
@ -1823,10 +1827,10 @@ function resolveNumberFormatInternals(lazyNumberFormatData) {
|
||||||
internalProps.maximumSignificantDigits = lazyNumberFormatData.maximumSignificantDigits;
|
internalProps.maximumSignificantDigits = lazyNumberFormatData.maximumSignificantDigits;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 35.
|
// Step 27.
|
||||||
internalProps.useGrouping = lazyNumberFormatData.useGrouping;
|
internalProps.useGrouping = lazyNumberFormatData.useGrouping;
|
||||||
|
|
||||||
// Step 42.
|
// Step 34.
|
||||||
internalProps.boundFormat = undefined;
|
internalProps.boundFormat = undefined;
|
||||||
|
|
||||||
// The caller is responsible for associating |internalProps| with the right
|
// The caller is responsible for associating |internalProps| with the right
|
||||||
|
@ -1854,6 +1858,41 @@ function getNumberFormatInternals(obj, methodName) {
|
||||||
return internalProps;
|
return internalProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies digit options used for number formatting onto the intl object.
|
||||||
|
*
|
||||||
|
* Spec: ECMAScript Internationalization API Specification, 11.1.1.
|
||||||
|
*/
|
||||||
|
function SetNumberFormatDigitOptions(lazyData, options, mnfdDefault) {
|
||||||
|
// We skip Step 1 because we set the properties on a lazyData object.
|
||||||
|
|
||||||
|
// Step 2-3.
|
||||||
|
assert(IsObject(options), "SetNumberFormatDigitOptions");
|
||||||
|
assert(typeof mnfdDefault === "number", "SetNumberFormatDigitOptions");
|
||||||
|
|
||||||
|
// Steps 4-6.
|
||||||
|
const mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
|
||||||
|
const mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault);
|
||||||
|
const mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20);
|
||||||
|
|
||||||
|
// Steps 7-8.
|
||||||
|
let mnsd = options.minimumSignificantDigits;
|
||||||
|
let mxsd = options.maximumSignificantDigits;
|
||||||
|
|
||||||
|
// Steps 9-11.
|
||||||
|
lazyData.minimumIntegerDigits = mnid;
|
||||||
|
lazyData.minimumFractionDigits = mnfd;
|
||||||
|
lazyData.maximumFractionDigits = mxfd;
|
||||||
|
|
||||||
|
// Step 12.
|
||||||
|
if (mnsd !== undefined || mxsd !== undefined) {
|
||||||
|
mnsd = GetNumberOption(options, "minimumSignificantDigits", 1, 21, 1);
|
||||||
|
mxsd = GetNumberOption(options, "maximumSignificantDigits", mnsd, 21, 21);
|
||||||
|
lazyData.minimumSignificantDigits = mnsd;
|
||||||
|
lazyData.maximumSignificantDigits = mxsd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes an object as a NumberFormat.
|
* Initializes an object as a NumberFormat.
|
||||||
|
@ -1903,7 +1942,7 @@ function InitializeNumberFormat(numberFormat, locales, options) {
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Note that lazy data is only installed as a final step of initialization,
|
// Note that lazy data is only installed as a final step of initialization,
|
||||||
// so every Collator lazy data object has *all* these properties, never a
|
// so every NumberFormat lazy data object has *all* these properties, never a
|
||||||
// subset of them.
|
// subset of them.
|
||||||
var lazyNumberFormatData = std_Object_create(null);
|
var lazyNumberFormatData = std_Object_create(null);
|
||||||
|
|
||||||
|
@ -1933,11 +1972,11 @@ function InitializeNumberFormat(numberFormat, locales, options) {
|
||||||
opt.localeMatcher = matcher;
|
opt.localeMatcher = matcher;
|
||||||
|
|
||||||
// Compute formatting options.
|
// Compute formatting options.
|
||||||
// Step 15.
|
// Step 14.
|
||||||
var s = GetOption(options, "style", "string", ["decimal", "percent", "currency"], "decimal");
|
var s = GetOption(options, "style", "string", ["decimal", "percent", "currency"], "decimal");
|
||||||
lazyNumberFormatData.style = s;
|
lazyNumberFormatData.style = s;
|
||||||
|
|
||||||
// Steps 17-20.
|
// Steps 16-19.
|
||||||
var c = GetOption(options, "currency", "string", undefined, undefined);
|
var c = GetOption(options, "currency", "string", undefined, undefined);
|
||||||
if (c !== undefined && !IsWellFormedCurrencyCode(c))
|
if (c !== undefined && !IsWellFormedCurrencyCode(c))
|
||||||
ThrowRangeError(JSMSG_INVALID_CURRENCY_CODE, c);
|
ThrowRangeError(JSMSG_INVALID_CURRENCY_CODE, c);
|
||||||
|
@ -1946,54 +1985,36 @@ function InitializeNumberFormat(numberFormat, locales, options) {
|
||||||
if (c === undefined)
|
if (c === undefined)
|
||||||
ThrowTypeError(JSMSG_UNDEFINED_CURRENCY);
|
ThrowTypeError(JSMSG_UNDEFINED_CURRENCY);
|
||||||
|
|
||||||
// Steps 20.a-c.
|
// Steps 19.a-c.
|
||||||
c = toASCIIUpperCase(c);
|
c = toASCIIUpperCase(c);
|
||||||
lazyNumberFormatData.currency = c;
|
lazyNumberFormatData.currency = c;
|
||||||
cDigits = CurrencyDigits(c);
|
cDigits = CurrencyDigits(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 21.
|
// Step 20.
|
||||||
var cd = GetOption(options, "currencyDisplay", "string", ["code", "symbol", "name"], "symbol");
|
var cd = GetOption(options, "currencyDisplay", "string", ["code", "symbol", "name"], "symbol");
|
||||||
if (s === "currency")
|
if (s === "currency")
|
||||||
lazyNumberFormatData.currencyDisplay = cd;
|
lazyNumberFormatData.currencyDisplay = cd;
|
||||||
|
|
||||||
// Step 23.
|
// Steps 22-24.
|
||||||
var mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
|
SetNumberFormatDigitOptions(lazyNumberFormatData, options, s === "currency" ? cDigits: 0);
|
||||||
lazyNumberFormatData.minimumIntegerDigits = mnid;
|
|
||||||
|
|
||||||
// Steps 25-26.
|
// Step 25.
|
||||||
var mnfdDefault = (s === "currency") ? cDigits : 0;
|
if (lazyNumberFormatData.maximumFractionDigits === undefined) {
|
||||||
var mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault);
|
let mxfdDefault = s === "currency"
|
||||||
lazyNumberFormatData.minimumFractionDigits = mnfd;
|
? cDigits
|
||||||
|
: s === "percent"
|
||||||
// Steps 28-29.
|
? 0
|
||||||
var mxfdDefault;
|
: 3;
|
||||||
if (s === "currency")
|
lazyNumberFormatData.maximumFractionDigits =
|
||||||
mxfdDefault = std_Math_max(mnfd, cDigits);
|
std_Math_max(lazyNumberFormatData.minimumFractionDigits, mxfdDefault);
|
||||||
else if (s === "percent")
|
|
||||||
mxfdDefault = std_Math_max(mnfd, 0);
|
|
||||||
else
|
|
||||||
mxfdDefault = std_Math_max(mnfd, 3);
|
|
||||||
var mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20, mxfdDefault);
|
|
||||||
lazyNumberFormatData.maximumFractionDigits = mxfd;
|
|
||||||
|
|
||||||
// Steps 31-32.
|
|
||||||
var mnsd = options.minimumSignificantDigits;
|
|
||||||
var mxsd = options.maximumSignificantDigits;
|
|
||||||
|
|
||||||
// Step 33.
|
|
||||||
if (mnsd !== undefined || mxsd !== undefined) {
|
|
||||||
mnsd = GetNumberOption(options, "minimumSignificantDigits", 1, 21, 1);
|
|
||||||
mxsd = GetNumberOption(options, "maximumSignificantDigits", mnsd, 21, 21);
|
|
||||||
lazyNumberFormatData.minimumSignificantDigits = mnsd;
|
|
||||||
lazyNumberFormatData.maximumSignificantDigits = mxsd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 34.
|
// Step 26.
|
||||||
var g = GetOption(options, "useGrouping", "boolean", undefined, true);
|
var g = GetOption(options, "useGrouping", "boolean", undefined, true);
|
||||||
lazyNumberFormatData.useGrouping = g;
|
lazyNumberFormatData.useGrouping = g;
|
||||||
|
|
||||||
// Step 43.
|
// Steps 35-36.
|
||||||
//
|
//
|
||||||
// We've done everything that must be done now: mark the lazy data as fully
|
// We've done everything that must be done now: mark the lazy data as fully
|
||||||
// computed and install it.
|
// computed and install it.
|
||||||
|
@ -2990,6 +3011,234 @@ function resolveICUPattern(pattern, result) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********** Intl.PluralRules **********/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PluralRules internal properties.
|
||||||
|
*
|
||||||
|
* Spec: ECMAScript 402 API, PluralRules, 1.3.3.
|
||||||
|
*/
|
||||||
|
var pluralRulesInternalProperties = {
|
||||||
|
_availableLocales: null,
|
||||||
|
availableLocales: function()
|
||||||
|
{
|
||||||
|
var locales = this._availableLocales;
|
||||||
|
if (locales)
|
||||||
|
return locales;
|
||||||
|
|
||||||
|
locales = intl_PluralRules_availableLocales();
|
||||||
|
addSpecialMissingLanguageTags(locales);
|
||||||
|
return (this._availableLocales = locales);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute an internal properties object from |lazyPluralRulesData|.
|
||||||
|
*/
|
||||||
|
function resolvePluralRulesInternals(lazyPluralRulesData) {
|
||||||
|
assert(IsObject(lazyPluralRulesData), "lazy data not an object?");
|
||||||
|
|
||||||
|
var internalProps = std_Object_create(null);
|
||||||
|
|
||||||
|
var requestedLocales = lazyPluralRulesData.requestedLocales;
|
||||||
|
|
||||||
|
var PluralRules = pluralRulesInternalProperties;
|
||||||
|
|
||||||
|
// Step 13.
|
||||||
|
const r = ResolveLocale(callFunction(PluralRules.availableLocales, PluralRules),
|
||||||
|
lazyPluralRulesData.requestedLocales,
|
||||||
|
lazyPluralRulesData.opt,
|
||||||
|
noRelevantExtensionKeys, undefined);
|
||||||
|
|
||||||
|
// Step 14.
|
||||||
|
internalProps.locale = r.locale;
|
||||||
|
internalProps.type = lazyPluralRulesData.type;
|
||||||
|
|
||||||
|
internalProps.pluralCategories = intl_GetPluralCategories(
|
||||||
|
internalProps.locale,
|
||||||
|
internalProps.type);
|
||||||
|
|
||||||
|
internalProps.minimumIntegerDigits = lazyPluralRulesData.minimumIntegerDigits;
|
||||||
|
internalProps.minimumFractionDigits = lazyPluralRulesData.minimumFractionDigits;
|
||||||
|
internalProps.maximumFractionDigits = lazyPluralRulesData.maximumFractionDigits;
|
||||||
|
|
||||||
|
if ("minimumSignificantDigits" in lazyPluralRulesData) {
|
||||||
|
assert("maximumSignificantDigits" in lazyPluralRulesData, "min/max sig digits mismatch");
|
||||||
|
internalProps.minimumSignificantDigits = lazyPluralRulesData.minimumSignificantDigits;
|
||||||
|
internalProps.maximumSignificantDigits = lazyPluralRulesData.maximumSignificantDigits;
|
||||||
|
}
|
||||||
|
|
||||||
|
return internalProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object containing the PluralRules internal properties of |obj|,
|
||||||
|
* or throws a TypeError if |obj| isn't PluralRules-initialized.
|
||||||
|
*/
|
||||||
|
function getPluralRulesInternals(obj, methodName) {
|
||||||
|
var internals = getIntlObjectInternals(obj, "PluralRules", methodName);
|
||||||
|
assert(internals.type === "PluralRules", "bad type escaped getIntlObjectInternals");
|
||||||
|
|
||||||
|
var internalProps = maybeInternalProperties(internals);
|
||||||
|
if (internalProps)
|
||||||
|
return internalProps;
|
||||||
|
|
||||||
|
internalProps = resolvePluralRulesInternals(internals.lazyData);
|
||||||
|
setInternalProperties(internals, internalProps);
|
||||||
|
return internalProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes an object as a PluralRules.
|
||||||
|
*
|
||||||
|
* 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 PluralRules.
|
||||||
|
* This later work occurs in |resolvePluralRulesInternals|; steps not noted
|
||||||
|
* here occur there.
|
||||||
|
*
|
||||||
|
* Spec: ECMAScript 402 API, PluralRules, 1.1.1.
|
||||||
|
*/
|
||||||
|
function InitializePluralRules(pluralRules, locales, options) {
|
||||||
|
assert(IsObject(pluralRules), "InitializePluralRules");
|
||||||
|
|
||||||
|
// Step 1.
|
||||||
|
if (isInitializedIntlObject(pluralRules))
|
||||||
|
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
|
||||||
|
|
||||||
|
let internals = initializeIntlObject(pluralRules);
|
||||||
|
|
||||||
|
// Lazy PluralRules data has the following structure:
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// requestedLocales: List of locales,
|
||||||
|
// type: "cardinal" / "ordinal",
|
||||||
|
//
|
||||||
|
// opt: // opt object computer in InitializePluralRules
|
||||||
|
// {
|
||||||
|
// localeMatcher: "lookup" / "best fit",
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// minimumIntegerDigits: integer ∈ [1, 21],
|
||||||
|
// minimumFractionDigits: integer ∈ [0, 20],
|
||||||
|
// maximumFractionDigits: integer ∈ [0, 20],
|
||||||
|
//
|
||||||
|
// // optional
|
||||||
|
// minimumSignificantDigits: integer ∈ [1, 21],
|
||||||
|
// maximumSignificantDigits: integer ∈ [1, 21],
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Note that lazy data is only installed as a final step of initialization,
|
||||||
|
// so every PluralRules lazy data object has *all* these properties, never a
|
||||||
|
// subset of them.
|
||||||
|
const lazyPluralRulesData = std_Object_create(null);
|
||||||
|
|
||||||
|
// Step 3.
|
||||||
|
let requestedLocales = CanonicalizeLocaleList(locales);
|
||||||
|
lazyPluralRulesData.requestedLocales = requestedLocales;
|
||||||
|
|
||||||
|
// Steps 4-5.
|
||||||
|
if (options === undefined)
|
||||||
|
options = {};
|
||||||
|
else
|
||||||
|
options = ToObject(options);
|
||||||
|
|
||||||
|
// Step 6.
|
||||||
|
const type = GetOption(options, "type", "string", ["cardinal", "ordinal"], "cardinal");
|
||||||
|
lazyPluralRulesData.type = type;
|
||||||
|
|
||||||
|
// Step 8.
|
||||||
|
let opt = new Record();
|
||||||
|
lazyPluralRulesData.opt = opt;
|
||||||
|
|
||||||
|
// Steps 9-10.
|
||||||
|
let matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
|
||||||
|
opt.localeMatcher = matcher;
|
||||||
|
|
||||||
|
|
||||||
|
// Step 11.
|
||||||
|
SetNumberFormatDigitOptions(lazyPluralRulesData, options, 0);
|
||||||
|
|
||||||
|
// Step 12.
|
||||||
|
if (lazyPluralRulesData.maximumFractionDigits === undefined) {
|
||||||
|
lazyPluralRulesData.maximumFractionDigits =
|
||||||
|
std_Math_max(lazyPluralRulesData.minimumFractionDigits, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
setLazyData(internals, "PluralRules", lazyPluralRulesData)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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, PluralRules, 1.3.2.
|
||||||
|
*/
|
||||||
|
function Intl_PluralRules_supportedLocalesOf(locales /*, options*/) {
|
||||||
|
var options = arguments.length > 1 ? arguments[1] : undefined;
|
||||||
|
|
||||||
|
// Step 1.
|
||||||
|
var availableLocales = callFunction(pluralRulesInternalProperties.availableLocales,
|
||||||
|
pluralRulesInternalProperties);
|
||||||
|
// Step 2.
|
||||||
|
let requestedLocales = CanonicalizeLocaleList(locales);
|
||||||
|
|
||||||
|
// Step 3.
|
||||||
|
return SupportedLocales(availableLocales, requestedLocales, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a String value representing the plural category matching
|
||||||
|
* the number passed as value according to the
|
||||||
|
* effective locale and the formatting options of this PluralRules.
|
||||||
|
*
|
||||||
|
* Spec: ECMAScript 402 API, PluralRules, 1.4.3.
|
||||||
|
*/
|
||||||
|
function Intl_PluralRules_select(value) {
|
||||||
|
// Step 1.
|
||||||
|
let pluralRules = this;
|
||||||
|
// Step 2.
|
||||||
|
let internals = getPluralRulesInternals(pluralRules, "select");
|
||||||
|
|
||||||
|
// Steps 3-4.
|
||||||
|
let n = ToNumber(value);
|
||||||
|
|
||||||
|
// Step 5.
|
||||||
|
return intl_SelectPluralRule(pluralRules, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the resolved options for a PluralRules object.
|
||||||
|
*
|
||||||
|
* Spec: ECMAScript 402 API, PluralRules, 1.4.4.
|
||||||
|
*/
|
||||||
|
function Intl_PluralRules_resolvedOptions() {
|
||||||
|
var internals = getPluralRulesInternals(this, "resolvedOptions");
|
||||||
|
|
||||||
|
var result = {
|
||||||
|
locale: internals.locale,
|
||||||
|
type: internals.type,
|
||||||
|
pluralCategories: callFunction(std_Array_slice, internals.pluralCategories, 0),
|
||||||
|
minimumIntegerDigits: internals.minimumIntegerDigits,
|
||||||
|
minimumFractionDigits: internals.minimumFractionDigits,
|
||||||
|
maximumFractionDigits: internals.maximumFractionDigits,
|
||||||
|
};
|
||||||
|
|
||||||
|
var optionalProperties = [
|
||||||
|
"minimumSignificantDigits",
|
||||||
|
"maximumSignificantDigits"
|
||||||
|
];
|
||||||
|
|
||||||
|
for (var i = 0; i < optionalProperties.length; i++) {
|
||||||
|
var p = optionalProperties[i];
|
||||||
|
if (callFunction(std_Object_hasOwnProperty, internals, p))
|
||||||
|
_DefineDataProperty(result, p, internals[p]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function Intl_getCanonicalLocales(locales) {
|
function Intl_getCanonicalLocales(locales) {
|
||||||
let codes = CanonicalizeLocaleList(locales);
|
let codes = CanonicalizeLocaleList(locales);
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
|
@ -178,6 +178,7 @@
|
||||||
macro(InitializeCollator, InitializeCollator, "InitializeCollator") \
|
macro(InitializeCollator, InitializeCollator, "InitializeCollator") \
|
||||||
macro(InitializeDateTimeFormat, InitializeDateTimeFormat, "InitializeDateTimeFormat") \
|
macro(InitializeDateTimeFormat, InitializeDateTimeFormat, "InitializeDateTimeFormat") \
|
||||||
macro(InitializeNumberFormat, InitializeNumberFormat, "InitializeNumberFormat") \
|
macro(InitializeNumberFormat, InitializeNumberFormat, "InitializeNumberFormat") \
|
||||||
|
macro(InitializePluralRules, InitializePluralRules, "InitializePluralRules") \
|
||||||
macro(innermost, innermost, "innermost") \
|
macro(innermost, innermost, "innermost") \
|
||||||
macro(inNursery, inNursery, "inNursery") \
|
macro(inNursery, inNursery, "inNursery") \
|
||||||
macro(input, input, "input") \
|
macro(input, input, "input") \
|
||||||
|
@ -270,6 +271,8 @@
|
||||||
macro(parseInt, parseInt, "parseInt") \
|
macro(parseInt, parseInt, "parseInt") \
|
||||||
macro(pattern, pattern, "pattern") \
|
macro(pattern, pattern, "pattern") \
|
||||||
macro(pending, pending, "pending") \
|
macro(pending, pending, "pending") \
|
||||||
|
macro(PluralRules, PluralRules, "PluralRules") \
|
||||||
|
macro(PluralRulesSelect, PluralRulesSelect, "Intl_PluralRules_Select") \
|
||||||
macro(public, public_, "public") \
|
macro(public, public_, "public") \
|
||||||
macro(preventExtensions, preventExtensions, "preventExtensions") \
|
macro(preventExtensions, preventExtensions, "preventExtensions") \
|
||||||
macro(private, private_, "private") \
|
macro(private, private_, "private") \
|
||||||
|
|
|
@ -109,6 +109,7 @@ class GlobalObject : public NativeObject
|
||||||
COLLATOR_PROTO,
|
COLLATOR_PROTO,
|
||||||
NUMBER_FORMAT_PROTO,
|
NUMBER_FORMAT_PROTO,
|
||||||
DATE_TIME_FORMAT_PROTO,
|
DATE_TIME_FORMAT_PROTO,
|
||||||
|
PLURAL_RULES_PROTO,
|
||||||
MODULE_PROTO,
|
MODULE_PROTO,
|
||||||
IMPORT_ENTRY_PROTO,
|
IMPORT_ENTRY_PROTO,
|
||||||
EXPORT_ENTRY_PROTO,
|
EXPORT_ENTRY_PROTO,
|
||||||
|
@ -507,6 +508,11 @@ class GlobalObject : public NativeObject
|
||||||
return getOrCreateObject(cx, global, DATE_TIME_FORMAT_PROTO, initIntlObject);
|
return getOrCreateObject(cx, global, DATE_TIME_FORMAT_PROTO, initIntlObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JSObject*
|
||||||
|
getOrCreatePluralRulesPrototype(JSContext* cx, Handle<GlobalObject*> global) {
|
||||||
|
return getOrCreateObject(cx, global, PLURAL_RULES_PROTO, initIntlObject);
|
||||||
|
}
|
||||||
|
|
||||||
static bool ensureModulePrototypesCreated(JSContext *cx, Handle<GlobalObject*> global);
|
static bool ensureModulePrototypesCreated(JSContext *cx, Handle<GlobalObject*> global);
|
||||||
|
|
||||||
JSObject* maybeGetModulePrototype() {
|
JSObject* maybeGetModulePrototype() {
|
||||||
|
|
|
@ -2605,6 +2605,9 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||||
JS_FN("intl_NumberFormat_availableLocales", intl_NumberFormat_availableLocales, 0,0),
|
JS_FN("intl_NumberFormat_availableLocales", intl_NumberFormat_availableLocales, 0,0),
|
||||||
JS_FN("intl_numberingSystem", intl_numberingSystem, 1,0),
|
JS_FN("intl_numberingSystem", intl_numberingSystem, 1,0),
|
||||||
JS_FN("intl_patternForSkeleton", intl_patternForSkeleton, 2,0),
|
JS_FN("intl_patternForSkeleton", intl_patternForSkeleton, 2,0),
|
||||||
|
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_INLINABLE_FN("IsRegExpObject",
|
JS_INLINABLE_FN("IsRegExpObject",
|
||||||
intrinsic_IsInstanceOfBuiltin<RegExpObject>, 1,0,
|
intrinsic_IsInstanceOfBuiltin<RegExpObject>, 1,0,
|
||||||
|
|
Loading…
Reference in New Issue