mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-15 08:53:07 +09:00
Issue #2259 - Update Intl.* Object-ness to ECMA-402, 4th edition
- turn each Intl object into a NativeObject subclass - create them as PlainObject - ensure correct type is passed in self-hosted code - implement legacy constructor semantics for DateTime&NumberFormat - store internals on object slot instead of JS WeakMap Based-on: m-c 1328386, 1332604
This commit is contained in:
parent
adffcb127a
commit
30157344fe
31 changed files with 633 additions and 663 deletions
|
|
@ -913,7 +913,7 @@ struct JSClass {
|
|||
// application.
|
||||
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
|
||||
#define JSCLASS_GLOBAL_SLOT_COUNT \
|
||||
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 47)
|
||||
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 49)
|
||||
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
||||
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
||||
#define JSCLASS_GLOBAL_FLAGS \
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ function Date_toLocaleString() {
|
|||
}
|
||||
|
||||
// Step 7.
|
||||
return intl_FormatDateTime(dateTimeFormat, x, false);
|
||||
return intl_FormatDateTime(dateTimeFormat, x, /* formatToParts = */ false);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -137,7 +137,7 @@ function Date_toLocaleDateString() {
|
|||
}
|
||||
|
||||
// Step 7.
|
||||
return intl_FormatDateTime(dateTimeFormat, x, false);
|
||||
return intl_FormatDateTime(dateTimeFormat, x, /* formatToParts = */ false);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -170,5 +170,5 @@ function Date_toLocaleTimeString() {
|
|||
}
|
||||
|
||||
// Step 7.
|
||||
return intl_FormatDateTime(dateTimeFormat, x, false);
|
||||
return intl_FormatDateTime(dateTimeFormat, x, /* formatToParts = */ false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ const JSFunctionSpec SymbolObject::staticMethods[] = {
|
|||
};
|
||||
|
||||
JSObject*
|
||||
SymbolObject::initClass(JSContext* cx, HandleObject obj)
|
||||
SymbolObject::initClass(JSContext* cx, HandleObject obj, bool defineMembers)
|
||||
{
|
||||
Handle<GlobalObject*> global = obj.as<GlobalObject>();
|
||||
|
||||
|
|
@ -66,25 +66,33 @@ SymbolObject::initClass(JSContext* cx, HandleObject obj)
|
|||
if (!ctor)
|
||||
return nullptr;
|
||||
|
||||
// Define the well-known symbol properties, such as Symbol.iterator.
|
||||
ImmutablePropertyNamePtr* names = cx->names().wellKnownSymbolNames();
|
||||
RootedValue value(cx);
|
||||
unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
|
||||
WellKnownSymbols* wks = cx->runtime()->wellKnownSymbols;
|
||||
for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
|
||||
value.setSymbol(wks->get(i));
|
||||
if (!NativeDefineProperty(cx, ctor, names[i], value, nullptr, nullptr, attrs))
|
||||
return nullptr;
|
||||
if (defineMembers) {
|
||||
// Define the well-known symbol properties, such as Symbol.iterator.
|
||||
ImmutablePropertyNamePtr* names = cx->names().wellKnownSymbolNames();
|
||||
RootedValue value(cx);
|
||||
unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
|
||||
WellKnownSymbols* wks = cx->runtime()->wellKnownSymbols;
|
||||
for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
|
||||
value.setSymbol(wks->get(i));
|
||||
if (!NativeDefineProperty(cx, ctor, names[i], value, nullptr, nullptr, attrs))
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto) ||
|
||||
!DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
|
||||
!DefineToStringTag(cx, proto, cx->names().Symbol) ||
|
||||
!DefinePropertiesAndFunctions(cx, ctor, nullptr, staticMethods) ||
|
||||
!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Symbol, ctor, proto))
|
||||
{
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||
return nullptr;
|
||||
|
||||
if (defineMembers) {
|
||||
if (!DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
|
||||
!DefineToStringTag(cx, proto, cx->names().Symbol) ||
|
||||
!DefinePropertiesAndFunctions(cx, ctor, nullptr, staticMethods))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Symbol, ctor, proto))
|
||||
return nullptr;
|
||||
return proto;
|
||||
}
|
||||
|
||||
|
|
@ -258,5 +266,11 @@ SymbolObject::descriptionGetter(JSContext* cx, unsigned argc, Value* vp)
|
|||
JSObject*
|
||||
js::InitSymbolClass(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
return SymbolObject::initClass(cx, obj);
|
||||
return SymbolObject::initClass(cx, obj, true);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::InitBareSymbolCtor(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
return SymbolObject::initClass(cx, obj, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class SymbolObject : public NativeObject
|
|||
|
||||
static const Class class_;
|
||||
|
||||
static JSObject* initClass(JSContext* cx, js::HandleObject obj);
|
||||
static JSObject* initClass(JSContext* cx, js::HandleObject obj, bool defineMembers);
|
||||
|
||||
/*
|
||||
* Creates a new Symbol object boxing the given primitive Symbol. The
|
||||
|
|
@ -63,6 +63,9 @@ class SymbolObject : public NativeObject
|
|||
extern JSObject*
|
||||
InitSymbolClass(JSContext* cx, HandleObject obj);
|
||||
|
||||
extern JSObject*
|
||||
InitBareSymbolCtor(JSContext* cx, HandleObject obj);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* builtin_SymbolObject_h */
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@
|
|||
// Do not create an alias to a self-hosted builtin, otherwise it will be cloned
|
||||
// twice.
|
||||
//
|
||||
// Symbol is a bare constructor without properties or methods.
|
||||
var std_Symbol = Symbol;
|
||||
// WeakMap is a bare constructor without properties or methods.
|
||||
var std_WeakMap = WeakMap;
|
||||
// StopIteration is a bare constructor without properties or methods.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
// Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
|
||||
|
||||
// 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;
|
||||
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreateCollatorPrototype(cx, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
||||
obj = NewObjectWithGivenProto<CollatorObject>(cx, proto);
|
||||
if (!obj)
|
||||
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());
|
||||
|
||||
// Step 6.
|
||||
if (!intl::InitializeObject(cx, obj, cx->names().InitializeCollator, locales, options))
|
||||
// Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||
RootedObject proto(cx);
|
||||
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*obj);
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreateCollatorPrototype(cx, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
||||
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, collator, cx->names().InitializeCollator, locales, options))
|
||||
return false;
|
||||
|
||||
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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
assert(IsObject(collator), "InitializeCollator called with non-object");
|
||||
assert(IsCollator(collator), "InitializeCollator called with non-Collator");
|
||||
|
||||
// Step 1.
|
||||
if (isInitializedIntlObject(collator))
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
|
||||
|
||||
// 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,
|
||||
|
|
|
|||
|
|
@ -19,26 +19,10 @@
|
|||
|
||||
#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();
|
||||
|
|
|
|||
|
|
@ -26,12 +26,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.
|
||||
*/
|
||||
|
|
@ -39,6 +33,15 @@ extern bool
|
|||
InitializeObject(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
|
||||
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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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"].)
|
||||
// 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).
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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");
|
||||
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 = callFunction(std_WeakMap_get, internalsMap, obj);
|
||||
assert(internals === undefined || isInitializedIntlObject(obj), "bad mapping in internalsMap");
|
||||
var internals = UnsafeGetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT);
|
||||
|
||||
if (internals === undefined || internals.type !== className)
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, className, methodName, className);
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -91,63 +91,35 @@ static const JSFunctionSpec dateTimeFormat_methods[] = {
|
|||
static bool
|
||||
DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct)
|
||||
{
|
||||
RootedObject obj(cx);
|
||||
// Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
obj = NewObjectWithGivenProto<DateTimeFormatObject>(cx, proto);
|
||||
if (!obj)
|
||||
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))
|
||||
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||
RootedObject proto(cx);
|
||||
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*obj);
|
||||
return true;
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreateDateTimeFormatPrototype(cx, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<DateTimeFormatObject*> dateTimeFormat(cx);
|
||||
dateTimeFormat = NewObjectWithGivenProto<DateTimeFormatObject>(cx, proto);
|
||||
if (!dateTimeFormat)
|
||||
return false;
|
||||
|
||||
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());
|
||||
Rooted<DateTimeFormatObject*> dateTimeFormat(cx);
|
||||
dateTimeFormat = &args[0].toObject().as<DateTimeFormatObject>();
|
||||
|
||||
// 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.
|
||||
// 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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
function InitializeDateTimeFormat(dateTimeFormat, thisValue, locales, options) {
|
||||
assert(IsObject(dateTimeFormat), "InitializeDateTimeFormat called with non-Object");
|
||||
assert(IsDateTimeFormat(dateTimeFormat),
|
||||
"InitializeDateTimeFormat called with non-DateTimeFormat");
|
||||
|
||||
// Step 1.
|
||||
if (isInitializedIntlObject(dateTimeFormat))
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
|
||||
|
||||
// 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,
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
||||
function Intl_getCalendarInfo(locales) {
|
||||
const requestedLocales = CanonicalizeLocaleList(locales);
|
||||
|
||||
const DateTimeFormat = dateTimeFormatInternalProperties;
|
||||
const localeData = DateTimeFormat.localeData;
|
||||
|
||||
const localeOpt = new Record();
|
||||
localeOpt.localeMatcher = "best fit";
|
||||
|
||||
const r = ResolveLocale(callFunction(DateTimeFormat.availableLocales, DateTimeFormat),
|
||||
requestedLocales,
|
||||
localeOpt,
|
||||
DateTimeFormat.relevantExtensionKeys,
|
||||
localeData);
|
||||
|
||||
const result = intl_GetCalendarInfo(r.locale);
|
||||
result.calendar = r.ca;
|
||||
result.locale = r.locale;
|
||||
|
||||
return result;
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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) {
|
||||
// 1. Let requestLocales be ? CanonicalizeLocaleList(locales).
|
||||
const requestedLocales = CanonicalizeLocaleList(locales);
|
||||
|
||||
const DateTimeFormat = dateTimeFormatInternalProperties;
|
||||
|
||||
// 2. Let localeData be %DateTimeFormat%.[[localeData]].
|
||||
const localeData = DateTimeFormat.localeData;
|
||||
|
||||
// 3. Let localeOpt be a new Record.
|
||||
const localeOpt = new Record();
|
||||
|
||||
// 4. Set localeOpt.[[localeMatcher]] to "best fit".
|
||||
localeOpt.localeMatcher = "best fit";
|
||||
|
||||
// 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);
|
||||
|
||||
// 7. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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++) {
|
||||
|
|
|
|||
|
|
@ -92,63 +92,34 @@ static const JSFunctionSpec numberFormat_methods[] = {
|
|||
static bool
|
||||
NumberFormat(JSContext* cx, const CallArgs& args, bool construct)
|
||||
{
|
||||
RootedObject obj(cx);
|
||||
// Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
|
||||
|
||||
// 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;
|
||||
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreateNumberFormatPrototype(cx, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
||||
obj = NewObjectWithGivenProto<NumberFormatObject>(cx, proto);
|
||||
if (!obj)
|
||||
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))
|
||||
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||
RootedObject proto(cx);
|
||||
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*obj);
|
||||
return true;
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreateNumberFormatPrototype(cx, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<NumberFormatObject*> numberFormat(cx);
|
||||
numberFormat = NewObjectWithGivenProto<NumberFormatObject>(cx, proto);
|
||||
if (!numberFormat)
|
||||
return false;
|
||||
|
||||
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());
|
||||
Rooted<NumberFormatObject*> numberFormat(cx, &args[0].toObject().as<NumberFormatObject>());
|
||||
|
||||
// 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.
|
||||
// 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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
assert(IsObject(pluralRules), "InitializePluralRules called with non-object");
|
||||
assert(IsPluralRules(pluralRules), "InitializePluralRules called with non-PluralRules");
|
||||
|
||||
// Step 1.
|
||||
if (isInitializedIntlObject(pluralRules))
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
|
||||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ evalcx(`
|
|||
');
|
||||
oomTest(() => eval('Array(..."")'));
|
||||
if ('Intl' in this)
|
||||
Intl.NumberFormat.prototype.format(0);
|
||||
new Intl.NumberFormat().format(0);
|
||||
`, newGlobal());
|
||||
|
|
|
|||
|
|
@ -4,15 +4,8 @@ load(libdir + "asserts.js");
|
|||
// any value except "best fit" or "lookup" is okay.
|
||||
Object.prototype.localeMatcher = "invalid matcher option";
|
||||
|
||||
// The Intl API may not be available in the testing environment. Note that |hasOwnProperty("Intl")|
|
||||
// initializes the Intl API if present, so this if-statement needs to appear after "localeMatcher"
|
||||
// was added to Object.prototype.
|
||||
// The Intl API may not be available in the testing environment.
|
||||
if (this.hasOwnProperty("Intl")) {
|
||||
// Intl prototypes are properly initialized despite changed Object.prototype.
|
||||
Intl.Collator.prototype.compare("a", "b");
|
||||
Intl.NumberFormat.prototype.format(10);
|
||||
Intl.DateTimeFormat.prototype.format(new Date);
|
||||
|
||||
// Intl constructors no longer work properly, because "localeMatcher" defaults to the invalid
|
||||
// value from Object.prototype. Except for Intl.DateTimeFormat, cf. ECMA-402 ToDateTimeOptions.
|
||||
assertThrowsInstanceOf(() => new Intl.Collator(), RangeError);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ for (var i = 0; i < 3; i++) {
|
|||
x.toString();
|
||||
assertEq(0, 1);
|
||||
} catch (e) {
|
||||
assertEq(e.message, "y is undefined");
|
||||
assertEq(e.message === "y is undefined" ||
|
||||
e.message === "undefined has no properties", true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,12 @@
|
|||
_(AtomicsXor) \
|
||||
_(AtomicsIsLockFree) \
|
||||
\
|
||||
_(IntlIsCollator) \
|
||||
_(IntlIsDateTimeFormat) \
|
||||
_(IntlIsNumberFormat) \
|
||||
_(IntlIsPluralRules) \
|
||||
_(IntlIsRelativeTimeFormat) \
|
||||
\
|
||||
_(MathAbs) \
|
||||
_(MathFloor) \
|
||||
_(MathCeil) \
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@
|
|||
#include "builtin/SIMD.h"
|
||||
#include "builtin/TestingFunctions.h"
|
||||
#include "builtin/TypedObject.h"
|
||||
#include "builtin/intl/Collator.h"
|
||||
#include "builtin/intl/DateTimeFormat.h"
|
||||
#include "builtin/intl/NumberFormat.h"
|
||||
#include "builtin/intl/PluralRules.h"
|
||||
#include "builtin/intl/RelativeTimeFormat.h"
|
||||
#include "jit/BaselineInspector.h"
|
||||
#include "jit/InlinableNatives.h"
|
||||
#include "jit/IonBuilder.h"
|
||||
|
|
@ -104,6 +109,18 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
|
|||
case InlinableNative::AtomicsIsLockFree:
|
||||
return inlineAtomicsIsLockFree(callInfo);
|
||||
|
||||
// Intl natives.
|
||||
case InlinableNative::IntlIsCollator:
|
||||
return inlineHasClass(callInfo, &CollatorObject::class_);
|
||||
case InlinableNative::IntlIsDateTimeFormat:
|
||||
return inlineHasClass(callInfo, &DateTimeFormatObject::class_);
|
||||
case InlinableNative::IntlIsNumberFormat:
|
||||
return inlineHasClass(callInfo, &NumberFormatObject::class_);
|
||||
case InlinableNative::IntlIsPluralRules:
|
||||
return inlineHasClass(callInfo, &PluralRulesObject::class_);
|
||||
case InlinableNative::IntlIsRelativeTimeFormat:
|
||||
return inlineHasClass(callInfo, &RelativeTimeFormatObject::class_);
|
||||
|
||||
// Math natives.
|
||||
case InlinableNative::MathAbs:
|
||||
return inlineMathAbs(callInfo);
|
||||
|
|
|
|||
|
|
@ -487,7 +487,6 @@ MSG_DEF(JSMSG_TRACELOGGER_ENABLE_FAIL, 1, JSEXN_ERR, "enabling tracelogger faile
|
|||
MSG_DEF(JSMSG_DATE_NOT_FINITE, 2, JSEXN_RANGEERR, "date value is not finite in {0}.{1}()")
|
||||
MSG_DEF(JSMSG_INTERNAL_INTL_ERROR, 0, JSEXN_ERR, "internal error while computing Intl data")
|
||||
MSG_DEF(JSMSG_INTL_OBJECT_NOT_INITED, 3, JSEXN_TYPEERR, "Intl.{0}.prototype.{1} called on value that's not an object initialized as a {2}")
|
||||
MSG_DEF(JSMSG_INTL_OBJECT_REINITED, 0, JSEXN_TYPEERR, "can't initialize object twice as an object of an Intl constructor")
|
||||
MSG_DEF(JSMSG_INVALID_CURRENCY_CODE, 1, JSEXN_RANGEERR, "invalid currency code in NumberFormat(): {0}")
|
||||
MSG_DEF(JSMSG_INVALID_DIGITS_VALUE, 1, JSEXN_RANGEERR, "invalid digits value: {0}")
|
||||
MSG_DEF(JSMSG_INVALID_KEYS_TYPE, 0, JSEXN_TYPEERR, "calendar info keys must be an object or undefined")
|
||||
|
|
|
|||
|
|
@ -524,6 +524,7 @@ GlobalObject::initSelfHostingBuiltins(JSContext* cx, Handle<GlobalObject*> globa
|
|||
InitBareBuiltinCtor(cx, global, JSProto_TypedArray) &&
|
||||
InitBareBuiltinCtor(cx, global, JSProto_Uint8Array) &&
|
||||
InitBareBuiltinCtor(cx, global, JSProto_Int32Array) &&
|
||||
InitBareSymbolCtor(cx, global) &&
|
||||
InitBareWeakMapCtor(cx, global) &&
|
||||
InitStopIterationClass(cx, global) &&
|
||||
DefineFunctions(cx, global, builtins, AsIntrinsic);
|
||||
|
|
|
|||
|
|
@ -107,7 +107,9 @@ class GlobalObject : public NativeObject
|
|||
MAP_ITERATOR_PROTO,
|
||||
SET_ITERATOR_PROTO,
|
||||
COLLATOR_PROTO,
|
||||
NUMBER_FORMAT,
|
||||
NUMBER_FORMAT_PROTO,
|
||||
DATE_TIME_FORMAT,
|
||||
DATE_TIME_FORMAT_PROTO,
|
||||
PLURAL_RULES_PROTO,
|
||||
RELATIVE_TIME_FORMAT_PROTO,
|
||||
|
|
@ -499,11 +501,23 @@ class GlobalObject : public NativeObject
|
|||
return getOrCreateObject(cx, global, COLLATOR_PROTO, initIntlObject);
|
||||
}
|
||||
|
||||
static JSFunction*
|
||||
getOrCreateNumberFormatConstructor(JSContext* cx, Handle<GlobalObject*> global) {
|
||||
JSObject* obj = getOrCreateObject(cx, global, NUMBER_FORMAT, initIntlObject);
|
||||
return obj ? &obj->as<JSFunction>() : nullptr;
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
getOrCreateNumberFormatPrototype(JSContext* cx, Handle<GlobalObject*> global) {
|
||||
return getOrCreateObject(cx, global, NUMBER_FORMAT_PROTO, initIntlObject);
|
||||
}
|
||||
|
||||
static JSFunction*
|
||||
getOrCreateDateTimeFormatConstructor(JSContext* cx, Handle<GlobalObject*> global) {
|
||||
JSObject* obj = getOrCreateObject(cx, global, DATE_TIME_FORMAT, initIntlObject);
|
||||
return obj ? &obj->as<JSFunction>() : nullptr;
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
getOrCreateDateTimeFormatPrototype(JSContext* cx, Handle<GlobalObject*> global) {
|
||||
return getOrCreateObject(cx, global, DATE_TIME_FORMAT_PROTO, initIntlObject);
|
||||
|
|
|
|||
|
|
@ -1905,6 +1905,23 @@ intrinsic_RuntimeDefaultLocale(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
using GetOrCreateIntlConstructor = JSFunction* (*)(JSContext*, Handle<GlobalObject*>);
|
||||
|
||||
template <GetOrCreateIntlConstructor getOrCreateIntlConstructor>
|
||||
static bool
|
||||
intrinsic_GetBuiltinIntlConstructor(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 0);
|
||||
|
||||
JSFunction* constructor = getOrCreateIntlConstructor(cx, cx->global());
|
||||
if (!constructor)
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*constructor);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
intrinsic_ConstructFunction(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
|
|
@ -2469,6 +2486,28 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
|||
JS_FN("intl_RelativeTimeFormat_availableLocales", intl_RelativeTimeFormat_availableLocales, 0,0),
|
||||
JS_FN("intl_FormatRelativeTime", intl_FormatRelativeTime, 3,0),
|
||||
|
||||
JS_INLINABLE_FN("IsCollator",
|
||||
intrinsic_IsInstanceOfBuiltin<CollatorObject>, 1,0,
|
||||
IntlIsCollator),
|
||||
JS_INLINABLE_FN("IsDateTimeFormat",
|
||||
intrinsic_IsInstanceOfBuiltin<DateTimeFormatObject>, 1,0,
|
||||
IntlIsDateTimeFormat),
|
||||
JS_INLINABLE_FN("IsNumberFormat",
|
||||
intrinsic_IsInstanceOfBuiltin<NumberFormatObject>, 1,0,
|
||||
IntlIsNumberFormat),
|
||||
JS_INLINABLE_FN("IsPluralRules",
|
||||
intrinsic_IsInstanceOfBuiltin<PluralRulesObject>, 1,0,
|
||||
IntlIsPluralRules),
|
||||
JS_INLINABLE_FN("IsRelativeTimeFormat",
|
||||
intrinsic_IsInstanceOfBuiltin<RelativeTimeFormatObject>, 1,0,
|
||||
IntlIsRelativeTimeFormat),
|
||||
JS_FN("GetDateTimeFormatConstructor",
|
||||
intrinsic_GetBuiltinIntlConstructor<GlobalObject::getOrCreateDateTimeFormatConstructor>,
|
||||
0,0),
|
||||
JS_FN("GetNumberFormatConstructor",
|
||||
intrinsic_GetBuiltinIntlConstructor<GlobalObject::getOrCreateNumberFormatConstructor>,
|
||||
0,0),
|
||||
|
||||
JS_INLINABLE_FN("IsRegExpObject",
|
||||
intrinsic_IsInstanceOfBuiltin<RegExpObject>, 1,0,
|
||||
IsRegExpObject),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue