diff options
Diffstat (limited to 'js/src/builtin/intl')
-rw-r--r-- | js/src/builtin/intl/Collator.cpp | 134 | ||||
-rw-r--r-- | js/src/builtin/intl/Collator.js | 35 | ||||
-rw-r--r-- | js/src/builtin/intl/CommonFunctions.cpp | 54 | ||||
-rw-r--r-- | js/src/builtin/intl/CommonFunctions.h | 15 | ||||
-rw-r--r-- | js/src/builtin/intl/CommonFunctions.js | 185 | ||||
-rw-r--r-- | js/src/builtin/intl/DateTimeFormat.cpp | 146 | ||||
-rw-r--r-- | js/src/builtin/intl/DateTimeFormat.h | 4 | ||||
-rw-r--r-- | js/src/builtin/intl/DateTimeFormat.js | 99 | ||||
-rw-r--r-- | js/src/builtin/intl/IntlObject.cpp | 8 | ||||
-rw-r--r-- | js/src/builtin/intl/IntlObject.js | 91 | ||||
-rw-r--r-- | js/src/builtin/intl/NumberFormat.cpp | 151 | ||||
-rw-r--r-- | js/src/builtin/intl/NumberFormat.h | 5 | ||||
-rw-r--r-- | js/src/builtin/intl/NumberFormat.js | 88 | ||||
-rw-r--r-- | js/src/builtin/intl/PluralRules.cpp | 31 | ||||
-rw-r--r-- | js/src/builtin/intl/PluralRules.js | 37 | ||||
-rw-r--r-- | js/src/builtin/intl/RelativeTimeFormat.cpp | 27 | ||||
-rw-r--r-- | js/src/builtin/intl/RelativeTimeFormat.js | 31 |
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 = {
|