mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-29 20:07:30 +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.
|
// application.
|
||||||
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
|
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
|
||||||
#define JSCLASS_GLOBAL_SLOT_COUNT \
|
#define JSCLASS_GLOBAL_SLOT_COUNT \
|
||||||
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 47)
|
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 49)
|
||||||
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
||||||
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
||||||
#define JSCLASS_GLOBAL_FLAGS \
|
#define JSCLASS_GLOBAL_FLAGS \
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ function Date_toLocaleString() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 7.
|
// Step 7.
|
||||||
return intl_FormatDateTime(dateTimeFormat, x, false);
|
return intl_FormatDateTime(dateTimeFormat, x, /* formatToParts = */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -137,7 +137,7 @@ function Date_toLocaleDateString() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 7.
|
// Step 7.
|
||||||
return intl_FormatDateTime(dateTimeFormat, x, false);
|
return intl_FormatDateTime(dateTimeFormat, x, /* formatToParts = */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -170,5 +170,5 @@ function Date_toLocaleTimeString() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 7.
|
// Step 7.
|
||||||
return intl_FormatDateTime(dateTimeFormat, x, false);
|
return intl_FormatDateTime(dateTimeFormat, x, /* formatToParts = */ false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ const JSFunctionSpec SymbolObject::staticMethods[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
JSObject*
|
JSObject*
|
||||||
SymbolObject::initClass(JSContext* cx, HandleObject obj)
|
SymbolObject::initClass(JSContext* cx, HandleObject obj, bool defineMembers)
|
||||||
{
|
{
|
||||||
Handle<GlobalObject*> global = obj.as<GlobalObject>();
|
Handle<GlobalObject*> global = obj.as<GlobalObject>();
|
||||||
|
|
||||||
|
|
@ -66,6 +66,7 @@ SymbolObject::initClass(JSContext* cx, HandleObject obj)
|
||||||
if (!ctor)
|
if (!ctor)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
if (defineMembers) {
|
||||||
// Define the well-known symbol properties, such as Symbol.iterator.
|
// Define the well-known symbol properties, such as Symbol.iterator.
|
||||||
ImmutablePropertyNamePtr* names = cx->names().wellKnownSymbolNames();
|
ImmutablePropertyNamePtr* names = cx->names().wellKnownSymbolNames();
|
||||||
RootedValue value(cx);
|
RootedValue value(cx);
|
||||||
|
|
@ -76,15 +77,22 @@ SymbolObject::initClass(JSContext* cx, HandleObject obj)
|
||||||
if (!NativeDefineProperty(cx, ctor, names[i], value, nullptr, nullptr, attrs))
|
if (!NativeDefineProperty(cx, ctor, names[i], value, nullptr, nullptr, attrs))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!LinkConstructorAndPrototype(cx, ctor, proto) ||
|
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||||
!DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
|
return nullptr;
|
||||||
|
|
||||||
|
if (defineMembers) {
|
||||||
|
if (!DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
|
||||||
!DefineToStringTag(cx, proto, cx->names().Symbol) ||
|
!DefineToStringTag(cx, proto, cx->names().Symbol) ||
|
||||||
!DefinePropertiesAndFunctions(cx, ctor, nullptr, staticMethods) ||
|
!DefinePropertiesAndFunctions(cx, ctor, nullptr, staticMethods))
|
||||||
!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Symbol, ctor, proto))
|
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Symbol, ctor, proto))
|
||||||
|
return nullptr;
|
||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -258,5 +266,11 @@ SymbolObject::descriptionGetter(JSContext* cx, unsigned argc, Value* vp)
|
||||||
JSObject*
|
JSObject*
|
||||||
js::InitSymbolClass(JSContext* cx, HandleObject obj)
|
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 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
|
* Creates a new Symbol object boxing the given primitive Symbol. The
|
||||||
|
|
@ -63,6 +63,9 @@ class SymbolObject : public NativeObject
|
||||||
extern JSObject*
|
extern JSObject*
|
||||||
InitSymbolClass(JSContext* cx, HandleObject obj);
|
InitSymbolClass(JSContext* cx, HandleObject obj);
|
||||||
|
|
||||||
|
extern JSObject*
|
||||||
|
InitBareSymbolCtor(JSContext* cx, HandleObject obj);
|
||||||
|
|
||||||
} /* namespace js */
|
} /* namespace js */
|
||||||
|
|
||||||
#endif /* builtin_SymbolObject_h */
|
#endif /* builtin_SymbolObject_h */
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@
|
||||||
// Do not create an alias to a self-hosted builtin, otherwise it will be cloned
|
// Do not create an alias to a self-hosted builtin, otherwise it will be cloned
|
||||||
// twice.
|
// twice.
|
||||||
//
|
//
|
||||||
|
// Symbol is a bare constructor without properties or methods.
|
||||||
|
var std_Symbol = Symbol;
|
||||||
// WeakMap is a bare constructor without properties or methods.
|
// WeakMap is a bare constructor without properties or methods.
|
||||||
var std_WeakMap = WeakMap;
|
var std_WeakMap = WeakMap;
|
||||||
// StopIteration is a bare constructor without properties or methods.
|
// StopIteration is a bare constructor without properties or methods.
|
||||||
|
|
|
||||||
|
|
@ -79,37 +79,10 @@ static const JSFunctionSpec collator_methods[] = {
|
||||||
* ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
|
* ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
|
||||||
*/
|
*/
|
||||||
static bool
|
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).
|
// Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||||
RootedObject proto(cx);
|
RootedObject proto(cx);
|
||||||
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||||
|
|
@ -121,22 +94,21 @@ Collator(JSContext* cx, const CallArgs& args, bool construct)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = NewObjectWithGivenProto<CollatorObject>(cx, proto);
|
Rooted<CollatorObject*> collator(cx, NewObjectWithGivenProto<CollatorObject>(cx, proto));
|
||||||
if (!obj)
|
if (!collator)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
obj->as<NativeObject>().setReservedSlot(CollatorObject::INTERNALS_SLOT, NullValue());
|
collator->setReservedSlot(CollatorObject::INTERNALS_SLOT, NullValue());
|
||||||
obj->as<NativeObject>().setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(nullptr));
|
collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(nullptr));
|
||||||
}
|
|
||||||
|
|
||||||
RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
|
RootedValue locales(cx, args.get(0));
|
||||||
RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
|
RootedValue options(cx, args.get(1));
|
||||||
|
|
||||||
// Step 6.
|
// Step 6.
|
||||||
if (!intl::InitializeObject(cx, obj, cx->names().InitializeCollator, locales, options))
|
if (!intl::InitializeObject(cx, collator, cx->names().InitializeCollator, locales, options))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
args.rval().setObject(*obj);
|
args.rval().setObject(*collator);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,7 +116,7 @@ static bool
|
||||||
Collator(JSContext* cx, unsigned argc, Value* vp)
|
Collator(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
return Collator(cx, args, args.isConstructing());
|
return Collator(cx, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -153,9 +125,8 @@ js::intl_Collator(JSContext* cx, unsigned argc, Value* vp)
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
MOZ_ASSERT(args.length() == 2);
|
MOZ_ASSERT(args.length() == 2);
|
||||||
MOZ_ASSERT(!args.isConstructing());
|
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);
|
||||||
return Collator(cx, args, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -163,15 +134,9 @@ js::CollatorObject::finalize(FreeOp* fop, JSObject* obj)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(fop->onMainThread());
|
MOZ_ASSERT(fop->onMainThread());
|
||||||
|
|
||||||
// This is-undefined check shouldn't be necessary, but for internal
|
const Value& slot = obj->as<CollatorObject>().getReservedSlot(CollatorObject::UCOLLATOR_SLOT);
|
||||||
// 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()))
|
if (UCollator* coll = static_cast<UCollator*>(slot.toPrivate()))
|
||||||
ucol_close(coll);
|
ucol_close(coll);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObject*
|
JSObject*
|
||||||
|
|
@ -182,10 +147,9 @@ js::CreateCollatorPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObjec
|
||||||
if (!ctor)
|
if (!ctor)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &CollatorObject::class_));
|
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
|
||||||
if (!proto)
|
if (!proto)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
proto->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(nullptr));
|
|
||||||
|
|
||||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -213,14 +177,6 @@ js::CreateCollatorPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObjec
|
||||||
return nullptr;
|
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
|
// 8.1
|
||||||
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||||
if (!DefineProperty(cx, Intl, cx->names().Collator, ctorValue, nullptr, nullptr, 0))
|
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.
|
* of the given Collator.
|
||||||
*/
|
*/
|
||||||
static UCollator*
|
static UCollator*
|
||||||
NewUCollator(JSContext* cx, HandleObject collator)
|
NewUCollator(JSContext* cx, Handle<CollatorObject*> collator)
|
||||||
{
|
{
|
||||||
RootedValue value(cx);
|
RootedValue value(cx);
|
||||||
|
|
||||||
|
|
@ -491,39 +447,19 @@ js::intl_CompareStrings(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
|
||||||
Rooted<CollatorObject*> collator(cx, &args[0].toObject().as<CollatorObject>());
|
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?
|
// 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();
|
void* priv = collator->getReservedSlot(CollatorObject::UCOLLATOR_SLOT).toPrivate();
|
||||||
coll = static_cast<UCollator*>(priv);
|
UCollator* coll = static_cast<UCollator*>(priv);
|
||||||
if (!coll) {
|
if (!coll) {
|
||||||
coll = NewUCollator(cx, collator);
|
coll = NewUCollator(cx, collator);
|
||||||
if (!coll)
|
if (!coll)
|
||||||
return false;
|
return false;
|
||||||
collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(coll));
|
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.
|
|
||||||
coll = NewUCollator(cx, collator);
|
|
||||||
if (!coll)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the UCollator to actually compare the strings.
|
// Use the UCollator to actually compare the strings.
|
||||||
RootedString str1(cx, args[1].toString());
|
RootedString str1(cx, args[1].toString());
|
||||||
RootedString str2(cx, args[2].toString());
|
RootedString str2(cx, args[2].toString());
|
||||||
RootedValue result(cx);
|
return intl_CompareStrings(cx, coll, str1, str2, args.rval());
|
||||||
bool success = intl_CompareStrings(cx, coll, str1, str2, &result);
|
|
||||||
|
|
||||||
if (!isCollatorInstance)
|
|
||||||
ucol_close(coll);
|
|
||||||
if (!success)
|
|
||||||
return false;
|
|
||||||
args.rval().set(result);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,11 +107,13 @@ function resolveCollatorInternals(lazyCollatorData)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an object containing the Collator internal properties of |obj|, or
|
* Returns an object containing the Collator internal properties of |obj|.
|
||||||
* throws a TypeError if |obj| isn't Collator-initialized.
|
|
||||||
*/
|
*/
|
||||||
function getCollatorInternals(obj, methodName) {
|
function getCollatorInternals(obj) {
|
||||||
var internals = getIntlObjectInternals(obj, "Collator", methodName);
|
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");
|
assert(internals.type === "Collator", "bad type escaped getIntlObjectInternals");
|
||||||
|
|
||||||
// If internal properties have already been computed, use them.
|
// 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.
|
* Spec: ECMAScript Internationalization API Specification, 10.1.1.
|
||||||
*/
|
*/
|
||||||
function InitializeCollator(collator, locales, options) {
|
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.
|
// Steps 1-2 (These steps are no longer required and should be removed
|
||||||
if (isInitializedIntlObject(collator))
|
// from the spec; https://github.com/tc39/ecma402/issues/115).;
|
||||||
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
|
|
||||||
|
|
||||||
// Step 2.
|
|
||||||
var internals = initializeIntlObject(collator);
|
|
||||||
|
|
||||||
// Lazy Collator data has the following structure:
|
// 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
|
// We've done everything that must be done now: mark the lazy data as fully
|
||||||
// computed and install it.
|
// 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() {
|
function Intl_Collator_compare_get() {
|
||||||
// Check "this Collator object" per introduction of section 10.3.
|
// 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.
|
// Step 1.
|
||||||
if (internals.boundCompare === undefined) {
|
if (internals.boundCompare === undefined) {
|
||||||
// Step 1.a.
|
// Step 1.a.
|
||||||
var F = collatorCompareToBind;
|
var F = collatorCompareToBind;
|
||||||
|
|
||||||
// Step 1.b-d.
|
// Steps 1.b-d.
|
||||||
var bc = callFunction(FunctionBind, F, this);
|
var bc = callFunction(FunctionBind, F, this);
|
||||||
internals.boundCompare = bc;
|
internals.boundCompare = bc;
|
||||||
}
|
}
|
||||||
|
|
@ -335,7 +337,10 @@ function Intl_Collator_compare_get() {
|
||||||
*/
|
*/
|
||||||
function Intl_Collator_resolvedOptions() {
|
function Intl_Collator_resolvedOptions() {
|
||||||
// Check "this Collator object" per introduction of section 10.3.
|
// 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 = {
|
var result = {
|
||||||
locale: internals.locale,
|
locale: internals.locale,
|
||||||
|
|
|
||||||
|
|
@ -19,26 +19,10 @@
|
||||||
|
|
||||||
#include "jsobjinlines.h"
|
#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
|
bool
|
||||||
js::intl::InitializeObject(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
|
js::intl::InitializeObject(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
|
||||||
HandleValue locales, HandleValue options)
|
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);
|
FixedInvokeArgs<3> args(cx);
|
||||||
|
|
||||||
args[0].setObject(*obj);
|
args[0].setObject(*obj);
|
||||||
|
|
@ -47,7 +31,32 @@ js::intl::InitializeObject(JSContext* cx, HandleObject obj, Handle<PropertyName*
|
||||||
|
|
||||||
RootedValue thisv(cx, NullValue());
|
RootedValue thisv(cx, NullValue());
|
||||||
RootedValue ignored(cx);
|
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*
|
JSObject*
|
||||||
js::intl::GetInternalsObject(JSContext* cx, HandleObject obj)
|
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);
|
FixedInvokeArgs<1> args(cx);
|
||||||
|
|
||||||
args[0].setObject(*obj);
|
args[0].setObject(*obj);
|
||||||
|
|
||||||
RootedValue v(cx, NullValue());
|
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 nullptr;
|
||||||
|
|
||||||
return &v.toObject();
|
return &v.toObject();
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,6 @@ namespace js {
|
||||||
|
|
||||||
namespace intl {
|
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.
|
* 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,
|
InitializeObject(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
|
||||||
HandleValue locales, HandleValue options);
|
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.
|
* 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
|
* The [[FallbackSymbol]] symbol of the %Intl% intrinsic object.
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* Ideally we'd be using private symbols for internal properties, but
|
* This symbol is used to implement the legacy constructor semantics for
|
||||||
* SpiderMonkey doesn't have those yet.
|
* 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(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]]
|
// Intl-initialized objects are weird. They have [[initializedIntlObject]]
|
||||||
// set on them, but they don't *necessarily* have any other properties.
|
// 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.
|
// The meaning of an internals object for an object |obj| is as follows.
|
||||||
//
|
//
|
||||||
// If the .type is "partial", |obj| has [[initializedIntlObject]] set but
|
// The .type property indicates the type of Intl object that |obj| is:
|
||||||
// nothing else. No other property of |internals| can be used. (This
|
// "Collator", "DateTimeFormat", "NumberFormat", or "PluralRules" (likely
|
||||||
// occurs when InitializeCollator or similar marks an object as
|
// with more coming in future Intl specs).
|
||||||
// [[initializedIntlObject]] but fails before marking it as the appropriate
|
|
||||||
// more-specific type ["Collator", "DateTimeFormat", "NumberFormat"].)
|
|
||||||
//
|
//
|
||||||
// Otherwise, the .type indicates the type of Intl object that |obj| is:
|
// The .lazyData property stores information needed to compute -- without
|
||||||
// "Collator", "DateTimeFormat", or "NumberFormat" (likely with more coming
|
// observable side effects -- the actual internal Intl properties of
|
||||||
// in future Intl specs). In these cases |obj| *conceptually* also has
|
// |obj|. If it is non-null, then the actual internal properties haven't
|
||||||
// [[initializedCollator]] or similar set, and all the other properties
|
// been computed, and .lazyData must be processed by
|
||||||
// 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
|
|
||||||
// |setInternalProperties| before internal Intl property values are
|
// |setInternalProperties| before internal Intl property values are
|
||||||
// available. If it is null, then the .internalProps property contains an
|
// available. If it is null, then the .internalProps property contains an
|
||||||
// object whose properties are the internal Intl properties of |obj|.
|
// object whose properties are the internal Intl properties of |obj|.
|
||||||
|
|
||||||
internals.type = "partial";
|
var internals = std_Object_create(null);
|
||||||
internals.lazyData = null;
|
internals.type = type;
|
||||||
|
internals.lazyData = lazyData;
|
||||||
internals.internalProps = null;
|
internals.internalProps = null;
|
||||||
|
|
||||||
callFunction(std_WeakMap_set, internalsMap, obj, internals);
|
assert(UnsafeGetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT) === null,
|
||||||
return internals;
|
"Internal slot already initialized?");
|
||||||
}
|
UnsafeSetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1136,9 +1123,7 @@ function setLazyData(internals, type, lazyData)
|
||||||
* Set the internal properties object for an |internals| object previously
|
* Set the internal properties object for an |internals| object previously
|
||||||
* associated with lazy data.
|
* associated with lazy data.
|
||||||
*/
|
*/
|
||||||
function setInternalProperties(internals, internalProps)
|
function setInternalProperties(internals, internalProps) {
|
||||||
{
|
|
||||||
assert(internals.type !== "partial", "newborn internals can't have computed internals");
|
|
||||||
assert(IsObject(internals.lazyData), "lazy data must exist already");
|
assert(IsObject(internals.lazyData), "lazy data must exist already");
|
||||||
assert(IsObject(internalProps), "internalProps argument should be an object");
|
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
|
* Get the existing internal properties out of a non-newborn |internals|, or
|
||||||
* null if none have been computed.
|
* null if none have been computed.
|
||||||
*/
|
*/
|
||||||
function maybeInternalProperties(internals)
|
function maybeInternalProperties(internals) {
|
||||||
{
|
|
||||||
assert(IsObject(internals), "non-object passed to maybeInternalProperties");
|
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;
|
var lazyData = internals.lazyData;
|
||||||
if (lazyData)
|
if (lazyData)
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -1165,48 +1148,33 @@ function maybeInternalProperties(internals)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether |obj| has an[[initializedIntlObject]] property set to true.
|
* Return |obj|'s internals object (*not* the object holding its internal
|
||||||
*/
|
* properties!), with structure specified above.
|
||||||
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.
|
|
||||||
*
|
*
|
||||||
* Spec: ECMAScript Internationalization API Specification, 10.3.
|
* Spec: ECMAScript Internationalization API Specification, 10.3.
|
||||||
* Spec: ECMAScript Internationalization API Specification, 11.3.
|
* Spec: ECMAScript Internationalization API Specification, 11.3.
|
||||||
* Spec: ECMAScript Internationalization API Specification, 12.3.
|
* Spec: ECMAScript Internationalization API Specification, 12.3.
|
||||||
*/
|
*/
|
||||||
function getIntlObjectInternals(obj, className, methodName) {
|
function getIntlObjectInternals(obj) {
|
||||||
assert(typeof className === "string", "bad className for getIntlObjectInternals");
|
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);
|
var internals = UnsafeGetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT);
|
||||||
assert(internals === undefined || isInitializedIntlObject(obj), "bad mapping in internalsMap");
|
|
||||||
|
|
||||||
if (internals === undefined || internals.type !== className)
|
assert(IsObject(internals), "internals not an object");
|
||||||
ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, className, methodName, className);
|
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;
|
return internals;
|
||||||
}
|
}
|
||||||
|
|
@ -1216,35 +1184,32 @@ function getIntlObjectInternals(obj, className, methodName) {
|
||||||
* Get the internal properties of known-Intl object |obj|. For use only by
|
* Get the internal properties of known-Intl object |obj|. For use only by
|
||||||
* C++ code that knows what it's doing!
|
* C++ code that knows what it's doing!
|
||||||
*/
|
*/
|
||||||
function getInternals(obj)
|
function getInternals(obj) {
|
||||||
{
|
var internals = getIntlObjectInternals(obj);
|
||||||
assert(isInitializedIntlObject(obj), "for use only on guaranteed Intl objects");
|
|
||||||
|
|
||||||
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");
|
// Otherwise it's time to fully create them.
|
||||||
var lazyData = internals.lazyData;
|
|
||||||
if (!lazyData)
|
|
||||||
return internals.internalProps;
|
|
||||||
|
|
||||||
var internalProps;
|
|
||||||
var type = internals.type;
|
var type = internals.type;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "Collator":
|
case "Collator":
|
||||||
internalProps = resolveCollatorInternals(lazyData);
|
internalProps = resolveCollatorInternals(internals.lazyData);
|
||||||
break;
|
break;
|
||||||
case "DateTimeFormat":
|
case "DateTimeFormat":
|
||||||
internalProps = resolveDateTimeFormatInternals(lazyData);
|
internalProps = resolveDateTimeFormatInternals(internals.lazyData);
|
||||||
break;
|
break;
|
||||||
case "PluralRules":
|
case "PluralRules":
|
||||||
internalProps = resolvePluralRulesInternals(lazyData);
|
internalProps = resolvePluralRulesInternals(internals.lazyData);
|
||||||
break;
|
break;
|
||||||
case "RelativeTimeFormat":
|
case "NumberFormat":
|
||||||
internalProps = resolveRelativeTimeFormatInternals(lazyData);
|
internalProps = resolveNumberFormatInternals(internals.lazyData);
|
||||||
break;
|
break;
|
||||||
default: // type === "NumberFormat"
|
default: // type === "RelativeTimeFormat"
|
||||||
internalProps = resolveNumberFormatInternals(lazyData);
|
internalProps = resolveRelativeTimeFormatInternals(internals.lazyData);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
setInternalProperties(internals, internalProps);
|
setInternalProperties(internals, internalProps);
|
||||||
|
|
|
||||||
|
|
@ -91,35 +91,8 @@ static const JSFunctionSpec dateTimeFormat_methods[] = {
|
||||||
static bool
|
static bool
|
||||||
DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct)
|
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).
|
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||||
RootedObject proto(cx);
|
RootedObject proto(cx);
|
||||||
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||||
|
|
@ -131,23 +104,22 @@ DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = NewObjectWithGivenProto<DateTimeFormatObject>(cx, proto);
|
Rooted<DateTimeFormatObject*> dateTimeFormat(cx);
|
||||||
if (!obj)
|
dateTimeFormat = NewObjectWithGivenProto<DateTimeFormatObject>(cx, proto);
|
||||||
|
if (!dateTimeFormat)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
obj->as<NativeObject>().setReservedSlot(DateTimeFormatObject::INTERNALS_SLOT, NullValue());
|
dateTimeFormat->setReservedSlot(DateTimeFormatObject::INTERNALS_SLOT, NullValue());
|
||||||
obj->as<NativeObject>().setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT, PrivateValue(nullptr));
|
dateTimeFormat->setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT,
|
||||||
}
|
PrivateValue(nullptr));
|
||||||
|
|
||||||
RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
|
RootedValue thisValue(cx, construct ? ObjectValue(*dateTimeFormat) : args.thisv());
|
||||||
RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
|
RootedValue locales(cx, args.get(0));
|
||||||
|
RootedValue options(cx, args.get(1));
|
||||||
|
|
||||||
// Step 3.
|
// Step 3.
|
||||||
if (!intl::InitializeObject(cx, obj, cx->names().InitializeDateTimeFormat, locales, options))
|
return intl::LegacyIntlInitialize(cx, dateTimeFormat, cx->names().InitializeDateTimeFormat,
|
||||||
return false;
|
thisValue, locales, options, args.rval());
|
||||||
|
|
||||||
args.rval().setObject(*obj);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -174,30 +146,23 @@ js::DateTimeFormatObject::finalize(FreeOp* fop, JSObject* obj)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(fop->onMainThread());
|
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);
|
const Value& slot = obj->as<DateTimeFormatObject>().getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT);
|
||||||
if (!slot.isUndefined()) {
|
|
||||||
if (UDateFormat* df = static_cast<UDateFormat*>(slot.toPrivate()))
|
if (UDateFormat* df = static_cast<UDateFormat*>(slot.toPrivate()))
|
||||||
udat_close(df);
|
udat_close(df);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObject*
|
JSObject*
|
||||||
js::CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
|
js::CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global,
|
||||||
|
MutableHandleObject constructor)
|
||||||
{
|
{
|
||||||
RootedFunction ctor(cx);
|
RootedFunction ctor(cx);
|
||||||
ctor = GlobalObject::createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0);
|
ctor = GlobalObject::createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0);
|
||||||
if (!ctor)
|
if (!ctor)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global,
|
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
|
||||||
&DateTimeFormatObject::class_));
|
|
||||||
if (!proto)
|
if (!proto)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
proto->setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT, PrivateValue(nullptr));
|
|
||||||
|
|
||||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -226,22 +191,12 @@ js::CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<Globa
|
||||||
return nullptr;
|
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
|
// 8.1
|
||||||
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||||
if (!DefineProperty(cx, Intl, cx->names().DateTimeFormat, ctorValue, nullptr, nullptr, 0))
|
if (!DefineProperty(cx, Intl, cx->names().DateTimeFormat, ctorValue, nullptr, nullptr, 0))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
constructor.set(ctor);
|
||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -495,7 +450,7 @@ js::intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp)
|
||||||
* of the given DateTimeFormat.
|
* of the given DateTimeFormat.
|
||||||
*/
|
*/
|
||||||
static UDateFormat*
|
static UDateFormat*
|
||||||
NewUDateFormat(JSContext* cx, HandleObject dateTimeFormat)
|
NewUDateFormat(JSContext* cx, Handle<DateTimeFormatObject*> dateTimeFormat)
|
||||||
{
|
{
|
||||||
RootedValue value(cx);
|
RootedValue value(cx);
|
||||||
|
|
||||||
|
|
@ -779,42 +734,23 @@ js::intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp)
|
||||||
MOZ_ASSERT(args[1].isNumber());
|
MOZ_ASSERT(args[1].isNumber());
|
||||||
MOZ_ASSERT(args[2].isBoolean());
|
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.
|
// Obtain a cached UDateFormat object.
|
||||||
bool isDateTimeFormatInstance = dateTimeFormat->getClass() == &DateTimeFormatObject::class_;
|
|
||||||
UDateFormat* df;
|
|
||||||
if (isDateTimeFormatInstance) {
|
|
||||||
void* priv =
|
void* priv =
|
||||||
dateTimeFormat->as<NativeObject>().getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT).toPrivate();
|
dateTimeFormat->getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT).toPrivate();
|
||||||
df = static_cast<UDateFormat*>(priv);
|
UDateFormat* df = static_cast<UDateFormat*>(priv);
|
||||||
if (!df) {
|
if (!df) {
|
||||||
df = NewUDateFormat(cx, dateTimeFormat);
|
df = NewUDateFormat(cx, dateTimeFormat);
|
||||||
if (!df)
|
if (!df)
|
||||||
return false;
|
return false;
|
||||||
dateTimeFormat->as<NativeObject>().setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT, PrivateValue(df));
|
dateTimeFormat->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.
|
|
||||||
df = NewUDateFormat(cx, dateTimeFormat);
|
|
||||||
if (!df)
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the UDateFormat to actually format the time stamp.
|
// Use the UDateFormat to actually format the time stamp.
|
||||||
RootedValue result(cx);
|
return args[2].toBoolean()
|
||||||
bool success = args[2].toBoolean()
|
? intl_FormatToPartsDateTime(cx, df, args[1].toNumber(), args.rval())
|
||||||
? intl_FormatToPartsDateTime(cx, df, args[1].toNumber(), &result)
|
: intl_FormatDateTime(cx, df, args[1].toNumber(), args.rval());
|
||||||
: intl_FormatDateTime(cx, df, args[1].toNumber(), &result);
|
|
||||||
|
|
||||||
if (!isDateTimeFormatInstance)
|
|
||||||
udat_close(df);
|
|
||||||
if (!success)
|
|
||||||
return false;
|
|
||||||
args.rval().set(result);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ class DateTimeFormatObject : public NativeObject
|
||||||
extern JSObject*
|
extern JSObject*
|
||||||
|
|
||||||
CreateDateTimeFormatPrototype(JSContext* cx, JS::Handle<JSObject*> Intl,
|
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.
|
* 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.
|
* 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
|
extern MOZ_MUST_USE bool
|
||||||
intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp);
|
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|,
|
* Returns an object containing the DateTimeFormat internal properties of |obj|.
|
||||||
* or throws a TypeError if |obj| isn't DateTimeFormat-initialized.
|
|
||||||
*/
|
*/
|
||||||
function getDateTimeFormatInternals(obj, methodName) {
|
function getDateTimeFormatInternals(obj) {
|
||||||
var internals = getIntlObjectInternals(obj, "DateTimeFormat", methodName);
|
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");
|
assert(internals.type === "DateTimeFormat", "bad type escaped getIntlObjectInternals");
|
||||||
|
|
||||||
// If internal properties have already been computed, use them.
|
// If internal properties have already been computed, use them.
|
||||||
|
|
@ -214,6 +216,29 @@ function DefaultTimeZone() {
|
||||||
return 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.
|
* Initializes an object as a DateTimeFormat.
|
||||||
*
|
*
|
||||||
|
|
@ -225,15 +250,13 @@ function DefaultTimeZone() {
|
||||||
*
|
*
|
||||||
* Spec: ECMAScript Internationalization API Specification, 12.1.1.
|
* Spec: ECMAScript Internationalization API Specification, 12.1.1.
|
||||||
*/
|
*/
|
||||||
function InitializeDateTimeFormat(dateTimeFormat, locales, options) {
|
function InitializeDateTimeFormat(dateTimeFormat, thisValue, locales, options) {
|
||||||
assert(IsObject(dateTimeFormat), "InitializeDateTimeFormat");
|
assert(IsObject(dateTimeFormat), "InitializeDateTimeFormat called with non-Object");
|
||||||
|
assert(IsDateTimeFormat(dateTimeFormat),
|
||||||
|
"InitializeDateTimeFormat called with non-DateTimeFormat");
|
||||||
|
|
||||||
// Step 1.
|
// Steps 1-2 (These steps are no longer required and should be removed
|
||||||
if (isInitializedIntlObject(dateTimeFormat))
|
// from the spec; https://github.com/tc39/ecma402/issues/115).
|
||||||
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
|
|
||||||
|
|
||||||
// Step 2.
|
|
||||||
var internals = initializeIntlObject(dateTimeFormat);
|
|
||||||
|
|
||||||
// Lazy DateTimeFormat data has the following structure:
|
// 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
|
// We've done everything that must be done now: mark the lazy data as fully
|
||||||
// computed and install it.
|
// 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);
|
var x = (date === undefined) ? std_Date_now() : ToNumber(date);
|
||||||
|
|
||||||
// Step 1.a.iii.
|
// 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.
|
* Spec: ECMAScript Internationalization API Specification, 12.3.2.
|
||||||
*/
|
*/
|
||||||
function Intl_DateTimeFormat_format_get() {
|
function Intl_DateTimeFormat_format_get() {
|
||||||
// Check "this DateTimeFormat object" per introduction of section 12.3.
|
// Steps 1-3.
|
||||||
var internals = getDateTimeFormatInternals(this, "format");
|
var dtf = UnwrapDateTimeFormat(this, "format");
|
||||||
|
|
||||||
// Step 1.
|
var internals = getDateTimeFormatInternals(dtf);
|
||||||
|
|
||||||
|
// Step 4.
|
||||||
if (internals.boundFormat === undefined) {
|
if (internals.boundFormat === undefined) {
|
||||||
// Step 1.a.
|
// Step 4.a.
|
||||||
var F = dateTimeFormatFormatToBind;
|
var F = dateTimeFormatFormatToBind;
|
||||||
|
|
||||||
// Step 1.b-d.
|
// Steps 4.b-d.
|
||||||
var bf = callFunction(FunctionBind, F, this);
|
var bf = callFunction(FunctionBind, F, dtf);
|
||||||
internals.boundFormat = bf;
|
internals.boundFormat = bf;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2.
|
// Step 5.
|
||||||
return internals.boundFormat;
|
return internals.boundFormat;
|
||||||
}
|
}
|
||||||
|
_SetCanonicalName(Intl_DateTimeFormat_format_get, "get format");
|
||||||
|
|
||||||
|
|
||||||
function Intl_DateTimeFormat_formatToParts() {
|
function Intl_DateTimeFormat_formatToParts() {
|
||||||
// Check "this DateTimeFormat object" per introduction of section 12.3.
|
// Steps 1-3.
|
||||||
getDateTimeFormatInternals(this, "formatToParts");
|
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 date = arguments.length > 0 ? arguments[0] : undefined;
|
||||||
var x = (date === undefined) ? std_Date_now() : ToNumber(date);
|
var x = (date === undefined) ? std_Date_now() : ToNumber(date);
|
||||||
|
|
||||||
// Step 1.a.iii.
|
// Step 6.
|
||||||
return intl_FormatDateTime(this, x, true);
|
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.
|
* Spec: ECMAScript Internationalization API Specification, 12.3.3 and 12.4.
|
||||||
*/
|
*/
|
||||||
function Intl_DateTimeFormat_resolvedOptions() {
|
function Intl_DateTimeFormat_resolvedOptions() {
|
||||||
// Check "this DateTimeFormat object" per introduction of section 12.3.
|
// Invoke |UnwrapDateTimeFormat| per introduction of section 12.3.
|
||||||
var internals = getDateTimeFormatInternals(this, "resolvedOptions");
|
var dtf = UnwrapDateTimeFormat(this, "resolvedOptions");
|
||||||
|
|
||||||
|
var internals = getDateTimeFormatInternals(dtf);
|
||||||
|
|
||||||
var result = {
|
var result = {
|
||||||
locale: internals.locale,
|
locale: internals.locale,
|
||||||
|
|
|
||||||
|
|
@ -454,10 +454,12 @@ GlobalObject::initIntlObject(JSContext* cx, Handle<GlobalObject*> global)
|
||||||
RootedObject collatorProto(cx, CreateCollatorPrototype(cx, intl, global));
|
RootedObject collatorProto(cx, CreateCollatorPrototype(cx, intl, global));
|
||||||
if (!collatorProto)
|
if (!collatorProto)
|
||||||
return false;
|
return false;
|
||||||
RootedObject dateTimeFormatProto(cx, CreateDateTimeFormatPrototype(cx, intl, global));
|
RootedObject dateTimeFormatProto(cx), dateTimeFormat(cx);
|
||||||
|
dateTimeFormatProto = CreateDateTimeFormatPrototype(cx, intl, global, &dateTimeFormat);
|
||||||
if (!dateTimeFormatProto)
|
if (!dateTimeFormatProto)
|
||||||
return false;
|
return false;
|
||||||
RootedObject numberFormatProto(cx, CreateNumberFormatPrototype(cx, intl, global));
|
RootedObject numberFormatProto(cx), numberFormat(cx);
|
||||||
|
numberFormatProto = CreateNumberFormatPrototype(cx, intl, global, &numberFormat);
|
||||||
if (!numberFormatProto)
|
if (!numberFormatProto)
|
||||||
return false;
|
return false;
|
||||||
RootedObject pluralRulesProto(cx, CreatePluralRulesPrototype(cx, intl, global));
|
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
|
// |getPrototype(JSProto_*)|, but that has global-object-property-related
|
||||||
// baggage we don't need or want, so we use one-off reserved slots.
|
// baggage we don't need or want, so we use one-off reserved slots.
|
||||||
global->setReservedSlot(COLLATOR_PROTO, ObjectValue(*collatorProto));
|
global->setReservedSlot(COLLATOR_PROTO, ObjectValue(*collatorProto));
|
||||||
|
global->setReservedSlot(DATE_TIME_FORMAT, ObjectValue(*dateTimeFormat));
|
||||||
global->setReservedSlot(DATE_TIME_FORMAT_PROTO, ObjectValue(*dateTimeFormatProto));
|
global->setReservedSlot(DATE_TIME_FORMAT_PROTO, ObjectValue(*dateTimeFormatProto));
|
||||||
|
global->setReservedSlot(NUMBER_FORMAT, ObjectValue(*numberFormat));
|
||||||
global->setReservedSlot(NUMBER_FORMAT_PROTO, ObjectValue(*numberFormatProto));
|
global->setReservedSlot(NUMBER_FORMAT_PROTO, ObjectValue(*numberFormatProto));
|
||||||
global->setReservedSlot(PLURAL_RULES_PROTO, ObjectValue(*pluralRulesProto));
|
global->setReservedSlot(PLURAL_RULES_PROTO, ObjectValue(*pluralRulesProto));
|
||||||
global->setReservedSlot(RELATIVE_TIME_FORMAT_PROTO, ObjectValue(*relativeTimeFmtProto));
|
global->setReservedSlot(RELATIVE_TIME_FORMAT_PROTO, ObjectValue(*relativeTimeFmtProto));
|
||||||
|
|
|
||||||
|
|
@ -3,44 +3,79 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
function Intl_getCanonicalLocales(locales) {
|
function Intl_getCanonicalLocales(locales) {
|
||||||
let codes = CanonicalizeLocaleList(locales);
|
// Step 1.
|
||||||
let result = [];
|
var localeList = CanonicalizeLocaleList(locales);
|
||||||
|
|
||||||
let len = codes.length;
|
// Step 2 (Inlined CreateArrayFromList).
|
||||||
let k = 0;
|
var array = [];
|
||||||
|
|
||||||
while (k < len) {
|
for (var n = 0, len = localeList.length; n < len; n++)
|
||||||
_DefineDataProperty(result, k, codes[k]);
|
_DefineDataProperty(array, n, localeList[n]);
|
||||||
k++;
|
|
||||||
}
|
return array;
|
||||||
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.
|
||||||
|
*
|
||||||
|
* 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) {
|
function Intl_getCalendarInfo(locales) {
|
||||||
|
// 1. Let requestLocales be ? CanonicalizeLocaleList(locales).
|
||||||
const requestedLocales = CanonicalizeLocaleList(locales);
|
const requestedLocales = CanonicalizeLocaleList(locales);
|
||||||
|
|
||||||
const DateTimeFormat = dateTimeFormatInternalProperties;
|
const DateTimeFormat = dateTimeFormatInternalProperties;
|
||||||
|
|
||||||
|
// 2. Let localeData be %DateTimeFormat%.[[localeData]].
|
||||||
const localeData = DateTimeFormat.localeData;
|
const localeData = DateTimeFormat.localeData;
|
||||||
|
|
||||||
|
// 3. Let localeOpt be a new Record.
|
||||||
const localeOpt = new Record();
|
const localeOpt = new Record();
|
||||||
|
|
||||||
|
// 4. Set localeOpt.[[localeMatcher]] to "best fit".
|
||||||
localeOpt.localeMatcher = "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),
|
const r = ResolveLocale(callFunction(DateTimeFormat.availableLocales, DateTimeFormat),
|
||||||
requestedLocales,
|
requestedLocales,
|
||||||
localeOpt,
|
localeOpt,
|
||||||
DateTimeFormat.relevantExtensionKeys,
|
DateTimeFormat.relevantExtensionKeys,
|
||||||
localeData);
|
localeData);
|
||||||
|
|
||||||
|
// 6. Let result be GetCalendarInfo(r.[[locale]]).
|
||||||
const result = intl_GetCalendarInfo(r.locale);
|
const result = intl_GetCalendarInfo(r.locale);
|
||||||
result.calendar = r.ca;
|
_DefineDataProperty(result, "calendar", r.ca);
|
||||||
result.locale = r.locale;
|
_DefineDataProperty(result, "locale", r.locale);
|
||||||
|
|
||||||
|
// 7. Return result.
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is a custom method designed after Intl API, but currently
|
* This function is a custom function in the style of the standard Intl.*
|
||||||
* not part of the spec or spec proposal.
|
* functions, that isn't part of any spec or proposal yet.
|
||||||
* We want to use it internally to retrieve translated values from CLDR in
|
* We want to use it internally to retrieve translated values from CLDR in
|
||||||
* order to ensure they're aligned with what Intl API returns.
|
* 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]].
|
// 4. Let localeData be %DateTimeFormat%.[[localeData]].
|
||||||
const localeData = DateTimeFormat.localeData;
|
const localeData = DateTimeFormat.localeData;
|
||||||
|
|
||||||
// 5. Let opt be a new Record.
|
// 5. Let localeOpt be a new Record.
|
||||||
const localeOpt = new Record();
|
const localeOpt = new Record();
|
||||||
|
|
||||||
// 6. Set localeOpt.[[localeMatcher]] to "best fit".
|
// 6. Set localeOpt.[[localeMatcher]] to "best fit".
|
||||||
localeOpt.localeMatcher = "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").
|
// 8. Let style be ? GetOption(options, "style", "string", « "long", "short", "narrow" », "long").
|
||||||
const style = 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").
|
// 9. Let keys be ? Get(options, "keys").
|
||||||
let keys = 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
|
// |intl_ComputeDisplayNames| may infallibly access the list's length via
|
||||||
// |ArrayObject::length|.)
|
// |ArrayObject::length|.)
|
||||||
let processedKeys = [];
|
let processedKeys = [];
|
||||||
|
|
||||||
// 13. Let len be ? ToLength(? Get(keys, "length")).
|
// 13. Let len be ? ToLength(? Get(keys, "length")).
|
||||||
let len = ToLength(keys.length);
|
let len = ToLength(keys.length);
|
||||||
|
|
||||||
// 14. Let i be 0.
|
// 14. Let i be 0.
|
||||||
// 15. Repeat, while i < len
|
// 15. Repeat, while i < len
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
|
|
|
||||||
|
|
@ -92,35 +92,8 @@ static const JSFunctionSpec numberFormat_methods[] = {
|
||||||
static bool
|
static bool
|
||||||
NumberFormat(JSContext* cx, const CallArgs& args, bool construct)
|
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).
|
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||||
RootedObject proto(cx);
|
RootedObject proto(cx);
|
||||||
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||||
|
|
@ -132,23 +105,21 @@ NumberFormat(JSContext* cx, const CallArgs& args, bool construct)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = NewObjectWithGivenProto<NumberFormatObject>(cx, proto);
|
Rooted<NumberFormatObject*> numberFormat(cx);
|
||||||
if (!obj)
|
numberFormat = NewObjectWithGivenProto<NumberFormatObject>(cx, proto);
|
||||||
|
if (!numberFormat)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
obj->as<NativeObject>().setReservedSlot(NumberFormatObject::INTERNALS_SLOT, NullValue());
|
numberFormat->setReservedSlot(NumberFormatObject::INTERNALS_SLOT, NullValue());
|
||||||
obj->as<NativeObject>().setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
|
numberFormat->setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
|
||||||
}
|
|
||||||
|
|
||||||
RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
|
RootedValue thisValue(cx, construct ? ObjectValue(*numberFormat) : args.thisv());
|
||||||
RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
|
RootedValue locales(cx, args.get(0));
|
||||||
|
RootedValue options(cx, args.get(1));
|
||||||
|
|
||||||
// Step 3.
|
// Step 3.
|
||||||
if (!intl::InitializeObject(cx, obj, cx->names().InitializeNumberFormat, locales, options))
|
return intl::LegacyIntlInitialize(cx, numberFormat, cx->names().InitializeNumberFormat, thisValue,
|
||||||
return false;
|
locales, options, args.rval());
|
||||||
|
|
||||||
args.rval().setObject(*obj);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -175,30 +146,23 @@ js::NumberFormatObject::finalize(FreeOp* fop, JSObject* obj)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(fop->onMainThread());
|
MOZ_ASSERT(fop->onMainThread());
|
||||||
|
|
||||||
// This is-undefined check shouldn't be necessary, but for internal
|
const Value& slot = obj->as<NumberFormatObject>().getReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT);
|
||||||
// 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()))
|
if (UNumberFormat* nf = static_cast<UNumberFormat*>(slot.toPrivate()))
|
||||||
unum_close(nf);
|
unum_close(nf);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObject*
|
JSObject*
|
||||||
js::CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
|
js::CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global,
|
||||||
|
MutableHandleObject constructor)
|
||||||
{
|
{
|
||||||
RootedFunction ctor(cx);
|
RootedFunction ctor(cx);
|
||||||
ctor = GlobalObject::createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0);
|
ctor = GlobalObject::createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0);
|
||||||
if (!ctor)
|
if (!ctor)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global,
|
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
|
||||||
&NumberFormatObject::class_));
|
|
||||||
if (!proto)
|
if (!proto)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
proto->setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
|
|
||||||
|
|
||||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -229,22 +193,12 @@ js::CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalO
|
||||||
return nullptr;
|
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
|
// 8.1
|
||||||
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||||
if (!DefineProperty(cx, Intl, cx->names().NumberFormat, ctorValue, nullptr, nullptr, 0))
|
if (!DefineProperty(cx, Intl, cx->names().NumberFormat, ctorValue, nullptr, nullptr, 0))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
constructor.set(ctor);
|
||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -295,7 +249,7 @@ js::intl_numberingSystem(JSContext* cx, unsigned argc, Value* vp)
|
||||||
* of the given NumberFormat.
|
* of the given NumberFormat.
|
||||||
*/
|
*/
|
||||||
static UNumberFormat*
|
static UNumberFormat*
|
||||||
NewUNumberFormat(JSContext* cx, HandleObject numberFormat)
|
NewUNumberFormat(JSContext* cx, Handle<NumberFormatObject*> numberFormat)
|
||||||
{
|
{
|
||||||
RootedValue value(cx);
|
RootedValue value(cx);
|
||||||
|
|
||||||
|
|
@ -854,50 +808,23 @@ js::intl_FormatNumber(JSContext* cx, unsigned argc, Value* vp)
|
||||||
MOZ_ASSERT(args[1].isNumber());
|
MOZ_ASSERT(args[1].isNumber());
|
||||||
MOZ_ASSERT(args[2].isBoolean());
|
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.
|
// Obtain a cached UNumberFormat object.
|
||||||
bool isNumberFormatInstance = numberFormat->getClass() == &NumberFormatObject::class_;
|
|
||||||
UNumberFormat* nf;
|
|
||||||
if (isNumberFormatInstance) {
|
|
||||||
void* priv =
|
void* priv =
|
||||||
numberFormat->as<NativeObject>().getReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT).toPrivate();
|
numberFormat->getReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT).toPrivate();
|
||||||
nf = static_cast<UNumberFormat*>(priv);
|
UNumberFormat* nf = static_cast<UNumberFormat*>(priv);
|
||||||
if (!nf) {
|
if (!nf) {
|
||||||
nf = NewUNumberFormat(cx, numberFormat);
|
nf = NewUNumberFormat(cx, numberFormat);
|
||||||
if (!nf)
|
if (!nf)
|
||||||
return false;
|
return false;
|
||||||
numberFormat->as<NativeObject>().setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nf));
|
numberFormat->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.
|
|
||||||
nf = NewUNumberFormat(cx, numberFormat);
|
|
||||||
if (!nf)
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the UNumberFormat to actually format the number.
|
// Use the UNumberFormat to actually format the number.
|
||||||
double d = args[1].toNumber();
|
|
||||||
RootedValue result(cx);
|
|
||||||
|
|
||||||
bool success;
|
|
||||||
if (args[2].toBoolean()) {
|
if (args[2].toBoolean()) {
|
||||||
success = intl_FormatNumberToParts(cx, nf, d, &result);
|
return intl_FormatNumberToParts(cx, nf, args[1].toNumber(), args.rval());
|
||||||
} 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_FormatNumber(cx, nf, args[1].toNumber(), args.rval());
|
||||||
if (!isNumberFormatInstance)
|
|
||||||
unum_close(nf);
|
|
||||||
if (!success)
|
|
||||||
return false;
|
|
||||||
args.rval().set(result);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,8 @@ class NumberFormatObject : public NativeObject
|
||||||
};
|
};
|
||||||
|
|
||||||
extern JSObject*
|
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.
|
* 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.
|
* 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
|
extern MOZ_MUST_USE bool
|
||||||
intl_FormatNumber(JSContext* cx, unsigned argc, Value* vp);
|
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|,
|
* Returns an object containing the NumberFormat internal properties of |obj|.
|
||||||
* or throws a TypeError if |obj| isn't NumberFormat-initialized.
|
|
||||||
*/
|
*/
|
||||||
function getNumberFormatInternals(obj, methodName) {
|
function getNumberFormatInternals(obj) {
|
||||||
var internals = getIntlObjectInternals(obj, "NumberFormat", methodName);
|
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");
|
assert(internals.type === "NumberFormat", "bad type escaped getIntlObjectInternals");
|
||||||
|
|
||||||
// If internal properties have already been computed, use them.
|
// If internal properties have already been computed, use them.
|
||||||
|
|
@ -114,6 +116,25 @@ function getNumberFormatInternals(obj, methodName) {
|
||||||
return internalProps;
|
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.
|
* 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.
|
* Spec: ECMAScript Internationalization API Specification, 11.1.1.
|
||||||
*/
|
*/
|
||||||
function InitializeNumberFormat(numberFormat, locales, options) {
|
function InitializeNumberFormat(numberFormat, thisValue, locales, options) {
|
||||||
assert(IsObject(numberFormat), "InitializeNumberFormat");
|
assert(IsObject(numberFormat), "InitializeNumberFormat called with non-object");
|
||||||
|
assert(IsNumberFormat(numberFormat), "InitializeNumberFormat called with non-NumberFormat");
|
||||||
|
|
||||||
// Step 1.
|
// Steps 1-2 (These steps are no longer required and should be removed
|
||||||
if (isInitializedIntlObject(numberFormat))
|
// from the spec; https://github.com/tc39/ecma402/issues/115).
|
||||||
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
|
|
||||||
|
|
||||||
// Step 2.
|
|
||||||
var internals = initializeIntlObject(numberFormat);
|
|
||||||
|
|
||||||
// Lazy NumberFormat data has the following structure:
|
// 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
|
// We've done everything that must be done now: mark the lazy data as fully
|
||||||
// computed and install it.
|
// 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.
|
* Spec: ECMAScript Internationalization API Specification, 11.3.2.
|
||||||
*/
|
*/
|
||||||
function Intl_NumberFormat_format_get() {
|
function Intl_NumberFormat_format_get() {
|
||||||
// Check "this NumberFormat object" per introduction of section 11.3.
|
// Steps 1-3.
|
||||||
var internals = getNumberFormatInternals(this, "format");
|
var nf = UnwrapNumberFormat(this, "format");
|
||||||
|
|
||||||
// Step 1.
|
var internals = getNumberFormatInternals(nf);
|
||||||
|
|
||||||
|
// Step 4.
|
||||||
if (internals.boundFormat === undefined) {
|
if (internals.boundFormat === undefined) {
|
||||||
// Step 1.a.
|
// Step 4.a.
|
||||||
var F = numberFormatFormatToBind;
|
var F = numberFormatFormatToBind;
|
||||||
|
|
||||||
// Step 1.b-d.
|
// Steps 4.b-d.
|
||||||
var bf = callFunction(FunctionBind, F, this);
|
var bf = callFunction(FunctionBind, F, nf);
|
||||||
internals.boundFormat = bf;
|
internals.boundFormat = bf;
|
||||||
}
|
}
|
||||||
// Step 2.
|
|
||||||
|
// Step 5.
|
||||||
return internals.boundFormat;
|
return internals.boundFormat;
|
||||||
}
|
}
|
||||||
|
_SetCanonicalName(Intl_NumberFormat_format_get, "get format");
|
||||||
|
|
||||||
function Intl_NumberFormat_formatToParts(value) {
|
function Intl_NumberFormat_formatToParts(value) {
|
||||||
// Step 1.
|
// Steps 1-3.
|
||||||
var nf = this;
|
var nf = UnwrapNumberFormat(this, "formatToParts");
|
||||||
|
|
||||||
// Steps 2-3.
|
// Ensure the NumberFormat internals are resolved.
|
||||||
getNumberFormatInternals(nf, "formatToParts");
|
getNumberFormatInternals(nf);
|
||||||
|
|
||||||
// Step 4.
|
// Step 4.
|
||||||
var x = ToNumber(value);
|
var x = ToNumber(value);
|
||||||
|
|
@ -482,8 +516,10 @@ function Intl_NumberFormat_formatToParts(value) {
|
||||||
* Spec: ECMAScript Internationalization API Specification, 11.3.3 and 11.4.
|
* Spec: ECMAScript Internationalization API Specification, 11.3.3 and 11.4.
|
||||||
*/
|
*/
|
||||||
function Intl_NumberFormat_resolvedOptions() {
|
function Intl_NumberFormat_resolvedOptions() {
|
||||||
// Check "this NumberFormat object" per introduction of section 11.3.
|
// Invoke |UnwrapNumberFormat| per introduction of section 11.3.
|
||||||
var internals = getNumberFormatInternals(this, "resolvedOptions");
|
var nf = UnwrapNumberFormat(this, "resolvedOptions");
|
||||||
|
|
||||||
|
var internals = getNumberFormatInternals(nf);
|
||||||
|
|
||||||
var result = {
|
var result = {
|
||||||
locale: internals.locale,
|
locale: internals.locale,
|
||||||
|
|
|
||||||
|
|
@ -113,8 +113,8 @@ PluralRules(JSContext* cx, const CallArgs& args, bool construct)
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
obj->as<NativeObject>().setReservedSlot(PluralRulesObject::INTERNALS_SLOT, NullValue());
|
obj->as<PluralRulesObject>().setReservedSlot(PluralRulesObject::INTERNALS_SLOT, NullValue());
|
||||||
obj->as<NativeObject>().setReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT, PrivateValue(nullptr));
|
obj->as<PluralRulesObject>().setReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT, PrivateValue(nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
RootedValue locales(cx, args.get(0));
|
RootedValue locales(cx, args.get(0));
|
||||||
|
|
@ -147,15 +147,9 @@ js::PluralRulesObject::finalize(FreeOp* fop, JSObject* obj)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(fop->onMainThread());
|
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);
|
const Value& slot = obj->as<PluralRulesObject>().getReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT);
|
||||||
if (!slot.isUndefined()) {
|
|
||||||
if (UPluralRules* pr = static_cast<UPluralRules*>(slot.toPrivate()))
|
if (UPluralRules* pr = static_cast<UPluralRules*>(slot.toPrivate()))
|
||||||
uplrules_close(pr);
|
uplrules_close(pr);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObject*
|
JSObject*
|
||||||
|
|
@ -166,10 +160,9 @@ js::CreatePluralRulesPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalOb
|
||||||
if (!ctor)
|
if (!ctor)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &PluralRulesObject::class_));
|
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
|
||||||
if (!proto)
|
if (!proto)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
proto->setReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT, PrivateValue(nullptr));
|
|
||||||
|
|
||||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -180,16 +173,6 @@ js::CreatePluralRulesPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalOb
|
||||||
if (!JS_DefineFunctions(cx, proto, pluralRules_methods))
|
if (!JS_DefineFunctions(cx, proto, pluralRules_methods))
|
||||||
return nullptr;
|
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));
|
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||||
if (!DefineProperty(cx, Intl, cx->names().PluralRules, ctorValue, nullptr, nullptr, 0))
|
if (!DefineProperty(cx, Intl, cx->names().PluralRules, ctorValue, nullptr, nullptr, 0))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -222,7 +205,7 @@ js::intl_PluralRules_availableLocales(JSContext* cx, unsigned argc, Value* vp)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static UNumberFormat*
|
static UNumberFormat*
|
||||||
NewUNumberFormatForPluralRules(JSContext* cx, HandleObject pluralRules)
|
NewUNumberFormatForPluralRules(JSContext* cx, Handle<PluralRulesObject*> pluralRules)
|
||||||
{
|
{
|
||||||
RootedObject internals(cx, intl::GetInternalsObject(cx, pluralRules));
|
RootedObject internals(cx, intl::GetInternalsObject(cx, pluralRules));
|
||||||
if (!internals)
|
if (!internals)
|
||||||
|
|
@ -299,7 +282,7 @@ js::intl_SelectPluralRule(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
CallArgs args = CallArgsFromVp(argc, 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);
|
UNumberFormat* nf = NewUNumberFormatForPluralRules(cx, pluralRules);
|
||||||
if (!nf)
|
if (!nf)
|
||||||
|
|
|
||||||
|
|
@ -63,11 +63,13 @@ function resolvePluralRulesInternals(lazyPluralRulesData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an object containing the PluralRules internal properties of |obj|,
|
* Returns an object containing the PluralRules internal properties of |obj|.
|
||||||
* or throws a TypeError if |obj| isn't PluralRules-initialized.
|
|
||||||
*/
|
*/
|
||||||
function getPluralRulesInternals(obj, methodName) {
|
function getPluralRulesInternals(obj) {
|
||||||
var internals = getIntlObjectInternals(obj, "PluralRules", methodName);
|
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");
|
assert(internals.type === "PluralRules", "bad type escaped getIntlObjectInternals");
|
||||||
|
|
||||||
var internalProps = maybeInternalProperties(internals);
|
var internalProps = maybeInternalProperties(internals);
|
||||||
|
|
@ -91,13 +93,11 @@ function getPluralRulesInternals(obj, methodName) {
|
||||||
* Spec: ECMAScript 402 API, PluralRules, 1.1.1.
|
* Spec: ECMAScript 402 API, PluralRules, 1.1.1.
|
||||||
*/
|
*/
|
||||||
function InitializePluralRules(pluralRules, locales, options) {
|
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.
|
// Steps 1-2 (These steps are no longer required and should be removed
|
||||||
if (isInitializedIntlObject(pluralRules))
|
// from the spec; https://github.com/tc39/ecma402/issues/115).
|
||||||
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
|
|
||||||
|
|
||||||
let internals = initializeIntlObject(pluralRules);
|
|
||||||
|
|
||||||
// Lazy PluralRules data has the following structure:
|
// Lazy PluralRules data has the following structure:
|
||||||
//
|
//
|
||||||
|
|
@ -156,7 +156,7 @@ function InitializePluralRules(pluralRules, locales, options) {
|
||||||
std_Math_max(lazyPluralRulesData.minimumFractionDigits, 3);
|
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) {
|
function Intl_PluralRules_select(value) {
|
||||||
// Step 1.
|
// Step 1.
|
||||||
let pluralRules = this;
|
let pluralRules = this;
|
||||||
|
|
||||||
// Step 2.
|
// 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.
|
// Steps 3-4.
|
||||||
let n = ToNumber(value);
|
let n = ToNumber(value);
|
||||||
|
|
@ -205,7 +210,13 @@ function Intl_PluralRules_select(value) {
|
||||||
* Spec: ECMAScript 402 API, PluralRules, 1.4.4.
|
* Spec: ECMAScript 402 API, PluralRules, 1.4.4.
|
||||||
*/
|
*/
|
||||||
function Intl_PluralRules_resolvedOptions() {
|
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 = {
|
var result = {
|
||||||
locale: internals.locale,
|
locale: internals.locale,
|
||||||
|
|
|
||||||
|
|
@ -108,8 +108,8 @@ RelativeTimeFormat(JSContext* cx, unsigned argc, Value* vp)
|
||||||
if (!relativeTimeFormat)
|
if (!relativeTimeFormat)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
relativeTimeFormat->as<NativeObject>().setReservedSlot(RelativeTimeFormatObject::INTERNALS_SLOT, NullValue());
|
relativeTimeFormat->as<RelativeTimeFormatObject>().setReservedSlot(RelativeTimeFormatObject::INTERNALS_SLOT, NullValue());
|
||||||
relativeTimeFormat->as<NativeObject>().setReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
|
relativeTimeFormat->as<RelativeTimeFormatObject>().setReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
|
||||||
|
|
||||||
RootedValue locales(cx, args.get(0));
|
RootedValue locales(cx, args.get(0));
|
||||||
RootedValue options(cx, args.get(1));
|
RootedValue options(cx, args.get(1));
|
||||||
|
|
@ -127,15 +127,9 @@ js::RelativeTimeFormatObject::finalize(FreeOp* fop, JSObject* obj)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(fop->onMainThread());
|
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);
|
const Value& slot = obj->as<RelativeTimeFormatObject>().getReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT);
|
||||||
if (!slot.isUndefined()) {
|
|
||||||
if (URelativeDateTimeFormatter* rtf = static_cast<URelativeDateTimeFormatter*>(slot.toPrivate()))
|
if (URelativeDateTimeFormatter* rtf = static_cast<URelativeDateTimeFormatter*>(slot.toPrivate()))
|
||||||
ureldatefmt_close(rtf);
|
ureldatefmt_close(rtf);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObject*
|
JSObject*
|
||||||
|
|
@ -146,10 +140,9 @@ js::CreateRelativeTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<G
|
||||||
if (!ctor)
|
if (!ctor)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &RelativeTimeFormatObject::class_));
|
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
|
||||||
if (!proto)
|
if (!proto)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
proto->setReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
|
|
||||||
|
|
||||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -163,16 +156,6 @@ js::CreateRelativeTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<G
|
||||||
if (!JS_DefineProperties(cx, proto, relativeTimeFormat_properties))
|
if (!JS_DefineProperties(cx, proto, relativeTimeFormat_properties))
|
||||||
return nullptr;
|
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));
|
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||||
if (!DefineProperty(cx, Intl, cx->names().RelativeTimeFormat, ctorValue, nullptr, nullptr, 0)) {
|
if (!DefineProperty(cx, Intl, cx->names().RelativeTimeFormat, ctorValue, nullptr, nullptr, 0)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,11 @@ function resolveRelativeTimeFormatInternals(lazyRelativeTimeFormatData) {
|
||||||
* Returns an object containing the RelativeTimeFormat internal properties of |obj|,
|
* Returns an object containing the RelativeTimeFormat internal properties of |obj|,
|
||||||
* or throws a TypeError if |obj| isn't RelativeTimeFormat-initialized.
|
* or throws a TypeError if |obj| isn't RelativeTimeFormat-initialized.
|
||||||
*/
|
*/
|
||||||
function getRelativeTimeFormatInternals(obj, methodName) {
|
function getRelativeTimeFormatInternals(obj) {
|
||||||
var internals = getIntlObjectInternals(obj, "RelativeTimeFormat", methodName);
|
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");
|
assert(internals.type === "RelativeTimeFormat", "bad type escaped getIntlObjectInternals");
|
||||||
|
|
||||||
var internalProps = maybeInternalProperties(internals);
|
var internalProps = maybeInternalProperties(internals);
|
||||||
|
|
@ -94,12 +97,8 @@ function getRelativeTimeFormatInternals(obj, methodName) {
|
||||||
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.1.1.
|
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.1.1.
|
||||||
*/
|
*/
|
||||||
function InitializeRelativeTimeFormat(relativeTimeFormat, locales, options) {
|
function InitializeRelativeTimeFormat(relativeTimeFormat, locales, options) {
|
||||||
assert(IsObject(relativeTimeFormat), "InitializeRelativeTimeFormat");
|
assert(IsObject(relativeTimeFormat), "InitializeRelativeTimeFormat called with non-object");
|
||||||
|
assert(IsRelativeTimeFormat(relativeTimeFormat), "InitializeRelativeTimeFormat called with non-RelativeTimeFormat");
|
||||||
if (isInitializedIntlObject(relativeTimeFormat))
|
|
||||||
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
|
|
||||||
|
|
||||||
let internals = initializeIntlObject(relativeTimeFormat);
|
|
||||||
|
|
||||||
// Lazy RelativeTimeFormat data has the following structure:
|
// 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");
|
const numeric = GetOption(options, "numeric", "string", ["always", "auto"], "always");
|
||||||
lazyRelativeTimeFormatData.numeric = numeric;
|
lazyRelativeTimeFormatData.numeric = numeric;
|
||||||
|
|
||||||
setLazyData(internals, "RelativeTimeFormat", lazyRelativeTimeFormatData)
|
initializeIntlObject(relativeTimeFormat, "RelativeTimeFormat", lazyRelativeTimeFormatData)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -181,7 +180,11 @@ function Intl_RelativeTimeFormat_format(value, unit) {
|
||||||
let relativeTimeFormat = this;
|
let relativeTimeFormat = this;
|
||||||
|
|
||||||
// Step 2.
|
// 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.
|
// Step 3.
|
||||||
let t = ToNumber(value);
|
let t = ToNumber(value);
|
||||||
|
|
@ -227,7 +230,13 @@ function Intl_RelativeTimeFormat_format(value, unit) {
|
||||||
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.4.4.
|
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.4.4.
|
||||||
*/
|
*/
|
||||||
function Intl_RelativeTimeFormat_resolvedOptions() {
|
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.
|
// Steps 4-5.
|
||||||
var result = {
|
var result = {
|
||||||
|
|
|
||||||
|
|
@ -13,5 +13,5 @@ evalcx(`
|
||||||
');
|
');
|
||||||
oomTest(() => eval('Array(..."")'));
|
oomTest(() => eval('Array(..."")'));
|
||||||
if ('Intl' in this)
|
if ('Intl' in this)
|
||||||
Intl.NumberFormat.prototype.format(0);
|
new Intl.NumberFormat().format(0);
|
||||||
`, newGlobal());
|
`, newGlobal());
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,8 @@ load(libdir + "asserts.js");
|
||||||
// any value except "best fit" or "lookup" is okay.
|
// any value except "best fit" or "lookup" is okay.
|
||||||
Object.prototype.localeMatcher = "invalid matcher option";
|
Object.prototype.localeMatcher = "invalid matcher option";
|
||||||
|
|
||||||
// The Intl API may not be available in the testing environment. Note that |hasOwnProperty("Intl")|
|
// The Intl API may not be available in the testing environment.
|
||||||
// initializes the Intl API if present, so this if-statement needs to appear after "localeMatcher"
|
|
||||||
// was added to Object.prototype.
|
|
||||||
if (this.hasOwnProperty("Intl")) {
|
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
|
// Intl constructors no longer work properly, because "localeMatcher" defaults to the invalid
|
||||||
// value from Object.prototype. Except for Intl.DateTimeFormat, cf. ECMA-402 ToDateTimeOptions.
|
// value from Object.prototype. Except for Intl.DateTimeFormat, cf. ECMA-402 ToDateTimeOptions.
|
||||||
assertThrowsInstanceOf(() => new Intl.Collator(), RangeError);
|
assertThrowsInstanceOf(() => new Intl.Collator(), RangeError);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ for (var i = 0; i < 3; i++) {
|
||||||
x.toString();
|
x.toString();
|
||||||
assertEq(0, 1);
|
assertEq(0, 1);
|
||||||
} catch (e) {
|
} 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) \
|
_(AtomicsXor) \
|
||||||
_(AtomicsIsLockFree) \
|
_(AtomicsIsLockFree) \
|
||||||
\
|
\
|
||||||
|
_(IntlIsCollator) \
|
||||||
|
_(IntlIsDateTimeFormat) \
|
||||||
|
_(IntlIsNumberFormat) \
|
||||||
|
_(IntlIsPluralRules) \
|
||||||
|
_(IntlIsRelativeTimeFormat) \
|
||||||
|
\
|
||||||
_(MathAbs) \
|
_(MathAbs) \
|
||||||
_(MathFloor) \
|
_(MathFloor) \
|
||||||
_(MathCeil) \
|
_(MathCeil) \
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,11 @@
|
||||||
#include "builtin/SIMD.h"
|
#include "builtin/SIMD.h"
|
||||||
#include "builtin/TestingFunctions.h"
|
#include "builtin/TestingFunctions.h"
|
||||||
#include "builtin/TypedObject.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/BaselineInspector.h"
|
||||||
#include "jit/InlinableNatives.h"
|
#include "jit/InlinableNatives.h"
|
||||||
#include "jit/IonBuilder.h"
|
#include "jit/IonBuilder.h"
|
||||||
|
|
@ -104,6 +109,18 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
|
||||||
case InlinableNative::AtomicsIsLockFree:
|
case InlinableNative::AtomicsIsLockFree:
|
||||||
return inlineAtomicsIsLockFree(callInfo);
|
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.
|
// Math natives.
|
||||||
case InlinableNative::MathAbs:
|
case InlinableNative::MathAbs:
|
||||||
return inlineMathAbs(callInfo);
|
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_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_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_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_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_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")
|
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_TypedArray) &&
|
||||||
InitBareBuiltinCtor(cx, global, JSProto_Uint8Array) &&
|
InitBareBuiltinCtor(cx, global, JSProto_Uint8Array) &&
|
||||||
InitBareBuiltinCtor(cx, global, JSProto_Int32Array) &&
|
InitBareBuiltinCtor(cx, global, JSProto_Int32Array) &&
|
||||||
|
InitBareSymbolCtor(cx, global) &&
|
||||||
InitBareWeakMapCtor(cx, global) &&
|
InitBareWeakMapCtor(cx, global) &&
|
||||||
InitStopIterationClass(cx, global) &&
|
InitStopIterationClass(cx, global) &&
|
||||||
DefineFunctions(cx, global, builtins, AsIntrinsic);
|
DefineFunctions(cx, global, builtins, AsIntrinsic);
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,9 @@ class GlobalObject : public NativeObject
|
||||||
MAP_ITERATOR_PROTO,
|
MAP_ITERATOR_PROTO,
|
||||||
SET_ITERATOR_PROTO,
|
SET_ITERATOR_PROTO,
|
||||||
COLLATOR_PROTO,
|
COLLATOR_PROTO,
|
||||||
|
NUMBER_FORMAT,
|
||||||
NUMBER_FORMAT_PROTO,
|
NUMBER_FORMAT_PROTO,
|
||||||
|
DATE_TIME_FORMAT,
|
||||||
DATE_TIME_FORMAT_PROTO,
|
DATE_TIME_FORMAT_PROTO,
|
||||||
PLURAL_RULES_PROTO,
|
PLURAL_RULES_PROTO,
|
||||||
RELATIVE_TIME_FORMAT_PROTO,
|
RELATIVE_TIME_FORMAT_PROTO,
|
||||||
|
|
@ -499,11 +501,23 @@ class GlobalObject : public NativeObject
|
||||||
return getOrCreateObject(cx, global, COLLATOR_PROTO, initIntlObject);
|
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*
|
static JSObject*
|
||||||
getOrCreateNumberFormatPrototype(JSContext* cx, Handle<GlobalObject*> global) {
|
getOrCreateNumberFormatPrototype(JSContext* cx, Handle<GlobalObject*> global) {
|
||||||
return getOrCreateObject(cx, global, NUMBER_FORMAT_PROTO, initIntlObject);
|
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*
|
static JSObject*
|
||||||
getOrCreateDateTimeFormatPrototype(JSContext* cx, Handle<GlobalObject*> global) {
|
getOrCreateDateTimeFormatPrototype(JSContext* cx, Handle<GlobalObject*> global) {
|
||||||
return getOrCreateObject(cx, global, DATE_TIME_FORMAT_PROTO, initIntlObject);
|
return getOrCreateObject(cx, global, DATE_TIME_FORMAT_PROTO, initIntlObject);
|
||||||
|
|
|
||||||
|
|
@ -1905,6 +1905,23 @@ intrinsic_RuntimeDefaultLocale(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return true;
|
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
|
static bool
|
||||||
intrinsic_ConstructFunction(JSContext* cx, unsigned argc, Value* vp)
|
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_RelativeTimeFormat_availableLocales", intl_RelativeTimeFormat_availableLocales, 0,0),
|
||||||
JS_FN("intl_FormatRelativeTime", intl_FormatRelativeTime, 3,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",
|
JS_INLINABLE_FN("IsRegExpObject",
|
||||||
intrinsic_IsInstanceOfBuiltin<RegExpObject>, 1,0,
|
intrinsic_IsInstanceOfBuiltin<RegExpObject>, 1,0,
|
||||||
IsRegExpObject),
|
IsRegExpObject),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue