Bug 1715007 - Part 1: Add testing function to retrieve all available locales. r=tcampbell

Add a testing function to retrieve all available locales in preparation to test
the changes in part two.

Differential Revision: https://phabricator.services.mozilla.com/D117016
This commit is contained in:
André Bargull 2021-06-18 09:22:13 +00:00
parent c121de17f1
commit 5fb308d38c
5 changed files with 182 additions and 1 deletions

View File

@ -40,6 +40,7 @@
#ifdef JS_HAS_INTL_API
# include "builtin/intl/CommonFunctions.h"
# include "builtin/intl/SharedIntlData.h"
#endif
#include "builtin/ModuleObject.h"
#include "builtin/Promise.h"
@ -7177,6 +7178,64 @@ static bool GetICUOptions(JSContext* cx, unsigned argc, Value* vp) {
return true;
}
static bool GetAvailableLocalesOf(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject callee(cx, &args.callee());
if (!args.requireAtLeast(cx, "getAvailableLocalesOf", 1)) {
return false;
}
HandleValue arg = args[0];
if (!arg.isString()) {
ReportUsageErrorASCII(cx, callee, "First argument must be a string");
return false;
}
ArrayObject* result;
#ifdef JS_HAS_INTL_API
using SupportedLocaleKind = js::intl::SharedIntlData::SupportedLocaleKind;
SupportedLocaleKind kind;
{
JSLinearString* typeStr = arg.toString()->ensureLinear(cx);
if (!typeStr) {
return false;
}
if (StringEqualsLiteral(typeStr, "Collator")) {
kind = SupportedLocaleKind::Collator;
} else if (StringEqualsLiteral(typeStr, "DateTimeFormat")) {
kind = SupportedLocaleKind::DateTimeFormat;
} else if (StringEqualsLiteral(typeStr, "DisplayNames")) {
kind = SupportedLocaleKind::DisplayNames;
} else if (StringEqualsLiteral(typeStr, "ListFormat")) {
kind = SupportedLocaleKind::ListFormat;
} else if (StringEqualsLiteral(typeStr, "NumberFormat")) {
kind = SupportedLocaleKind::NumberFormat;
} else if (StringEqualsLiteral(typeStr, "PluralRules")) {
kind = SupportedLocaleKind::PluralRules;
} else if (StringEqualsLiteral(typeStr, "RelativeTimeFormat")) {
kind = SupportedLocaleKind::RelativeTimeFormat;
} else {
ReportUsageErrorASCII(cx, callee, "Unsupported Intl constructor name");
return false;
}
}
intl::SharedIntlData& sharedIntlData = cx->runtime()->sharedIntlData.ref();
result = sharedIntlData.availableLocalesOf(cx, kind);
#else
result = NewDenseEmptyArray(cx);
#endif
if (!result) {
return false;
}
args.rval().setObject(*result);
return true;
}
static bool IsSmallFunction(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject callee(cx, &args.callee());
@ -8221,7 +8280,6 @@ JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE)
"sequence of ECMAScript execution completes. This is used for testing\n"
"WeakRefs.\n"),
JS_FN_HELP("numberToDouble", NumberToDouble, 1, 0,
"numberToDouble(number)",
" Return the input number as double-typed number."),
@ -8236,6 +8294,10 @@ JS_FN_HELP("getICUOptions", GetICUOptions, 0, 0,
" timezone: the ICU default time zone, e.g. 'America/Los_Angeles'\n"
" host-timezone: the host time zone, e.g. 'America/Los_Angeles'"),
JS_FN_HELP("getAvailableLocalesOf", GetAvailableLocalesOf, 0, 0,
"getAvailableLocalesOf(name)",
" Return an array of all available locales for the given Intl constuctor."),
JS_FN_HELP("isSmallFunction", IsSmallFunction, 1, 0,
"isSmallFunction(fun)",
" Returns true if a scripted function is small enough to be inlinable."),

View File

@ -17,6 +17,7 @@
#include <stdint.h>
#include <utility>
#include "builtin/Array.h"
#include "builtin/intl/CommonFunctions.h"
#include "builtin/intl/ScopedICUObject.h"
#include "builtin/intl/TimeZoneDataGenerated.h"
@ -31,7 +32,9 @@
#include "unicode/uloc.h"
#include "unicode/unum.h"
#include "unicode/utypes.h"
#include "vm/ArrayObject.h"
#include "vm/JSAtom.h"
#include "vm/JSContext.h"
#include "vm/StringType.h"
using js::HashNumber;
@ -484,6 +487,48 @@ bool js::intl::SharedIntlData::isSupportedLocale(JSContext* cx,
MOZ_CRASH("Invalid Intl constructor");
}
js::ArrayObject* js::intl::SharedIntlData::availableLocalesOf(
JSContext* cx, SupportedLocaleKind kind) {
if (!ensureSupportedLocales(cx)) {
return nullptr;
}
LocaleSet* localeSet = nullptr;
switch (kind) {
case SupportedLocaleKind::Collator:
localeSet = &collatorSupportedLocales;
break;
case SupportedLocaleKind::DateTimeFormat:
case SupportedLocaleKind::DisplayNames:
case SupportedLocaleKind::ListFormat:
case SupportedLocaleKind::NumberFormat:
case SupportedLocaleKind::PluralRules:
case SupportedLocaleKind::RelativeTimeFormat:
localeSet = &supportedLocales;
break;
default:
MOZ_CRASH("Invalid Intl constructor");
}
const uint32_t count = localeSet->count();
ArrayObject* result = NewDenseFullyAllocatedArray(cx, count);
if (!result) {
return nullptr;
}
result->setDenseInitializedLength(count);
uint32_t index = 0;
for (auto range = localeSet->iter(); !range.done(); range.next()) {
JSAtom* locale = range.get();
cx->markAtom(locale);
result->initDenseElement(index++, StringValue(locale));
}
MOZ_ASSERT(index == count);
return result;
}
#if DEBUG || MOZ_SYSTEM_ICU
bool js::intl::SharedIntlData::ensureUpperCaseFirstLocales(JSContext* cx) {
if (upperCaseFirstInitialized) {

View File

@ -26,6 +26,8 @@ class DateTimePatternGenerator;
namespace js {
class ArrayObject;
namespace intl {
/**
@ -245,6 +247,11 @@ class SharedIntlData {
JS::Handle<JSString*> locale,
bool* supported);
/**
* Returns all available locales for |kind|.
*/
ArrayObject* availableLocalesOf(JSContext* cx, SupportedLocaleKind kind);
private:
/**
* The case first parameter (BCP47 key "kf") allows to switch the order of

View File

@ -0,0 +1,41 @@
// |reftest| skip-if(!this.hasOwnProperty('Intl'))
if (typeof getAvailableLocalesOf === "undefined") {
var getAvailableLocalesOf = SpecialPowers.Cu.getJSTestingFunctions().getAvailableLocalesOf;
}
function IsIntlService(c) {
return typeof c === "function" &&
c.hasOwnProperty("prototype") &&
c.prototype.hasOwnProperty("resolvedOptions");
}
const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsIntlService);
// Test all Intl service constructors.
for (let intlConstructor of intlConstructors) {
// Retrieve all available locales of the given Intl service constructor.
let available = getAvailableLocalesOf(intlConstructor.name);
// "best fit" matchers could potentially return a different locale, so we only
// test with "lookup" locale matchers. (NB: We don't yet support "best fit"
// matchers.)
let options = {localeMatcher: "lookup"};
if (intlConstructor === Intl.DisplayNames) {
// Intl.DisplayNames can't be constructed without the "type" option.
options.type = "language";
}
for (let locale of available) {
let obj = new intlConstructor(locale, options);
let resolved = obj.resolvedOptions();
// If |locale| is an available locale, the "lookup" locale matcher should
// pick exactly that locale.
assertEq(resolved.locale, locale);
}
}
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

@ -0,0 +1,26 @@
// |reftest| skip-if(!this.hasOwnProperty('Intl'))
if (typeof getAvailableLocalesOf === "undefined") {
var getAvailableLocalesOf = SpecialPowers.Cu.getJSTestingFunctions().getAvailableLocalesOf;
}
function IsIntlService(c) {
return typeof c === "function" &&
c.hasOwnProperty("prototype") &&
c.prototype.hasOwnProperty("resolvedOptions");
}
const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsIntlService);
// Test all Intl service constructors.
for (let intlConstructor of intlConstructors) {
// Retrieve all available locales of the given Intl service constructor.
let available = getAvailableLocalesOf(intlConstructor.name);
// All available locales must be reported as supported.
let supported = intlConstructor.supportedLocalesOf(available);
assertEqArray(supported, available);
}
if (typeof reportCompare === "function")
reportCompare(true, true);