mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-04 23:08:39 +09:00
Bug 1379222 - Avoid [[Get]] for "prototype" property when calling builtin constructors. r=jandem
This commit is contained in:
parent
c324e4df46
commit
35ca1152a7
21 changed files with 2921 additions and 2942 deletions
|
|
@ -590,8 +590,7 @@ MapObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
|||
return false;
|
||||
|
||||
RootedObject proto(cx);
|
||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
Rooted<MapObject*> obj(cx, MapObject::create(cx, proto));
|
||||
|
|
@ -1196,8 +1195,7 @@ SetObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
|||
return false;
|
||||
|
||||
RootedObject proto(cx);
|
||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
Rooted<SetObject*> obj(cx, SetObject::create(cx, proto));
|
||||
|
|
|
|||
|
|
@ -2009,7 +2009,6 @@ PromiseConstructor(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
// Steps 3-10.
|
||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||
RootedObject originalNewTarget(cx, newTarget);
|
||||
bool needsWrapping = false;
|
||||
|
||||
// If the constructor is called via an Xray wrapper, then the newTarget
|
||||
|
|
@ -2061,10 +2060,15 @@ PromiseConstructor(JSContext* cx, unsigned argc, Value* vp)
|
|||
}
|
||||
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeFromConstructor(cx, needsWrapping ? newTarget : originalNewTarget, &proto))
|
||||
return false;
|
||||
if (needsWrapping && !cx->compartment()->wrap(cx, &proto))
|
||||
return false;
|
||||
if (needsWrapping) {
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||
return false;
|
||||
if (!cx->compartment()->wrap(cx, &proto))
|
||||
return false;
|
||||
} else {
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
}
|
||||
Rooted<PromiseObject*> promise(cx, PromiseObject::create(cx, executor, proto, needsWrapping));
|
||||
if (!promise)
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -530,7 +530,7 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp)
|
|||
return false;
|
||||
|
||||
// We can delay step 3 and step 4a until later, during
|
||||
// GetPrototypeFromCallableConstructor calls. Accessing the new.target
|
||||
// GetPrototypeFromBuiltinConstructor calls. Accessing the new.target
|
||||
// and the callee from the stack is unobservable.
|
||||
if (!args.isConstructing()) {
|
||||
// Step 3.b.
|
||||
|
|
@ -578,7 +578,7 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
// Step 7.
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, proto));
|
||||
|
|
@ -637,7 +637,7 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
// Step 7.
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, proto));
|
||||
|
|
|
|||
|
|
@ -294,8 +294,11 @@ WeakMap_construct(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!ThrowIfNotConstructing(cx, args, "WeakMap"))
|
||||
return false;
|
||||
|
||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||
RootedObject obj(cx, CreateThis(cx, &WeakMapObject::class_, newTarget));
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
RootedObject obj(cx, NewObjectWithClassProto<WeakMapObject>(cx, proto));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -92,8 +92,7 @@ WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
|||
return false;
|
||||
|
||||
RootedObject proto(cx);
|
||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
Rooted<WeakSetObject*> obj(cx, WeakSetObject::create(cx, proto));
|
||||
|
|
|
|||
|
|
@ -1,472 +1,472 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Intl.Collator implementation. */
|
||||
|
||||
#include "builtin/intl/Collator.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Span.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "builtin/intl/CommonFunctions.h"
|
||||
#include "builtin/intl/ICUHeader.h"
|
||||
#include "builtin/intl/LanguageTag.h"
|
||||
#include "builtin/intl/ScopedICUObject.h"
|
||||
#include "builtin/intl/SharedIntlData.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/Runtime.h"
|
||||
#include "vm/String.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using namespace js;
|
||||
using js::intl::IcuLocale;
|
||||
using js::intl::ReportInternalError;
|
||||
using js::intl::SharedIntlData;
|
||||
using js::intl::StringsAreEqual;
|
||||
|
||||
/******************** Collator ********************/
|
||||
|
||||
const ClassOps CollatorObject::classOps_ = {
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
nullptr, /* getProperty */
|
||||
nullptr, /* setProperty */
|
||||
nullptr, /* enumerate */
|
||||
nullptr, /* resolve */
|
||||
nullptr, /* mayResolve */
|
||||
CollatorObject::finalize
|
||||
};
|
||||
|
||||
const Class CollatorObject::class_ = {
|
||||
js_Object_str,
|
||||
JSCLASS_HAS_RESERVED_SLOTS(CollatorObject::SLOT_COUNT) |
|
||||
JSCLASS_FOREGROUND_FINALIZE,
|
||||
&CollatorObject::classOps_
|
||||
};
|
||||
|
||||
#if JS_HAS_TOSOURCE
|
||||
static bool
|
||||
collator_toSource(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setString(cx->names().Collator);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const JSFunctionSpec collator_static_methods[] = {
|
||||
JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_Collator_supportedLocalesOf", 1, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static const JSFunctionSpec collator_methods[] = {
|
||||
JS_SELF_HOSTED_FN("resolvedOptions", "Intl_Collator_resolvedOptions", 0, 0),
|
||||
#if JS_HAS_TOSOURCE
|
||||
JS_FN(js_toSource_str, collator_toSource, 0, 0),
|
||||
#endif
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
/**
|
||||
* 10.1.2 Intl.Collator([ locales [, options]])
|
||||
*
|
||||
* ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
|
||||
*/
|
||||
static bool
|
||||
Collator(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
// Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
|
||||
|
||||
// Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||
RootedObject proto(cx);
|
||||
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreateCollatorPrototype(cx, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<CollatorObject*> collator(cx, NewObjectWithGivenProto<CollatorObject>(cx, proto));
|
||||
if (!collator)
|
||||
return false;
|
||||
|
||||
collator->setReservedSlot(CollatorObject::INTERNALS_SLOT, NullValue());
|
||||
collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(nullptr));
|
||||
|
||||
RootedValue locales(cx, args.get(0));
|
||||
RootedValue options(cx, args.get(1));
|
||||
|
||||
// Step 6.
|
||||
if (!intl::InitializeObject(cx, collator, cx->names().InitializeCollator, locales, options))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*collator);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
Collator(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return Collator(cx, args);
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_Collator(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
MOZ_ASSERT(!args.isConstructing());
|
||||
|
||||
return Collator(cx, args);
|
||||
}
|
||||
|
||||
void
|
||||
js::CollatorObject::finalize(FreeOp* fop, JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(fop->onMainThread());
|
||||
|
||||
const Value& slot = obj->as<CollatorObject>().getReservedSlot(CollatorObject::UCOLLATOR_SLOT);
|
||||
if (UCollator* coll = static_cast<UCollator*>(slot.toPrivate()))
|
||||
ucol_close(coll);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::CreateCollatorPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
|
||||
{
|
||||
RootedFunction ctor(cx, GlobalObject::createConstructor(cx, &Collator, cx->names().Collator,
|
||||
0));
|
||||
if (!ctor)
|
||||
return nullptr;
|
||||
|
||||
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||
return nullptr;
|
||||
|
||||
// 10.2.2
|
||||
if (!JS_DefineFunctions(cx, ctor, collator_static_methods))
|
||||
return nullptr;
|
||||
|
||||
// 10.3.2 and 10.3.3
|
||||
if (!JS_DefineFunctions(cx, proto, collator_methods))
|
||||
return nullptr;
|
||||
|
||||
/*
|
||||
* Install the getter for Collator.prototype.compare, which returns a bound
|
||||
* comparison function for the specified Collator object (suitable for
|
||||
* passing to methods like Array.prototype.sort).
|
||||
*/
|
||||
RootedValue getter(cx);
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().CollatorCompareGet, &getter))
|
||||
return nullptr;
|
||||
if (!DefineProperty(cx, proto, cx->names().compare, UndefinedHandleValue,
|
||||
JS_DATA_TO_FUNC_PTR(JSGetterOp, &getter.toObject()),
|
||||
nullptr, JSPROP_GETTER | JSPROP_SHARED))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 8.1
|
||||
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||
if (!DefineProperty(cx, Intl, cx->names().Collator, ctorValue, nullptr, nullptr, 0))
|
||||
return nullptr;
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
MOZ_ASSERT(args[0].isString());
|
||||
|
||||
JSAutoByteString locale(cx, args[0].toString());
|
||||
if (!locale)
|
||||
return false;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UEnumeration* values = ucol_getKeywordValuesForLocale("co", locale.ptr(), false, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
intl::ReportInternalError(cx);
|
||||
return false;
|
||||
}
|
||||
ScopedICUObject<UEnumeration, uenum_close> toClose(values);
|
||||
|
||||
uint32_t count = uenum_count(values, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
intl::ReportInternalError(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject collations(cx, NewDenseEmptyArray(cx));
|
||||
if (!collations)
|
||||
return false;
|
||||
|
||||
uint32_t index = 0;
|
||||
|
||||
// The first element of the collations array must be |null| per
|
||||
// ES2017 Intl, 10.2.3 Internal Slots.
|
||||
if (!DefineElement(cx, collations, index++, NullHandleValue))
|
||||
return false;
|
||||
|
||||
RootedValue element(cx);
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
const char* collation = uenum_next(values, nullptr, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
intl::ReportInternalError(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Per ECMA-402, 10.2.3, we don't include standard and search:
|
||||
// "The values 'standard' and 'search' must not be used as elements in
|
||||
// any [[sortLocaleData]][locale].co and [[searchLocaleData]][locale].co
|
||||
// array."
|
||||
if (StringsAreEqual(collation, "standard") || StringsAreEqual(collation, "search"))
|
||||
continue;
|
||||
|
||||
// ICU returns old-style keyword values; map them to BCP 47 equivalents.
|
||||
JSString* jscollation = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("co", collation));
|
||||
if (!jscollation)
|
||||
return false;
|
||||
element = StringValue(jscollation);
|
||||
if (!DefineElement(cx, collations, index++, element))
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*collations);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new UCollator with the locale and collation options
|
||||
* of the given Collator.
|
||||
*/
|
||||
static UCollator*
|
||||
NewUCollator(JSContext* cx, Handle<CollatorObject*> collator)
|
||||
{
|
||||
RootedValue value(cx);
|
||||
|
||||
RootedObject internals(cx, intl::GetInternalsObject(cx, collator));
|
||||
if (!internals)
|
||||
return nullptr;
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
|
||||
return nullptr;
|
||||
JSAutoByteString locale(cx, value.toString());
|
||||
if (!locale)
|
||||
return nullptr;
|
||||
|
||||
// UCollator options with default values.
|
||||
UColAttributeValue uStrength = UCOL_DEFAULT;
|
||||
UColAttributeValue uCaseLevel = UCOL_OFF;
|
||||
UColAttributeValue uAlternate = UCOL_DEFAULT;
|
||||
UColAttributeValue uNumeric = UCOL_OFF;
|
||||
// Normalization is always on to meet the canonical equivalence requirement.
|
||||
UColAttributeValue uNormalization = UCOL_ON;
|
||||
UColAttributeValue uCaseFirst = UCOL_DEFAULT;
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().usage, &value))
|
||||
return nullptr;
|
||||
JSAutoByteString usage(cx, value.toString());
|
||||
if (!usage)
|
||||
return nullptr;
|
||||
if (StringsAreEqual(usage, "search")) {
|
||||
// ICU expects search as a Unicode locale extension on locale.
|
||||
intl::LanguageTag tag(cx);
|
||||
if (!intl::LanguageTagParser::parse(
|
||||
cx, mozilla::MakeCStringSpan(locale.ptr()), tag)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::RootedVector<intl::UnicodeExtensionKeyword> keywords(cx);
|
||||
|
||||
if (!keywords.emplaceBack("co", cx->names().search)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// |ApplyUnicodeExtensionToTag| applies the new keywords to the front of
|
||||
// the Unicode extension subtag. We're then relying on ICU to follow RFC
|
||||
// 6067, which states that any trailing keywords using the same key
|
||||
// should be ignored.
|
||||
if (!intl::ApplyUnicodeExtensionToTag(cx, tag, keywords)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
locale.clear();
|
||||
locale.encodeLatin1(cx, tag.toString(cx));
|
||||
if (!locale) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(StringsAreEqual(usage, "sort"));
|
||||
}
|
||||
|
||||
// We don't need to look at the collation property - it can only be set
|
||||
// via the Unicode locale extension and is therefore already set on
|
||||
// locale.
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().sensitivity, &value))
|
||||
return nullptr;
|
||||
JSAutoByteString sensitivity(cx, value.toString());
|
||||
if (!sensitivity)
|
||||
return nullptr;
|
||||
if (StringsAreEqual(sensitivity, "base")) {
|
||||
uStrength = UCOL_PRIMARY;
|
||||
} else if (StringsAreEqual(sensitivity, "accent")) {
|
||||
uStrength = UCOL_SECONDARY;
|
||||
} else if (StringsAreEqual(sensitivity, "case")) {
|
||||
uStrength = UCOL_PRIMARY;
|
||||
uCaseLevel = UCOL_ON;
|
||||
} else {
|
||||
MOZ_ASSERT(StringsAreEqual(sensitivity, "variant"));
|
||||
uStrength = UCOL_TERTIARY;
|
||||
}
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().ignorePunctuation, &value))
|
||||
return nullptr;
|
||||
// According to the ICU team, UCOL_SHIFTED causes punctuation to be
|
||||
// ignored. Looking at Unicode Technical Report 35, Unicode Locale Data
|
||||
// Markup Language, "shifted" causes whitespace and punctuation to be
|
||||
// ignored - that's a bit more than asked for, but there's no way to get
|
||||
// less.
|
||||
if (value.toBoolean())
|
||||
uAlternate = UCOL_SHIFTED;
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().numeric, &value))
|
||||
return nullptr;
|
||||
if (!value.isUndefined() && value.toBoolean())
|
||||
uNumeric = UCOL_ON;
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().caseFirst, &value))
|
||||
return nullptr;
|
||||
if (!value.isUndefined()) {
|
||||
JSAutoByteString caseFirst(cx, value.toString());
|
||||
if (!caseFirst)
|
||||
return nullptr;
|
||||
if (StringsAreEqual(caseFirst, "upper"))
|
||||
uCaseFirst = UCOL_UPPER_FIRST;
|
||||
else if (StringsAreEqual(caseFirst, "lower"))
|
||||
uCaseFirst = UCOL_LOWER_FIRST;
|
||||
else {
|
||||
MOZ_ASSERT(StringsAreEqual(caseFirst, "false"));
|
||||
uCaseFirst = UCOL_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UCollator* coll = ucol_open(IcuLocale(locale.ptr()), &status);
|
||||
if (U_FAILURE(status)) {
|
||||
intl::ReportInternalError(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ucol_setAttribute(coll, UCOL_STRENGTH, uStrength, &status);
|
||||
ucol_setAttribute(coll, UCOL_CASE_LEVEL, uCaseLevel, &status);
|
||||
ucol_setAttribute(coll, UCOL_ALTERNATE_HANDLING, uAlternate, &status);
|
||||
ucol_setAttribute(coll, UCOL_NUMERIC_COLLATION, uNumeric, &status);
|
||||
ucol_setAttribute(coll, UCOL_NORMALIZATION_MODE, uNormalization, &status);
|
||||
ucol_setAttribute(coll, UCOL_CASE_FIRST, uCaseFirst, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
ucol_close(coll);
|
||||
intl::ReportInternalError(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return coll;
|
||||
}
|
||||
|
||||
static bool
|
||||
intl_CompareStrings(JSContext* cx, UCollator* coll, HandleString str1, HandleString str2,
|
||||
MutableHandleValue result)
|
||||
{
|
||||
MOZ_ASSERT(str1);
|
||||
MOZ_ASSERT(str2);
|
||||
|
||||
if (str1 == str2) {
|
||||
result.setInt32(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoStableStringChars stableChars1(cx);
|
||||
if (!stableChars1.initTwoByte(cx, str1))
|
||||
return false;
|
||||
|
||||
AutoStableStringChars stableChars2(cx);
|
||||
if (!stableChars2.initTwoByte(cx, str2))
|
||||
return false;
|
||||
|
||||
mozilla::Range<const char16_t> chars1 = stableChars1.twoByteRange();
|
||||
mozilla::Range<const char16_t> chars2 = stableChars2.twoByteRange();
|
||||
|
||||
UCollationResult uresult = ucol_strcoll(coll,
|
||||
Char16ToUChar(chars1.begin().get()), chars1.length(),
|
||||
Char16ToUChar(chars2.begin().get()), chars2.length());
|
||||
int32_t res;
|
||||
switch (uresult) {
|
||||
case UCOL_LESS: res = -1; break;
|
||||
case UCOL_EQUAL: res = 0; break;
|
||||
case UCOL_GREATER: res = 1; break;
|
||||
default: MOZ_CRASH("ucol_strcoll returned bad UCollationResult");
|
||||
}
|
||||
result.setInt32(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_CompareStrings(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 3);
|
||||
MOZ_ASSERT(args[0].isObject());
|
||||
MOZ_ASSERT(args[1].isString());
|
||||
MOZ_ASSERT(args[2].isString());
|
||||
|
||||
Rooted<CollatorObject*> collator(cx, &args[0].toObject().as<CollatorObject>());
|
||||
|
||||
// Obtain a cached UCollator object.
|
||||
// XXX Does this handle Collator instances from other globals correctly?
|
||||
void* priv = collator->getReservedSlot(CollatorObject::UCOLLATOR_SLOT).toPrivate();
|
||||
UCollator* coll = static_cast<UCollator*>(priv);
|
||||
if (!coll) {
|
||||
coll = NewUCollator(cx, collator);
|
||||
if (!coll)
|
||||
return false;
|
||||
collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(coll));
|
||||
}
|
||||
|
||||
// Use the UCollator to actually compare the strings.
|
||||
RootedString str1(cx, args[1].toString());
|
||||
RootedString str2(cx, args[2].toString());
|
||||
return intl_CompareStrings(cx, coll, str1, str2, args.rval());
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_isUpperCaseFirst(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
MOZ_ASSERT(args[0].isString());
|
||||
|
||||
SharedIntlData& sharedIntlData = cx->sharedIntlData;
|
||||
|
||||
RootedString locale(cx, args[0].toString());
|
||||
bool isUpperFirst;
|
||||
if (!sharedIntlData.isUpperCaseFirst(cx, locale, &isUpperFirst))
|
||||
return false;
|
||||
|
||||
args.rval().setBoolean(isUpperFirst);
|
||||
return true;
|
||||
}
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Intl.Collator implementation. */
|
||||
|
||||
#include "builtin/intl/Collator.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Span.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "builtin/intl/CommonFunctions.h"
|
||||
#include "builtin/intl/ICUHeader.h"
|
||||
#include "builtin/intl/LanguageTag.h"
|
||||
#include "builtin/intl/ScopedICUObject.h"
|
||||
#include "builtin/intl/SharedIntlData.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/Runtime.h"
|
||||
#include "vm/String.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using namespace js;
|
||||
using js::intl::IcuLocale;
|
||||
using js::intl::ReportInternalError;
|
||||
using js::intl::SharedIntlData;
|
||||
using js::intl::StringsAreEqual;
|
||||
|
||||
/******************** Collator ********************/
|
||||
|
||||
const ClassOps CollatorObject::classOps_ = {
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
nullptr, /* getProperty */
|
||||
nullptr, /* setProperty */
|
||||
nullptr, /* enumerate */
|
||||
nullptr, /* resolve */
|
||||
nullptr, /* mayResolve */
|
||||
CollatorObject::finalize
|
||||
};
|
||||
|
||||
const Class CollatorObject::class_ = {
|
||||
js_Object_str,
|
||||
JSCLASS_HAS_RESERVED_SLOTS(CollatorObject::SLOT_COUNT) |
|
||||
JSCLASS_FOREGROUND_FINALIZE,
|
||||
&CollatorObject::classOps_
|
||||
};
|
||||
|
||||
#if JS_HAS_TOSOURCE
|
||||
static bool
|
||||
collator_toSource(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setString(cx->names().Collator);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const JSFunctionSpec collator_static_methods[] = {
|
||||
JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_Collator_supportedLocalesOf", 1, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static const JSFunctionSpec collator_methods[] = {
|
||||
JS_SELF_HOSTED_FN("resolvedOptions", "Intl_Collator_resolvedOptions", 0, 0),
|
||||
#if JS_HAS_TOSOURCE
|
||||
JS_FN(js_toSource_str, collator_toSource, 0, 0),
|
||||
#endif
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
/**
|
||||
* 10.1.2 Intl.Collator([ locales [, options]])
|
||||
*
|
||||
* ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
|
||||
*/
|
||||
static bool
|
||||
Collator(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
// Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
|
||||
|
||||
// Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreateCollatorPrototype(cx, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<CollatorObject*> collator(cx, NewObjectWithGivenProto<CollatorObject>(cx, proto));
|
||||
if (!collator)
|
||||
return false;
|
||||
|
||||
collator->setReservedSlot(CollatorObject::INTERNALS_SLOT, NullValue());
|
||||
collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(nullptr));
|
||||
|
||||
RootedValue locales(cx, args.get(0));
|
||||
RootedValue options(cx, args.get(1));
|
||||
|
||||
// Step 6.
|
||||
if (!intl::InitializeObject(cx, collator, cx->names().InitializeCollator, locales, options))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*collator);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
Collator(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return Collator(cx, args);
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_Collator(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
MOZ_ASSERT(!args.isConstructing());
|
||||
|
||||
return Collator(cx, args);
|
||||
}
|
||||
|
||||
void
|
||||
js::CollatorObject::finalize(FreeOp* fop, JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(fop->onActiveCooperatingThread());
|
||||
|
||||
const Value& slot = obj->as<CollatorObject>().getReservedSlot(CollatorObject::UCOLLATOR_SLOT);
|
||||
if (UCollator* coll = static_cast<UCollator*>(slot.toPrivate()))
|
||||
ucol_close(coll);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::CreateCollatorPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
|
||||
{
|
||||
RootedFunction ctor(cx, GlobalObject::createConstructor(cx, &Collator, cx->names().Collator,
|
||||
0));
|
||||
if (!ctor)
|
||||
return nullptr;
|
||||
|
||||
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||
return nullptr;
|
||||
|
||||
// 10.2.2
|
||||
if (!JS_DefineFunctions(cx, ctor, collator_static_methods))
|
||||
return nullptr;
|
||||
|
||||
// 10.3.2 and 10.3.3
|
||||
if (!JS_DefineFunctions(cx, proto, collator_methods))
|
||||
return nullptr;
|
||||
|
||||
/*
|
||||
* Install the getter for Collator.prototype.compare, which returns a bound
|
||||
* comparison function for the specified Collator object (suitable for
|
||||
* passing to methods like Array.prototype.sort).
|
||||
*/
|
||||
RootedValue getter(cx);
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().CollatorCompareGet, &getter))
|
||||
return nullptr;
|
||||
if (!DefineProperty(cx, proto, cx->names().compare, UndefinedHandleValue,
|
||||
JS_DATA_TO_FUNC_PTR(JSGetterOp, &getter.toObject()),
|
||||
nullptr, JSPROP_GETTER | JSPROP_SHARED))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 8.1
|
||||
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||
if (!DefineProperty(cx, Intl, cx->names().Collator, ctorValue, nullptr, nullptr, 0))
|
||||
return nullptr;
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
MOZ_ASSERT(args[0].isString());
|
||||
|
||||
JSAutoByteString locale(cx, args[0].toString());
|
||||
if (!locale)
|
||||
return false;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UEnumeration* values = ucol_getKeywordValuesForLocale("co", locale.ptr(), false, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
intl::ReportInternalError(cx);
|
||||
return false;
|
||||
}
|
||||
ScopedICUObject<UEnumeration, uenum_close> toClose(values);
|
||||
|
||||
uint32_t count = uenum_count(values, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
intl::ReportInternalError(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject collations(cx, NewDenseEmptyArray(cx));
|
||||
if (!collations)
|
||||
return false;
|
||||
|
||||
uint32_t index = 0;
|
||||
|
||||
// The first element of the collations array must be |null| per
|
||||
// ES2017 Intl, 10.2.3 Internal Slots.
|
||||
if (!DefineElement(cx, collations, index++, NullHandleValue))
|
||||
return false;
|
||||
|
||||
RootedValue element(cx);
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
const char* collation = uenum_next(values, nullptr, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
intl::ReportInternalError(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Per ECMA-402, 10.2.3, we don't include standard and search:
|
||||
// "The values 'standard' and 'search' must not be used as elements in
|
||||
// any [[sortLocaleData]][locale].co and [[searchLocaleData]][locale].co
|
||||
// array."
|
||||
if (StringsAreEqual(collation, "standard") || StringsAreEqual(collation, "search"))
|
||||
continue;
|
||||
|
||||
// ICU returns old-style keyword values; map them to BCP 47 equivalents.
|
||||
JSString* jscollation = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("co", collation));
|
||||
if (!jscollation)
|
||||
return false;
|
||||
element = StringValue(jscollation);
|
||||
if (!DefineElement(cx, collations, index++, element))
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*collations);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new UCollator with the locale and collation options
|
||||
* of the given Collator.
|
||||
*/
|
||||
static UCollator*
|
||||
NewUCollator(JSContext* cx, Handle<CollatorObject*> collator)
|
||||
{
|
||||
RootedValue value(cx);
|
||||
|
||||
RootedObject internals(cx, intl::GetInternalsObject(cx, collator));
|
||||
if (!internals)
|
||||
return nullptr;
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
|
||||
return nullptr;
|
||||
JSAutoByteString locale(cx, value.toString());
|
||||
if (!locale)
|
||||
return nullptr;
|
||||
|
||||
// UCollator options with default values.
|
||||
UColAttributeValue uStrength = UCOL_DEFAULT;
|
||||
UColAttributeValue uCaseLevel = UCOL_OFF;
|
||||
UColAttributeValue uAlternate = UCOL_DEFAULT;
|
||||
UColAttributeValue uNumeric = UCOL_OFF;
|
||||
// Normalization is always on to meet the canonical equivalence requirement.
|
||||
UColAttributeValue uNormalization = UCOL_ON;
|
||||
UColAttributeValue uCaseFirst = UCOL_DEFAULT;
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().usage, &value))
|
||||
return nullptr;
|
||||
JSAutoByteString usage(cx, value.toString());
|
||||
if (!usage)
|
||||
return nullptr;
|
||||
if (StringsAreEqual(usage, "search")) {
|
||||
// ICU expects search as a Unicode locale extension on locale.
|
||||
intl::LanguageTag tag(cx);
|
||||
if (!intl::LanguageTagParser::parse(
|
||||
cx, mozilla::MakeCStringSpan(locale.ptr()), tag)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::RootedVector<intl::UnicodeExtensionKeyword> keywords(cx);
|
||||
|
||||
if (!keywords.emplaceBack("co", cx->names().search)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// |ApplyUnicodeExtensionToTag| applies the new keywords to the front of
|
||||
// the Unicode extension subtag. We're then relying on ICU to follow RFC
|
||||
// 6067, which states that any trailing keywords using the same key
|
||||
// should be ignored.
|
||||
if (!intl::ApplyUnicodeExtensionToTag(cx, tag, keywords)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
locale.clear();
|
||||
locale.encodeLatin1(cx, tag.toString(cx));
|
||||
if (!locale) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(StringsAreEqual(usage, "sort"));
|
||||
}
|
||||
|
||||
// We don't need to look at the collation property - it can only be set
|
||||
// via the Unicode locale extension and is therefore already set on
|
||||
// locale.
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().sensitivity, &value))
|
||||
return nullptr;
|
||||
JSAutoByteString sensitivity(cx, value.toString());
|
||||
if (!sensitivity)
|
||||
return nullptr;
|
||||
if (StringsAreEqual(sensitivity, "base")) {
|
||||
uStrength = UCOL_PRIMARY;
|
||||
} else if (StringsAreEqual(sensitivity, "accent")) {
|
||||
uStrength = UCOL_SECONDARY;
|
||||
} else if (StringsAreEqual(sensitivity, "case")) {
|
||||
uStrength = UCOL_PRIMARY;
|
||||
uCaseLevel = UCOL_ON;
|
||||
} else {
|
||||
MOZ_ASSERT(StringsAreEqual(sensitivity, "variant"));
|
||||
uStrength = UCOL_TERTIARY;
|
||||
}
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().ignorePunctuation, &value))
|
||||
return nullptr;
|
||||
// According to the ICU team, UCOL_SHIFTED causes punctuation to be
|
||||
// ignored. Looking at Unicode Technical Report 35, Unicode Locale Data
|
||||
// Markup Language, "shifted" causes whitespace and punctuation to be
|
||||
// ignored - that's a bit more than asked for, but there's no way to get
|
||||
// less.
|
||||
if (value.toBoolean())
|
||||
uAlternate = UCOL_SHIFTED;
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().numeric, &value))
|
||||
return nullptr;
|
||||
if (!value.isUndefined() && value.toBoolean())
|
||||
uNumeric = UCOL_ON;
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().caseFirst, &value))
|
||||
return nullptr;
|
||||
if (!value.isUndefined()) {
|
||||
JSAutoByteString caseFirst(cx, value.toString());
|
||||
if (!caseFirst)
|
||||
return nullptr;
|
||||
if (StringsAreEqual(caseFirst, "upper"))
|
||||
uCaseFirst = UCOL_UPPER_FIRST;
|
||||
else if (StringsAreEqual(caseFirst, "lower"))
|
||||
uCaseFirst = UCOL_LOWER_FIRST;
|
||||
else {
|
||||
MOZ_ASSERT(StringsAreEqual(caseFirst, "false"));
|
||||
uCaseFirst = UCOL_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UCollator* coll = ucol_open(IcuLocale(locale.ptr()), &status);
|
||||
if (U_FAILURE(status)) {
|
||||
intl::ReportInternalError(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ucol_setAttribute(coll, UCOL_STRENGTH, uStrength, &status);
|
||||
ucol_setAttribute(coll, UCOL_CASE_LEVEL, uCaseLevel, &status);
|
||||
ucol_setAttribute(coll, UCOL_ALTERNATE_HANDLING, uAlternate, &status);
|
||||
ucol_setAttribute(coll, UCOL_NUMERIC_COLLATION, uNumeric, &status);
|
||||
ucol_setAttribute(coll, UCOL_NORMALIZATION_MODE, uNormalization, &status);
|
||||
ucol_setAttribute(coll, UCOL_CASE_FIRST, uCaseFirst, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
ucol_close(coll);
|
||||
intl::ReportInternalError(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return coll;
|
||||
}
|
||||
|
||||
static bool
|
||||
intl_CompareStrings(JSContext* cx, UCollator* coll, HandleString str1, HandleString str2,
|
||||
MutableHandleValue result)
|
||||
{
|
||||
MOZ_ASSERT(str1);
|
||||
MOZ_ASSERT(str2);
|
||||
|
||||
if (str1 == str2) {
|
||||
result.setInt32(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoStableStringChars stableChars1(cx);
|
||||
if (!stableChars1.initTwoByte(cx, str1))
|
||||
return false;
|
||||
|
||||
AutoStableStringChars stableChars2(cx);
|
||||
if (!stableChars2.initTwoByte(cx, str2))
|
||||
return false;
|
||||
|
||||
mozilla::Range<const char16_t> chars1 = stableChars1.twoByteRange();
|
||||
mozilla::Range<const char16_t> chars2 = stableChars2.twoByteRange();
|
||||
|
||||
UCollationResult uresult = ucol_strcoll(coll,
|
||||
Char16ToUChar(chars1.begin().get()), chars1.length(),
|
||||
Char16ToUChar(chars2.begin().get()), chars2.length());
|
||||
int32_t res;
|
||||
switch (uresult) {
|
||||
case UCOL_LESS: res = -1; break;
|
||||
case UCOL_EQUAL: res = 0; break;
|
||||
case UCOL_GREATER: res = 1; break;
|
||||
default: MOZ_CRASH("ucol_strcoll returned bad UCollationResult");
|
||||
}
|
||||
result.setInt32(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_CompareStrings(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 3);
|
||||
MOZ_ASSERT(args[0].isObject());
|
||||
MOZ_ASSERT(args[1].isString());
|
||||
MOZ_ASSERT(args[2].isString());
|
||||
|
||||
Rooted<CollatorObject*> collator(cx, &args[0].toObject().as<CollatorObject>());
|
||||
|
||||
// Obtain a cached UCollator object.
|
||||
// XXX Does this handle Collator instances from other globals correctly?
|
||||
void* priv = collator->getReservedSlot(CollatorObject::UCOLLATOR_SLOT).toPrivate();
|
||||
UCollator* coll = static_cast<UCollator*>(priv);
|
||||
if (!coll) {
|
||||
coll = NewUCollator(cx, collator);
|
||||
if (!coll)
|
||||
return false;
|
||||
collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(coll));
|
||||
}
|
||||
|
||||
// Use the UCollator to actually compare the strings.
|
||||
RootedString str1(cx, args[1].toString());
|
||||
RootedString str2(cx, args[2].toString());
|
||||
return intl_CompareStrings(cx, coll, str1, str2, args.rval());
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_isUpperCaseFirst(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
MOZ_ASSERT(args[0].isString());
|
||||
|
||||
SharedIntlData& sharedIntlData = cx->runtime()->sharedIntlData.ref();
|
||||
|
||||
RootedString locale(cx, args[0].toString());
|
||||
bool isUpperFirst;
|
||||
if (!sharedIntlData.isUpperCaseFirst(cx, locale, &isUpperFirst))
|
||||
return false;
|
||||
|
||||
args.rval().setBoolean(isUpperFirst);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -481,7 +481,7 @@ static bool Locale(JSContext* cx, unsigned argc, Value* vp) {
|
|||
|
||||
// Steps 2-6 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) {
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,287 +1,287 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Implementation of the Intl.RelativeTimeFormat proposal. */
|
||||
|
||||
#include "builtin/intl/RelativeTimeFormat.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Casting.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "builtin/intl/CommonFunctions.h"
|
||||
#include "builtin/intl/ICUHeader.h"
|
||||
#include "builtin/intl/ScopedICUObject.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
|
||||
#include "vm/NativeObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
using mozilla::IsNegativeZero;
|
||||
using mozilla::Range;
|
||||
using mozilla::RangedPtr;
|
||||
|
||||
using js::intl::CallICU;
|
||||
using js::intl::IcuLocale;
|
||||
using js::intl::INITIAL_CHAR_BUFFER_SIZE;
|
||||
using js::intl::StringsAreEqual;
|
||||
|
||||
/**************** RelativeTimeFormat *****************/
|
||||
|
||||
const ClassOps RelativeTimeFormatObject::classOps_ = {
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
nullptr, /* getProperty */
|
||||
nullptr, /* enumerate */
|
||||
nullptr, /* newEnumerate */
|
||||
nullptr, /* resolve */
|
||||
nullptr, /* mayResolve */
|
||||
RelativeTimeFormatObject::finalize
|
||||
};
|
||||
|
||||
const Class RelativeTimeFormatObject::class_ = {
|
||||
js_Object_str,
|
||||
JSCLASS_HAS_RESERVED_SLOTS(RelativeTimeFormatObject::SLOT_COUNT) |
|
||||
JSCLASS_FOREGROUND_FINALIZE,
|
||||
&RelativeTimeFormatObject::classOps_
|
||||
};
|
||||
|
||||
#if JS_HAS_TOSOURCE
|
||||
static bool
|
||||
relativeTimeFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setString(cx->names().RelativeTimeFormat);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const JSFunctionSpec relativeTimeFormat_static_methods[] = {
|
||||
JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_RelativeTimeFormat_supportedLocalesOf", 1, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static const JSFunctionSpec relativeTimeFormat_methods[] = {
|
||||
JS_SELF_HOSTED_FN("resolvedOptions", "Intl_RelativeTimeFormat_resolvedOptions", 0, 0),
|
||||
JS_SELF_HOSTED_FN("format", "Intl_RelativeTimeFormat_format", 2, 0),
|
||||
#if JS_HAS_TOSOURCE
|
||||
JS_FN(js_toSource_str, relativeTimeFormat_toSource, 0, 0),
|
||||
#endif
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static const JSPropertySpec relativeTimeFormat_properties[] = {
|
||||
JS_STRING_SYM_PS(toStringTag, "Intl.RelativeTimeFormat", JSPROP_READONLY),
|
||||
JS_PS_END};
|
||||
|
||||
/**
|
||||
* RelativeTimeFormat constructor.
|
||||
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.1
|
||||
*/
|
||||
static bool
|
||||
RelativeTimeFormat(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
// Step 1.
|
||||
if (!ThrowIfNotConstructing(cx, args, "Intl.RelativeTimeFormat"))
|
||||
return false;
|
||||
|
||||
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||
RootedObject proto(cx);
|
||||
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreateRelativeTimeFormatPrototype(cx, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject relativeTimeFormat(cx);
|
||||
relativeTimeFormat = NewObjectWithGivenProto<RelativeTimeFormatObject>(cx, proto);
|
||||
if (!relativeTimeFormat)
|
||||
return false;
|
||||
|
||||
relativeTimeFormat->as<RelativeTimeFormatObject>().setReservedSlot(RelativeTimeFormatObject::INTERNALS_SLOT, NullValue());
|
||||
relativeTimeFormat->as<RelativeTimeFormatObject>().setReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
|
||||
|
||||
RootedValue locales(cx, args.get(0));
|
||||
RootedValue options(cx, args.get(1));
|
||||
|
||||
// Step 3.
|
||||
if (!intl::InitializeObject(cx, relativeTimeFormat, cx->names().InitializeRelativeTimeFormat, locales, options))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*relativeTimeFormat);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
js::RelativeTimeFormatObject::finalize(FreeOp* fop, JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(fop->onMainThread());
|
||||
|
||||
const Value& slot = obj->as<RelativeTimeFormatObject>().getReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT);
|
||||
if (URelativeDateTimeFormatter* rtf = static_cast<URelativeDateTimeFormatter*>(slot.toPrivate()))
|
||||
ureldatefmt_close(rtf);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::CreateRelativeTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
|
||||
{
|
||||
RootedFunction ctor(cx);
|
||||
ctor = global->createConstructor(cx, &RelativeTimeFormat, cx->names().RelativeTimeFormat, 0);
|
||||
if (!ctor)
|
||||
return nullptr;
|
||||
|
||||
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||
return nullptr;
|
||||
|
||||
if (!JS_DefineFunctions(cx, ctor, relativeTimeFormat_static_methods))
|
||||
return nullptr;
|
||||
|
||||
if (!JS_DefineFunctions(cx, proto, relativeTimeFormat_methods))
|
||||
return nullptr;
|
||||
|
||||
if (!JS_DefineProperties(cx, proto, relativeTimeFormat_properties))
|
||||
return nullptr;
|
||||
|
||||
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||
if (!DefineProperty(cx, Intl, cx->names().RelativeTimeFormat, ctorValue, nullptr, nullptr, 0)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
enum class RelativeTimeNumeric
|
||||
{
|
||||
/**
|
||||
* Only strings with numeric components like `1 day ago`.
|
||||
*/
|
||||
Always,
|
||||
/**
|
||||
* Natural-language strings like `yesterday` when possible,
|
||||
* otherwise strings with numeric components as in `7 months ago`.
|
||||
*/
|
||||
Auto,
|
||||
};
|
||||
|
||||
bool
|
||||
js::intl_FormatRelativeTime(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 4);
|
||||
|
||||
RootedObject relativeTimeFormat(cx, &args[0].toObject());
|
||||
|
||||
RootedObject internals(cx, intl::GetInternalsObject(cx, relativeTimeFormat));
|
||||
if (!internals)
|
||||
return false;
|
||||
|
||||
RootedValue value(cx);
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
|
||||
return false;
|
||||
JSAutoByteString locale(cx, value.toString());
|
||||
if (!locale)
|
||||
return false;
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().style, &value))
|
||||
return false;
|
||||
RootedLinearString style(cx, value.toString()->ensureLinear(cx));
|
||||
if (!style)
|
||||
return false;
|
||||
|
||||
double t = args[1].toNumber();
|
||||
|
||||
UDateRelativeDateTimeFormatterStyle relDateTimeStyle;
|
||||
|
||||
if (StringEqualsAscii(style, "short")) {
|
||||
relDateTimeStyle = UDAT_STYLE_SHORT;
|
||||
} else if (StringEqualsAscii(style, "narrow")) {
|
||||
relDateTimeStyle = UDAT_STYLE_NARROW;
|
||||
} else {
|
||||
MOZ_ASSERT(StringEqualsAscii(style, "long"));
|
||||
relDateTimeStyle = UDAT_STYLE_LONG;
|
||||
}
|
||||
|
||||
URelativeDateTimeUnit relDateTimeUnit;
|
||||
{
|
||||
JSLinearString* unit = args[2].toString()->ensureLinear(cx);
|
||||
if (!unit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StringEqualsAscii(unit, "second") || StringEqualsAscii(unit, "seconds")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_SECOND;
|
||||
} else if (StringEqualsAscii(unit, "minute") || StringEqualsAscii(unit, "minutes")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_MINUTE;
|
||||
} else if (StringEqualsAscii(unit, "hour") || StringEqualsAscii(unit, "hours")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_HOUR;
|
||||
} else if (StringEqualsAscii(unit, "day") || StringEqualsAscii(unit, "days")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_DAY;
|
||||
} else if (StringEqualsAscii(unit, "week") || StringEqualsAscii(unit, "weeks")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_WEEK;
|
||||
} else if (StringEqualsAscii(unit, "month") || StringEqualsAscii(unit, "months")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_MONTH;
|
||||
} else if (StringEqualsAscii(unit, "quarter") || StringEqualsAscii(unit, "quarters")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_QUARTER;
|
||||
} else {
|
||||
MOZ_ASSERT(StringEqualsAscii(unit, "year") || StringEqualsAscii(unit, "years"));
|
||||
relDateTimeUnit = UDAT_REL_UNIT_YEAR;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().numeric, &value))
|
||||
return false;
|
||||
RootedLinearString numeric(cx, value.toString()->ensureLinear(cx));
|
||||
if (!numeric)
|
||||
return false;
|
||||
|
||||
RelativeTimeNumeric relDateTimeNumeric;
|
||||
|
||||
if (StringEqualsAscii(numeric, "auto")) {
|
||||
relDateTimeNumeric = RelativeTimeNumeric::Auto;
|
||||
} else {
|
||||
MOZ_ASSERT(StringEqualsAscii(numeric, "always"));
|
||||
relDateTimeNumeric = RelativeTimeNumeric::Always;
|
||||
}
|
||||
|
||||
Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
|
||||
if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
|
||||
return false;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
URelativeDateTimeFormatter* rtf =
|
||||
ureldatefmt_open(IcuLocale(locale.ptr()), nullptr, relDateTimeStyle,
|
||||
UDISPCTX_CAPITALIZATION_FOR_STANDALONE, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
intl::ReportInternalError(cx);
|
||||
return false;
|
||||
}
|
||||
ScopedICUObject<URelativeDateTimeFormatter, ureldatefmt_close> closeRelativeTimeFormat(rtf);
|
||||
|
||||
JSString* str =
|
||||
CallICU(cx, [rtf, t, relDateTimeUnit, relDateTimeNumeric](UChar* chars, int32_t size,
|
||||
UErrorCode* status)
|
||||
{
|
||||
auto fmt = relDateTimeNumeric == RelativeTimeNumeric::Auto
|
||||
? ureldatefmt_format
|
||||
: ureldatefmt_formatNumeric;
|
||||
return fmt(rtf, t, relDateTimeUnit, chars, size, status);
|
||||
});
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
args.rval().setString(str);
|
||||
return true;
|
||||
}
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Implementation of the Intl.RelativeTimeFormat proposal. */
|
||||
|
||||
#include "builtin/intl/RelativeTimeFormat.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Casting.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "builtin/intl/CommonFunctions.h"
|
||||
#include "builtin/intl/ICUHeader.h"
|
||||
#include "builtin/intl/ScopedICUObject.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
|
||||
#include "vm/NativeObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
using mozilla::IsNegativeZero;
|
||||
using mozilla::Range;
|
||||
using mozilla::RangedPtr;
|
||||
|
||||
using js::intl::CallICU;
|
||||
using js::intl::IcuLocale;
|
||||
using js::intl::INITIAL_CHAR_BUFFER_SIZE;
|
||||
using js::intl::StringsAreEqual;
|
||||
|
||||
/**************** RelativeTimeFormat *****************/
|
||||
|
||||
const ClassOps RelativeTimeFormatObject::classOps_ = {
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
nullptr, /* getProperty */
|
||||
nullptr, /* enumerate */
|
||||
nullptr, /* newEnumerate */
|
||||
nullptr, /* resolve */
|
||||
nullptr, /* mayResolve */
|
||||
RelativeTimeFormatObject::finalize
|
||||
};
|
||||
|
||||
const Class RelativeTimeFormatObject::class_ = {
|
||||
js_Object_str,
|
||||
JSCLASS_HAS_RESERVED_SLOTS(RelativeTimeFormatObject::SLOT_COUNT) |
|
||||
JSCLASS_FOREGROUND_FINALIZE,
|
||||
&RelativeTimeFormatObject::classOps_
|
||||
};
|
||||
|
||||
#if JS_HAS_TOSOURCE
|
||||
static bool
|
||||
relativeTimeFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setString(cx->names().RelativeTimeFormat);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const JSFunctionSpec relativeTimeFormat_static_methods[] = {
|
||||
JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_RelativeTimeFormat_supportedLocalesOf", 1, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static const JSFunctionSpec relativeTimeFormat_methods[] = {
|
||||
JS_SELF_HOSTED_FN("resolvedOptions", "Intl_RelativeTimeFormat_resolvedOptions", 0, 0),
|
||||
JS_SELF_HOSTED_FN("format", "Intl_RelativeTimeFormat_format", 2, 0),
|
||||
#if JS_HAS_TOSOURCE
|
||||
JS_FN(js_toSource_str, relativeTimeFormat_toSource, 0, 0),
|
||||
#endif
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static const JSPropertySpec relativeTimeFormat_properties[] = {
|
||||
JS_STRING_SYM_PS(toStringTag, "Intl.RelativeTimeFormat", JSPROP_READONLY),
|
||||
JS_PS_END};
|
||||
|
||||
/**
|
||||
* RelativeTimeFormat constructor.
|
||||
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.1
|
||||
*/
|
||||
static bool
|
||||
RelativeTimeFormat(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
// Step 1.
|
||||
if (!ThrowIfNotConstructing(cx, args, "Intl.RelativeTimeFormat"))
|
||||
return false;
|
||||
|
||||
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreateRelativeTimeFormatPrototype(cx, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject relativeTimeFormat(cx);
|
||||
relativeTimeFormat = NewObjectWithGivenProto<RelativeTimeFormatObject>(cx, proto);
|
||||
if (!relativeTimeFormat)
|
||||
return false;
|
||||
|
||||
relativeTimeFormat->as<RelativeTimeFormatObject>().setReservedSlot(RelativeTimeFormatObject::INTERNALS_SLOT, NullValue());
|
||||
relativeTimeFormat->as<RelativeTimeFormatObject>().setReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
|
||||
|
||||
RootedValue locales(cx, args.get(0));
|
||||
RootedValue options(cx, args.get(1));
|
||||
|
||||
// Step 3.
|
||||
if (!intl::InitializeObject(cx, relativeTimeFormat, cx->names().InitializeRelativeTimeFormat, locales, options))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*relativeTimeFormat);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
js::RelativeTimeFormatObject::finalize(FreeOp* fop, JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(fop->onActiveCooperatingThread());
|
||||
|
||||
const Value& slot = obj->as<RelativeTimeFormatObject>().getReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT);
|
||||
if (URelativeDateTimeFormatter* rtf = static_cast<URelativeDateTimeFormatter*>(slot.toPrivate()))
|
||||
ureldatefmt_close(rtf);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::CreateRelativeTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
|
||||
{
|
||||
RootedFunction ctor(cx);
|
||||
ctor = global->createConstructor(cx, &RelativeTimeFormat, cx->names().RelativeTimeFormat, 0);
|
||||
if (!ctor)
|
||||
return nullptr;
|
||||
|
||||
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||
return nullptr;
|
||||
|
||||
if (!JS_DefineFunctions(cx, ctor, relativeTimeFormat_static_methods))
|
||||
return nullptr;
|
||||
|
||||
if (!JS_DefineFunctions(cx, proto, relativeTimeFormat_methods))
|
||||
return nullptr;
|
||||
|
||||
if (!JS_DefineProperties(cx, proto, relativeTimeFormat_properties))
|
||||
return nullptr;
|
||||
|
||||
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||
if (!DefineProperty(cx, Intl, cx->names().RelativeTimeFormat, ctorValue, nullptr, nullptr, 0)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
enum class RelativeTimeNumeric
|
||||
{
|
||||
/**
|
||||
* Only strings with numeric components like `1 day ago`.
|
||||
*/
|
||||
Always,
|
||||
/**
|
||||
* Natural-language strings like `yesterday` when possible,
|
||||
* otherwise strings with numeric components as in `7 months ago`.
|
||||
*/
|
||||
Auto,
|
||||
};
|
||||
|
||||
bool
|
||||
js::intl_FormatRelativeTime(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 4);
|
||||
|
||||
RootedObject relativeTimeFormat(cx, &args[0].toObject());
|
||||
|
||||
RootedObject internals(cx, intl::GetInternalsObject(cx, relativeTimeFormat));
|
||||
if (!internals)
|
||||
return false;
|
||||
|
||||
RootedValue value(cx);
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
|
||||
return false;
|
||||
JSAutoByteString locale(cx, value.toString());
|
||||
if (!locale)
|
||||
return false;
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().style, &value))
|
||||
return false;
|
||||
RootedLinearString style(cx, value.toString()->ensureLinear(cx));
|
||||
if (!style)
|
||||
return false;
|
||||
|
||||
double t = args[1].toNumber();
|
||||
|
||||
UDateRelativeDateTimeFormatterStyle relDateTimeStyle;
|
||||
|
||||
if (StringEqualsAscii(style, "short")) {
|
||||
relDateTimeStyle = UDAT_STYLE_SHORT;
|
||||
} else if (StringEqualsAscii(style, "narrow")) {
|
||||
relDateTimeStyle = UDAT_STYLE_NARROW;
|
||||
} else {
|
||||
MOZ_ASSERT(StringEqualsAscii(style, "long"));
|
||||
relDateTimeStyle = UDAT_STYLE_LONG;
|
||||
}
|
||||
|
||||
URelativeDateTimeUnit relDateTimeUnit;
|
||||
{
|
||||
JSLinearString* unit = args[2].toString()->ensureLinear(cx);
|
||||
if (!unit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StringEqualsAscii(unit, "second") || StringEqualsAscii(unit, "seconds")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_SECOND;
|
||||
} else if (StringEqualsAscii(unit, "minute") || StringEqualsAscii(unit, "minutes")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_MINUTE;
|
||||
} else if (StringEqualsAscii(unit, "hour") || StringEqualsAscii(unit, "hours")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_HOUR;
|
||||
} else if (StringEqualsAscii(unit, "day") || StringEqualsAscii(unit, "days")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_DAY;
|
||||
} else if (StringEqualsAscii(unit, "week") || StringEqualsAscii(unit, "weeks")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_WEEK;
|
||||
} else if (StringEqualsAscii(unit, "month") || StringEqualsAscii(unit, "months")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_MONTH;
|
||||
} else if (StringEqualsAscii(unit, "quarter") || StringEqualsAscii(unit, "quarters")) {
|
||||
relDateTimeUnit = UDAT_REL_UNIT_QUARTER;
|
||||
} else {
|
||||
MOZ_ASSERT(StringEqualsAscii(unit, "year") || StringEqualsAscii(unit, "years"));
|
||||
relDateTimeUnit = UDAT_REL_UNIT_YEAR;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().numeric, &value))
|
||||
return false;
|
||||
RootedLinearString numeric(cx, value.toString()->ensureLinear(cx));
|
||||
if (!numeric)
|
||||
return false;
|
||||
|
||||
RelativeTimeNumeric relDateTimeNumeric;
|
||||
|
||||
if (StringEqualsAscii(numeric, "auto")) {
|
||||
relDateTimeNumeric = RelativeTimeNumeric::Auto;
|
||||
} else {
|
||||
MOZ_ASSERT(StringEqualsAscii(numeric, "always"));
|
||||
relDateTimeNumeric = RelativeTimeNumeric::Always;
|
||||
}
|
||||
|
||||
Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
|
||||
if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
|
||||
return false;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
URelativeDateTimeFormatter* rtf =
|
||||
ureldatefmt_open(IcuLocale(locale.ptr()), nullptr, relDateTimeStyle,
|
||||
UDISPCTX_CAPITALIZATION_FOR_STANDALONE, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
intl::ReportInternalError(cx);
|
||||
return false;
|
||||
}
|
||||
ScopedICUObject<URelativeDateTimeFormatter, ureldatefmt_close> closeRelativeTimeFormat(rtf);
|
||||
|
||||
JSString* str =
|
||||
CallICU(cx, [rtf, t, relDateTimeUnit, relDateTimeNumeric](UChar* chars, int32_t size,
|
||||
UErrorCode* status)
|
||||
{
|
||||
auto fmt = relDateTimeNumeric == RelativeTimeNumeric::Auto
|
||||
? ureldatefmt_format
|
||||
: ureldatefmt_formatNumeric;
|
||||
return fmt(rtf, t, relDateTimeUnit, chars, size, status);
|
||||
});
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
args.rval().setString(str);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3271,7 +3271,7 @@ ArrayConstructorImpl(JSContext* cx, CallArgs& args, bool isConstructor)
|
|||
|
||||
RootedObject proto(cx);
|
||||
if (isConstructor) {
|
||||
if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
} else {
|
||||
// We're emulating |new Array(n)| with |std_Array(n)| in self-hosted JS,
|
||||
|
|
|
|||
|
|
@ -116,10 +116,8 @@ Boolean(JSContext* cx, unsigned argc, Value* vp)
|
|||
bool b = args.length() != 0 ? JS::ToBoolean(args[0]) : false;
|
||||
|
||||
if (args.isConstructing()) {
|
||||
RootedObject newTarget (cx, &args.newTarget().toObject());
|
||||
RootedObject proto(cx);
|
||||
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
JSObject* obj = BooleanObject::create(cx, b, proto);
|
||||
|
|
|
|||
|
|
@ -3014,8 +3014,7 @@ NewDateObject(JSContext* cx, const CallArgs& args, ClippedTime t)
|
|||
MOZ_ASSERT(args.isConstructing());
|
||||
|
||||
RootedObject proto(cx);
|
||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
JSObject* obj = NewDateObjectMsec(cx, t, proto);
|
||||
|
|
|
|||
|
|
@ -1750,7 +1750,7 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator
|
|||
// Step 24.
|
||||
RootedObject proto(cx);
|
||||
if (!isAsync) {
|
||||
if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -520,19 +520,17 @@ Number(JSContext* cx, unsigned argc, Value* vp)
|
|||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
/* Sample JS_CALLEE before clobbering. */
|
||||
bool isConstructing = args.isConstructing();
|
||||
|
||||
if (args.length() > 0) {
|
||||
// BigInt proposal section 6.2, steps 2a-c.
|
||||
if (!ToNumeric(cx, args[0]))
|
||||
return false;
|
||||
|
||||
if (args[0].isBigInt())
|
||||
args[0].setNumber(BigInt::numberValue(args[0].toBigInt()));
|
||||
MOZ_ASSERT(args[0].isNumber());
|
||||
}
|
||||
|
||||
if (!isConstructing) {
|
||||
if (!args.isConstructing()) {
|
||||
if (args.length() > 0) {
|
||||
args.rval().set(args[0]);
|
||||
} else {
|
||||
|
|
@ -541,9 +539,8 @@ Number(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
double d = args.length() > 0 ? args[0].toNumber() : 0;
|
||||
|
|
|
|||
|
|
@ -970,17 +970,6 @@ js::GetPrototypeFromConstructor(JSContext* cx, HandleObject newTarget, MutableHa
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::GetPrototypeFromCallableConstructor(JSContext* cx, const CallArgs& args, MutableHandleObject proto)
|
||||
{
|
||||
RootedObject newTarget(cx);
|
||||
if (args.isConstructing())
|
||||
newTarget = &args.newTarget().toObject();
|
||||
else
|
||||
newTarget = &args.callee();
|
||||
return GetPrototypeFromConstructor(cx, newTarget, proto);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::CreateThisForFunction(JSContext* cx, HandleObject callee, HandleObject newTarget,
|
||||
NewObjectKind newKind)
|
||||
|
|
|
|||
|
|
@ -1125,8 +1125,21 @@ NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle<TaggedProto>
|
|||
extern bool
|
||||
GetPrototypeFromConstructor(JSContext* cx, js::HandleObject newTarget, js::MutableHandleObject proto);
|
||||
|
||||
extern bool
|
||||
GetPrototypeFromCallableConstructor(JSContext* cx, const CallArgs& args, js::MutableHandleObject proto);
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
GetPrototypeFromBuiltinConstructor(JSContext* cx, const CallArgs& args, js::MutableHandleObject proto)
|
||||
{
|
||||
// When proto is set to nullptr, the caller is expected to select the
|
||||
// correct default built-in prototype for this constructor.
|
||||
if (!args.isConstructing() || &args.newTarget().toObject() == &args.callee()) {
|
||||
proto.set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
// We're calling this constructor from a derived class, retrieve the
|
||||
// actual prototype from newTarget.
|
||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||
return GetPrototypeFromConstructor(cx, newTarget, proto);
|
||||
}
|
||||
|
||||
// Specialized call for constructing |this| with a known function callee,
|
||||
// and a known prototype.
|
||||
|
|
|
|||
|
|
@ -3402,8 +3402,7 @@ js::StringConstructor(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
if (args.isConstructing()) {
|
||||
RootedObject proto(cx);
|
||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
StringObject* strobj = StringObject::create(cx, str, proto);
|
||||
|
|
|
|||
|
|
@ -290,7 +290,7 @@ static bool Error(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
// ES6 19.5.1.1 mandates the .prototype lookup happens before the toString
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
auto* obj = CreateErrorObject(cx, args, 0, exnType, proto);
|
||||
|
|
@ -341,7 +341,7 @@ static bool AggregateError(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
// Steps 1-2. (9.1.13 OrdinaryCreateFromConstructor, steps 1-2).
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) {
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -254,8 +254,7 @@ SharedArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value*
|
|||
}
|
||||
|
||||
RootedObject proto(cx);
|
||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
JSObject* bufobj = New(cx, length, proto);
|
||||
|
|
|
|||
|
|
@ -347,20 +347,6 @@ NewArray(JSContext* cx, uint32_t nelements);
|
|||
|
||||
namespace {
|
||||
|
||||
// We allow nullptr for newTarget for all the creation methods, to allow for
|
||||
// JSFriendAPI functions that don't care about subclassing
|
||||
static bool
|
||||
GetPrototypeForInstance(JSContext* cx, HandleObject newTarget, MutableHandleObject proto)
|
||||
{
|
||||
if (newTarget) {
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, proto))
|
||||
return false;
|
||||
} else {
|
||||
proto.set(nullptr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
enum class SpeciesConstructorOverride {
|
||||
None,
|
||||
ArrayBuffer
|
||||
|
|
@ -497,7 +483,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
|||
// the time, though, that [[Prototype]] will not be interesting. If
|
||||
// it isn't, we can do some more TI optimizations.
|
||||
RootedObject checkProto(cx);
|
||||
if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &checkProto))
|
||||
if (proto && !GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &checkProto))
|
||||
return nullptr;
|
||||
|
||||
AutoSetNewObjectMetadata metadata(cx);
|
||||
|
|
@ -753,28 +739,28 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
|||
if (!ToIndex(cx, args.get(0), JSMSG_BAD_ARRAY_LENGTH, &len))
|
||||
return nullptr;
|
||||
|
||||
return fromLength(cx, len, newTarget);
|
||||
// 22.2.4.1, step 3 and 22.2.4.2, step 5.
|
||||
// 22.2.4.2.1 AllocateTypedArray, step 1.
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return nullptr;
|
||||
|
||||
return fromLength(cx, len, proto);
|
||||
}
|
||||
|
||||
RootedObject dataObj(cx, &args[0].toObject());
|
||||
|
||||
/*
|
||||
* (typedArray)
|
||||
* (sharedTypedArray)
|
||||
* (type[] array)
|
||||
*
|
||||
* Otherwise create a new typed array and copy elements 0..len-1
|
||||
* properties from the object, treating it as some sort of array.
|
||||
* Note that offset and length will be ignored. Note that a
|
||||
* shared array's values are copied here.
|
||||
*/
|
||||
if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObjectMaybeShared>())
|
||||
return fromArray(cx, dataObj, newTarget);
|
||||
// 22.2.4.1, step 3 and 22.2.4.2, step 5.
|
||||
// 22.2.4.2.1 AllocateTypedArray, step 1.
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
|
||||
return nullptr;
|
||||
|
||||
/* (ArrayBuffer, [byteOffset, [length]]) */
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||
return nullptr;
|
||||
|
||||
if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObjectMaybeShared>())
|
||||
return fromArray(cx, dataObj, proto);
|
||||
|
||||
// 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
|
||||
|
||||
int32_t byteOffset = 0;
|
||||
if (args.hasDefined(1)) {
|
||||
|
|
@ -956,11 +942,9 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
|||
}
|
||||
|
||||
static JSObject*
|
||||
fromLength(JSContext* cx, uint64_t nelements, HandleObject newTarget = nullptr)
|
||||
fromLength(JSContext* cx, uint64_t nelements, HandleObject proto = nullptr)
|
||||
{
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeForInstance(cx, newTarget, &proto))
|
||||
return nullptr;
|
||||
// 22.2.4.1, step 3 and 22.2.4.2, step 5 (call AllocateTypedArray).
|
||||
|
||||
if (nelements > UINT32_MAX) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
|
||||
|
|
@ -986,13 +970,13 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
|||
MutableHandle<ArrayBufferObject*> buffer);
|
||||
|
||||
static JSObject*
|
||||
fromArray(JSContext* cx, HandleObject other, HandleObject newTarget = nullptr);
|
||||
fromArray(JSContext* cx, HandleObject other, HandleObject proto = nullptr);
|
||||
|
||||
static JSObject*
|
||||
fromTypedArray(JSContext* cx, HandleObject other, bool isWrapped, HandleObject newTarget);
|
||||
fromTypedArray(JSContext* cx, HandleObject other, bool isWrapped, HandleObject proto);
|
||||
|
||||
static JSObject*
|
||||
fromObject(JSContext* cx, HandleObject other, HandleObject newTarget);
|
||||
fromObject(JSContext* cx, HandleObject other, HandleObject proto);
|
||||
|
||||
static const NativeType
|
||||
getIndex(JSObject* obj, uint32_t index)
|
||||
|
|
@ -1259,17 +1243,17 @@ TypedArrayObjectTemplate<T>::CloneArrayBufferNoCopy(JSContext* cx,
|
|||
template<typename T>
|
||||
/* static */ JSObject*
|
||||
TypedArrayObjectTemplate<T>::fromArray(JSContext* cx, HandleObject other,
|
||||
HandleObject newTarget /* = nullptr */)
|
||||
HandleObject proto /* = nullptr */)
|
||||
{
|
||||
// Allow nullptr newTarget for FriendAPI methods, which don't care about
|
||||
// Allow nullptr proto for FriendAPI methods, which don't care about
|
||||
// subclassing.
|
||||
if (other->is<TypedArrayObject>())
|
||||
return fromTypedArray(cx, other, /* wrapped= */ false, newTarget);
|
||||
return fromTypedArray(cx, other, /* wrapped= */ false, proto);
|
||||
|
||||
if (other->is<WrapperObject>() && UncheckedUnwrap(other)->is<TypedArrayObject>())
|
||||
return fromTypedArray(cx, other, /* wrapped= */ true, newTarget);
|
||||
return fromTypedArray(cx, other, /* wrapped= */ true, proto);
|
||||
|
||||
return fromObject(cx, other, newTarget);
|
||||
return fromObject(cx, other, proto);
|
||||
}
|
||||
|
||||
// ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210
|
||||
|
|
@ -1277,7 +1261,7 @@ TypedArrayObjectTemplate<T>::fromArray(JSContext* cx, HandleObject other,
|
|||
template<typename T>
|
||||
/* static */ JSObject*
|
||||
TypedArrayObjectTemplate<T>::fromTypedArray(JSContext* cx, HandleObject other, bool isWrapped,
|
||||
HandleObject newTarget)
|
||||
HandleObject proto)
|
||||
{
|
||||
// Step 1.
|
||||
MOZ_ASSERT_IF(!isWrapped, other->is<TypedArrayObject>());
|
||||
|
|
@ -1285,12 +1269,9 @@ TypedArrayObjectTemplate<T>::fromTypedArray(JSContext* cx, HandleObject other, b
|
|||
other->is<WrapperObject>() &&
|
||||
UncheckedUnwrap(other)->is<TypedArrayObject>());
|
||||
|
||||
// Step 2 (done in caller).
|
||||
// Step 2 (Already performed in caller).
|
||||
|
||||
// Step 4 (partially).
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeForInstance(cx, newTarget, &proto))
|
||||
return nullptr;
|
||||
// Step 4 (Allocation deferred until later).
|
||||
|
||||
// Step 5.
|
||||
Rooted<TypedArrayObject*> srcArray(cx);
|
||||
|
|
@ -1406,14 +1387,11 @@ IsOptimizableInit(JSContext* cx, HandleObject iterable, bool* optimized)
|
|||
// 22.2.4.4 TypedArray ( object )
|
||||
template<typename T>
|
||||
/* static */ JSObject*
|
||||
TypedArrayObjectTemplate<T>::fromObject(JSContext* cx, HandleObject other, HandleObject newTarget)
|
||||
TypedArrayObjectTemplate<T>::fromObject(JSContext* cx, HandleObject other, HandleObject proto)
|
||||
{
|
||||
// Steps 1-2 (Already performed in caller).
|
||||
|
||||
// Steps 3-4 (Allocation deferred until later).
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeForInstance(cx, newTarget, &proto))
|
||||
return nullptr;
|
||||
|
||||
bool optimized = false;
|
||||
if (!IsOptimizableInit(cx, other, &optimized))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue