summaryrefslogtreecommitdiff
path: root/js/src/builtin/intl
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/builtin/intl')
-rw-r--r--js/src/builtin/intl/Collator.cpp134
-rw-r--r--js/src/builtin/intl/Collator.js35
-rw-r--r--js/src/builtin/intl/CommonFunctions.cpp54
-rw-r--r--js/src/builtin/intl/CommonFunctions.h15
-rw-r--r--js/src/builtin/intl/CommonFunctions.js185
-rw-r--r--js/src/builtin/intl/DateTimeFormat.cpp146
-rw-r--r--js/src/builtin/intl/DateTimeFormat.h4
-rw-r--r--js/src/builtin/intl/DateTimeFormat.js99
-rw-r--r--js/src/builtin/intl/IntlObject.cpp8
-rw-r--r--js/src/builtin/intl/IntlObject.js91
-rw-r--r--js/src/builtin/intl/NumberFormat.cpp151
-rw-r--r--js/src/builtin/intl/NumberFormat.h5
-rw-r--r--js/src/builtin/intl/NumberFormat.js88
-rw-r--r--js/src/builtin/intl/PluralRules.cpp31
-rw-r--r--js/src/builtin/intl/PluralRules.js37
-rw-r--r--js/src/builtin/intl/RelativeTimeFormat.cpp27
-rw-r--r--js/src/builtin/intl/RelativeTimeFormat.js31
17 files changed, 511 insertions, 630 deletions
diff --git a/js/src/builtin/intl/Collator.cpp b/js/src/builtin/intl/Collator.cpp
index 0d2ac1df02..d54bcaa971 100644
--- a/js/src/builtin/intl/Collator.cpp
+++ b/js/src/builtin/intl/Collator.cpp
@@ -79,64 +79,36 @@ static const JSFunctionSpec collator_methods[] = {
* ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
*/
static bool
-Collator(JSContext* cx, const CallArgs& args, bool construct)
+Collator(JSContext* cx, const CallArgs& args)
{
- RootedObject obj(cx);
-
- // We're following ECMA-402 1st Edition when Collator is called because of
- // backward compatibility issues.
- // See https://github.com/tc39/ecma402/issues/57
- if (!construct) {
- // ES Intl 1st ed., 10.1.2.1 step 3
- JSObject* intl = GlobalObject::getOrCreateIntlObject(cx, cx->global());
- if (!intl)
- return false;
- RootedValue self(cx, args.thisv());
- if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
- // ES Intl 1st ed., 10.1.2.1 step 4
- obj = ToObject(cx, self);
- if (!obj)
- return false;
-
- // ES Intl 1st ed., 10.1.2.1 step 5
- bool extensible;
- if (!IsExtensible(cx, obj, &extensible))
- return false;
- if (!extensible)
- return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
- } else {
- // ES Intl 1st ed., 10.1.2.1 step 3.a
- construct = true;
- }
- }
- if (construct) {
- // Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
- RootedObject proto(cx);
- if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
- return false;
+ // Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
- if (!proto) {
- proto = GlobalObject::getOrCreateCollatorPrototype(cx, cx->global());
- if (!proto)
- return false;
- }
+ // Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
+ RootedObject proto(cx);
+ if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
+ return false;
- obj = NewObjectWithGivenProto<CollatorObject>(cx, proto);
- if (!obj)
+ if (!proto) {
+ proto = GlobalObject::getOrCreateCollatorPrototype(cx, cx->global());
+ if (!proto)
return false;
-
- obj->as<NativeObject>().setReservedSlot(CollatorObject::INTERNALS_SLOT, NullValue());
- obj->as<NativeObject>().setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(nullptr));
}
- RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
- RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
+ Rooted<CollatorObject*> collator(cx, NewObjectWithGivenProto<CollatorObject>(cx, proto));
+ if (!collator)
+ return false;
+
+ collator->setReservedSlot(CollatorObject::INTERNALS_SLOT, NullValue());
+ collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(nullptr));
+
+ RootedValue locales(cx, args.get(0));
+ RootedValue options(cx, args.get(1));
// Step 6.
- if (!intl::InitializeObject(cx, obj, cx->names().InitializeCollator, locales, options))
+ if (!intl::InitializeObject(cx, collator, cx->names().InitializeCollator, locales, options))
return false;
- args.rval().setObject(*obj);
+ args.rval().setObject(*collator);
return true;
}
@@ -144,7 +116,7 @@ static bool
Collator(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
- return Collator(cx, args, args.isConstructing());
+ return Collator(cx, args);
}
bool
@@ -153,9 +125,8 @@ js::intl_Collator(JSContext* cx, unsigned argc, Value* vp)
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
MOZ_ASSERT(!args.isConstructing());
- // intl_Collator is an intrinsic for self-hosted JavaScript, so it cannot
- // be used with "new", but it still has to be treated as a constructor.
- return Collator(cx, args, true);
+
+ return Collator(cx, args);
}
void
@@ -163,15 +134,9 @@ js::CollatorObject::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(CollatorObject::UCOLLATOR_SLOT);
- if (!slot.isUndefined()) {
- if (UCollator* coll = static_cast<UCollator*>(slot.toPrivate()))
- ucol_close(coll);
- }
+ const Value& slot = obj->as<CollatorObject>().getReservedSlot(CollatorObject::UCOLLATOR_SLOT);
+ if (UCollator* coll = static_cast<UCollator*>(slot.toPrivate()))
+ ucol_close(coll);
}
JSObject*
@@ -182,10 +147,9 @@ js::CreateCollatorPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObjec
if (!ctor)
return nullptr;
- RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &CollatorObject::class_));
+ RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
if (!proto)
return nullptr;
- proto->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(nullptr));
if (!LinkConstructorAndPrototype(cx, ctor, proto))
return nullptr;
@@ -213,14 +177,6 @@ js::CreateCollatorPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObjec
return nullptr;
}
- RootedValue options(cx);
- if (!intl::CreateDefaultOptions(cx, &options))
- return nullptr;
-
- // 10.2.1 and 10.3
- if (!intl::InitializeObject(cx, proto, cx->names().InitializeCollator, UndefinedHandleValue, options))
- return nullptr;
-
// 8.1
RootedValue ctorValue(cx, ObjectValue(*ctor));
if (!DefineProperty(cx, Intl, cx->names().Collator, ctorValue, nullptr, nullptr, 0))
@@ -313,7 +269,7 @@ js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp)
* of the given Collator.
*/
static UCollator*
-NewUCollator(JSContext* cx, HandleObject collator)
+NewUCollator(JSContext* cx, Handle<CollatorObject*> collator)
{
RootedValue value(cx);
@@ -490,40 +446,20 @@ js::intl_CompareStrings(JSContext* cx, unsigned argc, Value* vp)
MOZ_ASSERT(args[2].isString());
Rooted<CollatorObject*> collator(cx, &args[0].toObject().as<CollatorObject>());
-
- // Obtain a UCollator object, cached if possible.
+
+ // Obtain a cached UCollator object.
// XXX Does this handle Collator instances from other globals correctly?
- bool isCollatorInstance = collator->getClass() == &CollatorObject::class_;
- UCollator* coll;
- if (isCollatorInstance) {
- void* priv = collator->getReservedSlot(CollatorObject::UCOLLATOR_SLOT).toPrivate();
- coll = static_cast<UCollator*>(priv);
- if (!coll) {
- coll = NewUCollator(cx, collator);
- if (!coll)
- return false;
- collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(coll));
- }
- } else {
- // There's no good place to cache the ICU collator for an object
- // that has been initialized as a Collator but is not a Collator
- // instance. One possibility might be to add a Collator instance as an
- // internal property to each such object.
+ void* priv = collator->getReservedSlot(CollatorObject::UCOLLATOR_SLOT).toPrivate();
+ UCollator* coll = static_cast<UCollator*>(priv);
+ if (!coll) {
coll = NewUCollator(cx, collator);
if (!coll)
return false;
+ collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(coll));
}
// Use the UCollator to actually compare the strings.
RootedString str1(cx, args[1].toString());
RootedString str2(cx, args[2].toString());
- RootedValue result(cx);
- bool success = intl_CompareStrings(cx, coll, str1, str2, &result);
-
- if (!isCollatorInstance)
- ucol_close(coll);
- if (!success)
- return false;
- args.rval().set(result);
- return true;
+ return intl_CompareStrings(cx, coll, str1, str2, args.rval());
}
diff --git a/js/src/builtin/intl/Collator.js b/js/src/builtin/intl/Collator.js
index 48040e1abd..eb96f6cc54 100644
--- a/js/src/builtin/intl/Collator.js
+++ b/js/src/builtin/intl/Collator.js
@@ -107,11 +107,13 @@ function resolveCollatorInternals(lazyCollatorData)
/**
- * Returns an object containing the Collator internal properties of |obj|, or
- * throws a TypeError if |obj| isn't Collator-initialized.
+ * Returns an object containing the Collator internal properties of |obj|.
*/
-function getCollatorInternals(obj, methodName) {
- var internals = getIntlObjectInternals(obj, "Collator", methodName);
+function getCollatorInternals(obj) {
+ assert(IsObject(obj), "getCollatorInternals called with non-object");
+ assert(IsCollator(obj), "getCollatorInternals called with non-Collator");
+
+ var internals = getIntlObjectInternals(obj);
assert(internals.type === "Collator", "bad type escaped getIntlObjectInternals");
// If internal properties have already been computed, use them.
@@ -138,14 +140,11 @@ function getCollatorInternals(obj, methodName) {
* Spec: ECMAScript Internationalization API Specification, 10.1.1.
*/
function InitializeCollator(collator, locales, options) {
- assert(IsObject(collator), "InitializeCollator");
-
- // Step 1.
- if (isInitializedIntlObject(collator))
- ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
+ assert(IsObject(collator), "InitializeCollator called with non-object");
+ assert(IsCollator(collator), "InitializeCollator called with non-Collator");
- // Step 2.
- var internals = initializeIntlObject(collator);
+ // Steps 1-2 (These steps are no longer required and should be removed
+ // from the spec; https://github.com/tc39/ecma402/issues/115).;
// Lazy Collator data has the following structure:
//
@@ -219,7 +218,7 @@ function InitializeCollator(collator, locales, options) {
//
// We've done everything that must be done now: mark the lazy data as fully
// computed and install it.
- setLazyData(internals, "Collator", lazyCollatorData);
+ initializeIntlObject(collator, "Collator", lazyCollatorData);
}
@@ -311,14 +310,17 @@ function collatorCompareToBind(x, y) {
*/
function Intl_Collator_compare_get() {
// Check "this Collator object" per introduction of section 10.3.
- var internals = getCollatorInternals(this, "compare");
+ if (!IsObject(this) || !IsCollator(this))
+ ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "Collator", "compare", "Collator");
+
+ var internals = getCollatorInternals(this);
// Step 1.
if (internals.boundCompare === undefined) {
// Step 1.a.
var F = collatorCompareToBind;
- // Step 1.b-d.
+ // Steps 1.b-d.
var bc = callFunction(FunctionBind, F, this);
internals.boundCompare = bc;
}
@@ -335,7 +337,10 @@ function Intl_Collator_compare_get() {
*/
function Intl_Collator_resolvedOptions() {
// Check "this Collator object" per introduction of section 10.3.
- var internals = getCollatorInternals(this, "resolvedOptions");
+ if (!IsObject(this) || !IsCollator(this))
+ ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "Collator", "resolvedOptions", "Collator");
+
+ var internals = getCollatorInternals(this);
var result = {
locale: internals.locale,
diff --git a/js/src/builtin/intl/CommonFunctions.cpp b/js/src/builtin/intl/CommonFunctions.cpp
index 1de598bd9a..98432966ed 100644
--- a/js/src/builtin/intl/CommonFunctions.cpp
+++ b/js/src/builtin/intl/CommonFunctions.cpp
@@ -20,25 +20,9 @@
#include "jsobjinlines.h"
bool
-js::intl::CreateDefaultOptions(JSContext* cx, MutableHandleValue defaultOptions)
-{
- RootedObject options(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
- if (!options)
- return false;
- defaultOptions.setObject(*options);
- return true;
-}
-
-bool
js::intl::InitializeObject(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
HandleValue locales, HandleValue options)
{
- RootedValue initializerValue(cx);
- if (!GlobalObject::getIntrinsicValue(cx, cx->global(), initializer, &initializerValue))
- return false;
- MOZ_ASSERT(initializerValue.isObject());
- MOZ_ASSERT(initializerValue.toObject().is<JSFunction>());
-
FixedInvokeArgs<3> args(cx);
args[0].setObject(*obj);
@@ -47,7 +31,32 @@ js::intl::InitializeObject(JSContext* cx, HandleObject obj, Handle<PropertyName*
RootedValue thisv(cx, NullValue());
RootedValue ignored(cx);
- return js::Call(cx, initializerValue, thisv, args, &ignored);
+ if (!js::CallSelfHostedFunction(cx, initializer, thisv, args, &ignored))
+ return false;
+
+ MOZ_ASSERT(ignored.isUndefined(),
+ "Unexpected return value from non-legacy Intl object initializer");
+ return true;
+}
+
+bool
+js::intl::LegacyIntlInitialize(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
+ HandleValue thisValue, HandleValue locales, HandleValue options,
+ MutableHandleValue result)
+{
+ FixedInvokeArgs<4> args(cx);
+
+ args[0].setObject(*obj);
+ args[1].set(thisValue);
+ args[2].set(locales);
+ args[3].set(options);
+
+ RootedValue thisv(cx, NullValue());
+ if (!js::CallSelfHostedFunction(cx, initializer, thisv, args, result))
+ return false;
+
+ MOZ_ASSERT(result.isObject(), "Legacy Intl object initializer must return an object");
+ return true;
}
/**
@@ -56,21 +65,12 @@ js::intl::InitializeObject(JSContext* cx, HandleObject obj, Handle<PropertyName*
JSObject*
js::intl::GetInternalsObject(JSContext* cx, HandleObject obj)
{
- RootedValue getInternalsValue(cx);
- if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().getInternals,
- &getInternalsValue))
- {
- return nullptr;
- }
- MOZ_ASSERT(getInternalsValue.isObject());
- MOZ_ASSERT(getInternalsValue.toObject().is<JSFunction>());
-
FixedInvokeArgs<1> args(cx);
args[0].setObject(*obj);
RootedValue v(cx, NullValue());
- if (!js::Call(cx, getInternalsValue, v, args, &v))
+ if (!js::CallSelfHostedFunction(cx, cx->names().getInternals, v, args, &v))
return nullptr;
return &v.toObject();
diff --git a/js/src/builtin/intl/CommonFunctions.h b/js/src/builtin/intl/CommonFunctions.h
index 47705ab7c7..b364698d2c 100644
--- a/js/src/builtin/intl/CommonFunctions.h
+++ b/js/src/builtin/intl/CommonFunctions.h
@@ -27,12 +27,6 @@ namespace js {
namespace intl {
/**
- * Setup the |options| argument of |IntlInitialize|
- */
-extern bool
-CreateDefaultOptions(JSContext* cx, MutableHandleValue defaultOptions);
-
-/**
* Initialize a new Intl.* object using the named self-hosted function.
*/
extern bool
@@ -40,6 +34,15 @@ InitializeObject(JSContext* cx, HandleObject obj, Handle<PropertyName*> initiali
HandleValue locales, HandleValue options);
/**
+ * Initialize an existing object as an Intl.* object using the named
+ * self-hosted function. This is only for a few old Intl.* constructors, for
+ * legacy reasons -- new ones should use the function above instead.
+ */
+extern bool
+LegacyIntlInitialize(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
+ HandleValue thisValue, HandleValue locales, HandleValue options,
+ MutableHandleValue result);
+/**
* Returns the object holding the internal properties for obj.
*/
extern JSObject*
diff --git a/js/src/builtin/intl/CommonFunctions.js b/js/src/builtin/intl/CommonFunctions.js
index 5a963edfbf..10e02a5ac6 100644
--- a/js/src/builtin/intl/CommonFunctions.js
+++ b/js/src/builtin/intl/CommonFunctions.js
@@ -1057,26 +1057,38 @@ function GetNumberOption(options, property, minimum, maximum, fallback) {
}
+// Symbols in the self-hosting compartment can't be cloned, use a separate
+// object to hold the actual symbol value.
+// TODO: Can we add support to clone symbols?
+var intlFallbackSymbolHolder = { value: undefined };
+
/**
- * Weak map used to track the initialize-as-Intl status (and, if an object has
- * been so initialized, the Intl-specific internal properties) of all objects.
- * Presence of an object as a key within this map indicates that the object has
- * its [[initializedIntlObject]] internal property set to true. The associated
- * value is an object whose structure is documented in |initializeIntlObject|
- * below.
+ * The [[FallbackSymbol]] symbol of the %Intl% intrinsic object.
*
- * Ideally we'd be using private symbols for internal properties, but
- * SpiderMonkey doesn't have those yet.
+ * This symbol is used to implement the legacy constructor semantics for
+ * Intl.DateTimeFormat and Intl.NumberFormat.
*/
-var internalsMap = new WeakMap();
+function intlFallbackSymbol() {
+ var fallbackSymbol = intlFallbackSymbolHolder.value;
+ if (!fallbackSymbol)
+ intlFallbackSymbolHolder.value = fallbackSymbol = std_Symbol();
+ return fallbackSymbol;
+}
/**
- * Set the [[initializedIntlObject]] internal property of |obj| to true.
+ * Initializes the INTL_INTERNALS_OBJECT_SLOT of the given object.
*/
-function initializeIntlObject(obj) {
+function initializeIntlObject(obj, type, lazyData) {
assert(IsObject(obj), "Non-object passed to initializeIntlObject");
+ assert((type === "Collator" && IsCollator(obj)) ||
+ (type === "DateTimeFormat" && IsDateTimeFormat(obj)) ||
+ (type === "NumberFormat" && IsNumberFormat(obj)) ||
+ (type === "PluralRules" && IsPluralRules(obj)) ||
+ (type === "RelativeTimeFormat" && IsRelativeTimeFormat(obj)),
+ "type must match the object's class");
+ assert(IsObject(lazyData), "non-object lazy data");
// Intl-initialized objects are weird. They have [[initializedIntlObject]]
// set on them, but they don't *necessarily* have any other properties.
@@ -1084,51 +1096,26 @@ function initializeIntlObject(obj) {
// The meaning of an internals object for an object |obj| is as follows.
//
- // If the .type is "partial", |obj| has [[initializedIntlObject]] set but
- // nothing else. No other property of |internals| can be used. (This
- // occurs when InitializeCollator or similar marks an object as
- // [[initializedIntlObject]] but fails before marking it as the appropriate
- // more-specific type ["Collator", "DateTimeFormat", "NumberFormat"].)
- //
- // Otherwise, the .type indicates the type of Intl object that |obj| is:
- // "Collator", "DateTimeFormat", or "NumberFormat" (likely with more coming
- // in future Intl specs). In these cases |obj| *conceptually* also has
- // [[initializedCollator]] or similar set, and all the other properties
- // implied by that.
+ // The .type property indicates the type of Intl object that |obj| is:
+ // "Collator", "DateTimeFormat", "NumberFormat", or "PluralRules" (likely
+ // with more coming in future Intl specs).
//
- // If |internals| doesn't have a "partial" .type, two additional properties
- // have meaning. The .lazyData property stores information needed to
- // compute -- without observable side effects -- the actual internal Intl
- // properties of |obj|. If it is non-null, then the actual internal
- // properties haven't been computed, and .lazyData must be processed by
+ // The .lazyData property stores information needed to compute -- without
+ // observable side effects -- the actual internal Intl properties of
+ // |obj|. If it is non-null, then the actual internal properties haven't
+ // been computed, and .lazyData must be processed by
// |setInternalProperties| before internal Intl property values are
// available. If it is null, then the .internalProps property contains an
// object whose properties are the internal Intl properties of |obj|.
- internals.type = "partial";
- internals.lazyData = null;
+ var internals = std_Object_create(null);
+ internals.type = type;
+ internals.lazyData = lazyData;
internals.internalProps = null;
- callFunction(std_WeakMap_set, internalsMap, obj, internals);
- return internals;
-}
-
-
-/**
- * Mark |internals| as having the given type and lazy data.
- */
-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 === "RelativeTimeFormat",
- "bad type");
- assert(IsObject(lazyData), "non-object lazy data");
-
- // Set in reverse order so that the .type change is a barrier.
- internals.lazyData = lazyData;
- internals.type = type;
+ assert(UnsafeGetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT) === null,
+ "Internal slot already initialized?");
+ UnsafeSetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT, internals);
}
@@ -1136,9 +1123,7 @@ function setLazyData(internals, type, lazyData)
* Set the internal properties object for an |internals| object previously
* associated with lazy data.
*/
-function setInternalProperties(internals, internalProps)
-{
- assert(internals.type !== "partial", "newborn internals can't have computed internals");
+function setInternalProperties(internals, internalProps) {
assert(IsObject(internals.lazyData), "lazy data must exist already");
assert(IsObject(internalProps), "internalProps argument should be an object");
@@ -1152,10 +1137,8 @@ function setInternalProperties(internals, internalProps)
* Get the existing internal properties out of a non-newborn |internals|, or
* null if none have been computed.
*/
-function maybeInternalProperties(internals)
-{
+function maybeInternalProperties(internals) {
assert(IsObject(internals), "non-object passed to maybeInternalProperties");
- assert(internals.type !== "partial", "maybeInternalProperties must only be used on completely-initialized internals objects");
var lazyData = internals.lazyData;
if (lazyData)
return null;
@@ -1165,48 +1148,33 @@ function maybeInternalProperties(internals)
/**
- * Return whether |obj| has an[[initializedIntlObject]] property set to true.
- */
-function isInitializedIntlObject(obj) {
-#ifdef DEBUG
- var internals = callFunction(std_WeakMap_get, internalsMap, obj);
- if (IsObject(internals)) {
- assert(hasOwn("type", internals), "missing type");
- var type = internals.type;
- assert(type === "partial" || type === "Collator" ||
- type === "DateTimeFormat" || type === "NumberFormat" ||
- type === "PluralRules" || type === "RelativeTimeFormat",
- "unexpected type");
- assert(hasOwn("lazyData", internals), "missing lazyData");
- assert(hasOwn("internalProps", internals), "missing internalProps");
- } else {
- assert(internals === undefined, "bad mapping for |obj|");
- }
-#endif
- return callFunction(std_WeakMap_has, internalsMap, obj);
-}
-
-
-/**
- * Check that |obj| meets the requirements for "this Collator object", "this
- * NumberFormat object", or "this DateTimeFormat object" as used in the method
- * with the given name. Throw a TypeError if |obj| doesn't meet these
- * requirements. But if it does, return |obj|'s internals object (*not* the
- * object holding its internal properties!), associated with it by
- * |internalsMap|, with structure specified above.
+ * Return |obj|'s internals object (*not* the object holding its internal
+ * properties!), with structure specified above.
*
* Spec: ECMAScript Internationalization API Specification, 10.3.
* Spec: ECMAScript Internationalization API Specification, 11.3.
* Spec: ECMAScript Internationalization API Specification, 12.3.
*/
-function getIntlObjectInternals(obj, className, methodName) {
- assert(typeof className === "string", "bad className for getIntlObjectInternals");
-
- var internals = callFunction(std_WeakMap_get, internalsMap, obj);
- assert(internals === undefined || isInitializedIntlObject(obj), "bad mapping in internalsMap");
-
- if (internals === undefined || internals.type !== className)
- ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, className, methodName, className);
+function getIntlObjectInternals(obj) {
+ assert(IsObject(obj), "getIntlObjectInternals called with non-Object");
+ assert(IsCollator(obj) || IsDateTimeFormat(obj) || IsNumberFormat(obj) ||
+ IsPluralRules(obj) || IsRelativeTimeFormat(obj),
+ "getIntlObjectInternals called with non-Intl object");
+
+ var internals = UnsafeGetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT);
+
+ assert(IsObject(internals), "internals not an object");
+ assert(hasOwn("type", internals), "missing type");
+ assert((internals.type === "Collator" && IsCollator(obj)) ||
+ (internals.type === "DateTimeFormat" && IsDateTimeFormat(obj)) ||
+ (internals.type === "NumberFormat" && IsNumberFormat(obj)) ||
+ (internals.type === "PluralRules" && IsPluralRules(obj)) ||
+ (internals.type === "RelativeTimeFormat" && IsRelativeTimeFormat(obj)),
+ "type must match the object's class");
+ assert(hasOwn("lazyData", internals),
+ "missing lazyData");
+ assert(hasOwn("internalProps", internals),
+ "missing internalProps");
return internals;
}
@@ -1216,35 +1184,32 @@ function getIntlObjectInternals(obj, className, methodName) {
* Get the internal properties of known-Intl object |obj|. For use only by
* C++ code that knows what it's doing!
*/
-function getInternals(obj)
-{
- assert(isInitializedIntlObject(obj), "for use only on guaranteed Intl objects");
+function getInternals(obj) {
+ var internals = getIntlObjectInternals(obj);
- var internals = callFunction(std_WeakMap_get, internalsMap, obj);
+ // If internal properties have already been computed, use them.
+ var internalProps = maybeInternalProperties(internals);
+ if (internalProps)
+ return internalProps;
- assert(internals.type !== "partial", "must have been successfully initialized");
- var lazyData = internals.lazyData;
- if (!lazyData)
- return internals.internalProps;
-
- var internalProps;
+ // Otherwise it's time to fully create them.
var type = internals.type;
-
+
switch (type) {
case "Collator":
- internalProps = resolveCollatorInternals(lazyData);
+ internalProps = resolveCollatorInternals(internals.lazyData);
break;
case "DateTimeFormat":
- internalProps = resolveDateTimeFormatInternals(lazyData);
+ internalProps = resolveDateTimeFormatInternals(internals.lazyData);
break;
case "PluralRules":
- internalProps = resolvePluralRulesInternals(lazyData);
+ internalProps = resolvePluralRulesInternals(internals.lazyData);
break;
- case "RelativeTimeFormat":
- internalProps = resolveRelativeTimeFormatInternals(lazyData);
+ case "NumberFormat":
+ internalProps = resolveNumberFormatInternals(internals.lazyData);
break;
- default: // type === "NumberFormat"
- internalProps = resolveNumberFormatInternals(lazyData);
+ default: // type === "RelativeTimeFormat"
+ internalProps = resolveRelativeTimeFormatInternals(internals.lazyData);
break;
}
setInternalProperties(internals, internalProps);
diff --git a/js/src/builtin/intl/DateTimeFormat.cpp b/js/src/builtin/intl/DateTimeFormat.cpp
index 5aa8e0d7a2..cff7519cbb 100644
--- a/js/src/builtin/intl/DateTimeFormat.cpp
+++ b/js/src/builtin/intl/DateTimeFormat.cpp
@@ -91,63 +91,35 @@ static const JSFunctionSpec dateTimeFormat_methods[] = {
static bool
DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct)
{
- RootedObject obj(cx);
-
- // We're following ECMA-402 1st Edition when DateTimeFormat is called
- // because of backward compatibility issues.
- // See https://github.com/tc39/ecma402/issues/57
- if (!construct) {
- // ES Intl 1st ed., 12.1.2.1 step 3
- JSObject* intl = GlobalObject::getOrCreateIntlObject(cx, cx->global());
- if (!intl)
- return false;
- RootedValue self(cx, args.thisv());
- if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
- // ES Intl 1st ed., 12.1.2.1 step 4
- obj = ToObject(cx, self);
- if (!obj)
- return false;
+ // Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
- // ES Intl 1st ed., 12.1.2.1 step 5
- bool extensible;
- if (!IsExtensible(cx, obj, &extensible))
- return false;
- if (!extensible)
- return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
- } else {
- // ES Intl 1st ed., 12.1.2.1 step 3.a
- construct = true;
- }
- }
- if (construct) {
- // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
- RootedObject proto(cx);
- if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
- return false;
-
- if (!proto) {
- proto = GlobalObject::getOrCreateDateTimeFormatPrototype(cx, cx->global());
- if (!proto)
- return false;
- }
+ // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
+ RootedObject proto(cx);
+ if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
+ return false;
- obj = NewObjectWithGivenProto<DateTimeFormatObject>(cx, proto);
- if (!obj)
+ if (!proto) {
+ proto = GlobalObject::getOrCreateDateTimeFormatPrototype(cx, cx->global());
+ if (!proto)
return false;
-
- obj->as<NativeObject>().setReservedSlot(DateTimeFormatObject::INTERNALS_SLOT, NullValue());
- obj->as<NativeObject>().setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT, PrivateValue(nullptr));
}
- RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
- RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
-
- // Step 3.
- if (!intl::InitializeObject(cx, obj, cx->names().InitializeDateTimeFormat, locales, options))
+ Rooted<DateTimeFormatObject*> dateTimeFormat(cx);
+ dateTimeFormat = NewObjectWithGivenProto<DateTimeFormatObject>(cx, proto);
+ if (!dateTimeFormat)
return false;
- args.rval().setObject(*obj);
- return true;
+ dateTimeFormat->setReservedSlot(DateTimeFormatObject::INTERNALS_SLOT, NullValue());
+ dateTimeFormat->setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT,
+ PrivateValue(nullptr));
+
+ RootedValue thisValue(cx, construct ? ObjectValue(*dateTimeFormat) : args.thisv());
+ RootedValue locales(cx, args.get(0));
+ RootedValue options(cx, args.get(1));
+
+ // Step 3.
+ return intl::LegacyIntlInitialize(cx, dateTimeFormat, cx->names().InitializeDateTimeFormat,
+ thisValue, locales, options, args.rval());
}
static bool
@@ -174,30 +146,23 @@ js::DateTimeFormatObject::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<DateTimeFormatObject>().getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT);
- if (!slot.isUndefined()) {
- if (UDateFormat* df = static_cast<UDateFormat*>(slot.toPrivate()))
- udat_close(df);
- }
+ if (UDateFormat* df = static_cast<UDateFormat*>(slot.toPrivate()))
+ udat_close(df);
}
JSObject*
-js::CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
+js::CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global,
+ MutableHandleObject constructor)
{
RootedFunction ctor(cx);
ctor = GlobalObject::createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0);
if (!ctor)
return nullptr;
- RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global,
- &DateTimeFormatObject::class_));
+ RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
if (!proto)
return nullptr;
- proto->setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT, PrivateValue(nullptr));
if (!LinkConstructorAndPrototype(cx, ctor, proto))
return nullptr;
@@ -226,22 +191,12 @@ js::CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<Globa
return nullptr;
}
- RootedValue options(cx);
- if (!intl::CreateDefaultOptions(cx, &options))
- return nullptr;
-
- // 12.2.1 and 12.3
- if (!intl::InitializeObject(cx, proto, cx->names().InitializeDateTimeFormat, UndefinedHandleValue,
- options))
- {
- return nullptr;
- }
-
// 8.1
RootedValue ctorValue(cx, ObjectValue(*ctor));
if (!DefineProperty(cx, Intl, cx->names().DateTimeFormat, ctorValue, nullptr, nullptr, 0))
return nullptr;
+ constructor.set(ctor);
return proto;
}
@@ -495,7 +450,7 @@ js::intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp)
* of the given DateTimeFormat.
*/
static UDateFormat*
-NewUDateFormat(JSContext* cx, HandleObject dateTimeFormat)
+NewUDateFormat(JSContext* cx, Handle<DateTimeFormatObject*> dateTimeFormat)
{
RootedValue value(cx);
@@ -779,42 +734,23 @@ js::intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp)
MOZ_ASSERT(args[1].isNumber());
MOZ_ASSERT(args[2].isBoolean());
- RootedObject dateTimeFormat(cx, &args[0].toObject());
-
- // Obtain a UDateFormat object, cached if possible.
- bool isDateTimeFormatInstance = dateTimeFormat->getClass() == &DateTimeFormatObject::class_;
- UDateFormat* df;
- if (isDateTimeFormatInstance) {
- void* priv =
- dateTimeFormat->as<NativeObject>().getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT).toPrivate();
- df = static_cast<UDateFormat*>(priv);
- if (!df) {
- df = NewUDateFormat(cx, dateTimeFormat);
- if (!df)
- return false;
- dateTimeFormat->as<NativeObject>().setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT, PrivateValue(df));
- }
- } else {
- // There's no good place to cache the ICU date-time format for an object
- // that has been initialized as a DateTimeFormat but is not a
- // DateTimeFormat instance. One possibility might be to add a
- // DateTimeFormat instance as an internal property to each such object.
+ Rooted<DateTimeFormatObject*> dateTimeFormat(cx);
+ dateTimeFormat = &args[0].toObject().as<DateTimeFormatObject>();
+
+ // Obtain a cached UDateFormat object.
+ void* priv =
+ dateTimeFormat->getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT).toPrivate();
+ UDateFormat* df = static_cast<UDateFormat*>(priv);
+ if (!df) {
df = NewUDateFormat(cx, dateTimeFormat);
if (!df)
return false;
+ dateTimeFormat->setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT, PrivateValue(df));
}
// Use the UDateFormat to actually format the time stamp.
- RootedValue result(cx);
- bool success = args[2].toBoolean()
- ? intl_FormatToPartsDateTime(cx, df, args[1].toNumber(), &result)
- : intl_FormatDateTime(cx, df, args[1].toNumber(), &result);
-
- if (!isDateTimeFormatInstance)
- udat_close(df);
- if (!success)
- return false;
- args.rval().set(result);
- return true;
+ return args[2].toBoolean()
+ ? intl_FormatToPartsDateTime(cx, df, args[1].toNumber(), args.rval())
+ : intl_FormatDateTime(cx, df, args[1].toNumber(), args.rval());
}
diff --git a/js/src/builtin/intl/DateTimeFormat.h b/js/src/builtin/intl/DateTimeFormat.h
index ff40de3c80..4eff39ec62 100644
--- a/js/src/builtin/intl/DateTimeFormat.h
+++ b/js/src/builtin/intl/DateTimeFormat.h
@@ -41,7 +41,7 @@ class DateTimeFormatObject : public NativeObject
extern JSObject*
CreateDateTimeFormatPrototype(JSContext* cx, JS::Handle<JSObject*> Intl,
- JS::Handle<GlobalObject*> global);
+ JS::Handle<GlobalObject*> global, MutableHandleObject constructor);
/**
* Returns a new instance of the standard built-in DateTimeFormat constructor.
@@ -131,7 +131,7 @@ intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp);
*
* Spec: ECMAScript Internationalization API Specification, 12.3.2.
*
- * Usage: formatted = intl_FormatDateTime(dateTimeFormat, x)
+ * Usage: formatted = intl_FormatDateTime(dateTimeFormat, x, formatToParts)
*/
extern MOZ_MUST_USE bool
intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp);
diff --git a/js/src/builtin/intl/DateTimeFormat.js b/js/src/builtin/intl/DateTimeFormat.js
index e0167cda30..bff6464250 100644
--- a/js/src/builtin/intl/DateTimeFormat.js
+++ b/js/src/builtin/intl/DateTimeFormat.js
@@ -90,11 +90,13 @@ function resolveDateTimeFormatInternals(lazyDateTimeFormatData) {
/**
- * Returns an object containing the DateTimeFormat internal properties of |obj|,
- * or throws a TypeError if |obj| isn't DateTimeFormat-initialized.
+ * Returns an object containing the DateTimeFormat internal properties of |obj|.
*/
-function getDateTimeFormatInternals(obj, methodName) {
- var internals = getIntlObjectInternals(obj, "DateTimeFormat", methodName);
+function getDateTimeFormatInternals(obj) {
+ assert(IsObject(obj), "getDateTimeFormatInternals called with non-object");
+ assert(IsDateTimeFormat(obj), "getDateTimeFormatInternals called with non-DateTimeFormat");
+
+ var internals = getIntlObjectInternals(obj);
assert(internals.type === "DateTimeFormat", "bad type escaped getIntlObjectInternals");
// If internal properties have already been computed, use them.
@@ -214,6 +216,29 @@ function DefaultTimeZone() {
return defaultTimeZone;
}
+
+/**
+ * UnwrapDateTimeFormat(dtf)
+ */
+function UnwrapDateTimeFormat(dtf, methodName) {
+ // Step 1.
+ if ((!IsObject(dtf) || !IsDateTimeFormat(dtf)) &&
+ dtf instanceof GetDateTimeFormatConstructor())
+ {
+ dtf = dtf[intlFallbackSymbol()];
+ }
+
+ // Step 2.
+ if (!IsObject(dtf) || !IsDateTimeFormat(dtf)) {
+ ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "DateTimeFormat", methodName,
+ "DateTimeFormat");
+ }
+
+ // Step 3.
+ return dtf;
+}
+
+
/**
* Initializes an object as a DateTimeFormat.
*
@@ -225,15 +250,13 @@ function DefaultTimeZone() {
*
* Spec: ECMAScript Internationalization API Specification, 12.1.1.
*/
-function InitializeDateTimeFormat(dateTimeFormat, locales, options) {
- assert(IsObject(dateTimeFormat), "InitializeDateTimeFormat");
-
- // Step 1.
- if (isInitializedIntlObject(dateTimeFormat))
- ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
+function InitializeDateTimeFormat(dateTimeFormat, thisValue, locales, options) {
+ assert(IsObject(dateTimeFormat), "InitializeDateTimeFormat called with non-Object");
+ assert(IsDateTimeFormat(dateTimeFormat),
+ "InitializeDateTimeFormat called with non-DateTimeFormat");
- // Step 2.
- var internals = initializeIntlObject(dateTimeFormat);
+ // Steps 1-2 (These steps are no longer required and should be removed
+ // from the spec; https://github.com/tc39/ecma402/issues/115).
// Lazy DateTimeFormat data has the following structure:
//
@@ -334,7 +357,19 @@ function InitializeDateTimeFormat(dateTimeFormat, locales, options) {
//
// We've done everything that must be done now: mark the lazy data as fully
// computed and install it.
- setLazyData(internals, "DateTimeFormat", lazyDateTimeFormatData);
+ initializeIntlObject(dateTimeFormat, "DateTimeFormat", lazyDateTimeFormatData);
+
+ if (dateTimeFormat !== thisValue && thisValue instanceof GetDateTimeFormatConstructor()) {
+ if (!IsObject(thisValue))
+ ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, typeof thisValue);
+
+ _DefineDataProperty(thisValue, intlFallbackSymbol(), dateTimeFormat,
+ ATTR_NONENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
+
+ return thisValue;
+ }
+
+ return dateTimeFormat;
}
@@ -734,7 +769,7 @@ function dateTimeFormatFormatToBind() {
var x = (date === undefined) ? std_Date_now() : ToNumber(date);
// Step 1.a.iii.
- return intl_FormatDateTime(this, x, false);
+ return intl_FormatDateTime(this, x, /* formatToParts = */ false);
}
/**
@@ -745,34 +780,40 @@ function dateTimeFormatFormatToBind() {
* Spec: ECMAScript Internationalization API Specification, 12.3.2.
*/
function Intl_DateTimeFormat_format_get() {
- // Check "this DateTimeFormat object" per introduction of section 12.3.
- var internals = getDateTimeFormatInternals(this, "format");
+ // Steps 1-3.
+ var dtf = UnwrapDateTimeFormat(this, "format");
- // Step 1.
+ var internals = getDateTimeFormatInternals(dtf);
+
+ // Step 4.
if (internals.boundFormat === undefined) {
- // Step 1.a.
+ // Step 4.a.
var F = dateTimeFormatFormatToBind;
- // Step 1.b-d.
- var bf = callFunction(FunctionBind, F, this);
+ // Steps 4.b-d.
+ var bf = callFunction(FunctionBind, F, dtf);
internals.boundFormat = bf;
}
- // Step 2.
+ // Step 5.
return internals.boundFormat;
}
+_SetCanonicalName(Intl_DateTimeFormat_format_get, "get format");
function Intl_DateTimeFormat_formatToParts() {
- // Check "this DateTimeFormat object" per introduction of section 12.3.
- getDateTimeFormatInternals(this, "formatToParts");
+ // Steps 1-3.
+ var dtf = UnwrapDateTimeFormat(this, "formatToParts");
- // Steps 1.a.i-ii
+ // Ensure the DateTimeFormat internals are resolved.
+ getDateTimeFormatInternals(dtf);
+
+ // Steps 4-5.
var date = arguments.length > 0 ? arguments[0] : undefined;
var x = (date === undefined) ? std_Date_now() : ToNumber(date);
- // Step 1.a.iii.
- return intl_FormatDateTime(this, x, true);
+ // Step 6.
+ return intl_FormatDateTime(dtf, x, /* formatToParts = */ true);
}
@@ -782,8 +823,10 @@ function Intl_DateTimeFormat_formatToParts() {
* Spec: ECMAScript Internationalization API Specification, 12.3.3 and 12.4.
*/
function Intl_DateTimeFormat_resolvedOptions() {
- // Check "this DateTimeFormat object" per introduction of section 12.3.
- var internals = getDateTimeFormatInternals(this, "resolvedOptions");
+ // Invoke |UnwrapDateTimeFormat| per introduction of section 12.3.
+ var dtf = UnwrapDateTimeFormat(this, "resolvedOptions");
+
+ var internals = getDateTimeFormatInternals(dtf);
var result = {
locale: internals.locale,
diff --git a/js/src/builtin/intl/IntlObject.cpp b/js/src/builtin/intl/IntlObject.cpp
index 6bb57adf41..893eb5ce40 100644
--- a/js/src/builtin/intl/IntlObject.cpp
+++ b/js/src/builtin/intl/IntlObject.cpp
@@ -454,10 +454,12 @@ GlobalObject::initIntlObject(JSContext* cx, Handle<GlobalObject*> global)
RootedObject collatorProto(cx, CreateCollatorPrototype(cx, intl, global));
if (!collatorProto)
return false;
- RootedObject dateTimeFormatProto(cx, CreateDateTimeFormatPrototype(cx, intl, global));
+ RootedObject dateTimeFormatProto(cx), dateTimeFormat(cx);
+ dateTimeFormatProto = CreateDateTimeFormatPrototype(cx, intl, global, &dateTimeFormat);
if (!dateTimeFormatProto)
return false;
- RootedObject numberFormatProto(cx, CreateNumberFormatPrototype(cx, intl, global));
+ RootedObject numberFormatProto(cx), numberFormat(cx);
+ numberFormatProto = CreateNumberFormatPrototype(cx, intl, global, &numberFormat);
if (!numberFormatProto)
return false;
RootedObject pluralRulesProto(cx, CreatePluralRulesPrototype(cx, intl, global));
@@ -487,7 +489,9 @@ GlobalObject::initIntlObject(JSContext* cx, Handle<GlobalObject*> global)
// |getPrototype(JSProto_*)|, but that has global-object-property-related
// baggage we don't need or want, so we use one-off reserved slots.
global->setReservedSlot(COLLATOR_PROTO, ObjectValue(*collatorProto));
+ global->setReservedSlot(DATE_TIME_FORMAT, ObjectValue(*dateTimeFormat));
global->setReservedSlot(DATE_TIME_FORMAT_PROTO, ObjectValue(*dateTimeFormatProto));
+ global->setReservedSlot(NUMBER_FORMAT, ObjectValue(*numberFormat));
global->setReservedSlot(NUMBER_FORMAT_PROTO, ObjectValue(*numberFormatProto));
global->setReservedSlot(PLURAL_RULES_PROTO, ObjectValue(*pluralRulesProto));
global->setReservedSlot(RELATIVE_TIME_FORMAT_PROTO, ObjectValue(*relativeTimeFmtProto));
diff --git a/js/src/builtin/intl/IntlObject.js b/js/src/builtin/intl/IntlObject.js
index 454d580995..2832124cc8 100644
--- a/js/src/builtin/intl/IntlObject.js
+++ b/js/src/builtin/intl/IntlObject.js
@@ -3,44 +3,79 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function Intl_getCanonicalLocales(locales) {
- let codes = CanonicalizeLocaleList(locales);
- let result = [];
+ // Step 1.
+ var localeList = CanonicalizeLocaleList(locales);
- let len = codes.length;
- let k = 0;
+ // Step 2 (Inlined CreateArrayFromList).
+ var array = [];
- while (k < len) {
- _DefineDataProperty(result, k, codes[k]);
- k++;
- }
- return result;
+ for (var n = 0, len = localeList.length; n < len; n++)
+ _DefineDataProperty(array, n, localeList[n]);
+
+ return array;
}
+/**
+ * This function is a custom function in the style of the standard Intl.*
+ * functions, that isn't part of any spec or proposal yet.
+ *
+ * Returns an object with the following properties:
+ * locale:
+ * The actual resolved locale.
+ *
+ * calendar:
+ * The default calendar of the resolved locale.
+ *
+ * firstDayOfWeek:
+ * The first day of the week for the resolved locale.
+ *
+ * minDays:
+ * The minimum number of days in a week for the resolved locale.
+ *
+ * weekendStart:
+ * The day considered the beginning of a weekend for the resolved locale.
+ *
+ * weekendEnd:
+ * The day considered the end of a weekend for the resolved locale.
+ *
+ * Days are encoded as integers in the range 1=Sunday to 7=Saturday.
+ */
function Intl_getCalendarInfo(locales) {
- const requestedLocales = CanonicalizeLocaleList(locales);
+ // 1. Let requestLocales be ? CanonicalizeLocaleList(locales).
+ const requestedLocales = CanonicalizeLocaleList(locales);
+
+ const DateTimeFormat = dateTimeFormatInternalProperties;
- const DateTimeFormat = dateTimeFormatInternalProperties;
- const localeData = DateTimeFormat.localeData;
+ // 2. Let localeData be %DateTimeFormat%.[[localeData]].
+ const localeData = DateTimeFormat.localeData;
- const localeOpt = new Record();
- localeOpt.localeMatcher = "best fit";
+ // 3. Let localeOpt be a new Record.
+ const localeOpt = new Record();
- const r = ResolveLocale(callFunction(DateTimeFormat.availableLocales, DateTimeFormat),
- requestedLocales,
- localeOpt,
- DateTimeFormat.relevantExtensionKeys,
- localeData);
+ // 4. Set localeOpt.[[localeMatcher]] to "best fit".
+ localeOpt.localeMatcher = "best fit";
- const result = intl_GetCalendarInfo(r.locale);
- result.calendar = r.ca;
- result.locale = r.locale;
+ // 5. Let r be ResolveLocale(%DateTimeFormat%.[[availableLocales]],
+ // requestedLocales, localeOpt,
+ // %DateTimeFormat%.[[relevantExtensionKeys]], localeData).
+ const r = ResolveLocale(callFunction(DateTimeFormat.availableLocales, DateTimeFormat),
+ requestedLocales,
+ localeOpt,
+ DateTimeFormat.relevantExtensionKeys,
+ localeData);
+
+ // 6. Let result be GetCalendarInfo(r.[[locale]]).
+ const result = intl_GetCalendarInfo(r.locale);
+ _DefineDataProperty(result, "calendar", r.ca);
+ _DefineDataProperty(result, "locale", r.locale);
- return result;
+ // 7. Return result.
+ return result;
}
/**
- * This function is a custom method designed after Intl API, but currently
- * not part of the spec or spec proposal.
+ * This function is a custom function in the style of the standard Intl.*
+ * functions, that isn't part of any spec or proposal yet.
* We want to use it internally to retrieve translated values from CLDR in
* order to ensure they're aligned with what Intl API returns.
*
@@ -86,8 +121,9 @@ function Intl_getDisplayNames(locales, options) {
// 4. Let localeData be %DateTimeFormat%.[[localeData]].
const localeData = DateTimeFormat.localeData;
- // 5. Let opt be a new Record.
+ // 5. Let localeOpt be a new Record.
const localeOpt = new Record();
+
// 6. Set localeOpt.[[localeMatcher]] to "best fit".
localeOpt.localeMatcher = "best fit";
@@ -101,6 +137,7 @@ function Intl_getDisplayNames(locales, options) {
// 8. Let style be ? GetOption(options, "style", "string", « "long", "short", "narrow" », "long").
const style = GetOption(options, "style", "string", ["long", "short", "narrow"], "long");
+
// 9. Let keys be ? Get(options, "keys").
let keys = options.keys;
@@ -119,8 +156,10 @@ function Intl_getDisplayNames(locales, options) {
// |intl_ComputeDisplayNames| may infallibly access the list's length via
// |ArrayObject::length|.)
let processedKeys = [];
+
// 13. Let len be ? ToLength(? Get(keys, "length")).
let len = ToLength(keys.length);
+
// 14. Let i be 0.
// 15. Repeat, while i < len
for (let i = 0; i < len; i++) {
diff --git a/js/src/builtin/intl/NumberFormat.cpp b/js/src/builtin/intl/NumberFormat.cpp
index 1cd173fd54..b8a8ebcd64 100644
--- a/js/src/builtin/intl/NumberFormat.cpp
+++ b/js/src/builtin/intl/NumberFormat.cpp
@@ -92,63 +92,34 @@ static const JSFunctionSpec numberFormat_methods[] = {
static bool
NumberFormat(JSContext* cx, const CallArgs& args, bool construct)
{
- RootedObject obj(cx);
-
- // We're following ECMA-402 1st Edition when NumberFormat is called
- // because of backward compatibility issues.
- // See https://github.com/tc39/ecma402/issues/57
- if (!construct) {
- // ES Intl 1st ed., 11.1.2.1 step 3
- JSObject* intl = GlobalObject::getOrCreateIntlObject(cx, cx->global());
- if (!intl)
- return false;
- RootedValue self(cx, args.thisv());
- if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
- // ES Intl 1st ed., 11.1.2.1 step 4
- obj = ToObject(cx, self);
- if (!obj)
- return false;
-
- // ES Intl 1st ed., 11.1.2.1 step 5
- bool extensible;
- if (!IsExtensible(cx, obj, &extensible))
- return false;
- if (!extensible)
- return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
- } else {
- // ES Intl 1st ed., 11.1.2.1 step 3.a
- construct = true;
- }
- }
- if (construct) {
- // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
- RootedObject proto(cx);
- if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
- return false;
+ // Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
- if (!proto) {
- proto = GlobalObject::getOrCreateNumberFormatPrototype(cx, cx->global());
- if (!proto)
- return false;
- }
+ // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
+ RootedObject proto(cx);
+ if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
+ return false;
- obj = NewObjectWithGivenProto<NumberFormatObject>(cx, proto);
- if (!obj)
+ if (!proto) {
+ proto = GlobalObject::getOrCreateNumberFormatPrototype(cx, cx->global());
+ if (!proto)
return false;
-
- obj->as<NativeObject>().setReservedSlot(NumberFormatObject::INTERNALS_SLOT, NullValue());
- obj->as<NativeObject>().setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
}
- RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
- RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
-
- // Step 3.
- if (!intl::InitializeObject(cx, obj, cx->names().InitializeNumberFormat, locales, options))
+ Rooted<NumberFormatObject*> numberFormat(cx);
+ numberFormat = NewObjectWithGivenProto<NumberFormatObject>(cx, proto);
+ if (!numberFormat)
return false;
- args.rval().setObject(*obj);
- return true;
+ numberFormat->setReservedSlot(NumberFormatObject::INTERNALS_SLOT, NullValue());
+ numberFormat->setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
+
+ RootedValue thisValue(cx, construct ? ObjectValue(*numberFormat) : args.thisv());
+ RootedValue locales(cx, args.get(0));
+ RootedValue options(cx, args.get(1));
+
+ // Step 3.
+ return intl::LegacyIntlInitialize(cx, numberFormat, cx->names().InitializeNumberFormat, thisValue,
+ locales, options, args.rval());
}
static bool
@@ -175,30 +146,23 @@ js::NumberFormatObject::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(NumberFormatObject::UNUMBER_FORMAT_SLOT);
- if (!slot.isUndefined()) {
- if (UNumberFormat* nf = static_cast<UNumberFormat*>(slot.toPrivate()))
- unum_close(nf);
- }
+ const Value& slot = obj->as<NumberFormatObject>().getReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT);
+ if (UNumberFormat* nf = static_cast<UNumberFormat*>(slot.toPrivate()))
+ unum_close(nf);
}
JSObject*
-js::CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
+js::CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global,
+ MutableHandleObject constructor)
{
RootedFunction ctor(cx);
ctor = GlobalObject::createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0);
if (!ctor)
return nullptr;
- RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global,
- &NumberFormatObject::class_));
+ RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
if (!proto)
return nullptr;
- proto->setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
if (!LinkConstructorAndPrototype(cx, ctor, proto))
return nullptr;
@@ -229,22 +193,12 @@ js::CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalO
return nullptr;
}
- RootedValue options(cx);
- if (!intl::CreateDefaultOptions(cx, &options))
- return nullptr;
-
- // 11.2.1 and 11.3
- if (!intl::InitializeObject(cx, proto, cx->names().InitializeNumberFormat, UndefinedHandleValue,
- options))
- {
- return nullptr;
- }
-
// 8.1
RootedValue ctorValue(cx, ObjectValue(*ctor));
if (!DefineProperty(cx, Intl, cx->names().NumberFormat, ctorValue, nullptr, nullptr, 0))
return nullptr;
+ constructor.set(ctor);
return proto;
}
@@ -295,7 +249,7 @@ js::intl_numberingSystem(JSContext* cx, unsigned argc, Value* vp)
* of the given NumberFormat.
*/
static UNumberFormat*
-NewUNumberFormat(JSContext* cx, HandleObject numberFormat)
+NewUNumberFormat(JSContext* cx, Handle<NumberFormatObject*> numberFormat)
{
RootedValue value(cx);
@@ -854,50 +808,23 @@ js::intl_FormatNumber(JSContext* cx, unsigned argc, Value* vp)
MOZ_ASSERT(args[1].isNumber());
MOZ_ASSERT(args[2].isBoolean());
- RootedObject numberFormat(cx, &args[0].toObject());
-
- // Obtain a UNumberFormat object, cached if possible.
- bool isNumberFormatInstance = numberFormat->getClass() == &NumberFormatObject::class_;
- UNumberFormat* nf;
- if (isNumberFormatInstance) {
- void* priv =
- numberFormat->as<NativeObject>().getReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT).toPrivate();
- nf = static_cast<UNumberFormat*>(priv);
- if (!nf) {
- nf = NewUNumberFormat(cx, numberFormat);
- if (!nf)
- return false;
- numberFormat->as<NativeObject>().setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nf));
- }
- } else {
- // There's no good place to cache the ICU number format for an object
- // that has been initialized as a NumberFormat but is not a
- // NumberFormat instance. One possibility might be to add a
- // NumberFormat instance as an internal property to each such object.
+ Rooted<NumberFormatObject*> numberFormat(cx, &args[0].toObject().as<NumberFormatObject>());
+
+ // Obtain a cached UNumberFormat object.
+ void* priv =
+ numberFormat->getReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT).toPrivate();
+ UNumberFormat* nf = static_cast<UNumberFormat*>(priv);
+ if (!nf) {
nf = NewUNumberFormat(cx, numberFormat);
if (!nf)
return false;
+ numberFormat->setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nf));
}
// Use the UNumberFormat to actually format the number.
- double d = args[1].toNumber();
- RootedValue result(cx);
-
- bool success;
if (args[2].toBoolean()) {
- success = intl_FormatNumberToParts(cx, nf, d, &result);
- } else {
- MOZ_ASSERT(!args[2].toBoolean(),
- "shouldn't be doing formatToParts without an ICU that "
- "supports it");
- success = js::intl_FormatNumber(cx, nf, d, &result);
+ return intl_FormatNumberToParts(cx, nf, args[1].toNumber(), args.rval());
}
-
- if (!isNumberFormatInstance)
- unum_close(nf);
- if (!success)
- return false;
- args.rval().set(result);
- return true;
+ return intl_FormatNumber(cx, nf, args[1].toNumber(), args.rval());
}
diff --git a/js/src/builtin/intl/NumberFormat.h b/js/src/builtin/intl/NumberFormat.h
index 60da423bd2..9dccccc1c5 100644
--- a/js/src/builtin/intl/NumberFormat.h
+++ b/js/src/builtin/intl/NumberFormat.h
@@ -37,7 +37,8 @@ class NumberFormatObject : public NativeObject
};
extern JSObject*
-CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global);
+CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global,
+ MutableHandleObject constructor);
/**
* Returns a new instance of the standard built-in NumberFormat constructor.
@@ -76,7 +77,7 @@ intl_numberingSystem(JSContext* cx, unsigned argc, Value* vp);
*
* Spec: ECMAScript Internationalization API Specification, 11.3.2.
*
- * Usage: formatted = intl_FormatNumber(numberFormat, x)
+ * Usage: formatted = intl_FormatNumber(numberFormat, x, formatToParts)
*/
extern MOZ_MUST_USE bool
intl_FormatNumber(JSContext* cx, unsigned argc, Value* vp);
diff --git a/js/src/builtin/intl/NumberFormat.js b/js/src/builtin/intl/NumberFormat.js
index 639f8a9dde..fca1bdb8ae 100644
--- a/js/src/builtin/intl/NumberFormat.js
+++ b/js/src/builtin/intl/NumberFormat.js
@@ -96,11 +96,13 @@ function resolveNumberFormatInternals(lazyNumberFormatData) {
/**
- * Returns an object containing the NumberFormat internal properties of |obj|,
- * or throws a TypeError if |obj| isn't NumberFormat-initialized.
+ * Returns an object containing the NumberFormat internal properties of |obj|.
*/
-function getNumberFormatInternals(obj, methodName) {
- var internals = getIntlObjectInternals(obj, "NumberFormat", methodName);
+function getNumberFormatInternals(obj) {
+ assert(IsObject(obj), "getNumberFormatInternals called with non-object");
+ assert(IsNumberFormat(obj), "getNumberFormatInternals called with non-NumberFormat");
+
+ var internals = getIntlObjectInternals(obj);
assert(internals.type === "NumberFormat", "bad type escaped getIntlObjectInternals");
// If internal properties have already been computed, use them.
@@ -114,6 +116,25 @@ function getNumberFormatInternals(obj, methodName) {
return internalProps;
}
+
+/**
+ * UnwrapNumberFormat(nf)
+ */
+function UnwrapNumberFormat(nf, methodName) {
+ // Step 1.
+ if ((!IsObject(nf) || !IsNumberFormat(nf)) && nf instanceof GetNumberFormatConstructor()) {
+ nf = nf[intlFallbackSymbol()];
+ }
+
+ // Step 2.
+ if (!IsObject(nf) || !IsNumberFormat(nf))
+ ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "NumberFormat", methodName, "NumberFormat");
+
+ // Step 3.
+ return nf;
+}
+
+
/**
* Applies digit options used for number formatting onto the intl object.
*
@@ -199,15 +220,12 @@ function IsWellFormedCurrencyCode(currency) {
*
* Spec: ECMAScript Internationalization API Specification, 11.1.1.
*/
-function InitializeNumberFormat(numberFormat, locales, options) {
- assert(IsObject(numberFormat), "InitializeNumberFormat");
+function InitializeNumberFormat(numberFormat, thisValue, locales, options) {
+ assert(IsObject(numberFormat), "InitializeNumberFormat called with non-object");
+ assert(IsNumberFormat(numberFormat), "InitializeNumberFormat called with non-NumberFormat");
- // Step 1.
- if (isInitializedIntlObject(numberFormat))
- ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
-
- // Step 2.
- var internals = initializeIntlObject(numberFormat);
+ // Steps 1-2 (These steps are no longer required and should be removed
+ // from the spec; https://github.com/tc39/ecma402/issues/115).
// Lazy NumberFormat data has the following structure:
//
@@ -312,7 +330,19 @@ function InitializeNumberFormat(numberFormat, locales, options) {
//
// We've done everything that must be done now: mark the lazy data as fully
// computed and install it.
- setLazyData(internals, "NumberFormat", lazyNumberFormatData);
+ initializeIntlObject(numberFormat, "NumberFormat", lazyNumberFormatData);
+
+ if (numberFormat !== thisValue && thisValue instanceof GetNumberFormatConstructor()) {
+ if (!IsObject(thisValue))
+ ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, typeof thisValue);
+
+ _DefineDataProperty(thisValue, intlFallbackSymbol(), numberFormat,
+ ATTR_NONENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
+
+ return thisValue;
+ }
+
+ return numberFormat;
}
@@ -446,28 +476,32 @@ function numberFormatFormatToBind(value) {
* Spec: ECMAScript Internationalization API Specification, 11.3.2.
*/
function Intl_NumberFormat_format_get() {
- // Check "this NumberFormat object" per introduction of section 11.3.
- var internals = getNumberFormatInternals(this, "format");
+ // Steps 1-3.
+ var nf = UnwrapNumberFormat(this, "format");
- // Step 1.
+ var internals = getNumberFormatInternals(nf);
+
+ // Step 4.
if (internals.boundFormat === undefined) {
- // Step 1.a.
+ // Step 4.a.
var F = numberFormatFormatToBind;
- // Step 1.b-d.
- var bf = callFunction(FunctionBind, F, this);
+ // Steps 4.b-d.
+ var bf = callFunction(FunctionBind, F, nf);
internals.boundFormat = bf;
}
- // Step 2.
+
+ // Step 5.
return internals.boundFormat;
}
+_SetCanonicalName(Intl_NumberFormat_format_get, "get format");
function Intl_NumberFormat_formatToParts(value) {
- // Step 1.
- var nf = this;
+ // Steps 1-3.
+ var nf = UnwrapNumberFormat(this, "formatToParts");
- // Steps 2-3.
- getNumberFormatInternals(nf, "formatToParts");
+ // Ensure the NumberFormat internals are resolved.
+ getNumberFormatInternals(nf);
// Step 4.
var x = ToNumber(value);
@@ -482,8 +516,10 @@ function Intl_NumberFormat_formatToParts(value) {
* Spec: ECMAScript Internationalization API Specification, 11.3.3 and 11.4.
*/
function Intl_NumberFormat_resolvedOptions() {
- // Check "this NumberFormat object" per introduction of section 11.3.
- var internals = getNumberFormatInternals(this, "resolvedOptions");
+ // Invoke |UnwrapNumberFormat| per introduction of section 11.3.
+ var nf = UnwrapNumberFormat(this, "resolvedOptions");
+
+ var internals = getNumberFormatInternals(nf);
var result = {
locale: internals.locale,
diff --git a/js/src/builtin/intl/PluralRules.cpp b/js/src/builtin/intl/PluralRules.cpp
index 624b1510a5..78bd9e5d74 100644
--- a/js/src/builtin/intl/PluralRules.cpp
+++ b/js/src/builtin/intl/PluralRules.cpp
@@ -113,8 +113,8 @@ PluralRules(JSContext* cx, const CallArgs& args, bool construct)
if (!obj)
return false;
- obj->as<NativeObject>().setReservedSlot(PluralRulesObject::INTERNALS_SLOT, NullValue());
- obj->as<NativeObject>().setReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT, PrivateValue(nullptr));
+ obj->as<PluralRulesObject>().setReservedSlot(PluralRulesObject::INTERNALS_SLOT, NullValue());
+ obj->as<PluralRulesObject>().setReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT, PrivateValue(nullptr));
}
RootedValue locales(cx, args.get(0));
@@ -147,15 +147,9 @@ js::PluralRulesObject::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<PluralRulesObject>().getReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT);
- if (!slot.isUndefined()) {
- if (UPluralRules* pr = static_cast<UPluralRules*>(slot.toPrivate()))
- uplrules_close(pr);
- }
+ if (UPluralRules* pr = static_cast<UPluralRules*>(slot.toPrivate()))
+ uplrules_close(pr);
}
JSObject*
@@ -166,10 +160,9 @@ js::CreatePluralRulesPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalOb
if (!ctor)
return nullptr;
- RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &PluralRulesObject::class_));
+ RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
if (!proto)
return nullptr;
- proto->setReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT, PrivateValue(nullptr));
if (!LinkConstructorAndPrototype(cx, ctor, proto))
return nullptr;
@@ -180,16 +173,6 @@ js::CreatePluralRulesPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalOb
if (!JS_DefineFunctions(cx, proto, pluralRules_methods))
return nullptr;
- RootedValue options(cx);
- if (!intl::CreateDefaultOptions(cx, &options))
- return nullptr;
-
- if (!intl::InitializeObject(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;
@@ -222,7 +205,7 @@ js::intl_PluralRules_availableLocales(JSContext* cx, unsigned argc, Value* vp)
*
*/
static UNumberFormat*
-NewUNumberFormatForPluralRules(JSContext* cx, HandleObject pluralRules)
+NewUNumberFormatForPluralRules(JSContext* cx, Handle<PluralRulesObject*> pluralRules)
{
RootedObject internals(cx, intl::GetInternalsObject(cx, pluralRules));
if (!internals)
@@ -299,7 +282,7 @@ js::intl_SelectPluralRule(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
- RootedObject pluralRules(cx, &args[0].toObject());
+ Rooted<PluralRulesObject*> pluralRules(cx, &args[0].toObject().as<PluralRulesObject>());
UNumberFormat* nf = NewUNumberFormatForPluralRules(cx, pluralRules);
if (!nf)
diff --git a/js/src/builtin/intl/PluralRules.js b/js/src/builtin/intl/PluralRules.js
index d271a8d397..997fa4db7d 100644
--- a/js/src/builtin/intl/PluralRules.js
+++ b/js/src/builtin/intl/PluralRules.js
@@ -63,11 +63,13 @@ function resolvePluralRulesInternals(lazyPluralRulesData) {
}
/**
- * Returns an object containing the PluralRules internal properties of |obj|,
- * or throws a TypeError if |obj| isn't PluralRules-initialized.
+ * Returns an object containing the PluralRules internal properties of |obj|.
*/
-function getPluralRulesInternals(obj, methodName) {
- var internals = getIntlObjectInternals(obj, "PluralRules", methodName);
+function getPluralRulesInternals(obj) {
+ assert(IsObject(obj), "getPluralRulesInternals called with non-object");
+ assert(IsPluralRules(obj), "getPluralRulesInternals called with non-PluralRules");
+
+ var internals = getIntlObjectInternals(obj);
assert(internals.type === "PluralRules", "bad type escaped getIntlObjectInternals");
var internalProps = maybeInternalProperties(internals);
@@ -91,13 +93,11 @@ function getPluralRulesInternals(obj, methodName) {
* 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);
+ assert(IsObject(pluralRules), "InitializePluralRules called with non-object");
+ assert(IsPluralRules(pluralRules), "InitializePluralRules called with non-PluralRules");
- let internals = initializeIntlObject(pluralRules);
+ // Steps 1-2 (These steps are no longer required and should be removed
+ // from the spec; https://github.com/tc39/ecma402/issues/115).
// Lazy PluralRules data has the following structure:
//
@@ -156,7 +156,7 @@ function InitializePluralRules(pluralRules, locales, options) {
std_Math_max(lazyPluralRulesData.minimumFractionDigits, 3);
}
- setLazyData(internals, "PluralRules", lazyPluralRulesData)
+ initializeIntlObject(pluralRules, "PluralRules", lazyPluralRulesData)
}
/**
@@ -189,8 +189,13 @@ function Intl_PluralRules_supportedLocalesOf(locales /*, options*/) {
function Intl_PluralRules_select(value) {
// Step 1.
let pluralRules = this;
+
// Step 2.
- let internals = getPluralRulesInternals(pluralRules, "select");
+ if (!IsObject(pluralRules) || !IsPluralRules(pluralRules))
+ ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "PluralRules", "select", "PluralRules");
+
+ // Ensure the PluralRules internals are resolved.
+ getPluralRulesInternals(pluralRules);
// Steps 3-4.
let n = ToNumber(value);
@@ -205,7 +210,13 @@ function Intl_PluralRules_select(value) {
* Spec: ECMAScript 402 API, PluralRules, 1.4.4.
*/
function Intl_PluralRules_resolvedOptions() {
- var internals = getPluralRulesInternals(this, "resolvedOptions");
+ // Check "this PluralRules object" per introduction of section 1.4.
+ if (!IsObject(this) || !IsPluralRules(this)) {
+ ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "PluralRules", "resolvedOptions",
+ "PluralRules");
+ }
+
+ var internals = getPluralRulesInternals(this);
var result = {
locale: internals.locale,
diff --git a/js/src/builtin/intl/RelativeTimeFormat.cpp b/js/src/builtin/intl/RelativeTimeFormat.cpp
index eb8d152d63..8a504cda99 100644
--- a/js/src/builtin/intl/RelativeTimeFormat.cpp
+++ b/js/src/builtin/intl/RelativeTimeFormat.cpp
@@ -108,8 +108,8 @@ RelativeTimeFormat(JSContext* cx, unsigned argc, Value* vp)
if (!relativeTimeFormat)
return false;
- relativeTimeFormat->as<NativeObject>().setReservedSlot(RelativeTimeFormatObject::INTERNALS_SLOT, NullValue());
- relativeTimeFormat->as<NativeObject>().setReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
+ relativeTimeFormat->as<RelativeTimeFormatObject>().setReservedSlot(RelativeTimeFormatObject::INTERNALS_SLOT, NullValue());
+ relativeTimeFormat->as<RelativeTimeFormatObject>().setReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
RootedValue locales(cx, args.get(0));
RootedValue options(cx, args.get(1));
@@ -127,15 +127,9 @@ js::RelativeTimeFormatObject::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<RelativeTimeFormatObject>().getReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT);
- if (!slot.isUndefined()) {
- if (URelativeDateTimeFormatter* rtf = static_cast<URelativeDateTimeFormatter*>(slot.toPrivate()))
- ureldatefmt_close(rtf);
- }
+ if (URelativeDateTimeFormatter* rtf = static_cast<URelativeDateTimeFormatter*>(slot.toPrivate()))
+ ureldatefmt_close(rtf);
}
JSObject*
@@ -146,10 +140,9 @@ js::CreateRelativeTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<G
if (!ctor)
return nullptr;
- RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &RelativeTimeFormatObject::class_));
+ RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
if (!proto)
return nullptr;
- proto->setReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
if (!LinkConstructorAndPrototype(cx, ctor, proto))
return nullptr;
@@ -163,16 +156,6 @@ js::CreateRelativeTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<G
if (!JS_DefineProperties(cx, proto, relativeTimeFormat_properties))
return nullptr;
- RootedValue options(cx);
- if (!intl::CreateDefaultOptions(cx, &options))
- return nullptr;
-
- if (!intl::InitializeObject(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;
diff --git a/js/src/builtin/intl/RelativeTimeFormat.js b/js/src/builtin/intl/RelativeTimeFormat.js
index 1118df9025..73e1813df7 100644
--- a/js/src/builtin/intl/RelativeTimeFormat.js
+++ b/js/src/builtin/intl/RelativeTimeFormat.js
@@ -69,8 +69,11 @@ function resolveRelativeTimeFormatInternals(lazyRelativeTimeFormatData) {
* 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);
+function getRelativeTimeFormatInternals(obj) {
+ assert(IsObject(obj), "getRelativeTimeFormatInternals called with non-object");
+ assert(IsRelativeTimeFormat(obj), "getRelativeTimeFormatInternals called with non-RelativeTimeFormat");
+
+ var internals = getIntlObjectInternals(obj);
assert(internals.type === "RelativeTimeFormat", "bad type escaped getIntlObjectInternals");
var internalProps = maybeInternalProperties(internals);
@@ -94,12 +97,8 @@ function getRelativeTimeFormatInternals(obj, methodName) {
* 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);
+ assert(IsObject(relativeTimeFormat), "InitializeRelativeTimeFormat called with non-object");
+ assert(IsRelativeTimeFormat(relativeTimeFormat), "InitializeRelativeTimeFormat called with non-RelativeTimeFormat");
// Lazy RelativeTimeFormat data has the following structure:
//
@@ -146,7 +145,7 @@ function InitializeRelativeTimeFormat(relativeTimeFormat, locales, options) {
const numeric = GetOption(options, "numeric", "string", ["always", "auto"], "always");
lazyRelativeTimeFormatData.numeric = numeric;
- setLazyData(internals, "RelativeTimeFormat", lazyRelativeTimeFormatData)
+ initializeIntlObject(relativeTimeFormat, "RelativeTimeFormat", lazyRelativeTimeFormatData)
}
/**
@@ -181,7 +180,11 @@ function Intl_RelativeTimeFormat_format(value, unit) {
let relativeTimeFormat = this;
// Step 2.
- let internals = getRelativeTimeFormatInternals(relativeTimeFormat, "format");
+ if (!IsObject(relativeTimeFormat) || !IsRelativeTimeFormat(relativeTimeFormat))
+ ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "RelativeTimeFormat", "format", "RelativeTimeFormat");
+
+ // Ensure the RelativeTimeFormat internals are resolved.
+ let internals = getRelativeTimeFormatInternals(relativeTimeFormat);
// Step 3.
let t = ToNumber(value);
@@ -227,7 +230,13 @@ function Intl_RelativeTimeFormat_format(value, unit) {
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.4.4.
*/
function Intl_RelativeTimeFormat_resolvedOptions() {
- var internals = getRelativeTimeFormatInternals(this, "resolvedOptions");
+ // Check "this RelativeTimeFormat object" per introduction of section 1.4.
+ if (!IsObject(this) || !IsRelativeTimeFormat(this)) {
+ ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "RelativeTimeFormat", "resolvedOptions",
+ "RelativeTimeFormat");
+ }
+
+ var internals = getRelativeTimeFormatInternals(this);
// Steps 4-5.
var result = {