mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-07 08:18:41 +09:00
Merge remote-tracking branch 'origin/tracking' into custom
This commit is contained in:
commit
522a2a6c64
140 changed files with 88916 additions and 72964 deletions
|
|
@ -82,6 +82,7 @@ included_inclnames_to_ignore = set([
|
|||
'unicode/plurrule.h', # ICU
|
||||
'unicode/timezone.h', # ICU
|
||||
'unicode/ucal.h', # ICU
|
||||
'unicode/uchar.h', # ICU
|
||||
'unicode/uclean.h', # ICU
|
||||
'unicode/ucol.h', # ICU
|
||||
'unicode/udat.h', # ICU
|
||||
|
|
|
|||
|
|
@ -913,7 +913,7 @@ struct JSClass {
|
|||
// application.
|
||||
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
|
||||
#define JSCLASS_GLOBAL_SLOT_COUNT \
|
||||
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 47)
|
||||
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 50)
|
||||
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
||||
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
||||
#define JSCLASS_GLOBAL_FLAGS \
|
||||
|
|
|
|||
|
|
@ -130,6 +130,17 @@ class GCVector
|
|||
}
|
||||
};
|
||||
|
||||
// AllocPolicy is optional. It has a default value declared in TypeDecls.h
|
||||
template <typename T, typename AllocPolicy>
|
||||
class MOZ_STACK_CLASS StackGCVector : public GCVector<T, 8, AllocPolicy> {
|
||||
public:
|
||||
using Base = GCVector<T, 8, AllocPolicy>;
|
||||
|
||||
private:
|
||||
// Inherit constructor from GCVector.
|
||||
using Base::Base;
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
||||
namespace js {
|
||||
|
|
@ -191,7 +202,7 @@ class MutableWrappedPtrOperations<JS::GCVector<T, Capacity, AllocPolicy>, Wrappe
|
|||
void clearAndFree() { vec().clearAndFree(); }
|
||||
template<typename U> bool append(U&& aU) { return vec().append(mozilla::Forward<U>(aU)); }
|
||||
template<typename... Args> bool emplaceBack(Args&&... aArgs) {
|
||||
return vec().emplaceBack(mozilla::Forward<Args...>(aArgs...));
|
||||
return vec().emplaceBack(mozilla::Forward<Args>(aArgs)...);
|
||||
}
|
||||
template<typename U, size_t O, class BP>
|
||||
bool appendAll(const mozilla::Vector<U, O, BP>& aU) { return vec().appendAll(aU); }
|
||||
|
|
@ -223,6 +234,29 @@ class MutableWrappedPtrOperations<JS::GCVector<T, Capacity, AllocPolicy>, Wrappe
|
|||
void erase(T* aBegin, T* aEnd) { vec().erase(aBegin, aEnd); }
|
||||
};
|
||||
|
||||
template <typename Wrapper, typename T, typename AllocPolicy>
|
||||
class WrappedPtrOperations<JS::StackGCVector<T, AllocPolicy>, Wrapper> :
|
||||
public WrappedPtrOperations<typename JS::StackGCVector<T, AllocPolicy>::Base,
|
||||
Wrapper> {};
|
||||
|
||||
template <typename Wrapper, typename T, typename AllocPolicy>
|
||||
class MutableWrappedPtrOperations<JS::StackGCVector<T, AllocPolicy>, Wrapper> :
|
||||
public MutableWrappedPtrOperations<typename JS::StackGCVector<T, AllocPolicy>::Base,
|
||||
Wrapper> {};
|
||||
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
|
||||
// An automatically rooted GCVector for stack use.
|
||||
template <typename T>
|
||||
class RootedVector : public Rooted<StackGCVector<T>> {
|
||||
using Vec = StackGCVector<T>;
|
||||
using Base = Rooted<Vec>;
|
||||
|
||||
public:
|
||||
explicit RootedVector(JSContext* cx) : Base(cx, Vec(cx)) {}
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
#endif // js_GCVector_h
|
||||
|
|
|
|||
223
js/public/Result.h
Normal file
223
js/public/Result.h
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/*
|
||||
* `Result` is used as the return type of many SpiderMonkey functions that
|
||||
* can either succeed or fail. See "/mfbt/Result.h".
|
||||
*
|
||||
*
|
||||
* ## Which return type to use
|
||||
*
|
||||
* `Result` is for return values. Obviously, if you're writing a function that
|
||||
* can't fail, don't use Result. Otherwise:
|
||||
*
|
||||
* JS::Result<> - function can fail, doesn't return anything on success
|
||||
* (defaults to `JS::Result<JS::Ok, JS::Error&>`)
|
||||
* JS::Result<JS::OOM&> - like JS::Result<>, but fails only on OOM
|
||||
*
|
||||
* JS::Result<Data> - function can fail, returns Data on success
|
||||
* JS::Result<Data, JS::OOM&> - returns Data, fails only on OOM
|
||||
*
|
||||
* mozilla::GenericErrorResult<JS::Error&> - always fails
|
||||
*
|
||||
* That last type is like a Result with no success type. It's used for
|
||||
* functions like `js::ReportNotFunction` that always return an error
|
||||
* result. `GenericErrorResult<E>` implicitly converts to `Result<V, E>`,
|
||||
* regardless of V.
|
||||
*
|
||||
*
|
||||
* ## Checking Results when your return type is Result
|
||||
*
|
||||
* When you call a function that returns a `Result`, use the `MOZ_TRY` macro to
|
||||
* check for errors:
|
||||
*
|
||||
* MOZ_TRY(DefenestrateObject(cx, obj));
|
||||
*
|
||||
* If `DefenestrateObject` returns a success result, `MOZ_TRY` is done, and
|
||||
* control flows to the next statement. If `DefenestrateObject` returns an
|
||||
* error result, `MOZ_TRY` will immediately return it, propagating the error to
|
||||
* your caller. It's kind of like exceptions, but more explicit -- you can see
|
||||
* in the code exactly where errors can happen.
|
||||
*
|
||||
* You can do a tail call instead of using `MOZ_TRY`:
|
||||
*
|
||||
* return DefenestrateObject(cx, obj);
|
||||
*
|
||||
* Indicate success with `return Ok();`.
|
||||
*
|
||||
* If the function returns a value on success, use `MOZ_TRY_VAR` to get it:
|
||||
*
|
||||
* RootedValue thrug(cx);
|
||||
* MOZ_TRY_VAR(thrug, GetObjectThrug(cx, obj));
|
||||
*
|
||||
* This behaves the same as `MOZ_TRY` on error. On success, the success
|
||||
* value of `GetObjectThrug(cx, obj)` is assigned to the variable `thrug`.
|
||||
*
|
||||
*
|
||||
* ## Checking Results when your return type is not Result
|
||||
*
|
||||
* This header defines alternatives to MOZ_TRY and MOZ_TRY_VAR for when you
|
||||
* need to call a `Result` function from a function that uses false or nullptr
|
||||
* to indicate errors:
|
||||
*
|
||||
* JS_TRY_OR_RETURN_FALSE(cx, DefenestrateObject(cx, obj));
|
||||
* JS_TRY_VAR_OR_RETURN_FALSE(cx, v, GetObjectThrug(cx, obj));
|
||||
*
|
||||
* JS_TRY_OR_RETURN_NULL(cx, DefenestrateObject(cx, obj));
|
||||
* JS_TRY_VAR_OR_RETURN_NULL(cx, v, GetObjectThrug(cx, obj));
|
||||
*
|
||||
* When TRY is not what you want, because you need to do some cleanup or
|
||||
* recovery on error, use this idiom:
|
||||
*
|
||||
* if (!cx->resultToBool(expr_that_is_a_Result)) {
|
||||
* ... your recovery code here ...
|
||||
* }
|
||||
*
|
||||
* In place of a tail call, you can use one of these methods:
|
||||
*
|
||||
* return cx->resultToBool(expr); // false on error
|
||||
* return cx->resultToPtr(expr); // null on error
|
||||
*
|
||||
* Once we are using `Result` everywhere, including in public APIs, all of
|
||||
* these will go away.
|
||||
*
|
||||
*
|
||||
* ## GC safety
|
||||
*
|
||||
* When a function returns a `JS::Result<JSObject*>`, it is the program's
|
||||
* responsibility to check for errors and root the object before continuing:
|
||||
*
|
||||
* RootedObject wrapper(cx);
|
||||
* MOZ_TRY_VAR(wrapper, Enwrapify(cx, thing));
|
||||
*
|
||||
* This is ideal. On error, there is no object to root; on success, the
|
||||
* assignment to wrapper roots it. GC safety is ensured.
|
||||
*
|
||||
* `Result` has methods .isOk(), .isErr(), .unwrap(), and .unwrapErr(), but if
|
||||
* you're actually using them, it's possible to create a GC hazard. The static
|
||||
* analysis will catch it if so, but that's hardly convenient. So try to stick
|
||||
* to the idioms shown above.
|
||||
*
|
||||
*
|
||||
* ## Future directions
|
||||
*
|
||||
* At present, JS::Error and JS::OOM are empty structs. The plan is to make them
|
||||
* GC things that contain the actual error information (including the exception
|
||||
* value and a saved stack).
|
||||
*
|
||||
* The long-term plan is to remove JS_IsExceptionPending and
|
||||
* JS_GetPendingException in favor of JS::Error. Exception state will no longer
|
||||
* exist.
|
||||
*/
|
||||
|
||||
#ifndef js_Result_h
|
||||
#define js_Result_h
|
||||
|
||||
#include "mozilla/Result.h"
|
||||
|
||||
struct JSContext;
|
||||
|
||||
/**
|
||||
* Evaluate the boolean expression expr. If it's true, do nothing.
|
||||
* If it's false, return an error result.
|
||||
*/
|
||||
#define JS_TRY_BOOL_TO_RESULT(cx, expr) \
|
||||
do { \
|
||||
bool ok_ = (expr); \
|
||||
if (!ok_) \
|
||||
return (cx)->boolToResult(ok_); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* JS_TRY_OR_RETURN_FALSE(cx, expr) runs expr to compute a Result value.
|
||||
* On success, nothing happens; on error, it returns false immediately.
|
||||
*
|
||||
* Implementation note: this involves cx because this may eventually
|
||||
* do the work of setting a pending exception or reporting OOM.
|
||||
*/
|
||||
#define JS_TRY_OR_RETURN_FALSE(cx, expr) \
|
||||
do { \
|
||||
auto tmpResult_ = (expr); \
|
||||
if (tmpResult_.isErr()) \
|
||||
return (cx)->resultToBool(tmpResult_); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Like JS_TRY_OR_RETURN_FALSE, but returning nullptr on error,
|
||||
* rather than false.
|
||||
*/
|
||||
#define JS_TRY_OR_RETURN_NULL(cx, expr) \
|
||||
do { \
|
||||
auto tmpResult_ = (expr); \
|
||||
if (tmpResult_.isErr()) { \
|
||||
JS_ALWAYS_FALSE((cx)->resultToBool(tmpResult_)); \
|
||||
return nullptr; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define JS_TRY_VAR_OR_RETURN_FALSE(cx, target, expr) \
|
||||
do { \
|
||||
auto tmpResult_ = (expr); \
|
||||
if (tmpResult_.isErr()) \
|
||||
return (cx)->resultToBool(tmpResult_); \
|
||||
(target) = tmpResult_.unwrap(); \
|
||||
} while (0)
|
||||
|
||||
#define JS_TRY_VAR_OR_RETURN_NULL(cx, target, expr) \
|
||||
do { \
|
||||
auto tmpResult_ = (expr); \
|
||||
if (tmpResult_.isErr()) { \
|
||||
JS_ALWAYS_FALSE((cx)->resultToBool(tmpResult_)); \
|
||||
return nullptr; \
|
||||
} \
|
||||
(target) = tmpResult_.unwrap(); \
|
||||
} while (0)
|
||||
|
||||
namespace JS {
|
||||
|
||||
using mozilla::Ok;
|
||||
|
||||
/**
|
||||
* Type representing a JS error or exception. At the moment this only "represents"
|
||||
* an error in a rather abstract way.
|
||||
*/
|
||||
struct Error
|
||||
{
|
||||
// Ensure sizeof(Error) > 1 so that Result<V, Error&> can use pointer
|
||||
// tagging.
|
||||
int dummy;
|
||||
};
|
||||
|
||||
struct OOM : public Error
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* `Result` is intended to be the return type of JSAPI calls and internal
|
||||
* functions that can run JS code or allocate memory from the JS GC heap. Such
|
||||
* functions can:
|
||||
*
|
||||
* - succeed, possibly returning a value;
|
||||
*
|
||||
* - fail with a JS exception (out-of-memory falls in this category); or
|
||||
*
|
||||
* - fail because JS execution was terminated, which occurs when e.g. a
|
||||
* user kills a script from the "slow script" UI. This is also how we
|
||||
* unwind the stack when the debugger forces the current function to
|
||||
* return. JS `catch` blocks can't catch this kind of failure,
|
||||
* and JS `finally` blocks don't execute.
|
||||
*/
|
||||
template <typename V = Ok, typename E = Error&>
|
||||
using Result = mozilla::Result<V, E>;
|
||||
|
||||
static_assert(sizeof(Result<>) == sizeof(uintptr_t),
|
||||
"Result<> should be pointer-sized");
|
||||
|
||||
static_assert(sizeof(Result<int*, Error&>) == sizeof(uintptr_t),
|
||||
"Result<V*, Error&> should be pointer-sized");
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif // js_Result_h
|
||||
|
|
@ -30,6 +30,10 @@ class JSAddonId;
|
|||
|
||||
struct jsid;
|
||||
|
||||
namespace js {
|
||||
class TempAllocPolicy;
|
||||
}; // namespace js
|
||||
|
||||
namespace JS {
|
||||
|
||||
typedef unsigned char Latin1Char;
|
||||
|
|
@ -40,6 +44,8 @@ template <typename T> class Handle;
|
|||
template <typename T> class MutableHandle;
|
||||
template <typename T> class Rooted;
|
||||
template <typename T> class PersistentRooted;
|
||||
template <typename T> class RootedVector;
|
||||
template <typename T, typename AllocPolicy = js::TempAllocPolicy> class StackGCVector;
|
||||
|
||||
typedef Handle<JSFunction*> HandleFunction;
|
||||
typedef Handle<jsid> HandleId;
|
||||
|
|
@ -48,6 +54,7 @@ typedef Handle<JSScript*> HandleScript;
|
|||
typedef Handle<JSString*> HandleString;
|
||||
typedef Handle<JS::Symbol*> HandleSymbol;
|
||||
typedef Handle<Value> HandleValue;
|
||||
typedef Handle<StackGCVector<Value>> HandleValueVector;
|
||||
|
||||
typedef MutableHandle<JSFunction*> MutableHandleFunction;
|
||||
typedef MutableHandle<jsid> MutableHandleId;
|
||||
|
|
@ -56,6 +63,7 @@ typedef MutableHandle<JSScript*> MutableHandleScript;
|
|||
typedef MutableHandle<JSString*> MutableHandleString;
|
||||
typedef MutableHandle<JS::Symbol*> MutableHandleSymbol;
|
||||
typedef MutableHandle<Value> MutableHandleValue;
|
||||
typedef MutableHandle<StackGCVector<Value>> MutableHandleValueVector;
|
||||
|
||||
typedef Rooted<JSObject*> RootedObject;
|
||||
typedef Rooted<JSFunction*> RootedFunction;
|
||||
|
|
@ -65,6 +73,8 @@ typedef Rooted<JS::Symbol*> RootedSymbol;
|
|||
typedef Rooted<jsid> RootedId;
|
||||
typedef Rooted<JS::Value> RootedValue;
|
||||
|
||||
typedef RootedVector<JS::Value> RootedValueVector;
|
||||
|
||||
typedef PersistentRooted<JSFunction*> PersistentRootedFunction;
|
||||
typedef PersistentRooted<jsid> PersistentRootedId;
|
||||
typedef PersistentRooted<JSObject*> PersistentRootedObject;
|
||||
|
|
@ -73,6 +83,11 @@ typedef PersistentRooted<JSString*> PersistentRootedString;
|
|||
typedef PersistentRooted<JS::Symbol*> PersistentRootedSymbol;
|
||||
typedef PersistentRooted<Value> PersistentRootedValue;
|
||||
|
||||
|
||||
template <typename T>
|
||||
using HandleVector = Handle<StackGCVector<T>>;
|
||||
template <typename T>
|
||||
using MutableHandleVector = MutableHandle<StackGCVector<T>>;
|
||||
} // namespace JS
|
||||
|
||||
#endif /* js_TypeDecls_h */
|
||||
|
|
|
|||
|
|
@ -82,10 +82,18 @@ using JS::UTF8CharsZ;
|
|||
using JS::UniqueChars;
|
||||
using JS::UniqueTwoByteChars;
|
||||
|
||||
using JS::Result;
|
||||
using JS::Ok;
|
||||
using JS::OOM;
|
||||
|
||||
using JS::AutoValueVector;
|
||||
using JS::AutoIdVector;
|
||||
using JS::AutoObjectVector;
|
||||
|
||||
using JS::RootedValueVector;
|
||||
using JS::HandleValueVector;
|
||||
using JS::MutableHandleValueVector;
|
||||
|
||||
using JS::ValueVector;
|
||||
using JS::IdVector;
|
||||
using JS::ScriptVector;
|
||||
|
|
|
|||
|
|
@ -195,17 +195,42 @@ function ArrayStaticSome(list, callbackfn/*, thisArg*/) {
|
|||
return callFunction(ArraySome, list, callbackfn, T);
|
||||
}
|
||||
|
||||
/* ES6 draft 2016-1-15 22.1.3.25 Array.prototype.sort (comparefn) */
|
||||
// ES2018 draft rev 3bbc87cd1b9d3bf64c3e68ca2fe9c5a3f2c304c0
|
||||
// 22.1.3.25 Array.prototype.sort ( comparefn )
|
||||
function ArraySort(comparefn) {
|
||||
/* Step 1. */
|
||||
var O = ToObject(this);
|
||||
if (comparefn !== undefined) {
|
||||
if (!IsCallable(comparefn)) {
|
||||
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, comparefn));
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 2. */
|
||||
var O = ToObject(this);
|
||||
|
||||
/* Step 3. */
|
||||
var len = ToLength(O.length);
|
||||
|
||||
if (len <= 1)
|
||||
return this;
|
||||
|
||||
if (comparefn === undefined) {
|
||||
// {Goanna} This implementation slightly breaks the standard. The default
|
||||
// comparator function depends on the type of items in the Array
|
||||
// (Strings, Numbers, etc.) and can be literal, lexicograpic, numeric,
|
||||
// lexicograpic-number...
|
||||
// Mozilla implements this correctly only in the native implementation.
|
||||
// Note that this must be stable regardless of casting, so we can only
|
||||
// use one of > or <, as the other may involve weird equality.
|
||||
comparefn = function(x, y) {
|
||||
/* Step 4.a. */
|
||||
if (x == y)
|
||||
return 0;
|
||||
if (x > y)
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* 22.1.3.25.1 Runtime Semantics: SortCompare( x, y ) */
|
||||
var wrappedCompareFn = comparefn;
|
||||
comparefn = function(x, y) {
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ function Date_toLocaleString() {
|
|||
}
|
||||
|
||||
// Step 7.
|
||||
return intl_FormatDateTime(dateTimeFormat, x, false);
|
||||
return intl_FormatDateTime(dateTimeFormat, x, /* formatToParts = */ false);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -137,7 +137,7 @@ function Date_toLocaleDateString() {
|
|||
}
|
||||
|
||||
// Step 7.
|
||||
return intl_FormatDateTime(dateTimeFormat, x, false);
|
||||
return intl_FormatDateTime(dateTimeFormat, x, /* formatToParts = */ false);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -170,5 +170,5 @@ function Date_toLocaleTimeString() {
|
|||
}
|
||||
|
||||
// Step 7.
|
||||
return intl_FormatDateTime(dateTimeFormat, x, false);
|
||||
return intl_FormatDateTime(dateTimeFormat, x, /* formatToParts = */ false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -974,8 +974,7 @@ IsTrailSurrogateWithLeadSurrogate(JSContext* cx, HandleLinearString input, int32
|
|||
*/
|
||||
static RegExpRunStatus
|
||||
ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
|
||||
int32_t lastIndex,
|
||||
MatchPairs* matches, size_t* endIndex, RegExpStaticsUpdate staticsUpdate)
|
||||
int32_t lastIndex, MatchPairs* matches, size_t* endIndex)
|
||||
{
|
||||
/*
|
||||
* WARNING: Despite the presence of spec step comment numbers, this
|
||||
|
|
@ -990,14 +989,9 @@ ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
|
|||
if (!RegExpObject::getShared(cx, reobj, &re))
|
||||
return RegExpRunStatus_Error;
|
||||
|
||||
RegExpStatics* res;
|
||||
if (staticsUpdate == UpdateRegExpStatics) {
|
||||
res = GlobalObject::getRegExpStatics(cx, cx->global());
|
||||
if (!res)
|
||||
return RegExpRunStatus_Error;
|
||||
} else {
|
||||
res = nullptr;
|
||||
}
|
||||
RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global());
|
||||
if (!res)
|
||||
return RegExpRunStatus_Error;
|
||||
|
||||
RootedLinearString input(cx, string->ensureLinear(cx));
|
||||
if (!input)
|
||||
|
|
@ -1051,15 +1045,14 @@ ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
|
|||
* steps 3, 9-25, except 12.a.i, 12.c.i.1, 15.
|
||||
*/
|
||||
static bool
|
||||
RegExpMatcherImpl(JSContext* cx, HandleObject regexp, HandleString string,
|
||||
int32_t lastIndex, RegExpStaticsUpdate staticsUpdate, MutableHandleValue rval)
|
||||
RegExpMatcherImpl(JSContext* cx, HandleObject regexp, HandleString string, int32_t lastIndex,
|
||||
MutableHandleValue rval)
|
||||
{
|
||||
/* Execute regular expression and gather matches. */
|
||||
ScopedMatchPairs matches(&cx->tempLifoAlloc());
|
||||
|
||||
/* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex,
|
||||
&matches, nullptr, staticsUpdate);
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex, &matches, nullptr);
|
||||
if (status == RegExpRunStatus_Error)
|
||||
return false;
|
||||
|
||||
|
|
@ -1099,8 +1092,7 @@ js::RegExpMatcher(JSContext* cx, unsigned argc, Value* vp)
|
|||
return false;
|
||||
|
||||
/* Steps 3, 9-25, except 12.a.i, 12.c.i.1, 15. */
|
||||
return RegExpMatcherImpl(cx, regexp, string, lastIndex,
|
||||
UpdateRegExpStatics, args.rval());
|
||||
return RegExpMatcherImpl(cx, regexp, string, lastIndex, args.rval());
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1123,8 +1115,7 @@ js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
|||
return false;
|
||||
return CreateRegExpMatchResult(cx, *shared, input, *maybeMatches, output);
|
||||
}
|
||||
return RegExpMatcherImpl(cx, regexp, input, lastIndex,
|
||||
UpdateRegExpStatics, output);
|
||||
return RegExpMatcherImpl(cx, regexp, input, lastIndex, output);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1135,14 +1126,13 @@ js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
|||
*/
|
||||
static bool
|
||||
RegExpSearcherImpl(JSContext* cx, HandleObject regexp, HandleString string,
|
||||
int32_t lastIndex, RegExpStaticsUpdate staticsUpdate, int32_t* result)
|
||||
int32_t lastIndex, int32_t* result)
|
||||
{
|
||||
/* Execute regular expression and gather matches. */
|
||||
ScopedMatchPairs matches(&cx->tempLifoAlloc());
|
||||
|
||||
/* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex,
|
||||
&matches, nullptr, staticsUpdate);
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex, &matches, nullptr);
|
||||
if (status == RegExpRunStatus_Error)
|
||||
return false;
|
||||
|
||||
|
|
@ -1180,7 +1170,7 @@ js::RegExpSearcher(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
/* Steps 3, 9-25, except 12.a.i, 12.c.i.1, 15. */
|
||||
int32_t result = 0;
|
||||
if (!RegExpSearcherImpl(cx, regexp, string, lastIndex, UpdateRegExpStatics, &result))
|
||||
if (!RegExpSearcherImpl(cx, regexp, string, lastIndex, &result))
|
||||
return false;
|
||||
|
||||
args.rval().setInt32(result);
|
||||
|
|
@ -1203,23 +1193,7 @@ js::RegExpSearcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
|||
*result = CreateRegExpSearchResult(cx, *maybeMatches);
|
||||
return true;
|
||||
}
|
||||
return RegExpSearcherImpl(cx, regexp, input, lastIndex,
|
||||
UpdateRegExpStatics, result);
|
||||
}
|
||||
|
||||
bool
|
||||
js::regexp_exec_no_statics(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
MOZ_ASSERT(IsRegExpObject(args[0]));
|
||||
MOZ_ASSERT(args[1].isString());
|
||||
|
||||
RootedObject regexp(cx, &args[0].toObject());
|
||||
RootedString string(cx, args[1].toString());
|
||||
|
||||
return RegExpMatcherImpl(cx, regexp, string, 0,
|
||||
DontUpdateRegExpStatics, args.rval());
|
||||
return RegExpSearcherImpl(cx, regexp, input, lastIndex, result);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1245,8 +1219,7 @@ js::RegExpTester(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
/* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
|
||||
size_t endIndex = 0;
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex,
|
||||
nullptr, &endIndex, UpdateRegExpStatics);
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex, nullptr, &endIndex);
|
||||
|
||||
if (status == RegExpRunStatus_Error)
|
||||
return false;
|
||||
|
|
@ -1271,8 +1244,7 @@ js::RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
|||
MOZ_ASSERT(lastIndex >= 0);
|
||||
|
||||
size_t endIndexTmp = 0;
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, lastIndex,
|
||||
nullptr, &endIndexTmp, UpdateRegExpStatics);
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, lastIndex, nullptr, &endIndexTmp);
|
||||
|
||||
if (status == RegExpRunStatus_Success) {
|
||||
MOZ_ASSERT(endIndexTmp <= INT32_MAX);
|
||||
|
|
@ -1287,24 +1259,6 @@ js::RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
js::regexp_test_no_statics(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
MOZ_ASSERT(IsRegExpObject(args[0]));
|
||||
MOZ_ASSERT(args[1].isString());
|
||||
|
||||
RootedObject regexp(cx, &args[0].toObject());
|
||||
RootedString string(cx, args[1].toString());
|
||||
|
||||
size_t ignored = 0;
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, 0,
|
||||
nullptr, &ignored, DontUpdateRegExpStatics);
|
||||
args.rval().setBoolean(status == RegExpRunStatus_Success);
|
||||
return status != RegExpRunStatus_Error;
|
||||
}
|
||||
|
||||
static void
|
||||
GetParen(JSLinearString* matched, const JS::Value& capture, JSSubString* out)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,10 +18,6 @@ namespace js {
|
|||
JSObject*
|
||||
InitRegExpClass(JSContext* cx, HandleObject obj);
|
||||
|
||||
// Whether RegExp statics should be updated with the input and results of a
|
||||
// regular expression execution.
|
||||
enum RegExpStaticsUpdate { UpdateRegExpStatics, DontUpdateRegExpStatics };
|
||||
|
||||
/*
|
||||
* Legacy behavior of ExecuteRegExp(), which is baked into the JSAPI.
|
||||
*
|
||||
|
|
@ -71,22 +67,6 @@ intrinsic_GetStringDataProperty(JSContext* cx, unsigned argc, Value* vp);
|
|||
* The following functions are for use by self-hosted code.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Behaves like regexp.exec(string), but doesn't set RegExp statics.
|
||||
*
|
||||
* Usage: match = regexp_exec_no_statics(regexp, string)
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
regexp_exec_no_statics(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/*
|
||||
* Behaves like regexp.test(string), but doesn't set RegExp statics.
|
||||
*
|
||||
* Usage: does_match = regexp_test_no_statics(regexp, string)
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
regexp_test_no_statics(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/*
|
||||
* Behaves like RegExp(pattern, flags).
|
||||
* |pattern| should be a RegExp object, |flags| should be a raw integer value.
|
||||
|
|
|
|||
|
|
@ -731,6 +731,88 @@ function String_localeCompare(that) {
|
|||
return intl_CompareStrings(collator, S, That);
|
||||
}
|
||||
|
||||
/**
|
||||
* 13.1.2 String.prototype.toLocaleLowerCase ( [ locales ] )
|
||||
*
|
||||
* ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
|
||||
*/
|
||||
function String_toLocaleLowerCase() {
|
||||
// Step 1.
|
||||
RequireObjectCoercible(this);
|
||||
|
||||
// Step 2.
|
||||
var string = ToString(this);
|
||||
|
||||
// Handle the common cases (no locales argument or a single string
|
||||
// argument) first.
|
||||
var locales = arguments.length > 0 ? arguments[0] : undefined;
|
||||
var requestedLocale;
|
||||
if (locales === undefined) {
|
||||
// Steps 3, 6.
|
||||
requestedLocale = undefined;
|
||||
} else if (typeof locales === "string") {
|
||||
// Steps 3, 5.
|
||||
requestedLocale = intl_ValidateAndCanonicalizeLanguageTag(locales, false);
|
||||
} else {
|
||||
// Step 3.
|
||||
var requestedLocales = CanonicalizeLocaleList(locales);
|
||||
|
||||
// Steps 4-6.
|
||||
requestedLocale = requestedLocales.length > 0 ? requestedLocales[0] : undefined;
|
||||
}
|
||||
|
||||
// Trivial case: When the input is empty, directly return the empty string.
|
||||
if (string.length === 0)
|
||||
return "";
|
||||
|
||||
if (requestedLocale === undefined)
|
||||
requestedLocale = DefaultLocale();
|
||||
|
||||
// Steps 7-16.
|
||||
return intl_toLocaleLowerCase(string, requestedLocale);
|
||||
}
|
||||
|
||||
/**
|
||||
* 13.1.3 String.prototype.toLocaleUpperCase ( [ locales ] )
|
||||
*
|
||||
* ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
|
||||
*/
|
||||
function String_toLocaleUpperCase() {
|
||||
// Step 1.
|
||||
RequireObjectCoercible(this);
|
||||
|
||||
// Step 2.
|
||||
var string = ToString(this);
|
||||
|
||||
// Handle the common cases (no locales argument or a single string
|
||||
// argument) first.
|
||||
var locales = arguments.length > 0 ? arguments[0] : undefined;
|
||||
var requestedLocale;
|
||||
if (locales === undefined) {
|
||||
// Steps 3, 6.
|
||||
requestedLocale = undefined;
|
||||
} else if (typeof locales === "string") {
|
||||
// Steps 3, 5.
|
||||
requestedLocale = intl_ValidateAndCanonicalizeLanguageTag(locales, false);
|
||||
} else {
|
||||
// Step 3.
|
||||
var requestedLocales = CanonicalizeLocaleList(locales);
|
||||
|
||||
// Steps 4-6.
|
||||
requestedLocale = requestedLocales.length > 0 ? requestedLocales[0] : undefined;
|
||||
}
|
||||
|
||||
// Trivial case: When the input is empty, directly return the empty string.
|
||||
if (string.length === 0)
|
||||
return "";
|
||||
|
||||
if (requestedLocale === undefined)
|
||||
requestedLocale = DefaultLocale();
|
||||
|
||||
// Steps 7-16.
|
||||
return intl_toLocaleUpperCase(string, requestedLocale);
|
||||
}
|
||||
|
||||
/* ES6 Draft May 22, 2014 21.1.2.4 */
|
||||
function String_static_raw(callSite, ...substitutions) {
|
||||
// Step 1 (implicit).
|
||||
|
|
@ -1014,13 +1096,15 @@ _SetCanonicalName(String_static_trimEnd, "trimEnd");
|
|||
function String_static_toLocaleLowerCase(string) {
|
||||
if (arguments.length < 1)
|
||||
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.toLocaleLowerCase');
|
||||
return callFunction(std_String_toLocaleLowerCase, string);
|
||||
var locales = arguments.length > 1 ? arguments[1] : undefined;
|
||||
return callFunction(String_toLocaleLowerCase, string, locales);
|
||||
}
|
||||
|
||||
function String_static_toLocaleUpperCase(string) {
|
||||
if (arguments.length < 1)
|
||||
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.toLocaleUpperCase');
|
||||
return callFunction(std_String_toLocaleUpperCase, string);
|
||||
var locales = arguments.length > 1 ? arguments[1] : undefined;
|
||||
return callFunction(String_toLocaleUpperCase, string, locales);
|
||||
}
|
||||
|
||||
function String_static_normalize(string) {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ const JSFunctionSpec SymbolObject::staticMethods[] = {
|
|||
};
|
||||
|
||||
JSObject*
|
||||
SymbolObject::initClass(JSContext* cx, HandleObject obj)
|
||||
SymbolObject::initClass(JSContext* cx, HandleObject obj, bool defineMembers)
|
||||
{
|
||||
Handle<GlobalObject*> global = obj.as<GlobalObject>();
|
||||
|
||||
|
|
@ -66,25 +66,33 @@ SymbolObject::initClass(JSContext* cx, HandleObject obj)
|
|||
if (!ctor)
|
||||
return nullptr;
|
||||
|
||||
// Define the well-known symbol properties, such as Symbol.iterator.
|
||||
ImmutablePropertyNamePtr* names = cx->names().wellKnownSymbolNames();
|
||||
RootedValue value(cx);
|
||||
unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
|
||||
WellKnownSymbols* wks = cx->runtime()->wellKnownSymbols;
|
||||
for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
|
||||
value.setSymbol(wks->get(i));
|
||||
if (!NativeDefineProperty(cx, ctor, names[i], value, nullptr, nullptr, attrs))
|
||||
return nullptr;
|
||||
if (defineMembers) {
|
||||
// Define the well-known symbol properties, such as Symbol.iterator.
|
||||
ImmutablePropertyNamePtr* names = cx->names().wellKnownSymbolNames();
|
||||
RootedValue value(cx);
|
||||
unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
|
||||
WellKnownSymbols* wks = cx->runtime()->wellKnownSymbols;
|
||||
for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
|
||||
value.setSymbol(wks->get(i));
|
||||
if (!NativeDefineProperty(cx, ctor, names[i], value, nullptr, nullptr, attrs))
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto) ||
|
||||
!DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
|
||||
!DefineToStringTag(cx, proto, cx->names().Symbol) ||
|
||||
!DefinePropertiesAndFunctions(cx, ctor, nullptr, staticMethods) ||
|
||||
!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Symbol, ctor, proto))
|
||||
{
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||
return nullptr;
|
||||
|
||||
if (defineMembers) {
|
||||
if (!DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
|
||||
!DefineToStringTag(cx, proto, cx->names().Symbol) ||
|
||||
!DefinePropertiesAndFunctions(cx, ctor, nullptr, staticMethods))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Symbol, ctor, proto))
|
||||
return nullptr;
|
||||
return proto;
|
||||
}
|
||||
|
||||
|
|
@ -258,5 +266,11 @@ SymbolObject::descriptionGetter(JSContext* cx, unsigned argc, Value* vp)
|
|||
JSObject*
|
||||
js::InitSymbolClass(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
return SymbolObject::initClass(cx, obj);
|
||||
return SymbolObject::initClass(cx, obj, true);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::InitBareSymbolCtor(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
return SymbolObject::initClass(cx, obj, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class SymbolObject : public NativeObject
|
|||
|
||||
static const Class class_;
|
||||
|
||||
static JSObject* initClass(JSContext* cx, js::HandleObject obj);
|
||||
static JSObject* initClass(JSContext* cx, js::HandleObject obj, bool defineMembers);
|
||||
|
||||
/*
|
||||
* Creates a new Symbol object boxing the given primitive Symbol. The
|
||||
|
|
@ -63,6 +63,9 @@ class SymbolObject : public NativeObject
|
|||
extern JSObject*
|
||||
InitSymbolClass(JSContext* cx, HandleObject obj);
|
||||
|
||||
extern JSObject*
|
||||
InitBareSymbolCtor(JSContext* cx, HandleObject obj);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* builtin_SymbolObject_h */
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@
|
|||
// Do not create an alias to a self-hosted builtin, otherwise it will be cloned
|
||||
// twice.
|
||||
//
|
||||
// Symbol is a bare constructor without properties or methods.
|
||||
var std_Symbol = Symbol;
|
||||
// WeakMap is a bare constructor without properties or methods.
|
||||
var std_WeakMap = WeakMap;
|
||||
// StopIteration is a bare constructor without properties or methods.
|
||||
|
|
@ -78,12 +80,6 @@ MakeConstructible(Record, {});
|
|||
/********** Abstract operations defined in ECMAScript Language Specification **********/
|
||||
|
||||
|
||||
/* Spec: ECMAScript Language Specification, 5.1 edition, 8.12.6 and 11.8.7 */
|
||||
function HasProperty(o, p) {
|
||||
return p in o;
|
||||
}
|
||||
|
||||
|
||||
/* Spec: ECMAScript Language Specification, 5.1 edition, 9.2 and 11.4.9 */
|
||||
function ToBoolean(v) {
|
||||
return !!v;
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@
|
|||
#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"
|
||||
|
|
@ -24,7 +26,6 @@
|
|||
#include "jsobjinlines.h"
|
||||
|
||||
using namespace js;
|
||||
using js::intl::GetAvailableLocales;
|
||||
using js::intl::IcuLocale;
|
||||
using js::intl::ReportInternalError;
|
||||
using js::intl::SharedIntlData;
|
||||
|
|
@ -79,64 +80,36 @@ static const JSFunctionSpec collator_methods[] = {
|
|||
* ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
|
||||
*/
|
||||
static bool
|
||||
Collator(JSContext* cx, const CallArgs& args, bool construct)
|
||||
Collator(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
RootedObject obj(cx);
|
||||
// Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
|
||||
|
||||
// We're following ECMA-402 1st Edition when Collator is called because of
|
||||
// backward compatibility issues.
|
||||
// See https://github.com/tc39/ecma402/issues/57
|
||||
if (!construct) {
|
||||
// ES Intl 1st ed., 10.1.2.1 step 3
|
||||
JSObject* intl = GlobalObject::getOrCreateIntlObject(cx, cx->global());
|
||||
if (!intl)
|
||||
return false;
|
||||
RootedValue self(cx, args.thisv());
|
||||
if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
|
||||
// ES Intl 1st ed., 10.1.2.1 step 4
|
||||
obj = ToObject(cx, self);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
// ES Intl 1st ed., 10.1.2.1 step 5
|
||||
bool extensible;
|
||||
if (!IsExtensible(cx, obj, &extensible))
|
||||
return false;
|
||||
if (!extensible)
|
||||
return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
|
||||
} else {
|
||||
// ES Intl 1st ed., 10.1.2.1 step 3.a
|
||||
construct = true;
|
||||
}
|
||||
}
|
||||
if (construct) {
|
||||
// Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||
RootedObject proto(cx);
|
||||
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreateCollatorPrototype(cx, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
||||
obj = NewObjectWithGivenProto<CollatorObject>(cx, proto);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
obj->as<NativeObject>().setReservedSlot(CollatorObject::INTERNALS_SLOT, NullValue());
|
||||
obj->as<NativeObject>().setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(nullptr));
|
||||
}
|
||||
|
||||
RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
|
||||
RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
|
||||
|
||||
// Step 6.
|
||||
if (!intl::InitializeObject(cx, obj, cx->names().InitializeCollator, locales, options))
|
||||
// Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||
RootedObject proto(cx);
|
||||
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*obj);
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreateCollatorPrototype(cx, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<CollatorObject*> collator(cx, NewObjectWithGivenProto<CollatorObject>(cx, proto));
|
||||
if (!collator)
|
||||
return false;
|
||||
|
||||
collator->setReservedSlot(CollatorObject::INTERNALS_SLOT, NullValue());
|
||||
collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(nullptr));
|
||||
|
||||
RootedValue locales(cx, args.get(0));
|
||||
RootedValue options(cx, args.get(1));
|
||||
|
||||
// Step 6.
|
||||
if (!intl::InitializeObject(cx, collator, cx->names().InitializeCollator, locales, options))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*collator);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -144,7 +117,7 @@ static bool
|
|||
Collator(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return Collator(cx, args, args.isConstructing());
|
||||
return Collator(cx, args);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -153,9 +126,8 @@ js::intl_Collator(JSContext* cx, unsigned argc, Value* vp)
|
|||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
MOZ_ASSERT(!args.isConstructing());
|
||||
// intl_Collator is an intrinsic for self-hosted JavaScript, so it cannot
|
||||
// be used with "new", but it still has to be treated as a constructor.
|
||||
return Collator(cx, args, true);
|
||||
|
||||
return Collator(cx, args);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -163,15 +135,9 @@ js::CollatorObject::finalize(FreeOp* fop, JSObject* obj)
|
|||
{
|
||||
MOZ_ASSERT(fop->onMainThread());
|
||||
|
||||
// This is-undefined check shouldn't be necessary, but for internal
|
||||
// brokenness in object allocation code. For the moment, hack around it by
|
||||
// explicitly guarding against the possibility of the reserved slot not
|
||||
// containing a private. See bug 949220.
|
||||
const Value& slot = obj->as<NativeObject>().getReservedSlot(CollatorObject::UCOLLATOR_SLOT);
|
||||
if (!slot.isUndefined()) {
|
||||
if (UCollator* coll = static_cast<UCollator*>(slot.toPrivate()))
|
||||
ucol_close(coll);
|
||||
}
|
||||
const Value& slot = obj->as<CollatorObject>().getReservedSlot(CollatorObject::UCOLLATOR_SLOT);
|
||||
if (UCollator* coll = static_cast<UCollator*>(slot.toPrivate()))
|
||||
ucol_close(coll);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
|
|
@ -182,10 +148,9 @@ js::CreateCollatorPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObjec
|
|||
if (!ctor)
|
||||
return nullptr;
|
||||
|
||||
RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &CollatorObject::class_));
|
||||
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
proto->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(nullptr));
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||
return nullptr;
|
||||
|
|
@ -213,14 +178,6 @@ js::CreateCollatorPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObjec
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
RootedValue options(cx);
|
||||
if (!intl::CreateDefaultOptions(cx, &options))
|
||||
return nullptr;
|
||||
|
||||
// 10.2.1 and 10.3
|
||||
if (!intl::InitializeObject(cx, proto, cx->names().InitializeCollator, UndefinedHandleValue, options))
|
||||
return nullptr;
|
||||
|
||||
// 8.1
|
||||
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||
if (!DefineProperty(cx, Intl, cx->names().Collator, ctorValue, nullptr, nullptr, 0))
|
||||
|
|
@ -229,19 +186,6 @@ js::CreateCollatorPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObjec
|
|||
return proto;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_Collator_availableLocales(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 0);
|
||||
|
||||
RootedValue result(cx);
|
||||
if (!GetAvailableLocales(cx, ucol_countAvailable, ucol_getAvailable, &result))
|
||||
return false;
|
||||
args.rval().set(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
|
|
@ -271,6 +215,14 @@ js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp)
|
|||
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)) {
|
||||
|
|
@ -285,21 +237,11 @@ js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (StringsAreEqual(collation, "standard") || StringsAreEqual(collation, "search"))
|
||||
continue;
|
||||
|
||||
// ICU returns old-style keyword values; map them to BCP 47 equivalents
|
||||
// (see http://bugs.icu-project.org/trac/ticket/9620).
|
||||
if (StringsAreEqual(collation, "dictionary"))
|
||||
collation = "dict";
|
||||
else if (StringsAreEqual(collation, "gb2312han"))
|
||||
collation = "gb2312";
|
||||
else if (StringsAreEqual(collation, "phonebook"))
|
||||
collation = "phonebk";
|
||||
else if (StringsAreEqual(collation, "traditional"))
|
||||
collation = "trad";
|
||||
|
||||
RootedString jscollation(cx, JS_NewStringCopyZ(cx, collation));
|
||||
// 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;
|
||||
RootedValue element(cx, StringValue(jscollation));
|
||||
element = StringValue(jscollation);
|
||||
if (!DefineElement(cx, collations, index++, element))
|
||||
return false;
|
||||
}
|
||||
|
|
@ -313,7 +255,7 @@ js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp)
|
|||
* of the given Collator.
|
||||
*/
|
||||
static UCollator*
|
||||
NewUCollator(JSContext* cx, HandleObject collator)
|
||||
NewUCollator(JSContext* cx, Handle<CollatorObject*> collator)
|
||||
{
|
||||
RootedValue value(cx);
|
||||
|
||||
|
|
@ -343,32 +285,33 @@ NewUCollator(JSContext* cx, HandleObject collator)
|
|||
return nullptr;
|
||||
if (StringsAreEqual(usage, "search")) {
|
||||
// ICU expects search as a Unicode locale extension on locale.
|
||||
// Unicode locale extensions must occur before private use extensions.
|
||||
const char* oldLocale = locale.ptr();
|
||||
const char* p;
|
||||
size_t index;
|
||||
size_t localeLen = strlen(oldLocale);
|
||||
if ((p = strstr(oldLocale, "-x-")))
|
||||
index = p - oldLocale;
|
||||
else
|
||||
index = localeLen;
|
||||
|
||||
const char* insert;
|
||||
if ((p = strstr(oldLocale, "-u-")) && static_cast<size_t>(p - oldLocale) < index) {
|
||||
index = p - oldLocale + 2;
|
||||
insert = "-co-search";
|
||||
} else {
|
||||
insert = "-u-co-search";
|
||||
}
|
||||
size_t insertLen = strlen(insert);
|
||||
char* newLocale = cx->pod_malloc<char>(localeLen + insertLen + 1);
|
||||
if (!newLocale)
|
||||
intl::LanguageTag tag(cx);
|
||||
if (!intl::LanguageTagParser::parse(
|
||||
cx, mozilla::MakeCStringSpan(locale.ptr()), tag)) {
|
||||
return nullptr;
|
||||
memcpy(newLocale, oldLocale, index);
|
||||
memcpy(newLocale + index, insert, insertLen);
|
||||
memcpy(newLocale + index + insertLen, oldLocale + index, localeLen - index + 1); // '\0'
|
||||
}
|
||||
|
||||
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.initBytes(newLocale);
|
||||
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
|
||||
|
|
@ -417,8 +360,10 @@ NewUCollator(JSContext* cx, HandleObject collator)
|
|||
uCaseFirst = UCOL_UPPER_FIRST;
|
||||
else if (StringsAreEqual(caseFirst, "lower"))
|
||||
uCaseFirst = UCOL_LOWER_FIRST;
|
||||
else
|
||||
else {
|
||||
MOZ_ASSERT(StringsAreEqual(caseFirst, "false"));
|
||||
uCaseFirst = UCOL_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
|
@ -490,40 +435,38 @@ js::intl_CompareStrings(JSContext* cx, unsigned argc, Value* vp)
|
|||
MOZ_ASSERT(args[2].isString());
|
||||
|
||||
Rooted<CollatorObject*> collator(cx, &args[0].toObject().as<CollatorObject>());
|
||||
|
||||
// Obtain a UCollator object, cached if possible.
|
||||
|
||||
// Obtain a cached UCollator object.
|
||||
// XXX Does this handle Collator instances from other globals correctly?
|
||||
bool isCollatorInstance = collator->getClass() == &CollatorObject::class_;
|
||||
UCollator* coll;
|
||||
if (isCollatorInstance) {
|
||||
void* priv = collator->getReservedSlot(CollatorObject::UCOLLATOR_SLOT).toPrivate();
|
||||
coll = static_cast<UCollator*>(priv);
|
||||
if (!coll) {
|
||||
coll = NewUCollator(cx, collator);
|
||||
if (!coll)
|
||||
return false;
|
||||
collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(coll));
|
||||
}
|
||||
} else {
|
||||
// There's no good place to cache the ICU collator for an object
|
||||
// that has been initialized as a Collator but is not a Collator
|
||||
// instance. One possibility might be to add a Collator instance as an
|
||||
// internal property to each such object.
|
||||
void* priv = collator->getReservedSlot(CollatorObject::UCOLLATOR_SLOT).toPrivate();
|
||||
UCollator* coll = static_cast<UCollator*>(priv);
|
||||
if (!coll) {
|
||||
coll = NewUCollator(cx, collator);
|
||||
if (!coll)
|
||||
return false;
|
||||
collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(coll));
|
||||
}
|
||||
|
||||
// Use the UCollator to actually compare the strings.
|
||||
RootedString str1(cx, args[1].toString());
|
||||
RootedString str2(cx, args[2].toString());
|
||||
RootedValue result(cx);
|
||||
bool success = intl_CompareStrings(cx, coll, str1, str2, &result);
|
||||
return intl_CompareStrings(cx, coll, str1, str2, args.rval());
|
||||
}
|
||||
|
||||
if (!isCollatorInstance)
|
||||
ucol_close(coll);
|
||||
if (!success)
|
||||
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().set(result);
|
||||
|
||||
args.rval().setBoolean(isUpperFirst);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,17 +52,6 @@ CreateCollatorPrototype(JSContext* cx, JS::Handle<JSObject*> Intl,
|
|||
extern MOZ_MUST_USE bool
|
||||
intl_Collator(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Returns an object indicating the supported locales for collation
|
||||
* by having a true-valued property for each such locale with the
|
||||
* canonicalized language tag as the property name. The object has no
|
||||
* prototype.
|
||||
*
|
||||
* Usage: availableLocales = intl_Collator_availableLocales()
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_Collator_availableLocales(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Returns an array with the collation type identifiers per Unicode
|
||||
* Technical Standard 35, Unicode Locale Data Markup Language, for the
|
||||
|
|
@ -87,6 +76,15 @@ intl_availableCollations(JSContext* cx, unsigned argc, Value* vp);
|
|||
extern MOZ_MUST_USE bool
|
||||
intl_CompareStrings(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Returns true if the given locale sorts upper-case before lower-case
|
||||
* characters.
|
||||
*
|
||||
* Usage: result = intl_isUpperCaseFirst(locale)
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_isUpperCaseFirst(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
|
||||
} // namespace js
|
||||
|
||||
|
|
|
|||
|
|
@ -5,18 +5,6 @@
|
|||
/********** Intl.Collator **********/
|
||||
|
||||
|
||||
/**
|
||||
* Mapping from Unicode extension keys for collation to options properties,
|
||||
* their types and permissible values.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 10.1.1.
|
||||
*/
|
||||
var collatorKeyMappings = {
|
||||
kn: {property: "numeric", type: "boolean"},
|
||||
kf: {property: "caseFirst", type: "string", values: ["upper", "lower", "false"]}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Compute an internal properties object from |lazyCollatorData|.
|
||||
*/
|
||||
|
|
@ -26,80 +14,64 @@ function resolveCollatorInternals(lazyCollatorData)
|
|||
|
||||
var internalProps = std_Object_create(null);
|
||||
|
||||
// Step 7.
|
||||
internalProps.usage = lazyCollatorData.usage;
|
||||
|
||||
// Step 8.
|
||||
var Collator = collatorInternalProperties;
|
||||
|
||||
// Step 9.
|
||||
// Step 5.
|
||||
internalProps.usage = lazyCollatorData.usage;
|
||||
|
||||
// Steps 6-7.
|
||||
var collatorIsSorting = lazyCollatorData.usage === "sort";
|
||||
var localeData = collatorIsSorting
|
||||
? Collator.sortLocaleData
|
||||
: Collator.searchLocaleData;
|
||||
|
||||
// Compute effective locale.
|
||||
// Step 14.
|
||||
// Step 16.
|
||||
var relevantExtensionKeys = Collator.relevantExtensionKeys;
|
||||
|
||||
// Step 15.
|
||||
var r = ResolveLocale(callFunction(Collator.availableLocales, Collator),
|
||||
// Step 17.
|
||||
var r = ResolveLocale("Collator",
|
||||
lazyCollatorData.requestedLocales,
|
||||
lazyCollatorData.opt,
|
||||
relevantExtensionKeys,
|
||||
localeData);
|
||||
|
||||
// Step 16.
|
||||
// Step 18.
|
||||
internalProps.locale = r.locale;
|
||||
|
||||
// Steps 17-19.
|
||||
var key, property, value, mapping;
|
||||
var i = 0, len = relevantExtensionKeys.length;
|
||||
while (i < len) {
|
||||
// Step 19.a.
|
||||
key = relevantExtensionKeys[i];
|
||||
if (key === "co") {
|
||||
// Step 19.b.
|
||||
property = "collation";
|
||||
value = r.co === null ? "default" : r.co;
|
||||
} else {
|
||||
// Step 19.c.
|
||||
mapping = collatorKeyMappings[key];
|
||||
property = mapping.property;
|
||||
value = r[key];
|
||||
if (mapping.type === "boolean")
|
||||
value = value === "true";
|
||||
}
|
||||
// Step 19.
|
||||
var collation = r.co;
|
||||
|
||||
// Step 19.d.
|
||||
internalProps[property] = value;
|
||||
// Step 20.
|
||||
if (collation === null)
|
||||
collation = "default";
|
||||
|
||||
// Step 19.e.
|
||||
i++;
|
||||
}
|
||||
// Step 21.
|
||||
internalProps.collation = collation;
|
||||
|
||||
// Step 22.
|
||||
internalProps.numeric = r.kn === "true";
|
||||
|
||||
// Step 23.
|
||||
internalProps.caseFirst = r.kf;
|
||||
|
||||
// Compute remaining collation options.
|
||||
// Steps 21-22.
|
||||
// Step 25.
|
||||
var s = lazyCollatorData.rawSensitivity;
|
||||
if (s === undefined) {
|
||||
if (collatorIsSorting) {
|
||||
// Step 21.a.
|
||||
s = "variant";
|
||||
} else {
|
||||
// Step 21.b.
|
||||
var dataLocale = r.dataLocale;
|
||||
var dataLocaleData = localeData(dataLocale);
|
||||
s = dataLocaleData.sensitivity;
|
||||
}
|
||||
// In theory the default sensitivity for the "search" collator is
|
||||
// locale dependent; in reality the CLDR/ICU default strength is
|
||||
// always tertiary. Therefore use "variant" as the default value for
|
||||
// both collation modes.
|
||||
s = "variant";
|
||||
}
|
||||
|
||||
// Step 26.
|
||||
internalProps.sensitivity = s;
|
||||
|
||||
// Step 24.
|
||||
// Step 28.
|
||||
internalProps.ignorePunctuation = lazyCollatorData.ignorePunctuation;
|
||||
|
||||
// Step 25.
|
||||
internalProps.boundFormat = undefined;
|
||||
|
||||
// The caller is responsible for associating |internalProps| with the right
|
||||
// object using |setInternalProperties|.
|
||||
return internalProps;
|
||||
|
|
@ -107,11 +79,13 @@ function resolveCollatorInternals(lazyCollatorData)
|
|||
|
||||
|
||||
/**
|
||||
* Returns an object containing the Collator internal properties of |obj|, or
|
||||
* throws a TypeError if |obj| isn't Collator-initialized.
|
||||
* Returns an object containing the Collator internal properties of |obj|.
|
||||
*/
|
||||
function getCollatorInternals(obj, methodName) {
|
||||
var internals = getIntlObjectInternals(obj, "Collator", methodName);
|
||||
function getCollatorInternals(obj) {
|
||||
assert(IsObject(obj), "getCollatorInternals called with non-object");
|
||||
assert(IsCollator(obj), "getCollatorInternals called with non-Collator");
|
||||
|
||||
var internals = getIntlObjectInternals(obj);
|
||||
assert(internals.type === "Collator", "bad type escaped getIntlObjectInternals");
|
||||
|
||||
// If internal properties have already been computed, use them.
|
||||
|
|
@ -138,14 +112,8 @@ function getCollatorInternals(obj, methodName) {
|
|||
* Spec: ECMAScript Internationalization API Specification, 10.1.1.
|
||||
*/
|
||||
function InitializeCollator(collator, locales, options) {
|
||||
assert(IsObject(collator), "InitializeCollator");
|
||||
|
||||
// Step 1.
|
||||
if (isInitializedIntlObject(collator))
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
|
||||
|
||||
// Step 2.
|
||||
var internals = initializeIntlObject(collator);
|
||||
assert(IsObject(collator), "InitializeCollator called with non-object");
|
||||
assert(IsCollator(collator), "InitializeCollator called with non-Collator");
|
||||
|
||||
// Lazy Collator data has the following structure:
|
||||
//
|
||||
|
|
@ -167,11 +135,11 @@ function InitializeCollator(collator, locales, options) {
|
|||
// subset of them.
|
||||
var lazyCollatorData = std_Object_create(null);
|
||||
|
||||
// Step 3.
|
||||
// Step 1.
|
||||
var requestedLocales = CanonicalizeLocaleList(locales);
|
||||
lazyCollatorData.requestedLocales = requestedLocales;
|
||||
|
||||
// Steps 4-5.
|
||||
// Steps 2-3.
|
||||
//
|
||||
// If we ever need more speed here at startup, we should try to detect the
|
||||
// case where |options === undefined| and Object.prototype hasn't been
|
||||
|
|
@ -184,42 +152,43 @@ function InitializeCollator(collator, locales, options) {
|
|||
options = ToObject(options);
|
||||
|
||||
// Compute options that impact interpretation of locale.
|
||||
// Step 6.
|
||||
// Step 4.
|
||||
var u = GetOption(options, "usage", "string", ["sort", "search"], "sort");
|
||||
lazyCollatorData.usage = u;
|
||||
|
||||
// Step 10.
|
||||
// Step 8.
|
||||
var opt = new Record();
|
||||
lazyCollatorData.opt = opt;
|
||||
|
||||
// Steps 11-12.
|
||||
// Steps 9-10.
|
||||
var matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
|
||||
opt.localeMatcher = matcher;
|
||||
|
||||
// Step 13, unrolled.
|
||||
// Steps 11-13.
|
||||
var numericValue = GetOption(options, "numeric", "boolean", undefined, undefined);
|
||||
if (numericValue !== undefined)
|
||||
numericValue = numericValue ? 'true' : 'false';
|
||||
opt.kn = numericValue;
|
||||
|
||||
// Steps 14-15.
|
||||
var caseFirstValue = GetOption(options, "caseFirst", "string", ["upper", "lower", "false"], undefined);
|
||||
opt.kf = caseFirstValue;
|
||||
|
||||
// Compute remaining collation options.
|
||||
// Step 20.
|
||||
// Step 24.
|
||||
var s = GetOption(options, "sensitivity", "string",
|
||||
["base", "accent", "case", "variant"], undefined);
|
||||
lazyCollatorData.rawSensitivity = s;
|
||||
|
||||
// Step 23.
|
||||
// Step 27.
|
||||
var ip = GetOption(options, "ignorePunctuation", "boolean", undefined, false);
|
||||
lazyCollatorData.ignorePunctuation = ip;
|
||||
|
||||
// Step 26.
|
||||
// Step 29.
|
||||
//
|
||||
// We've done everything that must be done now: mark the lazy data as fully
|
||||
// computed and install it.
|
||||
setLazyData(internals, "Collator", lazyCollatorData);
|
||||
initializeIntlObject(collator, "Collator", lazyCollatorData);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -233,9 +202,13 @@ function InitializeCollator(collator, locales, options) {
|
|||
function Intl_Collator_supportedLocalesOf(locales /*, options*/) {
|
||||
var options = arguments.length > 1 ? arguments[1] : undefined;
|
||||
|
||||
var availableLocales = callFunction(collatorInternalProperties.availableLocales,
|
||||
collatorInternalProperties);
|
||||
// Step 1.
|
||||
var availableLocales = "Collator";
|
||||
|
||||
// Step 2.
|
||||
var requestedLocales = CanonicalizeLocaleList(locales);
|
||||
|
||||
// Step 3.
|
||||
return SupportedLocales(availableLocales, requestedLocales, options);
|
||||
}
|
||||
|
||||
|
|
@ -248,46 +221,106 @@ function Intl_Collator_supportedLocalesOf(locales /*, options*/) {
|
|||
var collatorInternalProperties = {
|
||||
sortLocaleData: collatorSortLocaleData,
|
||||
searchLocaleData: collatorSearchLocaleData,
|
||||
_availableLocales: null,
|
||||
availableLocales: function()
|
||||
{
|
||||
var locales = this._availableLocales;
|
||||
if (locales)
|
||||
return locales;
|
||||
|
||||
locales = intl_Collator_availableLocales();
|
||||
addSpecialMissingLanguageTags(locales);
|
||||
return (this._availableLocales = locales);
|
||||
},
|
||||
relevantExtensionKeys: ["co", "kn"]
|
||||
relevantExtensionKeys: ["co", "kn", "kf"]
|
||||
};
|
||||
|
||||
|
||||
function collatorSortLocaleData(locale) {
|
||||
var collations = intl_availableCollations(locale);
|
||||
callFunction(std_Array_unshift, collations, null);
|
||||
return {
|
||||
co: collations,
|
||||
kn: ["false", "true"]
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Returns the actual locale used when a collator for |locale| is constructed.
|
||||
*/
|
||||
function collatorActualLocale(locale) {
|
||||
assert(typeof locale === "string", "locale should be string");
|
||||
|
||||
|
||||
function collatorSearchLocaleData(locale) {
|
||||
return {
|
||||
co: [null],
|
||||
kn: ["false", "true"],
|
||||
// In theory the default sensitivity is locale dependent;
|
||||
// in reality the CLDR/ICU default strength is always tertiary.
|
||||
sensitivity: "variant"
|
||||
};
|
||||
// If |locale| is the default locale (e.g. da-DK), but only supported
|
||||
// through a fallback (da), we need to get the actual locale before we
|
||||
// can call intl_isUpperCaseFirst. Also see intl_BestAvailableLocale.
|
||||
return BestAvailableLocaleIgnoringDefault("Collator", locale);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function to be bound and returned by Intl.Collator.prototype.format.
|
||||
* Returns the default caseFirst values for the given locale. The first
|
||||
* element in the returned array denotes the default value per ES2017 Intl,
|
||||
* 9.1 Internal slots of Service Constructors.
|
||||
*/
|
||||
function collatorSortCaseFirst(locale) {
|
||||
var actualLocale = collatorActualLocale(locale);
|
||||
if (intl_isUpperCaseFirst(actualLocale))
|
||||
return ["upper", "false", "lower"];
|
||||
|
||||
// Default caseFirst values for all other languages.
|
||||
return ["false", "lower", "upper"];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the default caseFirst value for the given locale.
|
||||
*/
|
||||
function collatorSortCaseFirstDefault(locale) {
|
||||
var actualLocale = collatorActualLocale(locale);
|
||||
if (intl_isUpperCaseFirst(actualLocale))
|
||||
return "upper";
|
||||
|
||||
// Default caseFirst value for all other languages.
|
||||
return "false";
|
||||
}
|
||||
|
||||
function collatorSortLocaleData() {
|
||||
/* eslint-disable object-shorthand */
|
||||
return {
|
||||
co: intl_availableCollations,
|
||||
kn: function() {
|
||||
return ["false", "true"];
|
||||
},
|
||||
kf: collatorSortCaseFirst,
|
||||
default: {
|
||||
co: function() {
|
||||
// The first element of the collations array must be |null|
|
||||
// per ES2017 Intl, 10.2.3 Internal Slots.
|
||||
return null;
|
||||
},
|
||||
kn: function() {
|
||||
return "false";
|
||||
},
|
||||
kf: collatorSortCaseFirstDefault,
|
||||
}
|
||||
};
|
||||
/* eslint-enable object-shorthand */
|
||||
}
|
||||
|
||||
|
||||
function collatorSearchLocaleData() {
|
||||
/* eslint-disable object-shorthand */
|
||||
return {
|
||||
co: function() {
|
||||
return [null];
|
||||
},
|
||||
kn: function() {
|
||||
return ["false", "true"];
|
||||
},
|
||||
kf: function() {
|
||||
return ["false", "lower", "upper"];
|
||||
},
|
||||
default: {
|
||||
co: function() {
|
||||
return null;
|
||||
},
|
||||
kn: function() {
|
||||
return "false";
|
||||
},
|
||||
kf: function() {
|
||||
return "false";
|
||||
},
|
||||
}
|
||||
};
|
||||
/* eslint-enable object-shorthand */
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function to be bound and returned by Intl.Collator.prototype.compare.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 12.3.2.
|
||||
* Spec: ECMAScript Internationalization API Specification, 10.3.3.1.
|
||||
*/
|
||||
function collatorCompareToBind(x, y) {
|
||||
// Steps 1.a.i-ii implemented by ECMAScript declaration binding instantiation,
|
||||
|
|
@ -307,49 +340,60 @@ function collatorCompareToBind(x, y) {
|
|||
* than 0 if x > y according to the sort order for the locale and collation
|
||||
* options of this Collator object.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 10.3.2.
|
||||
* Spec: ECMAScript Internationalization API Specification, 10.3.3.
|
||||
*/
|
||||
function Intl_Collator_compare_get() {
|
||||
// Check "this Collator object" per introduction of section 10.3.
|
||||
var internals = getCollatorInternals(this, "compare");
|
||||
|
||||
// Step 1.
|
||||
if (internals.boundCompare === undefined) {
|
||||
// Step 1.a.
|
||||
var F = collatorCompareToBind;
|
||||
var collator = this;
|
||||
|
||||
// Step 1.b-d.
|
||||
var bc = callFunction(FunctionBind, F, this);
|
||||
internals.boundCompare = bc;
|
||||
// Steps 2-3.
|
||||
if (!IsObject(collator) || !IsCollator(collator))
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "Collator", "compare", "Collator");
|
||||
|
||||
var internals = getCollatorInternals(collator);
|
||||
|
||||
// Step 4.
|
||||
if (internals.boundCompare === undefined) {
|
||||
// Steps 4.a-b.
|
||||
var F = callFunction(FunctionBind, collatorCompareToBind, collator);
|
||||
|
||||
// Step 4.c.
|
||||
internals.boundCompare = F;
|
||||
}
|
||||
|
||||
// Step 2.
|
||||
// Step 5.
|
||||
return internals.boundCompare;
|
||||
}
|
||||
_SetCanonicalName(Intl_Collator_compare_get, "get compare");
|
||||
|
||||
|
||||
/**
|
||||
* Returns the resolved options for a Collator object.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 10.3.3 and 10.4.
|
||||
* Spec: ECMAScript Internationalization API Specification, 10.3.4.
|
||||
*/
|
||||
function Intl_Collator_resolvedOptions() {
|
||||
// Check "this Collator object" per introduction of section 10.3.
|
||||
var internals = getCollatorInternals(this, "resolvedOptions");
|
||||
// Step 1.
|
||||
var collator = this;
|
||||
|
||||
// Steps 2-3.
|
||||
if (!IsObject(collator) || !IsCollator(collator))
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "Collator", "resolvedOptions", "Collator");
|
||||
|
||||
var internals = getCollatorInternals(collator);
|
||||
|
||||
// Steps 4-5.
|
||||
var result = {
|
||||
locale: internals.locale,
|
||||
usage: internals.usage,
|
||||
sensitivity: internals.sensitivity,
|
||||
ignorePunctuation: internals.ignorePunctuation
|
||||
ignorePunctuation: internals.ignorePunctuation,
|
||||
collation: internals.collation,
|
||||
numeric: internals.numeric,
|
||||
caseFirst: internals.caseFirst,
|
||||
};
|
||||
|
||||
var relevantExtensionKeys = collatorInternalProperties.relevantExtensionKeys;
|
||||
for (var i = 0; i < relevantExtensionKeys.length; i++) {
|
||||
var key = relevantExtensionKeys[i];
|
||||
var property = (key === "co") ? "collation" : collatorKeyMappings[key].property;
|
||||
_DefineDataProperty(result, property, internals[property]);
|
||||
}
|
||||
// Step 6.
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,26 +19,10 @@
|
|||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
bool
|
||||
js::intl::CreateDefaultOptions(JSContext* cx, MutableHandleValue defaultOptions)
|
||||
{
|
||||
RootedObject options(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
|
||||
if (!options)
|
||||
return false;
|
||||
defaultOptions.setObject(*options);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl::InitializeObject(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
|
||||
HandleValue locales, HandleValue options)
|
||||
{
|
||||
RootedValue initializerValue(cx);
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), initializer, &initializerValue))
|
||||
return false;
|
||||
MOZ_ASSERT(initializerValue.isObject());
|
||||
MOZ_ASSERT(initializerValue.toObject().is<JSFunction>());
|
||||
|
||||
FixedInvokeArgs<3> args(cx);
|
||||
|
||||
args[0].setObject(*obj);
|
||||
|
|
@ -47,7 +31,33 @@ js::intl::InitializeObject(JSContext* cx, HandleObject obj, Handle<PropertyName*
|
|||
|
||||
RootedValue thisv(cx, NullValue());
|
||||
RootedValue ignored(cx);
|
||||
return js::Call(cx, initializerValue, thisv, args, &ignored);
|
||||
if (!js::CallSelfHostedFunction(cx, initializer, thisv, args, &ignored))
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(ignored.isUndefined(),
|
||||
"Unexpected return value from non-legacy Intl object initializer");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl::LegacyIntlInitialize(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
|
||||
HandleValue thisValue, HandleValue locales, HandleValue options,
|
||||
DateTimeFormatOptions dtfOptions, MutableHandleValue result)
|
||||
{
|
||||
FixedInvokeArgs<5> args(cx);
|
||||
|
||||
args[0].setObject(*obj);
|
||||
args[1].set(thisValue);
|
||||
args[2].set(locales);
|
||||
args[3].set(options);
|
||||
args[4].setBoolean(dtfOptions == DateTimeFormatOptions::EnableMozExtensions);
|
||||
|
||||
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 +66,12 @@ js::intl::InitializeObject(JSContext* cx, HandleObject obj, Handle<PropertyName*
|
|||
JSObject*
|
||||
js::intl::GetInternalsObject(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
RootedValue getInternalsValue(cx);
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().getInternals,
|
||||
&getInternalsValue))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(getInternalsValue.isObject());
|
||||
MOZ_ASSERT(getInternalsValue.toObject().is<JSFunction>());
|
||||
|
||||
FixedInvokeArgs<1> args(cx);
|
||||
|
||||
args[0].setObject(*obj);
|
||||
|
||||
RootedValue v(cx, NullValue());
|
||||
if (!js::Call(cx, getInternalsValue, v, args, &v))
|
||||
if (!js::CallSelfHostedFunction(cx, cx->names().getInternals, v, args, &v))
|
||||
return nullptr;
|
||||
|
||||
return &v.toObject();
|
||||
|
|
@ -82,34 +83,10 @@ js::intl::ReportInternalError(JSContext* cx)
|
|||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl::GetAvailableLocales(JSContext* cx, CountAvailable countAvailable,
|
||||
GetAvailable getAvailable, MutableHandleValue result)
|
||||
{
|
||||
RootedObject locales(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
|
||||
if (!locales)
|
||||
return false;
|
||||
const js::intl::OldStyleLanguageTagMapping
|
||||
js::intl::oldStyleLanguageTagMappings[] = {
|
||||
{"pa-PK", "pa-Arab-PK"}, {"zh-CN", "zh-Hans-CN"},
|
||||
{"zh-HK", "zh-Hant-HK"}, {"zh-SG", "zh-Hans-SG"},
|
||||
{"zh-TW", "zh-Hant-TW"},
|
||||
};
|
||||
|
||||
uint32_t count = countAvailable();
|
||||
RootedValue t(cx, BooleanValue(true));
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
const char* locale = getAvailable(i);
|
||||
auto lang = DuplicateString(cx, locale);
|
||||
if (!lang)
|
||||
return false;
|
||||
char* p;
|
||||
while ((p = strchr(lang.get(), '_')))
|
||||
*p = '-';
|
||||
RootedAtom a(cx, Atomize(cx, lang.get(), strlen(lang.get())));
|
||||
if (!a)
|
||||
return false;
|
||||
if (!DefineProperty(cx, locales, a->asPropertyName(), t, nullptr, nullptr,
|
||||
JSPROP_ENUMERATE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
result.setObject(*locales);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,12 +26,6 @@ namespace js {
|
|||
|
||||
namespace intl {
|
||||
|
||||
/**
|
||||
* Setup the |options| argument of |IntlInitialize|
|
||||
*/
|
||||
extern bool
|
||||
CreateDefaultOptions(JSContext* cx, MutableHandleValue defaultOptions);
|
||||
|
||||
/**
|
||||
* Initialize a new Intl.* object using the named self-hosted function.
|
||||
*/
|
||||
|
|
@ -39,6 +33,22 @@ extern bool
|
|||
InitializeObject(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
|
||||
HandleValue locales, HandleValue options);
|
||||
|
||||
/**
|
||||
* Initialize an existing object as an Intl.* object using the named
|
||||
* self-hosted function. This is only for a few old Intl.* constructors, for
|
||||
* legacy reasons -- new ones should use the function above instead.
|
||||
*/
|
||||
|
||||
enum class DateTimeFormatOptions
|
||||
{
|
||||
Standard,
|
||||
EnableMozExtensions,
|
||||
};
|
||||
|
||||
extern bool
|
||||
LegacyIntlInitialize(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
|
||||
HandleValue thisValue, HandleValue locales, HandleValue options,
|
||||
DateTimeFormatOptions dtfOptions, MutableHandleValue result);
|
||||
/**
|
||||
* Returns the object holding the internal properties for obj.
|
||||
*/
|
||||
|
|
@ -61,6 +71,32 @@ StringsAreEqual(JSAutoByteString& s1, const char* s2)
|
|||
return !strcmp(s1.ptr(), s2);
|
||||
}
|
||||
|
||||
/**
|
||||
* The last-ditch locale is used if none of the available locales satisfies a
|
||||
* request. "en-GB" is used based on the assumptions that English is the most
|
||||
* common second language, that both en-GB and en-US are normally available in
|
||||
* an implementation, and that en-GB is more representative of the English used
|
||||
* in other locales.
|
||||
*/
|
||||
static inline const char* LastDitchLocale() { return "en-GB"; }
|
||||
|
||||
/**
|
||||
* Certain old, commonly-used language tags that lack a script, are expected to
|
||||
* nonetheless imply one. This object maps these old-style tags to modern
|
||||
* equivalents.
|
||||
*/
|
||||
struct OldStyleLanguageTagMapping {
|
||||
const char* const oldStyle;
|
||||
const char* const modernStyle;
|
||||
|
||||
// Provide a constructor to catch missing initializers in the mappings array.
|
||||
constexpr OldStyleLanguageTagMapping(const char* oldStyle,
|
||||
const char* modernStyle)
|
||||
: oldStyle(oldStyle), modernStyle(modernStyle) {}
|
||||
};
|
||||
|
||||
extern const OldStyleLanguageTagMapping oldStyleLanguageTagMappings[5];
|
||||
|
||||
static inline const char*
|
||||
IcuLocale(const char* locale)
|
||||
{
|
||||
|
|
@ -79,9 +115,9 @@ static_assert(mozilla::IsSame<UChar, char16_t>::value,
|
|||
// buffer's entire inline capacity before growing it and heap-allocating.
|
||||
static const size_t INITIAL_CHAR_BUFFER_SIZE = 32;
|
||||
|
||||
template <typename ICUStringFunction, size_t InlineCapacity>
|
||||
template <typename ICUStringFunction, typename CharT, size_t InlineCapacity>
|
||||
static int32_t
|
||||
CallICU(JSContext* cx, Vector<char16_t, InlineCapacity>& chars, const ICUStringFunction& strFn)
|
||||
CallICU(JSContext* cx, Vector<CharT, InlineCapacity>& chars, const ICUStringFunction& strFn)
|
||||
{
|
||||
MOZ_ASSERT(chars.length() == 0);
|
||||
MOZ_ALWAYS_TRUE(chars.resize(InlineCapacity));
|
||||
|
|
@ -119,25 +155,6 @@ CallICU(JSContext* cx, const ICUStringFunction& strFn)
|
|||
return NewStringCopyN<CanGC>(cx, chars.begin(), size_t(size));
|
||||
}
|
||||
|
||||
// CountAvailable and GetAvailable describe the signatures used for ICU API
|
||||
// to determine available locales for various functionality.
|
||||
using CountAvailable = int32_t (*)();
|
||||
using GetAvailable = const char* (*)(int32_t localeIndex);
|
||||
|
||||
/**
|
||||
* Return an object whose own property names are the locales indicated as
|
||||
* available by |countAvailable| that provides an overall count, and by
|
||||
* |getAvailable| that when called passing a number less than that count,
|
||||
* returns the corresponding locale as a borrowed string. For example:
|
||||
*
|
||||
* RootedValue v(cx);
|
||||
* if (!GetAvailableLocales(cx, unum_countAvailable, unum_getAvailable, &v))
|
||||
* return false;
|
||||
*/
|
||||
extern bool
|
||||
GetAvailableLocales(JSContext* cx, CountAvailable countAvailable, GetAvailable getAvailable,
|
||||
JS::MutableHandle<JS::Value> result);
|
||||
|
||||
} // namespace intl
|
||||
|
||||
} // namespace js
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -9,12 +9,14 @@
|
|||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Range.h"
|
||||
#include "mozilla/Span.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jsfriendapi.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 "builtin/intl/TimeZoneDataGenerated.h"
|
||||
|
|
@ -33,7 +35,7 @@ using JS::ClippedTime;
|
|||
using JS::TimeClip;
|
||||
|
||||
using js::intl::CallICU;
|
||||
using js::intl::GetAvailableLocales;
|
||||
using js::intl::DateTimeFormatOptions;
|
||||
using js::intl::IcuLocale;
|
||||
using js::intl::INITIAL_CHAR_BUFFER_SIZE;
|
||||
using js::intl::SharedIntlData;
|
||||
|
|
@ -89,72 +91,58 @@ static const JSFunctionSpec dateTimeFormat_methods[] = {
|
|||
* ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
|
||||
*/
|
||||
static bool
|
||||
DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct)
|
||||
DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct, DateTimeFormatOptions dtfOptions)
|
||||
{
|
||||
RootedObject obj(cx);
|
||||
// Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
|
||||
|
||||
// We're following ECMA-402 1st Edition when DateTimeFormat is called
|
||||
// because of backward compatibility issues.
|
||||
// See https://github.com/tc39/ecma402/issues/57
|
||||
if (!construct) {
|
||||
// ES Intl 1st ed., 12.1.2.1 step 3
|
||||
JSObject* intl = GlobalObject::getOrCreateIntlObject(cx, cx->global());
|
||||
if (!intl)
|
||||
return false;
|
||||
RootedValue self(cx, args.thisv());
|
||||
if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
|
||||
// ES Intl 1st ed., 12.1.2.1 step 4
|
||||
obj = ToObject(cx, self);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
// ES Intl 1st ed., 12.1.2.1 step 5
|
||||
bool extensible;
|
||||
if (!IsExtensible(cx, obj, &extensible))
|
||||
return false;
|
||||
if (!extensible)
|
||||
return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
|
||||
} else {
|
||||
// ES Intl 1st ed., 12.1.2.1 step 3.a
|
||||
construct = true;
|
||||
}
|
||||
}
|
||||
if (construct) {
|
||||
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||
RootedObject proto(cx);
|
||||
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreateDateTimeFormatPrototype(cx, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
||||
obj = NewObjectWithGivenProto<DateTimeFormatObject>(cx, proto);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
obj->as<NativeObject>().setReservedSlot(DateTimeFormatObject::INTERNALS_SLOT, NullValue());
|
||||
obj->as<NativeObject>().setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT, PrivateValue(nullptr));
|
||||
}
|
||||
|
||||
RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
|
||||
RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
|
||||
|
||||
// Step 3.
|
||||
if (!intl::InitializeObject(cx, obj, cx->names().InitializeDateTimeFormat, locales, options))
|
||||
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||
RootedObject proto(cx);
|
||||
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*obj);
|
||||
return true;
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreateDateTimeFormatPrototype(cx, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<DateTimeFormatObject*> dateTimeFormat(cx);
|
||||
dateTimeFormat = NewObjectWithGivenProto<DateTimeFormatObject>(cx, proto);
|
||||
if (!dateTimeFormat)
|
||||
return false;
|
||||
|
||||
dateTimeFormat->setReservedSlot(DateTimeFormatObject::INTERNALS_SLOT, NullValue());
|
||||
dateTimeFormat->setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT,
|
||||
PrivateValue(nullptr));
|
||||
|
||||
RootedValue thisValue(cx, construct ? ObjectValue(*dateTimeFormat) : args.thisv());
|
||||
RootedValue locales(cx, args.get(0));
|
||||
RootedValue options(cx, args.get(1));
|
||||
|
||||
// Step 3.
|
||||
return intl::LegacyIntlInitialize(cx, dateTimeFormat, cx->names().InitializeDateTimeFormat,
|
||||
thisValue, locales, options, dtfOptions, args.rval());
|
||||
}
|
||||
|
||||
static bool
|
||||
DateTimeFormat(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return DateTimeFormat(cx, args, args.isConstructing());
|
||||
return DateTimeFormat(cx, args, args.isConstructing(), DateTimeFormatOptions::Standard);
|
||||
}
|
||||
|
||||
static bool
|
||||
MozDateTimeFormat(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
// Don't allow to call mozIntl.DateTimeFormat as a function. That way we
|
||||
// don't need to worry how to handle the legacy initialization semantics
|
||||
// when applied on mozIntl.DateTimeFormat.
|
||||
if (!ThrowIfNotConstructing(cx, args, "mozIntl.DateTimeFormat"))
|
||||
return false;
|
||||
|
||||
return DateTimeFormat(cx, args, true, DateTimeFormatOptions::EnableMozExtensions);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -166,7 +154,7 @@ js::intl_DateTimeFormat(JSContext* cx, unsigned argc, Value* vp)
|
|||
// intl_DateTimeFormat 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 DateTimeFormat(cx, args, true);
|
||||
return DateTimeFormat(cx, args, true, DateTimeFormatOptions::Standard);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -174,30 +162,25 @@ js::DateTimeFormatObject::finalize(FreeOp* fop, JSObject* obj)
|
|||
{
|
||||
MOZ_ASSERT(fop->onMainThread());
|
||||
|
||||
// This is-undefined check shouldn't be necessary, but for internal
|
||||
// brokenness in object allocation code. For the moment, hack around it by
|
||||
// explicitly guarding against the possibility of the reserved slot not
|
||||
// containing a private. See bug 949220.
|
||||
const Value& slot = obj->as<DateTimeFormatObject>().getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT);
|
||||
if (!slot.isUndefined()) {
|
||||
if (UDateFormat* df = static_cast<UDateFormat*>(slot.toPrivate()))
|
||||
udat_close(df);
|
||||
}
|
||||
if (UDateFormat* df = static_cast<UDateFormat*>(slot.toPrivate()))
|
||||
udat_close(df);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
|
||||
js::CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global,
|
||||
MutableHandleObject constructor, DateTimeFormatOptions dtfOptions)
|
||||
{
|
||||
RootedFunction ctor(cx);
|
||||
ctor = GlobalObject::createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0);
|
||||
ctor = dtfOptions == DateTimeFormatOptions::EnableMozExtensions
|
||||
? GlobalObject::createConstructor(cx, MozDateTimeFormat, cx->names().DateTimeFormat, 0)
|
||||
: GlobalObject::createConstructor(cx, DateTimeFormat, cx->names().DateTimeFormat, 0);
|
||||
if (!ctor)
|
||||
return nullptr;
|
||||
|
||||
RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global,
|
||||
&DateTimeFormatObject::class_));
|
||||
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
proto->setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT, PrivateValue(nullptr));
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||
return nullptr;
|
||||
|
|
@ -226,51 +209,60 @@ js::CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<Globa
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
RootedValue options(cx);
|
||||
if (!intl::CreateDefaultOptions(cx, &options))
|
||||
return nullptr;
|
||||
|
||||
// 12.2.1 and 12.3
|
||||
if (!intl::InitializeObject(cx, proto, cx->names().InitializeDateTimeFormat, UndefinedHandleValue,
|
||||
options))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 8.1
|
||||
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||
if (!DefineProperty(cx, Intl, cx->names().DateTimeFormat, ctorValue, nullptr, nullptr, 0))
|
||||
return nullptr;
|
||||
|
||||
constructor.set(ctor);
|
||||
return proto;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_DateTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp)
|
||||
js::AddMozDateTimeFormatConstructor(JSContext* cx, JS::Handle<JSObject*> intl)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 0);
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
|
||||
RootedValue result(cx);
|
||||
if (!GetAvailableLocales(cx, udat_countAvailable, udat_getAvailable, &result))
|
||||
RootedObject mozDateTimeFormat(cx);
|
||||
JSObject* mozDateTimeFormatProto =
|
||||
CreateDateTimeFormatPrototype(cx, intl, global, &mozDateTimeFormat, DateTimeFormatOptions::EnableMozExtensions);
|
||||
return mozDateTimeFormatProto != nullptr;
|
||||
}
|
||||
|
||||
static bool
|
||||
DefaultCalendar(JSContext* cx, const JSAutoByteString& locale, MutableHandleValue rval)
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UCalendar* cal = ucal_open(nullptr, 0, locale.ptr(), UCAL_DEFAULT, &status);
|
||||
|
||||
// This correctly handles nullptr |cal| when opening failed.
|
||||
ScopedICUObject<UCalendar, ucal_close> closeCalendar(cal);
|
||||
|
||||
const char* calendar = ucal_getType(cal, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||
return false;
|
||||
args.rval().set(result);
|
||||
}
|
||||
|
||||
// ICU returns old-style keyword values; map them to BCP 47 equivalents
|
||||
JSString* str = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("ca", calendar));
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
rval.setString(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ICU returns old-style keyword values; map them to BCP 47 equivalents
|
||||
// (see http://bugs.icu-project.org/trac/ticket/9620).
|
||||
static const char*
|
||||
bcp47CalendarName(const char* icuName)
|
||||
struct CalendarAlias
|
||||
{
|
||||
if (StringsAreEqual(icuName, "ethiopic-amete-alem"))
|
||||
return "ethioaa";
|
||||
if (StringsAreEqual(icuName, "gregorian"))
|
||||
return "gregory";
|
||||
if (StringsAreEqual(icuName, "islamic-civil"))
|
||||
return "islamicc";
|
||||
return icuName;
|
||||
}
|
||||
const char* const calendar;
|
||||
const char* const alias;
|
||||
};
|
||||
|
||||
const CalendarAlias calendarAliases[] = {
|
||||
{ "islamic-civil", "islamicc" },
|
||||
{ "ethioaa", "ethiopic-amete-alem" }
|
||||
};
|
||||
|
||||
bool
|
||||
js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
|
@ -289,30 +281,15 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
|
|||
uint32_t index = 0;
|
||||
|
||||
// We need the default calendar for the locale as the first result.
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
RootedString jscalendar(cx);
|
||||
{
|
||||
UCalendar* cal = ucal_open(nullptr, 0, locale.ptr(), UCAL_DEFAULT, &status);
|
||||
RootedValue element(cx);
|
||||
if (!DefaultCalendar(cx, locale, &element))
|
||||
return false;
|
||||
|
||||
// This correctly handles nullptr |cal| when opening failed.
|
||||
ScopedICUObject<UCalendar, ucal_close> closeCalendar(cal);
|
||||
|
||||
const char* calendar = ucal_getType(cal, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
intl::ReportInternalError(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
jscalendar = JS_NewStringCopyZ(cx, bcp47CalendarName(calendar));
|
||||
if (!jscalendar)
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue element(cx, StringValue(jscalendar));
|
||||
if (!DefineElement(cx, calendars, index++, element))
|
||||
return false;
|
||||
|
||||
// Now get the calendars that "would make a difference", i.e., not the default.
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UEnumeration* values = ucal_getKeywordValuesForLocale("ca", locale.ptr(), false, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
intl::ReportInternalError(cx);
|
||||
|
|
@ -333,18 +310,47 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
|
|||
return false;
|
||||
}
|
||||
|
||||
jscalendar = JS_NewStringCopyZ(cx, bcp47CalendarName(calendar));
|
||||
// ICU returns old-style keyword values; map them to BCP 47 equivalents
|
||||
calendar = uloc_toUnicodeLocaleType("ca", calendar);
|
||||
|
||||
JSString* jscalendar = JS_NewStringCopyZ(cx, calendar);
|
||||
if (!jscalendar)
|
||||
return false;
|
||||
element = StringValue(jscalendar);
|
||||
if (!DefineElement(cx, calendars, index++, element))
|
||||
return false;
|
||||
|
||||
// ICU doesn't return calendar aliases, append them here.
|
||||
for (const auto& calendarAlias : calendarAliases) {
|
||||
if (StringsAreEqual(calendar, calendarAlias.calendar)) {
|
||||
JSString* jscalendar = JS_NewStringCopyZ(cx, calendarAlias.alias);
|
||||
if (!jscalendar)
|
||||
return false;
|
||||
element = StringValue(jscalendar);
|
||||
if (!DefineElement(cx, calendars, index++, element))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
args.rval().setObject(*calendars);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_defaultCalendar(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;
|
||||
|
||||
return DefaultCalendar(cx, locale, args.rval());
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_IsValidTimeZoneName(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
|
|
@ -448,13 +454,139 @@ js::intl_defaultTimeZoneOffset(JSContext* cx, unsigned argc, Value* vp) {
|
|||
return true;
|
||||
}
|
||||
|
||||
enum class HourCycle {
|
||||
// 12 hour cycle, from 0 to 11.
|
||||
H11,
|
||||
|
||||
// 12 hour cycle, from 1 to 12.
|
||||
H12,
|
||||
|
||||
// 24 hour cycle, from 0 to 23.
|
||||
H23,
|
||||
|
||||
// 24 hour cycle, from 1 to 24.
|
||||
H24
|
||||
};
|
||||
|
||||
static bool
|
||||
IsHour12(HourCycle hc)
|
||||
{
|
||||
return hc == HourCycle::H11 || hc == HourCycle::H12;
|
||||
}
|
||||
|
||||
static char16_t
|
||||
HourSymbol(HourCycle hc)
|
||||
{
|
||||
switch (hc) {
|
||||
case HourCycle::H11:
|
||||
return 'K';
|
||||
case HourCycle::H12:
|
||||
return 'h';
|
||||
case HourCycle::H23:
|
||||
return 'H';
|
||||
case HourCycle::H24:
|
||||
return 'k';
|
||||
}
|
||||
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected hour cycle");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a pattern according to the format specified in
|
||||
* <https://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns>.
|
||||
*/
|
||||
template <typename CharT>
|
||||
class PatternIterator {
|
||||
CharT* iter_;
|
||||
const CharT* const end_;
|
||||
|
||||
public:
|
||||
explicit PatternIterator(mozilla::Span<CharT> pattern)
|
||||
: iter_(pattern.data()), end_(pattern.data() + pattern.size()) {}
|
||||
|
||||
CharT* next() {
|
||||
MOZ_ASSERT(iter_ != nullptr);
|
||||
|
||||
bool inQuote = false;
|
||||
while (iter_ < end_) {
|
||||
CharT* cur = iter_++;
|
||||
if (*cur == '\'') {
|
||||
inQuote = !inQuote;
|
||||
} else if (!inQuote) {
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
|
||||
iter_ = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the hour cycle for the given option string.
|
||||
*/
|
||||
static HourCycle
|
||||
HourCycleFromOption(JSLinearString* str)
|
||||
{
|
||||
if (StringEqualsAscii(str, "h11")) {
|
||||
return HourCycle::H11;
|
||||
}
|
||||
if (StringEqualsAscii(str, "h12")) {
|
||||
return HourCycle::H12;
|
||||
}
|
||||
if (StringEqualsAscii(str, "h23")) {
|
||||
return HourCycle::H23;
|
||||
}
|
||||
MOZ_ASSERT(StringEqualsAscii(str, "h24"));
|
||||
return HourCycle::H24;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hour cycle used in the input pattern or Nothing if none was found.
|
||||
*/
|
||||
static mozilla::Maybe<HourCycle>
|
||||
HourCycleFromPattern(mozilla::Span<const char16_t> pattern)
|
||||
{
|
||||
PatternIterator<const char16_t> iter(pattern);
|
||||
while (const auto* ptr = iter.next()) {
|
||||
switch (*ptr) {
|
||||
case 'K':
|
||||
return mozilla::Some(HourCycle::H11);
|
||||
case 'h':
|
||||
return mozilla::Some(HourCycle::H12);
|
||||
case 'H':
|
||||
return mozilla::Some(HourCycle::H23);
|
||||
case 'k':
|
||||
return mozilla::Some(HourCycle::H24);
|
||||
}
|
||||
}
|
||||
return mozilla::Nothing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all hour pattern characters in |pattern| to use the matching hour
|
||||
* representation for |hourCycle|.
|
||||
*/
|
||||
static void
|
||||
ReplaceHourSymbol(mozilla::Span<char16_t> pattern, HourCycle hc)
|
||||
{
|
||||
char16_t replacement = HourSymbol(hc);
|
||||
PatternIterator<char16_t> iter(pattern);
|
||||
while (auto* ptr = iter.next()) {
|
||||
char16_t ch = *ptr;
|
||||
if (ch == 'K' || ch == 'h' || ch == 'H' || ch == 'k') {
|
||||
*ptr = replacement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
MOZ_ASSERT(args.length() == 3);
|
||||
MOZ_ASSERT(args[0].isString());
|
||||
MOZ_ASSERT(args[1].isString());
|
||||
MOZ_ASSERT(args[2].isString() || args[2].isUndefined());
|
||||
|
||||
JSAutoByteString locale(cx, args[0].toString());
|
||||
if (!locale)
|
||||
|
|
@ -468,6 +600,16 @@ js::intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!stableChars.initTwoByte(cx, skeletonFlat))
|
||||
return false;
|
||||
|
||||
mozilla::Maybe<HourCycle> hourCycle;
|
||||
if (args[2].isString()) {
|
||||
JSLinearString* hourCycleStr = args[2].toString()->ensureLinear(cx);
|
||||
if (!hourCycleStr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hourCycle.emplace(HourCycleFromOption(hourCycleStr));
|
||||
}
|
||||
|
||||
mozilla::Range<const char16_t> skeletonChars = stableChars.twoByteRange();
|
||||
uint32_t skeletonLen = u_strlen(Char16ToUChar(skeletonChars.begin().get()));
|
||||
|
||||
|
|
@ -479,11 +621,220 @@ js::intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp)
|
|||
}
|
||||
ScopedICUObject<UDateTimePatternGenerator, udatpg_close> toClose(gen);
|
||||
|
||||
JSString* str =
|
||||
CallICU(cx, [gen, &skeletonChars, skeletonLen](UChar* chars, uint32_t size, UErrorCode* status) {
|
||||
return udatpg_getBestPattern(gen, skeletonChars.begin().get(), skeletonLen,
|
||||
chars, size, status);
|
||||
Vector<char16_t, intl::INITIAL_CHAR_BUFFER_SIZE> pattern(cx);
|
||||
|
||||
int32_t patternSize = CallICU(
|
||||
cx,
|
||||
pattern,
|
||||
[gen, &skeletonChars](UChar* chars, uint32_t size, UErrorCode* status) {
|
||||
return udatpg_getBestPattern(gen, skeletonChars.begin().get(),
|
||||
skeletonChars.length(), chars, size, status);
|
||||
});
|
||||
if (patternSize < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the hourCycle option was set, adjust the resolved pattern to use the
|
||||
// requested hour cycle representation.
|
||||
if (hourCycle) {
|
||||
ReplaceHourSymbol(pattern, hourCycle.value());
|
||||
}
|
||||
|
||||
JSString* str = NewStringCopyN<CanGC>(cx, pattern.begin(), pattern.length());
|
||||
if (!str) {
|
||||
return false;
|
||||
}
|
||||
args.rval().setString(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a matching pattern using the requested hour-12 options.
|
||||
*
|
||||
* This function is needed to work around the following two issues.
|
||||
* - https://unicode-org.atlassian.net/browse/ICU-21023
|
||||
* - https://unicode-org.atlassian.net/browse/CLDR-13425
|
||||
*
|
||||
* We're currently using a relatively simple workaround, which doesn't give the
|
||||
* most accurate results. For example:
|
||||
*
|
||||
* ```
|
||||
* var dtf = new Intl.DateTimeFormat("en", {
|
||||
* timeZone: "UTC",
|
||||
* dateStyle: "long",
|
||||
* timeStyle: "long",
|
||||
* hourCycle: "h12",
|
||||
* });
|
||||
* print(dtf.format(new Date("2020-01-01T00:00Z")));
|
||||
* ```
|
||||
*
|
||||
* Returns the pattern "MMMM d, y 'at' h:mm:ss a z", but when going through
|
||||
* |udatpg_getSkeleton| and then |udatpg_getBestPattern| to find an equivalent
|
||||
* pattern for "h23", we'll end up with the pattern "MMMM d, y, HH:mm:ss z", so
|
||||
* the combinator element " 'at' " was lost in the process.
|
||||
*/
|
||||
template <size_t N>
|
||||
static bool
|
||||
FindPatternWithHourCycle(JSContext* cx, const char* locale,
|
||||
Vector<char16_t, N>& pattern, bool hour12)
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UDateTimePatternGenerator* gen = udatpg_open(IcuLocale(locale), &status);
|
||||
if (U_FAILURE(status)) {
|
||||
intl::ReportInternalError(cx);
|
||||
return false;
|
||||
}
|
||||
ScopedICUObject<UDateTimePatternGenerator, udatpg_close> toClose(gen);
|
||||
|
||||
if (!gen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector<char16_t, intl::INITIAL_CHAR_BUFFER_SIZE> skeleton(cx);
|
||||
|
||||
int32_t skeletonSize = CallICU(
|
||||
cx,
|
||||
skeleton,
|
||||
[&pattern](UChar* chars, uint32_t size, UErrorCode* status) {
|
||||
return udatpg_getSkeleton(nullptr, pattern.begin(), pattern.length(),
|
||||
chars, size, status);
|
||||
});
|
||||
if (skeletonSize < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Input skeletons don't differentiate between "K" and "h" resp. "k" and "H".
|
||||
ReplaceHourSymbol(skeleton, hour12 ? HourCycle::H12 : HourCycle::H23);
|
||||
|
||||
MOZ_ALWAYS_TRUE(pattern.resize(0));
|
||||
|
||||
int32_t patternSize = CallICU(
|
||||
cx,
|
||||
pattern,
|
||||
[gen, &skeleton](UChar* chars, uint32_t size, UErrorCode* status) {
|
||||
return udatpg_getBestPattern(gen, skeleton.begin(), skeleton.length(),
|
||||
chars, size, status);
|
||||
});
|
||||
if (patternSize < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_patternForStyle(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 6);
|
||||
MOZ_ASSERT(args[0].isString());
|
||||
MOZ_ASSERT(args[1].isString() || args[1].isUndefined());
|
||||
MOZ_ASSERT(args[2].isString() || args[2].isUndefined());
|
||||
MOZ_ASSERT(args[3].isString());
|
||||
MOZ_ASSERT(args[4].isBoolean() || args[4].isUndefined());
|
||||
MOZ_ASSERT(args[5].isString() || args[5].isUndefined());
|
||||
|
||||
JSAutoByteString locale(cx, args[0].toString());
|
||||
if (!locale)
|
||||
return false;
|
||||
|
||||
auto toDateFormatStyle = [](JSLinearString* str) {
|
||||
if (StringEqualsAscii(str, "full")) {
|
||||
return UDAT_FULL;
|
||||
}
|
||||
if (StringEqualsAscii(str, "long")) {
|
||||
return UDAT_LONG;
|
||||
}
|
||||
if (StringEqualsAscii(str, "medium")) {
|
||||
return UDAT_MEDIUM;
|
||||
}
|
||||
MOZ_ASSERT(StringEqualsAscii(str, "short"));
|
||||
return UDAT_SHORT;
|
||||
};
|
||||
|
||||
UDateFormatStyle dateStyle = UDAT_NONE;
|
||||
|
||||
if (args[1].isString()) {
|
||||
JSLinearString* dateStyleStr = args[1].toString()->ensureLinear(cx);
|
||||
if (!dateStyleStr)
|
||||
return false;
|
||||
|
||||
dateStyle = toDateFormatStyle(dateStyleStr);
|
||||
}
|
||||
|
||||
UDateFormatStyle timeStyle = UDAT_NONE;
|
||||
if (args[2].isString()) {
|
||||
JSLinearString* timeStyleStr = args[2].toString()->ensureLinear(cx);
|
||||
if (!timeStyleStr)
|
||||
return false;
|
||||
|
||||
timeStyle = toDateFormatStyle(timeStyleStr);
|
||||
}
|
||||
|
||||
AutoStableStringChars timeZone(cx);
|
||||
if (!timeZone.initTwoByte(cx, args[3].toString()))
|
||||
return false;
|
||||
|
||||
mozilla::Maybe<bool> hour12;
|
||||
if (args[4].isBoolean()) {
|
||||
hour12.emplace(args[4].toBoolean());
|
||||
}
|
||||
|
||||
mozilla::Maybe<HourCycle> hourCycle;
|
||||
if (args[5].isString()) {
|
||||
JSLinearString* hourCycleStr = args[5].toString()->ensureLinear(cx);
|
||||
if (!hourCycleStr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hourCycle.emplace(HourCycleFromOption(hourCycleStr));
|
||||
}
|
||||
|
||||
mozilla::Range<const char16_t> timeZoneChars = timeZone.twoByteRange();
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UDateFormat* df = udat_open(timeStyle, dateStyle, IcuLocale(locale.ptr()),
|
||||
Char16ToUChar(timeZoneChars.begin().get()),
|
||||
timeZoneChars.length(), nullptr, -1, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||
return false;
|
||||
}
|
||||
ScopedICUObject<UDateFormat, udat_close> toClose(df);
|
||||
|
||||
Vector<char16_t, intl::INITIAL_CHAR_BUFFER_SIZE> pattern(cx);
|
||||
|
||||
int32_t patternSize = CallICU(
|
||||
cx,
|
||||
pattern,
|
||||
[df](UChar* chars, uint32_t size, UErrorCode* status) {
|
||||
return udat_toPattern(df, false, chars, size, status);
|
||||
});
|
||||
if (patternSize < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If a specific hour cycle was requested and this hour cycle doesn't match
|
||||
// the hour cycle used in the resolved pattern, find an equivalent pattern
|
||||
// with the correct hour cycle.
|
||||
if (timeStyle != UDAT_NONE && (hour12 || hourCycle)) {
|
||||
if (auto hcPattern = HourCycleFromPattern(pattern)) {
|
||||
bool wantHour12 = hour12 ? hour12.value() : IsHour12(hourCycle.value());
|
||||
if (wantHour12 != IsHour12(hcPattern.value())) {
|
||||
if (!FindPatternWithHourCycle(cx, locale.ptr(), pattern, wantHour12)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the hourCycle option was set, adjust the resolved pattern to use the
|
||||
// requested hour cycle representation.
|
||||
if (hourCycle) {
|
||||
ReplaceHourSymbol(pattern, hourCycle.value());
|
||||
}
|
||||
|
||||
JSString* str = NewStringCopyN<CanGC>(cx, pattern.begin(), pattern.length());
|
||||
if (!str)
|
||||
return false;
|
||||
args.rval().setString(str);
|
||||
|
|
@ -495,7 +846,7 @@ js::intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp)
|
|||
* of the given DateTimeFormat.
|
||||
*/
|
||||
static UDateFormat*
|
||||
NewUDateFormat(JSContext* cx, HandleObject dateTimeFormat)
|
||||
NewUDateFormat(JSContext* cx, Handle<DateTimeFormatObject*> dateTimeFormat)
|
||||
{
|
||||
RootedValue value(cx);
|
||||
|
||||
|
|
@ -505,13 +856,56 @@ NewUDateFormat(JSContext* cx, HandleObject dateTimeFormat)
|
|||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
|
||||
return nullptr;
|
||||
JSAutoByteString locale(cx, value.toString());
|
||||
if (!locale)
|
||||
|
||||
// ICU expects calendar and numberingSystem as Unicode locale extensions on
|
||||
// locale.
|
||||
|
||||
intl::LanguageTag tag(cx);
|
||||
{
|
||||
JSLinearString* locale = value.toString()->ensureLinear(cx);
|
||||
if (!locale)
|
||||
return nullptr;
|
||||
|
||||
if (!intl::LanguageTagParser::parse(cx, locale, tag))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::RootedVector<intl::UnicodeExtensionKeyword> keywords(cx);
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().calendar, &value))
|
||||
return nullptr;
|
||||
|
||||
// We don't need to look at calendar and numberingSystem - they can only be
|
||||
// set via the Unicode locale extension and are therefore already set on
|
||||
// locale.
|
||||
{
|
||||
JSLinearString* calendar = value.toString()->ensureLinear(cx);
|
||||
if (!calendar)
|
||||
return nullptr;
|
||||
|
||||
if (!keywords.emplaceBack("ca", calendar))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().numberingSystem, &value))
|
||||
return nullptr;
|
||||
|
||||
{
|
||||
JSLinearString* numberingSystem = value.toString()->ensureLinear(cx);
|
||||
if (!numberingSystem)
|
||||
return nullptr;
|
||||
|
||||
if (!keywords.emplaceBack("nu", numberingSystem))
|
||||
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;
|
||||
|
||||
UniqueChars locale = tag.toStringZ(cx);
|
||||
if (!locale)
|
||||
return nullptr;
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().timeZone, &value))
|
||||
return nullptr;
|
||||
|
|
@ -537,7 +931,7 @@ NewUDateFormat(JSContext* cx, HandleObject dateTimeFormat)
|
|||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UDateFormat* df =
|
||||
udat_open(UDAT_PATTERN, UDAT_PATTERN, IcuLocale(locale.ptr()), uTimeZone, uTimeZoneLength,
|
||||
udat_open(UDAT_PATTERN, UDAT_PATTERN, IcuLocale(locale.get()), uTimeZone, uTimeZoneLength,
|
||||
uPattern, uPatternLength, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
intl::ReportInternalError(cx);
|
||||
|
|
@ -558,7 +952,8 @@ static bool
|
|||
intl_FormatDateTime(JSContext* cx, UDateFormat* df, double x, MutableHandleValue result)
|
||||
{
|
||||
if (!IsFinite(x)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DATE_NOT_FINITE);
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DATE_NOT_FINITE,
|
||||
"DateTimeFormat", "format");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -664,7 +1059,8 @@ static bool
|
|||
intl_FormatToPartsDateTime(JSContext* cx, UDateFormat* df, double x, MutableHandleValue result)
|
||||
{
|
||||
if (!IsFinite(x)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DATE_NOT_FINITE);
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DATE_NOT_FINITE,
|
||||
"DateTimeFormat", "formatToParts");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -777,42 +1173,23 @@ js::intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp)
|
|||
MOZ_ASSERT(args[1].isNumber());
|
||||
MOZ_ASSERT(args[2].isBoolean());
|
||||
|
||||
RootedObject dateTimeFormat(cx, &args[0].toObject());
|
||||
Rooted<DateTimeFormatObject*> dateTimeFormat(cx);
|
||||
dateTimeFormat = &args[0].toObject().as<DateTimeFormatObject>();
|
||||
|
||||
// Obtain a UDateFormat object, cached if possible.
|
||||
bool isDateTimeFormatInstance = dateTimeFormat->getClass() == &DateTimeFormatObject::class_;
|
||||
UDateFormat* df;
|
||||
if (isDateTimeFormatInstance) {
|
||||
void* priv =
|
||||
dateTimeFormat->as<NativeObject>().getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT).toPrivate();
|
||||
df = static_cast<UDateFormat*>(priv);
|
||||
if (!df) {
|
||||
df = NewUDateFormat(cx, dateTimeFormat);
|
||||
if (!df)
|
||||
return false;
|
||||
dateTimeFormat->as<NativeObject>().setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT, PrivateValue(df));
|
||||
}
|
||||
} else {
|
||||
// There's no good place to cache the ICU date-time format for an object
|
||||
// that has been initialized as a DateTimeFormat but is not a
|
||||
// DateTimeFormat instance. One possibility might be to add a
|
||||
// DateTimeFormat instance as an internal property to each such object.
|
||||
// Obtain a cached UDateFormat object.
|
||||
void* priv =
|
||||
dateTimeFormat->getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT).toPrivate();
|
||||
UDateFormat* df = static_cast<UDateFormat*>(priv);
|
||||
if (!df) {
|
||||
df = NewUDateFormat(cx, dateTimeFormat);
|
||||
if (!df)
|
||||
return false;
|
||||
dateTimeFormat->setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT, PrivateValue(df));
|
||||
}
|
||||
|
||||
// Use the UDateFormat to actually format the time stamp.
|
||||
RootedValue result(cx);
|
||||
bool success = args[2].toBoolean()
|
||||
? intl_FormatToPartsDateTime(cx, df, args[1].toNumber(), &result)
|
||||
: intl_FormatDateTime(cx, df, args[1].toNumber(), &result);
|
||||
|
||||
if (!isDateTimeFormatInstance)
|
||||
udat_close(df);
|
||||
if (!success)
|
||||
return false;
|
||||
args.rval().set(result);
|
||||
return true;
|
||||
return args[2].toBoolean()
|
||||
? intl_FormatToPartsDateTime(cx, df, args[1].toNumber(), args.rval())
|
||||
: intl_FormatDateTime(cx, df, args[1].toNumber(), args.rval());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ class DateTimeFormatObject : public NativeObject
|
|||
extern JSObject*
|
||||
|
||||
CreateDateTimeFormatPrototype(JSContext* cx, JS::Handle<JSObject*> Intl,
|
||||
JS::Handle<GlobalObject*> global);
|
||||
JS::Handle<GlobalObject*> global, MutableHandleObject constructor,
|
||||
intl::DateTimeFormatOptions dtfOptions);
|
||||
|
||||
/**
|
||||
* Returns a new instance of the standard built-in DateTimeFormat constructor.
|
||||
|
|
@ -53,17 +54,6 @@ CreateDateTimeFormatPrototype(JSContext* cx, JS::Handle<JSObject*> Intl,
|
|||
extern MOZ_MUST_USE bool
|
||||
intl_DateTimeFormat(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Returns an object indicating the supported locales for date and time
|
||||
* formatting by having a true-valued property for each such locale with the
|
||||
* canonicalized language tag as the property name. The object has no
|
||||
* prototype.
|
||||
*
|
||||
* Usage: availableLocales = intl_DateTimeFormat_availableLocales()
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_DateTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Returns an array with the calendar type identifiers per Unicode
|
||||
* Technical Standard 35, Unicode Locale Data Markup Language, for the
|
||||
|
|
@ -75,6 +65,16 @@ intl_DateTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp);
|
|||
extern MOZ_MUST_USE bool
|
||||
intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Returns the calendar type identifier per Unicode Technical Standard 35,
|
||||
* Unicode Locale Data Markup Language, for the default calendar for the given
|
||||
* locale.
|
||||
*
|
||||
* Usage: calendar = intl_defaultCalendar(locale)
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_defaultCalendar(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* 6.4.1 IsValidTimeZoneName ( timeZone )
|
||||
*
|
||||
|
|
@ -119,11 +119,45 @@ intl_defaultTimeZoneOffset(JSContext* cx, unsigned argc, Value* vp);
|
|||
* best-fit date-time format pattern corresponding to skeleton for the
|
||||
* given locale.
|
||||
*
|
||||
* Usage: pattern = intl_patternForSkeleton(locale, skeleton)
|
||||
* Usage: pattern = intl_patternForSkeleton(locale, skeleton, hourCycle)
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Return a pattern in the date-time format pattern language of Unicode
|
||||
* Technical Standard 35, Unicode Locale Data Markup Language, for the
|
||||
* best-fit date-time style for the given locale.
|
||||
* The function takes six arguments:
|
||||
*
|
||||
* locale
|
||||
* BCP47 compliant locale string
|
||||
* dateStyle
|
||||
* A string with values: full or long or medium or short, or `undefined`
|
||||
* timeStyle
|
||||
* A string with values: full or long or medium or short, or `undefined`
|
||||
* timeZone
|
||||
* IANA time zone name
|
||||
* hour12
|
||||
* A boolean to request hour12 representation, or `undefined`
|
||||
* hourCycle
|
||||
* A string with values: h11, h12, h23, or h24, or `undefined`
|
||||
*
|
||||
* Date and time style categories map to CLDR time/date standard
|
||||
* format patterns.
|
||||
*
|
||||
* For the definition of a pattern string, see LDML 4.8:
|
||||
* http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
|
||||
*
|
||||
* If `undefined` is passed to `dateStyle` or `timeStyle`, the respective
|
||||
* portions of the pattern will not be included in the result.
|
||||
*
|
||||
* Usage: pattern = intl_patternForStyle(locale, dateStyle, timeStyle, timeZone,
|
||||
* hour12, hourCycle)
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_patternForStyle(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Returns a String value representing x (which must be a Number value)
|
||||
* according to the effective locale and the formatting options of the
|
||||
|
|
@ -131,7 +165,7 @@ intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp);
|
|||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 12.3.2.
|
||||
*
|
||||
* Usage: formatted = intl_FormatDateTime(dateTimeFormat, x)
|
||||
* Usage: formatted = intl_FormatDateTime(dateTimeFormat, x, formatToParts)
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,11 @@ function resolveDateTimeFormatInternals(lazyDateTimeFormatData) {
|
|||
// {
|
||||
// localeMatcher: "lookup" / "best fit",
|
||||
//
|
||||
// hour12: true / false, // optional
|
||||
// ca: string matching a Unicode extension type, // optional
|
||||
//
|
||||
// nu: string matching a Unicode extension type, // optional
|
||||
//
|
||||
// hc: "h11" / "h12" / "h23" / "h24", // optional
|
||||
// }
|
||||
//
|
||||
// timeZone: IANA time zone name,
|
||||
|
|
@ -29,9 +33,18 @@ function resolveDateTimeFormatInternals(lazyDateTimeFormatData) {
|
|||
// {
|
||||
// // all the properties/values listed in Table 3
|
||||
// // (weekday, era, year, month, day, &c.)
|
||||
//
|
||||
// hour12: true / false, // optional
|
||||
// }
|
||||
//
|
||||
// formatMatcher: "basic" / "best fit",
|
||||
//
|
||||
// dateStyle: "full" / "long" / "medium" / "short" / undefined,
|
||||
//
|
||||
// timeStyle: "full" / "long" / "medium" / "short" / undefined,
|
||||
//
|
||||
// patternOption:
|
||||
// String representing LDML Date Format pattern or undefined
|
||||
// }
|
||||
//
|
||||
// Note that lazy data is only installed as a final step of initialization,
|
||||
|
|
@ -39,22 +52,22 @@ function resolveDateTimeFormatInternals(lazyDateTimeFormatData) {
|
|||
// never a subset of them.
|
||||
|
||||
var internalProps = std_Object_create(null);
|
||||
|
||||
// Compute effective locale.
|
||||
// Step 8.
|
||||
|
||||
var DateTimeFormat = dateTimeFormatInternalProperties;
|
||||
|
||||
// Step 9.
|
||||
var localeData = DateTimeFormat.localeData;
|
||||
// Compute effective locale.
|
||||
|
||||
// Step 10.
|
||||
var r = ResolveLocale(callFunction(DateTimeFormat.availableLocales, DateTimeFormat),
|
||||
var localeData = DateTimeFormat.localeData;
|
||||
|
||||
// Step 11.
|
||||
var r = ResolveLocale("DateTimeFormat",
|
||||
lazyDateTimeFormatData.requestedLocales,
|
||||
lazyDateTimeFormatData.localeOpt,
|
||||
DateTimeFormat.relevantExtensionKeys,
|
||||
localeData);
|
||||
|
||||
// Steps 11-13.
|
||||
// Steps 12-13, 15.
|
||||
internalProps.locale = r.locale;
|
||||
internalProps.calendar = r.ca;
|
||||
internalProps.numberingSystem = r.nu;
|
||||
|
|
@ -63,26 +76,43 @@ function resolveDateTimeFormatInternals(lazyDateTimeFormatData) {
|
|||
// Step 14.
|
||||
var dataLocale = r.dataLocale;
|
||||
|
||||
// Steps 15-17.
|
||||
var tz = lazyDateTimeFormatData.timeZone;
|
||||
if (tz === undefined) {
|
||||
// Step 16.
|
||||
tz = DefaultTimeZone();
|
||||
}
|
||||
internalProps.timeZone = tz;
|
||||
// Steps 20.
|
||||
internalProps.timeZone = lazyDateTimeFormatData.timeZone;
|
||||
|
||||
// Step 18.
|
||||
// Step 21.
|
||||
var formatOpt = lazyDateTimeFormatData.formatOpt;
|
||||
|
||||
// Steps 27-28, more or less - see comment after this function.
|
||||
var pattern = toBestICUPattern(dataLocale, formatOpt);
|
||||
// Step 16.
|
||||
// Copy the hourCycle setting, if present, to the format options. But
|
||||
// only do this if no hour12 option is present, because the latter takes
|
||||
// precedence over hourCycle.
|
||||
if (r.hc !== null && formatOpt.hour12 === undefined)
|
||||
formatOpt.hourCycle = r.hc;
|
||||
|
||||
// Step 29.
|
||||
// Steps 26-30, more or less - see comment after this function.
|
||||
var pattern;
|
||||
if (lazyDateTimeFormatData.patternOption !== undefined) {
|
||||
pattern = lazyDateTimeFormatData.patternOption;
|
||||
|
||||
internalProps.patternOption = lazyDateTimeFormatData.patternOption;
|
||||
} else if (lazyDateTimeFormatData.dateStyle !== undefined ||
|
||||
lazyDateTimeFormatData.timeStyle !== undefined) {
|
||||
pattern = intl_patternForStyle(dataLocale,
|
||||
lazyDateTimeFormatData.dateStyle,
|
||||
lazyDateTimeFormatData.timeStyle,
|
||||
lazyDateTimeFormatData.timeZone,
|
||||
formatOpt.hour12,
|
||||
formatOpt.hourCycle);
|
||||
|
||||
internalProps.dateStyle = lazyDateTimeFormatData.dateStyle;
|
||||
internalProps.timeStyle = lazyDateTimeFormatData.timeStyle;
|
||||
} else {
|
||||
pattern = toBestICUPattern(dataLocale, formatOpt);
|
||||
}
|
||||
|
||||
// Step 31.
|
||||
internalProps.pattern = pattern;
|
||||
|
||||
// Step 30.
|
||||
internalProps.boundFormat = undefined;
|
||||
|
||||
// The caller is responsible for associating |internalProps| with the right
|
||||
// object using |setInternalProperties|.
|
||||
return internalProps;
|
||||
|
|
@ -90,11 +120,13 @@ function resolveDateTimeFormatInternals(lazyDateTimeFormatData) {
|
|||
|
||||
|
||||
/**
|
||||
* Returns an object containing the DateTimeFormat internal properties of |obj|,
|
||||
* or throws a TypeError if |obj| isn't DateTimeFormat-initialized.
|
||||
* Returns an object containing the DateTimeFormat internal properties of |obj|.
|
||||
*/
|
||||
function getDateTimeFormatInternals(obj, methodName) {
|
||||
var internals = getIntlObjectInternals(obj, "DateTimeFormat", methodName);
|
||||
function getDateTimeFormatInternals(obj) {
|
||||
assert(IsObject(obj), "getDateTimeFormatInternals called with non-object");
|
||||
assert(IsDateTimeFormat(obj), "getDateTimeFormatInternals called with non-DateTimeFormat");
|
||||
|
||||
var internals = getIntlObjectInternals(obj);
|
||||
assert(internals.type === "DateTimeFormat", "bad type escaped getIntlObjectInternals");
|
||||
|
||||
// If internal properties have already been computed, use them.
|
||||
|
|
@ -214,6 +246,31 @@ function DefaultTimeZone() {
|
|||
return defaultTimeZone;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 12.1.10 UnwrapDateTimeFormat( dtf )
|
||||
*/
|
||||
function UnwrapDateTimeFormat(dtf, methodName) {
|
||||
// Step 1 (not applicable in our implementation).
|
||||
|
||||
// Step 2.
|
||||
if ((!IsObject(dtf) || !IsDateTimeFormat(dtf)) &&
|
||||
dtf instanceof GetDateTimeFormatConstructor())
|
||||
{
|
||||
dtf = dtf[intlFallbackSymbol()];
|
||||
}
|
||||
|
||||
// Step 3.
|
||||
if (!IsObject(dtf) || !IsDateTimeFormat(dtf)) {
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "DateTimeFormat", methodName,
|
||||
"DateTimeFormat");
|
||||
}
|
||||
|
||||
// Step 4.
|
||||
return dtf;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes an object as a DateTimeFormat.
|
||||
*
|
||||
|
|
@ -225,15 +282,10 @@ function DefaultTimeZone() {
|
|||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 12.1.1.
|
||||
*/
|
||||
function InitializeDateTimeFormat(dateTimeFormat, locales, options) {
|
||||
assert(IsObject(dateTimeFormat), "InitializeDateTimeFormat");
|
||||
|
||||
// Step 1.
|
||||
if (isInitializedIntlObject(dateTimeFormat))
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
|
||||
|
||||
// Step 2.
|
||||
var internals = initializeIntlObject(dateTimeFormat);
|
||||
function InitializeDateTimeFormat(dateTimeFormat, thisValue, locales, options, mozExtensions) {
|
||||
assert(IsObject(dateTimeFormat), "InitializeDateTimeFormat called with non-Object");
|
||||
assert(IsDateTimeFormat(dateTimeFormat),
|
||||
"InitializeDateTimeFormat called with non-DateTimeFormat");
|
||||
|
||||
// Lazy DateTimeFormat data has the following structure:
|
||||
//
|
||||
|
|
@ -243,6 +295,12 @@ function InitializeDateTimeFormat(dateTimeFormat, locales, options) {
|
|||
// localeOpt: // *first* opt computed in InitializeDateTimeFormat
|
||||
// {
|
||||
// localeMatcher: "lookup" / "best fit",
|
||||
//
|
||||
// ca: string matching a Unicode extension type, // optional
|
||||
//
|
||||
// nu: string matching a Unicode extension type, // optional
|
||||
//
|
||||
// hc: "h11" / "h12" / "h23" / "h24", // optional
|
||||
// }
|
||||
//
|
||||
// timeZone: IANA time zone name,
|
||||
|
|
@ -252,7 +310,7 @@ function InitializeDateTimeFormat(dateTimeFormat, locales, options) {
|
|||
// // all the properties/values listed in Table 3
|
||||
// // (weekday, era, year, month, day, &c.)
|
||||
//
|
||||
// hour12: true / false // optional
|
||||
// hour12: true / false, // optional
|
||||
// }
|
||||
//
|
||||
// formatMatcher: "basic" / "best fit",
|
||||
|
|
@ -263,45 +321,89 @@ function InitializeDateTimeFormat(dateTimeFormat, locales, options) {
|
|||
// never a subset of them.
|
||||
var lazyDateTimeFormatData = std_Object_create(null);
|
||||
|
||||
// Step 3.
|
||||
// Step 1.
|
||||
var requestedLocales = CanonicalizeLocaleList(locales);
|
||||
lazyDateTimeFormatData.requestedLocales = requestedLocales;
|
||||
|
||||
// Step 4.
|
||||
// Step24.
|
||||
options = ToDateTimeOptions(options, "any", "date");
|
||||
|
||||
// Compute options that impact interpretation of locale.
|
||||
// Step 5.
|
||||
// Step 3.
|
||||
var localeOpt = new Record();
|
||||
lazyDateTimeFormatData.localeOpt = localeOpt;
|
||||
|
||||
// Steps 6-7.
|
||||
// Steps 4-5.
|
||||
var localeMatcher =
|
||||
GetOption(options, "localeMatcher", "string", ["lookup", "best fit"],
|
||||
"best fit");
|
||||
localeOpt.localeMatcher = localeMatcher;
|
||||
|
||||
// Steps 15-17.
|
||||
var calendar = GetOption(options, "calendar", "string", undefined, undefined);
|
||||
|
||||
if (calendar !== undefined) {
|
||||
calendar = intl_ValidateAndCanonicalizeUnicodeExtensionType(calendar, "calendar", "ca");
|
||||
}
|
||||
|
||||
localeOpt.ca = calendar;
|
||||
|
||||
var numberingSystem = GetOption(options, "numberingSystem", "string", undefined, undefined);
|
||||
|
||||
if (numberingSystem !== undefined) {
|
||||
numberingSystem = intl_ValidateAndCanonicalizeUnicodeExtensionType(numberingSystem,
|
||||
"numberingSystem",
|
||||
"nu");
|
||||
}
|
||||
|
||||
localeOpt.nu = numberingSystem;
|
||||
|
||||
// Step 6.
|
||||
var hr12 = GetOption(options, "hour12", "boolean", undefined, undefined);
|
||||
|
||||
// Step 7.
|
||||
var hc = GetOption(options, "hourCycle", "string", ["h11", "h12", "h23", "h24"], undefined);
|
||||
|
||||
// Step 8.
|
||||
if (hr12 !== undefined) {
|
||||
// The "hourCycle" option is ignored if "hr12" is also present.
|
||||
hc = null;
|
||||
}
|
||||
|
||||
// Step 9.
|
||||
localeOpt.hc = hc;
|
||||
|
||||
// Steps 10-16 (see resolveDateTimeFormatInternals).
|
||||
|
||||
// Steps 17-20.
|
||||
var tz = options.timeZone;
|
||||
if (tz !== undefined) {
|
||||
// Step 15.a.
|
||||
// Step 18.a.
|
||||
tz = ToString(tz);
|
||||
|
||||
// Step 15.b.
|
||||
// Step 18.b.
|
||||
var timeZone = intl_IsValidTimeZoneName(tz);
|
||||
if (timeZone === null)
|
||||
ThrowRangeError(JSMSG_INVALID_TIME_ZONE, tz);
|
||||
|
||||
// Step 15.c.
|
||||
// Step 18.c.
|
||||
tz = CanonicalizeTimeZoneName(timeZone);
|
||||
} else {
|
||||
// Step 19.
|
||||
tz = DefaultTimeZone();
|
||||
}
|
||||
lazyDateTimeFormatData.timeZone = tz;
|
||||
|
||||
// Step 18.
|
||||
// Step 21.
|
||||
var formatOpt = new Record();
|
||||
lazyDateTimeFormatData.formatOpt = formatOpt;
|
||||
|
||||
// Step 19.
|
||||
if (mozExtensions) {
|
||||
let pattern = GetOption(options, "pattern", "string", undefined, undefined);
|
||||
lazyDateTimeFormatData.patternOption = pattern;
|
||||
}
|
||||
|
||||
// Step 22.
|
||||
// 12.1, Table 5: Components of date and time formats.
|
||||
var i, prop;
|
||||
for (i = 0; i < dateTimeComponents.length; i++) {
|
||||
prop = dateTimeComponents[i];
|
||||
|
|
@ -309,9 +411,9 @@ function InitializeDateTimeFormat(dateTimeFormat, locales, options) {
|
|||
formatOpt[prop] = value;
|
||||
}
|
||||
|
||||
// Steps 20-21 provided by ICU - see comment after this function.
|
||||
// Steps 23-24 provided by ICU - see comment after this function.
|
||||
|
||||
// Step 22.
|
||||
// Step 25.
|
||||
//
|
||||
// For some reason (ICU not exposing enough interface?) we drop the
|
||||
// requested format matcher on the floor after this. In any case, even if
|
||||
|
|
@ -321,20 +423,58 @@ function InitializeDateTimeFormat(dateTimeFormat, locales, options) {
|
|||
GetOption(options, "formatMatcher", "string", ["basic", "best fit"],
|
||||
"best fit");
|
||||
|
||||
// Steps 23-25 provided by ICU, more or less - see comment after this function.
|
||||
// "DateTimeFormat dateStyle & timeStyle" propsal
|
||||
// https://github.com/tc39/proposal-intl-datetime-style
|
||||
var dateStyle = GetOption(options, "dateStyle", "string", ["full", "long", "medium", "short"],
|
||||
undefined);
|
||||
lazyDateTimeFormatData.dateStyle = dateStyle;
|
||||
|
||||
// Step 26.
|
||||
var hr12 = GetOption(options, "hour12", "boolean", undefined, undefined);
|
||||
var timeStyle = GetOption(options, "timeStyle", "string", ["full", "long", "medium", "short"],
|
||||
undefined);
|
||||
lazyDateTimeFormatData.timeStyle = timeStyle;
|
||||
|
||||
if (dateStyle !== undefined || timeStyle !== undefined) {
|
||||
var optionsList = [
|
||||
"weekday", "era", "year", "month", "day", "hour", "minute", "second", "timeZoneName",
|
||||
];
|
||||
|
||||
for (var i = 0; i < optionsList.length; i++) {
|
||||
var option = optionsList[i];
|
||||
if (formatOpt[option] !== undefined) {
|
||||
ThrowTypeError(JSMSG_INVALID_DATETIME_OPTION, option,
|
||||
dateStyle !== undefined ? "dateStyle" : "timeStyle");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Steps 26-28 provided by ICU, more or less - see comment after this function.
|
||||
|
||||
// Steps 29-30.
|
||||
// Pass hr12 on to ICU.
|
||||
if (hr12 !== undefined)
|
||||
formatOpt.hour12 = hr12;
|
||||
|
||||
// Step 31.
|
||||
// Step 32.
|
||||
//
|
||||
// We've done everything that must be done now: mark the lazy data as fully
|
||||
// computed and install it.
|
||||
setLazyData(internals, "DateTimeFormat", lazyDateTimeFormatData);
|
||||
initializeIntlObject(dateTimeFormat, "DateTimeFormat", lazyDateTimeFormatData);
|
||||
|
||||
// 12.2.1, steps 4-5.
|
||||
// TODO: spec issue - The current spec doesn't have the IsObject check,
|
||||
// which means |Intl.DateTimeFormat.call(null)| is supposed to throw here.
|
||||
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;
|
||||
}
|
||||
|
||||
// 12.2.1, step 6.
|
||||
return dateTimeFormat;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -396,6 +536,7 @@ function InitializeDateTimeFormat(dateTimeFormat, locales, options) {
|
|||
// - [[weekday]], [[era]], [[year]], [[month]], [[day]], [[hour]], [[minute]],
|
||||
// [[second]], [[timeZoneName]]
|
||||
// - [[hour12]]
|
||||
// - [[hourCycle]]
|
||||
// - [[hourNo0]]
|
||||
// When needed for the resolvedOptions method, the resolveICUPattern function
|
||||
// maps the instance's ICU pattern back to the specified properties of the
|
||||
|
|
@ -469,12 +610,24 @@ function toBestICUPattern(locale, options) {
|
|||
skeleton += "d";
|
||||
break;
|
||||
}
|
||||
// If hour12 and hourCycle are both present, hour12 takes precedence.
|
||||
var hourSkeletonChar = "j";
|
||||
if (options.hour12 !== undefined) {
|
||||
if (options.hour12)
|
||||
hourSkeletonChar = "h";
|
||||
else
|
||||
hourSkeletonChar = "H";
|
||||
} else {
|
||||
switch (options.hourCycle) {
|
||||
case "h11":
|
||||
case "h12":
|
||||
hourSkeletonChar = "h";
|
||||
break;
|
||||
case "h23":
|
||||
case "h24":
|
||||
hourSkeletonChar = "H";
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (options.hour) {
|
||||
case "2-digit":
|
||||
|
|
@ -510,7 +663,7 @@ function toBestICUPattern(locale, options) {
|
|||
}
|
||||
|
||||
// Let ICU convert the ICU skeleton to an ICU pattern for the given locale.
|
||||
return intl_patternForSkeleton(locale, skeleton);
|
||||
return intl_patternForSkeleton(locale, skeleton, options.hourCycle);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -526,17 +679,19 @@ function ToDateTimeOptions(options, required, defaults) {
|
|||
assert(typeof required === "string", "ToDateTimeOptions");
|
||||
assert(typeof defaults === "string", "ToDateTimeOptions");
|
||||
|
||||
// Steps 1-3.
|
||||
// Steps 1-2.
|
||||
if (options === undefined)
|
||||
options = null;
|
||||
else
|
||||
options = ToObject(options);
|
||||
options = std_Object_create(options);
|
||||
|
||||
// Step 4.
|
||||
// Step 3.
|
||||
var needDefaults = true;
|
||||
|
||||
// Step 5.
|
||||
// Step 4.
|
||||
// TODO: spec issue - The spec requires to retrieve all options, so using
|
||||
// the ||-operator with its lazy evaluation semantics is incorrect.
|
||||
if ((required === "date" || required === "any") &&
|
||||
(options.weekday !== undefined || options.year !== undefined ||
|
||||
options.month !== undefined || options.day !== undefined))
|
||||
|
|
@ -544,7 +699,9 @@ function ToDateTimeOptions(options, required, defaults) {
|
|||
needDefaults = false;
|
||||
}
|
||||
|
||||
// Step 6.
|
||||
// Step 5.
|
||||
// TODO: spec issue - The spec requires to retrieve all options, so using
|
||||
// the ||-operator with its lazy evaluation semantics is incorrect.
|
||||
if ((required === "time" || required === "any") &&
|
||||
(options.hour !== undefined || options.minute !== undefined ||
|
||||
options.second !== undefined))
|
||||
|
|
@ -552,7 +709,21 @@ function ToDateTimeOptions(options, required, defaults) {
|
|||
needDefaults = false;
|
||||
}
|
||||
|
||||
// Step 7.
|
||||
// "DateTimeFormat dateStyle & timeStyle" propsal
|
||||
// https://github.com/tc39/proposal-intl-datetime-style
|
||||
var dateStyle = options.dateStyle;
|
||||
var timeStyle = options.timeStyle;
|
||||
|
||||
if (dateStyle !== undefined || timeStyle !== undefined)
|
||||
needDefaults = false;
|
||||
|
||||
if (required === "date" && timeStyle !== undefined)
|
||||
ThrowTypeError(JSMSG_INVALID_DATETIME_STYLE, "timeStyle", "toLocaleDateString");
|
||||
|
||||
if (required === "time" && dateStyle !== undefined)
|
||||
ThrowTypeError(JSMSG_INVALID_DATETIME_STYLE, "dateStyle", "toLocaleTimeString");
|
||||
|
||||
// Step 6.
|
||||
if (needDefaults && (defaults === "date" || defaults === "all")) {
|
||||
// The specification says to call [[DefineOwnProperty]] with false for
|
||||
// the Throw parameter, while Object.defineProperty uses true. For the
|
||||
|
|
@ -563,7 +734,7 @@ function ToDateTimeOptions(options, required, defaults) {
|
|||
_DefineDataProperty(options, "day", "numeric");
|
||||
}
|
||||
|
||||
// Step 8.
|
||||
// Step 7.
|
||||
if (needDefaults && (defaults === "time" || defaults === "all")) {
|
||||
// See comment for step 7.
|
||||
_DefineDataProperty(options, "hour", "numeric");
|
||||
|
|
@ -571,7 +742,7 @@ function ToDateTimeOptions(options, required, defaults) {
|
|||
_DefineDataProperty(options, "second", "numeric");
|
||||
}
|
||||
|
||||
// Step 9.
|
||||
// Step 8.
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
@ -623,7 +794,7 @@ function BasicFormatMatcher(options, formats) {
|
|||
formatProp = undefined;
|
||||
|
||||
// Steps 11.c.ii-iii.
|
||||
if (callFunction(std_Object_hasOwnProperty, format, property))
|
||||
if (hasOwn(property, format))
|
||||
formatProp = format[property];
|
||||
|
||||
if (optionsProp === undefined && formatProp !== undefined) {
|
||||
|
|
@ -681,14 +852,18 @@ function BestFitFormatMatcher(options, formats) {
|
|||
* matching (possibly fallback) locale. Locales appear in the same order in the
|
||||
* returned list as in the input list.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 12.2.2.
|
||||
* Spec: ECMAScript Internationalization API Specification, 12.3.2.
|
||||
*/
|
||||
function Intl_DateTimeFormat_supportedLocalesOf(locales /*, options*/) {
|
||||
var options = arguments.length > 1 ? arguments[1] : undefined;
|
||||
|
||||
var availableLocales = callFunction(dateTimeFormatInternalProperties.availableLocales,
|
||||
dateTimeFormatInternalProperties);
|
||||
// Step 1.
|
||||
var availableLocales = "DateTimeFormat";
|
||||
|
||||
// Step 2.
|
||||
var requestedLocales = CanonicalizeLocaleList(locales);
|
||||
|
||||
// Step 3.
|
||||
return SupportedLocales(availableLocales, requestedLocales, options);
|
||||
}
|
||||
|
||||
|
|
@ -696,29 +871,28 @@ function Intl_DateTimeFormat_supportedLocalesOf(locales /*, options*/) {
|
|||
/**
|
||||
* DateTimeFormat internal properties.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 9.1 and 12.2.3.
|
||||
* Spec: ECMAScript Internationalization API Specification, 9.1 and 12.3.3.
|
||||
*/
|
||||
var dateTimeFormatInternalProperties = {
|
||||
localeData: dateTimeFormatLocaleData,
|
||||
_availableLocales: null,
|
||||
availableLocales: function()
|
||||
{
|
||||
var locales = this._availableLocales;
|
||||
if (locales)
|
||||
return locales;
|
||||
|
||||
locales = intl_DateTimeFormat_availableLocales();
|
||||
addSpecialMissingLanguageTags(locales);
|
||||
return (this._availableLocales = locales);
|
||||
},
|
||||
relevantExtensionKeys: ["ca", "nu"]
|
||||
relevantExtensionKeys: ["ca", "nu", "hc"]
|
||||
};
|
||||
|
||||
|
||||
function dateTimeFormatLocaleData(locale) {
|
||||
function dateTimeFormatLocaleData() {
|
||||
return {
|
||||
ca: intl_availableCalendars(locale),
|
||||
nu: getNumberingSystems(locale)
|
||||
ca: intl_availableCalendars,
|
||||
nu: getNumberingSystems,
|
||||
hc: () => {
|
||||
return [null, "h11", "h12", "h23", "h24"];
|
||||
},
|
||||
default: {
|
||||
ca: intl_defaultCalendar,
|
||||
nu: intl_numberingSystem,
|
||||
hc: () => {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -726,7 +900,7 @@ function dateTimeFormatLocaleData(locale) {
|
|||
/**
|
||||
* Function to be bound and returned by Intl.DateTimeFormat.prototype.format.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 12.3.2.
|
||||
* Spec: ECMAScript Internationalization API Specification, 12.1.5.
|
||||
*/
|
||||
function dateTimeFormatFormatToBind() {
|
||||
// Steps 1.a.i-ii
|
||||
|
|
@ -734,7 +908,7 @@ function dateTimeFormatFormatToBind() {
|
|||
var x = (date === undefined) ? std_Date_now() : ToNumber(date);
|
||||
|
||||
// Step 1.a.iii.
|
||||
return intl_FormatDateTime(this, x, false);
|
||||
return intl_FormatDateTime(this, x, /* formatToParts = */ false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -742,82 +916,97 @@ function dateTimeFormatFormatToBind() {
|
|||
* representing the result of calling ToNumber(date) according to the
|
||||
* effective locale and the formatting options of this DateTimeFormat.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 12.3.2.
|
||||
* Spec: ECMAScript Internationalization API Specification, 12.4.3.
|
||||
*/
|
||||
function Intl_DateTimeFormat_format_get() {
|
||||
// Check "this DateTimeFormat object" per introduction of section 12.3.
|
||||
var internals = getDateTimeFormatInternals(this, "format");
|
||||
// Steps 1-3.
|
||||
var dtf = UnwrapDateTimeFormat(this, "format");
|
||||
|
||||
// Step 1.
|
||||
var internals = getDateTimeFormatInternals(dtf);
|
||||
|
||||
// Step 4.
|
||||
if (internals.boundFormat === undefined) {
|
||||
// Step 1.a.
|
||||
var F = dateTimeFormatFormatToBind;
|
||||
// Steps 4.a-b.
|
||||
var F = callFunction(FunctionBind, dateTimeFormatFormatToBind, dtf);
|
||||
|
||||
// Step 1.b-d.
|
||||
var bf = callFunction(FunctionBind, F, this);
|
||||
internals.boundFormat = bf;
|
||||
// Step 4.c.
|
||||
internals.boundFormat = F;
|
||||
}
|
||||
|
||||
// Step 2.
|
||||
// Step 5.
|
||||
return internals.boundFormat;
|
||||
}
|
||||
_SetCanonicalName(Intl_DateTimeFormat_format_get, "get format");
|
||||
|
||||
|
||||
/**
|
||||
* Intl.DateTimeFormat.prototype.formatToParts ( date )
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 12.4.4.
|
||||
*/
|
||||
function Intl_DateTimeFormat_formatToParts() {
|
||||
// Check "this DateTimeFormat object" per introduction of section 12.3.
|
||||
getDateTimeFormatInternals(this, "formatToParts");
|
||||
// Steps 1-3.
|
||||
var dtf = UnwrapDateTimeFormat(this, "formatToParts");
|
||||
|
||||
// Steps 1.a.i-ii
|
||||
// Ensure the DateTimeFormat internals are resolved.
|
||||
getDateTimeFormatInternals(dtf);
|
||||
|
||||
// Steps 4-5.
|
||||
var date = arguments.length > 0 ? arguments[0] : undefined;
|
||||
var x = (date === undefined) ? std_Date_now() : ToNumber(date);
|
||||
|
||||
// Step 1.a.iii.
|
||||
return intl_FormatDateTime(this, x, true);
|
||||
// Step 6.
|
||||
return intl_FormatDateTime(dtf, x, /* formatToParts = */ true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the resolved options for a DateTimeFormat object.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 12.3.3 and 12.4.
|
||||
* Spec: ECMAScript Internationalization API Specification, 12.4.5.
|
||||
*/
|
||||
function Intl_DateTimeFormat_resolvedOptions() {
|
||||
// Check "this DateTimeFormat object" per introduction of section 12.3.
|
||||
var internals = getDateTimeFormatInternals(this, "resolvedOptions");
|
||||
// Steps 1-3.
|
||||
var dtf = UnwrapDateTimeFormat(this, "resolvedOptions");
|
||||
|
||||
var internals = getDateTimeFormatInternals(dtf);
|
||||
|
||||
// Steps 4-5.
|
||||
var result = {
|
||||
locale: internals.locale,
|
||||
calendar: internals.calendar,
|
||||
numberingSystem: internals.numberingSystem,
|
||||
timeZone: internals.timeZone
|
||||
timeZone: internals.timeZone,
|
||||
};
|
||||
resolveICUPattern(internals.pattern, result);
|
||||
|
||||
if (internals.patternOption !== undefined) {
|
||||
_DefineDataProperty(result, "pattern", internals.pattern);
|
||||
}
|
||||
|
||||
var hasDateStyle = internals.dateStyle !== undefined;
|
||||
var hasTimeStyle = internals.timeStyle !== undefined;
|
||||
|
||||
if (hasDateStyle || hasTimeStyle) {
|
||||
if (hasTimeStyle) {
|
||||
// timeStyle (unlike dateStyle) requires resolving the pattern to
|
||||
// ensure "hourCycle" and "hour12" properties are added to |result|.
|
||||
resolveICUPattern(internals.pattern, result, /* includeDateTimeFields = */ false);
|
||||
}
|
||||
if (hasDateStyle) {
|
||||
_DefineDataProperty(result, "dateStyle", internals.dateStyle);
|
||||
}
|
||||
if (hasTimeStyle) {
|
||||
_DefineDataProperty(result, "timeStyle", internals.timeStyle);
|
||||
}
|
||||
} else {
|
||||
resolveICUPattern(internals.pattern, result, /* includeDateTimeFields = */ true);
|
||||
}
|
||||
|
||||
// Step 6.
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Table mapping ICU pattern characters back to the corresponding date-time
|
||||
// components of DateTimeFormat. See
|
||||
// http://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
|
||||
var icuPatternCharToComponent = {
|
||||
E: "weekday",
|
||||
G: "era",
|
||||
y: "year",
|
||||
M: "month",
|
||||
L: "month",
|
||||
d: "day",
|
||||
h: "hour",
|
||||
H: "hour",
|
||||
k: "hour",
|
||||
K: "hour",
|
||||
m: "minute",
|
||||
s: "second",
|
||||
z: "timeZoneName",
|
||||
v: "timeZoneName",
|
||||
V: "timeZoneName"
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Maps an ICU pattern string to a corresponding set of date-time components
|
||||
* and their values, and adds properties for these components to the result
|
||||
|
|
@ -825,8 +1014,12 @@ var icuPatternCharToComponent = {
|
|||
* interpretation of ICU pattern characters, see
|
||||
* http://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
|
||||
*/
|
||||
function resolveICUPattern(pattern, result) {
|
||||
function resolveICUPattern(pattern, result, includeDateTimeFields) {
|
||||
assert(IsObject(result), "resolveICUPattern");
|
||||
|
||||
var hourCycle, weekday, era, year, month, day, hour, minute, second,
|
||||
timeZoneName;
|
||||
|
||||
var i = 0;
|
||||
while (i < pattern.length) {
|
||||
var c = pattern[i++];
|
||||
|
|
@ -886,13 +1079,91 @@ function resolveICUPattern(pattern, result) {
|
|||
default:
|
||||
// skip other pattern characters and literal text
|
||||
}
|
||||
if (callFunction(std_Object_hasOwnProperty, icuPatternCharToComponent, c))
|
||||
_DefineDataProperty(result, icuPatternCharToComponent[c], value);
|
||||
if (c === "h" || c === "K")
|
||||
_DefineDataProperty(result, "hour12", true);
|
||||
else if (c === "H" || c === "k")
|
||||
_DefineDataProperty(result, "hour12", false);
|
||||
|
||||
// Map ICU pattern characters back to the corresponding date-time
|
||||
// components of DateTimeFormat. See
|
||||
// http://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
|
||||
switch (c) {
|
||||
case "E":
|
||||
case "c":
|
||||
weekday = value;
|
||||
break;
|
||||
case "G":
|
||||
era = value;
|
||||
break;
|
||||
case "y":
|
||||
year = value;
|
||||
break;
|
||||
case "M":
|
||||
case "L":
|
||||
month = value;
|
||||
break;
|
||||
case "d":
|
||||
day = value;
|
||||
break;
|
||||
case "h":
|
||||
hourCycle = "h12";
|
||||
hour = value;
|
||||
break;
|
||||
case "H":
|
||||
hourCycle = "h23";
|
||||
hour = value;
|
||||
break;
|
||||
case "k":
|
||||
hourCycle = "h24";
|
||||
hour = value;
|
||||
break;
|
||||
case "K":
|
||||
hourCycle = "h11";
|
||||
hour = value;
|
||||
break;
|
||||
case "m":
|
||||
minute = value;
|
||||
break;
|
||||
case "s":
|
||||
second = value;
|
||||
break;
|
||||
case "z":
|
||||
case "v":
|
||||
case "V":
|
||||
timeZoneName = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hourCycle) {
|
||||
_DefineDataProperty(result, "hourCycle", hourCycle);
|
||||
_DefineDataProperty(result, "hour12", hourCycle === "h11" || hourCycle === "h12");
|
||||
}
|
||||
if (!includeDateTimeFields) {
|
||||
return;
|
||||
}
|
||||
if (weekday) {
|
||||
_DefineDataProperty(result, "weekday", weekday);
|
||||
}
|
||||
if (era) {
|
||||
_DefineDataProperty(result, "era", era);
|
||||
}
|
||||
if (year) {
|
||||
_DefineDataProperty(result, "year", year);
|
||||
}
|
||||
if (month) {
|
||||
_DefineDataProperty(result, "month", month);
|
||||
}
|
||||
if (day) {
|
||||
_DefineDataProperty(result, "day", day);
|
||||
}
|
||||
if (hour) {
|
||||
_DefineDataProperty(result, "hour", hour);
|
||||
}
|
||||
if (minute) {
|
||||
_DefineDataProperty(result, "minute", minute);
|
||||
}
|
||||
if (second) {
|
||||
_DefineDataProperty(result, "second", second);
|
||||
}
|
||||
if (timeZoneName) {
|
||||
_DefineDataProperty(result, "timeZoneName", timeZoneName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "unicode/udatpg.h"
|
||||
#include "unicode/udisplaycontext.h"
|
||||
#include "unicode/uenum.h"
|
||||
#include "unicode/uloc.h"
|
||||
#include "unicode/unum.h"
|
||||
#include "unicode/unumsys.h"
|
||||
#include "unicode/upluralrules.h"
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@
|
|||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/Range.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsobj.h"
|
||||
|
|
@ -18,11 +21,15 @@
|
|||
#include "builtin/intl/Collator.h"
|
||||
#include "builtin/intl/CommonFunctions.h"
|
||||
#include "builtin/intl/DateTimeFormat.h"
|
||||
#include "builtin/intl/LanguageTag.h"
|
||||
#include "builtin/intl/ICUHeader.h"
|
||||
#include "builtin/intl/Locale.h"
|
||||
#include "builtin/intl/NumberFormat.h"
|
||||
#include "builtin/intl/PluralRules.h"
|
||||
#include "builtin/intl/RelativeTimeFormat.h"
|
||||
#include "builtin/intl/ScopedICUObject.h"
|
||||
#include "builtin/intl/SharedIntlData.h"
|
||||
#include "js/Result.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
|
@ -30,7 +37,7 @@
|
|||
using namespace js;
|
||||
|
||||
using js::intl::CallICU;
|
||||
using js::intl::GetAvailableLocales;
|
||||
using js::intl::DateTimeFormatOptions;
|
||||
using js::intl::IcuLocale;
|
||||
using js::intl::INITIAL_CHAR_BUFFER_SIZE;
|
||||
using js::intl::StringsAreEqual;
|
||||
|
|
@ -405,6 +412,272 @@ js::intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
using SupportedLocaleKind = js::intl::SharedIntlData::SupportedLocaleKind;
|
||||
|
||||
// 9.2.2 BestAvailableLocale ( availableLocales, locale )
|
||||
static JS::Result<JSString*>
|
||||
BestAvailableLocale(JSContext* cx, SupportedLocaleKind kind, HandleLinearString locale,
|
||||
HandleLinearString defaultLocale)
|
||||
{
|
||||
// In the spec, [[availableLocales]] is formally a list of all available
|
||||
// locales. But in our implementation, it's an *incomplete* list, not
|
||||
// necessarily including the default locale (and all locales implied by it,
|
||||
// e.g. "de" implied by "de-CH"), if that locale isn't in every
|
||||
// [[availableLocales]] list (because that locale is supported through
|
||||
// fallback, e.g. "de-CH" supported through "de").
|
||||
//
|
||||
// If we're considering the default locale, augment the spec loop with
|
||||
// additional checks to also test whether the current prefix is a prefix of
|
||||
// the default locale.
|
||||
|
||||
intl::SharedIntlData& sharedIntlData = cx->sharedIntlData;
|
||||
|
||||
auto findLast = [](const auto* chars, size_t length) {
|
||||
auto rbegin = std::make_reverse_iterator(chars + length);
|
||||
auto rend = std::make_reverse_iterator(chars);
|
||||
auto p = std::find(rbegin, rend, '-');
|
||||
|
||||
// |dist(chars, p.base())| is equal to |dist(p, rend)|, pick whichever you
|
||||
// find easier to reason about when using reserve iterators.
|
||||
ptrdiff_t r = std::distance(chars, p.base());
|
||||
MOZ_ASSERT(r == std::distance(p, rend));
|
||||
|
||||
// But always subtract one to convert from the reverse iterator result to
|
||||
// the corresponding forward iterator value, because reserve iterators point
|
||||
// to one element past the forward iterator value.
|
||||
return r - 1;
|
||||
};
|
||||
|
||||
// Step 1.
|
||||
RootedLinearString candidate(cx, locale);
|
||||
|
||||
// Step 2.
|
||||
while (true) {
|
||||
// Step 2.a.
|
||||
bool supported = false;
|
||||
if (!sharedIntlData.isSupportedLocale(cx, kind, candidate, &supported)) {
|
||||
return cx->alreadyReportedError();
|
||||
}
|
||||
if (supported) {
|
||||
return candidate.get();
|
||||
}
|
||||
|
||||
if (defaultLocale && candidate->length() <= defaultLocale->length()) {
|
||||
if (EqualStrings(candidate, defaultLocale)) {
|
||||
return candidate.get();
|
||||
}
|
||||
|
||||
if (candidate->length() < defaultLocale->length() &&
|
||||
HasSubstringAt(defaultLocale, candidate, 0) &&
|
||||
defaultLocale->latin1OrTwoByteChar(candidate->length()) == '-') {
|
||||
return candidate.get();
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2.b.
|
||||
ptrdiff_t pos;
|
||||
if (candidate->hasLatin1Chars()) {
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
pos = findLast(candidate->latin1Chars(nogc), candidate->length());
|
||||
} else {
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
pos = findLast(candidate->twoByteChars(nogc), candidate->length());
|
||||
}
|
||||
|
||||
if (pos < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 2.c.
|
||||
size_t length = size_t(pos);
|
||||
if (length >= 2 && candidate->latin1OrTwoByteChar(length - 2) == '-') {
|
||||
length -= 2;
|
||||
}
|
||||
|
||||
// Step 2.d.
|
||||
candidate = NewDependentString(cx, candidate, 0, length);
|
||||
if (!candidate) {
|
||||
return cx->alreadyReportedError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 9.2.2 BestAvailableLocale ( availableLocales, locale )
|
||||
//
|
||||
// Carries an additional third argument in our implementation to provide the
|
||||
// default locale. See the doc-comment in the header file.
|
||||
bool
|
||||
js::intl_BestAvailableLocale(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 3);
|
||||
|
||||
SupportedLocaleKind kind;
|
||||
{
|
||||
JSLinearString* typeStr = args[0].toString()->ensureLinear(cx);
|
||||
if (!typeStr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StringEqualsAscii(typeStr, "Collator")) {
|
||||
kind = SupportedLocaleKind::Collator;
|
||||
} else if (StringEqualsAscii(typeStr, "DateTimeFormat")) {
|
||||
kind = SupportedLocaleKind::DateTimeFormat;
|
||||
} else if (StringEqualsAscii(typeStr, "NumberFormat")) {
|
||||
kind = SupportedLocaleKind::NumberFormat;
|
||||
} else if (StringEqualsAscii(typeStr, "PluralRules")) {
|
||||
kind = SupportedLocaleKind::PluralRules;
|
||||
} else {
|
||||
MOZ_ASSERT(StringEqualsAscii(typeStr, "RelativeTimeFormat"));
|
||||
kind = SupportedLocaleKind::RelativeTimeFormat;
|
||||
}
|
||||
}
|
||||
|
||||
RootedLinearString locale(cx, args[1].toString()->ensureLinear(cx));
|
||||
if (!locale) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
intl::LanguageTag tag(cx);
|
||||
bool ok;
|
||||
JS_TRY_VAR_OR_RETURN_FALSE(cx, ok, intl::LanguageTagParser::tryParse(cx, locale, tag));
|
||||
MOZ_ASSERT(ok, "locale is a structurally valid language tag");
|
||||
|
||||
MOZ_ASSERT(!tag.unicodeExtension(),
|
||||
"locale must contain no Unicode extensions");
|
||||
|
||||
if (!tag.canonicalize(cx)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSString* tagStr = tag.toString(cx);
|
||||
if (!tagStr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool canonical;
|
||||
if (!EqualStrings(cx, locale, tagStr, &canonical)) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(canonical, "locale is a canonicalized language tag");
|
||||
}
|
||||
#endif
|
||||
|
||||
MOZ_ASSERT(args[2].isNull() || args[2].isString());
|
||||
|
||||
RootedLinearString defaultLocale(cx);
|
||||
if (args[2].isString()) {
|
||||
defaultLocale = args[2].toString()->ensureLinear(cx);
|
||||
if (!defaultLocale) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
JSString* result;
|
||||
JS_TRY_VAR_OR_RETURN_FALSE(cx, result, BestAvailableLocale(cx, kind, locale, defaultLocale));
|
||||
|
||||
if (result) {
|
||||
args.rval().setString(result);
|
||||
} else {
|
||||
args.rval().setUndefined();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_supportedLocaleOrFallback(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
|
||||
RootedLinearString locale(cx, args[0].toString()->ensureLinear(cx));
|
||||
if (!locale) {
|
||||
return false;
|
||||
}
|
||||
|
||||
intl::LanguageTag tag(cx);
|
||||
bool ok;
|
||||
JS_TRY_VAR_OR_RETURN_FALSE(cx, ok, intl::LanguageTagParser::tryParse(cx, locale, tag));
|
||||
|
||||
RootedLinearString candidate(cx);
|
||||
if (!ok) {
|
||||
candidate = NewStringCopyZ<CanGC>(cx, intl::LastDitchLocale());
|
||||
if (!candidate) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!tag.canonicalize(cx)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The default locale must be in [[AvailableLocales]], and that list must
|
||||
// not contain any locales with Unicode extension sequences, so remove any
|
||||
// present in the candidate.
|
||||
tag.clearUnicodeExtension();
|
||||
|
||||
JSString* canonical = tag.toString(cx);
|
||||
if (!canonical) {
|
||||
return false;
|
||||
}
|
||||
|
||||
candidate = canonical->ensureLinear(cx);
|
||||
if (!candidate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& mapping : js::intl::oldStyleLanguageTagMappings) {
|
||||
const char* oldStyle = mapping.oldStyle;
|
||||
const char* modernStyle = mapping.modernStyle;
|
||||
|
||||
if (StringEqualsAscii(candidate, oldStyle)) {
|
||||
candidate = NewStringCopyZ<CanGC>(cx, modernStyle);
|
||||
if (!candidate) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 9.1 Internal slots of Service Constructors
|
||||
//
|
||||
// - [[AvailableLocales]] is a List [...]. The list must include the value
|
||||
// returned by the DefaultLocale abstract operation (6.2.4), [...].
|
||||
//
|
||||
// That implies we must ignore any candidate which isn't supported by all Intl
|
||||
// service constructors.
|
||||
//
|
||||
// Note: We don't test the supported locales of either Intl.PluralRules or
|
||||
// Intl.RelativeTimeFormat, because ICU doesn't provide the necessary API to
|
||||
// return actual set of supported locales for these constructors. Instead it
|
||||
// returns the complete set of available locales for ULocale, which is a
|
||||
// superset of the locales supported by Collator, NumberFormat, and
|
||||
// DateTimeFormat.
|
||||
bool isSupported = true;
|
||||
for (auto kind : {SupportedLocaleKind::Collator, SupportedLocaleKind::DateTimeFormat,
|
||||
SupportedLocaleKind::NumberFormat}) {
|
||||
JSString* supported;
|
||||
JS_TRY_VAR_OR_RETURN_FALSE(cx, supported, BestAvailableLocale(cx, kind, candidate, nullptr));
|
||||
|
||||
if (!supported) {
|
||||
isSupported = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSupported) {
|
||||
candidate = NewStringCopyZ<CanGC>(cx, intl::LastDitchLocale());
|
||||
if (!candidate) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
args.rval().setString(candidate);
|
||||
return true;
|
||||
}
|
||||
|
||||
const Class js::IntlClass = {
|
||||
js_Object_str,
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Intl)
|
||||
|
|
@ -454,10 +727,16 @@ GlobalObject::initIntlObject(JSContext* cx, Handle<GlobalObject*> global)
|
|||
RootedObject collatorProto(cx, CreateCollatorPrototype(cx, intl, global));
|
||||
if (!collatorProto)
|
||||
return false;
|
||||
RootedObject dateTimeFormatProto(cx, CreateDateTimeFormatPrototype(cx, intl, global));
|
||||
RootedObject dateTimeFormatProto(cx), dateTimeFormat(cx);
|
||||
dateTimeFormatProto = CreateDateTimeFormatPrototype(cx, intl, global, &dateTimeFormat, DateTimeFormatOptions::Standard);
|
||||
if (!dateTimeFormatProto)
|
||||
return false;
|
||||
RootedObject numberFormatProto(cx, CreateNumberFormatPrototype(cx, intl, global));
|
||||
RootedObject localeProto(cx);
|
||||
localeProto = CreateLocalePrototype(cx, intl, global);
|
||||
if (!localeProto)
|
||||
return false;
|
||||
RootedObject numberFormatProto(cx), numberFormat(cx);
|
||||
numberFormatProto = CreateNumberFormatPrototype(cx, intl, global, &numberFormat);
|
||||
if (!numberFormatProto)
|
||||
return false;
|
||||
RootedObject pluralRulesProto(cx, CreatePluralRulesPrototype(cx, intl, global));
|
||||
|
|
@ -487,7 +766,10 @@ GlobalObject::initIntlObject(JSContext* cx, Handle<GlobalObject*> global)
|
|||
// |getPrototype(JSProto_*)|, but that has global-object-property-related
|
||||
// baggage we don't need or want, so we use one-off reserved slots.
|
||||
global->setReservedSlot(COLLATOR_PROTO, ObjectValue(*collatorProto));
|
||||
global->setReservedSlot(DATE_TIME_FORMAT, ObjectValue(*dateTimeFormat));
|
||||
global->setReservedSlot(DATE_TIME_FORMAT_PROTO, ObjectValue(*dateTimeFormatProto));
|
||||
global->setReservedSlot(LOCALE_PROTO, ObjectValue(*localeProto));
|
||||
global->setReservedSlot(NUMBER_FORMAT, ObjectValue(*numberFormat));
|
||||
global->setReservedSlot(NUMBER_FORMAT_PROTO, ObjectValue(*numberFormatProto));
|
||||
global->setReservedSlot(PLURAL_RULES_PROTO, ObjectValue(*pluralRulesProto));
|
||||
global->setReservedSlot(RELATIVE_TIME_FORMAT_PROTO, ObjectValue(*relativeTimeFmtProto));
|
||||
|
|
|
|||
|
|
@ -95,6 +95,35 @@ intl_GetCalendarInfo(JSContext* cx, unsigned argc, JS::Value* vp);
|
|||
extern MOZ_MUST_USE bool
|
||||
intl_ComputeDisplayNames(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
/**
|
||||
* Compares a BCP 47 language tag against the locales in availableLocales and
|
||||
* returns the best available match -- or |undefined| if no match was found.
|
||||
* Uses the fallback mechanism of RFC 4647, section 3.4.
|
||||
*
|
||||
* The set of available locales consulted doesn't necessarily include the
|
||||
* default locale or any generalized forms of it (e.g. "de" is a more-general
|
||||
* form of "de-CH"). If you want to be sure to consider the default local and
|
||||
* its generalized forms (you usually will), pass the default locale as the
|
||||
* value of |defaultOrNull|; otherwise pass null.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 9.2.2.
|
||||
* Spec: RFC 4647, section 3.4.
|
||||
*
|
||||
* Usage: result = intl_BestAvailableLocale("Collator", locale, defaultOrNull)
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_BestAvailableLocale(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
/**
|
||||
* Returns the input locale in its canonicalized form if ICU supports that
|
||||
* locale (perhaps via fallback, e.g. supporting "de-ZA" through "de" support
|
||||
* implied by a "de-DE" locale). Otherwise uses the last-ditch locale.
|
||||
*
|
||||
* Usage: result = intl_supportedLocaleOrFallback(locale)
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_supportedLocaleOrFallback(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* builtin_intl_IntlObject_h */
|
||||
|
|
|
|||
|
|
@ -3,44 +3,79 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function Intl_getCanonicalLocales(locales) {
|
||||
let codes = CanonicalizeLocaleList(locales);
|
||||
let result = [];
|
||||
// Step 1.
|
||||
var localeList = CanonicalizeLocaleList(locales);
|
||||
|
||||
let len = codes.length;
|
||||
let k = 0;
|
||||
// Step 2 (Inlined CreateArrayFromList).
|
||||
var array = [];
|
||||
|
||||
while (k < len) {
|
||||
_DefineDataProperty(result, k, codes[k]);
|
||||
k++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
for (var n = 0, len = localeList.length; n < len; n++)
|
||||
_DefineDataProperty(array, n, localeList[n]);
|
||||
|
||||
function Intl_getCalendarInfo(locales) {
|
||||
const requestedLocales = CanonicalizeLocaleList(locales);
|
||||
|
||||
const DateTimeFormat = dateTimeFormatInternalProperties;
|
||||
const localeData = DateTimeFormat.localeData;
|
||||
|
||||
const localeOpt = new Record();
|
||||
localeOpt.localeMatcher = "best fit";
|
||||
|
||||
const r = ResolveLocale(callFunction(DateTimeFormat.availableLocales, DateTimeFormat),
|
||||
requestedLocales,
|
||||
localeOpt,
|
||||
DateTimeFormat.relevantExtensionKeys,
|
||||
localeData);
|
||||
|
||||
const result = intl_GetCalendarInfo(r.locale);
|
||||
result.calendar = r.ca;
|
||||
result.locale = r.locale;
|
||||
|
||||
return result;
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is a custom method designed after Intl API, but currently
|
||||
* not part of the spec or spec proposal.
|
||||
* This function is a custom function in the style of the standard Intl.*
|
||||
* functions, that isn't part of any spec or proposal yet.
|
||||
*
|
||||
* Returns an object with the following properties:
|
||||
* locale:
|
||||
* The actual resolved locale.
|
||||
*
|
||||
* calendar:
|
||||
* The default calendar of the resolved locale.
|
||||
*
|
||||
* firstDayOfWeek:
|
||||
* The first day of the week for the resolved locale.
|
||||
*
|
||||
* minDays:
|
||||
* The minimum number of days in a week for the resolved locale.
|
||||
*
|
||||
* weekendStart:
|
||||
* The day considered the beginning of a weekend for the resolved locale.
|
||||
*
|
||||
* weekendEnd:
|
||||
* The day considered the end of a weekend for the resolved locale.
|
||||
*
|
||||
* Days are encoded as integers in the range 1=Sunday to 7=Saturday.
|
||||
*/
|
||||
function Intl_getCalendarInfo(locales) {
|
||||
// 1. Let requestLocales be ? CanonicalizeLocaleList(locales).
|
||||
const requestedLocales = CanonicalizeLocaleList(locales);
|
||||
|
||||
const DateTimeFormat = dateTimeFormatInternalProperties;
|
||||
|
||||
// 2. Let localeData be %DateTimeFormat%.[[localeData]].
|
||||
const localeData = DateTimeFormat.localeData;
|
||||
|
||||
// 3. Let localeOpt be a new Record.
|
||||
const localeOpt = new Record();
|
||||
|
||||
// 4. Set localeOpt.[[localeMatcher]] to "best fit".
|
||||
localeOpt.localeMatcher = "best fit";
|
||||
|
||||
// 5. Let r be ResolveLocale(%DateTimeFormat%.[[availableLocales]],
|
||||
// requestedLocales, localeOpt,
|
||||
// %DateTimeFormat%.[[relevantExtensionKeys]], localeData).
|
||||
const r = ResolveLocale("DateTimeFormat",
|
||||
requestedLocales,
|
||||
localeOpt,
|
||||
DateTimeFormat.relevantExtensionKeys,
|
||||
localeData);
|
||||
|
||||
// 6. Let result be GetCalendarInfo(r.[[locale]]).
|
||||
const result = intl_GetCalendarInfo(r.locale);
|
||||
_DefineDataProperty(result, "calendar", r.ca);
|
||||
_DefineDataProperty(result, "locale", r.locale);
|
||||
|
||||
// 7. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is a custom function in the style of the standard Intl.*
|
||||
* functions, that isn't part of any spec or proposal yet.
|
||||
* We want to use it internally to retrieve translated values from CLDR in
|
||||
* order to ensure they're aligned with what Intl API returns.
|
||||
*
|
||||
|
|
@ -86,21 +121,23 @@ function Intl_getDisplayNames(locales, options) {
|
|||
// 4. Let localeData be %DateTimeFormat%.[[localeData]].
|
||||
const localeData = DateTimeFormat.localeData;
|
||||
|
||||
// 5. Let opt be a new Record.
|
||||
// 5. Let localeOpt be a new Record.
|
||||
const localeOpt = new Record();
|
||||
|
||||
// 6. Set localeOpt.[[localeMatcher]] to "best fit".
|
||||
localeOpt.localeMatcher = "best fit";
|
||||
|
||||
// 7. Let r be ResolveLocale(%DateTimeFormat%.[[availableLocales]], requestedLocales, localeOpt,
|
||||
// %DateTimeFormat%.[[relevantExtensionKeys]], localeData).
|
||||
const r = ResolveLocale(callFunction(DateTimeFormat.availableLocales, DateTimeFormat),
|
||||
requestedLocales,
|
||||
localeOpt,
|
||||
DateTimeFormat.relevantExtensionKeys,
|
||||
localeData);
|
||||
const r = ResolveLocale("DateTimeFormat",
|
||||
requestedLocales,
|
||||
localeOpt,
|
||||
DateTimeFormat.relevantExtensionKeys,
|
||||
localeData);
|
||||
|
||||
// 8. Let style be ? GetOption(options, "style", "string", « "long", "short", "narrow" », "long").
|
||||
const style = GetOption(options, "style", "string", ["long", "short", "narrow"], "long");
|
||||
|
||||
// 9. Let keys be ? Get(options, "keys").
|
||||
let keys = options.keys;
|
||||
|
||||
|
|
@ -119,8 +156,10 @@ function Intl_getDisplayNames(locales, options) {
|
|||
// |intl_ComputeDisplayNames| may infallibly access the list's length via
|
||||
// |ArrayObject::length|.)
|
||||
let processedKeys = [];
|
||||
|
||||
// 13. Let len be ? ToLength(? Get(keys, "length")).
|
||||
let len = ToLength(keys.length);
|
||||
|
||||
// 14. Let i be 0.
|
||||
// 15. Repeat, while i < len
|
||||
for (let i = 0; i < len; i++) {
|
||||
|
|
|
|||
|
|
@ -1,382 +0,0 @@
|
|||
// Generated by make_intl_data.py. DO NOT EDIT.
|
||||
|
||||
// Mappings from complete tags to preferred values.
|
||||
// Derived from IANA Language Subtag Registry, file date 2016-10-12.
|
||||
// http://www.iana.org/assignments/language-subtag-registry
|
||||
var langTagMappings = {
|
||||
"art-lojban": "jbo",
|
||||
"cel-gaulish": "cel-gaulish",
|
||||
"en-gb-oed": "en-GB-oxendict",
|
||||
"i-ami": "ami",
|
||||
"i-bnn": "bnn",
|
||||
"i-default": "i-default",
|
||||
"i-enochian": "i-enochian",
|
||||
"i-hak": "hak",
|
||||
"i-klingon": "tlh",
|
||||
"i-lux": "lb",
|
||||
"i-mingo": "i-mingo",
|
||||
"i-navajo": "nv",
|
||||
"i-pwn": "pwn",
|
||||
"i-tao": "tao",
|
||||
"i-tay": "tay",
|
||||
"i-tsu": "tsu",
|
||||
"ja-latn-hepburn-heploc": "ja-Latn-alalc97",
|
||||
"no-bok": "nb",
|
||||
"no-nyn": "nn",
|
||||
"sgn-be-fr": "sfb",
|
||||
"sgn-be-nl": "vgt",
|
||||
"sgn-br": "bzs",
|
||||
"sgn-ch-de": "sgg",
|
||||
"sgn-co": "csn",
|
||||
"sgn-de": "gsg",
|
||||
"sgn-dk": "dsl",
|
||||
"sgn-es": "ssp",
|
||||
"sgn-fr": "fsl",
|
||||
"sgn-gb": "bfi",
|
||||
"sgn-gr": "gss",
|
||||
"sgn-ie": "isg",
|
||||
"sgn-it": "ise",
|
||||
"sgn-jp": "jsl",
|
||||
"sgn-mx": "mfs",
|
||||
"sgn-ni": "ncs",
|
||||
"sgn-nl": "dse",
|
||||
"sgn-no": "nsl",
|
||||
"sgn-pt": "psr",
|
||||
"sgn-se": "swl",
|
||||
"sgn-us": "ase",
|
||||
"sgn-za": "sfs",
|
||||
"zh-cmn": "cmn",
|
||||
"zh-cmn-hans": "cmn-Hans",
|
||||
"zh-cmn-hant": "cmn-Hant",
|
||||
"zh-gan": "gan",
|
||||
"zh-guoyu": "cmn",
|
||||
"zh-hakka": "hak",
|
||||
"zh-min": "zh-min",
|
||||
"zh-min-nan": "nan",
|
||||
"zh-wuu": "wuu",
|
||||
"zh-xiang": "hsn",
|
||||
"zh-yue": "yue",
|
||||
};
|
||||
|
||||
// Mappings from non-extlang subtags to preferred values.
|
||||
// Derived from IANA Language Subtag Registry, file date 2016-10-12.
|
||||
// http://www.iana.org/assignments/language-subtag-registry
|
||||
var langSubtagMappings = {
|
||||
"BU": "MM",
|
||||
"DD": "DE",
|
||||
"FX": "FR",
|
||||
"TP": "TL",
|
||||
"YD": "YE",
|
||||
"ZR": "CD",
|
||||
"aam": "aas",
|
||||
"adp": "dz",
|
||||
"aue": "ktz",
|
||||
"ayx": "nun",
|
||||
"bgm": "bcg",
|
||||
"bjd": "drl",
|
||||
"ccq": "rki",
|
||||
"cjr": "mom",
|
||||
"cka": "cmr",
|
||||
"cmk": "xch",
|
||||
"coy": "pij",
|
||||
"cqu": "quh",
|
||||
"drh": "khk",
|
||||
"drw": "prs",
|
||||
"gav": "dev",
|
||||
"gfx": "vaj",
|
||||
"ggn": "gvr",
|
||||
"gti": "nyc",
|
||||
"guv": "duz",
|
||||
"hrr": "jal",
|
||||
"ibi": "opa",
|
||||
"ilw": "gal",
|
||||
"in": "id",
|
||||
"iw": "he",
|
||||
"ji": "yi",
|
||||
"jw": "jv",
|
||||
"kgc": "tdf",
|
||||
"kgh": "kml",
|
||||
"koj": "kwv",
|
||||
"ktr": "dtp",
|
||||
"kvs": "gdj",
|
||||
"kwq": "yam",
|
||||
"kxe": "tvd",
|
||||
"kzj": "dtp",
|
||||
"kzt": "dtp",
|
||||
"lii": "raq",
|
||||
"lmm": "rmx",
|
||||
"meg": "cir",
|
||||
"mo": "ro",
|
||||
"mst": "mry",
|
||||
"mwj": "vaj",
|
||||
"myt": "mry",
|
||||
"nad": "xny",
|
||||
"nnx": "ngv",
|
||||
"nts": "pij",
|
||||
"oun": "vaj",
|
||||
"pcr": "adx",
|
||||
"pmc": "huw",
|
||||
"pmu": "phr",
|
||||
"ppa": "bfy",
|
||||
"ppr": "lcq",
|
||||
"pry": "prt",
|
||||
"puz": "pub",
|
||||
"sca": "hle",
|
||||
"tdu": "dtp",
|
||||
"thc": "tpo",
|
||||
"thx": "oyb",
|
||||
"tie": "ras",
|
||||
"tkk": "twm",
|
||||
"tlw": "weo",
|
||||
"tmp": "tyj",
|
||||
"tne": "kak",
|
||||
"tnf": "prs",
|
||||
"tsf": "taj",
|
||||
"uok": "ema",
|
||||
"xba": "cax",
|
||||
"xia": "acn",
|
||||
"xkh": "waw",
|
||||
"xsj": "suj",
|
||||
"ybd": "rki",
|
||||
"yma": "lrr",
|
||||
"ymt": "mtm",
|
||||
"yos": "zom",
|
||||
"yuu": "yug",
|
||||
};
|
||||
|
||||
// Mappings from extlang subtags to preferred values.
|
||||
// Derived from IANA Language Subtag Registry, file date 2016-10-12.
|
||||
// http://www.iana.org/assignments/language-subtag-registry
|
||||
var extlangMappings = {
|
||||
"aao": {preferred: "aao", prefix: "ar"},
|
||||
"abh": {preferred: "abh", prefix: "ar"},
|
||||
"abv": {preferred: "abv", prefix: "ar"},
|
||||
"acm": {preferred: "acm", prefix: "ar"},
|
||||
"acq": {preferred: "acq", prefix: "ar"},
|
||||
"acw": {preferred: "acw", prefix: "ar"},
|
||||
"acx": {preferred: "acx", prefix: "ar"},
|
||||
"acy": {preferred: "acy", prefix: "ar"},
|
||||
"adf": {preferred: "adf", prefix: "ar"},
|
||||
"ads": {preferred: "ads", prefix: "sgn"},
|
||||
"aeb": {preferred: "aeb", prefix: "ar"},
|
||||
"aec": {preferred: "aec", prefix: "ar"},
|
||||
"aed": {preferred: "aed", prefix: "sgn"},
|
||||
"aen": {preferred: "aen", prefix: "sgn"},
|
||||
"afb": {preferred: "afb", prefix: "ar"},
|
||||
"afg": {preferred: "afg", prefix: "sgn"},
|
||||
"ajp": {preferred: "ajp", prefix: "ar"},
|
||||
"apc": {preferred: "apc", prefix: "ar"},
|
||||
"apd": {preferred: "apd", prefix: "ar"},
|
||||
"arb": {preferred: "arb", prefix: "ar"},
|
||||
"arq": {preferred: "arq", prefix: "ar"},
|
||||
"ars": {preferred: "ars", prefix: "ar"},
|
||||
"ary": {preferred: "ary", prefix: "ar"},
|
||||
"arz": {preferred: "arz", prefix: "ar"},
|
||||
"ase": {preferred: "ase", prefix: "sgn"},
|
||||
"asf": {preferred: "asf", prefix: "sgn"},
|
||||
"asp": {preferred: "asp", prefix: "sgn"},
|
||||
"asq": {preferred: "asq", prefix: "sgn"},
|
||||
"asw": {preferred: "asw", prefix: "sgn"},
|
||||
"auz": {preferred: "auz", prefix: "ar"},
|
||||
"avl": {preferred: "avl", prefix: "ar"},
|
||||
"ayh": {preferred: "ayh", prefix: "ar"},
|
||||
"ayl": {preferred: "ayl", prefix: "ar"},
|
||||
"ayn": {preferred: "ayn", prefix: "ar"},
|
||||
"ayp": {preferred: "ayp", prefix: "ar"},
|
||||
"bbz": {preferred: "bbz", prefix: "ar"},
|
||||
"bfi": {preferred: "bfi", prefix: "sgn"},
|
||||
"bfk": {preferred: "bfk", prefix: "sgn"},
|
||||
"bjn": {preferred: "bjn", prefix: "ms"},
|
||||
"bog": {preferred: "bog", prefix: "sgn"},
|
||||
"bqn": {preferred: "bqn", prefix: "sgn"},
|
||||
"bqy": {preferred: "bqy", prefix: "sgn"},
|
||||
"btj": {preferred: "btj", prefix: "ms"},
|
||||
"bve": {preferred: "bve", prefix: "ms"},
|
||||
"bvl": {preferred: "bvl", prefix: "sgn"},
|
||||
"bvu": {preferred: "bvu", prefix: "ms"},
|
||||
"bzs": {preferred: "bzs", prefix: "sgn"},
|
||||
"cdo": {preferred: "cdo", prefix: "zh"},
|
||||
"cds": {preferred: "cds", prefix: "sgn"},
|
||||
"cjy": {preferred: "cjy", prefix: "zh"},
|
||||
"cmn": {preferred: "cmn", prefix: "zh"},
|
||||
"coa": {preferred: "coa", prefix: "ms"},
|
||||
"cpx": {preferred: "cpx", prefix: "zh"},
|
||||
"csc": {preferred: "csc", prefix: "sgn"},
|
||||
"csd": {preferred: "csd", prefix: "sgn"},
|
||||
"cse": {preferred: "cse", prefix: "sgn"},
|
||||
"csf": {preferred: "csf", prefix: "sgn"},
|
||||
"csg": {preferred: "csg", prefix: "sgn"},
|
||||
"csl": {preferred: "csl", prefix: "sgn"},
|
||||
"csn": {preferred: "csn", prefix: "sgn"},
|
||||
"csq": {preferred: "csq", prefix: "sgn"},
|
||||
"csr": {preferred: "csr", prefix: "sgn"},
|
||||
"czh": {preferred: "czh", prefix: "zh"},
|
||||
"czo": {preferred: "czo", prefix: "zh"},
|
||||
"doq": {preferred: "doq", prefix: "sgn"},
|
||||
"dse": {preferred: "dse", prefix: "sgn"},
|
||||
"dsl": {preferred: "dsl", prefix: "sgn"},
|
||||
"dup": {preferred: "dup", prefix: "ms"},
|
||||
"ecs": {preferred: "ecs", prefix: "sgn"},
|
||||
"esl": {preferred: "esl", prefix: "sgn"},
|
||||
"esn": {preferred: "esn", prefix: "sgn"},
|
||||
"eso": {preferred: "eso", prefix: "sgn"},
|
||||
"eth": {preferred: "eth", prefix: "sgn"},
|
||||
"fcs": {preferred: "fcs", prefix: "sgn"},
|
||||
"fse": {preferred: "fse", prefix: "sgn"},
|
||||
"fsl": {preferred: "fsl", prefix: "sgn"},
|
||||
"fss": {preferred: "fss", prefix: "sgn"},
|
||||
"gan": {preferred: "gan", prefix: "zh"},
|
||||
"gds": {preferred: "gds", prefix: "sgn"},
|
||||
"gom": {preferred: "gom", prefix: "kok"},
|
||||
"gse": {preferred: "gse", prefix: "sgn"},
|
||||
"gsg": {preferred: "gsg", prefix: "sgn"},
|
||||
"gsm": {preferred: "gsm", prefix: "sgn"},
|
||||
"gss": {preferred: "gss", prefix: "sgn"},
|
||||
"gus": {preferred: "gus", prefix: "sgn"},
|
||||
"hab": {preferred: "hab", prefix: "sgn"},
|
||||
"haf": {preferred: "haf", prefix: "sgn"},
|
||||
"hak": {preferred: "hak", prefix: "zh"},
|
||||
"hds": {preferred: "hds", prefix: "sgn"},
|
||||
"hji": {preferred: "hji", prefix: "ms"},
|
||||
"hks": {preferred: "hks", prefix: "sgn"},
|
||||
"hos": {preferred: "hos", prefix: "sgn"},
|
||||
"hps": {preferred: "hps", prefix: "sgn"},
|
||||
"hsh": {preferred: "hsh", prefix: "sgn"},
|
||||
"hsl": {preferred: "hsl", prefix: "sgn"},
|
||||
"hsn": {preferred: "hsn", prefix: "zh"},
|
||||
"icl": {preferred: "icl", prefix: "sgn"},
|
||||
"iks": {preferred: "iks", prefix: "sgn"},
|
||||
"ils": {preferred: "ils", prefix: "sgn"},
|
||||
"inl": {preferred: "inl", prefix: "sgn"},
|
||||
"ins": {preferred: "ins", prefix: "sgn"},
|
||||
"ise": {preferred: "ise", prefix: "sgn"},
|
||||
"isg": {preferred: "isg", prefix: "sgn"},
|
||||
"isr": {preferred: "isr", prefix: "sgn"},
|
||||
"jak": {preferred: "jak", prefix: "ms"},
|
||||
"jax": {preferred: "jax", prefix: "ms"},
|
||||
"jcs": {preferred: "jcs", prefix: "sgn"},
|
||||
"jhs": {preferred: "jhs", prefix: "sgn"},
|
||||
"jls": {preferred: "jls", prefix: "sgn"},
|
||||
"jos": {preferred: "jos", prefix: "sgn"},
|
||||
"jsl": {preferred: "jsl", prefix: "sgn"},
|
||||
"jus": {preferred: "jus", prefix: "sgn"},
|
||||
"kgi": {preferred: "kgi", prefix: "sgn"},
|
||||
"knn": {preferred: "knn", prefix: "kok"},
|
||||
"kvb": {preferred: "kvb", prefix: "ms"},
|
||||
"kvk": {preferred: "kvk", prefix: "sgn"},
|
||||
"kvr": {preferred: "kvr", prefix: "ms"},
|
||||
"kxd": {preferred: "kxd", prefix: "ms"},
|
||||
"lbs": {preferred: "lbs", prefix: "sgn"},
|
||||
"lce": {preferred: "lce", prefix: "ms"},
|
||||
"lcf": {preferred: "lcf", prefix: "ms"},
|
||||
"liw": {preferred: "liw", prefix: "ms"},
|
||||
"lls": {preferred: "lls", prefix: "sgn"},
|
||||
"lsg": {preferred: "lsg", prefix: "sgn"},
|
||||
"lsl": {preferred: "lsl", prefix: "sgn"},
|
||||
"lso": {preferred: "lso", prefix: "sgn"},
|
||||
"lsp": {preferred: "lsp", prefix: "sgn"},
|
||||
"lst": {preferred: "lst", prefix: "sgn"},
|
||||
"lsy": {preferred: "lsy", prefix: "sgn"},
|
||||
"ltg": {preferred: "ltg", prefix: "lv"},
|
||||
"lvs": {preferred: "lvs", prefix: "lv"},
|
||||
"lzh": {preferred: "lzh", prefix: "zh"},
|
||||
"max": {preferred: "max", prefix: "ms"},
|
||||
"mdl": {preferred: "mdl", prefix: "sgn"},
|
||||
"meo": {preferred: "meo", prefix: "ms"},
|
||||
"mfa": {preferred: "mfa", prefix: "ms"},
|
||||
"mfb": {preferred: "mfb", prefix: "ms"},
|
||||
"mfs": {preferred: "mfs", prefix: "sgn"},
|
||||
"min": {preferred: "min", prefix: "ms"},
|
||||
"mnp": {preferred: "mnp", prefix: "zh"},
|
||||
"mqg": {preferred: "mqg", prefix: "ms"},
|
||||
"mre": {preferred: "mre", prefix: "sgn"},
|
||||
"msd": {preferred: "msd", prefix: "sgn"},
|
||||
"msi": {preferred: "msi", prefix: "ms"},
|
||||
"msr": {preferred: "msr", prefix: "sgn"},
|
||||
"mui": {preferred: "mui", prefix: "ms"},
|
||||
"mzc": {preferred: "mzc", prefix: "sgn"},
|
||||
"mzg": {preferred: "mzg", prefix: "sgn"},
|
||||
"mzy": {preferred: "mzy", prefix: "sgn"},
|
||||
"nan": {preferred: "nan", prefix: "zh"},
|
||||
"nbs": {preferred: "nbs", prefix: "sgn"},
|
||||
"ncs": {preferred: "ncs", prefix: "sgn"},
|
||||
"nsi": {preferred: "nsi", prefix: "sgn"},
|
||||
"nsl": {preferred: "nsl", prefix: "sgn"},
|
||||
"nsp": {preferred: "nsp", prefix: "sgn"},
|
||||
"nsr": {preferred: "nsr", prefix: "sgn"},
|
||||
"nzs": {preferred: "nzs", prefix: "sgn"},
|
||||
"okl": {preferred: "okl", prefix: "sgn"},
|
||||
"orn": {preferred: "orn", prefix: "ms"},
|
||||
"ors": {preferred: "ors", prefix: "ms"},
|
||||
"pel": {preferred: "pel", prefix: "ms"},
|
||||
"pga": {preferred: "pga", prefix: "ar"},
|
||||
"pgz": {preferred: "pgz", prefix: "sgn"},
|
||||
"pks": {preferred: "pks", prefix: "sgn"},
|
||||
"prl": {preferred: "prl", prefix: "sgn"},
|
||||
"prz": {preferred: "prz", prefix: "sgn"},
|
||||
"psc": {preferred: "psc", prefix: "sgn"},
|
||||
"psd": {preferred: "psd", prefix: "sgn"},
|
||||
"pse": {preferred: "pse", prefix: "ms"},
|
||||
"psg": {preferred: "psg", prefix: "sgn"},
|
||||
"psl": {preferred: "psl", prefix: "sgn"},
|
||||
"pso": {preferred: "pso", prefix: "sgn"},
|
||||
"psp": {preferred: "psp", prefix: "sgn"},
|
||||
"psr": {preferred: "psr", prefix: "sgn"},
|
||||
"pys": {preferred: "pys", prefix: "sgn"},
|
||||
"rms": {preferred: "rms", prefix: "sgn"},
|
||||
"rsi": {preferred: "rsi", prefix: "sgn"},
|
||||
"rsl": {preferred: "rsl", prefix: "sgn"},
|
||||
"rsm": {preferred: "rsm", prefix: "sgn"},
|
||||
"sdl": {preferred: "sdl", prefix: "sgn"},
|
||||
"sfb": {preferred: "sfb", prefix: "sgn"},
|
||||
"sfs": {preferred: "sfs", prefix: "sgn"},
|
||||
"sgg": {preferred: "sgg", prefix: "sgn"},
|
||||
"sgx": {preferred: "sgx", prefix: "sgn"},
|
||||
"shu": {preferred: "shu", prefix: "ar"},
|
||||
"slf": {preferred: "slf", prefix: "sgn"},
|
||||
"sls": {preferred: "sls", prefix: "sgn"},
|
||||
"sqk": {preferred: "sqk", prefix: "sgn"},
|
||||
"sqs": {preferred: "sqs", prefix: "sgn"},
|
||||
"ssh": {preferred: "ssh", prefix: "ar"},
|
||||
"ssp": {preferred: "ssp", prefix: "sgn"},
|
||||
"ssr": {preferred: "ssr", prefix: "sgn"},
|
||||
"svk": {preferred: "svk", prefix: "sgn"},
|
||||
"swc": {preferred: "swc", prefix: "sw"},
|
||||
"swh": {preferred: "swh", prefix: "sw"},
|
||||
"swl": {preferred: "swl", prefix: "sgn"},
|
||||
"syy": {preferred: "syy", prefix: "sgn"},
|
||||
"tmw": {preferred: "tmw", prefix: "ms"},
|
||||
"tse": {preferred: "tse", prefix: "sgn"},
|
||||
"tsm": {preferred: "tsm", prefix: "sgn"},
|
||||
"tsq": {preferred: "tsq", prefix: "sgn"},
|
||||
"tss": {preferred: "tss", prefix: "sgn"},
|
||||
"tsy": {preferred: "tsy", prefix: "sgn"},
|
||||
"tza": {preferred: "tza", prefix: "sgn"},
|
||||
"ugn": {preferred: "ugn", prefix: "sgn"},
|
||||
"ugy": {preferred: "ugy", prefix: "sgn"},
|
||||
"ukl": {preferred: "ukl", prefix: "sgn"},
|
||||
"uks": {preferred: "uks", prefix: "sgn"},
|
||||
"urk": {preferred: "urk", prefix: "ms"},
|
||||
"uzn": {preferred: "uzn", prefix: "uz"},
|
||||
"uzs": {preferred: "uzs", prefix: "uz"},
|
||||
"vgt": {preferred: "vgt", prefix: "sgn"},
|
||||
"vkk": {preferred: "vkk", prefix: "ms"},
|
||||
"vkt": {preferred: "vkt", prefix: "ms"},
|
||||
"vsi": {preferred: "vsi", prefix: "sgn"},
|
||||
"vsl": {preferred: "vsl", prefix: "sgn"},
|
||||
"vsv": {preferred: "vsv", prefix: "sgn"},
|
||||
"wuu": {preferred: "wuu", prefix: "zh"},
|
||||
"xki": {preferred: "xki", prefix: "sgn"},
|
||||
"xml": {preferred: "xml", prefix: "sgn"},
|
||||
"xmm": {preferred: "xmm", prefix: "ms"},
|
||||
"xms": {preferred: "xms", prefix: "sgn"},
|
||||
"ygs": {preferred: "ygs", prefix: "sgn"},
|
||||
"yhs": {preferred: "yhs", prefix: "sgn"},
|
||||
"ysl": {preferred: "ysl", prefix: "sgn"},
|
||||
"yue": {preferred: "yue", prefix: "zh"},
|
||||
"zib": {preferred: "zib", prefix: "sgn"},
|
||||
"zlm": {preferred: "zlm", prefix: "ms"},
|
||||
"zmi": {preferred: "zmi", prefix: "ms"},
|
||||
"zsl": {preferred: "zsl", prefix: "sgn"},
|
||||
"zsm": {preferred: "zsm", prefix: "ms"},
|
||||
};
|
||||
1728
js/src/builtin/intl/LanguageTag.cpp
Normal file
1728
js/src/builtin/intl/LanguageTag.cpp
Normal file
File diff suppressed because it is too large
Load diff
770
js/src/builtin/intl/LanguageTag.h
Normal file
770
js/src/builtin/intl/LanguageTag.h
Normal file
|
|
@ -0,0 +1,770 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* 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/. */
|
||||
|
||||
/* Structured representation of Unicode locale IDs used with Intl functions. */
|
||||
|
||||
#ifndef builtin_intl_LanguageTag_h
|
||||
#define builtin_intl_LanguageTag_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Span.h"
|
||||
#include "mozilla/TextUtils.h"
|
||||
#include "mozilla/TypedEnumBits.h"
|
||||
#include "mozilla/Variant.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <utility>
|
||||
|
||||
#include "jsalloc.h"
|
||||
#include "js/Result.h"
|
||||
|
||||
#include "js/GCAPI.h"
|
||||
#include "js/Utility.h"
|
||||
#include "js/Vector.h"
|
||||
|
||||
struct JSContext;
|
||||
class JSLinearString;
|
||||
class JSString;
|
||||
class JSTracer;
|
||||
|
||||
namespace js {
|
||||
|
||||
namespace intl {
|
||||
|
||||
/**
|
||||
* Return true if |language| is a valid language subtag.
|
||||
*/
|
||||
template <typename CharT>
|
||||
bool IsStructurallyValidLanguageTag(mozilla::Span<const CharT> language);
|
||||
|
||||
/**
|
||||
* Return true if |script| is a valid script subtag.
|
||||
*/
|
||||
template <typename CharT>
|
||||
bool IsStructurallyValidScriptTag(mozilla::Span<const CharT> script);
|
||||
|
||||
/**
|
||||
* Return true if |region| is a valid region subtag.
|
||||
*/
|
||||
template <typename CharT>
|
||||
bool IsStructurallyValidRegionTag(mozilla::Span<const CharT> region);
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* Return true if |variant| is a valid variant subtag.
|
||||
*/
|
||||
bool IsStructurallyValidVariantTag(mozilla::Span<const char> variant);
|
||||
|
||||
/**
|
||||
* Return true if |extension| is a valid Unicode extension subtag.
|
||||
*/
|
||||
bool IsStructurallyValidUnicodeExtensionTag(
|
||||
mozilla::Span<const char> extension);
|
||||
|
||||
/**
|
||||
* Return true if |privateUse| is a valid private-use subtag.
|
||||
*/
|
||||
bool IsStructurallyValidPrivateUseTag(mozilla::Span<const char> privateUse);
|
||||
|
||||
#endif
|
||||
|
||||
template <typename CharT>
|
||||
char AsciiToLowerCase(CharT c) {
|
||||
MOZ_ASSERT(mozilla::IsAscii(c));
|
||||
return mozilla::IsAsciiUppercaseAlpha(c) ? (c + 0x20) : c;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
char AsciiToUpperCase(CharT c) {
|
||||
MOZ_ASSERT(mozilla::IsAscii(c));
|
||||
return mozilla::IsAsciiLowercaseAlpha(c) ? (c - 0x20) : c;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
void AsciiToLowerCase(CharT* chars, size_t length, char* dest) {
|
||||
// Tell the analysis the |std::transform| function can't GC.
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
|
||||
char (&fn)(CharT) = AsciiToLowerCase;
|
||||
std::transform(chars, chars + length, dest, fn);
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
void AsciiToUpperCase(CharT* chars, size_t length, char* dest) {
|
||||
// Tell the analysis the |std::transform| function can't GC.
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
|
||||
char (&fn)(CharT) = AsciiToUpperCase;
|
||||
std::transform(chars, chars + length, dest, fn);
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
void AsciiToTitleCase(CharT* chars, size_t length, char* dest) {
|
||||
if (length > 0) {
|
||||
AsciiToUpperCase(chars, 1, dest);
|
||||
AsciiToLowerCase(chars + 1, length - 1, dest + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Constants for language subtag lengths.
|
||||
namespace LanguageTagLimits {
|
||||
|
||||
// unicode_language_subtag = alpha{2,3} | alpha{5,8} ;
|
||||
static constexpr size_t LanguageLength = 8;
|
||||
|
||||
// unicode_script_subtag = alpha{4} ;
|
||||
static constexpr size_t ScriptLength = 4;
|
||||
|
||||
// unicode_region_subtag = (alpha{2} | digit{3}) ;
|
||||
static constexpr size_t RegionLength = 3;
|
||||
static constexpr size_t AlphaRegionLength = 2;
|
||||
static constexpr size_t DigitRegionLength = 3;
|
||||
|
||||
// key = alphanum alpha ;
|
||||
static constexpr size_t UnicodeKeyLength = 2;
|
||||
|
||||
// tkey = alpha digit ;
|
||||
static constexpr size_t TransformKeyLength = 2;
|
||||
|
||||
} // namespace LanguageTagLimits
|
||||
|
||||
// Fixed size language subtag which is stored inline in LanguageTag.
|
||||
template <size_t Length>
|
||||
class LanguageTagSubtag final {
|
||||
uint8_t length_ = 0;
|
||||
char chars_[Length] = {}; // zero initialize
|
||||
|
||||
public:
|
||||
LanguageTagSubtag() = default;
|
||||
|
||||
LanguageTagSubtag(const LanguageTagSubtag&) = delete;
|
||||
LanguageTagSubtag& operator=(const LanguageTagSubtag&) = delete;
|
||||
|
||||
size_t length() const { return length_; }
|
||||
bool missing() const { return length_ == 0; }
|
||||
bool present() const { return length_ > 0; }
|
||||
|
||||
mozilla::Span<const char> span() const { return {chars_, length_}; }
|
||||
|
||||
template <typename CharT>
|
||||
void set(mozilla::Span<const CharT> str) {
|
||||
MOZ_ASSERT(str.size() <= Length);
|
||||
std::copy_n(str.data(), str.size(), chars_);
|
||||
length_ = str.size();
|
||||
}
|
||||
|
||||
// The toXYZCase() methods are using |Length| instead of |length()|, because
|
||||
// current compilers (tested GCC and Clang) can't infer the maximum string
|
||||
// length - even when using hints like |std::min| - and instead are emitting
|
||||
// SIMD optimized code. Using a fixed sized length avoids emitting the SIMD
|
||||
// code. (Emitting SIMD code doesn't make sense here, because the SIMD code
|
||||
// only kicks in for long strings.) A fixed length will additionally ensure
|
||||
// the compiler unrolls the loop in the case conversion code.
|
||||
|
||||
void toLowerCase() { AsciiToLowerCase(chars_, Length, chars_); }
|
||||
|
||||
void toUpperCase() { AsciiToUpperCase(chars_, Length, chars_); }
|
||||
|
||||
void toTitleCase() { AsciiToTitleCase(chars_, Length, chars_); }
|
||||
|
||||
template <size_t N>
|
||||
bool equalTo(const char (&str)[N]) const {
|
||||
static_assert(N - 1 <= Length,
|
||||
"subtag literals must not exceed the maximum subtag length");
|
||||
|
||||
return length_ == N - 1 && memcmp(chars_, str, N - 1) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
using LanguageSubtag = LanguageTagSubtag<LanguageTagLimits::LanguageLength>;
|
||||
using ScriptSubtag = LanguageTagSubtag<LanguageTagLimits::ScriptLength>;
|
||||
using RegionSubtag = LanguageTagSubtag<LanguageTagLimits::RegionLength>;
|
||||
|
||||
/**
|
||||
* Object representing a language tag.
|
||||
*
|
||||
* All subtags are already in canonicalized case.
|
||||
*/
|
||||
class MOZ_STACK_CLASS LanguageTag final {
|
||||
LanguageSubtag language_ = {};
|
||||
ScriptSubtag script_ = {};
|
||||
RegionSubtag region_ = {};
|
||||
|
||||
using VariantsVector = Vector<JS::UniqueChars, 2>;
|
||||
using ExtensionsVector = Vector<JS::UniqueChars, 2>;
|
||||
|
||||
VariantsVector variants_;
|
||||
ExtensionsVector extensions_;
|
||||
JS::UniqueChars privateuse_ = nullptr;
|
||||
|
||||
friend class LanguageTagParser;
|
||||
|
||||
bool canonicalizeUnicodeExtension(JSContext* cx,
|
||||
JS::UniqueChars& unicodeExtension);
|
||||
|
||||
bool canonicalizeTransformExtension(JSContext* cx,
|
||||
JS::UniqueChars& transformExtension);
|
||||
|
||||
public:
|
||||
static bool languageMapping(LanguageSubtag& language);
|
||||
static bool complexLanguageMapping(const LanguageSubtag& language);
|
||||
|
||||
private:
|
||||
static bool regionMapping(RegionSubtag& region);
|
||||
static bool complexRegionMapping(const RegionSubtag& region);
|
||||
|
||||
void performComplexLanguageMappings();
|
||||
void performComplexRegionMappings();
|
||||
MOZ_MUST_USE bool performVariantMappings(JSContext* cx);
|
||||
|
||||
MOZ_MUST_USE bool updateGrandfatheredMappings(JSContext* cx);
|
||||
|
||||
static const char* replaceTransformExtensionType(
|
||||
mozilla::Span<const char> key, mozilla::Span<const char> type);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Given a Unicode key and type, return the null-terminated preferred
|
||||
* replacement for that type if there is one, or null if there is none, e.g.
|
||||
* in effect
|
||||
* |replaceUnicodeExtensionType("ca", "islamicc") == "islamic-civil"|
|
||||
* and
|
||||
* |replaceUnicodeExtensionType("ca", "islamic-civil") == nullptr|.
|
||||
*/
|
||||
static const char* replaceUnicodeExtensionType(
|
||||
mozilla::Span<const char> key, mozilla::Span<const char> type);
|
||||
|
||||
public:
|
||||
explicit LanguageTag(JSContext* cx) : variants_(cx), extensions_(cx) {}
|
||||
|
||||
LanguageTag(const LanguageTag&) = delete;
|
||||
LanguageTag& operator=(const LanguageTag&) = delete;
|
||||
|
||||
const LanguageSubtag& language() const { return language_; }
|
||||
const ScriptSubtag& script() const { return script_; }
|
||||
const RegionSubtag& region() const { return region_; }
|
||||
const auto& variants() const { return variants_; }
|
||||
const auto& extensions() const { return extensions_; }
|
||||
const char* privateuse() const { return privateuse_.get(); }
|
||||
|
||||
/**
|
||||
* Return the Unicode extension subtag or nullptr if not present.
|
||||
*/
|
||||
const char* unicodeExtension() const;
|
||||
|
||||
private:
|
||||
ptrdiff_t unicodeExtensionIndex() const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Set the language subtag. The input must be a valid language subtag.
|
||||
*/
|
||||
template <size_t N>
|
||||
void setLanguage(const char (&language)[N]) {
|
||||
mozilla::Span<const char> span(language, N - 1);
|
||||
MOZ_ASSERT(IsStructurallyValidLanguageTag(span));
|
||||
language_.set(span);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the language subtag. The input must be a valid language subtag.
|
||||
*/
|
||||
void setLanguage(const LanguageSubtag& language) {
|
||||
MOZ_ASSERT(IsStructurallyValidLanguageTag(language.span()));
|
||||
language_.set(language.span());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the script subtag. The input must be a valid script subtag.
|
||||
*/
|
||||
template <size_t N>
|
||||
void setScript(const char (&script)[N]) {
|
||||
mozilla::Span<const char> span(script, N - 1);
|
||||
MOZ_ASSERT(IsStructurallyValidScriptTag(span));
|
||||
script_.set(span);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the script subtag. The input must be a valid script subtag or the empty
|
||||
* string.
|
||||
*/
|
||||
void setScript(const ScriptSubtag& script) {
|
||||
MOZ_ASSERT(script.missing() || IsStructurallyValidScriptTag(script.span()));
|
||||
script_.set(script.span());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the region subtag. The input must be a valid region subtag.
|
||||
*/
|
||||
template <size_t N>
|
||||
void setRegion(const char (®ion)[N]) {
|
||||
mozilla::Span<const char> span(region, N - 1);
|
||||
MOZ_ASSERT(IsStructurallyValidRegionTag(span));
|
||||
region_.set(span);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the region subtag. The input must be a valid region subtag or the empty
|
||||
* empty string.
|
||||
*/
|
||||
void setRegion(const RegionSubtag& region) {
|
||||
MOZ_ASSERT(region.missing() || IsStructurallyValidRegionTag(region.span()));
|
||||
region_.set(region.span());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all variant subtags.
|
||||
*/
|
||||
void clearVariants() { variants_.clearAndFree(); }
|
||||
|
||||
/**
|
||||
* Set the Unicode extension subtag. The input must be a valid Unicode
|
||||
* extension subtag.
|
||||
*/
|
||||
bool setUnicodeExtension(JS::UniqueChars extension);
|
||||
|
||||
/**
|
||||
* Remove any Unicode extension subtag if present.
|
||||
*/
|
||||
void clearUnicodeExtension();
|
||||
|
||||
/**
|
||||
* Set the private-use subtag. The input must be a valid private-use subtag
|
||||
* or nullptr.
|
||||
*/
|
||||
void setPrivateuse(JS::UniqueChars privateuse) {
|
||||
MOZ_ASSERT(!privateuse ||
|
||||
IsStructurallyValidPrivateUseTag(
|
||||
{privateuse.get(), strlen(privateuse.get())}));
|
||||
privateuse_ = std::move(privateuse);
|
||||
}
|
||||
|
||||
private:
|
||||
enum class DuplicateVariants { Reject, Accept };
|
||||
|
||||
bool canonicalizeBaseName(JSContext* cx, DuplicateVariants duplicateVariants);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Canonicalize the base-name subtags, that means the language, script,
|
||||
* region, and variant subtags.
|
||||
*/
|
||||
bool canonicalizeBaseName(JSContext* cx) {
|
||||
return canonicalizeBaseName(cx, DuplicateVariants::Reject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Canonicalize all extension subtags.
|
||||
*/
|
||||
bool canonicalizeExtensions(JSContext* cx);
|
||||
|
||||
/**
|
||||
* Canonicalizes the given structurally valid Unicode BCP 47 locale
|
||||
* identifier, including regularized case of subtags. For example, the
|
||||
* language tag Zh-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE,
|
||||
* where
|
||||
*
|
||||
* Zh ; 2*3ALPHA
|
||||
* -haNS ; ["-" script]
|
||||
* -bu ; ["-" region]
|
||||
* -variant2 ; *("-" variant)
|
||||
* -Variant1
|
||||
* -u-ca-chinese ; *("-" extension)
|
||||
* -t-Zh-laTN
|
||||
* -x-PRIVATE ; ["-" privateuse]
|
||||
*
|
||||
* becomes zh-Hans-MM-variant1-variant2-t-zh-latn-u-ca-chinese-x-private
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 6.2.3.
|
||||
*/
|
||||
bool canonicalize(JSContext* cx) {
|
||||
return canonicalizeBaseName(cx) && canonicalizeExtensions(cx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the string representation of this language tag.
|
||||
*/
|
||||
JSString* toString(JSContext* cx) const;
|
||||
|
||||
/**
|
||||
* Return the string representation of this language tag as a null-terminated
|
||||
* C-string.
|
||||
*/
|
||||
JS::UniqueChars toStringZ(JSContext* cx) const;
|
||||
|
||||
/**
|
||||
* Add likely-subtags to the language tag.
|
||||
*
|
||||
* Spec: <https://www.unicode.org/reports/tr35/#Likely_Subtags>
|
||||
*/
|
||||
bool addLikelySubtags(JSContext* cx);
|
||||
|
||||
/**
|
||||
* Remove likely-subtags from the language tag.
|
||||
*
|
||||
* Spec: <https://www.unicode.org/reports/tr35/#Likely_Subtags>
|
||||
*/
|
||||
bool removeLikelySubtags(JSContext* cx);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parser for Unicode BCP 47 locale identifiers.
|
||||
*
|
||||
* <https://unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers>
|
||||
*/
|
||||
class MOZ_STACK_CLASS LanguageTagParser final {
|
||||
public:
|
||||
// Exposed as |public| for |MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS|.
|
||||
enum class TokenKind : uint8_t {
|
||||
None = 0b000,
|
||||
Alpha = 0b001,
|
||||
Digit = 0b010,
|
||||
AlphaDigit = 0b011,
|
||||
Error = 0b100
|
||||
};
|
||||
|
||||
private:
|
||||
class Token final {
|
||||
size_t index_;
|
||||
size_t length_;
|
||||
TokenKind kind_;
|
||||
|
||||
public:
|
||||
Token(TokenKind kind, size_t index, size_t length)
|
||||
: index_(index), length_(length), kind_(kind) {}
|
||||
|
||||
TokenKind kind() const { return kind_; }
|
||||
size_t index() const { return index_; }
|
||||
size_t length() const { return length_; }
|
||||
|
||||
bool isError() const { return kind_ == TokenKind::Error; }
|
||||
bool isNone() const { return kind_ == TokenKind::None; }
|
||||
bool isAlpha() const { return kind_ == TokenKind::Alpha; }
|
||||
bool isDigit() const { return kind_ == TokenKind::Digit; }
|
||||
bool isAlphaDigit() const { return kind_ == TokenKind::AlphaDigit; }
|
||||
};
|
||||
|
||||
using LocaleChars = mozilla::Variant<const JS::Latin1Char*, const char16_t*>;
|
||||
|
||||
const LocaleChars& locale_;
|
||||
size_t length_;
|
||||
size_t index_ = 0;
|
||||
|
||||
LanguageTagParser(const LocaleChars& locale, size_t length)
|
||||
: locale_(locale), length_(length) {}
|
||||
|
||||
char16_t charAtUnchecked(size_t index) const {
|
||||
if (locale_.is<const JS::Latin1Char*>()) {
|
||||
return locale_.as<const JS::Latin1Char*>()[index];
|
||||
}
|
||||
return locale_.as<const char16_t*>()[index];
|
||||
}
|
||||
|
||||
char charAt(size_t index) const {
|
||||
char16_t c = charAtUnchecked(index);
|
||||
MOZ_ASSERT(mozilla::IsAscii(c));
|
||||
return c;
|
||||
}
|
||||
|
||||
// Copy the token characters into |subtag|.
|
||||
template <size_t N>
|
||||
void copyChars(const Token& tok, LanguageTagSubtag<N>& subtag) const {
|
||||
size_t index = tok.index();
|
||||
size_t length = tok.length();
|
||||
if (locale_.is<const JS::Latin1Char*>()) {
|
||||
using T = const JS::Latin1Char;
|
||||
subtag.set(mozilla::MakeSpan(locale_.as<T*>() + index, length));
|
||||
} else {
|
||||
using T = const char16_t;
|
||||
subtag.set(mozilla::MakeSpan(locale_.as<T*>() + index, length));
|
||||
}
|
||||
}
|
||||
|
||||
// Create a string copy of |length| characters starting at |index|.
|
||||
JS::UniqueChars chars(JSContext* cx, size_t index, size_t length) const;
|
||||
|
||||
// Create a string copy of the token characters.
|
||||
JS::UniqueChars chars(JSContext* cx, const Token& tok) const {
|
||||
return chars(cx, tok.index(), tok.length());
|
||||
}
|
||||
|
||||
JS::UniqueChars extension(JSContext* cx, const Token& start,
|
||||
const Token& end) const {
|
||||
MOZ_ASSERT(start.index() < end.index());
|
||||
|
||||
size_t length = end.index() - 1 - start.index();
|
||||
return chars(cx, start.index(), length);
|
||||
}
|
||||
|
||||
Token nextToken();
|
||||
|
||||
// unicode_language_subtag = alpha{2,3} | alpha{5,8} ;
|
||||
//
|
||||
// Four character language subtags are not allowed in Unicode BCP 47 locale
|
||||
// identifiers. Also see the comparison to Unicode CLDR locale identifiers in
|
||||
// <https://unicode.org/reports/tr35/#BCP_47_Conformance>.
|
||||
bool isLanguage(const Token& tok) const {
|
||||
return tok.isAlpha() && ((2 <= tok.length() && tok.length() <= 3) ||
|
||||
(5 <= tok.length() && tok.length() <= 8));
|
||||
}
|
||||
|
||||
// unicode_script_subtag = alpha{4} ;
|
||||
bool isScript(const Token& tok) const {
|
||||
return tok.isAlpha() && tok.length() == 4;
|
||||
}
|
||||
|
||||
// unicode_region_subtag = (alpha{2} | digit{3}) ;
|
||||
bool isRegion(const Token& tok) const {
|
||||
return (tok.isAlpha() && tok.length() == 2) ||
|
||||
(tok.isDigit() && tok.length() == 3);
|
||||
}
|
||||
|
||||
// unicode_variant_subtag = (alphanum{5,8} | digit alphanum{3}) ;
|
||||
bool isVariant(const Token& tok) const {
|
||||
return (5 <= tok.length() && tok.length() <= 8) ||
|
||||
(tok.length() == 4 && mozilla::IsAsciiDigit(charAt(tok.index())));
|
||||
}
|
||||
|
||||
// Returns the code unit of the first character at the given singleton token.
|
||||
// Always returns the lower case form of an alphabetical character.
|
||||
char singletonKey(const Token& tok) const {
|
||||
MOZ_ASSERT(tok.length() == 1);
|
||||
return AsciiToLowerCase(charAt(tok.index()));
|
||||
}
|
||||
|
||||
// extensions = unicode_locale_extensions |
|
||||
// transformed_extensions |
|
||||
// other_extensions ;
|
||||
//
|
||||
// unicode_locale_extensions = sep [uU] ((sep keyword)+ |
|
||||
// (sep attribute)+ (sep keyword)*) ;
|
||||
//
|
||||
// transformed_extensions = sep [tT] ((sep tlang (sep tfield)*) |
|
||||
// (sep tfield)+) ;
|
||||
//
|
||||
// other_extensions = sep [alphanum-[tTuUxX]] (sep alphanum{2,8})+ ;
|
||||
bool isExtensionStart(const Token& tok) const {
|
||||
return tok.length() == 1 && singletonKey(tok) != 'x';
|
||||
}
|
||||
|
||||
// other_extensions = sep [alphanum-[tTuUxX]] (sep alphanum{2,8})+ ;
|
||||
bool isOtherExtensionPart(const Token& tok) const {
|
||||
return 2 <= tok.length() && tok.length() <= 8;
|
||||
}
|
||||
|
||||
// unicode_locale_extensions = sep [uU] ((sep keyword)+ |
|
||||
// (sep attribute)+ (sep keyword)*) ;
|
||||
// keyword = key (sep type)? ;
|
||||
bool isUnicodeExtensionPart(const Token& tok) const {
|
||||
return isUnicodeExtensionKey(tok) || isUnicodeExtensionType(tok) ||
|
||||
isUnicodeExtensionAttribute(tok);
|
||||
}
|
||||
|
||||
// attribute = alphanum{3,8} ;
|
||||
bool isUnicodeExtensionAttribute(const Token& tok) const {
|
||||
return 3 <= tok.length() && tok.length() <= 8;
|
||||
}
|
||||
|
||||
// key = alphanum alpha ;
|
||||
bool isUnicodeExtensionKey(const Token& tok) const {
|
||||
return tok.length() == 2 && mozilla::IsAsciiAlpha(charAt(tok.index() + 1));
|
||||
}
|
||||
|
||||
// type = alphanum{3,8} (sep alphanum{3,8})* ;
|
||||
bool isUnicodeExtensionType(const Token& tok) const {
|
||||
return 3 <= tok.length() && tok.length() <= 8;
|
||||
}
|
||||
|
||||
// tkey = alpha digit ;
|
||||
bool isTransformExtensionKey(const Token& tok) const {
|
||||
return tok.length() == 2 && mozilla::IsAsciiAlpha(charAt(tok.index())) &&
|
||||
mozilla::IsAsciiDigit(charAt(tok.index() + 1));
|
||||
}
|
||||
|
||||
// tvalue = (sep alphanum{3,8})+ ;
|
||||
bool isTransformExtensionPart(const Token& tok) const {
|
||||
return 3 <= tok.length() && tok.length() <= 8;
|
||||
}
|
||||
|
||||
// pu_extensions = sep [xX] (sep alphanum{1,8})+ ;
|
||||
bool isPrivateUseStart(const Token& tok) const {
|
||||
return tok.length() == 1 && singletonKey(tok) == 'x';
|
||||
}
|
||||
|
||||
// pu_extensions = sep [xX] (sep alphanum{1,8})+ ;
|
||||
bool isPrivateUsePart(const Token& tok) const {
|
||||
return 1 <= tok.length() && tok.length() <= 8;
|
||||
}
|
||||
|
||||
// Helper function for use in |parseBaseName| and
|
||||
// |parseTlangInTransformExtension|. Do not use this directly!
|
||||
static JS::Result<bool> internalParseBaseName(JSContext* cx,
|
||||
LanguageTagParser& ts,
|
||||
LanguageTag& tag, Token& tok);
|
||||
|
||||
// Parse the `unicode_language_id` production, i.e. the
|
||||
// language/script/region/variants portion of a language tag, into |tag|.
|
||||
// |tok| must be the current token.
|
||||
static JS::Result<bool> parseBaseName(JSContext* cx, LanguageTagParser& ts,
|
||||
LanguageTag& tag, Token& tok) {
|
||||
return internalParseBaseName(cx, ts, tag, tok);
|
||||
}
|
||||
|
||||
// Parse the `tlang` production within a parsed 't' transform extension.
|
||||
// The precise requirements for "previously parsed" are:
|
||||
//
|
||||
// * the input begins from current token |tok| with a valid `tlang`
|
||||
// * the `tlang` is wholly lowercase (*not* canonical case)
|
||||
// * variant subtags in the `tlang` may contain duplicates and be
|
||||
// unordered
|
||||
//
|
||||
// Return an error on internal failure. Otherwise, return a success value. If
|
||||
// there was no `tlang`, then |tag.language().missing()|. But if there was a
|
||||
// `tlang`, then |tag| is filled with subtags exactly as they appeared in the
|
||||
// parse input.
|
||||
static JS::Result<JS::Ok> parseTlangInTransformExtension(
|
||||
JSContext* cx, LanguageTagParser& ts, LanguageTag& tag, Token& tok) {
|
||||
MOZ_ASSERT(ts.isLanguage(tok));
|
||||
return internalParseBaseName(cx, ts, tag, tok).map([](bool parsed) {
|
||||
MOZ_ASSERT(parsed);
|
||||
return JS::Ok();
|
||||
});
|
||||
}
|
||||
|
||||
friend class LanguageTag;
|
||||
|
||||
class Range final {
|
||||
size_t begin_;
|
||||
size_t length_;
|
||||
|
||||
public:
|
||||
Range(size_t begin, size_t length) : begin_(begin), length_(length) {}
|
||||
|
||||
template <typename T>
|
||||
T* begin(T* ptr) const {
|
||||
return ptr + begin_;
|
||||
}
|
||||
|
||||
size_t length() const { return length_; }
|
||||
};
|
||||
|
||||
using TFieldVector = js::Vector<Range, 8>;
|
||||
using AttributesVector = js::Vector<Range, 8>;
|
||||
using KeywordsVector = js::Vector<Range, 8>;
|
||||
|
||||
// Parse |extension|, which must be a validated, fully lowercase
|
||||
// `transformed_extensions` subtag, and fill |tag| and |fields| from the
|
||||
// `tlang` and `tfield` components. Data in |tag| is lowercase, consistent
|
||||
// with |extension|.
|
||||
static JS::Result<bool> parseTransformExtension(
|
||||
JSContext* cx, mozilla::Span<const char> extension, LanguageTag& tag,
|
||||
TFieldVector& fields);
|
||||
|
||||
// Parse |extension|, which must be a validated, fully lowercase
|
||||
// `unicode_locale_extensions` subtag, and fill |attributes| and |keywords|
|
||||
// from the `attribute` and `keyword` components.
|
||||
static JS::Result<bool> parseUnicodeExtension(
|
||||
JSContext* cx, mozilla::Span<const char> extension,
|
||||
AttributesVector& attributes, KeywordsVector& keywords);
|
||||
|
||||
static JS::Result<bool> tryParse(JSContext* cx, LocaleChars& localeChars,
|
||||
size_t localeLength, LanguageTag& tag);
|
||||
|
||||
public:
|
||||
// Parse the input string as a language tag. Reports an error to the context
|
||||
// if the input can't be parsed completely.
|
||||
static bool parse(JSContext* cx, JSLinearString* locale, LanguageTag& tag);
|
||||
|
||||
// Parse the input string as a language tag. Reports an error to the context
|
||||
// if the input can't be parsed completely.
|
||||
static bool parse(JSContext* cx, mozilla::Span<const char> locale,
|
||||
LanguageTag& tag);
|
||||
|
||||
// Parse the input string as a language tag. Returns Ok(true) if the input
|
||||
// could be completely parsed, Ok(false) if the input couldn't be parsed,
|
||||
// or Err() in case of internal error.
|
||||
static JS::Result<bool> tryParse(JSContext* cx, JSLinearString* locale,
|
||||
LanguageTag& tag);
|
||||
|
||||
// Parse the input string as a language tag. Returns Ok(true) if the input
|
||||
// could be completely parsed, Ok(false) if the input couldn't be parsed,
|
||||
// or Err() in case of internal error.
|
||||
static JS::Result<bool> tryParse(JSContext* cx,
|
||||
mozilla::Span<const char> locale,
|
||||
LanguageTag& tag);
|
||||
|
||||
// Parse the input string as the base-name parts (language, script, region,
|
||||
// variants) of a language tag. Ignores any trailing characters.
|
||||
static bool parseBaseName(JSContext* cx, mozilla::Span<const char> locale,
|
||||
LanguageTag& tag);
|
||||
|
||||
// Return true iff |extension| can be parsed as a Unicode extension subtag.
|
||||
static bool canParseUnicodeExtension(mozilla::Span<const char> extension);
|
||||
|
||||
// Return true iff |unicodeType| can be parsed as a Unicode extension type.
|
||||
static bool canParseUnicodeExtensionType(JSLinearString* unicodeType);
|
||||
};
|
||||
|
||||
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(LanguageTagParser::TokenKind)
|
||||
|
||||
/**
|
||||
* Parse a string as a standalone |language| tag. If |str| is a standalone
|
||||
* language tag, store it in |result| and return true. Otherwise return false.
|
||||
*/
|
||||
MOZ_MUST_USE bool ParseStandaloneLanguageTag(JS::Handle<JSLinearString*> str,
|
||||
LanguageSubtag& result);
|
||||
|
||||
/**
|
||||
* Parse a string as a standalone |script| tag. If |str| is a standalone script
|
||||
* tag, store it in |result| and return true. Otherwise return false.
|
||||
*/
|
||||
MOZ_MUST_USE bool ParseStandaloneScriptTag(JS::Handle<JSLinearString*> str,
|
||||
ScriptSubtag& result);
|
||||
|
||||
/**
|
||||
* Parse a string as a standalone |region| tag. If |str| is a standalone region
|
||||
* tag, store it in |result| and return true. Otherwise return false.
|
||||
*/
|
||||
MOZ_MUST_USE bool ParseStandaloneRegionTag(JS::Handle<JSLinearString*> str,
|
||||
RegionSubtag& result);
|
||||
|
||||
/**
|
||||
* Parse a string as an ISO-639 language code. Return |nullptr| in the result if
|
||||
* the input could not be parsed or the canonical form of the resulting language
|
||||
* tag contains more than a single language subtag.
|
||||
*/
|
||||
JS::Result<JSString*> ParseStandaloneISO639LanguageTag(
|
||||
JSContext* cx, JS::Handle<JSLinearString*> str);
|
||||
|
||||
class UnicodeExtensionKeyword final {
|
||||
char key_[LanguageTagLimits::UnicodeKeyLength];
|
||||
JSLinearString* type_;
|
||||
|
||||
public:
|
||||
using UnicodeKey = const char (&)[LanguageTagLimits::UnicodeKeyLength + 1];
|
||||
using UnicodeKeySpan =
|
||||
mozilla::Span<const char, LanguageTagLimits::UnicodeKeyLength>;
|
||||
|
||||
UnicodeExtensionKeyword(UnicodeKey key, JSLinearString* type)
|
||||
: key_{key[0], key[1]}, type_(type) {}
|
||||
|
||||
UnicodeKeySpan key() const { return {key_, sizeof(key_)}; }
|
||||
JSLinearString* type() const { return type_; }
|
||||
|
||||
void trace(JSTracer* trc);
|
||||
};
|
||||
|
||||
extern MOZ_MUST_USE bool ApplyUnicodeExtensionToTag(
|
||||
JSContext* cx, LanguageTag& tag,
|
||||
JS::HandleVector<UnicodeExtensionKeyword> keywords);
|
||||
|
||||
} // namespace intl
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* builtin_intl_LanguageTag_h */
|
||||
950
js/src/builtin/intl/LanguageTagGenerated.cpp
Normal file
950
js/src/builtin/intl/LanguageTagGenerated.cpp
Normal file
|
|
@ -0,0 +1,950 @@
|
|||
// Generated by make_intl_data.py. DO NOT EDIT.
|
||||
// Version: CLDR-35.1
|
||||
// URL: https://unicode.org/Public/cldr/35.1/core.zip
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Span.h"
|
||||
#include "mozilla/TextUtils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jsstr.h"
|
||||
|
||||
#include "builtin/intl/LanguageTag.h"
|
||||
|
||||
using namespace js::intl::LanguageTagLimits;
|
||||
|
||||
template <size_t Length, size_t TagLength, size_t SubtagLength>
|
||||
static inline bool HasReplacement(
|
||||
const char (&subtags)[Length][TagLength],
|
||||
const js::intl::LanguageTagSubtag<SubtagLength>& subtag) {
|
||||
MOZ_ASSERT(subtag.length() == TagLength - 1,
|
||||
"subtag must have the same length as the list of subtags");
|
||||
|
||||
const char* ptr = subtag.span().data();
|
||||
return std::binary_search(std::begin(subtags), std::end(subtags), ptr,
|
||||
[](const char* a, const char* b) {
|
||||
return memcmp(a, b, TagLength - 1) < 0;
|
||||
});
|
||||
}
|
||||
|
||||
template <size_t Length, size_t TagLength, size_t SubtagLength>
|
||||
static inline const char* SearchReplacement(
|
||||
const char (&subtags)[Length][TagLength],
|
||||
const char* (&aliases)[Length],
|
||||
const js::intl::LanguageTagSubtag<SubtagLength>& subtag) {
|
||||
MOZ_ASSERT(subtag.length() == TagLength - 1,
|
||||
"subtag must have the same length as the list of subtags");
|
||||
|
||||
const char* ptr = subtag.span().data();
|
||||
auto p = std::lower_bound(std::begin(subtags), std::end(subtags), ptr,
|
||||
[](const char* a, const char* b) {
|
||||
return memcmp(a, b, TagLength - 1) < 0;
|
||||
});
|
||||
if (p != std::end(subtags) && memcmp(*p, ptr, TagLength - 1) == 0) {
|
||||
return aliases[std::distance(std::begin(subtags), p)];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool IsAsciiLowercaseAlphanumeric(char c) {
|
||||
return mozilla::IsAsciiLowercaseAlpha(c) || mozilla::IsAsciiDigit(c);
|
||||
}
|
||||
|
||||
static bool IsAsciiLowercaseAlphanumericOrDash(char c) {
|
||||
return IsAsciiLowercaseAlphanumeric(c) || c == '-';
|
||||
}
|
||||
|
||||
static bool IsCanonicallyCasedLanguageTag(mozilla::Span<const char> span) {
|
||||
// Tell the analysis the |std::all_of| function can't GC.
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
|
||||
return std::all_of(span.begin(), span.end(), mozilla::IsAsciiLowercaseAlpha<char>);
|
||||
}
|
||||
|
||||
static bool IsCanonicallyCasedRegionTag(mozilla::Span<const char> span) {
|
||||
// Tell the analysis the |std::all_of| function can't GC.
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
|
||||
return std::all_of(span.begin(), span.end(), mozilla::IsAsciiUppercaseAlpha<char>) ||
|
||||
std::all_of(span.begin(), span.end(), mozilla::IsAsciiDigit<char>);
|
||||
}
|
||||
|
||||
static bool IsCanonicallyCasedVariantTag(mozilla::Span<const char> span) {
|
||||
// Tell the analysis the |std::all_of| function can't GC.
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
|
||||
return std::all_of(span.begin(), span.end(), IsAsciiLowercaseAlphanumeric);
|
||||
}
|
||||
|
||||
static bool IsCanonicallyCasedUnicodeKey(mozilla::Span<const char> key) {
|
||||
return std::all_of(key.begin(), key.end(), IsAsciiLowercaseAlphanumeric);
|
||||
}
|
||||
|
||||
static bool IsCanonicallyCasedUnicodeType(mozilla::Span<const char> type) {
|
||||
return std::all_of(type.begin(), type.end(), IsAsciiLowercaseAlphanumericOrDash);
|
||||
}
|
||||
|
||||
static bool IsCanonicallyCasedTransformKey(mozilla::Span<const char> key) {
|
||||
return std::all_of(key.begin(), key.end(), IsAsciiLowercaseAlphanumeric);
|
||||
}
|
||||
|
||||
static bool IsCanonicallyCasedTransformType(mozilla::Span<const char> type) {
|
||||
return std::all_of(type.begin(), type.end(), IsAsciiLowercaseAlphanumericOrDash);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Mappings from language subtags to preferred values.
|
||||
// Derived from CLDR Supplemental Data, version 35.1.
|
||||
// https://unicode.org/Public/cldr/35.1/core.zip
|
||||
bool js::intl::LanguageTag::languageMapping(LanguageSubtag& language) {
|
||||
MOZ_ASSERT(IsStructurallyValidLanguageTag(language.span()));
|
||||
MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language.span()));
|
||||
|
||||
if (language.length() == 2) {
|
||||
static const char languages[9][3] = {
|
||||
"bh", "in", "iw", "ji", "jw", "mo", "no", "tl", "tw",
|
||||
};
|
||||
static const char* aliases[9] = {
|
||||
"bho", "id", "he", "yi", "jv", "ro", "nb", "fil", "ak",
|
||||
};
|
||||
|
||||
if (const char* replacement = SearchReplacement(languages, aliases, language)) {
|
||||
language.set(mozilla::MakeCStringSpan(replacement));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (language.length() == 3) {
|
||||
static const char languages[340][4] = {
|
||||
"aam", "aar", "abk", "adp", "afr", "aju", "aka", "alb", "als", "amh",
|
||||
"ara", "arb", "arg", "arm", "asm", "aue", "ava", "ave", "aym", "ayr",
|
||||
"ayx", "aze", "azj", "bak", "bam", "baq", "bcc", "bcl", "bel", "ben",
|
||||
"bgm", "bih", "bis", "bjd", "bod", "bos", "bre", "bul", "bur", "bxk",
|
||||
"bxr", "cat", "ccq", "ces", "cha", "che", "chi", "chu", "chv", "cjr",
|
||||
"cka", "cld", "cmk", "cmn", "cor", "cos", "coy", "cqu", "cre", "cwd",
|
||||
"cym", "cze", "dan", "deu", "dgo", "dhd", "dik", "diq", "div", "drh",
|
||||
"dut", "dzo", "ekk", "ell", "emk", "eng", "epo", "esk", "est", "eus",
|
||||
"ewe", "fao", "fas", "fat", "fij", "fin", "fra", "fre", "fry", "fuc",
|
||||
"ful", "gav", "gaz", "gbo", "geo", "ger", "gfx", "ggn", "gla", "gle",
|
||||
"glg", "glv", "gno", "gre", "grn", "gti", "gug", "guj", "guv", "gya",
|
||||
"hat", "hau", "hdn", "hea", "heb", "her", "him", "hin", "hmo", "hrr",
|
||||
"hrv", "hun", "hye", "ibi", "ibo", "ice", "ido", "iii", "ike", "iku",
|
||||
"ile", "ilw", "ina", "ind", "ipk", "isl", "ita", "jav", "jeg", "jpn",
|
||||
"kal", "kan", "kas", "kat", "kau", "kaz", "kgc", "kgh", "khk", "khm",
|
||||
"kik", "kin", "kir", "kmr", "knc", "kng", "knn", "koj", "kom", "kon",
|
||||
"kor", "kpv", "krm", "ktr", "kua", "kur", "kvs", "kwq", "kxe", "kzj",
|
||||
"kzt", "lao", "lat", "lav", "lbk", "lii", "lim", "lin", "lit", "lmm",
|
||||
"ltz", "lub", "lug", "lvs", "mac", "mah", "mal", "mao", "mar", "may",
|
||||
"meg", "mhr", "mkd", "mlg", "mlt", "mnk", "mol", "mon", "mri", "msa",
|
||||
"mst", "mup", "mwj", "mya", "myt", "nad", "nau", "nav", "nbl", "ncp",
|
||||
"nde", "ndo", "nep", "nld", "nno", "nnx", "nob", "nor", "npi", "nts",
|
||||
"nya", "oci", "ojg", "oji", "ori", "orm", "ory", "oss", "oun", "pan",
|
||||
"pbu", "pcr", "per", "pes", "pli", "plt", "pmc", "pmu", "pnb", "pol",
|
||||
"por", "ppa", "ppr", "pry", "pus", "puz", "que", "quz", "rmy", "roh",
|
||||
"ron", "rum", "run", "rus", "sag", "san", "sca", "scc", "scr", "sin",
|
||||
"skk", "slk", "slo", "slv", "sme", "smo", "sna", "snd", "som", "sot",
|
||||
"spa", "spy", "sqi", "src", "srd", "srp", "ssw", "sun", "swa", "swe",
|
||||
"swh", "tah", "tam", "tat", "tdu", "tel", "tgk", "tgl", "tha", "thc",
|
||||
"thx", "tib", "tie", "tir", "tkk", "tlw", "tmp", "tne", "ton", "tsf",
|
||||
"tsn", "tso", "ttq", "tuk", "tur", "twi", "uig", "ukr", "umu", "uok",
|
||||
"urd", "uzb", "uzn", "ven", "vie", "vol", "wel", "wln", "wol", "xba",
|
||||
"xho", "xia", "xkh", "xpe", "xsj", "xsl", "ybd", "ydd", "yid", "yma",
|
||||
"ymt", "yor", "yos", "yuu", "zai", "zha", "zho", "zsm", "zul", "zyb",
|
||||
};
|
||||
static const char* aliases[340] = {
|
||||
"aas", "aa", "ab", "dz", "af", "jrb", "ak", "sq", "sq", "am",
|
||||
"ar", "ar", "an", "hy", "as", "ktz", "av", "ae", "ay", "ay",
|
||||
"nun", "az", "az", "ba", "bm", "eu", "bal", "bik", "be", "bn",
|
||||
"bcg", "bho", "bi", "drl", "bo", "bs", "br", "bg", "my", "luy",
|
||||
"bua", "ca", "rki", "cs", "ch", "ce", "zh", "cu", "cv", "mom",
|
||||
"cmr", "syr", "xch", "zh", "kw", "co", "pij", "quh", "cr", "cr",
|
||||
"cy", "cs", "da", "de", "doi", "mwr", "din", "zza", "dv", "mn",
|
||||
"nl", "dz", "et", "el", "man", "en", "eo", "ik", "et", "eu",
|
||||
"ee", "fo", "fa", "ak", "fj", "fi", "fr", "fr", "fy", "ff",
|
||||
"ff", "dev", "om", "grb", "ka", "de", "vaj", "gvr", "gd", "ga",
|
||||
"gl", "gv", "gon", "el", "gn", "nyc", "gn", "gu", "duz", "gba",
|
||||
"ht", "ha", "hai", "hmn", "he", "hz", "srx", "hi", "ho", "jal",
|
||||
"hr", "hu", "hy", "opa", "ig", "is", "io", "ii", "iu", "iu",
|
||||
"ie", "gal", "ia", "id", "ik", "is", "it", "jv", "oyb", "ja",
|
||||
"kl", "kn", "ks", "ka", "kr", "kk", "tdf", "kml", "mn", "km",
|
||||
"ki", "rw", "ky", "ku", "kr", "kg", "kok", "kwv", "kv", "kg",
|
||||
"ko", "kv", "bmf", "dtp", "kj", "ku", "gdj", "yam", "tvd", "dtp",
|
||||
"dtp", "lo", "la", "lv", "bnc", "raq", "li", "ln", "lt", "rmx",
|
||||
"lb", "lu", "lg", "lv", "mk", "mh", "ml", "mi", "mr", "ms",
|
||||
"cir", "chm", "mk", "mg", "mt", "man", "ro", "mn", "mi", "ms",
|
||||
"mry", "raj", "vaj", "my", "mry", "xny", "na", "nv", "nr", "kdz",
|
||||
"nd", "ng", "ne", "nl", "nn", "ngv", "nb", "nb", "ne", "pij",
|
||||
"ny", "oc", "oj", "oj", "or", "om", "or", "os", "vaj", "pa",
|
||||
"ps", "adx", "fa", "fa", "pi", "mg", "huw", "phr", "lah", "pl",
|
||||
"pt", "bfy", "lcq", "prt", "ps", "pub", "qu", "qu", "rom", "rm",
|
||||
"ro", "ro", "rn", "ru", "sg", "sa", "hle", "sr", "hr", "si",
|
||||
"oyb", "sk", "sk", "sl", "se", "sm", "sn", "sd", "so", "st",
|
||||
"es", "kln", "sq", "sc", "sc", "sr", "ss", "su", "sw", "sv",
|
||||
"sw", "ty", "ta", "tt", "dtp", "te", "tg", "fil", "th", "tpo",
|
||||
"oyb", "bo", "ras", "ti", "twm", "weo", "tyj", "kak", "to", "taj",
|
||||
"tn", "ts", "tmh", "tk", "tr", "ak", "ug", "uk", "del", "ema",
|
||||
"ur", "uz", "uz", "ve", "vi", "vo", "cy", "wa", "wo", "cax",
|
||||
"xh", "acn", "waw", "kpe", "suj", "den", "rki", "yi", "yi", "lrr",
|
||||
"mtm", "yo", "zom", "yug", "zap", "za", "zh", "ms", "zu", "za",
|
||||
};
|
||||
|
||||
if (const char* replacement = SearchReplacement(languages, aliases, language)) {
|
||||
language.set(mozilla::MakeCStringSpan(replacement));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Language subtags with complex mappings.
|
||||
// Derived from CLDR Supplemental Data, version 35.1.
|
||||
// https://unicode.org/Public/cldr/35.1/core.zip
|
||||
bool js::intl::LanguageTag::complexLanguageMapping(const LanguageSubtag& language) {
|
||||
MOZ_ASSERT(IsStructurallyValidLanguageTag(language.span()));
|
||||
MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language.span()));
|
||||
|
||||
if (language.length() == 2) {
|
||||
return language.equalTo("sh");
|
||||
}
|
||||
|
||||
if (language.length() == 3) {
|
||||
static const char languages[6][4] = {
|
||||
"cnr", "drw", "hbs", "prs", "swc", "tnf",
|
||||
};
|
||||
|
||||
return HasReplacement(languages, language);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mappings from region subtags to preferred values.
|
||||
// Derived from CLDR Supplemental Data, version 35.1.
|
||||
// https://unicode.org/Public/cldr/35.1/core.zip
|
||||
bool js::intl::LanguageTag::regionMapping(RegionSubtag& region) {
|
||||
MOZ_ASSERT(IsStructurallyValidRegionTag(region.span()));
|
||||
MOZ_ASSERT(IsCanonicallyCasedRegionTag(region.span()));
|
||||
|
||||
if (region.length() == 2) {
|
||||
static const char regions[23][3] = {
|
||||
"BU", "CS", "CT", "DD", "DY", "FQ", "FX", "HV", "JT", "MI",
|
||||
"NH", "NQ", "PU", "PZ", "QU", "RH", "TP", "UK", "VD", "WK",
|
||||
"YD", "YU", "ZR",
|
||||
};
|
||||
static const char* aliases[23] = {
|
||||
"MM", "RS", "KI", "DE", "BJ", "AQ", "FR", "BF", "UM", "UM",
|
||||
"VU", "AQ", "UM", "PA", "EU", "ZW", "TL", "GB", "VN", "UM",
|
||||
"YE", "RS", "CD",
|
||||
};
|
||||
|
||||
if (const char* replacement = SearchReplacement(regions, aliases, region)) {
|
||||
region.set(mozilla::MakeCStringSpan(replacement));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
static const char regions[300][4] = {
|
||||
"004", "008", "010", "012", "016", "020", "024", "028", "031", "032",
|
||||
"036", "040", "044", "048", "050", "051", "052", "056", "060", "062",
|
||||
"064", "068", "070", "072", "074", "076", "084", "086", "090", "092",
|
||||
"096", "100", "104", "108", "112", "116", "120", "124", "132", "136",
|
||||
"140", "144", "148", "152", "156", "158", "162", "166", "170", "174",
|
||||
"175", "178", "180", "184", "188", "191", "192", "196", "203", "204",
|
||||
"208", "212", "214", "218", "222", "226", "230", "231", "232", "233",
|
||||
"234", "238", "239", "242", "246", "248", "249", "250", "254", "258",
|
||||
"260", "262", "266", "268", "270", "275", "276", "278", "280", "288",
|
||||
"292", "296", "300", "304", "308", "312", "316", "320", "324", "328",
|
||||
"332", "334", "336", "340", "344", "348", "352", "356", "360", "364",
|
||||
"368", "372", "376", "380", "384", "388", "392", "398", "400", "404",
|
||||
"408", "410", "414", "417", "418", "422", "426", "428", "430", "434",
|
||||
"438", "440", "442", "446", "450", "454", "458", "462", "466", "470",
|
||||
"474", "478", "480", "484", "492", "496", "498", "499", "500", "504",
|
||||
"508", "512", "516", "520", "524", "528", "531", "533", "534", "535",
|
||||
"540", "548", "554", "558", "562", "566", "570", "574", "578", "580",
|
||||
"581", "583", "584", "585", "586", "591", "598", "600", "604", "608",
|
||||
"612", "616", "620", "624", "626", "630", "634", "638", "642", "643",
|
||||
"646", "652", "654", "659", "660", "662", "663", "666", "670", "674",
|
||||
"678", "682", "686", "688", "690", "694", "702", "703", "704", "705",
|
||||
"706", "710", "716", "720", "724", "728", "729", "732", "736", "740",
|
||||
"744", "748", "752", "756", "760", "762", "764", "768", "772", "776",
|
||||
"780", "784", "788", "792", "795", "796", "798", "800", "804", "807",
|
||||
"818", "826", "830", "831", "832", "833", "834", "840", "850", "854",
|
||||
"858", "860", "862", "876", "882", "886", "887", "891", "894", "958",
|
||||
"959", "960", "962", "963", "964", "965", "966", "967", "968", "969",
|
||||
"970", "971", "972", "973", "974", "975", "976", "977", "978", "979",
|
||||
"980", "981", "982", "983", "984", "985", "986", "987", "988", "989",
|
||||
"990", "991", "992", "993", "994", "995", "996", "997", "998", "999",
|
||||
};
|
||||
static const char* aliases[300] = {
|
||||
"AF", "AL", "AQ", "DZ", "AS", "AD", "AO", "AG", "AZ", "AR",
|
||||
"AU", "AT", "BS", "BH", "BD", "AM", "BB", "BE", "BM", "034",
|
||||
"BT", "BO", "BA", "BW", "BV", "BR", "BZ", "IO", "SB", "VG",
|
||||
"BN", "BG", "MM", "BI", "BY", "KH", "CM", "CA", "CV", "KY",
|
||||
"CF", "LK", "TD", "CL", "CN", "TW", "CX", "CC", "CO", "KM",
|
||||
"YT", "CG", "CD", "CK", "CR", "HR", "CU", "CY", "CZ", "BJ",
|
||||
"DK", "DM", "DO", "EC", "SV", "GQ", "ET", "ET", "ER", "EE",
|
||||
"FO", "FK", "GS", "FJ", "FI", "AX", "FR", "FR", "GF", "PF",
|
||||
"TF", "DJ", "GA", "GE", "GM", "PS", "DE", "DE", "DE", "GH",
|
||||
"GI", "KI", "GR", "GL", "GD", "GP", "GU", "GT", "GN", "GY",
|
||||
"HT", "HM", "VA", "HN", "HK", "HU", "IS", "IN", "ID", "IR",
|
||||
"IQ", "IE", "IL", "IT", "CI", "JM", "JP", "KZ", "JO", "KE",
|
||||
"KP", "KR", "KW", "KG", "LA", "LB", "LS", "LV", "LR", "LY",
|
||||
"LI", "LT", "LU", "MO", "MG", "MW", "MY", "MV", "ML", "MT",
|
||||
"MQ", "MR", "MU", "MX", "MC", "MN", "MD", "ME", "MS", "MA",
|
||||
"MZ", "OM", "NA", "NR", "NP", "NL", "CW", "AW", "SX", "BQ",
|
||||
"NC", "VU", "NZ", "NI", "NE", "NG", "NU", "NF", "NO", "MP",
|
||||
"UM", "FM", "MH", "PW", "PK", "PA", "PG", "PY", "PE", "PH",
|
||||
"PN", "PL", "PT", "GW", "TL", "PR", "QA", "RE", "RO", "RU",
|
||||
"RW", "BL", "SH", "KN", "AI", "LC", "MF", "PM", "VC", "SM",
|
||||
"ST", "SA", "SN", "RS", "SC", "SL", "SG", "SK", "VN", "SI",
|
||||
"SO", "ZA", "ZW", "YE", "ES", "SS", "SD", "EH", "SD", "SR",
|
||||
"SJ", "SZ", "SE", "CH", "SY", "TJ", "TH", "TG", "TK", "TO",
|
||||
"TT", "AE", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "MK",
|
||||
"EG", "GB", "JE", "GG", "JE", "IM", "TZ", "US", "VI", "BF",
|
||||
"UY", "UZ", "VE", "WF", "WS", "YE", "YE", "RS", "ZM", "AA",
|
||||
"QM", "QN", "QP", "QQ", "QR", "QS", "QT", "EU", "QV", "QW",
|
||||
"QX", "QY", "QZ", "XA", "XB", "XC", "XD", "XE", "XF", "XG",
|
||||
"XH", "XI", "XJ", "XK", "XL", "XM", "XN", "XO", "XP", "XQ",
|
||||
"XR", "XS", "XT", "XU", "XV", "XW", "XX", "XY", "XZ", "ZZ",
|
||||
};
|
||||
|
||||
if (const char* replacement = SearchReplacement(regions, aliases, region)) {
|
||||
region.set(mozilla::MakeCStringSpan(replacement));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Region subtags with complex mappings.
|
||||
// Derived from CLDR Supplemental Data, version 35.1.
|
||||
// https://unicode.org/Public/cldr/35.1/core.zip
|
||||
bool js::intl::LanguageTag::complexRegionMapping(const RegionSubtag& region) {
|
||||
MOZ_ASSERT(IsStructurallyValidRegionTag(region.span()));
|
||||
MOZ_ASSERT(IsCanonicallyCasedRegionTag(region.span()));
|
||||
|
||||
if (region.length() == 2) {
|
||||
return region.equalTo("AN") ||
|
||||
region.equalTo("NT") ||
|
||||
region.equalTo("PC") ||
|
||||
region.equalTo("SU");
|
||||
}
|
||||
|
||||
{
|
||||
static const char regions[8][4] = {
|
||||
"172", "200", "530", "532", "536", "582", "810", "890",
|
||||
};
|
||||
|
||||
return HasReplacement(regions, region);
|
||||
}
|
||||
}
|
||||
|
||||
// Language subtags with complex mappings.
|
||||
// Derived from CLDR Supplemental Data, version 35.1.
|
||||
// https://unicode.org/Public/cldr/35.1/core.zip
|
||||
void js::intl::LanguageTag::performComplexLanguageMappings() {
|
||||
MOZ_ASSERT(IsStructurallyValidLanguageTag(language().span()));
|
||||
MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language().span()));
|
||||
|
||||
if (language().equalTo("cnr")) {
|
||||
setLanguage("sr");
|
||||
if (region().missing()) {
|
||||
setRegion("ME");
|
||||
}
|
||||
}
|
||||
else if (language().equalTo("drw") ||
|
||||
language().equalTo("prs") ||
|
||||
language().equalTo("tnf")) {
|
||||
setLanguage("fa");
|
||||
if (region().missing()) {
|
||||
setRegion("AF");
|
||||
}
|
||||
}
|
||||
else if (language().equalTo("hbs") ||
|
||||
language().equalTo("sh")) {
|
||||
setLanguage("sr");
|
||||
if (script().missing()) {
|
||||
setScript("Latn");
|
||||
}
|
||||
}
|
||||
else if (language().equalTo("swc")) {
|
||||
setLanguage("sw");
|
||||
if (region().missing()) {
|
||||
setRegion("CD");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Region subtags with complex mappings.
|
||||
// Derived from CLDR Supplemental Data, version 35.1.
|
||||
// https://unicode.org/Public/cldr/35.1/core.zip
|
||||
void js::intl::LanguageTag::performComplexRegionMappings() {
|
||||
MOZ_ASSERT(IsStructurallyValidLanguageTag(language().span()));
|
||||
MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language().span()));
|
||||
MOZ_ASSERT(IsStructurallyValidRegionTag(region().span()));
|
||||
MOZ_ASSERT(IsCanonicallyCasedRegionTag(region().span()));
|
||||
|
||||
if (region().equalTo("172")) {
|
||||
if (language().equalTo("hy") ||
|
||||
(language().equalTo("und") && script().equalTo("Armn"))) {
|
||||
setRegion("AM");
|
||||
}
|
||||
else if (language().equalTo("az") ||
|
||||
language().equalTo("tkr") ||
|
||||
language().equalTo("tly") ||
|
||||
language().equalTo("ttt")) {
|
||||
setRegion("AZ");
|
||||
}
|
||||
else if (language().equalTo("be")) {
|
||||
setRegion("BY");
|
||||
}
|
||||
else if (language().equalTo("ab") ||
|
||||
language().equalTo("ka") ||
|
||||
language().equalTo("os") ||
|
||||
(language().equalTo("und") && script().equalTo("Geor")) ||
|
||||
language().equalTo("xmf")) {
|
||||
setRegion("GE");
|
||||
}
|
||||
else if (language().equalTo("ky")) {
|
||||
setRegion("KG");
|
||||
}
|
||||
else if (language().equalTo("kk") ||
|
||||
(language().equalTo("ug") && script().equalTo("Cyrl"))) {
|
||||
setRegion("KZ");
|
||||
}
|
||||
else if (language().equalTo("gag")) {
|
||||
setRegion("MD");
|
||||
}
|
||||
else if (language().equalTo("tg")) {
|
||||
setRegion("TJ");
|
||||
}
|
||||
else if (language().equalTo("tk")) {
|
||||
setRegion("TM");
|
||||
}
|
||||
else if (language().equalTo("crh") ||
|
||||
language().equalTo("got") ||
|
||||
language().equalTo("ji") ||
|
||||
language().equalTo("rue") ||
|
||||
language().equalTo("uk") ||
|
||||
(language().equalTo("und") && script().equalTo("Goth"))) {
|
||||
setRegion("UA");
|
||||
}
|
||||
else if (language().equalTo("kaa") ||
|
||||
language().equalTo("sog") ||
|
||||
(language().equalTo("und") && script().equalTo("Sogd")) ||
|
||||
(language().equalTo("und") && script().equalTo("Sogo")) ||
|
||||
language().equalTo("uz")) {
|
||||
setRegion("UZ");
|
||||
}
|
||||
else {
|
||||
setRegion("RU");
|
||||
}
|
||||
}
|
||||
else if (region().equalTo("200")) {
|
||||
if (language().equalTo("sk")) {
|
||||
setRegion("SK");
|
||||
}
|
||||
else {
|
||||
setRegion("CZ");
|
||||
}
|
||||
}
|
||||
else if (region().equalTo("530") ||
|
||||
region().equalTo("532") ||
|
||||
region().equalTo("AN")) {
|
||||
if (language().equalTo("vic")) {
|
||||
setRegion("SX");
|
||||
}
|
||||
else {
|
||||
setRegion("CW");
|
||||
}
|
||||
}
|
||||
else if (region().equalTo("536") ||
|
||||
region().equalTo("NT")) {
|
||||
if (language().equalTo("akk") ||
|
||||
language().equalTo("ckb") ||
|
||||
(language().equalTo("ku") && script().equalTo("Arab")) ||
|
||||
language().equalTo("mis") ||
|
||||
language().equalTo("syr") ||
|
||||
(language().equalTo("und") && script().equalTo("Syrc")) ||
|
||||
(language().equalTo("und") && script().equalTo("Xsux")) ||
|
||||
(language().equalTo("und") && script().equalTo("Hatr"))) {
|
||||
setRegion("IQ");
|
||||
}
|
||||
else {
|
||||
setRegion("SA");
|
||||
}
|
||||
}
|
||||
else if (region().equalTo("582") ||
|
||||
region().equalTo("PC")) {
|
||||
if (language().equalTo("mh")) {
|
||||
setRegion("MH");
|
||||
}
|
||||
else if (language().equalTo("pau")) {
|
||||
setRegion("PW");
|
||||
}
|
||||
else {
|
||||
setRegion("FM");
|
||||
}
|
||||
}
|
||||
else if (region().equalTo("810") ||
|
||||
region().equalTo("SU")) {
|
||||
if (language().equalTo("hy") ||
|
||||
(language().equalTo("und") && script().equalTo("Armn"))) {
|
||||
setRegion("AM");
|
||||
}
|
||||
else if (language().equalTo("az") ||
|
||||
language().equalTo("tkr") ||
|
||||
language().equalTo("tly") ||
|
||||
language().equalTo("ttt")) {
|
||||
setRegion("AZ");
|
||||
}
|
||||
else if (language().equalTo("be")) {
|
||||
setRegion("BY");
|
||||
}
|
||||
else if (language().equalTo("et") ||
|
||||
language().equalTo("vro")) {
|
||||
setRegion("EE");
|
||||
}
|
||||
else if (language().equalTo("ab") ||
|
||||
language().equalTo("ka") ||
|
||||
language().equalTo("os") ||
|
||||
(language().equalTo("und") && script().equalTo("Geor")) ||
|
||||
language().equalTo("xmf")) {
|
||||
setRegion("GE");
|
||||
}
|
||||
else if (language().equalTo("ky")) {
|
||||
setRegion("KG");
|
||||
}
|
||||
else if (language().equalTo("kk") ||
|
||||
(language().equalTo("ug") && script().equalTo("Cyrl"))) {
|
||||
setRegion("KZ");
|
||||
}
|
||||
else if (language().equalTo("lt") ||
|
||||
language().equalTo("sgs")) {
|
||||
setRegion("LT");
|
||||
}
|
||||
else if (language().equalTo("ltg") ||
|
||||
language().equalTo("lv")) {
|
||||
setRegion("LV");
|
||||
}
|
||||
else if (language().equalTo("gag")) {
|
||||
setRegion("MD");
|
||||
}
|
||||
else if (language().equalTo("tg")) {
|
||||
setRegion("TJ");
|
||||
}
|
||||
else if (language().equalTo("tk")) {
|
||||
setRegion("TM");
|
||||
}
|
||||
else if (language().equalTo("crh") ||
|
||||
language().equalTo("got") ||
|
||||
language().equalTo("ji") ||
|
||||
language().equalTo("rue") ||
|
||||
language().equalTo("uk") ||
|
||||
(language().equalTo("und") && script().equalTo("Goth"))) {
|
||||
setRegion("UA");
|
||||
}
|
||||
else if (language().equalTo("kaa") ||
|
||||
language().equalTo("sog") ||
|
||||
(language().equalTo("und") && script().equalTo("Sogd")) ||
|
||||
(language().equalTo("und") && script().equalTo("Sogo")) ||
|
||||
language().equalTo("uz")) {
|
||||
setRegion("UZ");
|
||||
}
|
||||
else {
|
||||
setRegion("RU");
|
||||
}
|
||||
}
|
||||
else if (region().equalTo("890")) {
|
||||
if (language().equalTo("bs")) {
|
||||
setRegion("BA");
|
||||
}
|
||||
else if (language().equalTo("hr")) {
|
||||
setRegion("HR");
|
||||
}
|
||||
else if (language().equalTo("mk")) {
|
||||
setRegion("MK");
|
||||
}
|
||||
else if (language().equalTo("sl")) {
|
||||
setRegion("SI");
|
||||
}
|
||||
else {
|
||||
setRegion("RS");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char* ToCharPointer(const char* str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
static const char* ToCharPointer(const js::UniqueChars& str) {
|
||||
return str.get();
|
||||
}
|
||||
|
||||
template <typename T, typename U = T>
|
||||
static bool IsLessThan(const T& a, const U& b) {
|
||||
return strcmp(ToCharPointer(a), ToCharPointer(b)) < 0;
|
||||
}
|
||||
|
||||
// Mappings from variant subtags to preferred values.
|
||||
// Derived from CLDR Supplemental Data, version 35.1.
|
||||
// https://unicode.org/Public/cldr/35.1/core.zip
|
||||
bool js::intl::LanguageTag::performVariantMappings(JSContext* cx) {
|
||||
// The variant subtags need to be sorted for binary search.
|
||||
MOZ_ASSERT(std::is_sorted(variants_.begin(), variants_.end(),
|
||||
IsLessThan<decltype(variants_)::ElementType>));
|
||||
|
||||
auto insertVariantSortedIfNotPresent = [&](const char* variant) {
|
||||
auto* p = std::lower_bound(variants_.begin(), variants_.end(), variant,
|
||||
IsLessThan<decltype(variants_)::ElementType,
|
||||
decltype(variant)>);
|
||||
|
||||
// Don't insert the replacement when already present.
|
||||
if (p != variants_.end() && strcmp(p->get(), variant) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Insert the preferred variant in sort order.
|
||||
auto preferred = DuplicateString(cx, variant);
|
||||
if (!preferred) {
|
||||
return false;
|
||||
}
|
||||
return !!variants_.insert(p, std::move(preferred));
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < variants_.length(); ) {
|
||||
auto& variant = variants_[i];
|
||||
MOZ_ASSERT(IsCanonicallyCasedVariantTag(mozilla::MakeCStringSpan(variant.get())));
|
||||
|
||||
if (strcmp(variant.get(), "aaland") == 0) {
|
||||
variants_.erase(variants_.begin() + i);
|
||||
setRegion("AX");
|
||||
}
|
||||
else if (strcmp(variant.get(), "arevela") == 0) {
|
||||
variants_.erase(variants_.begin() + i);
|
||||
setLanguage("hy");
|
||||
}
|
||||
else if (strcmp(variant.get(), "arevmda") == 0) {
|
||||
variants_.erase(variants_.begin() + i);
|
||||
setLanguage("hyw");
|
||||
}
|
||||
else if (strcmp(variant.get(), "heploc") == 0) {
|
||||
variants_.erase(variants_.begin() + i);
|
||||
if (!insertVariantSortedIfNotPresent("alalc97")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (strcmp(variant.get(), "polytoni") == 0) {
|
||||
variants_.erase(variants_.begin() + i);
|
||||
if (!insertVariantSortedIfNotPresent("polyton")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Canonicalize grandfathered locale identifiers.
|
||||
// Derived from CLDR Supplemental Data, version 35.1.
|
||||
// https://unicode.org/Public/cldr/35.1/core.zip
|
||||
bool js::intl::LanguageTag::updateGrandfatheredMappings(JSContext* cx) {
|
||||
// We're mapping regular grandfathered tags to non-grandfathered form here.
|
||||
// Other tags remain unchanged.
|
||||
//
|
||||
// regular = "art-lojban"
|
||||
// / "cel-gaulish"
|
||||
// / "no-bok"
|
||||
// / "no-nyn"
|
||||
// / "zh-guoyu"
|
||||
// / "zh-hakka"
|
||||
// / "zh-min"
|
||||
// / "zh-min-nan"
|
||||
// / "zh-xiang"
|
||||
//
|
||||
// Therefore we can quickly exclude most tags by checking every
|
||||
// |unicode_locale_id| subcomponent for characteristics not shared by any of
|
||||
// the regular grandfathered (RG) tags:
|
||||
//
|
||||
// * Real-world |unicode_language_subtag|s are all two or three letters,
|
||||
// so don't waste time running a useless |language.length > 3| fast-path.
|
||||
// * No RG tag has a "script"-looking component.
|
||||
// * No RG tag has a "region"-looking component.
|
||||
// * The RG tags that match |unicode_locale_id| (art-lojban, cel-gaulish,
|
||||
// zh-guoyu, zh-hakka, zh-xiang) have exactly one "variant". (no-bok,
|
||||
// no-nyn, zh-min, and zh-min-nan require BCP47's extlang subtag
|
||||
// that |unicode_locale_id| doesn't support.)
|
||||
// * No RG tag contains |extensions| or |pu_extensions|.
|
||||
if (script().present() ||
|
||||
region().present() ||
|
||||
variants().length() != 1 ||
|
||||
extensions().length() != 0 ||
|
||||
privateuse()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language().span()));
|
||||
MOZ_ASSERT(IsCanonicallyCasedVariantTag(mozilla::MakeCStringSpan(variants()[0].get())));
|
||||
|
||||
auto variantEqualTo = [this](const char* variant) {
|
||||
return strcmp(variants()[0].get(), variant) == 0;
|
||||
};
|
||||
|
||||
// art-lojban -> jbo
|
||||
if (language().equalTo("art") && variantEqualTo("lojban")) {
|
||||
setLanguage("jbo");
|
||||
clearVariants();
|
||||
return true;
|
||||
}
|
||||
|
||||
// cel-gaulish -> xtg-x-cel-gaulish
|
||||
else if (language().equalTo("cel") && variantEqualTo("gaulish")) {
|
||||
setLanguage("xtg");
|
||||
clearVariants();
|
||||
|
||||
auto privateuse = DuplicateString(cx, "x-cel-gaulish");
|
||||
if (!privateuse) {
|
||||
return false;
|
||||
}
|
||||
setPrivateuse(std::move(privateuse));
|
||||
return true;
|
||||
}
|
||||
|
||||
// zh-guoyu -> zh
|
||||
else if (language().equalTo("zh") && variantEqualTo("guoyu")) {
|
||||
setLanguage("zh");
|
||||
clearVariants();
|
||||
return true;
|
||||
}
|
||||
|
||||
// zh-hakka -> hak
|
||||
else if (language().equalTo("zh") && variantEqualTo("hakka")) {
|
||||
setLanguage("hak");
|
||||
clearVariants();
|
||||
return true;
|
||||
}
|
||||
|
||||
// zh-xiang -> hsn
|
||||
else if (language().equalTo("zh") && variantEqualTo("xiang")) {
|
||||
setLanguage("hsn");
|
||||
clearVariants();
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <size_t Length>
|
||||
static inline bool IsUnicodeKey(
|
||||
mozilla::Span<const char> key, const char (&str)[Length]) {
|
||||
static_assert(Length == UnicodeKeyLength + 1,
|
||||
"Unicode extension key is two characters long");
|
||||
return memcmp(key.data(), str, Length - 1) == 0;
|
||||
}
|
||||
|
||||
template <size_t Length>
|
||||
static inline bool IsUnicodeType(
|
||||
mozilla::Span<const char> type, const char (&str)[Length]) {
|
||||
static_assert(Length > UnicodeKeyLength + 1,
|
||||
"Unicode extension type contains more than two characters");
|
||||
return type.size() == (Length - 1) &&
|
||||
memcmp(type.data(), str, Length - 1) == 0;
|
||||
}
|
||||
|
||||
static int32_t CompareUnicodeType(const char* a, mozilla::Span<const char> b) {
|
||||
MOZ_ASSERT(!std::char_traits<char>::find(b.data(), b.size(), '\0'),
|
||||
"unexpected null-character in string");
|
||||
|
||||
using UnsignedChar = unsigned char;
|
||||
for (size_t i = 0; i < b.size(); i++) {
|
||||
// |a| is zero-terminated and |b| doesn't contain a null-terminator. So if
|
||||
// we've reached the end of |a|, the below if-statement will always be true.
|
||||
// That ensures we don't read past the end of |a|.
|
||||
if (int32_t r = UnsignedChar(a[i]) - UnsignedChar(b[i])) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
// Return zero if both strings are equal or a negative number if |b| is a
|
||||
// prefix of |a|.
|
||||
return -int32_t(UnsignedChar(a[b.size()]));
|
||||
}
|
||||
|
||||
template <size_t Length>
|
||||
static inline const char* SearchUnicodeReplacement(
|
||||
const char* (&types)[Length], const char* (&aliases)[Length],
|
||||
mozilla::Span<const char> type) {
|
||||
|
||||
auto p = std::lower_bound(std::begin(types), std::end(types), type,
|
||||
[](const auto& a, const auto& b) {
|
||||
return CompareUnicodeType(a, b) < 0;
|
||||
});
|
||||
if (p != std::end(types) && CompareUnicodeType(*p, type) == 0) {
|
||||
return aliases[std::distance(std::begin(types), p)];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping from deprecated BCP 47 Unicode extension types to their preferred
|
||||
* values.
|
||||
*
|
||||
* Spec: https://www.unicode.org/reports/tr35/#Unicode_Locale_Extension_Data_Files
|
||||
* Spec: https://www.unicode.org/reports/tr35/#t_Extension
|
||||
*/
|
||||
const char* js::intl::LanguageTag::replaceUnicodeExtensionType(
|
||||
mozilla::Span<const char> key, mozilla::Span<const char> type) {
|
||||
MOZ_ASSERT(key.size() == UnicodeKeyLength);
|
||||
MOZ_ASSERT(IsCanonicallyCasedUnicodeKey(key));
|
||||
|
||||
MOZ_ASSERT(type.size() > UnicodeKeyLength);
|
||||
MOZ_ASSERT(IsCanonicallyCasedUnicodeType(type));
|
||||
|
||||
if (IsUnicodeKey(key, "ca")) {
|
||||
if (IsUnicodeType(type, "ethiopic-amete-alem")) {
|
||||
return "ethioaa";
|
||||
}
|
||||
if (IsUnicodeType(type, "islamicc")) {
|
||||
return "islamic-civil";
|
||||
}
|
||||
}
|
||||
else if (IsUnicodeKey(key, "kb") ||
|
||||
IsUnicodeKey(key, "kc") ||
|
||||
IsUnicodeKey(key, "kh") ||
|
||||
IsUnicodeKey(key, "kk") ||
|
||||
IsUnicodeKey(key, "kn")) {
|
||||
if (IsUnicodeType(type, "yes")) {
|
||||
return "true";
|
||||
}
|
||||
}
|
||||
else if (IsUnicodeKey(key, "ks")) {
|
||||
if (IsUnicodeType(type, "primary")) {
|
||||
return "level1";
|
||||
}
|
||||
if (IsUnicodeType(type, "tertiary")) {
|
||||
return "level3";
|
||||
}
|
||||
}
|
||||
else if (IsUnicodeKey(key, "ms")) {
|
||||
if (IsUnicodeType(type, "imperial")) {
|
||||
return "uksystem";
|
||||
}
|
||||
}
|
||||
else if (IsUnicodeKey(key, "rg") ||
|
||||
IsUnicodeKey(key, "sd")) {
|
||||
static const char* types[116] = {
|
||||
"cn11", "cn12", "cn13", "cn14", "cn15", "cn21", "cn22", "cn23",
|
||||
"cn31", "cn32", "cn33", "cn34", "cn35", "cn36", "cn37", "cn41",
|
||||
"cn42", "cn43", "cn44", "cn45", "cn46", "cn50", "cn51", "cn52",
|
||||
"cn53", "cn54", "cn61", "cn62", "cn63", "cn64", "cn65", "cz10a",
|
||||
"cz10b", "cz10c", "cz10d", "cz10e", "cz10f", "cz611", "cz612", "cz613",
|
||||
"cz614", "cz615", "cz621", "cz622", "cz623", "cz624", "cz626", "cz627",
|
||||
"czjc", "czjm", "czka", "czkr", "czli", "czmo", "czol", "czpa",
|
||||
"czpl", "czpr", "czst", "czus", "czvy", "czzl", "fra", "frb",
|
||||
"frc", "frd", "fre", "frf", "frg", "frh", "fri", "frj",
|
||||
"frk", "frl", "frm", "frn", "fro", "frp", "frq", "frr",
|
||||
"frs", "frt", "fru", "frv", "laxn", "lud", "lug", "lul",
|
||||
"mrnkc", "nzn", "nzs", "omba", "omsh", "plds", "plkp", "pllb",
|
||||
"plld", "pllu", "plma", "plmz", "plop", "plpd", "plpk", "plpm",
|
||||
"plsk", "plsl", "plwn", "plwp", "plzp", "tteto", "ttrcm", "ttwto",
|
||||
"twkhq", "twtnq", "twtpq", "twtxq",
|
||||
};
|
||||
static const char* aliases[116] = {
|
||||
"cnbj", "cntj", "cnhe", "cnsx", "cnmn", "cnln", "cnjl", "cnhl",
|
||||
"cnsh", "cnjs", "cnzj", "cnah", "cnfj", "cnjx", "cnsd", "cnha",
|
||||
"cnhb", "cnhn", "cngd", "cngx", "cnhi", "cncq", "cnsc", "cngz",
|
||||
"cnyn", "cnxz", "cnsn", "cngs", "cnqh", "cnnx", "cnxj", "cz110",
|
||||
"cz111", "cz112", "cz113", "cz114", "cz115", "cz663", "cz632", "cz633",
|
||||
"cz634", "cz635", "cz641", "cz642", "cz643", "cz644", "cz646", "cz647",
|
||||
"cz31", "cz64", "cz41", "cz52", "cz51", "cz80", "cz71", "cz53",
|
||||
"cz32", "cz10", "cz20", "cz42", "cz63", "cz72", "frges", "frnaq",
|
||||
"frara", "frbfc", "frbre", "frcvl", "frges", "frcor", "frbfc", "fridf",
|
||||
"frocc", "frnaq", "frges", "frocc", "frhdf", "frnor", "frnor", "frpdl",
|
||||
"frhdf", "frnaq", "frpac", "frara", "laxs", "lucl", "luec", "luca",
|
||||
"mr13", "nzauk", "nzcan", "ombj", "omsj", "pl02", "pl04", "pl08",
|
||||
"pl10", "pl06", "pl12", "pl14", "pl16", "pl20", "pl18", "pl22",
|
||||
"pl26", "pl24", "pl28", "pl30", "pl32", "tttob", "ttmrc", "tttob",
|
||||
"twkhh", "twtnn", "twnwt", "twtxg",
|
||||
};
|
||||
return SearchUnicodeReplacement(types, aliases, type);
|
||||
}
|
||||
else if (IsUnicodeKey(key, "tz")) {
|
||||
static const char* types[28] = {
|
||||
"aqams", "cnckg", "cnhrb", "cnkhg", "cuba", "egypt",
|
||||
"eire", "est", "gmt0", "hongkong", "hst", "iceland",
|
||||
"iran", "israel", "jamaica", "japan", "libya", "mst",
|
||||
"navajo", "poland", "portugal", "prc", "roc", "rok",
|
||||
"turkey", "uct", "usnavajo", "zulu",
|
||||
};
|
||||
static const char* aliases[28] = {
|
||||
"nzakl", "cnsha", "cnsha", "cnurc", "cuhav", "egcai",
|
||||
"iedub", "utcw05", "gmt", "hkhkg", "utcw10", "isrey",
|
||||
"irthr", "jeruslm", "jmkin", "jptyo", "lytip", "utcw07",
|
||||
"usden", "plwaw", "ptlis", "cnsha", "twtpe", "krsel",
|
||||
"trist", "utc", "usden", "utc",
|
||||
};
|
||||
return SearchUnicodeReplacement(types, aliases, type);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <size_t Length>
|
||||
static inline bool IsTransformKey(
|
||||
mozilla::Span<const char> key, const char (&str)[Length]) {
|
||||
static_assert(Length == TransformKeyLength + 1,
|
||||
"Transform extension key is two characters long");
|
||||
return memcmp(key.data(), str, Length - 1) == 0;
|
||||
}
|
||||
|
||||
template <size_t Length>
|
||||
static inline bool IsTransformType(
|
||||
mozilla::Span<const char> type, const char (&str)[Length]) {
|
||||
static_assert(Length > TransformKeyLength + 1,
|
||||
"Transform extension type contains more than two characters");
|
||||
return type.size() == (Length - 1) &&
|
||||
memcmp(type.data(), str, Length - 1) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping from deprecated BCP 47 Transform extension types to their preferred
|
||||
* values.
|
||||
*
|
||||
* Spec: https://www.unicode.org/reports/tr35/#Unicode_Locale_Extension_Data_Files
|
||||
* Spec: https://www.unicode.org/reports/tr35/#t_Extension
|
||||
*/
|
||||
const char* js::intl::LanguageTag::replaceTransformExtensionType(
|
||||
mozilla::Span<const char> key, mozilla::Span<const char> type) {
|
||||
MOZ_ASSERT(key.size() == TransformKeyLength);
|
||||
MOZ_ASSERT(IsCanonicallyCasedTransformKey(key));
|
||||
|
||||
MOZ_ASSERT(type.size() > TransformKeyLength);
|
||||
MOZ_ASSERT(IsCanonicallyCasedTransformType(type));
|
||||
|
||||
if (IsTransformKey(key, "d0")) {
|
||||
if (IsTransformType(type, "name")) {
|
||||
return "charname";
|
||||
}
|
||||
}
|
||||
else if (IsTransformKey(key, "m0")) {
|
||||
if (IsTransformType(type, "names")) {
|
||||
return "prprname";
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
1430
js/src/builtin/intl/Locale.cpp
Normal file
1430
js/src/builtin/intl/Locale.cpp
Normal file
File diff suppressed because it is too large
Load diff
63
js/src/builtin/intl/Locale.h
Normal file
63
js/src/builtin/intl/Locale.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* 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/. */
|
||||
|
||||
#ifndef builtin_intl_Locale_h
|
||||
#define builtin_intl_Locale_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "builtin/SelfHostingDefines.h"
|
||||
#include "js/Class.h"
|
||||
#include "vm/NativeObject.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class GlobalObject;
|
||||
|
||||
class LocaleObject : public NativeObject {
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
static constexpr uint32_t LANGUAGE_TAG_SLOT = 0;
|
||||
static constexpr uint32_t BASENAME_SLOT = 1;
|
||||
static constexpr uint32_t UNICODE_EXTENSION_SLOT = 2;
|
||||
static constexpr uint32_t SLOT_COUNT = 3;
|
||||
|
||||
/**
|
||||
* Returns the complete language tag, including any extensions and privateuse
|
||||
* subtags.
|
||||
*/
|
||||
JSString* languageTag() const {
|
||||
return getFixedSlot(LANGUAGE_TAG_SLOT).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the basename subtags, i.e. excluding any extensions and privateuse
|
||||
* subtags.
|
||||
*/
|
||||
JSString* baseName() const { return getFixedSlot(BASENAME_SLOT).toString(); }
|
||||
|
||||
const Value& unicodeExtension() const {
|
||||
return getFixedSlot(UNICODE_EXTENSION_SLOT);
|
||||
}
|
||||
};
|
||||
|
||||
extern JSObject* CreateLocalePrototype(JSContext* cx,
|
||||
JS::Handle<JSObject*> Intl,
|
||||
JS::Handle<GlobalObject*> global);
|
||||
|
||||
extern MOZ_MUST_USE bool intl_ValidateAndCanonicalizeLanguageTag(JSContext* cx,
|
||||
unsigned argc,
|
||||
Value* vp);
|
||||
|
||||
extern MOZ_MUST_USE bool intl_TryValidateAndCanonicalizeLanguageTag(
|
||||
JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
extern MOZ_MUST_USE bool intl_ValidateAndCanonicalizeUnicodeExtensionType(
|
||||
JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* builtin_intl_Locale_h */
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "builtin/intl/CommonFunctions.h"
|
||||
#include "builtin/intl/ICUHeader.h"
|
||||
#include "builtin/intl/LanguageTag.h"
|
||||
#include "builtin/intl/ScopedICUObject.h"
|
||||
#include "ds/Sort.h"
|
||||
#include "js/RootingAPI.h"
|
||||
|
|
@ -35,7 +36,7 @@ using mozilla::IsFinite;
|
|||
using mozilla::IsNaN;
|
||||
using mozilla::IsNegativeZero;
|
||||
using js::intl::CallICU;
|
||||
using js::intl::GetAvailableLocales;
|
||||
using js::intl::DateTimeFormatOptions;
|
||||
using js::intl::IcuLocale;
|
||||
using js::intl::INITIAL_CHAR_BUFFER_SIZE;
|
||||
using js::intl::StringsAreEqual;
|
||||
|
|
@ -92,63 +93,34 @@ static const JSFunctionSpec numberFormat_methods[] = {
|
|||
static bool
|
||||
NumberFormat(JSContext* cx, const CallArgs& args, bool construct)
|
||||
{
|
||||
RootedObject obj(cx);
|
||||
// Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
|
||||
|
||||
// We're following ECMA-402 1st Edition when NumberFormat is called
|
||||
// because of backward compatibility issues.
|
||||
// See https://github.com/tc39/ecma402/issues/57
|
||||
if (!construct) {
|
||||
// ES Intl 1st ed., 11.1.2.1 step 3
|
||||
JSObject* intl = GlobalObject::getOrCreateIntlObject(cx, cx->global());
|
||||
if (!intl)
|
||||
return false;
|
||||
RootedValue self(cx, args.thisv());
|
||||
if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
|
||||
// ES Intl 1st ed., 11.1.2.1 step 4
|
||||
obj = ToObject(cx, self);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
// ES Intl 1st ed., 11.1.2.1 step 5
|
||||
bool extensible;
|
||||
if (!IsExtensible(cx, obj, &extensible))
|
||||
return false;
|
||||
if (!extensible)
|
||||
return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
|
||||
} else {
|
||||
// ES Intl 1st ed., 11.1.2.1 step 3.a
|
||||
construct = true;
|
||||
}
|
||||
}
|
||||
if (construct) {
|
||||
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||
RootedObject proto(cx);
|
||||
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreateNumberFormatPrototype(cx, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
||||
obj = NewObjectWithGivenProto<NumberFormatObject>(cx, proto);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
obj->as<NativeObject>().setReservedSlot(NumberFormatObject::INTERNALS_SLOT, NullValue());
|
||||
obj->as<NativeObject>().setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
|
||||
}
|
||||
|
||||
RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
|
||||
RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
|
||||
|
||||
// Step 3.
|
||||
if (!intl::InitializeObject(cx, obj, cx->names().InitializeNumberFormat, locales, options))
|
||||
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||
RootedObject proto(cx);
|
||||
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*obj);
|
||||
return true;
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreateNumberFormatPrototype(cx, cx->global());
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<NumberFormatObject*> numberFormat(cx);
|
||||
numberFormat = NewObjectWithGivenProto<NumberFormatObject>(cx, proto);
|
||||
if (!numberFormat)
|
||||
return false;
|
||||
|
||||
numberFormat->setReservedSlot(NumberFormatObject::INTERNALS_SLOT, NullValue());
|
||||
numberFormat->setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
|
||||
|
||||
RootedValue thisValue(cx, construct ? ObjectValue(*numberFormat) : args.thisv());
|
||||
RootedValue locales(cx, args.get(0));
|
||||
RootedValue options(cx, args.get(1));
|
||||
|
||||
// Step 3.
|
||||
return intl::LegacyIntlInitialize(cx, numberFormat, cx->names().InitializeNumberFormat, thisValue,
|
||||
locales, options, DateTimeFormatOptions::Standard, args.rval());
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -175,30 +147,23 @@ js::NumberFormatObject::finalize(FreeOp* fop, JSObject* obj)
|
|||
{
|
||||
MOZ_ASSERT(fop->onMainThread());
|
||||
|
||||
// This is-undefined check shouldn't be necessary, but for internal
|
||||
// brokenness in object allocation code. For the moment, hack around it by
|
||||
// explicitly guarding against the possibility of the reserved slot not
|
||||
// containing a private. See bug 949220.
|
||||
const Value& slot = obj->as<NativeObject>().getReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT);
|
||||
if (!slot.isUndefined()) {
|
||||
if (UNumberFormat* nf = static_cast<UNumberFormat*>(slot.toPrivate()))
|
||||
unum_close(nf);
|
||||
}
|
||||
const Value& slot = obj->as<NumberFormatObject>().getReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT);
|
||||
if (UNumberFormat* nf = static_cast<UNumberFormat*>(slot.toPrivate()))
|
||||
unum_close(nf);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
|
||||
js::CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global,
|
||||
MutableHandleObject constructor)
|
||||
{
|
||||
RootedFunction ctor(cx);
|
||||
ctor = GlobalObject::createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0);
|
||||
if (!ctor)
|
||||
return nullptr;
|
||||
|
||||
RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global,
|
||||
&NumberFormatObject::class_));
|
||||
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
proto->setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||
return nullptr;
|
||||
|
|
@ -229,38 +194,15 @@ js::CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalO
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
RootedValue options(cx);
|
||||
if (!intl::CreateDefaultOptions(cx, &options))
|
||||
return nullptr;
|
||||
|
||||
// 11.2.1 and 11.3
|
||||
if (!intl::InitializeObject(cx, proto, cx->names().InitializeNumberFormat, UndefinedHandleValue,
|
||||
options))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 8.1
|
||||
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||
if (!DefineProperty(cx, Intl, cx->names().NumberFormat, ctorValue, nullptr, nullptr, 0))
|
||||
return nullptr;
|
||||
|
||||
constructor.set(ctor);
|
||||
return proto;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_NumberFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 0);
|
||||
|
||||
RootedValue result(cx);
|
||||
if (!GetAvailableLocales(cx, unum_countAvailable, unum_getAvailable, &result))
|
||||
return false;
|
||||
args.rval().set(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_numberingSystem(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
|
|
@ -295,7 +237,7 @@ js::intl_numberingSystem(JSContext* cx, unsigned argc, Value* vp)
|
|||
* of the given NumberFormat.
|
||||
*/
|
||||
static UNumberFormat*
|
||||
NewUNumberFormat(JSContext* cx, HandleObject numberFormat)
|
||||
NewUNumberFormat(JSContext* cx, Handle<NumberFormatObject*> numberFormat)
|
||||
{
|
||||
RootedValue value(cx);
|
||||
|
||||
|
|
@ -305,7 +247,41 @@ NewUNumberFormat(JSContext* cx, HandleObject numberFormat)
|
|||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
|
||||
return nullptr;
|
||||
JSAutoByteString locale(cx, value.toString());
|
||||
|
||||
// ICU expects numberingSystem as a Unicode locale extensions on locale.
|
||||
|
||||
intl::LanguageTag tag(cx);
|
||||
{
|
||||
JSLinearString* locale = value.toString()->ensureLinear(cx);
|
||||
if (!locale)
|
||||
return nullptr;
|
||||
|
||||
if (!intl::LanguageTagParser::parse(cx, locale, tag))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::RootedVector<intl::UnicodeExtensionKeyword> keywords(cx);
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().numberingSystem, &value))
|
||||
return nullptr;
|
||||
|
||||
{
|
||||
JSLinearString* numberingSystem = value.toString()->ensureLinear(cx);
|
||||
if (!numberingSystem)
|
||||
return nullptr;
|
||||
|
||||
if (!keywords.emplaceBack("nu", numberingSystem))
|
||||
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;
|
||||
|
||||
UniqueChars locale = tag.toStringZ(cx);
|
||||
if (!locale)
|
||||
return nullptr;
|
||||
|
||||
|
|
@ -323,9 +299,6 @@ NewUNumberFormat(JSContext* cx, HandleObject numberFormat)
|
|||
RootedString currency(cx);
|
||||
AutoStableStringChars stableChars(cx);
|
||||
|
||||
// We don't need to look at numberingSystem - it can only be set via
|
||||
// the Unicode locale extension and is therefore already set on locale.
|
||||
|
||||
if (!GetProperty(cx, internals, internals, cx->names().style, &value))
|
||||
return nullptr;
|
||||
JSAutoByteString style(cx, value.toString());
|
||||
|
|
@ -398,7 +371,7 @@ NewUNumberFormat(JSContext* cx, HandleObject numberFormat)
|
|||
uUseGrouping = value.toBoolean();
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UNumberFormat* nf = unum_open(uStyle, nullptr, 0, IcuLocale(locale.ptr()), nullptr, &status);
|
||||
UNumberFormat* nf = unum_open(uStyle, nullptr, 0, IcuLocale(locale.get()), nullptr, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
intl::ReportInternalError(cx);
|
||||
return nullptr;
|
||||
|
|
@ -854,50 +827,23 @@ js::intl_FormatNumber(JSContext* cx, unsigned argc, Value* vp)
|
|||
MOZ_ASSERT(args[1].isNumber());
|
||||
MOZ_ASSERT(args[2].isBoolean());
|
||||
|
||||
RootedObject numberFormat(cx, &args[0].toObject());
|
||||
Rooted<NumberFormatObject*> numberFormat(cx, &args[0].toObject().as<NumberFormatObject>());
|
||||
|
||||
// Obtain a UNumberFormat object, cached if possible.
|
||||
bool isNumberFormatInstance = numberFormat->getClass() == &NumberFormatObject::class_;
|
||||
UNumberFormat* nf;
|
||||
if (isNumberFormatInstance) {
|
||||
void* priv =
|
||||
numberFormat->as<NativeObject>().getReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT).toPrivate();
|
||||
nf = static_cast<UNumberFormat*>(priv);
|
||||
if (!nf) {
|
||||
nf = NewUNumberFormat(cx, numberFormat);
|
||||
if (!nf)
|
||||
return false;
|
||||
numberFormat->as<NativeObject>().setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nf));
|
||||
}
|
||||
} else {
|
||||
// There's no good place to cache the ICU number format for an object
|
||||
// that has been initialized as a NumberFormat but is not a
|
||||
// NumberFormat instance. One possibility might be to add a
|
||||
// NumberFormat instance as an internal property to each such object.
|
||||
// Obtain a cached UNumberFormat object.
|
||||
void* priv =
|
||||
numberFormat->getReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT).toPrivate();
|
||||
UNumberFormat* nf = static_cast<UNumberFormat*>(priv);
|
||||
if (!nf) {
|
||||
nf = NewUNumberFormat(cx, numberFormat);
|
||||
if (!nf)
|
||||
return false;
|
||||
numberFormat->setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nf));
|
||||
}
|
||||
|
||||
// Use the UNumberFormat to actually format the number.
|
||||
double d = args[1].toNumber();
|
||||
RootedValue result(cx);
|
||||
|
||||
bool success;
|
||||
if (args[2].toBoolean()) {
|
||||
success = intl_FormatNumberToParts(cx, nf, d, &result);
|
||||
} else {
|
||||
MOZ_ASSERT(!args[2].toBoolean(),
|
||||
"shouldn't be doing formatToParts without an ICU that "
|
||||
"supports it");
|
||||
success = js::intl_FormatNumber(cx, nf, d, &result);
|
||||
return intl_FormatNumberToParts(cx, nf, args[1].toNumber(), args.rval());
|
||||
}
|
||||
|
||||
if (!isNumberFormatInstance)
|
||||
unum_close(nf);
|
||||
if (!success)
|
||||
return false;
|
||||
args.rval().set(result);
|
||||
return true;
|
||||
return intl_FormatNumber(cx, nf, args[1].toNumber(), args.rval());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@ class NumberFormatObject : public NativeObject
|
|||
};
|
||||
|
||||
extern JSObject*
|
||||
CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global);
|
||||
CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global,
|
||||
MutableHandleObject constructor);
|
||||
|
||||
/**
|
||||
* Returns a new instance of the standard built-in NumberFormat constructor.
|
||||
|
|
@ -49,17 +50,6 @@ CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObjec
|
|||
extern MOZ_MUST_USE bool
|
||||
intl_NumberFormat(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Returns an object indicating the supported locales for number formatting
|
||||
* by having a true-valued property for each such locale with the
|
||||
* canonicalized language tag as the property name. The object has no
|
||||
* prototype.
|
||||
*
|
||||
* Usage: availableLocales = intl_NumberFormat_availableLocales()
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_NumberFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Returns the numbering system type identifier per Unicode
|
||||
* Technical Standard 35, Unicode Locale Data Markup Language, for the
|
||||
|
|
@ -76,7 +66,7 @@ intl_numberingSystem(JSContext* cx, unsigned argc, Value* vp);
|
|||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 11.3.2.
|
||||
*
|
||||
* Usage: formatted = intl_FormatNumber(numberFormat, x)
|
||||
* Usage: formatted = intl_FormatNumber(numberFormat, x, formatToParts)
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_FormatNumber(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
|
|
|||
|
|
@ -8,21 +8,10 @@
|
|||
/**
|
||||
* NumberFormat internal properties.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 9.1 and 11.2.3.
|
||||
* Spec: ECMAScript Internationalization API Specification, 9.1 and 11.3.3.
|
||||
*/
|
||||
var numberFormatInternalProperties = {
|
||||
localeData: numberFormatLocaleData,
|
||||
_availableLocales: null,
|
||||
availableLocales: function()
|
||||
{
|
||||
var locales = this._availableLocales;
|
||||
if (locales)
|
||||
return locales;
|
||||
|
||||
locales = intl_NumberFormat_availableLocales();
|
||||
addSpecialMissingLanguageTags(locales);
|
||||
return (this._availableLocales = locales);
|
||||
},
|
||||
relevantExtensionKeys: ["nu"]
|
||||
};
|
||||
|
||||
|
|
@ -35,44 +24,38 @@ function resolveNumberFormatInternals(lazyNumberFormatData) {
|
|||
|
||||
var internalProps = std_Object_create(null);
|
||||
|
||||
// Step 3.
|
||||
var requestedLocales = lazyNumberFormatData.requestedLocales;
|
||||
|
||||
// Compute options that impact interpretation of locale.
|
||||
// Step 6.
|
||||
var opt = lazyNumberFormatData.opt;
|
||||
|
||||
var NumberFormat = numberFormatInternalProperties;
|
||||
|
||||
// Step 9.
|
||||
// Compute effective locale.
|
||||
|
||||
// Step 7.
|
||||
var localeData = NumberFormat.localeData;
|
||||
|
||||
// Step 10.
|
||||
var r = ResolveLocale(callFunction(NumberFormat.availableLocales, NumberFormat),
|
||||
// Step 8.
|
||||
var r = ResolveLocale("NumberFormat",
|
||||
lazyNumberFormatData.requestedLocales,
|
||||
lazyNumberFormatData.opt,
|
||||
NumberFormat.relevantExtensionKeys,
|
||||
localeData);
|
||||
|
||||
// Steps 11-12. (Step 13 is not relevant to our implementation.)
|
||||
// Steps 9-10. (Step 11 is not relevant to our implementation.)
|
||||
internalProps.locale = r.locale;
|
||||
internalProps.numberingSystem = r.nu;
|
||||
|
||||
// Compute formatting options.
|
||||
// Step 15.
|
||||
// Step 13.
|
||||
var s = lazyNumberFormatData.style;
|
||||
internalProps.style = s;
|
||||
|
||||
// Steps 19, 21.
|
||||
// Steps 17, 19.
|
||||
if (s === "currency") {
|
||||
internalProps.currency = lazyNumberFormatData.currency;
|
||||
internalProps.currencyDisplay = lazyNumberFormatData.currencyDisplay;
|
||||
}
|
||||
|
||||
// Step 22.
|
||||
internalProps.minimumIntegerDigits = lazyNumberFormatData.minimumIntegerDigits;
|
||||
|
||||
internalProps.minimumFractionDigits = lazyNumberFormatData.minimumFractionDigits;
|
||||
|
||||
internalProps.maximumFractionDigits = lazyNumberFormatData.maximumFractionDigits;
|
||||
|
||||
if ("minimumSignificantDigits" in lazyNumberFormatData) {
|
||||
|
|
@ -83,12 +66,9 @@ function resolveNumberFormatInternals(lazyNumberFormatData) {
|
|||
internalProps.maximumSignificantDigits = lazyNumberFormatData.maximumSignificantDigits;
|
||||
}
|
||||
|
||||
// Step 27.
|
||||
// Step 24.
|
||||
internalProps.useGrouping = lazyNumberFormatData.useGrouping;
|
||||
|
||||
// Step 34.
|
||||
internalProps.boundFormat = undefined;
|
||||
|
||||
// The caller is responsible for associating |internalProps| with the right
|
||||
// object using |setInternalProperties|.
|
||||
return internalProps;
|
||||
|
|
@ -96,11 +76,13 @@ function resolveNumberFormatInternals(lazyNumberFormatData) {
|
|||
|
||||
|
||||
/**
|
||||
* Returns an object containing the NumberFormat internal properties of |obj|,
|
||||
* or throws a TypeError if |obj| isn't NumberFormat-initialized.
|
||||
* Returns an object containing the NumberFormat internal properties of |obj|.
|
||||
*/
|
||||
function getNumberFormatInternals(obj, methodName) {
|
||||
var internals = getIntlObjectInternals(obj, "NumberFormat", methodName);
|
||||
function getNumberFormatInternals(obj) {
|
||||
assert(IsObject(obj), "getNumberFormatInternals called with non-object");
|
||||
assert(IsNumberFormat(obj), "getNumberFormatInternals called with non-NumberFormat");
|
||||
|
||||
var internals = getIntlObjectInternals(obj);
|
||||
assert(internals.type === "NumberFormat", "bad type escaped getIntlObjectInternals");
|
||||
|
||||
// If internal properties have already been computed, use them.
|
||||
|
|
@ -114,24 +96,45 @@ function getNumberFormatInternals(obj, methodName) {
|
|||
return internalProps;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 11.1.11 UnwrapNumberFormat( nf )
|
||||
*/
|
||||
function UnwrapNumberFormat(nf, methodName) {
|
||||
// Step 1 (not applicable in our implementation).
|
||||
|
||||
// Step 2.
|
||||
if ((!IsObject(nf) || !IsNumberFormat(nf)) && nf instanceof GetNumberFormatConstructor()) {
|
||||
nf = nf[intlFallbackSymbol()];
|
||||
}
|
||||
|
||||
// Step 3.
|
||||
if (!IsObject(nf) || !IsNumberFormat(nf))
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "NumberFormat", methodName, "NumberFormat");
|
||||
|
||||
// Step 4.
|
||||
return nf;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Applies digit options used for number formatting onto the intl object.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 11.1.1.
|
||||
*/
|
||||
function SetNumberFormatDigitOptions(lazyData, options, mnfdDefault) {
|
||||
// We skip Step 1 because we set the properties on a lazyData object.
|
||||
// We skip step 1 because we set the properties on a lazyData object.
|
||||
|
||||
// Step 2-3.
|
||||
// Steps 2-4.
|
||||
assert(IsObject(options), "SetNumberFormatDigitOptions");
|
||||
assert(typeof mnfdDefault === "number", "SetNumberFormatDigitOptions");
|
||||
|
||||
// Steps 4-6.
|
||||
// Steps 5-8.
|
||||
const mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
|
||||
const mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault);
|
||||
const mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20);
|
||||
|
||||
// Steps 7-8.
|
||||
// Steps 9-10.
|
||||
let mnsd = options.minimumSignificantDigits;
|
||||
let mxsd = options.maximumSignificantDigits;
|
||||
|
||||
|
|
@ -175,17 +178,9 @@ function toASCIIUpperCase(s) {
|
|||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 6.3.1.
|
||||
*/
|
||||
function getIsWellFormedCurrencyCodeRE() {
|
||||
return internalIntlRegExps.isWellFormedCurrencyCodeRE ||
|
||||
(internalIntlRegExps.isWellFormedCurrencyCodeRE = RegExpCreate("[^A-Z]"));
|
||||
}
|
||||
|
||||
function IsWellFormedCurrencyCode(currency) {
|
||||
var c = ToString(currency);
|
||||
var normalized = toASCIIUpperCase(c);
|
||||
if (normalized.length !== 3)
|
||||
return false;
|
||||
return !regexp_test_no_statics(getIsWellFormedCurrencyCodeRE(), normalized);
|
||||
assert(typeof currency === "string", "currency is a string value");
|
||||
return currency.length === 3 && IsASCIIAlphaString(currency);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -197,17 +192,11 @@ function IsWellFormedCurrencyCode(currency) {
|
|||
* This later work occurs in |resolveNumberFormatInternals|; steps not noted
|
||||
* here occur there.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 11.1.1.
|
||||
* Spec: ECMAScript Internationalization API Specification, 11.1.2.
|
||||
*/
|
||||
function InitializeNumberFormat(numberFormat, locales, options) {
|
||||
assert(IsObject(numberFormat), "InitializeNumberFormat");
|
||||
|
||||
// Step 1.
|
||||
if (isInitializedIntlObject(numberFormat))
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
|
||||
|
||||
// Step 2.
|
||||
var internals = initializeIntlObject(numberFormat);
|
||||
function InitializeNumberFormat(numberFormat, thisValue, locales, options) {
|
||||
assert(IsObject(numberFormat), "InitializeNumberFormat called with non-object");
|
||||
assert(IsNumberFormat(numberFormat), "InitializeNumberFormat called with non-NumberFormat");
|
||||
|
||||
// Lazy NumberFormat data has the following structure:
|
||||
//
|
||||
|
|
@ -222,6 +211,8 @@ function InitializeNumberFormat(numberFormat, locales, options) {
|
|||
// opt: // opt object computed in InitializeNumberFormat
|
||||
// {
|
||||
// localeMatcher: "lookup" / "best fit",
|
||||
//
|
||||
// nu: string matching a Unicode extension type, // optional
|
||||
// }
|
||||
//
|
||||
// minimumIntegerDigits: integer ∈ [1, 21],
|
||||
|
|
@ -240,11 +231,11 @@ function InitializeNumberFormat(numberFormat, locales, options) {
|
|||
// subset of them.
|
||||
var lazyNumberFormatData = std_Object_create(null);
|
||||
|
||||
// Step 3.
|
||||
// Step 1.
|
||||
var requestedLocales = CanonicalizeLocaleList(locales);
|
||||
lazyNumberFormatData.requestedLocales = requestedLocales;
|
||||
|
||||
// Steps 4-5.
|
||||
// Steps 2-3.
|
||||
//
|
||||
// If we ever need more speed here at startup, we should try to detect the
|
||||
// case where |options === undefined| and Object.prototype hasn't been
|
||||
|
|
@ -257,20 +248,30 @@ function InitializeNumberFormat(numberFormat, locales, options) {
|
|||
options = ToObject(options);
|
||||
|
||||
// Compute options that impact interpretation of locale.
|
||||
// Step 6.
|
||||
// Step 4.
|
||||
var opt = new Record();
|
||||
lazyNumberFormatData.opt = opt;
|
||||
|
||||
// Steps 7-8.
|
||||
// Steps 5-6.
|
||||
var matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
|
||||
opt.localeMatcher = matcher;
|
||||
|
||||
var numberingSystem = GetOption(options, "numberingSystem", "string", undefined, undefined);
|
||||
|
||||
if (numberingSystem !== undefined) {
|
||||
numberingSystem = intl_ValidateAndCanonicalizeUnicodeExtensionType(numberingSystem,
|
||||
"numberingSystem",
|
||||
"nu");
|
||||
}
|
||||
|
||||
opt.nu = numberingSystem;
|
||||
|
||||
// Compute formatting options.
|
||||
// Step 14.
|
||||
// Step 12.
|
||||
var s = GetOption(options, "style", "string", ["decimal", "percent", "currency"], "decimal");
|
||||
lazyNumberFormatData.style = s;
|
||||
|
||||
// Steps 16-19.
|
||||
// Steps 14-17.
|
||||
var c = GetOption(options, "currency", "string", undefined, undefined);
|
||||
if (c !== undefined && !IsWellFormedCurrencyCode(c))
|
||||
ThrowRangeError(JSMSG_INVALID_CURRENCY_CODE, c);
|
||||
|
|
@ -285,12 +286,12 @@ function InitializeNumberFormat(numberFormat, locales, options) {
|
|||
cDigits = CurrencyDigits(c);
|
||||
}
|
||||
|
||||
// Step 20.
|
||||
// Step 18.
|
||||
var cd = GetOption(options, "currencyDisplay", "string", ["code", "symbol", "name"], "symbol");
|
||||
if (s === "currency")
|
||||
lazyNumberFormatData.currencyDisplay = cd;
|
||||
|
||||
// Steps 22-24.
|
||||
// Steps 20-22.
|
||||
SetNumberFormatDigitOptions(lazyNumberFormatData, options, s === "currency" ? cDigits: 0);
|
||||
|
||||
// Step 25.
|
||||
|
|
@ -304,15 +305,31 @@ function InitializeNumberFormat(numberFormat, locales, options) {
|
|||
std_Math_max(lazyNumberFormatData.minimumFractionDigits, mxfdDefault);
|
||||
}
|
||||
|
||||
// Step 26.
|
||||
// Steps 23.
|
||||
var g = GetOption(options, "useGrouping", "boolean", undefined, true);
|
||||
lazyNumberFormatData.useGrouping = g;
|
||||
|
||||
// Steps 35-36.
|
||||
// Step 31.
|
||||
//
|
||||
// We've done everything that must be done now: mark the lazy data as fully
|
||||
// computed and install it.
|
||||
setLazyData(internals, "NumberFormat", lazyNumberFormatData);
|
||||
initializeIntlObject(numberFormat, "NumberFormat", lazyNumberFormatData);
|
||||
|
||||
// 11.2.1, steps 4-5.
|
||||
// TODO: spec issue - The current spec doesn't have the IsObject check,
|
||||
// which means |Intl.NumberFormat.call(null)| is supposed to throw here.
|
||||
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;
|
||||
}
|
||||
|
||||
// 11.2.1, step 6.
|
||||
return numberFormat;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -356,17 +373,14 @@ var currencyDigits = {
|
|||
/**
|
||||
* Returns the number of decimal digits to be used for the given currency.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 11.1.1.
|
||||
* Spec: ECMAScript Internationalization API Specification, 11.1.3.
|
||||
*/
|
||||
function getCurrencyDigitsRE() {
|
||||
return internalIntlRegExps.currencyDigitsRE ||
|
||||
(internalIntlRegExps.currencyDigitsRE = RegExpCreate("^[A-Z]{3}$"));
|
||||
}
|
||||
function CurrencyDigits(currency) {
|
||||
assert(typeof currency === "string", "CurrencyDigits");
|
||||
assert(regexp_test_no_statics(getCurrencyDigitsRE(), currency), "CurrencyDigits");
|
||||
assert(typeof currency === "string", "currency is a string value");
|
||||
assert(IsWellFormedCurrencyCode(currency), "currency is well-formed");
|
||||
assert(currency == toASCIIUpperCase(currency), "currency is all upper-case");
|
||||
|
||||
if (callFunction(std_Object_hasOwnProperty, currencyDigits, currency))
|
||||
if (hasOwn(currency, currencyDigits))
|
||||
return currencyDigits[currency];
|
||||
return 2;
|
||||
}
|
||||
|
|
@ -377,14 +391,18 @@ function CurrencyDigits(currency) {
|
|||
* matching (possibly fallback) locale. Locales appear in the same order in the
|
||||
* returned list as in the input list.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 11.2.2.
|
||||
* Spec: ECMAScript Internationalization API Specification, 11.3.2.
|
||||
*/
|
||||
function Intl_NumberFormat_supportedLocalesOf(locales /*, options*/) {
|
||||
var options = arguments.length > 1 ? arguments[1] : undefined;
|
||||
|
||||
var availableLocales = callFunction(numberFormatInternalProperties.availableLocales,
|
||||
numberFormatInternalProperties);
|
||||
// Step 1.
|
||||
var availableLocales = "NumberFormat";
|
||||
|
||||
// Step 2.
|
||||
var requestedLocales = CanonicalizeLocaleList(locales);
|
||||
|
||||
// Step 3.
|
||||
return SupportedLocales(availableLocales, requestedLocales, options);
|
||||
}
|
||||
|
||||
|
|
@ -397,8 +415,8 @@ function getNumberingSystems(locale) {
|
|||
// Algorithmic numbering systems are typically tied to one locale, so for
|
||||
// lack of information we don't offer them. To increase chances that
|
||||
// other software will process output correctly, we further restrict to
|
||||
// those decimal numbering systems explicitly listed in table 2 of
|
||||
// the ECMAScript Internationalization API Specification, 11.3.2, which
|
||||
// those decimal numbering systems explicitly listed in table 3 of
|
||||
// the ECMAScript Internationalization API Specification, 11.1.6, which
|
||||
// in turn are those with full specifications in version 21 of Unicode
|
||||
// Technical Standard #35 using digits that were defined in Unicode 5.0,
|
||||
// the Unicode version supported in Windows Vista.
|
||||
|
|
@ -416,9 +434,12 @@ function getNumberingSystems(locale) {
|
|||
}
|
||||
|
||||
|
||||
function numberFormatLocaleData(locale) {
|
||||
function numberFormatLocaleData() {
|
||||
return {
|
||||
nu: getNumberingSystems(locale)
|
||||
nu: getNumberingSystems,
|
||||
default: {
|
||||
nu: intl_numberingSystem,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -426,7 +447,7 @@ function numberFormatLocaleData(locale) {
|
|||
/**
|
||||
* Function to be bound and returned by Intl.NumberFormat.prototype.format.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 11.3.2.
|
||||
* Spec: ECMAScript Internationalization API Specification, 11.1.4.
|
||||
*/
|
||||
function numberFormatFormatToBind(value) {
|
||||
// Steps 1.a.i implemented by ECMAScript declaration binding instantiation,
|
||||
|
|
@ -443,31 +464,37 @@ function numberFormatFormatToBind(value) {
|
|||
* representing the result of calling ToNumber(value) according to the
|
||||
* effective locale and the formatting options of this NumberFormat.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 11.3.2.
|
||||
* Spec: ECMAScript Internationalization API Specification, 11.4.3.
|
||||
*/
|
||||
function Intl_NumberFormat_format_get() {
|
||||
// Check "this NumberFormat object" per introduction of section 11.3.
|
||||
var internals = getNumberFormatInternals(this, "format");
|
||||
// Steps 1-3.
|
||||
var nf = UnwrapNumberFormat(this, "format");
|
||||
|
||||
// Step 1.
|
||||
var internals = getNumberFormatInternals(nf);
|
||||
|
||||
// Step 4.
|
||||
if (internals.boundFormat === undefined) {
|
||||
// Step 1.a.
|
||||
var F = numberFormatFormatToBind;
|
||||
// Steps 4.a-b.
|
||||
var F = callFunction(FunctionBind, numberFormatFormatToBind, nf);
|
||||
|
||||
// Step 1.b-d.
|
||||
var bf = callFunction(FunctionBind, F, this);
|
||||
internals.boundFormat = bf;
|
||||
// Step 4.c.
|
||||
internals.boundFormat = F;
|
||||
}
|
||||
// Step 2.
|
||||
|
||||
// Step 5.
|
||||
return internals.boundFormat;
|
||||
}
|
||||
_SetCanonicalName(Intl_NumberFormat_format_get, "get format");
|
||||
|
||||
/**
|
||||
* 11.4.4 Intl.NumberFormat.prototype.formatToParts ( value )
|
||||
*/
|
||||
function Intl_NumberFormat_formatToParts(value) {
|
||||
// Step 1.
|
||||
var nf = this;
|
||||
// Steps 1-3.
|
||||
var nf = UnwrapNumberFormat(this, "formatToParts");
|
||||
|
||||
// Steps 2-3.
|
||||
getNumberFormatInternals(nf, "formatToParts");
|
||||
// Ensure the NumberFormat internals are resolved.
|
||||
getNumberFormatInternals(nf);
|
||||
|
||||
// Step 4.
|
||||
var x = ToNumber(value);
|
||||
|
|
@ -479,12 +506,15 @@ function Intl_NumberFormat_formatToParts(value) {
|
|||
/**
|
||||
* Returns the resolved options for a NumberFormat object.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 11.3.3 and 11.4.
|
||||
* Spec: ECMAScript Internationalization API Specification, 11.4.5.
|
||||
*/
|
||||
function Intl_NumberFormat_resolvedOptions() {
|
||||
// Check "this NumberFormat object" per introduction of section 11.3.
|
||||
var internals = getNumberFormatInternals(this, "resolvedOptions");
|
||||
// Steps 1-3.
|
||||
var nf = UnwrapNumberFormat(this, "resolvedOptions");
|
||||
|
||||
var internals = getNumberFormatInternals(nf);
|
||||
|
||||
// Steps 4-5.
|
||||
var result = {
|
||||
locale: internals.locale,
|
||||
numberingSystem: internals.numberingSystem,
|
||||
|
|
@ -494,17 +524,31 @@ function Intl_NumberFormat_resolvedOptions() {
|
|||
maximumFractionDigits: internals.maximumFractionDigits,
|
||||
useGrouping: internals.useGrouping
|
||||
};
|
||||
var optionalProperties = [
|
||||
"currency",
|
||||
"currencyDisplay",
|
||||
"minimumSignificantDigits",
|
||||
"maximumSignificantDigits"
|
||||
];
|
||||
for (var i = 0; i < optionalProperties.length; i++) {
|
||||
var p = optionalProperties[i];
|
||||
if (callFunction(std_Object_hasOwnProperty, internals, p))
|
||||
_DefineDataProperty(result, p, internals[p]);
|
||||
|
||||
// currency and currencyDisplay are only present for currency formatters.
|
||||
assert(hasOwn("currency", internals) === (internals.style === "currency"),
|
||||
"currency is present iff style is 'currency'");
|
||||
assert(hasOwn("currencyDisplay", internals) === (internals.style === "currency"),
|
||||
"currencyDisplay is present iff style is 'currency'");
|
||||
|
||||
if (hasOwn("currency", internals)) {
|
||||
_DefineDataProperty(result, "currency", internals.currency);
|
||||
_DefineDataProperty(result, "currencyDisplay", internals.currencyDisplay);
|
||||
}
|
||||
|
||||
// Min/Max significant digits are either both present or not at all.
|
||||
assert(hasOwn("minimumSignificantDigits", internals) ===
|
||||
hasOwn("maximumSignificantDigits", internals),
|
||||
"minimumSignificantDigits is present iff maximumSignificantDigits is present");
|
||||
|
||||
if (hasOwn("minimumSignificantDigits", internals)) {
|
||||
_DefineDataProperty(result, "minimumSignificantDigits",
|
||||
internals.minimumSignificantDigits);
|
||||
_DefineDataProperty(result, "maximumSignificantDigits",
|
||||
internals.maximumSignificantDigits);
|
||||
}
|
||||
|
||||
// Step 6.
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ using namespace js;
|
|||
using mozilla::AssertedCast;
|
||||
|
||||
using js::intl::CallICU;
|
||||
using js::intl::GetAvailableLocales;
|
||||
using js::intl::IcuLocale;
|
||||
using js::intl::INITIAL_CHAR_BUFFER_SIZE;
|
||||
using js::intl::StringsAreEqual;
|
||||
|
|
@ -79,7 +78,7 @@ static const JSFunctionSpec pluralRules_methods[] = {
|
|||
|
||||
/**
|
||||
* PluralRules constructor.
|
||||
* Spec: ECMAScript 402 API, PluralRules, 1.1
|
||||
* Spec: ECMAScript 402 API, PluralRules, 13.2.1
|
||||
*/
|
||||
static bool
|
||||
PluralRules(JSContext* cx, const CallArgs& args, bool construct)
|
||||
|
|
@ -113,8 +112,8 @@ PluralRules(JSContext* cx, const CallArgs& args, bool construct)
|
|||
if (!obj)
|
||||
return false;
|
||||
|
||||
obj->as<NativeObject>().setReservedSlot(PluralRulesObject::INTERNALS_SLOT, NullValue());
|
||||
obj->as<NativeObject>().setReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT, PrivateValue(nullptr));
|
||||
obj->as<PluralRulesObject>().setReservedSlot(PluralRulesObject::INTERNALS_SLOT, NullValue());
|
||||
obj->as<PluralRulesObject>().setReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT, PrivateValue(nullptr));
|
||||
}
|
||||
|
||||
RootedValue locales(cx, args.get(0));
|
||||
|
|
@ -147,15 +146,9 @@ js::PluralRulesObject::finalize(FreeOp* fop, JSObject* obj)
|
|||
{
|
||||
MOZ_ASSERT(fop->onMainThread());
|
||||
|
||||
// This is-undefined check shouldn't be necessary, but for internal
|
||||
// brokenness in object allocation code. For the moment, hack around it by
|
||||
// explicitly guarding against the possibility of the reserved slot not
|
||||
// containing a private. See bug 949220.
|
||||
const Value& slot = obj->as<PluralRulesObject>().getReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT);
|
||||
if (!slot.isUndefined()) {
|
||||
if (UPluralRules* pr = static_cast<UPluralRules*>(slot.toPrivate()))
|
||||
uplrules_close(pr);
|
||||
}
|
||||
if (UPluralRules* pr = static_cast<UPluralRules*>(slot.toPrivate()))
|
||||
uplrules_close(pr);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
|
|
@ -166,10 +159,9 @@ js::CreatePluralRulesPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalOb
|
|||
if (!ctor)
|
||||
return nullptr;
|
||||
|
||||
RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &PluralRulesObject::class_));
|
||||
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
proto->setReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT, PrivateValue(nullptr));
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||
return nullptr;
|
||||
|
|
@ -180,16 +172,6 @@ js::CreatePluralRulesPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalOb
|
|||
if (!JS_DefineFunctions(cx, proto, pluralRules_methods))
|
||||
return nullptr;
|
||||
|
||||
RootedValue options(cx);
|
||||
if (!intl::CreateDefaultOptions(cx, &options))
|
||||
return nullptr;
|
||||
|
||||
if (!intl::InitializeObject(cx, proto, cx->names().InitializePluralRules, UndefinedHandleValue,
|
||||
options))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||
if (!DefineProperty(cx, Intl, cx->names().PluralRules, ctorValue, nullptr, nullptr, 0))
|
||||
return nullptr;
|
||||
|
|
@ -197,21 +179,6 @@ js::CreatePluralRulesPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalOb
|
|||
return proto;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_PluralRules_availableLocales(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 0);
|
||||
|
||||
RootedValue result(cx);
|
||||
// We're going to use ULocale availableLocales as per ICU recommendation:
|
||||
// https://ssl.icu-project.org/trac/ticket/12756
|
||||
if (!GetAvailableLocales(cx, uloc_countAvailable, uloc_getAvailable, &result))
|
||||
return false;
|
||||
args.rval().set(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* This creates new UNumberFormat with calculated digit formatting
|
||||
|
|
@ -222,7 +189,7 @@ js::intl_PluralRules_availableLocales(JSContext* cx, unsigned argc, Value* vp)
|
|||
*
|
||||
*/
|
||||
static UNumberFormat*
|
||||
NewUNumberFormatForPluralRules(JSContext* cx, HandleObject pluralRules)
|
||||
NewUNumberFormatForPluralRules(JSContext* cx, Handle<PluralRulesObject*> pluralRules)
|
||||
{
|
||||
RootedObject internals(cx, intl::GetInternalsObject(cx, pluralRules));
|
||||
if (!internals)
|
||||
|
|
@ -299,7 +266,7 @@ js::intl_SelectPluralRule(JSContext* cx, unsigned argc, Value* vp)
|
|||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
RootedObject pluralRules(cx, &args[0].toObject());
|
||||
Rooted<PluralRulesObject*> pluralRules(cx, &args[0].toObject().as<PluralRulesObject>());
|
||||
|
||||
UNumberFormat* nf = NewUNumberFormatForPluralRules(cx, pluralRules);
|
||||
if (!nf)
|
||||
|
|
|
|||
|
|
@ -49,17 +49,6 @@ CreatePluralRulesPrototype(JSContext* cx, JS::Handle<JSObject*> Intl,
|
|||
extern MOZ_MUST_USE bool
|
||||
intl_PluralRules(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Returns an object indicating the supported locales for plural rules
|
||||
* by having a true-valued property for each such locale with the
|
||||
* canonicalized language tag as the property name. The object has no
|
||||
* prototype.
|
||||
*
|
||||
* Usage: availableLocales = intl_PluralRules_availableLocales()
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_PluralRules_availableLocales(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Returns a plural rule for the number x according to the effective
|
||||
* locale and the formatting options of the given PluralRules.
|
||||
|
|
|
|||
|
|
@ -7,22 +7,20 @@
|
|||
/**
|
||||
* PluralRules internal properties.
|
||||
*
|
||||
* Spec: ECMAScript 402 API, PluralRules, 1.3.3.
|
||||
* Spec: ECMAScript 402 API, PluralRules, 13.3.3.
|
||||
*/
|
||||
var pluralRulesInternalProperties = {
|
||||
_availableLocales: null,
|
||||
availableLocales: function()
|
||||
{
|
||||
var locales = this._availableLocales;
|
||||
if (locales)
|
||||
return locales;
|
||||
|
||||
locales = intl_PluralRules_availableLocales();
|
||||
addSpecialMissingLanguageTags(locales);
|
||||
return (this._availableLocales = locales);
|
||||
}
|
||||
localeData: pluralRulesLocaleData,
|
||||
relevantExtensionKeys: [],
|
||||
};
|
||||
|
||||
|
||||
function pluralRulesLocaleData() {
|
||||
// PluralRules don't support any extension keys.
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute an internal properties object from |lazyPluralRulesData|.
|
||||
*/
|
||||
|
|
@ -35,20 +33,25 @@ function resolvePluralRulesInternals(lazyPluralRulesData) {
|
|||
|
||||
var PluralRules = pluralRulesInternalProperties;
|
||||
|
||||
// Step 13.
|
||||
const r = ResolveLocale(callFunction(PluralRules.availableLocales, PluralRules),
|
||||
lazyPluralRulesData.requestedLocales,
|
||||
lazyPluralRulesData.opt,
|
||||
noRelevantExtensionKeys, undefined);
|
||||
// Compute effective locale.
|
||||
|
||||
// Step 14.
|
||||
// Step 10.
|
||||
var localeData = PluralRules.localeData;
|
||||
|
||||
// Step 11.
|
||||
const r = ResolveLocale("PluralRules",
|
||||
lazyPluralRulesData.requestedLocales,
|
||||
lazyPluralRulesData.opt,
|
||||
PluralRules.relevantExtensionKeys,
|
||||
localeData);
|
||||
|
||||
// Step 12.
|
||||
internalProps.locale = r.locale;
|
||||
|
||||
// Step 8.
|
||||
internalProps.type = lazyPluralRulesData.type;
|
||||
|
||||
internalProps.pluralCategories = intl_GetPluralCategories(
|
||||
internalProps.locale,
|
||||
internalProps.type);
|
||||
|
||||
// Step 9.
|
||||
internalProps.minimumIntegerDigits = lazyPluralRulesData.minimumIntegerDigits;
|
||||
internalProps.minimumFractionDigits = lazyPluralRulesData.minimumFractionDigits;
|
||||
internalProps.maximumFractionDigits = lazyPluralRulesData.maximumFractionDigits;
|
||||
|
|
@ -59,15 +62,20 @@ function resolvePluralRulesInternals(lazyPluralRulesData) {
|
|||
internalProps.maximumSignificantDigits = lazyPluralRulesData.maximumSignificantDigits;
|
||||
}
|
||||
|
||||
// Step 13 (lazily computed on first access).
|
||||
internalProps.pluralCategories = null;
|
||||
|
||||
return internalProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object containing the PluralRules internal properties of |obj|,
|
||||
* or throws a TypeError if |obj| isn't PluralRules-initialized.
|
||||
* Returns an object containing the PluralRules internal properties of |obj|.
|
||||
*/
|
||||
function getPluralRulesInternals(obj, methodName) {
|
||||
var internals = getIntlObjectInternals(obj, "PluralRules", methodName);
|
||||
function getPluralRulesInternals(obj) {
|
||||
assert(IsObject(obj), "getPluralRulesInternals called with non-object");
|
||||
assert(IsPluralRules(obj), "getPluralRulesInternals called with non-PluralRules");
|
||||
|
||||
var internals = getIntlObjectInternals(obj);
|
||||
assert(internals.type === "PluralRules", "bad type escaped getIntlObjectInternals");
|
||||
|
||||
var internalProps = maybeInternalProperties(internals);
|
||||
|
|
@ -88,16 +96,11 @@ function getPluralRulesInternals(obj, methodName) {
|
|||
* This later work occurs in |resolvePluralRulesInternals|; steps not noted
|
||||
* here occur there.
|
||||
*
|
||||
* Spec: ECMAScript 402 API, PluralRules, 1.1.1.
|
||||
* Spec: ECMAScript 402 API, PluralRules, 13.1.1.
|
||||
*/
|
||||
function InitializePluralRules(pluralRules, locales, options) {
|
||||
assert(IsObject(pluralRules), "InitializePluralRules");
|
||||
|
||||
// Step 1.
|
||||
if (isInitializedIntlObject(pluralRules))
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
|
||||
|
||||
let internals = initializeIntlObject(pluralRules);
|
||||
assert(IsObject(pluralRules), "InitializePluralRules called with non-object");
|
||||
assert(IsPluralRules(pluralRules), "InitializePluralRules called with non-PluralRules");
|
||||
|
||||
// Lazy PluralRules data has the following structure:
|
||||
//
|
||||
|
|
@ -124,30 +127,29 @@ function InitializePluralRules(pluralRules, locales, options) {
|
|||
// subset of them.
|
||||
const lazyPluralRulesData = std_Object_create(null);
|
||||
|
||||
// Step 3.
|
||||
// Step 1.
|
||||
let requestedLocales = CanonicalizeLocaleList(locales);
|
||||
lazyPluralRulesData.requestedLocales = requestedLocales;
|
||||
|
||||
// Steps 4-5.
|
||||
// Steps 2-3.
|
||||
if (options === undefined)
|
||||
options = {};
|
||||
else
|
||||
options = ToObject(options);
|
||||
|
||||
// Step 6.
|
||||
const type = GetOption(options, "type", "string", ["cardinal", "ordinal"], "cardinal");
|
||||
lazyPluralRulesData.type = type;
|
||||
|
||||
// Step 8.
|
||||
// Step 4.
|
||||
let opt = new Record();
|
||||
lazyPluralRulesData.opt = opt;
|
||||
|
||||
// Steps 9-10.
|
||||
// Steps 5-6.
|
||||
let matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
|
||||
opt.localeMatcher = matcher;
|
||||
|
||||
// Step 7.
|
||||
const type = GetOption(options, "type", "string", ["cardinal", "ordinal"], "cardinal");
|
||||
lazyPluralRulesData.type = type;
|
||||
|
||||
// Step 11.
|
||||
// Step 9.
|
||||
SetNumberFormatDigitOptions(lazyPluralRulesData, options, 0);
|
||||
|
||||
// Step 12.
|
||||
|
|
@ -156,7 +158,11 @@ function InitializePluralRules(pluralRules, locales, options) {
|
|||
std_Math_max(lazyPluralRulesData.minimumFractionDigits, 3);
|
||||
}
|
||||
|
||||
setLazyData(internals, "PluralRules", lazyPluralRulesData)
|
||||
// Step 15.
|
||||
//
|
||||
// We've done everything that must be done now: mark the lazy data as fully
|
||||
// computed and install it.
|
||||
initializeIntlObject(pluralRules, "PluralRules", lazyPluralRulesData)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -164,14 +170,14 @@ function InitializePluralRules(pluralRules, locales, options) {
|
|||
* matching (possibly fallback) locale. Locales appear in the same order in the
|
||||
* returned list as in the input list.
|
||||
*
|
||||
* Spec: ECMAScript 402 API, PluralRules, 1.3.2.
|
||||
* Spec: ECMAScript 402 API, PluralRules, 13.3.2.
|
||||
*/
|
||||
function Intl_PluralRules_supportedLocalesOf(locales /*, options*/) {
|
||||
var options = arguments.length > 1 ? arguments[1] : undefined;
|
||||
|
||||
// Step 1.
|
||||
var availableLocales = callFunction(pluralRulesInternalProperties.availableLocales,
|
||||
pluralRulesInternalProperties);
|
||||
var availableLocales = "PluralRules";
|
||||
|
||||
// Step 2.
|
||||
let requestedLocales = CanonicalizeLocaleList(locales);
|
||||
|
||||
|
|
@ -184,15 +190,20 @@ function Intl_PluralRules_supportedLocalesOf(locales /*, options*/) {
|
|||
* the number passed as value according to the
|
||||
* effective locale and the formatting options of this PluralRules.
|
||||
*
|
||||
* Spec: ECMAScript 402 API, PluralRules, 1.4.3.
|
||||
* Spec: ECMAScript 402 API, PluralRules, 13.4.3.
|
||||
*/
|
||||
function Intl_PluralRules_select(value) {
|
||||
// Step 1.
|
||||
let pluralRules = this;
|
||||
// Step 2.
|
||||
let internals = getPluralRulesInternals(pluralRules, "select");
|
||||
|
||||
// Steps 3-4.
|
||||
// Steps 2-3.
|
||||
if (!IsObject(pluralRules) || !IsPluralRules(pluralRules))
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "PluralRules", "select", "PluralRules");
|
||||
|
||||
// Ensure the PluralRules internals are resolved.
|
||||
getPluralRulesInternals(pluralRules);
|
||||
|
||||
// Step 4.
|
||||
let n = ToNumber(value);
|
||||
|
||||
// Step 5.
|
||||
|
|
@ -202,11 +213,34 @@ function Intl_PluralRules_select(value) {
|
|||
/**
|
||||
* Returns the resolved options for a PluralRules object.
|
||||
*
|
||||
* Spec: ECMAScript 402 API, PluralRules, 1.4.4.
|
||||
* Spec: ECMAScript 402 API, PluralRules, 13.4.4.
|
||||
*/
|
||||
function Intl_PluralRules_resolvedOptions() {
|
||||
var internals = getPluralRulesInternals(this, "resolvedOptions");
|
||||
// Step 1.
|
||||
var pluralRules = this;
|
||||
|
||||
// Steps 2-3.
|
||||
if (!IsObject(pluralRules) || !IsPluralRules(pluralRules)) {
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "PluralRules", "resolvedOptions",
|
||||
"PluralRules");
|
||||
}
|
||||
|
||||
var internals = getPluralRulesInternals(pluralRules);
|
||||
|
||||
var internalsPluralCategories = internals.pluralCategories;
|
||||
if (internalsPluralCategories === null) {
|
||||
internalsPluralCategories = intl_GetPluralCategories(internals.locale, internals.type);
|
||||
internals.pluralCategories = internalsPluralCategories;
|
||||
}
|
||||
|
||||
// TODO: The current spec actually requires to return the internal array
|
||||
// object and not a copy of it.
|
||||
// <https://github.com/tc39/proposal-intl-plural-rules/issues/28#issuecomment-341557030>
|
||||
var pluralCategories = [];
|
||||
for (var i = 0; i < internalsPluralCategories.length; i++)
|
||||
_DefineDataProperty(pluralCategories, i, internalsPluralCategories[i]);
|
||||
|
||||
// Steps 4-5.
|
||||
var result = {
|
||||
locale: internals.locale,
|
||||
type: internals.type,
|
||||
|
|
@ -216,16 +250,19 @@ function Intl_PluralRules_resolvedOptions() {
|
|||
maximumFractionDigits: internals.maximumFractionDigits,
|
||||
};
|
||||
|
||||
var optionalProperties = [
|
||||
"minimumSignificantDigits",
|
||||
"maximumSignificantDigits"
|
||||
];
|
||||
// Min/Max significant digits are either both present or not at all.
|
||||
assert(hasOwn("minimumSignificantDigits", internals) ===
|
||||
hasOwn("maximumSignificantDigits", internals),
|
||||
"minimumSignificantDigits is present iff maximumSignificantDigits is present");
|
||||
|
||||
for (var i = 0; i < optionalProperties.length; i++) {
|
||||
var p = optionalProperties[i];
|
||||
if (callFunction(std_Object_hasOwnProperty, internals, p))
|
||||
_DefineDataProperty(result, p, internals[p]);
|
||||
if (hasOwn("minimumSignificantDigits", internals)) {
|
||||
_DefineDataProperty(result, "minimumSignificantDigits",
|
||||
internals.minimumSignificantDigits);
|
||||
_DefineDataProperty(result, "maximumSignificantDigits",
|
||||
internals.maximumSignificantDigits);
|
||||
}
|
||||
|
||||
// Step 6.
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ using mozilla::Range;
|
|||
using mozilla::RangedPtr;
|
||||
|
||||
using js::intl::CallICU;
|
||||
using js::intl::GetAvailableLocales;
|
||||
using js::intl::IcuLocale;
|
||||
using js::intl::INITIAL_CHAR_BUFFER_SIZE;
|
||||
using js::intl::StringsAreEqual;
|
||||
|
|
@ -108,8 +107,8 @@ RelativeTimeFormat(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!relativeTimeFormat)
|
||||
return false;
|
||||
|
||||
relativeTimeFormat->as<NativeObject>().setReservedSlot(RelativeTimeFormatObject::INTERNALS_SLOT, NullValue());
|
||||
relativeTimeFormat->as<NativeObject>().setReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
|
||||
relativeTimeFormat->as<RelativeTimeFormatObject>().setReservedSlot(RelativeTimeFormatObject::INTERNALS_SLOT, NullValue());
|
||||
relativeTimeFormat->as<RelativeTimeFormatObject>().setReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
|
||||
|
||||
RootedValue locales(cx, args.get(0));
|
||||
RootedValue options(cx, args.get(1));
|
||||
|
|
@ -127,15 +126,9 @@ js::RelativeTimeFormatObject::finalize(FreeOp* fop, JSObject* obj)
|
|||
{
|
||||
MOZ_ASSERT(fop->onMainThread());
|
||||
|
||||
// This is-undefined check shouldn't be necessary, but for internal
|
||||
// brokenness in object allocation code. For the moment, hack around it by
|
||||
// explicitly guarding against the possibility of the reserved slot not
|
||||
// containing a private. See bug 949220.
|
||||
const Value& slot = obj->as<RelativeTimeFormatObject>().getReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT);
|
||||
if (!slot.isUndefined()) {
|
||||
if (URelativeDateTimeFormatter* rtf = static_cast<URelativeDateTimeFormatter*>(slot.toPrivate()))
|
||||
ureldatefmt_close(rtf);
|
||||
}
|
||||
if (URelativeDateTimeFormatter* rtf = static_cast<URelativeDateTimeFormatter*>(slot.toPrivate()))
|
||||
ureldatefmt_close(rtf);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
|
|
@ -146,10 +139,9 @@ js::CreateRelativeTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<G
|
|||
if (!ctor)
|
||||
return nullptr;
|
||||
|
||||
RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &RelativeTimeFormatObject::class_));
|
||||
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
proto->setReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||
return nullptr;
|
||||
|
|
@ -163,16 +155,6 @@ js::CreateRelativeTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<G
|
|||
if (!JS_DefineProperties(cx, proto, relativeTimeFormat_properties))
|
||||
return nullptr;
|
||||
|
||||
RootedValue options(cx);
|
||||
if (!intl::CreateDefaultOptions(cx, &options))
|
||||
return nullptr;
|
||||
|
||||
if (!intl::InitializeObject(cx, proto, cx->names().InitializeRelativeTimeFormat, UndefinedHandleValue,
|
||||
options))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||
if (!DefineProperty(cx, Intl, cx->names().RelativeTimeFormat, ctorValue, nullptr, nullptr, 0)) {
|
||||
return nullptr;
|
||||
|
|
@ -181,22 +163,6 @@ js::CreateRelativeTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<G
|
|||
return proto;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_RelativeTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 0);
|
||||
|
||||
RootedValue result(cx);
|
||||
// We're going to use ULocale availableLocales as per ICU recommendation:
|
||||
// https://ssl.icu-project.org/trac/ticket/12756
|
||||
if (!GetAvailableLocales(cx, uloc_countAvailable, uloc_getAvailable, &result))
|
||||
return false;
|
||||
args.rval().set(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
enum class RelativeTimeNumeric
|
||||
{
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -39,17 +39,6 @@ extern JSObject*
|
|||
CreateRelativeTimeFormatPrototype(JSContext* cx, JS::Handle<JSObject*> Intl,
|
||||
JS::Handle<GlobalObject*> global);
|
||||
|
||||
/**
|
||||
* Returns an object indicating the supported locales for relative time format
|
||||
* by having a true-valued property for each such locale with the
|
||||
* canonicalized language tag as the property name. The object has no
|
||||
* prototype.
|
||||
*
|
||||
* Usage: availableLocales = intl_RelativeTimeFormat_availableLocales()
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_RelativeTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Returns a relative time as a string formatted according to the effective
|
||||
* locale and the formatting options of the given RelativeTimeFormat.
|
||||
|
|
|
|||
|
|
@ -11,17 +11,6 @@
|
|||
*/
|
||||
var relativeTimeFormatInternalProperties = {
|
||||
localeData: relativeTimeFormatLocaleData,
|
||||
_availableLocales: null,
|
||||
availableLocales: function() // eslint-disable-line object-shorthand
|
||||
{
|
||||
var locales = this._availableLocales;
|
||||
if (locales)
|
||||
return locales;
|
||||
|
||||
locales = intl_RelativeTimeFormat_availableLocales();
|
||||
addSpecialMissingLanguageTags(locales);
|
||||
return (this._availableLocales = locales);
|
||||
},
|
||||
relevantExtensionKeys: [],
|
||||
};
|
||||
|
||||
|
|
@ -41,7 +30,7 @@ function resolveRelativeTimeFormatInternals(lazyRelativeTimeFormatData) {
|
|||
var RelativeTimeFormat = relativeTimeFormatInternalProperties;
|
||||
|
||||
// Steps 7-8.
|
||||
const r = ResolveLocale(callFunction(RelativeTimeFormat.availableLocales, RelativeTimeFormat),
|
||||
const r = ResolveLocale("RelativeTimeFormat",
|
||||
lazyRelativeTimeFormatData.requestedLocales,
|
||||
lazyRelativeTimeFormatData.opt,
|
||||
RelativeTimeFormat.relevantExtensionKeys,
|
||||
|
|
@ -69,8 +58,11 @@ function resolveRelativeTimeFormatInternals(lazyRelativeTimeFormatData) {
|
|||
* Returns an object containing the RelativeTimeFormat internal properties of |obj|,
|
||||
* or throws a TypeError if |obj| isn't RelativeTimeFormat-initialized.
|
||||
*/
|
||||
function getRelativeTimeFormatInternals(obj, methodName) {
|
||||
var internals = getIntlObjectInternals(obj, "RelativeTimeFormat", methodName);
|
||||
function getRelativeTimeFormatInternals(obj) {
|
||||
assert(IsObject(obj), "getRelativeTimeFormatInternals called with non-object");
|
||||
assert(IsRelativeTimeFormat(obj), "getRelativeTimeFormatInternals called with non-RelativeTimeFormat");
|
||||
|
||||
var internals = getIntlObjectInternals(obj);
|
||||
assert(internals.type === "RelativeTimeFormat", "bad type escaped getIntlObjectInternals");
|
||||
|
||||
var internalProps = maybeInternalProperties(internals);
|
||||
|
|
@ -94,12 +86,8 @@ function getRelativeTimeFormatInternals(obj, methodName) {
|
|||
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.1.1.
|
||||
*/
|
||||
function InitializeRelativeTimeFormat(relativeTimeFormat, locales, options) {
|
||||
assert(IsObject(relativeTimeFormat), "InitializeRelativeTimeFormat");
|
||||
|
||||
if (isInitializedIntlObject(relativeTimeFormat))
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
|
||||
|
||||
let internals = initializeIntlObject(relativeTimeFormat);
|
||||
assert(IsObject(relativeTimeFormat), "InitializeRelativeTimeFormat called with non-object");
|
||||
assert(IsRelativeTimeFormat(relativeTimeFormat), "InitializeRelativeTimeFormat called with non-RelativeTimeFormat");
|
||||
|
||||
// Lazy RelativeTimeFormat data has the following structure:
|
||||
//
|
||||
|
|
@ -146,7 +134,7 @@ function InitializeRelativeTimeFormat(relativeTimeFormat, locales, options) {
|
|||
const numeric = GetOption(options, "numeric", "string", ["always", "auto"], "always");
|
||||
lazyRelativeTimeFormatData.numeric = numeric;
|
||||
|
||||
setLazyData(internals, "RelativeTimeFormat", lazyRelativeTimeFormatData)
|
||||
initializeIntlObject(relativeTimeFormat, "RelativeTimeFormat", lazyRelativeTimeFormatData)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -160,8 +148,8 @@ function Intl_RelativeTimeFormat_supportedLocalesOf(locales /*, options*/) {
|
|||
var options = arguments.length > 1 ? arguments[1] : undefined;
|
||||
|
||||
// Step 1.
|
||||
var availableLocales = callFunction(relativeTimeFormatInternalProperties.availableLocales,
|
||||
relativeTimeFormatInternalProperties);
|
||||
var availableLocales = "RelativeTimeFormat";
|
||||
|
||||
// Step 2.
|
||||
let requestedLocales = CanonicalizeLocaleList(locales);
|
||||
|
||||
|
|
@ -181,7 +169,11 @@ function Intl_RelativeTimeFormat_format(value, unit) {
|
|||
let relativeTimeFormat = this;
|
||||
|
||||
// Step 2.
|
||||
let internals = getRelativeTimeFormatInternals(relativeTimeFormat, "format");
|
||||
if (!IsObject(relativeTimeFormat) || !IsRelativeTimeFormat(relativeTimeFormat))
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "RelativeTimeFormat", "format", "RelativeTimeFormat");
|
||||
|
||||
// Ensure the RelativeTimeFormat internals are resolved.
|
||||
let internals = getRelativeTimeFormatInternals(relativeTimeFormat);
|
||||
|
||||
// Step 3.
|
||||
let t = ToNumber(value);
|
||||
|
|
@ -191,7 +183,7 @@ function Intl_RelativeTimeFormat_format(value, unit) {
|
|||
|
||||
// PartitionRelativeTimePattern, step 4.
|
||||
if (!Number_isFinite(t)) {
|
||||
ThrowRangeError(JSMSG_DATE_NOT_FINITE, "RelativeTimeFormat");
|
||||
ThrowRangeError(JSMSG_DATE_NOT_FINITE, "RelativeTimeFormat", "format");
|
||||
}
|
||||
|
||||
// PartitionRelativeTimePattern, step 5.
|
||||
|
|
@ -227,7 +219,13 @@ function Intl_RelativeTimeFormat_format(value, unit) {
|
|||
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.4.4.
|
||||
*/
|
||||
function Intl_RelativeTimeFormat_resolvedOptions() {
|
||||
var internals = getRelativeTimeFormatInternals(this, "resolvedOptions");
|
||||
// Check "this RelativeTimeFormat object" per introduction of section 1.4.
|
||||
if (!IsObject(this) || !IsRelativeTimeFormat(this)) {
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "RelativeTimeFormat", "resolvedOptions",
|
||||
"RelativeTimeFormat");
|
||||
}
|
||||
|
||||
var internals = getRelativeTimeFormatInternals(this);
|
||||
|
||||
// Steps 4-5.
|
||||
var result = {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/HashFunctions.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "jsatom.h"
|
||||
|
|
@ -21,6 +22,7 @@
|
|||
#include "builtin/intl/ScopedICUObject.h"
|
||||
#include "builtin/intl/TimeZoneDataGenerated.h"
|
||||
#include "js/Utility.h"
|
||||
#include "js/Vector.h"
|
||||
|
||||
using js::HashNumber;
|
||||
using js::intl::StringsAreEqual;
|
||||
|
|
@ -29,9 +31,7 @@ template<typename Char>
|
|||
static constexpr Char
|
||||
ToUpperASCII(Char c)
|
||||
{
|
||||
return ('a' <= c && c <= 'z')
|
||||
? (c & ~0x20)
|
||||
: c;
|
||||
return mozilla::IsAsciiLowercaseAlpha(c) ? (c - 0x20) : c;
|
||||
}
|
||||
|
||||
static_assert(ToUpperASCII('a') == 'A', "verifying 'a' uppercases correctly");
|
||||
|
|
@ -63,15 +63,12 @@ EqualCharsIgnoreCaseASCII(const Char1* s1, const Char2* s2, size_t len)
|
|||
}
|
||||
|
||||
js::intl::SharedIntlData::TimeZoneHasher::Lookup::Lookup(JSFlatString* timeZone)
|
||||
: isLatin1(timeZone->hasLatin1Chars()), length(timeZone->length())
|
||||
: js::intl::SharedIntlData::LinearStringLookup(timeZone)
|
||||
{
|
||||
if (isLatin1) {
|
||||
latin1Chars = timeZone->latin1Chars(nogc);
|
||||
if (isLatin1)
|
||||
hash = HashStringIgnoreCaseASCII(latin1Chars, length);
|
||||
} else {
|
||||
twoByteChars = timeZone->twoByteChars(nogc);
|
||||
else
|
||||
hash = HashStringIgnoreCaseASCII(twoByteChars, length);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -110,7 +107,7 @@ js::intl::SharedIntlData::ensureTimeZones(JSContext* cx)
|
|||
if (timeZoneDataInitialized)
|
||||
return true;
|
||||
|
||||
// If initTimeZones() was called previously, but didn't complete due to
|
||||
// If ensureTimeZones() was called previously, but didn't complete due to
|
||||
// OOM, clear all sets/maps and start from scratch.
|
||||
if (availableTimeZones.initialized())
|
||||
availableTimeZones.finish();
|
||||
|
|
@ -272,12 +269,307 @@ js::intl::SharedIntlData::tryCanonicalizeTimeZoneConsistentWithIANA(JSContext* c
|
|||
return true;
|
||||
}
|
||||
|
||||
js::intl::SharedIntlData::LocaleHasher::Lookup::Lookup(JSLinearString* locale)
|
||||
: js::intl::SharedIntlData::LinearStringLookup(locale)
|
||||
{
|
||||
if (isLatin1)
|
||||
hash = mozilla::HashString(latin1Chars, length);
|
||||
else
|
||||
hash = mozilla::HashString(twoByteChars, length);
|
||||
}
|
||||
|
||||
js::intl::SharedIntlData::LocaleHasher::Lookup::Lookup(const char* chars,
|
||||
size_t length)
|
||||
: js::intl::SharedIntlData::LinearStringLookup(chars, length)
|
||||
{
|
||||
hash = mozilla::HashString(latin1Chars, length);
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl::SharedIntlData::LocaleHasher::match(Locale key, const Lookup& lookup)
|
||||
{
|
||||
if (key->length() != lookup.length)
|
||||
return false;
|
||||
|
||||
if (key->hasLatin1Chars()) {
|
||||
const Latin1Char* keyChars = key->latin1Chars(lookup.nogc);
|
||||
if (lookup.isLatin1)
|
||||
return EqualChars(keyChars, lookup.latin1Chars, lookup.length);
|
||||
return EqualChars(keyChars, lookup.twoByteChars, lookup.length);
|
||||
}
|
||||
|
||||
const char16_t* keyChars = key->twoByteChars(lookup.nogc);
|
||||
if (lookup.isLatin1)
|
||||
return EqualChars(lookup.latin1Chars, keyChars, lookup.length);
|
||||
return EqualChars(keyChars, lookup.twoByteChars, lookup.length);
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl::SharedIntlData::getAvailableLocales(JSContext* cx, LocaleSet& locales,
|
||||
CountAvailable countAvailable,
|
||||
GetAvailable getAvailable)
|
||||
{
|
||||
auto addLocale = [cx, &locales](const char* locale, size_t length) {
|
||||
JSAtom* atom = Atomize(cx, locale, length);
|
||||
if (!atom)
|
||||
return false;
|
||||
|
||||
LocaleHasher::Lookup lookup(atom);
|
||||
LocaleSet::AddPtr p = locales.lookupForAdd(lookup);
|
||||
|
||||
// ICU shouldn't report any duplicate locales, but if it does, just
|
||||
// ignore the duplicated locale.
|
||||
if (!p && !locales.add(p, atom)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
js::Vector<char, 16> lang(cx);
|
||||
|
||||
int32_t count = countAvailable();
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
const char* locale = getAvailable(i);
|
||||
size_t length = strlen(locale);
|
||||
|
||||
lang.clear();
|
||||
if (!lang.append(locale, length))
|
||||
return false;
|
||||
|
||||
std::replace(lang.begin(), lang.end(), '_', '-');
|
||||
|
||||
if (!addLocale(lang.begin(), length))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add old-style language tags without script code for locales that in current
|
||||
// usage would include a script subtag. Also add an entry for the last-ditch
|
||||
// locale, in case ICU doesn't directly support it (but does support it
|
||||
// through fallback, e.g. supporting "en-GB" indirectly using "en" support).
|
||||
|
||||
// Certain old-style language tags lack a script code, but in current usage
|
||||
// they *would* include a script code. Map these over to modern forms.
|
||||
for (const auto& mapping : js::intl::oldStyleLanguageTagMappings) {
|
||||
const char* oldStyle = mapping.oldStyle;
|
||||
const char* modernStyle = mapping.modernStyle;
|
||||
|
||||
LocaleHasher::Lookup lookup(modernStyle, strlen(modernStyle));
|
||||
if (locales.has(lookup)) {
|
||||
if (!addLocale(oldStyle, strlen(oldStyle)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Also forcibly provide the last-ditch locale.
|
||||
{
|
||||
const char* lastDitch = intl::LastDitchLocale();
|
||||
MOZ_ASSERT(strcmp(lastDitch, "en-GB") == 0);
|
||||
|
||||
#ifdef DEBUG
|
||||
static constexpr char lastDitchParent[] = "en";
|
||||
|
||||
LocaleHasher::Lookup lookup(lastDitchParent, strlen(lastDitchParent));
|
||||
MOZ_ASSERT(locales.has(lookup),
|
||||
"shouldn't be a need to add every locale implied by the "
|
||||
"last-ditch locale, merely just the last-ditch locale");
|
||||
#endif
|
||||
|
||||
if (!addLocale(lastDitch, strlen(lastDitch)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
template <typename CountAvailable, typename GetAvailable>
|
||||
static bool
|
||||
IsSameAvailableLocales(CountAvailable countAvailable1,
|
||||
GetAvailable getAvailable1,
|
||||
CountAvailable countAvailable2,
|
||||
GetAvailable getAvailable2)
|
||||
{
|
||||
int32_t count = countAvailable1();
|
||||
if (count != countAvailable2()) {
|
||||
return false;
|
||||
}
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
if (getAvailable1(i) != getAvailable2(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
js::intl::SharedIntlData::ensureSupportedLocales(JSContext* cx)
|
||||
{
|
||||
if (supportedLocalesInitialized)
|
||||
return true;
|
||||
|
||||
// If ensureSupportedLocales() was called previously, but didn't complete due
|
||||
// to OOM, clear all data and start from scratch.
|
||||
if (supportedLocales.initialized())
|
||||
supportedLocales.finish();
|
||||
if (collatorSupportedLocales.initialized())
|
||||
collatorSupportedLocales.finish();
|
||||
if (!supportedLocales.init() ||
|
||||
!collatorSupportedLocales.init()) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!getAvailableLocales(cx, supportedLocales, uloc_countAvailable, uloc_getAvailable))
|
||||
return false;
|
||||
|
||||
if (!getAvailableLocales(cx, collatorSupportedLocales, ucol_countAvailable, ucol_getAvailable))
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(IsSameAvailableLocales(uloc_countAvailable, uloc_getAvailable,
|
||||
udat_countAvailable, udat_getAvailable));
|
||||
|
||||
MOZ_ASSERT(IsSameAvailableLocales(uloc_countAvailable, uloc_getAvailable,
|
||||
unum_countAvailable, unum_getAvailable));
|
||||
|
||||
MOZ_ASSERT(!supportedLocalesInitialized, "ensureSupportedLocales is neither reentrant nor thread-safe");
|
||||
supportedLocalesInitialized = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl::SharedIntlData::isSupportedLocale(JSContext* cx,
|
||||
SupportedLocaleKind kind,
|
||||
HandleString locale,
|
||||
bool* supported)
|
||||
{
|
||||
if (!ensureSupportedLocales(cx))
|
||||
return false;
|
||||
|
||||
|
||||
RootedLinearString localeLinear(cx, locale->ensureLinear(cx));
|
||||
if (!localeLinear)
|
||||
return false;
|
||||
|
||||
LocaleHasher::Lookup lookup(localeLinear);
|
||||
switch (kind) {
|
||||
case SupportedLocaleKind::Collator:
|
||||
*supported = collatorSupportedLocales.has(lookup);
|
||||
return true;
|
||||
case SupportedLocaleKind::DateTimeFormat:
|
||||
case SupportedLocaleKind::NumberFormat:
|
||||
case SupportedLocaleKind::PluralRules:
|
||||
case SupportedLocaleKind::RelativeTimeFormat:
|
||||
*supported = supportedLocales.has(lookup);
|
||||
return true;
|
||||
}
|
||||
MOZ_CRASH("Invalid Intl constructor");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl::SharedIntlData::ensureUpperCaseFirstLocales(JSContext* cx)
|
||||
{
|
||||
if (upperCaseFirstInitialized)
|
||||
return true;
|
||||
|
||||
// If ensureUpperCaseFirstLocales() was called previously, but didn't
|
||||
// complete due to OOM, clear all data and start from scratch.
|
||||
if (upperCaseFirstLocales.initialized())
|
||||
upperCaseFirstLocales.finish();
|
||||
if (!upperCaseFirstLocales.init()) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UEnumeration* available = ucol_openAvailableLocales(&status);
|
||||
if (U_FAILURE(status)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||
return false;
|
||||
}
|
||||
ScopedICUObject<UEnumeration, uenum_close> toClose(available);
|
||||
|
||||
RootedAtom locale(cx);
|
||||
while (true) {
|
||||
int32_t size;
|
||||
const char* rawLocale = uenum_next(available, &size, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rawLocale == nullptr)
|
||||
break;
|
||||
|
||||
UCollator* collator = ucol_open(rawLocale, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||
return false;
|
||||
}
|
||||
ScopedICUObject<UCollator, ucol_close> toCloseCollator(collator);
|
||||
|
||||
UColAttributeValue caseFirst = ucol_getAttribute(collator, UCOL_CASE_FIRST, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (caseFirst != UCOL_UPPER_FIRST)
|
||||
continue;
|
||||
|
||||
MOZ_ASSERT(size >= 0);
|
||||
locale = Atomize(cx, rawLocale, size_t(size));
|
||||
if (!locale)
|
||||
return false;
|
||||
|
||||
LocaleHasher::Lookup lookup(locale);
|
||||
LocaleSet::AddPtr p = upperCaseFirstLocales.lookupForAdd(lookup);
|
||||
|
||||
// ICU shouldn't report any duplicate locales, but if it does, just
|
||||
// ignore the duplicated locale.
|
||||
if (!p && !upperCaseFirstLocales.add(p, locale)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!upperCaseFirstInitialized,
|
||||
"ensureUpperCaseFirstLocales is neither reentrant nor thread-safe");
|
||||
upperCaseFirstInitialized = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl::SharedIntlData::isUpperCaseFirst(JSContext* cx, HandleString locale, bool* isUpperFirst)
|
||||
{
|
||||
if (!ensureUpperCaseFirstLocales(cx))
|
||||
return false;
|
||||
|
||||
RootedLinearString localeLinear(cx, locale->ensureLinear(cx));
|
||||
if (!localeLinear)
|
||||
return false;
|
||||
|
||||
LocaleHasher::Lookup lookup(localeLinear);
|
||||
*isUpperFirst = upperCaseFirstLocales.has(lookup);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
js::intl::SharedIntlData::destroyInstance()
|
||||
{
|
||||
availableTimeZones.finish();
|
||||
ianaZonesTreatedAsLinksByICU.finish();
|
||||
ianaLinksCanonicalizedDifferentlyByICU.finish();
|
||||
supportedLocales.finish();
|
||||
collatorSupportedLocales.finish();
|
||||
upperCaseFirstLocales.finish();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -288,6 +580,9 @@ js::intl::SharedIntlData::trace(JSTracer* trc)
|
|||
availableTimeZones.trace(trc);
|
||||
ianaZonesTreatedAsLinksByICU.trace(trc);
|
||||
ianaLinksCanonicalizedDifferentlyByICU.trace(trc);
|
||||
supportedLocales.trace(trc);
|
||||
collatorSupportedLocales.trace(trc);
|
||||
upperCaseFirstLocales.trace(trc);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -296,5 +591,8 @@ js::intl::SharedIntlData::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
|
|||
{
|
||||
return availableTimeZones.sizeOfExcludingThis(mallocSizeOf) +
|
||||
ianaZonesTreatedAsLinksByICU.sizeOfExcludingThis(mallocSizeOf) +
|
||||
ianaLinksCanonicalizedDifferentlyByICU.sizeOfExcludingThis(mallocSizeOf);
|
||||
ianaLinksCanonicalizedDifferentlyByICU.sizeOfExcludingThis(mallocSizeOf) +
|
||||
supportedLocales.sizeOfExcludingThis(mallocSizeOf) +
|
||||
collatorSupportedLocales.sizeOfExcludingThis(mallocSizeOf) +
|
||||
upperCaseFirstLocales.sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,34 @@ namespace intl {
|
|||
*/
|
||||
class SharedIntlData
|
||||
{
|
||||
struct LinearStringLookup
|
||||
{
|
||||
union {
|
||||
const JS::Latin1Char* latin1Chars;
|
||||
const char16_t* twoByteChars;
|
||||
};
|
||||
bool isLatin1;
|
||||
size_t length;
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
HashNumber hash = 0;
|
||||
|
||||
explicit LinearStringLookup(JSLinearString* string)
|
||||
: isLatin1(string->hasLatin1Chars()), length(string->length())
|
||||
{
|
||||
if (isLatin1)
|
||||
latin1Chars = string->latin1Chars(nogc);
|
||||
else
|
||||
twoByteChars = string->twoByteChars(nogc);
|
||||
}
|
||||
|
||||
LinearStringLookup(const char* chars, size_t length)
|
||||
: isLatin1(true), length(length)
|
||||
{
|
||||
latin1Chars = reinterpret_cast<const JS::Latin1Char*>(chars);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
/**
|
||||
* Information tracking the set of the supported time zone names, derived
|
||||
* from the IANA time zone database <https://www.iana.org/time-zones>.
|
||||
|
|
@ -59,17 +87,8 @@ class SharedIntlData
|
|||
|
||||
struct TimeZoneHasher
|
||||
{
|
||||
struct Lookup
|
||||
struct Lookup : LinearStringLookup
|
||||
{
|
||||
union {
|
||||
const JS::Latin1Char* latin1Chars;
|
||||
const char16_t* twoByteChars;
|
||||
};
|
||||
bool isLatin1;
|
||||
size_t length;
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
HashNumber hash;
|
||||
|
||||
explicit Lookup(JSFlatString* timeZone);
|
||||
};
|
||||
|
||||
|
|
@ -148,7 +167,110 @@ class SharedIntlData
|
|||
*/
|
||||
bool tryCanonicalizeTimeZoneConsistentWithIANA(JSContext* cx, JS::HandleString timeZone,
|
||||
JS::MutableHandleString result);
|
||||
private:
|
||||
using Locale = JSAtom*;
|
||||
|
||||
struct LocaleHasher
|
||||
{
|
||||
struct Lookup : LinearStringLookup
|
||||
{
|
||||
explicit Lookup(JSLinearString* locale);
|
||||
Lookup(const char* chars, size_t length);
|
||||
};
|
||||
|
||||
static js::HashNumber hash(const Lookup& lookup) { return lookup.hash; }
|
||||
static bool match(Locale key, const Lookup& lookup);
|
||||
};
|
||||
|
||||
using LocaleSet = GCHashSet<Locale, LocaleHasher, SystemAllocPolicy>;
|
||||
|
||||
// Set of supported locales for all Intl service constructors except Collator,
|
||||
// which uses its own set.
|
||||
//
|
||||
// UDateFormat:
|
||||
// udat_[count,get]Available() return the same results as their
|
||||
// uloc_[count,get]Available() counterparts.
|
||||
//
|
||||
// UNumberFormatter:
|
||||
// unum_[count,get]Available() return the same results as their
|
||||
// uloc_[count,get]Available() counterparts.
|
||||
//
|
||||
// UPluralRules and URelativeDateTimeFormatter:
|
||||
// We're going to use ULocale availableLocales as per ICU recommendation:
|
||||
// https://unicode-org.atlassian.net/browse/ICU-12756
|
||||
LocaleSet supportedLocales;
|
||||
|
||||
// ucol_[count,get]Available() return different results compared to
|
||||
// uloc_[count,get]Available(), we can't use |supportedLocales| here.
|
||||
LocaleSet collatorSupportedLocales;
|
||||
|
||||
bool supportedLocalesInitialized = false;
|
||||
|
||||
// CountAvailable and GetAvailable describe the signatures used for ICU API
|
||||
// to determine available locales for various functionality.
|
||||
using CountAvailable = int32_t (*)();
|
||||
using GetAvailable = const char* (*)(int32_t localeIndex);
|
||||
|
||||
static bool getAvailableLocales(JSContext* cx, LocaleSet& locales,
|
||||
CountAvailable countAvailable,
|
||||
GetAvailable getAvailable);
|
||||
|
||||
/**
|
||||
* Precomputes the available locales sets.
|
||||
*/
|
||||
bool ensureSupportedLocales(JSContext* cx);
|
||||
|
||||
public:
|
||||
enum class SupportedLocaleKind {
|
||||
Collator,
|
||||
DateTimeFormat,
|
||||
NumberFormat,
|
||||
PluralRules,
|
||||
RelativeTimeFormat
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets |supported| to true if |locale| is supported by the requested Intl
|
||||
* service constructor. Otherwise sets |supported| to false.
|
||||
*/
|
||||
MOZ_MUST_USE bool isSupportedLocale(JSContext* cx, SupportedLocaleKind kind,
|
||||
JS::Handle<JSString*> locale,
|
||||
bool* supported);
|
||||
|
||||
private:
|
||||
/**
|
||||
* The case first parameter (BCP47 key "kf") allows to switch the order of
|
||||
* upper- and lower-case characters. ICU doesn't directly provide an API
|
||||
* to query the default case first value of a given locale, but instead
|
||||
* requires to instantiate a collator object and then query the case first
|
||||
* attribute (UCOL_CASE_FIRST).
|
||||
* To avoid instantiating an additional collator object whenever we need
|
||||
* to retrieve the default case first value of a specific locale, we
|
||||
* compute the default case first value for every supported locale only
|
||||
* once and then keep a list of all locales which don't use the default
|
||||
* case first setting.
|
||||
* There is almost no difference between lower-case first and when case
|
||||
* first is disabled (UCOL_LOWER_FIRST resp. UCOL_OFF), so we only need to
|
||||
* track locales which use upper-case first as their default setting.
|
||||
*/
|
||||
|
||||
LocaleSet upperCaseFirstLocales;
|
||||
|
||||
bool upperCaseFirstInitialized = false;
|
||||
|
||||
/**
|
||||
* Precomputes the available locales which use upper-case first sorting.
|
||||
*/
|
||||
bool ensureUpperCaseFirstLocales(JSContext* cx);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Sets |isUpperFirst| to true if |locale| sorts upper-case characters
|
||||
* before lower-case characters.
|
||||
*/
|
||||
bool isUpperCaseFirst(JSContext* cx, JS::HandleString locale, bool* isUpperFirst);
|
||||
|
||||
public:
|
||||
void destroyInstance();
|
||||
|
||||
void trace(JSTracer* trc);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -254,7 +254,7 @@ GCRuntime::checkIncrementalZoneState(ExclusiveContext* cx, T* t)
|
|||
return;
|
||||
|
||||
Zone* zone = cx->asJSContext()->zone();
|
||||
MOZ_ASSERT_IF(t && zone->wasGCStarted() && (zone->isGCMarking() || zone->isGCSweeping()),
|
||||
MOZ_ASSERT_IF(t && zone->wasGCStarted() && (zone->shouldMarkInZone() || zone->isGCSweeping()),
|
||||
t->asTenured().arena()->allocatedDuringIncremental);
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,12 @@ class MarkingValidator;
|
|||
class AutoTraceSession;
|
||||
struct MovingTracer;
|
||||
|
||||
enum IncrementalProgress
|
||||
{
|
||||
NotFinished = 0,
|
||||
Finished
|
||||
};
|
||||
|
||||
class ChunkPool
|
||||
{
|
||||
Chunk* head_;
|
||||
|
|
@ -737,6 +743,8 @@ class GCRuntime
|
|||
|
||||
bool isShrinkingGC() const { return invocationKind == GC_SHRINK; }
|
||||
|
||||
static bool initializeSweepActions();
|
||||
|
||||
void setGrayRootsTracer(JSTraceDataOp traceOp, void* data);
|
||||
MOZ_MUST_USE bool addBlackRootsTracer(JSTraceDataOp traceOp, void* data);
|
||||
void removeBlackRootsTracer(JSTraceDataOp traceOp, void* data);
|
||||
|
|
@ -863,12 +871,6 @@ class GCRuntime
|
|||
static TenuredCell* refillFreeListInGC(Zone* zone, AllocKind thingKind);
|
||||
|
||||
private:
|
||||
enum IncrementalProgress
|
||||
{
|
||||
NotFinished = 0,
|
||||
Finished
|
||||
};
|
||||
|
||||
// For ArenaLists::allocateFromArena()
|
||||
friend class ArenaLists;
|
||||
Chunk* pickChunk(const AutoLockGC& lock,
|
||||
|
|
@ -951,7 +953,15 @@ class GCRuntime
|
|||
void beginSweepingZoneGroup(AutoLockForExclusiveAccess& lock);
|
||||
bool shouldReleaseObservedTypes();
|
||||
void endSweepingZoneGroup();
|
||||
IncrementalProgress sweepPhase(SliceBudget& sliceBudget, AutoLockForExclusiveAccess& lock);
|
||||
IncrementalProgress performSweepActions(SliceBudget& sliceBudget, AutoLockForExclusiveAccess& lock);
|
||||
static IncrementalProgress sweepTypeInformation(GCRuntime* gc, FreeOp* fop, Zone* zone,
|
||||
SliceBudget& budget, AllocKind kind);
|
||||
static IncrementalProgress mergeSweptObjectArenas(GCRuntime* gc, FreeOp* fop, Zone* zone,
|
||||
SliceBudget& budget, AllocKind kind);
|
||||
static IncrementalProgress finalizeAllocKind(GCRuntime* gc, FreeOp* fop, Zone* zone,
|
||||
SliceBudget& budget, AllocKind kind);
|
||||
static IncrementalProgress sweepShapeTree(GCRuntime* gc, FreeOp* fop, Zone* zone,
|
||||
SliceBudget& budget, AllocKind kind);
|
||||
void endSweepPhase(bool lastGC, AutoLockForExclusiveAccess& lock);
|
||||
void sweepZones(FreeOp* fop, bool lastGC);
|
||||
void decommitAllWithoutUnlocking(const AutoLockGC& lock);
|
||||
|
|
@ -1165,10 +1175,9 @@ class GCRuntime
|
|||
*/
|
||||
JS::Zone* zoneGroups;
|
||||
JS::Zone* currentZoneGroup;
|
||||
bool sweepingTypes;
|
||||
unsigned finalizePhase;
|
||||
size_t sweepPhaseIndex;
|
||||
JS::Zone* sweepZone;
|
||||
AllocKind sweepKind;
|
||||
size_t sweepActionIndex;
|
||||
bool abortSweepAfterCurrentGroup;
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ ShouldMarkCrossCompartment(JSTracer* trc, JSObject* src, Cell* cell)
|
|||
MOZ_ASSERT(!zone->isCollecting());
|
||||
trc->runtime()->gc.setFoundBlackGrayEdges(tenured);
|
||||
}
|
||||
return zone->isGCMarking();
|
||||
return zone->shouldMarkInZone();
|
||||
} else {
|
||||
if (zone->isGCMarkingBlack()) {
|
||||
/*
|
||||
|
|
@ -331,26 +331,26 @@ ShouldMarkCrossCompartment(JSTracer* trc, JSObject* src, const Value& val)
|
|||
}
|
||||
|
||||
static void
|
||||
AssertZoneIsMarking(Cell* thing)
|
||||
AssertShouldMarkInZone(Cell* thing)
|
||||
{
|
||||
MOZ_ASSERT(TenuredCell::fromPointer(thing)->zone()->isGCMarking());
|
||||
MOZ_ASSERT(thing->asTenured().zone()->shouldMarkInZone());
|
||||
}
|
||||
|
||||
static void
|
||||
AssertZoneIsMarking(JSString* str)
|
||||
AssertShouldMarkInZone(JSString* str)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Zone* zone = TenuredCell::fromPointer(str)->zone();
|
||||
MOZ_ASSERT(zone->isGCMarking() || zone->isAtomsZone());
|
||||
Zone* zone = str->asTenured().zone();
|
||||
MOZ_ASSERT(zone->shouldMarkInZone() || zone->isAtomsZone());
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
AssertZoneIsMarking(JS::Symbol* sym)
|
||||
AssertShouldMarkInZone(JS::Symbol* sym)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Zone* zone = TenuredCell::fromPointer(sym)->zone();
|
||||
MOZ_ASSERT(zone->isGCMarking() || zone->isAtomsZone());
|
||||
Zone* zone = sym->asTenured().zone();
|
||||
MOZ_ASSERT(zone->shouldMarkInZone() || zone->isAtomsZone());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -730,7 +730,7 @@ GCMarker::markImplicitEdgesHelper(T markedThing)
|
|||
return;
|
||||
|
||||
Zone* zone = gc::TenuredCell::fromPointer(markedThing)->zone();
|
||||
MOZ_ASSERT(zone->isGCMarking());
|
||||
MOZ_ASSERT(zone->shouldMarkInZone());
|
||||
MOZ_ASSERT(!zone->isGCSweeping());
|
||||
|
||||
auto p = zone->gcWeakKeys.get(JS::GCCellPtr(markedThing));
|
||||
|
|
@ -759,35 +759,35 @@ GCMarker::markImplicitEdges(T* thing)
|
|||
|
||||
template <typename T>
|
||||
static inline bool
|
||||
MustSkipMarking(GCMarker* gcmarker, T thing)
|
||||
ShouldMark(GCMarker* gcmarker, T thing)
|
||||
{
|
||||
// Don't trace things that are owned by another runtime.
|
||||
if (IsOwnedByOtherRuntime(gcmarker->runtime(), thing))
|
||||
return true;
|
||||
return false;
|
||||
|
||||
// Don't mark things outside a zone if we are in a per-zone GC.
|
||||
return !thing->zone()->isGCMarking();
|
||||
return thing->zone()->shouldMarkInZone();
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
MustSkipMarking<JSObject*>(GCMarker* gcmarker, JSObject* obj)
|
||||
ShouldMark<JSObject*>(GCMarker* gcmarker, JSObject* obj)
|
||||
{
|
||||
// Don't trace things that are owned by another runtime.
|
||||
if (IsOwnedByOtherRuntime(gcmarker->runtime(), obj))
|
||||
return true;
|
||||
return false;
|
||||
|
||||
// We may mark a Nursery thing outside the context of the
|
||||
// MinorCollectionTracer because of a pre-barrier. The pre-barrier is not
|
||||
// needed in this case because we perform a minor collection before each
|
||||
// incremental slice.
|
||||
if (IsInsideNursery(obj))
|
||||
return true;
|
||||
return false;
|
||||
|
||||
// Don't mark things outside a zone if we are in a per-zone GC. It is
|
||||
// faster to check our own arena, which we can do since we know that
|
||||
// the object is tenured.
|
||||
return !TenuredCell::fromPointer(obj)->zone()->isGCMarking();
|
||||
return obj->asTenured().zone()->shouldMarkInZone();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
|
@ -795,7 +795,7 @@ void
|
|||
DoMarking(GCMarker* gcmarker, T* thing)
|
||||
{
|
||||
// Do per-type marking precondition checks.
|
||||
if (MustSkipMarking(gcmarker, thing))
|
||||
if (!ShouldMark(gcmarker, thing))
|
||||
return;
|
||||
|
||||
CheckTracedThing(gcmarker, thing);
|
||||
|
|
@ -822,7 +822,7 @@ void
|
|||
NoteWeakEdge(GCMarker* gcmarker, T** thingp)
|
||||
{
|
||||
// Do per-type marking precondition checks.
|
||||
if (MustSkipMarking(gcmarker, *thingp))
|
||||
if (!ShouldMark(gcmarker, *thingp))
|
||||
return;
|
||||
|
||||
CheckTracedThing(gcmarker, *thingp);
|
||||
|
|
@ -971,7 +971,7 @@ template <typename T>
|
|||
bool
|
||||
js::GCMarker::mark(T* thing)
|
||||
{
|
||||
AssertZoneIsMarking(thing);
|
||||
AssertShouldMarkInZone(thing);
|
||||
MOZ_ASSERT(!IsInsideNursery(gc::TenuredCell::fromPointer(thing)));
|
||||
return gc::ParticipatesInCC<T>::value
|
||||
? gc::TenuredCell::fromPointer(thing)->markIfUnmarked(markColor())
|
||||
|
|
@ -1106,7 +1106,7 @@ JSString::traceBase(JSTracer* trc)
|
|||
inline void
|
||||
js::GCMarker::eagerlyMarkChildren(JSLinearString* linearStr)
|
||||
{
|
||||
AssertZoneIsMarking(linearStr);
|
||||
AssertShouldMarkInZone(linearStr);
|
||||
MOZ_ASSERT(linearStr->isMarked());
|
||||
MOZ_ASSERT(linearStr->JSString::isLinear());
|
||||
|
||||
|
|
@ -1116,7 +1116,7 @@ js::GCMarker::eagerlyMarkChildren(JSLinearString* linearStr)
|
|||
MOZ_ASSERT(linearStr->JSString::isLinear());
|
||||
if (linearStr->isPermanentAtom())
|
||||
break;
|
||||
AssertZoneIsMarking(linearStr);
|
||||
AssertShouldMarkInZone(linearStr);
|
||||
if (!mark(static_cast<JSString*>(linearStr)))
|
||||
break;
|
||||
}
|
||||
|
|
@ -1169,7 +1169,7 @@ js::GCMarker::eagerlyMarkChildren(JSRope* rope)
|
|||
|
||||
JS_DIAGNOSTICS_ASSERT(rope->getTraceKind() == JS::TraceKind::String);
|
||||
JS_DIAGNOSTICS_ASSERT(rope->JSString::isRope());
|
||||
AssertZoneIsMarking(rope);
|
||||
AssertShouldMarkInZone(rope);
|
||||
MOZ_ASSERT(rope->isMarked());
|
||||
JSRope* next = nullptr;
|
||||
|
||||
|
|
@ -1649,7 +1649,7 @@ GCMarker::processMarkStackTop(SliceBudget& budget)
|
|||
|
||||
case ObjectTag: {
|
||||
obj = reinterpret_cast<JSObject*>(addr);
|
||||
AssertZoneIsMarking(obj);
|
||||
AssertShouldMarkInZone(obj);
|
||||
goto scan_obj;
|
||||
}
|
||||
|
||||
|
|
@ -1712,7 +1712,7 @@ GCMarker::processMarkStackTop(SliceBudget& budget)
|
|||
|
||||
scan_obj:
|
||||
{
|
||||
AssertZoneIsMarking(obj);
|
||||
AssertShouldMarkInZone(obj);
|
||||
|
||||
budget.step();
|
||||
if (budget.isOverBudget()) {
|
||||
|
|
|
|||
|
|
@ -232,11 +232,9 @@ struct Zone : public JS::shadow::Zone,
|
|||
return rt->isHeapMajorCollecting() && !rt->gc.isHeapCompacting() && gcState_ != NoGC;
|
||||
}
|
||||
|
||||
bool isGCMarking() {
|
||||
if (runtimeFromMainThread()->isHeapCollecting())
|
||||
return gcState_ == Mark || gcState_ == MarkGray;
|
||||
else
|
||||
return needsIncrementalBarrier();
|
||||
bool shouldMarkInZone() const {
|
||||
return needsIncrementalBarrier() ||
|
||||
(gcState_ == Mark || gcState_ == MarkGray);
|
||||
}
|
||||
|
||||
GCState gcState() const { return gcState_; }
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ evalcx(`
|
|||
');
|
||||
oomTest(() => eval('Array(..."")'));
|
||||
if ('Intl' in this)
|
||||
Intl.NumberFormat.prototype.format(0);
|
||||
new Intl.NumberFormat().format(0);
|
||||
`, newGlobal());
|
||||
|
|
|
|||
|
|
@ -4,15 +4,8 @@ load(libdir + "asserts.js");
|
|||
// any value except "best fit" or "lookup" is okay.
|
||||
Object.prototype.localeMatcher = "invalid matcher option";
|
||||
|
||||
// The Intl API may not be available in the testing environment. Note that |hasOwnProperty("Intl")|
|
||||
// initializes the Intl API if present, so this if-statement needs to appear after "localeMatcher"
|
||||
// was added to Object.prototype.
|
||||
// The Intl API may not be available in the testing environment.
|
||||
if (this.hasOwnProperty("Intl")) {
|
||||
// Intl prototypes are properly initialized despite changed Object.prototype.
|
||||
Intl.Collator.prototype.compare("a", "b");
|
||||
Intl.NumberFormat.prototype.format(10);
|
||||
Intl.DateTimeFormat.prototype.format(new Date);
|
||||
|
||||
// Intl constructors no longer work properly, because "localeMatcher" defaults to the invalid
|
||||
// value from Object.prototype. Except for Intl.DateTimeFormat, cf. ECMA-402 ToDateTimeOptions.
|
||||
assertThrowsInstanceOf(() => new Intl.Collator(), RangeError);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ for (var i = 0; i < 3; i++) {
|
|||
x.toString();
|
||||
assertEq(0, 1);
|
||||
} catch (e) {
|
||||
assertEq(e.message, "y is undefined");
|
||||
assertEq(e.message === "y is undefined" ||
|
||||
e.message === "undefined has no properties", true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,12 @@
|
|||
_(AtomicsXor) \
|
||||
_(AtomicsIsLockFree) \
|
||||
\
|
||||
_(IntlIsCollator) \
|
||||
_(IntlIsDateTimeFormat) \
|
||||
_(IntlIsNumberFormat) \
|
||||
_(IntlIsPluralRules) \
|
||||
_(IntlIsRelativeTimeFormat) \
|
||||
\
|
||||
_(MathAbs) \
|
||||
_(MathFloor) \
|
||||
_(MathCeil) \
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@
|
|||
#include "builtin/SIMD.h"
|
||||
#include "builtin/TestingFunctions.h"
|
||||
#include "builtin/TypedObject.h"
|
||||
#include "builtin/intl/Collator.h"
|
||||
#include "builtin/intl/DateTimeFormat.h"
|
||||
#include "builtin/intl/NumberFormat.h"
|
||||
#include "builtin/intl/PluralRules.h"
|
||||
#include "builtin/intl/RelativeTimeFormat.h"
|
||||
#include "jit/BaselineInspector.h"
|
||||
#include "jit/InlinableNatives.h"
|
||||
#include "jit/IonBuilder.h"
|
||||
|
|
@ -104,6 +109,18 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
|
|||
case InlinableNative::AtomicsIsLockFree:
|
||||
return inlineAtomicsIsLockFree(callInfo);
|
||||
|
||||
// Intl natives.
|
||||
case InlinableNative::IntlIsCollator:
|
||||
return inlineHasClass(callInfo, &CollatorObject::class_);
|
||||
case InlinableNative::IntlIsDateTimeFormat:
|
||||
return inlineHasClass(callInfo, &DateTimeFormatObject::class_);
|
||||
case InlinableNative::IntlIsNumberFormat:
|
||||
return inlineHasClass(callInfo, &NumberFormatObject::class_);
|
||||
case InlinableNative::IntlIsPluralRules:
|
||||
return inlineHasClass(callInfo, &PluralRulesObject::class_);
|
||||
case InlinableNative::IntlIsRelativeTimeFormat:
|
||||
return inlineHasClass(callInfo, &RelativeTimeFormatObject::class_);
|
||||
|
||||
// Math natives.
|
||||
case InlinableNative::MathAbs:
|
||||
return inlineMathAbs(callInfo);
|
||||
|
|
|
|||
|
|
@ -484,10 +484,10 @@ MSG_DEF(JSMSG_DEBUG_PROMISE_NOT_REJECTED, 0, JSEXN_TYPEERR, "Promise hasn't been
|
|||
MSG_DEF(JSMSG_TRACELOGGER_ENABLE_FAIL, 1, JSEXN_ERR, "enabling tracelogger failed: {0}")
|
||||
|
||||
// Intl
|
||||
MSG_DEF(JSMSG_DATE_NOT_FINITE, 0, JSEXN_RANGEERR, "date value is not finite in DateTimeFormat.format()")
|
||||
MSG_DEF(JSMSG_DATE_NOT_FINITE, 2, JSEXN_RANGEERR, "date value is not finite in {0}.{1}()")
|
||||
MSG_DEF(JSMSG_DUPLICATE_VARIANT_SUBTAG, 1, JSEXN_RANGEERR, "duplicate variant subtag: {0}")
|
||||
MSG_DEF(JSMSG_INTERNAL_INTL_ERROR, 0, JSEXN_ERR, "internal error while computing Intl data")
|
||||
MSG_DEF(JSMSG_INTL_OBJECT_NOT_INITED, 3, JSEXN_TYPEERR, "Intl.{0}.prototype.{1} called on value that's not an object initialized as a {2}")
|
||||
MSG_DEF(JSMSG_INTL_OBJECT_REINITED, 0, JSEXN_TYPEERR, "can't initialize object twice as an object of an Intl constructor")
|
||||
MSG_DEF(JSMSG_INVALID_CURRENCY_CODE, 1, JSEXN_RANGEERR, "invalid currency code in NumberFormat(): {0}")
|
||||
MSG_DEF(JSMSG_INVALID_DIGITS_VALUE, 1, JSEXN_RANGEERR, "invalid digits value: {0}")
|
||||
MSG_DEF(JSMSG_INVALID_KEYS_TYPE, 0, JSEXN_TYPEERR, "calendar info keys must be an object or undefined")
|
||||
|
|
@ -497,6 +497,8 @@ MSG_DEF(JSMSG_INVALID_LOCALES_ELEMENT, 0, JSEXN_TYPEERR, "invalid element in loc
|
|||
MSG_DEF(JSMSG_INVALID_LOCALE_MATCHER, 1, JSEXN_RANGEERR, "invalid locale matcher in supportedLocalesOf(): {0}")
|
||||
MSG_DEF(JSMSG_INVALID_OPTION_VALUE, 2, JSEXN_RANGEERR, "invalid value {1} for option {0}")
|
||||
MSG_DEF(JSMSG_INVALID_TIME_ZONE, 1, JSEXN_RANGEERR, "invalid time zone in DateTimeFormat(): {0}")
|
||||
MSG_DEF(JSMSG_INVALID_DATETIME_OPTION, 2, JSEXN_TYPEERR, "can't set option {0} when {1} is used")
|
||||
MSG_DEF(JSMSG_INVALID_DATETIME_STYLE, 2, JSEXN_TYPEERR, "can't set option {0} in Date.{1}()")
|
||||
MSG_DEF(JSMSG_UNDEFINED_CURRENCY, 0, JSEXN_TYPEERR, "undefined currency in NumberFormat() with currency style")
|
||||
|
||||
// RegExp
|
||||
|
|
|
|||
|
|
@ -5327,8 +5327,8 @@ JS_ResetDefaultLocale(JSContext* cx);
|
|||
* Locale specific string conversion and error message callbacks.
|
||||
*/
|
||||
struct JSLocaleCallbacks {
|
||||
JSLocaleToUpperCase localeToUpperCase;
|
||||
JSLocaleToLowerCase localeToLowerCase;
|
||||
JSLocaleToUpperCase localeToUpperCase; // not used
|
||||
JSLocaleToLowerCase localeToLowerCase; // not used
|
||||
JSLocaleCompare localeCompare; // not used
|
||||
JSLocaleToUnicode localeToUnicode;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -245,6 +245,13 @@ js::ReportOutOfMemory(ExclusiveContext* cxArg)
|
|||
cx->setPendingException(oomMessage, nullptr);
|
||||
}
|
||||
|
||||
mozilla::GenericErrorResult<OOM&>
|
||||
js::ReportOutOfMemoryResult(ExclusiveContext* cx)
|
||||
{
|
||||
ReportOutOfMemory(cx);
|
||||
return cx->alreadyReportedOOM();
|
||||
}
|
||||
|
||||
void
|
||||
js::ReportOverRecursed(JSContext* maybecx, unsigned errorNumber)
|
||||
{
|
||||
|
|
@ -1009,6 +1016,34 @@ ExclusiveContext::recoverFromOutOfMemory()
|
|||
task->outOfMemory = false;
|
||||
}
|
||||
|
||||
JS::Error ExclusiveContext::reportedError;
|
||||
JS::OOM ExclusiveContext::reportedOOM;
|
||||
|
||||
mozilla::GenericErrorResult<OOM&>
|
||||
ExclusiveContext::alreadyReportedOOM()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (JSContext* maybecx = maybeJSContext()) {
|
||||
MOZ_ASSERT(maybecx->isThrowingOutOfMemory());
|
||||
} else {
|
||||
// Keep in sync with addPendingOutOfMemory.
|
||||
if (ParseTask* task = helperThread()->parseTask())
|
||||
MOZ_ASSERT(task->outOfMemory);
|
||||
}
|
||||
#endif
|
||||
return mozilla::MakeGenericErrorResult(reportedOOM);
|
||||
}
|
||||
|
||||
mozilla::GenericErrorResult<JS::Error&>
|
||||
ExclusiveContext::alreadyReportedError()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (JSContext* maybecx = maybeJSContext())
|
||||
MOZ_ASSERT(maybecx->isExceptionPending());
|
||||
#endif
|
||||
return mozilla::MakeGenericErrorResult(reportedError);
|
||||
}
|
||||
|
||||
JSContext::JSContext(JSRuntime* parentRuntime)
|
||||
: ExclusiveContext(this, &this->JSRuntime::mainThread, Context_JS, JS::ContextOptions()),
|
||||
JSRuntime(parentRuntime),
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "js/CharacterEncoding.h"
|
||||
#include "js/GCVector.h"
|
||||
#include "js/Result.h"
|
||||
#include "js/Utility.h"
|
||||
#include "js/Vector.h"
|
||||
#include "vm/Caches.h"
|
||||
|
|
@ -314,6 +315,30 @@ class ExclusiveContext : public ContextFriendFields,
|
|||
bool addPendingCompileError(frontend::CompileError** err);
|
||||
void addPendingOverRecursed();
|
||||
void addPendingOutOfMemory();
|
||||
|
||||
private:
|
||||
static JS::Error reportedError;
|
||||
static JS::OOM reportedOOM;
|
||||
|
||||
public:
|
||||
inline JS::Result<> boolToResult(bool ok);
|
||||
|
||||
/**
|
||||
* Intentionally awkward signpost method that is stationed on the
|
||||
* boundary between Result-using and non-Result-using code.
|
||||
*/
|
||||
template <typename V, typename E>
|
||||
bool resultToBool(JS::Result<V, E> result) {
|
||||
return result.isOk();
|
||||
}
|
||||
|
||||
template <typename V, typename E>
|
||||
V* resultToPtr(JS::Result<V*, E> result) {
|
||||
return result.isOk() ? result.unwrap() : nullptr;
|
||||
}
|
||||
|
||||
mozilla::GenericErrorResult<JS::OOM&> alreadyReportedOOM();
|
||||
mozilla::GenericErrorResult<JS::Error&> alreadyReportedError();
|
||||
};
|
||||
|
||||
void ReportOverRecursed(JSContext* cx, unsigned errorNumber);
|
||||
|
|
@ -340,6 +365,7 @@ struct JSContext : public js::ExclusiveContext,
|
|||
using ExclusiveContext::permanentAtoms;
|
||||
using ExclusiveContext::pod_calloc;
|
||||
using ExclusiveContext::pod_malloc;
|
||||
using ExclusiveContext::pod_realloc;
|
||||
using ExclusiveContext::staticStrings;
|
||||
using ExclusiveContext::updateMallocCounter;
|
||||
using ExclusiveContext::wellKnownSymbols;
|
||||
|
|
@ -490,7 +516,7 @@ struct JSContext : public js::ExclusiveContext,
|
|||
}
|
||||
|
||||
public:
|
||||
bool isExceptionPending() {
|
||||
bool isExceptionPending() const {
|
||||
return throwing;
|
||||
}
|
||||
|
||||
|
|
@ -540,6 +566,17 @@ struct JSContext : public js::ExclusiveContext,
|
|||
|
||||
namespace js {
|
||||
|
||||
inline JS::Result<>
|
||||
ExclusiveContext::boolToResult(bool ok)
|
||||
{
|
||||
if (MOZ_LIKELY(ok)) {
|
||||
MOZ_ASSERT_IF(isJSContext(), !asJSContext()->isExceptionPending());
|
||||
MOZ_ASSERT_IF(isJSContext(), !asJSContext()->isPropagatingForcedReturn());
|
||||
return JS::Ok();
|
||||
}
|
||||
return JS::Result<>(reportedError);
|
||||
}
|
||||
|
||||
struct MOZ_RAII AutoResolving {
|
||||
public:
|
||||
enum Kind {
|
||||
|
|
|
|||
|
|
@ -2862,6 +2862,20 @@ ToWindowProxyIfWindow(JSObject* obj);
|
|||
extern JS_FRIEND_API(JSObject*)
|
||||
ToWindowIfWindowProxy(JSObject* obj);
|
||||
|
||||
/*
|
||||
* This custom date/time formatter constructor gives users the ability
|
||||
* to specify a custom format pattern. This pattern is passed *directly*
|
||||
* to ICU with NO SYNTAX PARSING OR VALIDATION WHATSOEVER. ICU appears to
|
||||
* have a a modicum of testing of this, and it won't fall over completely
|
||||
* if passed bad input. But the current behavior is entirely under-specified
|
||||
* and emphatically not shippable on the web, and it *must* be fixed before
|
||||
* this functionality can be exposed in the real world. (There are also some
|
||||
* questions about whether the format exposed here is the *right* one to
|
||||
* standardize, that will also need to be resolved to ship this.)
|
||||
*/
|
||||
extern bool
|
||||
AddMozDateTimeFormatConstructor(JSContext* cx, JS::Handle<JSObject*> intl);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
class NativeProfiler
|
||||
|
|
|
|||
294
js/src/jsgc.cpp
294
js/src/jsgc.cpp
|
|
@ -384,6 +384,42 @@ static const FinalizePhase BackgroundFinalizePhases[] = {
|
|||
}
|
||||
};
|
||||
|
||||
// Incremental sweeping is controlled by a list of actions that describe what
|
||||
// happens and in what order. Due to the incremental nature of sweeping an
|
||||
// action does not necessarily run to completion so the current state is tracked
|
||||
// in the GCRuntime by the performSweepActions() method.
|
||||
//
|
||||
// Actions are performed in phases run per sweep group, and each action is run
|
||||
// for every zone in the group, i.e. as if by the following pseudocode:
|
||||
//
|
||||
// for each sweep group:
|
||||
// for each phase:
|
||||
// for each zone in sweep group:
|
||||
// for each action in phase:
|
||||
// perform_action
|
||||
|
||||
struct SweepAction
|
||||
{
|
||||
using Func = IncrementalProgress (*)(GCRuntime* gc, FreeOp* fop, Zone* zone,
|
||||
SliceBudget& budget, AllocKind kind);
|
||||
|
||||
Func func;
|
||||
AllocKind kind;
|
||||
|
||||
SweepAction(Func func, AllocKind kind) : func(func), kind(kind) {}
|
||||
};
|
||||
|
||||
using SweepActionVector = Vector<SweepAction, 0, SystemAllocPolicy>;
|
||||
using SweepPhaseVector = Vector<SweepActionVector, 0, SystemAllocPolicy>;
|
||||
|
||||
static SweepPhaseVector SweepPhases;
|
||||
|
||||
bool
|
||||
js::gc::InitializeStaticData()
|
||||
{
|
||||
return GCRuntime::initializeSweepActions();
|
||||
}
|
||||
|
||||
template<>
|
||||
JSObject*
|
||||
ArenaCellIterImpl::get<JSObject>() const
|
||||
|
|
@ -841,8 +877,9 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
|
|||
zoneGroupIndex(0),
|
||||
zoneGroups(nullptr),
|
||||
currentZoneGroup(nullptr),
|
||||
sweepPhaseIndex(0),
|
||||
sweepZone(nullptr),
|
||||
sweepKind(AllocKind::FIRST),
|
||||
sweepActionIndex(0),
|
||||
abortSweepAfterCurrentGroup(false),
|
||||
arenasAllocatedDuringSweep(nullptr),
|
||||
startedCompacting(false),
|
||||
|
|
@ -883,7 +920,7 @@ static const uint64_t JIT_SCRIPT_RELEASE_TYPES_PERIOD = 20;
|
|||
bool
|
||||
GCRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
|
||||
{
|
||||
InitMemorySubsystem();
|
||||
MOZ_ASSERT(SystemPageSize());
|
||||
|
||||
if (!rootsHash.init(256))
|
||||
return false;
|
||||
|
|
@ -3915,7 +3952,7 @@ struct AddOutgoingEdgeFunctor {
|
|||
*/
|
||||
if (needsEdge_) {
|
||||
JS::Zone* zone = other.zone();
|
||||
if (zone->isGCMarking())
|
||||
if (zone->shouldMarkInZone())
|
||||
finder_.addEdgeTo(zone);
|
||||
}
|
||||
}
|
||||
|
|
@ -3946,14 +3983,14 @@ Zone::findOutgoingEdges(ZoneComponentFinder& finder)
|
|||
*/
|
||||
JSRuntime* rt = runtimeFromMainThread();
|
||||
Zone* atomsZone = rt->atomsCompartment(finder.lock)->zone();
|
||||
if (atomsZone->isGCMarking())
|
||||
if (atomsZone->shouldMarkInZone())
|
||||
finder.addEdgeTo(atomsZone);
|
||||
|
||||
for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next())
|
||||
comp->findOutgoingEdges(finder);
|
||||
|
||||
for (ZoneSet::Range r = gcZoneGroupEdges.all(); !r.empty(); r.popFront()) {
|
||||
if (r.front()->isGCMarking())
|
||||
if (r.front()->shouldMarkInZone())
|
||||
finder.addEdgeTo(r.front());
|
||||
}
|
||||
|
||||
|
|
@ -3995,7 +4032,7 @@ GCRuntime::findZoneGroups(AutoLockForExclusiveAccess& lock)
|
|||
finder.useOneComponent();
|
||||
|
||||
for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
|
||||
MOZ_ASSERT(zone->isGCMarking());
|
||||
MOZ_ASSERT(zone->shouldMarkInZone());
|
||||
finder.addNode(zone);
|
||||
}
|
||||
zoneGroups = finder.getResultsList();
|
||||
|
|
@ -4008,7 +4045,7 @@ GCRuntime::findZoneGroups(AutoLockForExclusiveAccess& lock)
|
|||
#ifdef DEBUG
|
||||
for (Zone* head = currentZoneGroup; head; head = head->nextGroup()) {
|
||||
for (Zone* zone = head; zone; zone = zone->nextNodeInGroup())
|
||||
MOZ_ASSERT(zone->isGCMarking());
|
||||
MOZ_ASSERT(zone->shouldMarkInZone());
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(!isIncremental, !currentZoneGroup->nextGroup());
|
||||
|
|
@ -4031,7 +4068,7 @@ GCRuntime::getNextZoneGroup()
|
|||
}
|
||||
|
||||
for (Zone* zone = currentZoneGroup; zone; zone = zone->nextNodeInGroup()) {
|
||||
MOZ_ASSERT(zone->isGCMarking());
|
||||
MOZ_ASSERT(zone->shouldMarkInZone());
|
||||
MOZ_ASSERT(!zone->isQueuedForBackgroundSweep());
|
||||
}
|
||||
|
||||
|
|
@ -4042,7 +4079,7 @@ GCRuntime::getNextZoneGroup()
|
|||
MOZ_ASSERT(!isIncremental);
|
||||
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
|
||||
MOZ_ASSERT(!zone->gcNextGraphComponent);
|
||||
MOZ_ASSERT(zone->isGCMarking());
|
||||
MOZ_ASSERT(zone->shouldMarkInZone());
|
||||
zone->setNeedsIncrementalBarrier(false, Zone::UpdateJit);
|
||||
zone->setGCState(Zone::NoGC);
|
||||
zone->gcGrayRoots.clearAndFree();
|
||||
|
|
@ -4485,7 +4522,7 @@ GCRuntime::beginSweepingZoneGroup(AutoLockForExclusiveAccess& lock)
|
|||
bool sweepingAtoms = false;
|
||||
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
|
||||
/* Set the GC state to sweeping. */
|
||||
MOZ_ASSERT(zone->isGCMarking());
|
||||
MOZ_ASSERT(zone->shouldMarkInZone());
|
||||
zone->setGCState(Zone::Sweep);
|
||||
|
||||
/* Purge the ArenaLists before sweeping. */
|
||||
|
|
@ -4667,11 +4704,9 @@ GCRuntime::beginSweepingZoneGroup(AutoLockForExclusiveAccess& lock)
|
|||
zone->arenas.queueForegroundThingsForSweep(&fop);
|
||||
}
|
||||
|
||||
sweepingTypes = true;
|
||||
|
||||
finalizePhase = 0;
|
||||
sweepPhaseIndex = 0;
|
||||
sweepZone = currentZoneGroup;
|
||||
sweepKind = AllocKind::FIRST;
|
||||
sweepActionIndex = 0;
|
||||
|
||||
{
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_FINALIZE_END);
|
||||
|
|
@ -4765,7 +4800,7 @@ ArenaLists::foregroundFinalize(FreeOp* fop, AllocKind thingKind, SliceBudget& sl
|
|||
return true;
|
||||
}
|
||||
|
||||
GCRuntime::IncrementalProgress
|
||||
IncrementalProgress
|
||||
GCRuntime::drainMarkStack(SliceBudget& sliceBudget, gcstats::Phase phase)
|
||||
{
|
||||
/* Run a marking slice and return whether the stack is now empty. */
|
||||
|
|
@ -4810,105 +4845,160 @@ SweepArenaList(Arena** arenasToSweep, SliceBudget& sliceBudget, Args... args)
|
|||
return true;
|
||||
}
|
||||
|
||||
GCRuntime::IncrementalProgress
|
||||
GCRuntime::sweepPhase(SliceBudget& sliceBudget, AutoLockForExclusiveAccess& lock)
|
||||
/* static */ IncrementalProgress
|
||||
GCRuntime::sweepTypeInformation(GCRuntime* gc, FreeOp* fop, Zone* zone, SliceBudget& budget,
|
||||
AllocKind kind)
|
||||
{
|
||||
// Sweep dead type information stored in scripts and object groups, but
|
||||
// don't finalize them yet. We have to sweep dead information from both live
|
||||
// and dead scripts and object groups, so that no dead references remain in
|
||||
// them. Type inference can end up crawling these zones again, such as for
|
||||
// TypeCompartment::markSetsUnknown, and if this happens after sweeping for
|
||||
// the sweep group finishes we won't be able to determine which things in
|
||||
// the zone are live.
|
||||
|
||||
MOZ_ASSERT(kind == AllocKind::LIMIT);
|
||||
|
||||
gcstats::AutoPhase ap1(gc->stats, gcstats::PHASE_SWEEP_COMPARTMENTS);
|
||||
gcstats::AutoPhase ap2(gc->stats, gcstats::PHASE_SWEEP_TYPES);
|
||||
|
||||
ArenaLists& al = zone->arenas;
|
||||
|
||||
AutoClearTypeInferenceStateOnOOM oom(zone);
|
||||
|
||||
if (!SweepArenaList<JSScript>(&al.gcScriptArenasToUpdate, budget, &oom))
|
||||
return NotFinished;
|
||||
|
||||
if (!SweepArenaList<ObjectGroup>(&al.gcObjectGroupArenasToUpdate, budget, &oom))
|
||||
return NotFinished;
|
||||
|
||||
// Finish sweeping type information in the zone.
|
||||
{
|
||||
gcstats::AutoPhase ap(gc->stats, gcstats::PHASE_SWEEP_TYPES_END);
|
||||
zone->types.endSweep(gc->rt);
|
||||
}
|
||||
|
||||
return Finished;
|
||||
}
|
||||
|
||||
/* static */ IncrementalProgress
|
||||
GCRuntime::mergeSweptObjectArenas(GCRuntime* gc, FreeOp* fop, Zone* zone, SliceBudget& budget,
|
||||
AllocKind kind)
|
||||
{
|
||||
// Foreground finalized objects have already been finalized, and now their
|
||||
// arenas can be reclaimed by freeing empty ones and making non-empty ones
|
||||
// available for allocation.
|
||||
|
||||
MOZ_ASSERT(kind == AllocKind::LIMIT);
|
||||
zone->arenas.mergeForegroundSweptObjectArenas();
|
||||
return Finished;
|
||||
}
|
||||
|
||||
/* static */ IncrementalProgress
|
||||
GCRuntime::finalizeAllocKind(GCRuntime* gc, FreeOp* fop, Zone* zone, SliceBudget& budget,
|
||||
AllocKind kind)
|
||||
{
|
||||
// Set the number of things per arena for this AllocKind.
|
||||
size_t thingsPerArena = Arena::thingsPerArena(kind);
|
||||
auto& sweepList = gc->incrementalSweepList;
|
||||
sweepList.setThingsPerArena(thingsPerArena);
|
||||
|
||||
if (!zone->arenas.foregroundFinalize(fop, kind, budget, sweepList))
|
||||
return NotFinished;
|
||||
|
||||
// Reset the slots of the sweep list that we used.
|
||||
sweepList.reset(thingsPerArena);
|
||||
|
||||
return Finished;
|
||||
}
|
||||
|
||||
/* static */ IncrementalProgress
|
||||
GCRuntime::sweepShapeTree(GCRuntime* gc, FreeOp* fop, Zone* zone, SliceBudget& budget,
|
||||
AllocKind kind)
|
||||
{
|
||||
// Remove dead shapes from the shape tree, but don't finalize them yet.
|
||||
|
||||
MOZ_ASSERT(kind == AllocKind::LIMIT);
|
||||
|
||||
gcstats::AutoPhase ap(gc->stats, gcstats::PHASE_SWEEP_SHAPE);
|
||||
|
||||
ArenaLists& al = zone->arenas;
|
||||
|
||||
if (!SweepArenaList<Shape>(&al.gcShapeArenasToUpdate, budget))
|
||||
return NotFinished;
|
||||
|
||||
if (!SweepArenaList<AccessorShape>(&al.gcAccessorShapeArenasToUpdate, budget))
|
||||
return NotFinished;
|
||||
|
||||
return Finished;
|
||||
}
|
||||
|
||||
static void
|
||||
AddSweepPhase(bool* ok)
|
||||
{
|
||||
if (*ok)
|
||||
*ok = SweepPhases.emplaceBack();
|
||||
}
|
||||
|
||||
static void
|
||||
AddSweepAction(bool* ok, SweepAction::Func func, AllocKind kind = AllocKind::LIMIT)
|
||||
{
|
||||
if (*ok)
|
||||
*ok = SweepPhases.back().emplaceBack(func, kind);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
GCRuntime::initializeSweepActions()
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
AddSweepPhase(&ok);
|
||||
|
||||
AddSweepAction(&ok, GCRuntime::sweepTypeInformation);
|
||||
AddSweepAction(&ok, GCRuntime::mergeSweptObjectArenas);
|
||||
|
||||
for (const auto& finalizePhase : IncrementalFinalizePhases) {
|
||||
AddSweepPhase(&ok);
|
||||
for (auto kind : finalizePhase.kinds)
|
||||
AddSweepAction(&ok, GCRuntime::finalizeAllocKind, kind);
|
||||
}
|
||||
|
||||
AddSweepPhase(&ok);
|
||||
AddSweepAction(&ok, GCRuntime::sweepShapeTree);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
IncrementalProgress
|
||||
GCRuntime::performSweepActions(SliceBudget& budget, AutoLockForExclusiveAccess& lock)
|
||||
{
|
||||
AutoSetThreadIsSweeping threadIsSweeping;
|
||||
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP);
|
||||
FreeOp fop(rt);
|
||||
|
||||
if (drainMarkStack(sliceBudget, gcstats::PHASE_SWEEP_MARK) == NotFinished)
|
||||
if (drainMarkStack(budget, gcstats::PHASE_SWEEP_MARK) == NotFinished)
|
||||
return NotFinished;
|
||||
|
||||
|
||||
for (;;) {
|
||||
// Sweep dead type information stored in scripts and object groups, but
|
||||
// don't finalize them yet. We have to sweep dead information from both
|
||||
// live and dead scripts and object groups, so that no dead references
|
||||
// remain in them. Type inference can end up crawling these zones
|
||||
// again, such as for TypeCompartment::markSetsUnknown, and if this
|
||||
// happens after sweeping for the zone group finishes we won't be able
|
||||
// to determine which things in the zone are live.
|
||||
if (sweepingTypes) {
|
||||
gcstats::AutoPhase ap1(stats, gcstats::PHASE_SWEEP_COMPARTMENTS);
|
||||
gcstats::AutoPhase ap2(stats, gcstats::PHASE_SWEEP_TYPES);
|
||||
|
||||
for (; sweepPhaseIndex < SweepPhases.length(); sweepPhaseIndex++) {
|
||||
const auto& actions = SweepPhases[sweepPhaseIndex];
|
||||
for (; sweepZone; sweepZone = sweepZone->nextNodeInGroup()) {
|
||||
ArenaLists& al = sweepZone->arenas;
|
||||
|
||||
AutoClearTypeInferenceStateOnOOM oom(sweepZone);
|
||||
|
||||
if (!SweepArenaList<JSScript>(&al.gcScriptArenasToUpdate, sliceBudget, &oom))
|
||||
return NotFinished;
|
||||
|
||||
if (!SweepArenaList<ObjectGroup>(
|
||||
&al.gcObjectGroupArenasToUpdate, sliceBudget, &oom))
|
||||
{
|
||||
return NotFinished;
|
||||
}
|
||||
|
||||
// Finish sweeping type information in the zone.
|
||||
{
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_TYPES_END);
|
||||
sweepZone->types.endSweep(rt);
|
||||
}
|
||||
|
||||
// Foreground finalized objects have already been finalized,
|
||||
// and now their arenas can be reclaimed by freeing empty ones
|
||||
// and making non-empty ones available for allocation.
|
||||
al.mergeForegroundSweptObjectArenas();
|
||||
}
|
||||
|
||||
sweepZone = currentZoneGroup;
|
||||
sweepingTypes = false;
|
||||
}
|
||||
|
||||
/* Finalize foreground finalized things. */
|
||||
for (; finalizePhase < ArrayLength(IncrementalFinalizePhases) ; ++finalizePhase) {
|
||||
gcstats::AutoPhase ap(stats, IncrementalFinalizePhases[finalizePhase].statsPhase);
|
||||
|
||||
for (; sweepZone; sweepZone = sweepZone->nextNodeInGroup()) {
|
||||
Zone* zone = sweepZone;
|
||||
|
||||
for (auto kind : SomeAllocKinds(sweepKind, AllocKind::LIMIT)) {
|
||||
if (!IncrementalFinalizePhases[finalizePhase].kinds.contains(kind))
|
||||
continue;
|
||||
|
||||
/* Set the number of things per arena for this AllocKind. */
|
||||
size_t thingsPerArena = Arena::thingsPerArena(kind);
|
||||
incrementalSweepList.setThingsPerArena(thingsPerArena);
|
||||
|
||||
if (!zone->arenas.foregroundFinalize(&fop, kind, sliceBudget,
|
||||
incrementalSweepList))
|
||||
{
|
||||
sweepKind = kind;
|
||||
for (; sweepActionIndex < actions.length(); sweepActionIndex++) {
|
||||
const auto& action = actions[sweepActionIndex];
|
||||
if (action.func(this, &fop, sweepZone, budget, action.kind) == NotFinished)
|
||||
return NotFinished;
|
||||
}
|
||||
|
||||
/* Reset the slots of the sweep list that we used. */
|
||||
incrementalSweepList.reset(thingsPerArena);
|
||||
}
|
||||
sweepKind = AllocKind::FIRST;
|
||||
// Reset action index to first.
|
||||
sweepActionIndex = 0;
|
||||
}
|
||||
sweepZone = currentZoneGroup;
|
||||
}
|
||||
|
||||
/* Remove dead shapes from the shape tree, but don't finalize them yet. */
|
||||
{
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_SHAPE);
|
||||
|
||||
for (; sweepZone; sweepZone = sweepZone->nextNodeInGroup()) {
|
||||
ArenaLists& al = sweepZone->arenas;
|
||||
|
||||
if (!SweepArenaList<Shape>(&al.gcShapeArenasToUpdate, sliceBudget))
|
||||
return NotFinished;
|
||||
|
||||
if (!SweepArenaList<AccessorShape>(&al.gcAccessorShapeArenasToUpdate, sliceBudget))
|
||||
return NotFinished;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset phase index.
|
||||
sweepPhaseIndex = 0;
|
||||
|
||||
endSweepingZoneGroup();
|
||||
getNextZoneGroup();
|
||||
if (!currentZoneGroup)
|
||||
|
|
@ -4999,7 +5089,7 @@ GCRuntime::beginCompactPhase()
|
|||
startedCompacting = true;
|
||||
}
|
||||
|
||||
GCRuntime::IncrementalProgress
|
||||
IncrementalProgress
|
||||
GCRuntime::compactPhase(JS::gcreason::Reason reason, SliceBudget& sliceBudget,
|
||||
AutoLockForExclusiveAccess& lock)
|
||||
{
|
||||
|
|
@ -5141,7 +5231,7 @@ GCRuntime::resetIncrementalGC(gc::AbortReason reason, AutoLockForExclusiveAccess
|
|||
ResetGrayList(c);
|
||||
|
||||
for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
|
||||
MOZ_ASSERT(zone->isGCMarking());
|
||||
MOZ_ASSERT(zone->shouldMarkInZone());
|
||||
zone->setNeedsIncrementalBarrier(false, Zone::UpdateJit);
|
||||
zone->setGCState(Zone::NoGC);
|
||||
}
|
||||
|
|
@ -5265,7 +5355,7 @@ AutoGCSlice::AutoGCSlice(JSRuntime* rt)
|
|||
* is expensive) because Ion code doesn't run during GC. If need be,
|
||||
* we'll update the Ion barriers in ~AutoGCSlice.
|
||||
*/
|
||||
if (zone->isGCMarking()) {
|
||||
if (zone->shouldMarkInZone()) {
|
||||
MOZ_ASSERT(zone->needsIncrementalBarrier());
|
||||
zone->setNeedsIncrementalBarrier(false, Zone::DontUpdateJit);
|
||||
} else {
|
||||
|
|
@ -5278,7 +5368,7 @@ AutoGCSlice::~AutoGCSlice()
|
|||
{
|
||||
/* We can't use GCZonesIter if this is the end of the last slice. */
|
||||
for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next()) {
|
||||
if (zone->isGCMarking()) {
|
||||
if (zone->shouldMarkInZone()) {
|
||||
zone->setNeedsIncrementalBarrier(true, Zone::UpdateJit);
|
||||
zone->arenas.purge();
|
||||
} else {
|
||||
|
|
@ -5381,7 +5471,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
|
|||
MOZ_FALLTHROUGH;
|
||||
|
||||
case State::Sweep:
|
||||
if (sweepPhase(budget, lock) == NotFinished)
|
||||
if (performSweepActions(budget, lock) == NotFinished)
|
||||
break;
|
||||
|
||||
endSweepPhase(destroyingRuntime, lock);
|
||||
|
|
|
|||
|
|
@ -838,6 +838,9 @@ class ArenaLists
|
|||
/* The number of GC cycles an empty chunk can survive before been released. */
|
||||
const size_t MAX_EMPTY_CHUNK_AGE = 4;
|
||||
|
||||
extern bool
|
||||
InitializeStaticData();
|
||||
|
||||
} /* namespace gc */
|
||||
|
||||
class InterpreterFrame;
|
||||
|
|
|
|||
|
|
@ -553,10 +553,10 @@ NewPropertyIteratorObject(JSContext* cx, unsigned flags)
|
|||
if (!shape)
|
||||
return nullptr;
|
||||
|
||||
JSObject* obj = JSObject::create(cx, ITERATOR_FINALIZE_KIND,
|
||||
GetInitialHeap(GenericObject, clasp), shape, group);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
JSObject* obj;
|
||||
JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, ITERATOR_FINALIZE_KIND,
|
||||
GetInitialHeap(GenericObject, clasp),
|
||||
shape, group));
|
||||
|
||||
PropertyIteratorObject* res = &obj->as<PropertyIteratorObject>();
|
||||
|
||||
|
|
|
|||
|
|
@ -257,15 +257,15 @@ js::Throw(JSContext* cx, JSObject* obj, unsigned errorNumber)
|
|||
|
||||
/*** PropertyDescriptor operations and DefineProperties ******************************************/
|
||||
|
||||
bool
|
||||
static Result<>
|
||||
CheckCallable(JSContext* cx, JSObject* obj, const char* fieldName)
|
||||
{
|
||||
if (obj && !obj->isCallable()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
|
||||
fieldName);
|
||||
return false;
|
||||
return cx->alreadyReportedError();
|
||||
}
|
||||
return true;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -335,8 +335,8 @@ js::ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors
|
|||
hasGetOrSet = found;
|
||||
if (found) {
|
||||
if (v.isObject()) {
|
||||
if (checkAccessors && !CheckCallable(cx, &v.toObject(), js_getter_str))
|
||||
return false;
|
||||
if (checkAccessors)
|
||||
JS_TRY_OR_RETURN_FALSE(cx, CheckCallable(cx, &v.toObject(), js_getter_str));
|
||||
desc.setGetterObject(&v.toObject());
|
||||
} else if (!v.isUndefined()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
|
||||
|
|
@ -353,8 +353,8 @@ js::ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors
|
|||
hasGetOrSet |= found;
|
||||
if (found) {
|
||||
if (v.isObject()) {
|
||||
if (checkAccessors && !CheckCallable(cx, &v.toObject(), js_setter_str))
|
||||
return false;
|
||||
if (checkAccessors)
|
||||
JS_TRY_OR_RETURN_FALSE(cx, CheckCallable(cx, &v.toObject(), js_setter_str));
|
||||
desc.setSetterObject(&v.toObject());
|
||||
} else if (!v.isUndefined()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
|
||||
|
|
@ -381,18 +381,16 @@ js::ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Result<>
|
||||
js::CheckPropertyDescriptorAccessors(JSContext* cx, Handle<PropertyDescriptor> desc)
|
||||
{
|
||||
if (desc.hasGetterObject()) {
|
||||
if (!CheckCallable(cx, desc.getterObject(), js_getter_str))
|
||||
return false;
|
||||
}
|
||||
if (desc.hasSetterObject()) {
|
||||
if (!CheckCallable(cx, desc.setterObject(), js_setter_str))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
if (desc.hasGetterObject())
|
||||
MOZ_TRY(CheckCallable(cx, desc.getterObject(), js_getter_str));
|
||||
|
||||
if (desc.hasSetterObject())
|
||||
MOZ_TRY(CheckCallable(cx, desc.setterObject(), js_setter_str));
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -646,9 +644,8 @@ NewObject(ExclusiveContext* cx, HandleObjectGroup group, gc::AllocKind kind,
|
|||
return nullptr;
|
||||
|
||||
gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
|
||||
JSObject* obj = JSObject::create(cx, kind, heap, shape, group);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
JSObject* obj;
|
||||
JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, kind, heap, shape, group));
|
||||
|
||||
if (newKind == SingletonObject) {
|
||||
RootedObject nobj(cx, obj);
|
||||
|
|
|
|||
|
|
@ -179,11 +179,9 @@ class JSObject : public js::gc::Cell
|
|||
* Make a non-array object with the specified initial state. This method
|
||||
* takes ownership of any extantSlots it is passed.
|
||||
*/
|
||||
static inline JSObject* create(js::ExclusiveContext* cx,
|
||||
js::gc::AllocKind kind,
|
||||
js::gc::InitialHeap heap,
|
||||
js::HandleShape shape,
|
||||
js::HandleObjectGroup group);
|
||||
static inline JS::Result<JSObject*, JS::OOM&>
|
||||
create(js::ExclusiveContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
|
||||
js::HandleShape shape, js::HandleObjectGroup group);
|
||||
|
||||
// Set the initial slots and elements of an object. These pointers are only
|
||||
// valid for native objects, but during initialization are set for all
|
||||
|
|
@ -1173,7 +1171,7 @@ ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors,
|
|||
* callable. This performs exactly the checks omitted by ToPropertyDescriptor
|
||||
* when checkAccessors is false.
|
||||
*/
|
||||
bool
|
||||
Result<>
|
||||
CheckPropertyDescriptorAccessors(JSContext* cx, Handle<JS::PropertyDescriptor> desc);
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -319,7 +319,7 @@ SetNewObjectMetadata(ExclusiveContext* cxArg, JSObject* obj)
|
|||
|
||||
} // namespace js
|
||||
|
||||
/* static */ inline JSObject*
|
||||
/* static */ inline JS::Result<JSObject*, JS::OOM&>
|
||||
JSObject::create(js::ExclusiveContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
|
||||
js::HandleShape shape, js::HandleObjectGroup group)
|
||||
{
|
||||
|
|
@ -375,7 +375,7 @@ JSObject::create(js::ExclusiveContext* cx, js::gc::AllocKind kind, js::gc::Initi
|
|||
|
||||
JSObject* obj = js::Allocate<JSObject>(cx, kind, nDynamicSlots, heap, clasp);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
return cx->alreadyReportedOOM();
|
||||
|
||||
obj->group_.init(group);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include "jsprototypes.h"
|
||||
#include "jstypes.h"
|
||||
|
||||
#include "js/Result.h"
|
||||
#include "js/TraceKind.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
|
|
|
|||
663
js/src/jsstr.cpp
663
js/src/jsstr.cpp
|
|
@ -31,10 +31,12 @@
|
|||
#include "jsutil.h"
|
||||
|
||||
#include "builtin/intl/ICUHeader.h"
|
||||
#include "builtin/intl/CommonFunctions.h"
|
||||
#include "builtin/RegExp.h"
|
||||
#include "jit/InlinableNatives.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "js/UniquePtr.h"
|
||||
#include "unicode/uchar.h"
|
||||
#include "unicode/unorm2.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/Interpreter.h"
|
||||
|
|
@ -598,19 +600,210 @@ js::SubstringKernel(JSContext* cx, HandleString str, int32_t beginInt, int32_t l
|
|||
return NewDependentString(cx, str, begin, len);
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
static auto
|
||||
ReallocChars(JSContext* cx, UniquePtr<CharT[], JS::FreePolicy> chars, size_t oldLength,
|
||||
size_t newLength)
|
||||
-> decltype(chars)
|
||||
{
|
||||
using AnyCharPtr = decltype(chars);
|
||||
|
||||
CharT* oldChars = chars.release();
|
||||
CharT* newChars = cx->pod_realloc<CharT>(oldChars, oldLength, newLength);
|
||||
if (!newChars) {
|
||||
js_free(oldChars);
|
||||
return AnyCharPtr();
|
||||
}
|
||||
|
||||
return AnyCharPtr(newChars);
|
||||
}
|
||||
|
||||
/**
|
||||
* U+03A3 GREEK CAPITAL LETTER SIGMA has two different lower case mappings
|
||||
* depending on its context:
|
||||
* When it's preceded by a cased character and not followed by another cased
|
||||
* character, its lower case form is U+03C2 GREEK SMALL LETTER FINAL SIGMA.
|
||||
* Otherwise its lower case mapping is U+03C3 GREEK SMALL LETTER SIGMA.
|
||||
*
|
||||
* Unicode 9.0, §3.13 Default Case Algorithms
|
||||
*/
|
||||
static char16_t
|
||||
Final_Sigma(const char16_t* chars, size_t length, size_t index)
|
||||
{
|
||||
MOZ_ASSERT(index < length);
|
||||
MOZ_ASSERT(chars[index] == unicode::GREEK_CAPITAL_LETTER_SIGMA);
|
||||
MOZ_ASSERT(unicode::ToLowerCase(unicode::GREEK_CAPITAL_LETTER_SIGMA) ==
|
||||
unicode::GREEK_SMALL_LETTER_SIGMA);
|
||||
|
||||
// Tell the analysis the BinaryProperty.contains function pointer called by
|
||||
// u_hasBinaryProperty cannot GC.
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
|
||||
bool precededByCased = false;
|
||||
for (size_t i = index; i > 0; ) {
|
||||
char16_t c = chars[--i];
|
||||
uint32_t codePoint = c;
|
||||
if (unicode::IsTrailSurrogate(c) && i > 0) {
|
||||
char16_t lead = chars[i - 1];
|
||||
if (unicode::IsLeadSurrogate(lead)) {
|
||||
codePoint = unicode::UTF16Decode(lead, c);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore any characters with the property Case_Ignorable.
|
||||
// NB: We need to skip over all Case_Ignorable characters, even when
|
||||
// they also have the Cased binary property.
|
||||
if (u_hasBinaryProperty(codePoint, UCHAR_CASE_IGNORABLE))
|
||||
continue;
|
||||
|
||||
precededByCased = u_hasBinaryProperty(codePoint, UCHAR_CASED);
|
||||
break;
|
||||
}
|
||||
if (!precededByCased)
|
||||
return unicode::GREEK_SMALL_LETTER_SIGMA;
|
||||
|
||||
bool followedByCased = false;
|
||||
for (size_t i = index + 1; i < length; ) {
|
||||
char16_t c = chars[i++];
|
||||
uint32_t codePoint = c;
|
||||
if (unicode::IsLeadSurrogate(c) && i < length) {
|
||||
char16_t trail = chars[i];
|
||||
if (unicode::IsTrailSurrogate(trail)) {
|
||||
codePoint = unicode::UTF16Decode(c, trail);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore any characters with the property Case_Ignorable.
|
||||
// NB: We need to skip over all Case_Ignorable characters, even when
|
||||
// they also have the Cased binary property.
|
||||
if (u_hasBinaryProperty(codePoint, UCHAR_CASE_IGNORABLE))
|
||||
continue;
|
||||
|
||||
followedByCased = u_hasBinaryProperty(codePoint, UCHAR_CASED);
|
||||
break;
|
||||
}
|
||||
if (!followedByCased)
|
||||
return unicode::GREEK_SMALL_LETTER_FINAL_SIGMA;
|
||||
|
||||
return unicode::GREEK_SMALL_LETTER_SIGMA;
|
||||
}
|
||||
|
||||
static Latin1Char
|
||||
Final_Sigma(const Latin1Char* chars, size_t length, size_t index)
|
||||
{
|
||||
MOZ_ASSERT_UNREACHABLE("U+03A3 is not a Latin-1 character");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If |srcLength == destLength| is true, the destination buffer was allocated
|
||||
// with the same size as the source buffer. When we append characters which
|
||||
// have special casing mappings, we test |srcLength == destLength| to decide
|
||||
// if we need to back out and reallocate a sufficiently large destination
|
||||
// buffer. Otherwise the destination buffer was allocated with the correct
|
||||
// size to hold all lower case mapped characters, i.e.
|
||||
// |destLength == ToLowerCaseLength(srcChars, 0, srcLength)| is true.
|
||||
template <typename CharT>
|
||||
static size_t
|
||||
ToLowerCaseImpl(CharT* destChars, const CharT* srcChars, size_t startIndex, size_t srcLength,
|
||||
size_t destLength)
|
||||
{
|
||||
MOZ_ASSERT(startIndex < srcLength);
|
||||
MOZ_ASSERT(srcLength <= destLength);
|
||||
MOZ_ASSERT_IF((IsSame<CharT, Latin1Char>::value), srcLength == destLength);
|
||||
|
||||
size_t j = startIndex;
|
||||
for (size_t i = startIndex; i < srcLength; i++) {
|
||||
char16_t c = srcChars[i];
|
||||
if (!IsSame<CharT, Latin1Char>::value) {
|
||||
if (unicode::IsLeadSurrogate(c) && i + 1 < srcLength) {
|
||||
char16_t trail = srcChars[i + 1];
|
||||
if (unicode::IsTrailSurrogate(trail)) {
|
||||
trail = unicode::ToLowerCaseNonBMPTrail(c, trail);
|
||||
destChars[j++] = c;
|
||||
destChars[j++] = trail;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Special case: U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE
|
||||
// lowercases to <U+0069 U+0307>.
|
||||
if (c == unicode::LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE) {
|
||||
// Return if the output buffer is too small.
|
||||
if (srcLength == destLength)
|
||||
return i;
|
||||
|
||||
destChars[j++] = CharT('i');
|
||||
destChars[j++] = CharT(unicode::COMBINING_DOT_ABOVE);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Special case: U+03A3 GREEK CAPITAL LETTER SIGMA lowercases to
|
||||
// one of two codepoints depending on context.
|
||||
if (c == unicode::GREEK_CAPITAL_LETTER_SIGMA) {
|
||||
destChars[j++] = Final_Sigma(srcChars, srcLength, i);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
c = unicode::ToLowerCase(c);
|
||||
MOZ_ASSERT_IF((IsSame<CharT, Latin1Char>::value), c <= JSString::MAX_LATIN1_CHAR);
|
||||
destChars[j++] = c;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(j == destLength);
|
||||
destChars[destLength] = '\0';
|
||||
|
||||
return srcLength;
|
||||
}
|
||||
|
||||
static size_t
|
||||
ToLowerCaseLength(const char16_t* chars, size_t startIndex, size_t length)
|
||||
{
|
||||
size_t lowerLength = length;
|
||||
for (size_t i = startIndex; i < length; i++) {
|
||||
char16_t c = chars[i];
|
||||
|
||||
// U+0130 is lowercased to the two-element sequence <U+0069 U+0307>.
|
||||
if (c == unicode::LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE)
|
||||
lowerLength += 1;
|
||||
}
|
||||
return lowerLength;
|
||||
}
|
||||
|
||||
static size_t
|
||||
ToLowerCaseLength(const Latin1Char* chars, size_t startIndex, size_t length)
|
||||
{
|
||||
MOZ_ASSERT_UNREACHABLE("never called for Latin-1 strings");
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
static JSString*
|
||||
ToLowerCase(JSContext* cx, JSLinearString* str)
|
||||
{
|
||||
// Unlike toUpperCase, toLowerCase has the nice invariant that if the input
|
||||
// is a Latin1 string, the output is also a Latin1 string.
|
||||
UniquePtr<CharT[], JS::FreePolicy> newChars;
|
||||
size_t length = str->length();
|
||||
// Unlike toUpperCase, toLowerCase has the nice invariant that if the
|
||||
// input is a Latin-1 string, the output is also a Latin-1 string.
|
||||
using AnyCharPtr = UniquePtr<CharT[], JS::FreePolicy>;
|
||||
|
||||
AnyCharPtr newChars;
|
||||
const size_t length = str->length();
|
||||
size_t resultLength;
|
||||
{
|
||||
AutoCheckCannotGC nogc;
|
||||
const CharT* chars = str->chars<CharT>(nogc);
|
||||
|
||||
// Look for the first upper case character.
|
||||
// We don't need extra special casing checks in the loop below,
|
||||
// because U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE and U+03A3
|
||||
// GREEK CAPITAL LETTER SIGMA already have simple lower case mappings.
|
||||
MOZ_ASSERT(unicode::CanLowerCase(unicode::LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE),
|
||||
"U+0130 has a simple lower case mapping");
|
||||
MOZ_ASSERT(unicode::CanLowerCase(unicode::GREEK_CAPITAL_LETTER_SIGMA),
|
||||
"U+03A3 has a simple lower case mapping");
|
||||
|
||||
// Look for the first character that changes when lowercased.
|
||||
size_t i = 0;
|
||||
for (; i < length; i++) {
|
||||
char16_t c = chars[i];
|
||||
|
|
@ -630,40 +823,35 @@ ToLowerCase(JSContext* cx, JSLinearString* str)
|
|||
break;
|
||||
}
|
||||
|
||||
// If all characters are lower case, return the input string.
|
||||
// If no character needs to change, return the input string.
|
||||
if (i == length)
|
||||
return str;
|
||||
|
||||
newChars = cx->make_pod_array<CharT>(length + 1);
|
||||
resultLength = length;
|
||||
newChars = cx->make_pod_array<CharT>(resultLength + 1);
|
||||
if (!newChars)
|
||||
return nullptr;
|
||||
|
||||
PodCopy(newChars.get(), chars, i);
|
||||
|
||||
for (; i < length; i++) {
|
||||
char16_t c = chars[i];
|
||||
if (!IsSame<CharT, Latin1Char>::value) {
|
||||
if (unicode::IsLeadSurrogate(c) && i + 1 < length) {
|
||||
char16_t trail = chars[i + 1];
|
||||
if (unicode::IsTrailSurrogate(trail)) {
|
||||
trail = unicode::ToLowerCaseNonBMPTrail(c, trail);
|
||||
newChars[i] = c;
|
||||
newChars[i + 1] = trail;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t readChars = ToLowerCaseImpl(newChars.get(), chars, i, length, resultLength);
|
||||
if (readChars < length) {
|
||||
MOZ_ASSERT((!IsSame<CharT, Latin1Char>::value),
|
||||
"Latin-1 strings don't have special lower case mappings");
|
||||
resultLength = ToLowerCaseLength(chars, readChars, length);
|
||||
|
||||
c = unicode::ToLowerCase(c);
|
||||
MOZ_ASSERT_IF((IsSame<CharT, Latin1Char>::value), c <= JSString::MAX_LATIN1_CHAR);
|
||||
newChars[i] = c;
|
||||
AnyCharPtr buf = ReallocChars(cx, Move(newChars), length + 1, resultLength + 1);
|
||||
if (!buf)
|
||||
return nullptr;
|
||||
|
||||
newChars = Move(buf);
|
||||
|
||||
MOZ_ALWAYS_TRUE(length ==
|
||||
ToLowerCaseImpl(newChars.get(), chars, readChars, length, resultLength));
|
||||
}
|
||||
|
||||
newChars[length] = 0;
|
||||
}
|
||||
|
||||
JSString* res = NewStringDontDeflate<CanGC>(cx, newChars.get(), length);
|
||||
JSString* res = NewStringDontDeflate<CanGC>(cx, newChars.get(), resultLength);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
|
||||
|
|
@ -671,21 +859,102 @@ ToLowerCase(JSContext* cx, JSLinearString* str)
|
|||
return res;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ToLowerCaseHelper(JSContext* cx, const CallArgs& args)
|
||||
JSString*
|
||||
js::StringToLowerCase(JSContext* cx, HandleLinearString string)
|
||||
{
|
||||
if (string->hasLatin1Chars())
|
||||
return ToLowerCase<Latin1Char>(cx, string);
|
||||
return ToLowerCase<char16_t>(cx, string);
|
||||
}
|
||||
|
||||
bool
|
||||
js::str_toLowerCase(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
RootedString str(cx, ToStringForStringFunction(cx, args.thisv()));
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
JSLinearString* linear = str->ensureLinear(cx);
|
||||
RootedLinearString linear(cx, str->ensureLinear(cx));
|
||||
if (!linear)
|
||||
return false;
|
||||
|
||||
if (linear->hasLatin1Chars())
|
||||
str = ToLowerCase<Latin1Char>(cx, linear);
|
||||
else
|
||||
str = ToLowerCase<char16_t>(cx, linear);
|
||||
JSString* result = StringToLowerCase(cx, linear);
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
args.rval().setString(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char*
|
||||
CaseMappingLocale(JSContext* cx, JSString* str)
|
||||
{
|
||||
JSLinearString* locale = str->ensureLinear(cx);
|
||||
if (!locale)
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT(locale->length() >= 2, "locale is a valid language tag");
|
||||
|
||||
// Lithuanian, Turkish, and Azeri have language dependent case mappings.
|
||||
static const char languagesWithSpecialCasing[][3] = { "lt", "tr", "az" };
|
||||
|
||||
// All strings in |languagesWithSpecialCasing| are of length two, so we
|
||||
// only need to compare the first two characters to find a matching locale.
|
||||
// ES2017 Intl, §9.2.2 BestAvailableLocale
|
||||
if (locale->length() == 2 || locale->latin1OrTwoByteChar(2) == '-') {
|
||||
for (const auto& language : languagesWithSpecialCasing) {
|
||||
if (locale->latin1OrTwoByteChar(0) == language[0] &&
|
||||
locale->latin1OrTwoByteChar(1) == language[1])
|
||||
{
|
||||
return language;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""; // ICU root locale
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
MOZ_ASSERT(args[0].isString());
|
||||
MOZ_ASSERT(args[1].isString());
|
||||
|
||||
RootedLinearString linear(cx, args[0].toString()->ensureLinear(cx));
|
||||
if (!linear)
|
||||
return false;
|
||||
|
||||
const char* locale = CaseMappingLocale(cx, args[1].toString());
|
||||
if (!locale)
|
||||
return false;
|
||||
|
||||
// Call String.prototype.toLowerCase() for language independent casing.
|
||||
if (intl::StringsAreEqual(locale, "")) {
|
||||
JSString* str = StringToLowerCase(cx, linear);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
args.rval().setString(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoStableStringChars inputChars(cx);
|
||||
if (!inputChars.initTwoByte(cx, linear))
|
||||
return false;
|
||||
mozilla::Range<const char16_t> input = inputChars.twoByteRange();
|
||||
|
||||
// Maximum case mapping length is three characters.
|
||||
static_assert(JSString::MAX_LENGTH < INT32_MAX / 3,
|
||||
"Case conversion doesn't overflow int32_t indices");
|
||||
|
||||
JSString* str = intl::CallICU(cx, [&input, locale](UChar* chars, int32_t size, UErrorCode* status) {
|
||||
return u_strToLower(chars, size, Char16ToUChar(input.begin().get()), input.length(),
|
||||
locale, status);
|
||||
});
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
|
|
@ -693,82 +962,192 @@ ToLowerCaseHelper(JSContext* cx, const CallArgs& args)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::str_toLowerCase(JSContext* cx, unsigned argc, Value* vp)
|
||||
static inline bool
|
||||
CanUpperCaseSpecialCasing(Latin1Char charCode)
|
||||
{
|
||||
return ToLowerCaseHelper(cx, CallArgsFromVp(argc, vp));
|
||||
// Handle U+00DF LATIN SMALL LETTER SHARP S inline, all other Latin-1
|
||||
// characters don't have special casing rules.
|
||||
MOZ_ASSERT_IF(charCode != unicode::LATIN_SMALL_LETTER_SHARP_S,
|
||||
!unicode::CanUpperCaseSpecialCasing(charCode));
|
||||
|
||||
return charCode == unicode::LATIN_SMALL_LETTER_SHARP_S;
|
||||
}
|
||||
|
||||
bool
|
||||
js::str_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp)
|
||||
static inline bool
|
||||
CanUpperCaseSpecialCasing(char16_t charCode)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
/*
|
||||
* Forcefully ignore the first (or any) argument and return toLowerCase(),
|
||||
* ECMA has reserved that argument, presumably for defining the locale.
|
||||
*/
|
||||
if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeToLowerCase) {
|
||||
RootedString str(cx, ToStringForStringFunction(cx, args.thisv()));
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
RootedValue result(cx);
|
||||
if (!cx->runtime()->localeCallbacks->localeToLowerCase(cx, str, &result))
|
||||
return false;
|
||||
|
||||
args.rval().set(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
return ToLowerCaseHelper(cx, args);
|
||||
return unicode::CanUpperCaseSpecialCasing(charCode);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
LengthUpperCaseSpecialCasing(Latin1Char charCode)
|
||||
{
|
||||
// U+00DF LATIN SMALL LETTER SHARP S is uppercased to two 'S'.
|
||||
MOZ_ASSERT(charCode == unicode::LATIN_SMALL_LETTER_SHARP_S);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
LengthUpperCaseSpecialCasing(char16_t charCode)
|
||||
{
|
||||
MOZ_ASSERT(CanUpperCaseSpecialCasing(charCode));
|
||||
|
||||
return unicode::LengthUpperCaseSpecialCasing(charCode);
|
||||
}
|
||||
|
||||
static inline void
|
||||
AppendUpperCaseSpecialCasing(char16_t charCode, Latin1Char* elements, size_t* index)
|
||||
{
|
||||
// U+00DF LATIN SMALL LETTER SHARP S is uppercased to two 'S'.
|
||||
MOZ_ASSERT(charCode == unicode::LATIN_SMALL_LETTER_SHARP_S);
|
||||
static_assert('S' <= JSString::MAX_LATIN1_CHAR, "'S' is a Latin-1 character");
|
||||
|
||||
elements[(*index)++] = 'S';
|
||||
elements[(*index)++] = 'S';
|
||||
}
|
||||
|
||||
static inline void
|
||||
AppendUpperCaseSpecialCasing(char16_t charCode, char16_t* elements, size_t* index)
|
||||
{
|
||||
unicode::AppendUpperCaseSpecialCasing(charCode, elements, index);
|
||||
}
|
||||
|
||||
// See ToLowerCaseImpl for an explanation of the parameters.
|
||||
template <typename DestChar, typename SrcChar>
|
||||
static void
|
||||
ToUpperCaseImpl(DestChar* destChars, const SrcChar* srcChars, size_t firstLowerCase, size_t length)
|
||||
static size_t
|
||||
ToUpperCaseImpl(DestChar* destChars, const SrcChar* srcChars, size_t startIndex, size_t srcLength,
|
||||
size_t destLength)
|
||||
{
|
||||
MOZ_ASSERT(firstLowerCase < length);
|
||||
static_assert(IsSame<SrcChar, Latin1Char>::value || !IsSame<DestChar, Latin1Char>::value,
|
||||
"cannot write non-Latin-1 characters into Latin-1 string");
|
||||
MOZ_ASSERT(startIndex < srcLength);
|
||||
MOZ_ASSERT(srcLength <= destLength);
|
||||
|
||||
for (size_t i = 0; i < firstLowerCase; i++)
|
||||
destChars[i] = srcChars[i];
|
||||
|
||||
for (size_t i = firstLowerCase; i < length; i++) {
|
||||
size_t j = startIndex;
|
||||
for (size_t i = startIndex; i < srcLength; i++) {
|
||||
char16_t c = srcChars[i];
|
||||
if (!IsSame<DestChar, Latin1Char>::value) {
|
||||
if (unicode::IsLeadSurrogate(c) && i + 1 < length) {
|
||||
if (unicode::IsLeadSurrogate(c) && i + 1 < srcLength) {
|
||||
char16_t trail = srcChars[i + 1];
|
||||
if (unicode::IsTrailSurrogate(trail)) {
|
||||
trail = unicode::ToUpperCaseNonBMPTrail(c, trail);
|
||||
destChars[i] = c;
|
||||
destChars[i + 1] = trail;
|
||||
destChars[j++] = c;
|
||||
destChars[j++] = trail;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (MOZ_UNLIKELY(c > 0x7f && CanUpperCaseSpecialCasing(static_cast<SrcChar>(c)))) {
|
||||
// Return if the output buffer is too small.
|
||||
if (srcLength == destLength)
|
||||
return i;
|
||||
|
||||
AppendUpperCaseSpecialCasing(c, destChars, &j);
|
||||
continue;
|
||||
}
|
||||
|
||||
c = unicode::ToUpperCase(c);
|
||||
MOZ_ASSERT_IF((IsSame<DestChar, Latin1Char>::value), c <= JSString::MAX_LATIN1_CHAR);
|
||||
destChars[i] = c;
|
||||
destChars[j++] = c;
|
||||
}
|
||||
|
||||
destChars[length] = '\0';
|
||||
MOZ_ASSERT(j == destLength);
|
||||
destChars[destLength] = '\0';
|
||||
|
||||
return srcLength;
|
||||
}
|
||||
|
||||
// Explicit instantiation so we don't hit the static_assert from above.
|
||||
static bool
|
||||
ToUpperCaseImpl(Latin1Char* destChars, const char16_t* srcChars, size_t startIndex,
|
||||
size_t srcLength, size_t destLength)
|
||||
{
|
||||
MOZ_ASSERT_UNREACHABLE("cannot write non-Latin-1 characters into Latin-1 string");
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
static size_t
|
||||
ToUpperCaseLength(const CharT* chars, size_t startIndex, size_t length)
|
||||
{
|
||||
size_t upperLength = length;
|
||||
for (size_t i = startIndex; i < length; i++) {
|
||||
char16_t c = chars[i];
|
||||
|
||||
if (c > 0x7f && CanUpperCaseSpecialCasing(static_cast<CharT>(c)))
|
||||
upperLength += LengthUpperCaseSpecialCasing(static_cast<CharT>(c)) - 1;
|
||||
}
|
||||
return upperLength;
|
||||
}
|
||||
|
||||
template <typename DestChar, typename SrcChar>
|
||||
static inline void
|
||||
CopyChars(DestChar* destChars, const SrcChar* srcChars, size_t length)
|
||||
{
|
||||
static_assert(!IsSame<DestChar, SrcChar>::value, "PodCopy is used for the same type case");
|
||||
for (size_t i = 0; i < length; i++)
|
||||
destChars[i] = srcChars[i];
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
static inline void
|
||||
CopyChars(CharT* destChars, const CharT* srcChars, size_t length)
|
||||
{
|
||||
PodCopy(destChars, srcChars, length);
|
||||
}
|
||||
|
||||
template <typename DestChar, typename SrcChar>
|
||||
static inline UniquePtr<DestChar[], JS::FreePolicy>
|
||||
ToUpperCase(JSContext* cx, const SrcChar* chars, size_t startIndex, size_t length,
|
||||
size_t* resultLength)
|
||||
{
|
||||
MOZ_ASSERT(startIndex < length);
|
||||
|
||||
using DestCharPtr = UniquePtr<DestChar[], JS::FreePolicy>;
|
||||
|
||||
*resultLength = length;
|
||||
DestCharPtr buf = cx->make_pod_array<DestChar>(length + 1);
|
||||
if (!buf)
|
||||
return buf;
|
||||
|
||||
CopyChars(buf.get(), chars, startIndex);
|
||||
|
||||
size_t readChars = ToUpperCaseImpl(buf.get(), chars, startIndex, length, length);
|
||||
if (readChars < length) {
|
||||
size_t actualLength = ToUpperCaseLength(chars, readChars, length);
|
||||
|
||||
*resultLength = actualLength;
|
||||
DestCharPtr buf2 = ReallocChars(cx, Move(buf), length + 1, actualLength + 1);
|
||||
if (!buf2)
|
||||
return buf2;
|
||||
|
||||
buf = Move(buf2);
|
||||
|
||||
MOZ_ALWAYS_TRUE(length ==
|
||||
ToUpperCaseImpl(buf.get(), chars, readChars, length, actualLength));
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
static JSString*
|
||||
ToUpperCase(JSContext* cx, JSLinearString* str)
|
||||
{
|
||||
typedef UniquePtr<Latin1Char[], JS::FreePolicy> Latin1CharPtr;
|
||||
typedef UniquePtr<char16_t[], JS::FreePolicy> TwoByteCharPtr;
|
||||
using Latin1CharPtr = UniquePtr<Latin1Char[], JS::FreePolicy>;
|
||||
using TwoByteCharPtr = UniquePtr<char16_t[], JS::FreePolicy>;
|
||||
|
||||
mozilla::MaybeOneOf<Latin1CharPtr, TwoByteCharPtr> newChars;
|
||||
size_t length = str->length();
|
||||
const size_t length = str->length();
|
||||
size_t resultLength;
|
||||
{
|
||||
AutoCheckCannotGC nogc;
|
||||
const CharT* chars = str->chars<CharT>(nogc);
|
||||
|
||||
// Look for the first lower case character.
|
||||
// Look for the first character that changes when uppercased.
|
||||
size_t i = 0;
|
||||
for (; i < length; i++) {
|
||||
char16_t c = chars[i];
|
||||
|
|
@ -786,21 +1165,33 @@ ToUpperCase(JSContext* cx, JSLinearString* str)
|
|||
}
|
||||
if (unicode::CanUpperCase(c))
|
||||
break;
|
||||
if (MOZ_UNLIKELY(c > 0x7f && CanUpperCaseSpecialCasing(static_cast<CharT>(c))))
|
||||
break;
|
||||
}
|
||||
|
||||
// If all characters are upper case, return the input string.
|
||||
// If no character needs to change, return the input string.
|
||||
if (i == length)
|
||||
return str;
|
||||
|
||||
// If the string is Latin1, check if it contains the MICRO SIGN (0xb5)
|
||||
// or SMALL LETTER Y WITH DIAERESIS (0xff) character. The corresponding
|
||||
// upper case characters are not in the Latin1 range.
|
||||
// The string changes when uppercased, so we must create a new string.
|
||||
// Can it be Latin-1?
|
||||
//
|
||||
// If the original string is Latin-1, it can -- unless the string
|
||||
// contains U+00B5 MICRO SIGN or U+00FF SMALL LETTER Y WITH DIAERESIS,
|
||||
// the only Latin-1 codepoints that don't uppercase within Latin-1.
|
||||
// Search for those codepoints to decide whether the new string can be
|
||||
// Latin-1.
|
||||
// If the original string is a two-byte string, its uppercase form is
|
||||
// so rarely Latin-1 that we don't even consider creating a new
|
||||
// Latin-1 string.
|
||||
bool resultIsLatin1;
|
||||
if (IsSame<CharT, Latin1Char>::value) {
|
||||
resultIsLatin1 = true;
|
||||
for (size_t j = i; j < length; j++) {
|
||||
Latin1Char c = chars[j];
|
||||
if (c == 0xb5 || c == 0xff) {
|
||||
if (c == unicode::MICRO_SIGN ||
|
||||
c == unicode::LATIN_SMALL_LETTER_Y_WITH_DIAERESIS)
|
||||
{
|
||||
MOZ_ASSERT(unicode::ToUpperCase(c) > JSString::MAX_LATIN1_CHAR);
|
||||
resultIsLatin1 = false;
|
||||
break;
|
||||
|
|
@ -813,31 +1204,29 @@ ToUpperCase(JSContext* cx, JSLinearString* str)
|
|||
}
|
||||
|
||||
if (resultIsLatin1) {
|
||||
Latin1CharPtr buf = cx->make_pod_array<Latin1Char>(length + 1);
|
||||
Latin1CharPtr buf = ToUpperCase<Latin1Char>(cx, chars, i, length, &resultLength);
|
||||
if (!buf)
|
||||
return nullptr;
|
||||
|
||||
ToUpperCaseImpl(buf.get(), chars, i, length);
|
||||
newChars.construct<Latin1CharPtr>(Move(buf));
|
||||
} else {
|
||||
TwoByteCharPtr buf = cx->make_pod_array<char16_t>(length + 1);
|
||||
TwoByteCharPtr buf = ToUpperCase<char16_t>(cx, chars, i, length, &resultLength);
|
||||
if (!buf)
|
||||
return nullptr;
|
||||
|
||||
ToUpperCaseImpl(buf.get(), chars, i, length);
|
||||
newChars.construct<TwoByteCharPtr>(Move(buf));
|
||||
}
|
||||
}
|
||||
|
||||
JSString* res;
|
||||
if (newChars.constructed<Latin1CharPtr>()) {
|
||||
res = NewStringDontDeflate<CanGC>(cx, newChars.ref<Latin1CharPtr>().get(), length);
|
||||
res = NewStringDontDeflate<CanGC>(cx, newChars.ref<Latin1CharPtr>().get(), resultLength);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
|
||||
mozilla::Unused << newChars.ref<Latin1CharPtr>().release();
|
||||
} else {
|
||||
res = NewStringDontDeflate<CanGC>(cx, newChars.ref<TwoByteCharPtr>().get(), length);
|
||||
res = NewStringDontDeflate<CanGC>(cx, newChars.ref<TwoByteCharPtr>().get(), resultLength);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
|
||||
|
|
@ -847,57 +1236,79 @@ ToUpperCase(JSContext* cx, JSLinearString* str)
|
|||
return res;
|
||||
}
|
||||
|
||||
static bool
|
||||
ToUpperCaseHelper(JSContext* cx, const CallArgs& args)
|
||||
JSString*
|
||||
js::StringToUpperCase(JSContext* cx, HandleLinearString string)
|
||||
{
|
||||
RootedString str(cx, ToStringForStringFunction(cx, args.thisv()));
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
JSLinearString* linear = str->ensureLinear(cx);
|
||||
if (!linear)
|
||||
return false;
|
||||
|
||||
if (linear->hasLatin1Chars())
|
||||
str = ToUpperCase<Latin1Char>(cx, linear);
|
||||
else
|
||||
str = ToUpperCase<char16_t>(cx, linear);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
args.rval().setString(str);
|
||||
return true;
|
||||
if (string->hasLatin1Chars())
|
||||
return ToUpperCase<Latin1Char>(cx, string);
|
||||
return ToUpperCase<char16_t>(cx, string);
|
||||
}
|
||||
|
||||
bool
|
||||
js::str_toUpperCase(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
return ToUpperCaseHelper(cx, CallArgsFromVp(argc, vp));
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
RootedString str(cx, ToStringForStringFunction(cx, args.thisv()));
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
RootedLinearString linear(cx, str->ensureLinear(cx));
|
||||
if (!linear)
|
||||
return false;
|
||||
|
||||
JSString* result = StringToUpperCase(cx, linear);
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
args.rval().setString(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::str_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp)
|
||||
js::intl_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
MOZ_ASSERT(args[0].isString());
|
||||
MOZ_ASSERT(args[1].isString());
|
||||
|
||||
/*
|
||||
* Forcefully ignore the first (or any) argument and return toUpperCase(),
|
||||
* ECMA has reserved that argument, presumably for defining the locale.
|
||||
*/
|
||||
if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeToUpperCase) {
|
||||
RootedString str(cx, ToStringForStringFunction(cx, args.thisv()));
|
||||
RootedLinearString linear(cx, args[0].toString()->ensureLinear(cx));
|
||||
if (!linear)
|
||||
return false;
|
||||
|
||||
const char* locale = CaseMappingLocale(cx, args[1].toString());
|
||||
if (!locale)
|
||||
return false;
|
||||
|
||||
// Call String.prototype.toUpperCase() for language independent casing.
|
||||
if (intl::StringsAreEqual(locale, "")) {
|
||||
JSString* str = StringToUpperCase(cx, linear);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
RootedValue result(cx);
|
||||
if (!cx->runtime()->localeCallbacks->localeToUpperCase(cx, str, &result))
|
||||
return false;
|
||||
|
||||
args.rval().set(result);
|
||||
args.rval().setString(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
return ToUpperCaseHelper(cx, args);
|
||||
AutoStableStringChars inputChars(cx);
|
||||
if (!inputChars.initTwoByte(cx, linear))
|
||||
return false;
|
||||
mozilla::Range<const char16_t> input = inputChars.twoByteRange();
|
||||
|
||||
// Maximum case mapping length is three characters.
|
||||
static_assert(JSString::MAX_LENGTH < INT32_MAX / 3,
|
||||
"Case conversion doesn't overflow int32_t indices");
|
||||
|
||||
JSString* str = intl::CallICU(cx, [&input, locale](UChar* chars, int32_t size, UErrorCode* status) {
|
||||
return u_strToUpper(chars, size, Char16ToUChar(input.begin().get()), input.length(),
|
||||
locale, status);
|
||||
});
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
args.rval().setString(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ES2017 21.1.3.12. */
|
||||
|
|
@ -944,7 +1355,7 @@ js::str_normalize(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!linear)
|
||||
return false;
|
||||
|
||||
// Latin1 strings are already in Normalization Form C.
|
||||
// Latin-1 strings are already in Normalization Form C.
|
||||
if (form == NFC && linear->hasLatin1Chars()) {
|
||||
// Step 7.
|
||||
args.rval().setString(str);
|
||||
|
|
@ -1359,7 +1770,7 @@ StringMatch(const TextChar* text, uint32_t textLen, const PatChar* pat, uint32_t
|
|||
/*
|
||||
* For big patterns with large potential overlap we want the SIMD-optimized
|
||||
* speed of memcmp. For small patterns, a simple loop is faster. We also can't
|
||||
* use memcmp if one of the strings is TwoByte and the other is Latin1.
|
||||
* use memcmp if one of the strings is TwoByte and the other is Latin-1.
|
||||
*
|
||||
* FIXME: Linux memcmp performance is sad and the manual loop is faster.
|
||||
*/
|
||||
|
|
@ -1555,7 +1966,7 @@ RopeMatch(JSContext* cx, JSRope* text, JSLinearString* pat, int* match)
|
|||
* need to build the list of leaf nodes. Do both here: iterate over the
|
||||
* nodes so long as there are not too many.
|
||||
*
|
||||
* We also don't use rope matching if the rope contains both Latin1 and
|
||||
* We also don't use rope matching if the rope contains both Latin-1 and
|
||||
* TwoByte nodes, to simplify the match algorithm.
|
||||
*/
|
||||
{
|
||||
|
|
@ -2890,8 +3301,8 @@ static const JSFunctionSpec string_methods[] = {
|
|||
JS_FN("trimStart", str_trimStart, 0,0),
|
||||
JS_FN("trimRight", str_trimEnd, 0,0),
|
||||
JS_FN("trimEnd", str_trimEnd, 0,0),
|
||||
JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,0),
|
||||
JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,0),
|
||||
JS_SELF_HOSTED_FN("toLocaleLowerCase", "String_toLocaleLowerCase", 0,0),
|
||||
JS_SELF_HOSTED_FN("toLocaleUpperCase", "String_toLocaleUpperCase", 0,0),
|
||||
JS_SELF_HOSTED_FN("localeCompare", "String_localeCompare", 1,0),
|
||||
JS_SELF_HOSTED_FN("repeat", "String_repeat", 1,0),
|
||||
JS_FN("normalize", str_normalize, 0,0),
|
||||
|
|
@ -3000,7 +3411,7 @@ js::str_fromCharCode(JSContext* cx, unsigned argc, Value* vp)
|
|||
// string (thin or fat) and so we don't need to malloc the chars. (We could
|
||||
// cover some cases where args.length() goes up to
|
||||
// JSFatInlineString::MAX_LENGTH_LATIN1 if we also checked if the chars are
|
||||
// all Latin1, but it doesn't seem worth the effort.)
|
||||
// all Latin-1, but it doesn't seem worth the effort.)
|
||||
if (args.length() <= JSFatInlineString::MAX_LENGTH_TWO_BYTE)
|
||||
return str_fromCharCode_few_args(cx, args);
|
||||
|
||||
|
|
@ -3143,7 +3554,7 @@ js::str_fromCodePoint(JSContext* cx, unsigned argc, Value* vp)
|
|||
// string (thin or fat) and so we don't need to malloc the chars. (We could
|
||||
// cover some cases where |args.length()| goes up to
|
||||
// JSFatInlineString::MAX_LENGTH_LATIN1 / 2 if we also checked if the chars
|
||||
// are all Latin1, but it doesn't seem worth the effort.)
|
||||
// are all Latin-1, but it doesn't seem worth the effort.)
|
||||
if (args.length() <= JSFatInlineString::MAX_LENGTH_TWO_BYTE / 2)
|
||||
return str_fromCodePoint_few_args(cx, args);
|
||||
|
||||
|
|
|
|||
|
|
@ -371,11 +371,24 @@ str_trimStart(JSContext* cx, unsigned argc, Value* vp);
|
|||
extern bool
|
||||
str_trimEnd(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
extern bool
|
||||
str_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp);
|
||||
/**
|
||||
* Returns the input string converted to lower case based on the language
|
||||
* specific case mappings for the input locale.
|
||||
*
|
||||
* Usage: lowerCase = intl_toLocaleLowerCase(string, locale)
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Returns the input string converted to upper case based on the language
|
||||
* specific case mappings for the input locale.
|
||||
*
|
||||
* Usage: upperCase = intl_toLocaleUpperCase(string, locale)
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
extern bool
|
||||
str_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
extern bool
|
||||
str_normalize(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
|
@ -480,6 +493,12 @@ JSString*
|
|||
str_replaceAll_string_raw(JSContext* cx, HandleString string, HandleString pattern,
|
||||
HandleString replacement);
|
||||
|
||||
extern JSString*
|
||||
StringToLowerCase(JSContext* cx, HandleLinearString string);
|
||||
|
||||
extern JSString*
|
||||
StringToUpperCase(JSContext* cx, HandleLinearString string);
|
||||
|
||||
extern bool
|
||||
StringConstructor(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ WeakMapBase::restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps)
|
|||
{
|
||||
for (WeakMapSet::Range r = markedWeakMaps.all(); !r.empty(); r.popFront()) {
|
||||
WeakMapBase* map = r.front();
|
||||
MOZ_ASSERT(map->zone->isGCMarking());
|
||||
MOZ_ASSERT(map->zone->shouldMarkInZone());
|
||||
MOZ_ASSERT(!map->marked);
|
||||
map->marked = true;
|
||||
}
|
||||
|
|
@ -144,7 +144,7 @@ ObjectValueMap::findZoneEdges()
|
|||
if (!delegate)
|
||||
continue;
|
||||
Zone* delegateZone = delegate->zone();
|
||||
if (delegateZone == zone || !delegateZone->isGCMarking())
|
||||
if (delegateZone == zone || !delegateZone->shouldMarkInZone())
|
||||
continue;
|
||||
if (!delegateZone->gcZoneGroupEdges.put(key->zone()))
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ EXPORTS.js += [
|
|||
'../public/Proxy.h',
|
||||
'../public/Realm.h',
|
||||
'../public/RequiredDefines.h',
|
||||
'../public/Result.h',
|
||||
'../public/RootingAPI.h',
|
||||
'../public/SliceBudget.h',
|
||||
'../public/StructuredClone.h',
|
||||
|
|
@ -117,6 +118,9 @@ main_deunified_sources = [
|
|||
'builtin/intl/CommonFunctions.cpp',
|
||||
'builtin/intl/DateTimeFormat.cpp',
|
||||
'builtin/intl/IntlObject.cpp',
|
||||
'builtin/intl/LanguageTag.cpp',
|
||||
'builtin/intl/LanguageTagGenerated.cpp',
|
||||
'builtin/intl/Locale.cpp',
|
||||
'builtin/intl/NumberFormat.cpp',
|
||||
'builtin/intl/PluralRules.cpp',
|
||||
'builtin/intl/RelativeTimeFormat.cpp',
|
||||
|
|
@ -708,7 +712,6 @@ selfhosted.inputs = [
|
|||
'builtin/intl/CommonFunctions.js',
|
||||
'builtin/intl/DateTimeFormat.js',
|
||||
'builtin/intl/IntlObject.js',
|
||||
'builtin/intl/LangTagMappingsGenerated.js',
|
||||
'builtin/intl/NumberFormat.js',
|
||||
'builtin/intl/PluralRules.js',
|
||||
'builtin/intl/RelativeTimeFormat.js',
|
||||
|
|
|
|||
|
|
@ -927,6 +927,9 @@ AddIntlExtras(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!JS_DefineFunctions(cx, intl, funcs))
|
||||
return false;
|
||||
|
||||
if (!js::AddMozDateTimeFormatConstructor(cx, intl))
|
||||
return false;
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
69
js/src/tests/Intl/Collator/call.js
Normal file
69
js/src/tests/Intl/Collator/call.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
|
||||
|
||||
function IsConstructor(o) {
|
||||
try {
|
||||
new (new Proxy(o, {construct: () => ({})}));
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function IsObject(o) {
|
||||
return Object(o) === o;
|
||||
}
|
||||
|
||||
function thisValues() {
|
||||
const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsConstructor);
|
||||
|
||||
return [
|
||||
// Primitive values.
|
||||
...[undefined, null, true, "abc", Symbol(), 123],
|
||||
|
||||
// Object values.
|
||||
...[{}, [], /(?:)/, function(){}, new Proxy({}, {})],
|
||||
|
||||
// Intl objects.
|
||||
...[].concat(...intlConstructors.map(ctor => [
|
||||
// Instance of an Intl constructor.
|
||||
new ctor(),
|
||||
|
||||
// Instance of a subclassed Intl constructor.
|
||||
new class extends ctor {},
|
||||
|
||||
// Object inheriting from an Intl constructor prototype.
|
||||
Object.create(ctor.prototype),
|
||||
|
||||
// Intl object not inheriting from its default prototype.
|
||||
Object.setPrototypeOf(new ctor(), Object.prototype),
|
||||
])),
|
||||
];
|
||||
}
|
||||
|
||||
// Invoking [[Call]] for Intl.Collator always returns a new Collator instance.
|
||||
for (let thisValue of thisValues()) {
|
||||
let obj = Intl.Collator.call(thisValue);
|
||||
assertEq(Object.is(obj, thisValue), false);
|
||||
assertEq(obj instanceof Intl.Collator, true);
|
||||
|
||||
// Ensure Intl.[[FallbackSymbol]] wasn't installed on |thisValue|.
|
||||
if (IsObject(thisValue))
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
|
||||
}
|
||||
|
||||
// Intl.Collator doesn't use the legacy Intl constructor compromise semantics.
|
||||
for (let thisValue of thisValues()) {
|
||||
// Ensure instanceof operator isn't invoked for Intl.Collator.
|
||||
Object.defineProperty(Intl.Collator, Symbol.hasInstance, {
|
||||
get() {
|
||||
assertEq(false, true, "@@hasInstance operator called");
|
||||
}, configurable: true
|
||||
});
|
||||
let obj = Intl.Collator.call(thisValue);
|
||||
delete Intl.Collator[Symbol.hasInstance];
|
||||
assertEq(Object.is(obj, thisValue), false);
|
||||
assertEq(obj instanceof Intl.Collator, true);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
197
js/src/tests/Intl/Collator/caseFirst.js
Normal file
197
js/src/tests/Intl/Collator/caseFirst.js
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
|
||||
|
||||
/* 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/. */
|
||||
|
||||
// Locales which use caseFirst=off for the standard (sort) collation type.
|
||||
const defaultLocales = Intl.Collator.supportedLocalesOf(["en", "de", "es", "sv", "ar", "zh", "ja"]);
|
||||
|
||||
// Locales which use caseFirst=upper for the standard (sort) collation type.
|
||||
const upperFirstLocales = Intl.Collator.supportedLocalesOf(["cu", "da", "mt"]);
|
||||
|
||||
// Default collation for zh (pinyin) reorders "á" before "a" at secondary strength level.
|
||||
const accentReordered = ["zh"];
|
||||
|
||||
const allLocales = [...defaultLocales, ...upperFirstLocales];
|
||||
|
||||
|
||||
// Check default "caseFirst" option is resolved correctly.
|
||||
for (let locale of defaultLocales) {
|
||||
let col = new Intl.Collator(locale, {usage: "sort"});
|
||||
assertEq(col.resolvedOptions().caseFirst, "false");
|
||||
}
|
||||
for (let locale of upperFirstLocales) {
|
||||
let col = new Intl.Collator(locale, {usage: "sort"});
|
||||
assertEq(col.resolvedOptions().caseFirst, "upper");
|
||||
}
|
||||
for (let locale of allLocales) {
|
||||
let col = new Intl.Collator(locale, {usage: "search"});
|
||||
assertEq(col.resolvedOptions().caseFirst, "false");
|
||||
}
|
||||
|
||||
|
||||
const collOptions = {usage: "sort"};
|
||||
const primary = {sensitivity: "base"};
|
||||
const secondary = {sensitivity: "accent"};
|
||||
const tertiary = {sensitivity: "variant"};
|
||||
const caseLevel = {sensitivity: "case"};
|
||||
const strengths = [primary, secondary, tertiary, caseLevel];
|
||||
|
||||
// "A" is sorted after "a" when caseFirst=off is the default and strength is tertiary.
|
||||
for (let locale of defaultLocales) {
|
||||
let col = new Intl.Collator(locale, Object.assign({}, collOptions, tertiary));
|
||||
|
||||
assertEq(col.compare("A", "a"), 1);
|
||||
assertEq(col.compare("a", "A"), -1);
|
||||
}
|
||||
for (let locale of defaultLocales.filter(loc => !accentReordered.includes(loc))) {
|
||||
let col = new Intl.Collator(locale, Object.assign({}, collOptions, tertiary));
|
||||
|
||||
assertEq(col.compare("A", "á"), -1);
|
||||
assertEq(col.compare("á", "A"), 1);
|
||||
}
|
||||
|
||||
// Also sorted after "a" with the sensitivity=case collator.
|
||||
for (let locale of defaultLocales) {
|
||||
let col = new Intl.Collator(locale, Object.assign({}, collOptions, caseLevel));
|
||||
|
||||
assertEq(col.compare("A", "a"), 1);
|
||||
assertEq(col.compare("a", "A"), -1);
|
||||
|
||||
assertEq(col.compare("A", "á"), 1);
|
||||
assertEq(col.compare("á", "A"), -1);
|
||||
}
|
||||
|
||||
|
||||
// "A" is sorted before "a" when caseFirst=upper is the default and strength is tertiary.
|
||||
for (let locale of upperFirstLocales) {
|
||||
let col = new Intl.Collator(locale, Object.assign({}, collOptions, tertiary));
|
||||
|
||||
assertEq(col.compare("A", "a"), -1);
|
||||
assertEq(col.compare("a", "A"), 1);
|
||||
|
||||
assertEq(col.compare("A", "á"), -1);
|
||||
assertEq(col.compare("á", "A"), 1);
|
||||
}
|
||||
|
||||
// Also sorted before "a" with the sensitivity=case collator.
|
||||
for (let locale of upperFirstLocales) {
|
||||
let col = new Intl.Collator(locale, Object.assign({}, collOptions, caseLevel));
|
||||
|
||||
assertEq(col.compare("A", "a"), -1);
|
||||
assertEq(col.compare("a", "A"), 1);
|
||||
|
||||
assertEq(col.compare("A", "á"), -1);
|
||||
assertEq(col.compare("á", "A"), 1);
|
||||
}
|
||||
|
||||
|
||||
// caseFirst=upper doesn't change the sort order when strength is below tertiary.
|
||||
for (let locale of allLocales) {
|
||||
let col = new Intl.Collator(locale, Object.assign({}, collOptions, secondary));
|
||||
|
||||
assertEq(col.compare("A", "a"), 0);
|
||||
assertEq(col.compare("a", "A"), 0);
|
||||
}
|
||||
for (let locale of allLocales.filter(loc => !accentReordered.includes(loc))) {
|
||||
let col = new Intl.Collator(locale, Object.assign({}, collOptions, secondary));
|
||||
|
||||
assertEq(col.compare("A", "á"), -1);
|
||||
assertEq(col.compare("á", "A"), 1);
|
||||
}
|
||||
|
||||
for (let locale of allLocales) {
|
||||
let col = new Intl.Collator(locale, Object.assign({}, collOptions, primary));
|
||||
|
||||
assertEq(col.compare("A", "a"), 0);
|
||||
assertEq(col.compare("a", "A"), 0);
|
||||
|
||||
assertEq(col.compare("A", "á"), 0);
|
||||
assertEq(col.compare("á", "A"), 0);
|
||||
}
|
||||
|
||||
|
||||
// caseFirst=upper doesn't change the sort order when there's a primary difference.
|
||||
for (let locale of allLocales) {
|
||||
for (let strength of strengths) {
|
||||
let col = new Intl.Collator(locale, Object.assign({}, collOptions, strength));
|
||||
|
||||
assertEq(col.compare("A", "b"), -1);
|
||||
assertEq(col.compare("a", "B"), -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// caseFirst set through Unicode extension tag.
|
||||
for (let locale of allLocales) {
|
||||
let colKfFalse = new Intl.Collator(locale + "-u-kf-false", {});
|
||||
let colKfLower = new Intl.Collator(locale + "-u-kf-lower", {});
|
||||
let colKfUpper = new Intl.Collator(locale + "-u-kf-upper", {});
|
||||
|
||||
assertEq(colKfFalse.resolvedOptions().caseFirst, "false");
|
||||
assertEq(colKfFalse.compare("A", "a"), 1);
|
||||
assertEq(colKfFalse.compare("a", "A"), -1);
|
||||
|
||||
assertEq(colKfLower.resolvedOptions().caseFirst, "lower");
|
||||
assertEq(colKfLower.compare("A", "a"), 1);
|
||||
assertEq(colKfLower.compare("a", "A"), -1);
|
||||
|
||||
assertEq(colKfUpper.resolvedOptions().caseFirst, "upper");
|
||||
assertEq(colKfUpper.compare("A", "a"), -1);
|
||||
assertEq(colKfUpper.compare("a", "A"), 1);
|
||||
}
|
||||
|
||||
|
||||
// caseFirst set through options value.
|
||||
for (let locale of allLocales) {
|
||||
let colKfFalse = new Intl.Collator(locale, {caseFirst: "false"});
|
||||
let colKfLower = new Intl.Collator(locale, {caseFirst: "lower"});
|
||||
let colKfUpper = new Intl.Collator(locale, {caseFirst: "upper"});
|
||||
|
||||
assertEq(colKfFalse.resolvedOptions().caseFirst, "false");
|
||||
assertEq(colKfFalse.compare("A", "a"), 1);
|
||||
assertEq(colKfFalse.compare("a", "A"), -1);
|
||||
|
||||
assertEq(colKfLower.resolvedOptions().caseFirst, "lower");
|
||||
assertEq(colKfLower.compare("A", "a"), 1);
|
||||
assertEq(colKfLower.compare("a", "A"), -1);
|
||||
|
||||
assertEq(colKfUpper.resolvedOptions().caseFirst, "upper");
|
||||
assertEq(colKfUpper.compare("A", "a"), -1);
|
||||
assertEq(colKfUpper.compare("a", "A"), 1);
|
||||
}
|
||||
|
||||
|
||||
// Test Unicode extension tag and options value, the latter should win.
|
||||
for (let locale of allLocales) {
|
||||
let colKfFalse = new Intl.Collator(locale + "-u-kf-upper", {caseFirst: "false"});
|
||||
let colKfLower = new Intl.Collator(locale + "-u-kf-upper", {caseFirst: "lower"});
|
||||
let colKfUpper = new Intl.Collator(locale + "-u-kf-lower", {caseFirst: "upper"});
|
||||
|
||||
assertEq(colKfFalse.resolvedOptions().caseFirst, "false");
|
||||
assertEq(colKfFalse.compare("A", "a"), 1);
|
||||
assertEq(colKfFalse.compare("a", "A"), -1);
|
||||
|
||||
assertEq(colKfLower.resolvedOptions().caseFirst, "lower");
|
||||
assertEq(colKfLower.compare("A", "a"), 1);
|
||||
assertEq(colKfLower.compare("a", "A"), -1);
|
||||
|
||||
assertEq(colKfUpper.resolvedOptions().caseFirst, "upper");
|
||||
assertEq(colKfUpper.compare("A", "a"), -1);
|
||||
assertEq(colKfUpper.compare("a", "A"), 1);
|
||||
}
|
||||
|
||||
// Ensure languages are properly detected when additional subtags are present.
|
||||
if (Intl.Collator.supportedLocalesOf("da").length !== 0) {
|
||||
assertEq(new Intl.Collator("da-DK", {usage: "sort"}).resolvedOptions().caseFirst, "upper");
|
||||
assertEq(new Intl.Collator("da-Latn-DK", {usage: "sort"}).resolvedOptions().caseFirst, "upper");
|
||||
}
|
||||
if (Intl.Collator.supportedLocalesOf("mt").length !== 0) {
|
||||
assertEq(new Intl.Collator("mt-MT", {usage: "sort"}).resolvedOptions().caseFirst, "upper");
|
||||
assertEq(new Intl.Collator("mt-Latn-MT", {usage: "sort"}).resolvedOptions().caseFirst, "upper");
|
||||
}
|
||||
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0, "ok");
|
||||
167
js/src/tests/Intl/DateTimeFormat/call.js
Normal file
167
js/src/tests/Intl/DateTimeFormat/call.js
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
|
||||
|
||||
function IsConstructor(o) {
|
||||
try {
|
||||
new (new Proxy(o, {construct: () => ({})}));
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function IsObject(o) {
|
||||
return Object(o) === o;
|
||||
}
|
||||
|
||||
function thisValues() {
|
||||
const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsConstructor);
|
||||
|
||||
return [
|
||||
// Primitive values.
|
||||
...[undefined, null, true, "abc", Symbol(), 123],
|
||||
|
||||
// Object values.
|
||||
...[{}, [], /(?:)/, function(){}, new Proxy({}, {})],
|
||||
|
||||
// Intl objects.
|
||||
...[].concat(...intlConstructors.map(ctor => [
|
||||
// Instance of an Intl constructor.
|
||||
new ctor(),
|
||||
|
||||
// Instance of a subclassed Intl constructor.
|
||||
new class extends ctor {},
|
||||
|
||||
// Object inheriting from an Intl constructor prototype.
|
||||
Object.create(ctor.prototype),
|
||||
|
||||
// Intl object not inheriting from its default prototype.
|
||||
Object.setPrototypeOf(new ctor(), Object.prototype),
|
||||
])),
|
||||
];
|
||||
}
|
||||
|
||||
const intlFallbackSymbol = Object.getOwnPropertySymbols(Intl.DateTimeFormat.call(Object.create(Intl.DateTimeFormat.prototype)))[0];
|
||||
|
||||
// Invoking [[Call]] for Intl.DateTimeFormat returns a new instance unless called
|
||||
// with an instance inheriting from Intl.DateTimeFormat.prototype.
|
||||
for (let thisValue of thisValues()) {
|
||||
let obj = Intl.DateTimeFormat.call(thisValue);
|
||||
|
||||
if (!Intl.DateTimeFormat.prototype.isPrototypeOf(thisValue)) {
|
||||
assertEq(Object.is(obj, thisValue), false);
|
||||
assertEq(obj instanceof Intl.DateTimeFormat, true);
|
||||
if (IsObject(thisValue))
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
|
||||
} else {
|
||||
assertEq(Object.is(obj, thisValue), true);
|
||||
assertEq(obj instanceof Intl.DateTimeFormat, true);
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]);
|
||||
}
|
||||
}
|
||||
|
||||
// Intl.DateTimeFormat uses the legacy Intl constructor compromise semantics.
|
||||
// - Test when InstanceofOperator(thisValue, %DateTimeFormat%) returns true.
|
||||
for (let thisValue of thisValues()) {
|
||||
let hasInstanceCalled = false;
|
||||
Object.defineProperty(Intl.DateTimeFormat, Symbol.hasInstance, {
|
||||
value() {
|
||||
assertEq(hasInstanceCalled, false);
|
||||
hasInstanceCalled = true;
|
||||
return true;
|
||||
}, configurable: true
|
||||
});
|
||||
if (!IsObject(thisValue)) {
|
||||
// A TypeError is thrown when Intl.DateTimeFormat tries to install the
|
||||
// [[FallbackSymbol]] property on |thisValue|.
|
||||
assertThrowsInstanceOf(() => Intl.DateTimeFormat.call(thisValue), TypeError);
|
||||
delete Intl.DateTimeFormat[Symbol.hasInstance];
|
||||
} else {
|
||||
let obj = Intl.DateTimeFormat.call(thisValue);
|
||||
delete Intl.DateTimeFormat[Symbol.hasInstance];
|
||||
assertEq(Object.is(obj, thisValue), true);
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]);
|
||||
}
|
||||
assertEq(hasInstanceCalled, true);
|
||||
}
|
||||
// - Test when InstanceofOperator(thisValue, %DateTimeFormat%) returns false.
|
||||
for (let thisValue of thisValues()) {
|
||||
let hasInstanceCalled = false;
|
||||
Object.defineProperty(Intl.DateTimeFormat, Symbol.hasInstance, {
|
||||
value() {
|
||||
assertEq(hasInstanceCalled, false);
|
||||
hasInstanceCalled = true;
|
||||
return false;
|
||||
}, configurable: true
|
||||
});
|
||||
let obj = Intl.DateTimeFormat.call(thisValue);
|
||||
delete Intl.DateTimeFormat[Symbol.hasInstance];
|
||||
assertEq(Object.is(obj, thisValue), false);
|
||||
assertEq(obj instanceof Intl.DateTimeFormat, true);
|
||||
if (IsObject(thisValue))
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
|
||||
assertEq(hasInstanceCalled, true);
|
||||
}
|
||||
|
||||
// Throws an error when attempting to install [[FallbackSymbol]] twice.
|
||||
{
|
||||
let thisValue = Object.create(Intl.DateTimeFormat.prototype);
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
|
||||
|
||||
assertEq(Intl.DateTimeFormat.call(thisValue), thisValue);
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]);
|
||||
|
||||
assertThrowsInstanceOf(() => Intl.DateTimeFormat.call(thisValue), TypeError);
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]);
|
||||
}
|
||||
|
||||
// Throws an error when the thisValue is non-extensible.
|
||||
{
|
||||
let thisValue = Object.create(Intl.DateTimeFormat.prototype);
|
||||
Object.preventExtensions(thisValue);
|
||||
|
||||
assertThrowsInstanceOf(() => Intl.DateTimeFormat.call(thisValue), TypeError);
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
|
||||
}
|
||||
|
||||
// [[FallbackSymbol]] is installed as a frozen property holding an Intl.DateTimeFormat instance.
|
||||
{
|
||||
let thisValue = Object.create(Intl.DateTimeFormat.prototype);
|
||||
Intl.DateTimeFormat.call(thisValue);
|
||||
|
||||
let desc = Object.getOwnPropertyDescriptor(thisValue, intlFallbackSymbol);
|
||||
assertEq(desc !== undefined, true);
|
||||
assertEq(desc.writable, false);
|
||||
assertEq(desc.enumerable, false);
|
||||
assertEq(desc.configurable, false);
|
||||
assertEq(desc.value instanceof Intl.DateTimeFormat, true);
|
||||
}
|
||||
|
||||
// Ensure [[FallbackSymbol]] is installed last by changing the [[Prototype]]
|
||||
// during initialization.
|
||||
{
|
||||
let thisValue = {};
|
||||
let options = {
|
||||
get hour12() {
|
||||
Object.setPrototypeOf(thisValue, Intl.DateTimeFormat.prototype);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let obj = Intl.DateTimeFormat.call(thisValue, undefined, options);
|
||||
assertEq(Object.is(obj, thisValue), true);
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]);
|
||||
}
|
||||
{
|
||||
let thisValue = Object.create(Intl.DateTimeFormat.prototype);
|
||||
let options = {
|
||||
get hour12() {
|
||||
Object.setPrototypeOf(thisValue, Object.prototype);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let obj = Intl.DateTimeFormat.call(thisValue, undefined, options);
|
||||
assertEq(Object.is(obj, thisValue), false);
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
145
js/src/tests/Intl/DateTimeFormat/hourCycle.js
Normal file
145
js/src/tests/Intl/DateTimeFormat/hourCycle.js
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
|
||||
|
||||
const hourCycleToH12Map = {
|
||||
"h11": true,
|
||||
"h12": true,
|
||||
"h23": false,
|
||||
"h24": false,
|
||||
};
|
||||
|
||||
for (const key of Object.keys(hourCycleToH12Map)) {
|
||||
const langTag = "en-US";
|
||||
const loc = `${langTag}-u-hc-${key}`;
|
||||
|
||||
const dtf = new Intl.DateTimeFormat(loc, {hour: "numeric"});
|
||||
const dtf2 = new Intl.DateTimeFormat(langTag, {hour: "numeric", hourCycle: key});
|
||||
assertEq(dtf.resolvedOptions().hourCycle, dtf2.resolvedOptions().hourCycle);
|
||||
}
|
||||
|
||||
|
||||
/* Legacy hour12 compatibility */
|
||||
|
||||
// When constructed with hourCycle option, resolvedOptions' hour12 is correct.
|
||||
for (const key of Object.keys(hourCycleToH12Map)) {
|
||||
const dtf = new Intl.DateTimeFormat("en-US", {hour: "numeric", hourCycle: key});
|
||||
assertEq(dtf.resolvedOptions().hour12, hourCycleToH12Map[key]);
|
||||
}
|
||||
|
||||
// When constructed with hour12 option, resolvedOptions' hourCycle is correct
|
||||
for (const [key, value] of Object.entries(hourCycleToH12Map)) {
|
||||
const dtf = new Intl.DateTimeFormat("en-US", {hour: "numeric", hour12: value});
|
||||
assertEq(hourCycleToH12Map[dtf.resolvedOptions().hourCycle], value);
|
||||
}
|
||||
|
||||
// When constructed with both hour12 and hourCycle options that don't match
|
||||
// hour12 takes a precedence.
|
||||
for (const [key, value] of Object.entries(hourCycleToH12Map)) {
|
||||
const dtf = new Intl.DateTimeFormat("en-US", {
|
||||
hour: "numeric",
|
||||
hourCycle: key,
|
||||
hour12: !value
|
||||
});
|
||||
assertEq(hourCycleToH12Map[dtf.resolvedOptions().hourCycle], !value);
|
||||
assertEq(dtf.resolvedOptions().hour12, !value);
|
||||
}
|
||||
|
||||
// When constructed with hourCycle as extkey, resolvedOptions' hour12 is correct.
|
||||
for (const [key, value] of Object.entries(hourCycleToH12Map)) {
|
||||
const langTag = "en-US";
|
||||
const loc = `${langTag}-u-hc-${key}`;
|
||||
|
||||
const dtf = new Intl.DateTimeFormat(loc, {hour: "numeric"});
|
||||
assertEq(dtf.resolvedOptions().hour12, value);
|
||||
}
|
||||
|
||||
const expectedValuesENUS = {
|
||||
h11: "0 AM",
|
||||
h12: "12 AM",
|
||||
h23: "00",
|
||||
h24: "24"
|
||||
};
|
||||
|
||||
const exampleDate = new Date(2017, 10-1, 10, 0);
|
||||
for (const [key, val] of Object.entries(expectedValuesENUS)) {
|
||||
assertEq(
|
||||
Intl.DateTimeFormat("en-US", {hour: "numeric", hourCycle: key}).format(exampleDate),
|
||||
val
|
||||
);
|
||||
}
|
||||
|
||||
const invalidHourCycleValues = [
|
||||
"h5",
|
||||
"h0",
|
||||
"h28",
|
||||
"f28",
|
||||
"23",
|
||||
];
|
||||
|
||||
for (const key of invalidHourCycleValues) {
|
||||
const langTag = "en-US";
|
||||
const loc = `${langTag}-u-hc-${key}`;
|
||||
|
||||
const dtf = new Intl.DateTimeFormat(loc, {hour: "numeric"});
|
||||
assertEq(dtf.resolvedOptions().hour12, true); // default value for en-US
|
||||
assertEq(dtf.resolvedOptions().hourCycle, "h12"); //default value for en-US
|
||||
}
|
||||
|
||||
{
|
||||
// hourCycle is not present in resolvedOptions when the formatter has no hour field
|
||||
const options = Intl.DateTimeFormat("en-US", {hourCycle:"h11"}).resolvedOptions();
|
||||
assertEq("hourCycle" in options, false);
|
||||
assertEq("hour12" in options, false);
|
||||
}
|
||||
|
||||
{
|
||||
// Make sure that hourCycle option overrides the unicode extension
|
||||
let dtf = Intl.DateTimeFormat("en-US-u-hc-h23", {hourCycle: "h24", hour: "numeric"});
|
||||
assertEq(
|
||||
dtf.resolvedOptions().hourCycle,
|
||||
"h24"
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
// Make sure that hour12 option overrides the unicode extension
|
||||
let dtf = Intl.DateTimeFormat("en-US-u-hc-h23", {hour12: true, hour: "numeric"});
|
||||
assertEq(
|
||||
dtf.resolvedOptions().hourCycle,
|
||||
"h12"
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
// Make sure that hour12 option overrides hourCycle options
|
||||
let dtf = Intl.DateTimeFormat("en-US",
|
||||
{hourCycle: "h12", hour12: false, hour: "numeric"});
|
||||
assertEq(
|
||||
dtf.resolvedOptions().hourCycle,
|
||||
"h23"
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
// Make sure that hour12 option overrides hourCycle options
|
||||
let dtf = Intl.DateTimeFormat("en-u-hc-h11", {hour: "numeric"});
|
||||
assertEq(
|
||||
dtf.resolvedOptions().locale,
|
||||
"en-u-hc-h11"
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
// Make sure that hour12 option overrides unicode extension
|
||||
let dtf = Intl.DateTimeFormat("en-u-hc-h11", {hour: "numeric", hourCycle: "h24"});
|
||||
assertEq(
|
||||
dtf.resolvedOptions().locale,
|
||||
"en"
|
||||
);
|
||||
assertEq(
|
||||
dtf.resolvedOptions().hourCycle,
|
||||
"h24"
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0, "ok");
|
||||
58
js/src/tests/Intl/DateTimeFormat/mozExtensions.js
Normal file
58
js/src/tests/Intl/DateTimeFormat/mozExtensions.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty("addIntlExtras"))
|
||||
/* 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/. */
|
||||
|
||||
// Tests the format function with a diverse set of locales and options.
|
||||
// Always use UTC to avoid dependencies on test environment.
|
||||
|
||||
let mozIntl = {};
|
||||
addIntlExtras(mozIntl);
|
||||
|
||||
// Pattern
|
||||
|
||||
var dtf = new Intl.DateTimeFormat("en-US", {pattern: "HH:mm MM/dd/YYYY"});
|
||||
var mozDtf = new mozIntl.DateTimeFormat("en-US", {pattern: "HH:mm MM/dd/YYYY"});
|
||||
|
||||
assertEq(dtf.resolvedOptions().hasOwnProperty('pattern'), false);
|
||||
assertEq(mozDtf.resolvedOptions().pattern, "HH:mm MM/dd/YYYY");
|
||||
|
||||
// Date style
|
||||
|
||||
var dtf = new Intl.DateTimeFormat("en-US", {dateStyle: 'long'});
|
||||
assertEq(mozDtf.resolvedOptions().hasOwnProperty('dateStyle'), false);
|
||||
|
||||
var mozDtf = new mozIntl.DateTimeFormat("en-US", {dateStyle: 'long'});
|
||||
assertEq(mozDtf.resolvedOptions().dateStyle, 'long');
|
||||
assertEq(mozDtf.resolvedOptions().hasOwnProperty('year'), true);
|
||||
assertEq(mozDtf.resolvedOptions().hasOwnProperty('month'), true);
|
||||
assertEq(mozDtf.resolvedOptions().hasOwnProperty('day'), true);
|
||||
|
||||
// Time style
|
||||
|
||||
var dtf = new Intl.DateTimeFormat("en-US", {timeStyle: 'long'});
|
||||
assertEq(dtf.resolvedOptions().hasOwnProperty('dateStyle'), false);
|
||||
|
||||
var mozDtf = new mozIntl.DateTimeFormat("en-US", {timeStyle: 'long'});
|
||||
assertEq(mozDtf.resolvedOptions().timeStyle, 'long');
|
||||
assertEq(mozDtf.resolvedOptions().hasOwnProperty('hour'), true);
|
||||
assertEq(mozDtf.resolvedOptions().hasOwnProperty('minute'), true);
|
||||
assertEq(mozDtf.resolvedOptions().hasOwnProperty('second'), true);
|
||||
|
||||
// Date/Time style
|
||||
|
||||
var dtf = new Intl.DateTimeFormat("en-US", {timeStyle: 'medium', dateStyle: 'medium'});
|
||||
assertEq(dtf.resolvedOptions().hasOwnProperty('dateStyle'), false);
|
||||
assertEq(dtf.resolvedOptions().hasOwnProperty('timeStyle'), false);
|
||||
|
||||
var mozDtf = new mozIntl.DateTimeFormat("en-US", {dateStyle: 'medium', timeStyle: 'medium'});
|
||||
assertEq(mozDtf.resolvedOptions().timeStyle, 'medium');
|
||||
assertEq(mozDtf.resolvedOptions().dateStyle, 'medium');
|
||||
assertEq(mozDtf.resolvedOptions().hasOwnProperty('hour'), true);
|
||||
assertEq(mozDtf.resolvedOptions().hasOwnProperty('minute'), true);
|
||||
assertEq(mozDtf.resolvedOptions().hasOwnProperty('second'), true);
|
||||
assertEq(mozDtf.resolvedOptions().hasOwnProperty('year'), true);
|
||||
assertEq(mozDtf.resolvedOptions().hasOwnProperty('month'), true);
|
||||
assertEq(mozDtf.resolvedOptions().hasOwnProperty('day'), true);
|
||||
|
||||
reportCompare(0, 0, 'ok');
|
||||
224
js/src/tests/Intl/DateTimeFormat/unwrapping.js
Normal file
224
js/src/tests/Intl/DateTimeFormat/unwrapping.js
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
|
||||
|
||||
// Test UnwrapDateTimeFormat operation.
|
||||
|
||||
const dateTimeFormatFunctions = [];
|
||||
dateTimeFormatFunctions.push(Intl.DateTimeFormat.prototype.resolvedOptions);
|
||||
dateTimeFormatFunctions.push(Object.getOwnPropertyDescriptor(Intl.DateTimeFormat.prototype, "format").get);
|
||||
dateTimeFormatFunctions.push(Intl.DateTimeFormat.prototype.formatToParts);
|
||||
|
||||
function IsConstructor(o) {
|
||||
try {
|
||||
new (new Proxy(o, {construct: () => ({})}));
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function IsObject(o) {
|
||||
return Object(o) === o;
|
||||
}
|
||||
|
||||
function intlObjects(ctor) {
|
||||
return [
|
||||
// Instance of an Intl constructor.
|
||||
new ctor(),
|
||||
|
||||
// Instance of a subclassed Intl constructor.
|
||||
new class extends ctor {},
|
||||
|
||||
// Intl object not inheriting from its default prototype.
|
||||
Object.setPrototypeOf(new ctor(), Object.prototype),
|
||||
];
|
||||
}
|
||||
|
||||
function thisValues(C) {
|
||||
const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsConstructor);
|
||||
|
||||
return [
|
||||
// Primitive values.
|
||||
...[undefined, null, true, "abc", Symbol(), 123],
|
||||
|
||||
// Object values.
|
||||
...[{}, [], /(?:)/, function(){}, new Proxy({}, {})],
|
||||
|
||||
// Intl objects.
|
||||
...[].concat(...intlConstructors.filter(ctor => ctor !== C).map(intlObjects)),
|
||||
|
||||
// Object inheriting from an Intl constructor prototype.
|
||||
...intlConstructors.map(ctor => Object.create(ctor.prototype)),
|
||||
];
|
||||
}
|
||||
|
||||
const intlFallbackSymbol = Object.getOwnPropertySymbols(Intl.DateTimeFormat.call(Object.create(Intl.DateTimeFormat.prototype)))[0];
|
||||
|
||||
// Test Intl.DateTimeFormat.prototype methods.
|
||||
for (let dateTimeFormatFunction of dateTimeFormatFunctions) {
|
||||
// Test a TypeError is thrown when the this-value isn't an initialized
|
||||
// Intl.DateTimeFormat instance.
|
||||
for (let thisValue of thisValues(Intl.DateTimeFormat)) {
|
||||
assertThrowsInstanceOf(() => dateTimeFormatFunction.call(thisValue), TypeError);
|
||||
}
|
||||
|
||||
// And test no error is thrown for initialized Intl.DateTimeFormat instances.
|
||||
for (let thisValue of intlObjects(Intl.DateTimeFormat)) {
|
||||
dateTimeFormatFunction.call(thisValue);
|
||||
}
|
||||
|
||||
// Manually add [[FallbackSymbol]] to objects and then repeat the tests from above.
|
||||
for (let thisValue of thisValues(Intl.DateTimeFormat)) {
|
||||
assertThrowsInstanceOf(() => dateTimeFormatFunction.call({
|
||||
__proto__: Intl.DateTimeFormat.prototype,
|
||||
[intlFallbackSymbol]: thisValue,
|
||||
}), TypeError);
|
||||
}
|
||||
|
||||
for (let thisValue of intlObjects(Intl.DateTimeFormat)) {
|
||||
dateTimeFormatFunction.call({
|
||||
__proto__: Intl.DateTimeFormat.prototype,
|
||||
[intlFallbackSymbol]: thisValue,
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure [[FallbackSymbol]] isn't retrieved for Intl.DateTimeFormat instances.
|
||||
for (let thisValue of intlObjects(Intl.DateTimeFormat)) {
|
||||
Object.defineProperty(thisValue, intlFallbackSymbol, {
|
||||
get() { assertEq(false, true); }
|
||||
});
|
||||
dateTimeFormatFunction.call(thisValue);
|
||||
}
|
||||
|
||||
// Ensure [[FallbackSymbol]] is only retrieved for objects inheriting from Intl.DateTimeFormat.prototype.
|
||||
for (let thisValue of thisValues(Intl.DateTimeFormat)) {
|
||||
if (!IsObject(thisValue) || Intl.DateTimeFormat.prototype.isPrototypeOf(thisValue))
|
||||
continue;
|
||||
Object.defineProperty(thisValue, intlFallbackSymbol, {
|
||||
get() { assertEq(false, true); }
|
||||
});
|
||||
assertThrowsInstanceOf(() => dateTimeFormatFunction.call(thisValue), TypeError);
|
||||
}
|
||||
|
||||
// Repeat the test from above, but also change Intl.DateTimeFormat[@@hasInstance]
|
||||
// so it always returns |null|.
|
||||
for (let thisValue of thisValues(Intl.DateTimeFormat)) {
|
||||
let hasInstanceCalled = false, symbolGetterCalled = false;
|
||||
Object.defineProperty(Intl.DateTimeFormat, Symbol.hasInstance, {
|
||||
value() {
|
||||
assertEq(hasInstanceCalled, false);
|
||||
hasInstanceCalled = true;
|
||||
return true;
|
||||
}, configurable: true
|
||||
});
|
||||
let isUndefinedOrNull = thisValue !== undefined || thisValue !== null;
|
||||
let symbolHolder;
|
||||
if (!isUndefinedOrNull) {
|
||||
symbolHolder = IsObject(thisValue) ? thisValue : Object.getPrototypeOf(thisValue);
|
||||
Object.defineProperty(symbolHolder, intlFallbackSymbol, {
|
||||
get() {
|
||||
assertEq(symbolGetterCalled, false);
|
||||
symbolGetterCalled = true;
|
||||
return null;
|
||||
}, configurable: true
|
||||
});
|
||||
}
|
||||
|
||||
assertThrowsInstanceOf(() => dateTimeFormatFunction.call(thisValue), TypeError);
|
||||
|
||||
delete Intl.DateTimeFormat[Symbol.hasInstance];
|
||||
if (!isUndefinedOrNull && !IsObject(thisValue))
|
||||
delete symbolHolder[intlFallbackSymbol];
|
||||
|
||||
assertEq(hasInstanceCalled, true);
|
||||
assertEq(symbolGetterCalled, !isUndefinedOrNull);
|
||||
}
|
||||
}
|
||||
|
||||
// Test format() returns the correct result for objects initialized as Intl.DateTimeFormat instances.
|
||||
{
|
||||
// An actual Intl.DateTimeFormat instance.
|
||||
let dateTimeFormat = new Intl.DateTimeFormat();
|
||||
|
||||
// An object initialized as a DateTimeFormat instance.
|
||||
let thisValue = Object.create(Intl.DateTimeFormat.prototype);
|
||||
Intl.DateTimeFormat.call(thisValue);
|
||||
|
||||
// Object with [[FallbackSymbol]] set to DateTimeFormat instance.
|
||||
let fakeObj = {
|
||||
__proto__: Intl.DateTimeFormat.prototype,
|
||||
[intlFallbackSymbol]: dateTimeFormat,
|
||||
};
|
||||
|
||||
for (let number of [0, Date.now(), -Date.now()]) {
|
||||
let expected = dateTimeFormat.format(number);
|
||||
assertEq(thisValue.format(number), expected);
|
||||
assertEq(thisValue[intlFallbackSymbol].format(number), expected);
|
||||
assertEq(fakeObj.format(number), expected);
|
||||
}
|
||||
}
|
||||
|
||||
// Test formatToParts() returns the correct result for objects initialized as Intl.DateTimeFormat instances.
|
||||
if ("formatToParts" in Intl.DateTimeFormat.prototype) {
|
||||
// An actual Intl.DateTimeFormat instance.
|
||||
let dateTimeFormat = new Intl.DateTimeFormat();
|
||||
|
||||
// An object initialized as a DateTimeFormat instance.
|
||||
let thisValue = Object.create(Intl.DateTimeFormat.prototype);
|
||||
Intl.DateTimeFormat.call(thisValue);
|
||||
|
||||
// Object with [[FallbackSymbol]] set to DateTimeFormat instance.
|
||||
let fakeObj = {
|
||||
__proto__: Intl.DateTimeFormat.prototype,
|
||||
[intlFallbackSymbol]: dateTimeFormat,
|
||||
};
|
||||
|
||||
function assertEqParts(actual, expected) {
|
||||
assertEq(actual.length, expected.length, "parts count mismatch");
|
||||
for (var i = 0; i < expected.length; i++) {
|
||||
assertEq(actual[i].type, expected[i].type, "type mismatch at " + i);
|
||||
assertEq(actual[i].value, expected[i].value, "value mismatch at " + i);
|
||||
}
|
||||
}
|
||||
|
||||
for (let number of [0, Date.now(), -Date.now()]) {
|
||||
let expected = dateTimeFormat.formatToParts(number);
|
||||
assertEqParts(thisValue.formatToParts(number), expected);
|
||||
assertEqParts(thisValue[intlFallbackSymbol].formatToParts(number), expected);
|
||||
assertEqParts(fakeObj.formatToParts(number), expected);
|
||||
}
|
||||
}
|
||||
|
||||
// Test resolvedOptions() returns the same results.
|
||||
{
|
||||
// An actual Intl.DateTimeFormat instance.
|
||||
let dateTimeFormat = new Intl.DateTimeFormat();
|
||||
|
||||
// An object initialized as a DateTimeFormat instance.
|
||||
let thisValue = Object.create(Intl.DateTimeFormat.prototype);
|
||||
Intl.DateTimeFormat.call(thisValue);
|
||||
|
||||
// Object with [[FallbackSymbol]] set to DateTimeFormat instance.
|
||||
let fakeObj = {
|
||||
__proto__: Intl.DateTimeFormat.prototype,
|
||||
[intlFallbackSymbol]: dateTimeFormat,
|
||||
};
|
||||
|
||||
function assertEqOptions(actual, expected) {
|
||||
actual = Object.entries(actual);
|
||||
expected = Object.entries(expected);
|
||||
|
||||
assertEq(actual.length, expected.length, "options count mismatch");
|
||||
for (var i = 0; i < expected.length; i++) {
|
||||
assertEq(actual[i][0], expected[i][0], "key mismatch at " + i);
|
||||
assertEq(actual[i][1], expected[i][1], "value mismatch at " + i);
|
||||
}
|
||||
}
|
||||
|
||||
let expected = dateTimeFormat.resolvedOptions();
|
||||
assertEqOptions(thisValue.resolvedOptions(), expected);
|
||||
assertEqOptions(thisValue[intlFallbackSymbol].resolvedOptions(), expected);
|
||||
assertEqOptions(fakeObj.resolvedOptions(), expected);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
167
js/src/tests/Intl/NumberFormat/call.js
Normal file
167
js/src/tests/Intl/NumberFormat/call.js
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
|
||||
|
||||
function IsConstructor(o) {
|
||||
try {
|
||||
new (new Proxy(o, {construct: () => ({})}));
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function IsObject(o) {
|
||||
return Object(o) === o;
|
||||
}
|
||||
|
||||
function thisValues() {
|
||||
const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsConstructor);
|
||||
|
||||
return [
|
||||
// Primitive values.
|
||||
...[undefined, null, true, "abc", Symbol(), 123],
|
||||
|
||||
// Object values.
|
||||
...[{}, [], /(?:)/, function(){}, new Proxy({}, {})],
|
||||
|
||||
// Intl objects.
|
||||
...[].concat(...intlConstructors.map(ctor => [
|
||||
// Instance of an Intl constructor.
|
||||
new ctor(),
|
||||
|
||||
// Instance of a subclassed Intl constructor.
|
||||
new class extends ctor {},
|
||||
|
||||
// Object inheriting from an Intl constructor prototype.
|
||||
Object.create(ctor.prototype),
|
||||
|
||||
// Intl object not inheriting from its default prototype.
|
||||
Object.setPrototypeOf(new ctor(), Object.prototype),
|
||||
])),
|
||||
];
|
||||
}
|
||||
|
||||
const intlFallbackSymbol = Object.getOwnPropertySymbols(Intl.NumberFormat.call(Object.create(Intl.NumberFormat.prototype)))[0];
|
||||
|
||||
// Invoking [[Call]] for Intl.NumberFormat returns a new instance unless called
|
||||
// with an instance inheriting from Intl.NumberFormat.prototype.
|
||||
for (let thisValue of thisValues()) {
|
||||
let obj = Intl.NumberFormat.call(thisValue);
|
||||
|
||||
if (!Intl.NumberFormat.prototype.isPrototypeOf(thisValue)) {
|
||||
assertEq(Object.is(obj, thisValue), false);
|
||||
assertEq(obj instanceof Intl.NumberFormat, true);
|
||||
if (IsObject(thisValue))
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
|
||||
} else {
|
||||
assertEq(Object.is(obj, thisValue), true);
|
||||
assertEq(obj instanceof Intl.NumberFormat, true);
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]);
|
||||
}
|
||||
}
|
||||
|
||||
// Intl.NumberFormat uses the legacy Intl constructor compromise semantics.
|
||||
// - Test when InstanceofOperator(thisValue, %NumberFormat%) returns true.
|
||||
for (let thisValue of thisValues()) {
|
||||
let hasInstanceCalled = false;
|
||||
Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, {
|
||||
value() {
|
||||
assertEq(hasInstanceCalled, false);
|
||||
hasInstanceCalled = true;
|
||||
return true;
|
||||
}, configurable: true
|
||||
});
|
||||
if (!IsObject(thisValue)) {
|
||||
// A TypeError is thrown when Intl.NumberFormat tries to install the
|
||||
// [[FallbackSymbol]] property on |thisValue|.
|
||||
assertThrowsInstanceOf(() => Intl.NumberFormat.call(thisValue), TypeError);
|
||||
delete Intl.NumberFormat[Symbol.hasInstance];
|
||||
} else {
|
||||
let obj = Intl.NumberFormat.call(thisValue);
|
||||
delete Intl.NumberFormat[Symbol.hasInstance];
|
||||
assertEq(Object.is(obj, thisValue), true);
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]);
|
||||
}
|
||||
assertEq(hasInstanceCalled, true);
|
||||
}
|
||||
// - Test when InstanceofOperator(thisValue, %NumberFormat%) returns false.
|
||||
for (let thisValue of thisValues()) {
|
||||
let hasInstanceCalled = false;
|
||||
Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, {
|
||||
value() {
|
||||
assertEq(hasInstanceCalled, false);
|
||||
hasInstanceCalled = true;
|
||||
return false;
|
||||
}, configurable: true
|
||||
});
|
||||
let obj = Intl.NumberFormat.call(thisValue);
|
||||
delete Intl.NumberFormat[Symbol.hasInstance];
|
||||
assertEq(Object.is(obj, thisValue), false);
|
||||
assertEq(obj instanceof Intl.NumberFormat, true);
|
||||
if (IsObject(thisValue))
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
|
||||
assertEq(hasInstanceCalled, true);
|
||||
}
|
||||
|
||||
// Throws an error when attempting to install [[FallbackSymbol]] twice.
|
||||
{
|
||||
let thisValue = Object.create(Intl.NumberFormat.prototype);
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
|
||||
|
||||
assertEq(Intl.NumberFormat.call(thisValue), thisValue);
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]);
|
||||
|
||||
assertThrowsInstanceOf(() => Intl.NumberFormat.call(thisValue), TypeError);
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]);
|
||||
}
|
||||
|
||||
// Throws an error when the thisValue is non-extensible.
|
||||
{
|
||||
let thisValue = Object.create(Intl.NumberFormat.prototype);
|
||||
Object.preventExtensions(thisValue);
|
||||
|
||||
assertThrowsInstanceOf(() => Intl.NumberFormat.call(thisValue), TypeError);
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
|
||||
}
|
||||
|
||||
// [[FallbackSymbol]] is installed as a frozen property holding an Intl.NumberFormat instance.
|
||||
{
|
||||
let thisValue = Object.create(Intl.NumberFormat.prototype);
|
||||
Intl.NumberFormat.call(thisValue);
|
||||
|
||||
let desc = Object.getOwnPropertyDescriptor(thisValue, intlFallbackSymbol);
|
||||
assertEq(desc !== undefined, true);
|
||||
assertEq(desc.writable, false);
|
||||
assertEq(desc.enumerable, false);
|
||||
assertEq(desc.configurable, false);
|
||||
assertEq(desc.value instanceof Intl.NumberFormat, true);
|
||||
}
|
||||
|
||||
// Ensure [[FallbackSymbol]] is installed last by changing the [[Prototype]]
|
||||
// during initialization.
|
||||
{
|
||||
let thisValue = {};
|
||||
let options = {
|
||||
get useGrouping() {
|
||||
Object.setPrototypeOf(thisValue, Intl.NumberFormat.prototype);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let obj = Intl.NumberFormat.call(thisValue, undefined, options);
|
||||
assertEq(Object.is(obj, thisValue), true);
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]);
|
||||
}
|
||||
{
|
||||
let thisValue = Object.create(Intl.NumberFormat.prototype);
|
||||
let options = {
|
||||
get useGrouping() {
|
||||
Object.setPrototypeOf(thisValue, Object.prototype);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let obj = Intl.NumberFormat.call(thisValue, undefined, options);
|
||||
assertEq(Object.is(obj, thisValue), false);
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
226
js/src/tests/Intl/NumberFormat/unwrapping.js
Normal file
226
js/src/tests/Intl/NumberFormat/unwrapping.js
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
|
||||
|
||||
// Test UnwrapNumberFormat operation.
|
||||
|
||||
const numberFormatFunctions = [];
|
||||
numberFormatFunctions.push(Intl.NumberFormat.prototype.resolvedOptions);
|
||||
numberFormatFunctions.push(Object.getOwnPropertyDescriptor(Intl.NumberFormat.prototype, "format").get);
|
||||
// "formatToParts" isn't yet enabled by default.
|
||||
if ("formatToParts" in Intl.NumberFormat.prototype)
|
||||
numberFormatFunctions.push(Intl.NumberFormat.prototype.formatToParts);
|
||||
|
||||
function IsConstructor(o) {
|
||||
try {
|
||||
new (new Proxy(o, {construct: () => ({})}));
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function IsObject(o) {
|
||||
return Object(o) === o;
|
||||
}
|
||||
|
||||
function intlObjects(ctor) {
|
||||
return [
|
||||
// Instance of an Intl constructor.
|
||||
new ctor(),
|
||||
|
||||
// Instance of a subclassed Intl constructor.
|
||||
new class extends ctor {},
|
||||
|
||||
// Intl object not inheriting from its default prototype.
|
||||
Object.setPrototypeOf(new ctor(), Object.prototype),
|
||||
];
|
||||
}
|
||||
|
||||
function thisValues(C) {
|
||||
const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsConstructor);
|
||||
|
||||
return [
|
||||
// Primitive values.
|
||||
...[undefined, null, true, "abc", Symbol(), 123],
|
||||
|
||||
// Object values.
|
||||
...[{}, [], /(?:)/, function(){}, new Proxy({}, {})],
|
||||
|
||||
// Intl objects.
|
||||
...[].concat(...intlConstructors.filter(ctor => ctor !== C).map(intlObjects)),
|
||||
|
||||
// Object inheriting from an Intl constructor prototype.
|
||||
...intlConstructors.map(ctor => Object.create(ctor.prototype)),
|
||||
];
|
||||
}
|
||||
|
||||
const intlFallbackSymbol = Object.getOwnPropertySymbols(Intl.NumberFormat.call(Object.create(Intl.NumberFormat.prototype)))[0];
|
||||
|
||||
// Test Intl.NumberFormat.prototype methods.
|
||||
for (let numberFormatFunction of numberFormatFunctions) {
|
||||
// Test a TypeError is thrown when the this-value isn't an initialized
|
||||
// Intl.NumberFormat instance.
|
||||
for (let thisValue of thisValues(Intl.NumberFormat)) {
|
||||
assertThrowsInstanceOf(() => numberFormatFunction.call(thisValue), TypeError);
|
||||
}
|
||||
|
||||
// And test no error is thrown for initialized Intl.NumberFormat instances.
|
||||
for (let thisValue of intlObjects(Intl.NumberFormat)) {
|
||||
numberFormatFunction.call(thisValue);
|
||||
}
|
||||
|
||||
// Manually add [[FallbackSymbol]] to objects and then repeat the tests from above.
|
||||
for (let thisValue of thisValues(Intl.NumberFormat)) {
|
||||
assertThrowsInstanceOf(() => numberFormatFunction.call({
|
||||
__proto__: Intl.NumberFormat.prototype,
|
||||
[intlFallbackSymbol]: thisValue,
|
||||
}), TypeError);
|
||||
}
|
||||
|
||||
for (let thisValue of intlObjects(Intl.NumberFormat)) {
|
||||
numberFormatFunction.call({
|
||||
__proto__: Intl.NumberFormat.prototype,
|
||||
[intlFallbackSymbol]: thisValue,
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure [[FallbackSymbol]] isn't retrieved for Intl.NumberFormat instances.
|
||||
for (let thisValue of intlObjects(Intl.NumberFormat)) {
|
||||
Object.defineProperty(thisValue, intlFallbackSymbol, {
|
||||
get() { assertEq(false, true); }
|
||||
});
|
||||
numberFormatFunction.call(thisValue);
|
||||
}
|
||||
|
||||
// Ensure [[FallbackSymbol]] is only retrieved for objects inheriting from Intl.NumberFormat.prototype.
|
||||
for (let thisValue of thisValues(Intl.NumberFormat)) {
|
||||
if (!IsObject(thisValue) || Intl.NumberFormat.prototype.isPrototypeOf(thisValue))
|
||||
continue;
|
||||
Object.defineProperty(thisValue, intlFallbackSymbol, {
|
||||
get() { assertEq(false, true); }
|
||||
});
|
||||
assertThrowsInstanceOf(() => numberFormatFunction.call(thisValue), TypeError);
|
||||
}
|
||||
|
||||
// Repeat the test from above, but also change Intl.NumberFormat[@@hasInstance]
|
||||
// so it always returns |null|.
|
||||
for (let thisValue of thisValues(Intl.NumberFormat)) {
|
||||
let hasInstanceCalled = false, symbolGetterCalled = false;
|
||||
Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, {
|
||||
value() {
|
||||
assertEq(hasInstanceCalled, false);
|
||||
hasInstanceCalled = true;
|
||||
return true;
|
||||
}, configurable: true
|
||||
});
|
||||
let isUndefinedOrNull = thisValue !== undefined || thisValue !== null;
|
||||
let symbolHolder;
|
||||
if (!isUndefinedOrNull) {
|
||||
symbolHolder = IsObject(thisValue) ? thisValue : Object.getPrototypeOf(thisValue);
|
||||
Object.defineProperty(symbolHolder, intlFallbackSymbol, {
|
||||
get() {
|
||||
assertEq(symbolGetterCalled, false);
|
||||
symbolGetterCalled = true;
|
||||
return null;
|
||||
}, configurable: true
|
||||
});
|
||||
}
|
||||
|
||||
assertThrowsInstanceOf(() => numberFormatFunction.call(thisValue), TypeError);
|
||||
|
||||
delete Intl.NumberFormat[Symbol.hasInstance];
|
||||
if (!isUndefinedOrNull && !IsObject(thisValue))
|
||||
delete symbolHolder[intlFallbackSymbol];
|
||||
|
||||
assertEq(hasInstanceCalled, true);
|
||||
assertEq(symbolGetterCalled, !isUndefinedOrNull);
|
||||
}
|
||||
}
|
||||
|
||||
// Test format() returns the correct result for objects initialized as Intl.NumberFormat instances.
|
||||
{
|
||||
// An actual Intl.NumberFormat instance.
|
||||
let numberFormat = new Intl.NumberFormat();
|
||||
|
||||
// An object initialized as a NumberFormat instance.
|
||||
let thisValue = Object.create(Intl.NumberFormat.prototype);
|
||||
Intl.NumberFormat.call(thisValue);
|
||||
|
||||
// Object with [[FallbackSymbol]] set to NumberFormat instance.
|
||||
let fakeObj = {
|
||||
__proto__: Intl.NumberFormat.prototype,
|
||||
[intlFallbackSymbol]: numberFormat,
|
||||
};
|
||||
|
||||
for (let number of [0, 1, 1.5, Infinity, NaN]) {
|
||||
let expected = numberFormat.format(number);
|
||||
assertEq(thisValue.format(number), expected);
|
||||
assertEq(thisValue[intlFallbackSymbol].format(number), expected);
|
||||
assertEq(fakeObj.format(number), expected);
|
||||
}
|
||||
}
|
||||
|
||||
// Test formatToParts() returns the correct result for objects initialized as Intl.NumberFormat instances.
|
||||
if ("formatToParts" in Intl.NumberFormat.prototype) {
|
||||
// An actual Intl.NumberFormat instance.
|
||||
let numberFormat = new Intl.NumberFormat();
|
||||
|
||||
// An object initialized as a NumberFormat instance.
|
||||
let thisValue = Object.create(Intl.NumberFormat.prototype);
|
||||
Intl.NumberFormat.call(thisValue);
|
||||
|
||||
// Object with [[FallbackSymbol]] set to NumberFormat instance.
|
||||
let fakeObj = {
|
||||
__proto__: Intl.NumberFormat.prototype,
|
||||
[intlFallbackSymbol]: numberFormat,
|
||||
};
|
||||
|
||||
function assertEqParts(actual, expected) {
|
||||
assertEq(actual.length, expected.length, "parts count mismatch");
|
||||
for (var i = 0; i < expected.length; i++) {
|
||||
assertEq(actual[i].type, expected[i].type, "type mismatch at " + i);
|
||||
assertEq(actual[i].value, expected[i].value, "value mismatch at " + i);
|
||||
}
|
||||
}
|
||||
|
||||
for (let number of [0, 1, 1.5, Infinity, NaN]) {
|
||||
let expected = numberFormat.formatToParts(number);
|
||||
assertEqParts(thisValue.formatToParts(number), expected);
|
||||
assertEqParts(thisValue[intlFallbackSymbol].formatToParts(number), expected);
|
||||
assertEqParts(fakeObj.formatToParts(number), expected);
|
||||
}
|
||||
}
|
||||
|
||||
// Test resolvedOptions() returns the same results.
|
||||
{
|
||||
// An actual Intl.NumberFormat instance.
|
||||
let numberFormat = new Intl.NumberFormat();
|
||||
|
||||
// An object initialized as a NumberFormat instance.
|
||||
let thisValue = Object.create(Intl.NumberFormat.prototype);
|
||||
Intl.NumberFormat.call(thisValue);
|
||||
|
||||
// Object with [[FallbackSymbol]] set to NumberFormat instance.
|
||||
let fakeObj = {
|
||||
__proto__: Intl.NumberFormat.prototype,
|
||||
[intlFallbackSymbol]: numberFormat,
|
||||
};
|
||||
|
||||
function assertEqOptions(actual, expected) {
|
||||
actual = Object.entries(actual);
|
||||
expected = Object.entries(expected);
|
||||
|
||||
assertEq(actual.length, expected.length, "options count mismatch");
|
||||
for (var i = 0; i < expected.length; i++) {
|
||||
assertEq(actual[i][0], expected[i][0], "key mismatch at " + i);
|
||||
assertEq(actual[i][1], expected[i][1], "value mismatch at " + i);
|
||||
}
|
||||
}
|
||||
|
||||
let expected = numberFormat.resolvedOptions();
|
||||
assertEqOptions(thisValue.resolvedOptions(), expected);
|
||||
assertEqOptions(thisValue[intlFallbackSymbol].resolvedOptions(), expected);
|
||||
assertEqOptions(fakeObj.resolvedOptions(), expected);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
71
js/src/tests/Intl/PluralRules/call.js
Normal file
71
js/src/tests/Intl/PluralRules/call.js
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty("addIntlExtras"))
|
||||
|
||||
addIntlExtras(Intl);
|
||||
|
||||
function IsConstructor(o) {
|
||||
try {
|
||||
new (new Proxy(o, {construct: () => ({})}));
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function IsObject(o) {
|
||||
return Object(o) === o;
|
||||
}
|
||||
|
||||
function thisValues() {
|
||||
const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsConstructor);
|
||||
|
||||
return [
|
||||
// Primitive values.
|
||||
...[undefined, null, true, "abc", Symbol(), 123],
|
||||
|
||||
// Object values.
|
||||
...[{}, [], /(?:)/, function(){}, new Proxy({}, {})],
|
||||
|
||||
// Intl objects.
|
||||
...[].concat(...intlConstructors.map(ctor => [
|
||||
// Instance of an Intl constructor.
|
||||
new ctor(),
|
||||
|
||||
// Instance of a subclassed Intl constructor.
|
||||
new class extends ctor {},
|
||||
|
||||
// Object inheriting from an Intl constructor prototype.
|
||||
Object.create(ctor.prototype),
|
||||
|
||||
// Intl object not inheriting from its default prototype.
|
||||
Object.setPrototypeOf(new ctor(), Object.prototype),
|
||||
])),
|
||||
];
|
||||
}
|
||||
|
||||
// Invoking [[Call]] for Intl.PluralRules always returns a new PluralRules instance.
|
||||
for (let thisValue of thisValues()) {
|
||||
let obj = Intl.PluralRules.call(thisValue);
|
||||
assertEq(Object.is(obj, thisValue), false);
|
||||
assertEq(obj instanceof Intl.PluralRules, true);
|
||||
|
||||
// Ensure Intl.[[FallbackSymbol]] wasn't installed on |thisValue|.
|
||||
if (IsObject(thisValue))
|
||||
assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
|
||||
}
|
||||
|
||||
// Intl.PluralRules doesn't use the legacy Intl constructor compromise semantics.
|
||||
for (let thisValue of thisValues()) {
|
||||
// Ensure instanceof operator isn't invoked for Intl.PluralRules.
|
||||
Object.defineProperty(Intl.PluralRules, Symbol.hasInstance, {
|
||||
get() {
|
||||
assertEq(false, true, "@@hasInstance operator called");
|
||||
}, configurable: true
|
||||
});
|
||||
let obj = Intl.PluralRules.call(thisValue);
|
||||
delete Intl.PluralRules[Symbol.hasInstance];
|
||||
assertEq(Object.is(obj, thisValue), false);
|
||||
assertEq(obj instanceof Intl.PluralRules, true);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
0
js/src/tests/Intl/String/shell.js
Normal file
0
js/src/tests/Intl/String/shell.js
Normal file
69
js/src/tests/Intl/String/toLocaleLowerCase.js
Normal file
69
js/src/tests/Intl/String/toLocaleLowerCase.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
|
||||
|
||||
// Test language dependent special casing with different language tags.
|
||||
for (let locale of ["tr", "TR", "tr-TR", "tr-u-co-search", "tr-x-turkish"]) {
|
||||
assertEq("\u0130".toLocaleLowerCase(locale), "i");
|
||||
assertEq("\u0130".toLocaleLowerCase([locale]), "i");
|
||||
|
||||
// Additional language tags are ignored.
|
||||
assertEq("\u0130".toLocaleLowerCase([locale, "und"]), "i");
|
||||
assertEq("\u0130".toLocaleLowerCase(["und", locale]), "\u0069\u0307");
|
||||
}
|
||||
|
||||
// Ensure "trl" (Traveller Scottish) isn't misrecognized as "tr", even though
|
||||
// both share the same prefix.
|
||||
assertEq("\u0130".toLocaleLowerCase("trl"), "\u0069\u0307");
|
||||
assertEq("\u0130".toLocaleLowerCase(["trl"]), "\u0069\u0307");
|
||||
|
||||
// Language tag is always verified.
|
||||
for (let locale of ["no_locale", "tr-invalid_ext", ["no_locale"], ["en", "no_locale"]]) {
|
||||
// Empty input string.
|
||||
assertThrowsInstanceOf(() => "".toLocaleLowerCase(locale), RangeError);
|
||||
|
||||
// Non-empty input string.
|
||||
assertThrowsInstanceOf(() => "x".toLocaleLowerCase(locale), RangeError);
|
||||
}
|
||||
|
||||
// The language tag fast-path for String.prototype.toLocaleLowerCase doesn't
|
||||
// trip up on three element private-use only language tags.
|
||||
assertEq("A".toLocaleLowerCase("x-x"), "a");
|
||||
assertEq("A".toLocaleLowerCase("x-0"), "a");
|
||||
|
||||
// No locale argument, undefined as locale, and empty array or array-like all
|
||||
// return the same result. Testing with "a/A" because it has only simple case
|
||||
// mappings.
|
||||
assertEq("A".toLocaleLowerCase(), "a");
|
||||
assertEq("A".toLocaleLowerCase(undefined), "a");
|
||||
assertEq("A".toLocaleLowerCase([]), "a");
|
||||
assertEq("A".toLocaleLowerCase({}), "a");
|
||||
assertEq("A".toLocaleLowerCase({length: 0}), "a");
|
||||
assertEq("A".toLocaleLowerCase({length: -1}), "a");
|
||||
|
||||
// Test with incorrect locale type.
|
||||
for (let locale of [null, 0, Math.PI, NaN, Infinity, true, false, Symbol()]) {
|
||||
// Empty input string.
|
||||
assertThrowsInstanceOf(() => "".toLocaleLowerCase([locale]), TypeError);
|
||||
|
||||
// Non-empty input string.
|
||||
assertThrowsInstanceOf(() => "A".toLocaleLowerCase([locale]), TypeError);
|
||||
}
|
||||
|
||||
// Primitives are converted with ToObject and then queried for .length property.
|
||||
for (let locale of [null]) {
|
||||
// Empty input string.
|
||||
assertThrowsInstanceOf(() => "".toLocaleLowerCase([locale]), TypeError);
|
||||
|
||||
// Non-empty input string.
|
||||
assertThrowsInstanceOf(() => "A".toLocaleLowerCase([locale]), TypeError);
|
||||
}
|
||||
// ToLength(ToObject(<primitive>)) returns 0.
|
||||
for (let locale of [0, Math.PI, NaN, Infinity, true, false, Symbol()]) {
|
||||
// Empty input string.
|
||||
assertEq("".toLocaleLowerCase(locale), "");
|
||||
|
||||
// Non-empty input string.
|
||||
assertEq("A".toLocaleLowerCase(locale), "a");
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0, "ok");
|
||||
69
js/src/tests/Intl/String/toLocaleUpperCase.js
Normal file
69
js/src/tests/Intl/String/toLocaleUpperCase.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
|
||||
|
||||
// Test language dependent special casing with different language tags.
|
||||
for (let locale of ["lt", "LT", "lt-LT", "lt-u-co-phonebk", "lt-x-lietuva"]) {
|
||||
assertEq("i\u0307".toLocaleUpperCase(locale), "I");
|
||||
assertEq("i\u0307".toLocaleUpperCase([locale]), "I");
|
||||
|
||||
// Additional language tags are ignored.
|
||||
assertEq("i\u0307".toLocaleUpperCase([locale, "und"]), "I");
|
||||
assertEq("i\u0307".toLocaleUpperCase(["und", locale]), "I\u0307");
|
||||
}
|
||||
|
||||
// Ensure "lti" (Leti) isn't misrecognized as "lt", even though both share the
|
||||
// same prefix.
|
||||
assertEq("i\u0307".toLocaleUpperCase("lti"), "I\u0307");
|
||||
assertEq("i\u0307".toLocaleUpperCase(["lti"]), "I\u0307");
|
||||
|
||||
// Language tag is always verified.
|
||||
for (let locale of ["no_locale", "lt-invalid_ext", ["no_locale"], ["en", "no_locale"]]) {
|
||||
// Empty input string.
|
||||
assertThrowsInstanceOf(() => "".toLocaleUpperCase(locale), RangeError);
|
||||
|
||||
// Non-empty input string.
|
||||
assertThrowsInstanceOf(() => "a".toLocaleUpperCase(locale), RangeError);
|
||||
}
|
||||
|
||||
// The language tag fast-path for String.prototype.toLocaleUpperCase doesn't
|
||||
// trip up on three element private-use only language tags.
|
||||
assertEq("a".toLocaleUpperCase("x-x"), "A");
|
||||
assertEq("a".toLocaleUpperCase("x-0"), "A");
|
||||
|
||||
// No locale argument, undefined as locale, and empty array or array-like all
|
||||
// return the same result. Testing with "a/A" because it has only simple case
|
||||
// mappings.
|
||||
assertEq("a".toLocaleUpperCase(), "A");
|
||||
assertEq("a".toLocaleUpperCase(undefined), "A");
|
||||
assertEq("a".toLocaleUpperCase([]), "A");
|
||||
assertEq("a".toLocaleUpperCase({}), "A");
|
||||
assertEq("a".toLocaleUpperCase({length: 0}), "A");
|
||||
assertEq("a".toLocaleUpperCase({length: -1}), "A");
|
||||
|
||||
// Test with incorrect locale type.
|
||||
for (let locale of [null, 0, Math.PI, NaN, Infinity, true, false, Symbol()]) {
|
||||
// Empty input string.
|
||||
assertThrowsInstanceOf(() => "".toLocaleUpperCase([locale]), TypeError);
|
||||
|
||||
// Non-empty input string.
|
||||
assertThrowsInstanceOf(() => "a".toLocaleUpperCase([locale]), TypeError);
|
||||
}
|
||||
|
||||
// Primitives are converted with ToObject and then queried for .length property.
|
||||
for (let locale of [null]) {
|
||||
// Empty input string.
|
||||
assertThrowsInstanceOf(() => "".toLocaleUpperCase([locale]), TypeError);
|
||||
|
||||
// Non-empty input string.
|
||||
assertThrowsInstanceOf(() => "a".toLocaleUpperCase([locale]), TypeError);
|
||||
}
|
||||
// ToLength(ToObject(<primitive>)) returns 0.
|
||||
for (let locale of [0, Math.PI, NaN, Infinity, true, false, Symbol()]) {
|
||||
// Empty input string.
|
||||
assertEq("".toLocaleUpperCase(locale), "");
|
||||
|
||||
// Non-empty input string.
|
||||
assertEq("a".toLocaleUpperCase(locale), "A");
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0, "ok");
|
||||
|
|
@ -35,6 +35,9 @@ writeHeaderToLog( SECTION + " "+ TITLE);
|
|||
// Armenian
|
||||
// Range: U+0530 to U+058F
|
||||
for ( var i = 0x0530; i <= 0x058F; i++ ) {
|
||||
// U+0587 (ARMENIAN SMALL LIGATURE ECH YIWN) has special upper casing.
|
||||
if (i == 0x0587) continue;
|
||||
|
||||
var U = new Unicode( i );
|
||||
/*
|
||||
new TestCase( SECTION,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,33 @@
|
|||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
var onlySpace = String.fromCharCode(0x9, 0xa, 0xb, 0xc, 0xd, 0x20, 0xa0, 0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200a, 0x2028, 0x2029, 0x202f, 0x205f, 0x3000, 0xfeff);
|
||||
var onlySpace = String.fromCharCode(
|
||||
0x0009 /* <control> (CHARACTER TABULATION) */,
|
||||
0x000A /* <control> (LINE FEED (LF)) */,
|
||||
0x000B /* <control> (LINE TABULATION) */,
|
||||
0x000C /* <control> (FORM FEED (FF)) */,
|
||||
0x000D /* <control> (CARRIAGE RETURN (CR)) */,
|
||||
0x0020 /* SPACE */,
|
||||
0x00A0 /* NO-BREAK SPACE (NON-BREAKING SPACE) */,
|
||||
0x1680 /* OGHAM SPACE MARK */,
|
||||
0x2000 /* EN QUAD */,
|
||||
0x2001 /* EM QUAD */,
|
||||
0x2002 /* EN SPACE */,
|
||||
0x2003 /* EM SPACE */,
|
||||
0x2004 /* THREE-PER-EM SPACE */,
|
||||
0x2005 /* FOUR-PER-EM SPACE */,
|
||||
0x2006 /* SIX-PER-EM SPACE */,
|
||||
0x2007 /* FIGURE SPACE */,
|
||||
0x2008 /* PUNCTUATION SPACE */,
|
||||
0x2009 /* THIN SPACE */,
|
||||
0x200A /* HAIR SPACE */,
|
||||
0x2028 /* LINE SEPARATOR */,
|
||||
0x2029 /* PARAGRAPH SEPARATOR */,
|
||||
0x202F /* NARROW NO-BREAK SPACE */,
|
||||
0x205F /* MEDIUM MATHEMATICAL SPACE */,
|
||||
0x3000 /* IDEOGRAPHIC SPACE */,
|
||||
0xFEFF /* ZERO WIDTH NO-BREAK SPACE (BYTE ORDER MARK) */
|
||||
);
|
||||
|
||||
assertEq(onlySpace.trim(), "");
|
||||
assertEq((onlySpace + 'aaaa').trim(), 'aaaa');
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -5,456 +5,456 @@
|
|||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
assertEq(String.fromCodePoint(0x10428).toUpperCase().codePointAt(0), 0x10400);
|
||||
assertEq(String.fromCodePoint(0x10429).toUpperCase().codePointAt(0), 0x10401);
|
||||
assertEq(String.fromCodePoint(0x1042a).toUpperCase().codePointAt(0), 0x10402);
|
||||
assertEq(String.fromCodePoint(0x1042b).toUpperCase().codePointAt(0), 0x10403);
|
||||
assertEq(String.fromCodePoint(0x1042c).toUpperCase().codePointAt(0), 0x10404);
|
||||
assertEq(String.fromCodePoint(0x1042d).toUpperCase().codePointAt(0), 0x10405);
|
||||
assertEq(String.fromCodePoint(0x1042e).toUpperCase().codePointAt(0), 0x10406);
|
||||
assertEq(String.fromCodePoint(0x1042f).toUpperCase().codePointAt(0), 0x10407);
|
||||
assertEq(String.fromCodePoint(0x10430).toUpperCase().codePointAt(0), 0x10408);
|
||||
assertEq(String.fromCodePoint(0x10431).toUpperCase().codePointAt(0), 0x10409);
|
||||
assertEq(String.fromCodePoint(0x10432).toUpperCase().codePointAt(0), 0x1040a);
|
||||
assertEq(String.fromCodePoint(0x10433).toUpperCase().codePointAt(0), 0x1040b);
|
||||
assertEq(String.fromCodePoint(0x10434).toUpperCase().codePointAt(0), 0x1040c);
|
||||
assertEq(String.fromCodePoint(0x10435).toUpperCase().codePointAt(0), 0x1040d);
|
||||
assertEq(String.fromCodePoint(0x10436).toUpperCase().codePointAt(0), 0x1040e);
|
||||
assertEq(String.fromCodePoint(0x10437).toUpperCase().codePointAt(0), 0x1040f);
|
||||
assertEq(String.fromCodePoint(0x10438).toUpperCase().codePointAt(0), 0x10410);
|
||||
assertEq(String.fromCodePoint(0x10439).toUpperCase().codePointAt(0), 0x10411);
|
||||
assertEq(String.fromCodePoint(0x1043a).toUpperCase().codePointAt(0), 0x10412);
|
||||
assertEq(String.fromCodePoint(0x1043b).toUpperCase().codePointAt(0), 0x10413);
|
||||
assertEq(String.fromCodePoint(0x1043c).toUpperCase().codePointAt(0), 0x10414);
|
||||
assertEq(String.fromCodePoint(0x1043d).toUpperCase().codePointAt(0), 0x10415);
|
||||
assertEq(String.fromCodePoint(0x1043e).toUpperCase().codePointAt(0), 0x10416);
|
||||
assertEq(String.fromCodePoint(0x1043f).toUpperCase().codePointAt(0), 0x10417);
|
||||
assertEq(String.fromCodePoint(0x10440).toUpperCase().codePointAt(0), 0x10418);
|
||||
assertEq(String.fromCodePoint(0x10441).toUpperCase().codePointAt(0), 0x10419);
|
||||
assertEq(String.fromCodePoint(0x10442).toUpperCase().codePointAt(0), 0x1041a);
|
||||
assertEq(String.fromCodePoint(0x10443).toUpperCase().codePointAt(0), 0x1041b);
|
||||
assertEq(String.fromCodePoint(0x10444).toUpperCase().codePointAt(0), 0x1041c);
|
||||
assertEq(String.fromCodePoint(0x10445).toUpperCase().codePointAt(0), 0x1041d);
|
||||
assertEq(String.fromCodePoint(0x10446).toUpperCase().codePointAt(0), 0x1041e);
|
||||
assertEq(String.fromCodePoint(0x10447).toUpperCase().codePointAt(0), 0x1041f);
|
||||
assertEq(String.fromCodePoint(0x10448).toUpperCase().codePointAt(0), 0x10420);
|
||||
assertEq(String.fromCodePoint(0x10449).toUpperCase().codePointAt(0), 0x10421);
|
||||
assertEq(String.fromCodePoint(0x1044a).toUpperCase().codePointAt(0), 0x10422);
|
||||
assertEq(String.fromCodePoint(0x1044b).toUpperCase().codePointAt(0), 0x10423);
|
||||
assertEq(String.fromCodePoint(0x1044c).toUpperCase().codePointAt(0), 0x10424);
|
||||
assertEq(String.fromCodePoint(0x1044d).toUpperCase().codePointAt(0), 0x10425);
|
||||
assertEq(String.fromCodePoint(0x1044e).toUpperCase().codePointAt(0), 0x10426);
|
||||
assertEq(String.fromCodePoint(0x1044f).toUpperCase().codePointAt(0), 0x10427);
|
||||
assertEq(String.fromCodePoint(0x104d8).toUpperCase().codePointAt(0), 0x104b0);
|
||||
assertEq(String.fromCodePoint(0x104d9).toUpperCase().codePointAt(0), 0x104b1);
|
||||
assertEq(String.fromCodePoint(0x104da).toUpperCase().codePointAt(0), 0x104b2);
|
||||
assertEq(String.fromCodePoint(0x104db).toUpperCase().codePointAt(0), 0x104b3);
|
||||
assertEq(String.fromCodePoint(0x104dc).toUpperCase().codePointAt(0), 0x104b4);
|
||||
assertEq(String.fromCodePoint(0x104dd).toUpperCase().codePointAt(0), 0x104b5);
|
||||
assertEq(String.fromCodePoint(0x104de).toUpperCase().codePointAt(0), 0x104b6);
|
||||
assertEq(String.fromCodePoint(0x104df).toUpperCase().codePointAt(0), 0x104b7);
|
||||
assertEq(String.fromCodePoint(0x104e0).toUpperCase().codePointAt(0), 0x104b8);
|
||||
assertEq(String.fromCodePoint(0x104e1).toUpperCase().codePointAt(0), 0x104b9);
|
||||
assertEq(String.fromCodePoint(0x104e2).toUpperCase().codePointAt(0), 0x104ba);
|
||||
assertEq(String.fromCodePoint(0x104e3).toUpperCase().codePointAt(0), 0x104bb);
|
||||
assertEq(String.fromCodePoint(0x104e4).toUpperCase().codePointAt(0), 0x104bc);
|
||||
assertEq(String.fromCodePoint(0x104e5).toUpperCase().codePointAt(0), 0x104bd);
|
||||
assertEq(String.fromCodePoint(0x104e6).toUpperCase().codePointAt(0), 0x104be);
|
||||
assertEq(String.fromCodePoint(0x104e7).toUpperCase().codePointAt(0), 0x104bf);
|
||||
assertEq(String.fromCodePoint(0x104e8).toUpperCase().codePointAt(0), 0x104c0);
|
||||
assertEq(String.fromCodePoint(0x104e9).toUpperCase().codePointAt(0), 0x104c1);
|
||||
assertEq(String.fromCodePoint(0x104ea).toUpperCase().codePointAt(0), 0x104c2);
|
||||
assertEq(String.fromCodePoint(0x104eb).toUpperCase().codePointAt(0), 0x104c3);
|
||||
assertEq(String.fromCodePoint(0x104ec).toUpperCase().codePointAt(0), 0x104c4);
|
||||
assertEq(String.fromCodePoint(0x104ed).toUpperCase().codePointAt(0), 0x104c5);
|
||||
assertEq(String.fromCodePoint(0x104ee).toUpperCase().codePointAt(0), 0x104c6);
|
||||
assertEq(String.fromCodePoint(0x104ef).toUpperCase().codePointAt(0), 0x104c7);
|
||||
assertEq(String.fromCodePoint(0x104f0).toUpperCase().codePointAt(0), 0x104c8);
|
||||
assertEq(String.fromCodePoint(0x104f1).toUpperCase().codePointAt(0), 0x104c9);
|
||||
assertEq(String.fromCodePoint(0x104f2).toUpperCase().codePointAt(0), 0x104ca);
|
||||
assertEq(String.fromCodePoint(0x104f3).toUpperCase().codePointAt(0), 0x104cb);
|
||||
assertEq(String.fromCodePoint(0x104f4).toUpperCase().codePointAt(0), 0x104cc);
|
||||
assertEq(String.fromCodePoint(0x104f5).toUpperCase().codePointAt(0), 0x104cd);
|
||||
assertEq(String.fromCodePoint(0x104f6).toUpperCase().codePointAt(0), 0x104ce);
|
||||
assertEq(String.fromCodePoint(0x104f7).toUpperCase().codePointAt(0), 0x104cf);
|
||||
assertEq(String.fromCodePoint(0x104f8).toUpperCase().codePointAt(0), 0x104d0);
|
||||
assertEq(String.fromCodePoint(0x104f9).toUpperCase().codePointAt(0), 0x104d1);
|
||||
assertEq(String.fromCodePoint(0x104fa).toUpperCase().codePointAt(0), 0x104d2);
|
||||
assertEq(String.fromCodePoint(0x104fb).toUpperCase().codePointAt(0), 0x104d3);
|
||||
assertEq(String.fromCodePoint(0x10cc0).toUpperCase().codePointAt(0), 0x10c80);
|
||||
assertEq(String.fromCodePoint(0x10cc1).toUpperCase().codePointAt(0), 0x10c81);
|
||||
assertEq(String.fromCodePoint(0x10cc2).toUpperCase().codePointAt(0), 0x10c82);
|
||||
assertEq(String.fromCodePoint(0x10cc3).toUpperCase().codePointAt(0), 0x10c83);
|
||||
assertEq(String.fromCodePoint(0x10cc4).toUpperCase().codePointAt(0), 0x10c84);
|
||||
assertEq(String.fromCodePoint(0x10cc5).toUpperCase().codePointAt(0), 0x10c85);
|
||||
assertEq(String.fromCodePoint(0x10cc6).toUpperCase().codePointAt(0), 0x10c86);
|
||||
assertEq(String.fromCodePoint(0x10cc7).toUpperCase().codePointAt(0), 0x10c87);
|
||||
assertEq(String.fromCodePoint(0x10cc8).toUpperCase().codePointAt(0), 0x10c88);
|
||||
assertEq(String.fromCodePoint(0x10cc9).toUpperCase().codePointAt(0), 0x10c89);
|
||||
assertEq(String.fromCodePoint(0x10cca).toUpperCase().codePointAt(0), 0x10c8a);
|
||||
assertEq(String.fromCodePoint(0x10ccb).toUpperCase().codePointAt(0), 0x10c8b);
|
||||
assertEq(String.fromCodePoint(0x10ccc).toUpperCase().codePointAt(0), 0x10c8c);
|
||||
assertEq(String.fromCodePoint(0x10ccd).toUpperCase().codePointAt(0), 0x10c8d);
|
||||
assertEq(String.fromCodePoint(0x10cce).toUpperCase().codePointAt(0), 0x10c8e);
|
||||
assertEq(String.fromCodePoint(0x10ccf).toUpperCase().codePointAt(0), 0x10c8f);
|
||||
assertEq(String.fromCodePoint(0x10cd0).toUpperCase().codePointAt(0), 0x10c90);
|
||||
assertEq(String.fromCodePoint(0x10cd1).toUpperCase().codePointAt(0), 0x10c91);
|
||||
assertEq(String.fromCodePoint(0x10cd2).toUpperCase().codePointAt(0), 0x10c92);
|
||||
assertEq(String.fromCodePoint(0x10cd3).toUpperCase().codePointAt(0), 0x10c93);
|
||||
assertEq(String.fromCodePoint(0x10cd4).toUpperCase().codePointAt(0), 0x10c94);
|
||||
assertEq(String.fromCodePoint(0x10cd5).toUpperCase().codePointAt(0), 0x10c95);
|
||||
assertEq(String.fromCodePoint(0x10cd6).toUpperCase().codePointAt(0), 0x10c96);
|
||||
assertEq(String.fromCodePoint(0x10cd7).toUpperCase().codePointAt(0), 0x10c97);
|
||||
assertEq(String.fromCodePoint(0x10cd8).toUpperCase().codePointAt(0), 0x10c98);
|
||||
assertEq(String.fromCodePoint(0x10cd9).toUpperCase().codePointAt(0), 0x10c99);
|
||||
assertEq(String.fromCodePoint(0x10cda).toUpperCase().codePointAt(0), 0x10c9a);
|
||||
assertEq(String.fromCodePoint(0x10cdb).toUpperCase().codePointAt(0), 0x10c9b);
|
||||
assertEq(String.fromCodePoint(0x10cdc).toUpperCase().codePointAt(0), 0x10c9c);
|
||||
assertEq(String.fromCodePoint(0x10cdd).toUpperCase().codePointAt(0), 0x10c9d);
|
||||
assertEq(String.fromCodePoint(0x10cde).toUpperCase().codePointAt(0), 0x10c9e);
|
||||
assertEq(String.fromCodePoint(0x10cdf).toUpperCase().codePointAt(0), 0x10c9f);
|
||||
assertEq(String.fromCodePoint(0x10ce0).toUpperCase().codePointAt(0), 0x10ca0);
|
||||
assertEq(String.fromCodePoint(0x10ce1).toUpperCase().codePointAt(0), 0x10ca1);
|
||||
assertEq(String.fromCodePoint(0x10ce2).toUpperCase().codePointAt(0), 0x10ca2);
|
||||
assertEq(String.fromCodePoint(0x10ce3).toUpperCase().codePointAt(0), 0x10ca3);
|
||||
assertEq(String.fromCodePoint(0x10ce4).toUpperCase().codePointAt(0), 0x10ca4);
|
||||
assertEq(String.fromCodePoint(0x10ce5).toUpperCase().codePointAt(0), 0x10ca5);
|
||||
assertEq(String.fromCodePoint(0x10ce6).toUpperCase().codePointAt(0), 0x10ca6);
|
||||
assertEq(String.fromCodePoint(0x10ce7).toUpperCase().codePointAt(0), 0x10ca7);
|
||||
assertEq(String.fromCodePoint(0x10ce8).toUpperCase().codePointAt(0), 0x10ca8);
|
||||
assertEq(String.fromCodePoint(0x10ce9).toUpperCase().codePointAt(0), 0x10ca9);
|
||||
assertEq(String.fromCodePoint(0x10cea).toUpperCase().codePointAt(0), 0x10caa);
|
||||
assertEq(String.fromCodePoint(0x10ceb).toUpperCase().codePointAt(0), 0x10cab);
|
||||
assertEq(String.fromCodePoint(0x10cec).toUpperCase().codePointAt(0), 0x10cac);
|
||||
assertEq(String.fromCodePoint(0x10ced).toUpperCase().codePointAt(0), 0x10cad);
|
||||
assertEq(String.fromCodePoint(0x10cee).toUpperCase().codePointAt(0), 0x10cae);
|
||||
assertEq(String.fromCodePoint(0x10cef).toUpperCase().codePointAt(0), 0x10caf);
|
||||
assertEq(String.fromCodePoint(0x10cf0).toUpperCase().codePointAt(0), 0x10cb0);
|
||||
assertEq(String.fromCodePoint(0x10cf1).toUpperCase().codePointAt(0), 0x10cb1);
|
||||
assertEq(String.fromCodePoint(0x10cf2).toUpperCase().codePointAt(0), 0x10cb2);
|
||||
assertEq(String.fromCodePoint(0x118c0).toUpperCase().codePointAt(0), 0x118a0);
|
||||
assertEq(String.fromCodePoint(0x118c1).toUpperCase().codePointAt(0), 0x118a1);
|
||||
assertEq(String.fromCodePoint(0x118c2).toUpperCase().codePointAt(0), 0x118a2);
|
||||
assertEq(String.fromCodePoint(0x118c3).toUpperCase().codePointAt(0), 0x118a3);
|
||||
assertEq(String.fromCodePoint(0x118c4).toUpperCase().codePointAt(0), 0x118a4);
|
||||
assertEq(String.fromCodePoint(0x118c5).toUpperCase().codePointAt(0), 0x118a5);
|
||||
assertEq(String.fromCodePoint(0x118c6).toUpperCase().codePointAt(0), 0x118a6);
|
||||
assertEq(String.fromCodePoint(0x118c7).toUpperCase().codePointAt(0), 0x118a7);
|
||||
assertEq(String.fromCodePoint(0x118c8).toUpperCase().codePointAt(0), 0x118a8);
|
||||
assertEq(String.fromCodePoint(0x118c9).toUpperCase().codePointAt(0), 0x118a9);
|
||||
assertEq(String.fromCodePoint(0x118ca).toUpperCase().codePointAt(0), 0x118aa);
|
||||
assertEq(String.fromCodePoint(0x118cb).toUpperCase().codePointAt(0), 0x118ab);
|
||||
assertEq(String.fromCodePoint(0x118cc).toUpperCase().codePointAt(0), 0x118ac);
|
||||
assertEq(String.fromCodePoint(0x118cd).toUpperCase().codePointAt(0), 0x118ad);
|
||||
assertEq(String.fromCodePoint(0x118ce).toUpperCase().codePointAt(0), 0x118ae);
|
||||
assertEq(String.fromCodePoint(0x118cf).toUpperCase().codePointAt(0), 0x118af);
|
||||
assertEq(String.fromCodePoint(0x118d0).toUpperCase().codePointAt(0), 0x118b0);
|
||||
assertEq(String.fromCodePoint(0x118d1).toUpperCase().codePointAt(0), 0x118b1);
|
||||
assertEq(String.fromCodePoint(0x118d2).toUpperCase().codePointAt(0), 0x118b2);
|
||||
assertEq(String.fromCodePoint(0x118d3).toUpperCase().codePointAt(0), 0x118b3);
|
||||
assertEq(String.fromCodePoint(0x118d4).toUpperCase().codePointAt(0), 0x118b4);
|
||||
assertEq(String.fromCodePoint(0x118d5).toUpperCase().codePointAt(0), 0x118b5);
|
||||
assertEq(String.fromCodePoint(0x118d6).toUpperCase().codePointAt(0), 0x118b6);
|
||||
assertEq(String.fromCodePoint(0x118d7).toUpperCase().codePointAt(0), 0x118b7);
|
||||
assertEq(String.fromCodePoint(0x118d8).toUpperCase().codePointAt(0), 0x118b8);
|
||||
assertEq(String.fromCodePoint(0x118d9).toUpperCase().codePointAt(0), 0x118b9);
|
||||
assertEq(String.fromCodePoint(0x118da).toUpperCase().codePointAt(0), 0x118ba);
|
||||
assertEq(String.fromCodePoint(0x118db).toUpperCase().codePointAt(0), 0x118bb);
|
||||
assertEq(String.fromCodePoint(0x118dc).toUpperCase().codePointAt(0), 0x118bc);
|
||||
assertEq(String.fromCodePoint(0x118dd).toUpperCase().codePointAt(0), 0x118bd);
|
||||
assertEq(String.fromCodePoint(0x118de).toUpperCase().codePointAt(0), 0x118be);
|
||||
assertEq(String.fromCodePoint(0x118df).toUpperCase().codePointAt(0), 0x118bf);
|
||||
assertEq(String.fromCodePoint(0x16e60).toUpperCase().codePointAt(0), 0x16e40);
|
||||
assertEq(String.fromCodePoint(0x16e61).toUpperCase().codePointAt(0), 0x16e41);
|
||||
assertEq(String.fromCodePoint(0x16e62).toUpperCase().codePointAt(0), 0x16e42);
|
||||
assertEq(String.fromCodePoint(0x16e63).toUpperCase().codePointAt(0), 0x16e43);
|
||||
assertEq(String.fromCodePoint(0x16e64).toUpperCase().codePointAt(0), 0x16e44);
|
||||
assertEq(String.fromCodePoint(0x16e65).toUpperCase().codePointAt(0), 0x16e45);
|
||||
assertEq(String.fromCodePoint(0x16e66).toUpperCase().codePointAt(0), 0x16e46);
|
||||
assertEq(String.fromCodePoint(0x16e67).toUpperCase().codePointAt(0), 0x16e47);
|
||||
assertEq(String.fromCodePoint(0x16e68).toUpperCase().codePointAt(0), 0x16e48);
|
||||
assertEq(String.fromCodePoint(0x16e69).toUpperCase().codePointAt(0), 0x16e49);
|
||||
assertEq(String.fromCodePoint(0x16e6a).toUpperCase().codePointAt(0), 0x16e4a);
|
||||
assertEq(String.fromCodePoint(0x16e6b).toUpperCase().codePointAt(0), 0x16e4b);
|
||||
assertEq(String.fromCodePoint(0x16e6c).toUpperCase().codePointAt(0), 0x16e4c);
|
||||
assertEq(String.fromCodePoint(0x16e6d).toUpperCase().codePointAt(0), 0x16e4d);
|
||||
assertEq(String.fromCodePoint(0x16e6e).toUpperCase().codePointAt(0), 0x16e4e);
|
||||
assertEq(String.fromCodePoint(0x16e6f).toUpperCase().codePointAt(0), 0x16e4f);
|
||||
assertEq(String.fromCodePoint(0x16e70).toUpperCase().codePointAt(0), 0x16e50);
|
||||
assertEq(String.fromCodePoint(0x16e71).toUpperCase().codePointAt(0), 0x16e51);
|
||||
assertEq(String.fromCodePoint(0x16e72).toUpperCase().codePointAt(0), 0x16e52);
|
||||
assertEq(String.fromCodePoint(0x16e73).toUpperCase().codePointAt(0), 0x16e53);
|
||||
assertEq(String.fromCodePoint(0x16e74).toUpperCase().codePointAt(0), 0x16e54);
|
||||
assertEq(String.fromCodePoint(0x16e75).toUpperCase().codePointAt(0), 0x16e55);
|
||||
assertEq(String.fromCodePoint(0x16e76).toUpperCase().codePointAt(0), 0x16e56);
|
||||
assertEq(String.fromCodePoint(0x16e77).toUpperCase().codePointAt(0), 0x16e57);
|
||||
assertEq(String.fromCodePoint(0x16e78).toUpperCase().codePointAt(0), 0x16e58);
|
||||
assertEq(String.fromCodePoint(0x16e79).toUpperCase().codePointAt(0), 0x16e59);
|
||||
assertEq(String.fromCodePoint(0x16e7a).toUpperCase().codePointAt(0), 0x16e5a);
|
||||
assertEq(String.fromCodePoint(0x16e7b).toUpperCase().codePointAt(0), 0x16e5b);
|
||||
assertEq(String.fromCodePoint(0x16e7c).toUpperCase().codePointAt(0), 0x16e5c);
|
||||
assertEq(String.fromCodePoint(0x16e7d).toUpperCase().codePointAt(0), 0x16e5d);
|
||||
assertEq(String.fromCodePoint(0x16e7e).toUpperCase().codePointAt(0), 0x16e5e);
|
||||
assertEq(String.fromCodePoint(0x16e7f).toUpperCase().codePointAt(0), 0x16e5f);
|
||||
assertEq(String.fromCodePoint(0x1e922).toUpperCase().codePointAt(0), 0x1e900);
|
||||
assertEq(String.fromCodePoint(0x1e923).toUpperCase().codePointAt(0), 0x1e901);
|
||||
assertEq(String.fromCodePoint(0x1e924).toUpperCase().codePointAt(0), 0x1e902);
|
||||
assertEq(String.fromCodePoint(0x1e925).toUpperCase().codePointAt(0), 0x1e903);
|
||||
assertEq(String.fromCodePoint(0x1e926).toUpperCase().codePointAt(0), 0x1e904);
|
||||
assertEq(String.fromCodePoint(0x1e927).toUpperCase().codePointAt(0), 0x1e905);
|
||||
assertEq(String.fromCodePoint(0x1e928).toUpperCase().codePointAt(0), 0x1e906);
|
||||
assertEq(String.fromCodePoint(0x1e929).toUpperCase().codePointAt(0), 0x1e907);
|
||||
assertEq(String.fromCodePoint(0x1e92a).toUpperCase().codePointAt(0), 0x1e908);
|
||||
assertEq(String.fromCodePoint(0x1e92b).toUpperCase().codePointAt(0), 0x1e909);
|
||||
assertEq(String.fromCodePoint(0x1e92c).toUpperCase().codePointAt(0), 0x1e90a);
|
||||
assertEq(String.fromCodePoint(0x1e92d).toUpperCase().codePointAt(0), 0x1e90b);
|
||||
assertEq(String.fromCodePoint(0x1e92e).toUpperCase().codePointAt(0), 0x1e90c);
|
||||
assertEq(String.fromCodePoint(0x1e92f).toUpperCase().codePointAt(0), 0x1e90d);
|
||||
assertEq(String.fromCodePoint(0x1e930).toUpperCase().codePointAt(0), 0x1e90e);
|
||||
assertEq(String.fromCodePoint(0x1e931).toUpperCase().codePointAt(0), 0x1e90f);
|
||||
assertEq(String.fromCodePoint(0x1e932).toUpperCase().codePointAt(0), 0x1e910);
|
||||
assertEq(String.fromCodePoint(0x1e933).toUpperCase().codePointAt(0), 0x1e911);
|
||||
assertEq(String.fromCodePoint(0x1e934).toUpperCase().codePointAt(0), 0x1e912);
|
||||
assertEq(String.fromCodePoint(0x1e935).toUpperCase().codePointAt(0), 0x1e913);
|
||||
assertEq(String.fromCodePoint(0x1e936).toUpperCase().codePointAt(0), 0x1e914);
|
||||
assertEq(String.fromCodePoint(0x1e937).toUpperCase().codePointAt(0), 0x1e915);
|
||||
assertEq(String.fromCodePoint(0x1e938).toUpperCase().codePointAt(0), 0x1e916);
|
||||
assertEq(String.fromCodePoint(0x1e939).toUpperCase().codePointAt(0), 0x1e917);
|
||||
assertEq(String.fromCodePoint(0x1e93a).toUpperCase().codePointAt(0), 0x1e918);
|
||||
assertEq(String.fromCodePoint(0x1e93b).toUpperCase().codePointAt(0), 0x1e919);
|
||||
assertEq(String.fromCodePoint(0x1e93c).toUpperCase().codePointAt(0), 0x1e91a);
|
||||
assertEq(String.fromCodePoint(0x1e93d).toUpperCase().codePointAt(0), 0x1e91b);
|
||||
assertEq(String.fromCodePoint(0x1e93e).toUpperCase().codePointAt(0), 0x1e91c);
|
||||
assertEq(String.fromCodePoint(0x1e93f).toUpperCase().codePointAt(0), 0x1e91d);
|
||||
assertEq(String.fromCodePoint(0x1e940).toUpperCase().codePointAt(0), 0x1e91e);
|
||||
assertEq(String.fromCodePoint(0x1e941).toUpperCase().codePointAt(0), 0x1e91f);
|
||||
assertEq(String.fromCodePoint(0x1e942).toUpperCase().codePointAt(0), 0x1e920);
|
||||
assertEq(String.fromCodePoint(0x1e943).toUpperCase().codePointAt(0), 0x1e921);
|
||||
assertEq(String.fromCodePoint(0x10400).toLowerCase().codePointAt(0), 0x10428);
|
||||
assertEq(String.fromCodePoint(0x10401).toLowerCase().codePointAt(0), 0x10429);
|
||||
assertEq(String.fromCodePoint(0x10402).toLowerCase().codePointAt(0), 0x1042a);
|
||||
assertEq(String.fromCodePoint(0x10403).toLowerCase().codePointAt(0), 0x1042b);
|
||||
assertEq(String.fromCodePoint(0x10404).toLowerCase().codePointAt(0), 0x1042c);
|
||||
assertEq(String.fromCodePoint(0x10405).toLowerCase().codePointAt(0), 0x1042d);
|
||||
assertEq(String.fromCodePoint(0x10406).toLowerCase().codePointAt(0), 0x1042e);
|
||||
assertEq(String.fromCodePoint(0x10407).toLowerCase().codePointAt(0), 0x1042f);
|
||||
assertEq(String.fromCodePoint(0x10408).toLowerCase().codePointAt(0), 0x10430);
|
||||
assertEq(String.fromCodePoint(0x10409).toLowerCase().codePointAt(0), 0x10431);
|
||||
assertEq(String.fromCodePoint(0x1040a).toLowerCase().codePointAt(0), 0x10432);
|
||||
assertEq(String.fromCodePoint(0x1040b).toLowerCase().codePointAt(0), 0x10433);
|
||||
assertEq(String.fromCodePoint(0x1040c).toLowerCase().codePointAt(0), 0x10434);
|
||||
assertEq(String.fromCodePoint(0x1040d).toLowerCase().codePointAt(0), 0x10435);
|
||||
assertEq(String.fromCodePoint(0x1040e).toLowerCase().codePointAt(0), 0x10436);
|
||||
assertEq(String.fromCodePoint(0x1040f).toLowerCase().codePointAt(0), 0x10437);
|
||||
assertEq(String.fromCodePoint(0x10410).toLowerCase().codePointAt(0), 0x10438);
|
||||
assertEq(String.fromCodePoint(0x10411).toLowerCase().codePointAt(0), 0x10439);
|
||||
assertEq(String.fromCodePoint(0x10412).toLowerCase().codePointAt(0), 0x1043a);
|
||||
assertEq(String.fromCodePoint(0x10413).toLowerCase().codePointAt(0), 0x1043b);
|
||||
assertEq(String.fromCodePoint(0x10414).toLowerCase().codePointAt(0), 0x1043c);
|
||||
assertEq(String.fromCodePoint(0x10415).toLowerCase().codePointAt(0), 0x1043d);
|
||||
assertEq(String.fromCodePoint(0x10416).toLowerCase().codePointAt(0), 0x1043e);
|
||||
assertEq(String.fromCodePoint(0x10417).toLowerCase().codePointAt(0), 0x1043f);
|
||||
assertEq(String.fromCodePoint(0x10418).toLowerCase().codePointAt(0), 0x10440);
|
||||
assertEq(String.fromCodePoint(0x10419).toLowerCase().codePointAt(0), 0x10441);
|
||||
assertEq(String.fromCodePoint(0x1041a).toLowerCase().codePointAt(0), 0x10442);
|
||||
assertEq(String.fromCodePoint(0x1041b).toLowerCase().codePointAt(0), 0x10443);
|
||||
assertEq(String.fromCodePoint(0x1041c).toLowerCase().codePointAt(0), 0x10444);
|
||||
assertEq(String.fromCodePoint(0x1041d).toLowerCase().codePointAt(0), 0x10445);
|
||||
assertEq(String.fromCodePoint(0x1041e).toLowerCase().codePointAt(0), 0x10446);
|
||||
assertEq(String.fromCodePoint(0x1041f).toLowerCase().codePointAt(0), 0x10447);
|
||||
assertEq(String.fromCodePoint(0x10420).toLowerCase().codePointAt(0), 0x10448);
|
||||
assertEq(String.fromCodePoint(0x10421).toLowerCase().codePointAt(0), 0x10449);
|
||||
assertEq(String.fromCodePoint(0x10422).toLowerCase().codePointAt(0), 0x1044a);
|
||||
assertEq(String.fromCodePoint(0x10423).toLowerCase().codePointAt(0), 0x1044b);
|
||||
assertEq(String.fromCodePoint(0x10424).toLowerCase().codePointAt(0), 0x1044c);
|
||||
assertEq(String.fromCodePoint(0x10425).toLowerCase().codePointAt(0), 0x1044d);
|
||||
assertEq(String.fromCodePoint(0x10426).toLowerCase().codePointAt(0), 0x1044e);
|
||||
assertEq(String.fromCodePoint(0x10427).toLowerCase().codePointAt(0), 0x1044f);
|
||||
assertEq(String.fromCodePoint(0x104b0).toLowerCase().codePointAt(0), 0x104d8);
|
||||
assertEq(String.fromCodePoint(0x104b1).toLowerCase().codePointAt(0), 0x104d9);
|
||||
assertEq(String.fromCodePoint(0x104b2).toLowerCase().codePointAt(0), 0x104da);
|
||||
assertEq(String.fromCodePoint(0x104b3).toLowerCase().codePointAt(0), 0x104db);
|
||||
assertEq(String.fromCodePoint(0x104b4).toLowerCase().codePointAt(0), 0x104dc);
|
||||
assertEq(String.fromCodePoint(0x104b5).toLowerCase().codePointAt(0), 0x104dd);
|
||||
assertEq(String.fromCodePoint(0x104b6).toLowerCase().codePointAt(0), 0x104de);
|
||||
assertEq(String.fromCodePoint(0x104b7).toLowerCase().codePointAt(0), 0x104df);
|
||||
assertEq(String.fromCodePoint(0x104b8).toLowerCase().codePointAt(0), 0x104e0);
|
||||
assertEq(String.fromCodePoint(0x104b9).toLowerCase().codePointAt(0), 0x104e1);
|
||||
assertEq(String.fromCodePoint(0x104ba).toLowerCase().codePointAt(0), 0x104e2);
|
||||
assertEq(String.fromCodePoint(0x104bb).toLowerCase().codePointAt(0), 0x104e3);
|
||||
assertEq(String.fromCodePoint(0x104bc).toLowerCase().codePointAt(0), 0x104e4);
|
||||
assertEq(String.fromCodePoint(0x104bd).toLowerCase().codePointAt(0), 0x104e5);
|
||||
assertEq(String.fromCodePoint(0x104be).toLowerCase().codePointAt(0), 0x104e6);
|
||||
assertEq(String.fromCodePoint(0x104bf).toLowerCase().codePointAt(0), 0x104e7);
|
||||
assertEq(String.fromCodePoint(0x104c0).toLowerCase().codePointAt(0), 0x104e8);
|
||||
assertEq(String.fromCodePoint(0x104c1).toLowerCase().codePointAt(0), 0x104e9);
|
||||
assertEq(String.fromCodePoint(0x104c2).toLowerCase().codePointAt(0), 0x104ea);
|
||||
assertEq(String.fromCodePoint(0x104c3).toLowerCase().codePointAt(0), 0x104eb);
|
||||
assertEq(String.fromCodePoint(0x104c4).toLowerCase().codePointAt(0), 0x104ec);
|
||||
assertEq(String.fromCodePoint(0x104c5).toLowerCase().codePointAt(0), 0x104ed);
|
||||
assertEq(String.fromCodePoint(0x104c6).toLowerCase().codePointAt(0), 0x104ee);
|
||||
assertEq(String.fromCodePoint(0x104c7).toLowerCase().codePointAt(0), 0x104ef);
|
||||
assertEq(String.fromCodePoint(0x104c8).toLowerCase().codePointAt(0), 0x104f0);
|
||||
assertEq(String.fromCodePoint(0x104c9).toLowerCase().codePointAt(0), 0x104f1);
|
||||
assertEq(String.fromCodePoint(0x104ca).toLowerCase().codePointAt(0), 0x104f2);
|
||||
assertEq(String.fromCodePoint(0x104cb).toLowerCase().codePointAt(0), 0x104f3);
|
||||
assertEq(String.fromCodePoint(0x104cc).toLowerCase().codePointAt(0), 0x104f4);
|
||||
assertEq(String.fromCodePoint(0x104cd).toLowerCase().codePointAt(0), 0x104f5);
|
||||
assertEq(String.fromCodePoint(0x104ce).toLowerCase().codePointAt(0), 0x104f6);
|
||||
assertEq(String.fromCodePoint(0x104cf).toLowerCase().codePointAt(0), 0x104f7);
|
||||
assertEq(String.fromCodePoint(0x104d0).toLowerCase().codePointAt(0), 0x104f8);
|
||||
assertEq(String.fromCodePoint(0x104d1).toLowerCase().codePointAt(0), 0x104f9);
|
||||
assertEq(String.fromCodePoint(0x104d2).toLowerCase().codePointAt(0), 0x104fa);
|
||||
assertEq(String.fromCodePoint(0x104d3).toLowerCase().codePointAt(0), 0x104fb);
|
||||
assertEq(String.fromCodePoint(0x10c80).toLowerCase().codePointAt(0), 0x10cc0);
|
||||
assertEq(String.fromCodePoint(0x10c81).toLowerCase().codePointAt(0), 0x10cc1);
|
||||
assertEq(String.fromCodePoint(0x10c82).toLowerCase().codePointAt(0), 0x10cc2);
|
||||
assertEq(String.fromCodePoint(0x10c83).toLowerCase().codePointAt(0), 0x10cc3);
|
||||
assertEq(String.fromCodePoint(0x10c84).toLowerCase().codePointAt(0), 0x10cc4);
|
||||
assertEq(String.fromCodePoint(0x10c85).toLowerCase().codePointAt(0), 0x10cc5);
|
||||
assertEq(String.fromCodePoint(0x10c86).toLowerCase().codePointAt(0), 0x10cc6);
|
||||
assertEq(String.fromCodePoint(0x10c87).toLowerCase().codePointAt(0), 0x10cc7);
|
||||
assertEq(String.fromCodePoint(0x10c88).toLowerCase().codePointAt(0), 0x10cc8);
|
||||
assertEq(String.fromCodePoint(0x10c89).toLowerCase().codePointAt(0), 0x10cc9);
|
||||
assertEq(String.fromCodePoint(0x10c8a).toLowerCase().codePointAt(0), 0x10cca);
|
||||
assertEq(String.fromCodePoint(0x10c8b).toLowerCase().codePointAt(0), 0x10ccb);
|
||||
assertEq(String.fromCodePoint(0x10c8c).toLowerCase().codePointAt(0), 0x10ccc);
|
||||
assertEq(String.fromCodePoint(0x10c8d).toLowerCase().codePointAt(0), 0x10ccd);
|
||||
assertEq(String.fromCodePoint(0x10c8e).toLowerCase().codePointAt(0), 0x10cce);
|
||||
assertEq(String.fromCodePoint(0x10c8f).toLowerCase().codePointAt(0), 0x10ccf);
|
||||
assertEq(String.fromCodePoint(0x10c90).toLowerCase().codePointAt(0), 0x10cd0);
|
||||
assertEq(String.fromCodePoint(0x10c91).toLowerCase().codePointAt(0), 0x10cd1);
|
||||
assertEq(String.fromCodePoint(0x10c92).toLowerCase().codePointAt(0), 0x10cd2);
|
||||
assertEq(String.fromCodePoint(0x10c93).toLowerCase().codePointAt(0), 0x10cd3);
|
||||
assertEq(String.fromCodePoint(0x10c94).toLowerCase().codePointAt(0), 0x10cd4);
|
||||
assertEq(String.fromCodePoint(0x10c95).toLowerCase().codePointAt(0), 0x10cd5);
|
||||
assertEq(String.fromCodePoint(0x10c96).toLowerCase().codePointAt(0), 0x10cd6);
|
||||
assertEq(String.fromCodePoint(0x10c97).toLowerCase().codePointAt(0), 0x10cd7);
|
||||
assertEq(String.fromCodePoint(0x10c98).toLowerCase().codePointAt(0), 0x10cd8);
|
||||
assertEq(String.fromCodePoint(0x10c99).toLowerCase().codePointAt(0), 0x10cd9);
|
||||
assertEq(String.fromCodePoint(0x10c9a).toLowerCase().codePointAt(0), 0x10cda);
|
||||
assertEq(String.fromCodePoint(0x10c9b).toLowerCase().codePointAt(0), 0x10cdb);
|
||||
assertEq(String.fromCodePoint(0x10c9c).toLowerCase().codePointAt(0), 0x10cdc);
|
||||
assertEq(String.fromCodePoint(0x10c9d).toLowerCase().codePointAt(0), 0x10cdd);
|
||||
assertEq(String.fromCodePoint(0x10c9e).toLowerCase().codePointAt(0), 0x10cde);
|
||||
assertEq(String.fromCodePoint(0x10c9f).toLowerCase().codePointAt(0), 0x10cdf);
|
||||
assertEq(String.fromCodePoint(0x10ca0).toLowerCase().codePointAt(0), 0x10ce0);
|
||||
assertEq(String.fromCodePoint(0x10ca1).toLowerCase().codePointAt(0), 0x10ce1);
|
||||
assertEq(String.fromCodePoint(0x10ca2).toLowerCase().codePointAt(0), 0x10ce2);
|
||||
assertEq(String.fromCodePoint(0x10ca3).toLowerCase().codePointAt(0), 0x10ce3);
|
||||
assertEq(String.fromCodePoint(0x10ca4).toLowerCase().codePointAt(0), 0x10ce4);
|
||||
assertEq(String.fromCodePoint(0x10ca5).toLowerCase().codePointAt(0), 0x10ce5);
|
||||
assertEq(String.fromCodePoint(0x10ca6).toLowerCase().codePointAt(0), 0x10ce6);
|
||||
assertEq(String.fromCodePoint(0x10ca7).toLowerCase().codePointAt(0), 0x10ce7);
|
||||
assertEq(String.fromCodePoint(0x10ca8).toLowerCase().codePointAt(0), 0x10ce8);
|
||||
assertEq(String.fromCodePoint(0x10ca9).toLowerCase().codePointAt(0), 0x10ce9);
|
||||
assertEq(String.fromCodePoint(0x10caa).toLowerCase().codePointAt(0), 0x10cea);
|
||||
assertEq(String.fromCodePoint(0x10cab).toLowerCase().codePointAt(0), 0x10ceb);
|
||||
assertEq(String.fromCodePoint(0x10cac).toLowerCase().codePointAt(0), 0x10cec);
|
||||
assertEq(String.fromCodePoint(0x10cad).toLowerCase().codePointAt(0), 0x10ced);
|
||||
assertEq(String.fromCodePoint(0x10cae).toLowerCase().codePointAt(0), 0x10cee);
|
||||
assertEq(String.fromCodePoint(0x10caf).toLowerCase().codePointAt(0), 0x10cef);
|
||||
assertEq(String.fromCodePoint(0x10cb0).toLowerCase().codePointAt(0), 0x10cf0);
|
||||
assertEq(String.fromCodePoint(0x10cb1).toLowerCase().codePointAt(0), 0x10cf1);
|
||||
assertEq(String.fromCodePoint(0x10cb2).toLowerCase().codePointAt(0), 0x10cf2);
|
||||
assertEq(String.fromCodePoint(0x118a0).toLowerCase().codePointAt(0), 0x118c0);
|
||||
assertEq(String.fromCodePoint(0x118a1).toLowerCase().codePointAt(0), 0x118c1);
|
||||
assertEq(String.fromCodePoint(0x118a2).toLowerCase().codePointAt(0), 0x118c2);
|
||||
assertEq(String.fromCodePoint(0x118a3).toLowerCase().codePointAt(0), 0x118c3);
|
||||
assertEq(String.fromCodePoint(0x118a4).toLowerCase().codePointAt(0), 0x118c4);
|
||||
assertEq(String.fromCodePoint(0x118a5).toLowerCase().codePointAt(0), 0x118c5);
|
||||
assertEq(String.fromCodePoint(0x118a6).toLowerCase().codePointAt(0), 0x118c6);
|
||||
assertEq(String.fromCodePoint(0x118a7).toLowerCase().codePointAt(0), 0x118c7);
|
||||
assertEq(String.fromCodePoint(0x118a8).toLowerCase().codePointAt(0), 0x118c8);
|
||||
assertEq(String.fromCodePoint(0x118a9).toLowerCase().codePointAt(0), 0x118c9);
|
||||
assertEq(String.fromCodePoint(0x118aa).toLowerCase().codePointAt(0), 0x118ca);
|
||||
assertEq(String.fromCodePoint(0x118ab).toLowerCase().codePointAt(0), 0x118cb);
|
||||
assertEq(String.fromCodePoint(0x118ac).toLowerCase().codePointAt(0), 0x118cc);
|
||||
assertEq(String.fromCodePoint(0x118ad).toLowerCase().codePointAt(0), 0x118cd);
|
||||
assertEq(String.fromCodePoint(0x118ae).toLowerCase().codePointAt(0), 0x118ce);
|
||||
assertEq(String.fromCodePoint(0x118af).toLowerCase().codePointAt(0), 0x118cf);
|
||||
assertEq(String.fromCodePoint(0x118b0).toLowerCase().codePointAt(0), 0x118d0);
|
||||
assertEq(String.fromCodePoint(0x118b1).toLowerCase().codePointAt(0), 0x118d1);
|
||||
assertEq(String.fromCodePoint(0x118b2).toLowerCase().codePointAt(0), 0x118d2);
|
||||
assertEq(String.fromCodePoint(0x118b3).toLowerCase().codePointAt(0), 0x118d3);
|
||||
assertEq(String.fromCodePoint(0x118b4).toLowerCase().codePointAt(0), 0x118d4);
|
||||
assertEq(String.fromCodePoint(0x118b5).toLowerCase().codePointAt(0), 0x118d5);
|
||||
assertEq(String.fromCodePoint(0x118b6).toLowerCase().codePointAt(0), 0x118d6);
|
||||
assertEq(String.fromCodePoint(0x118b7).toLowerCase().codePointAt(0), 0x118d7);
|
||||
assertEq(String.fromCodePoint(0x118b8).toLowerCase().codePointAt(0), 0x118d8);
|
||||
assertEq(String.fromCodePoint(0x118b9).toLowerCase().codePointAt(0), 0x118d9);
|
||||
assertEq(String.fromCodePoint(0x118ba).toLowerCase().codePointAt(0), 0x118da);
|
||||
assertEq(String.fromCodePoint(0x118bb).toLowerCase().codePointAt(0), 0x118db);
|
||||
assertEq(String.fromCodePoint(0x118bc).toLowerCase().codePointAt(0), 0x118dc);
|
||||
assertEq(String.fromCodePoint(0x118bd).toLowerCase().codePointAt(0), 0x118dd);
|
||||
assertEq(String.fromCodePoint(0x118be).toLowerCase().codePointAt(0), 0x118de);
|
||||
assertEq(String.fromCodePoint(0x118bf).toLowerCase().codePointAt(0), 0x118df);
|
||||
assertEq(String.fromCodePoint(0x16e40).toLowerCase().codePointAt(0), 0x16e60);
|
||||
assertEq(String.fromCodePoint(0x16e41).toLowerCase().codePointAt(0), 0x16e61);
|
||||
assertEq(String.fromCodePoint(0x16e42).toLowerCase().codePointAt(0), 0x16e62);
|
||||
assertEq(String.fromCodePoint(0x16e43).toLowerCase().codePointAt(0), 0x16e63);
|
||||
assertEq(String.fromCodePoint(0x16e44).toLowerCase().codePointAt(0), 0x16e64);
|
||||
assertEq(String.fromCodePoint(0x16e45).toLowerCase().codePointAt(0), 0x16e65);
|
||||
assertEq(String.fromCodePoint(0x16e46).toLowerCase().codePointAt(0), 0x16e66);
|
||||
assertEq(String.fromCodePoint(0x16e47).toLowerCase().codePointAt(0), 0x16e67);
|
||||
assertEq(String.fromCodePoint(0x16e48).toLowerCase().codePointAt(0), 0x16e68);
|
||||
assertEq(String.fromCodePoint(0x16e49).toLowerCase().codePointAt(0), 0x16e69);
|
||||
assertEq(String.fromCodePoint(0x16e4a).toLowerCase().codePointAt(0), 0x16e6a);
|
||||
assertEq(String.fromCodePoint(0x16e4b).toLowerCase().codePointAt(0), 0x16e6b);
|
||||
assertEq(String.fromCodePoint(0x16e4c).toLowerCase().codePointAt(0), 0x16e6c);
|
||||
assertEq(String.fromCodePoint(0x16e4d).toLowerCase().codePointAt(0), 0x16e6d);
|
||||
assertEq(String.fromCodePoint(0x16e4e).toLowerCase().codePointAt(0), 0x16e6e);
|
||||
assertEq(String.fromCodePoint(0x16e4f).toLowerCase().codePointAt(0), 0x16e6f);
|
||||
assertEq(String.fromCodePoint(0x16e50).toLowerCase().codePointAt(0), 0x16e70);
|
||||
assertEq(String.fromCodePoint(0x16e51).toLowerCase().codePointAt(0), 0x16e71);
|
||||
assertEq(String.fromCodePoint(0x16e52).toLowerCase().codePointAt(0), 0x16e72);
|
||||
assertEq(String.fromCodePoint(0x16e53).toLowerCase().codePointAt(0), 0x16e73);
|
||||
assertEq(String.fromCodePoint(0x16e54).toLowerCase().codePointAt(0), 0x16e74);
|
||||
assertEq(String.fromCodePoint(0x16e55).toLowerCase().codePointAt(0), 0x16e75);
|
||||
assertEq(String.fromCodePoint(0x16e56).toLowerCase().codePointAt(0), 0x16e76);
|
||||
assertEq(String.fromCodePoint(0x16e57).toLowerCase().codePointAt(0), 0x16e77);
|
||||
assertEq(String.fromCodePoint(0x16e58).toLowerCase().codePointAt(0), 0x16e78);
|
||||
assertEq(String.fromCodePoint(0x16e59).toLowerCase().codePointAt(0), 0x16e79);
|
||||
assertEq(String.fromCodePoint(0x16e5a).toLowerCase().codePointAt(0), 0x16e7a);
|
||||
assertEq(String.fromCodePoint(0x16e5b).toLowerCase().codePointAt(0), 0x16e7b);
|
||||
assertEq(String.fromCodePoint(0x16e5c).toLowerCase().codePointAt(0), 0x16e7c);
|
||||
assertEq(String.fromCodePoint(0x16e5d).toLowerCase().codePointAt(0), 0x16e7d);
|
||||
assertEq(String.fromCodePoint(0x16e5e).toLowerCase().codePointAt(0), 0x16e7e);
|
||||
assertEq(String.fromCodePoint(0x16e5f).toLowerCase().codePointAt(0), 0x16e7f);
|
||||
assertEq(String.fromCodePoint(0x1e900).toLowerCase().codePointAt(0), 0x1e922);
|
||||
assertEq(String.fromCodePoint(0x1e901).toLowerCase().codePointAt(0), 0x1e923);
|
||||
assertEq(String.fromCodePoint(0x1e902).toLowerCase().codePointAt(0), 0x1e924);
|
||||
assertEq(String.fromCodePoint(0x1e903).toLowerCase().codePointAt(0), 0x1e925);
|
||||
assertEq(String.fromCodePoint(0x1e904).toLowerCase().codePointAt(0), 0x1e926);
|
||||
assertEq(String.fromCodePoint(0x1e905).toLowerCase().codePointAt(0), 0x1e927);
|
||||
assertEq(String.fromCodePoint(0x1e906).toLowerCase().codePointAt(0), 0x1e928);
|
||||
assertEq(String.fromCodePoint(0x1e907).toLowerCase().codePointAt(0), 0x1e929);
|
||||
assertEq(String.fromCodePoint(0x1e908).toLowerCase().codePointAt(0), 0x1e92a);
|
||||
assertEq(String.fromCodePoint(0x1e909).toLowerCase().codePointAt(0), 0x1e92b);
|
||||
assertEq(String.fromCodePoint(0x1e90a).toLowerCase().codePointAt(0), 0x1e92c);
|
||||
assertEq(String.fromCodePoint(0x1e90b).toLowerCase().codePointAt(0), 0x1e92d);
|
||||
assertEq(String.fromCodePoint(0x1e90c).toLowerCase().codePointAt(0), 0x1e92e);
|
||||
assertEq(String.fromCodePoint(0x1e90d).toLowerCase().codePointAt(0), 0x1e92f);
|
||||
assertEq(String.fromCodePoint(0x1e90e).toLowerCase().codePointAt(0), 0x1e930);
|
||||
assertEq(String.fromCodePoint(0x1e90f).toLowerCase().codePointAt(0), 0x1e931);
|
||||
assertEq(String.fromCodePoint(0x1e910).toLowerCase().codePointAt(0), 0x1e932);
|
||||
assertEq(String.fromCodePoint(0x1e911).toLowerCase().codePointAt(0), 0x1e933);
|
||||
assertEq(String.fromCodePoint(0x1e912).toLowerCase().codePointAt(0), 0x1e934);
|
||||
assertEq(String.fromCodePoint(0x1e913).toLowerCase().codePointAt(0), 0x1e935);
|
||||
assertEq(String.fromCodePoint(0x1e914).toLowerCase().codePointAt(0), 0x1e936);
|
||||
assertEq(String.fromCodePoint(0x1e915).toLowerCase().codePointAt(0), 0x1e937);
|
||||
assertEq(String.fromCodePoint(0x1e916).toLowerCase().codePointAt(0), 0x1e938);
|
||||
assertEq(String.fromCodePoint(0x1e917).toLowerCase().codePointAt(0), 0x1e939);
|
||||
assertEq(String.fromCodePoint(0x1e918).toLowerCase().codePointAt(0), 0x1e93a);
|
||||
assertEq(String.fromCodePoint(0x1e919).toLowerCase().codePointAt(0), 0x1e93b);
|
||||
assertEq(String.fromCodePoint(0x1e91a).toLowerCase().codePointAt(0), 0x1e93c);
|
||||
assertEq(String.fromCodePoint(0x1e91b).toLowerCase().codePointAt(0), 0x1e93d);
|
||||
assertEq(String.fromCodePoint(0x1e91c).toLowerCase().codePointAt(0), 0x1e93e);
|
||||
assertEq(String.fromCodePoint(0x1e91d).toLowerCase().codePointAt(0), 0x1e93f);
|
||||
assertEq(String.fromCodePoint(0x1e91e).toLowerCase().codePointAt(0), 0x1e940);
|
||||
assertEq(String.fromCodePoint(0x1e91f).toLowerCase().codePointAt(0), 0x1e941);
|
||||
assertEq(String.fromCodePoint(0x1e920).toLowerCase().codePointAt(0), 0x1e942);
|
||||
assertEq(String.fromCodePoint(0x1e921).toLowerCase().codePointAt(0), 0x1e943);
|
||||
assertEq(String.fromCodePoint(0x10428).toUpperCase().codePointAt(0), 0x10400); // DESERET SMALL LETTER LONG I, DESERET CAPITAL LETTER LONG I
|
||||
assertEq(String.fromCodePoint(0x10429).toUpperCase().codePointAt(0), 0x10401); // DESERET SMALL LETTER LONG E, DESERET CAPITAL LETTER LONG E
|
||||
assertEq(String.fromCodePoint(0x1042A).toUpperCase().codePointAt(0), 0x10402); // DESERET SMALL LETTER LONG A, DESERET CAPITAL LETTER LONG A
|
||||
assertEq(String.fromCodePoint(0x1042B).toUpperCase().codePointAt(0), 0x10403); // DESERET SMALL LETTER LONG AH, DESERET CAPITAL LETTER LONG AH
|
||||
assertEq(String.fromCodePoint(0x1042C).toUpperCase().codePointAt(0), 0x10404); // DESERET SMALL LETTER LONG O, DESERET CAPITAL LETTER LONG O
|
||||
assertEq(String.fromCodePoint(0x1042D).toUpperCase().codePointAt(0), 0x10405); // DESERET SMALL LETTER LONG OO, DESERET CAPITAL LETTER LONG OO
|
||||
assertEq(String.fromCodePoint(0x1042E).toUpperCase().codePointAt(0), 0x10406); // DESERET SMALL LETTER SHORT I, DESERET CAPITAL LETTER SHORT I
|
||||
assertEq(String.fromCodePoint(0x1042F).toUpperCase().codePointAt(0), 0x10407); // DESERET SMALL LETTER SHORT E, DESERET CAPITAL LETTER SHORT E
|
||||
assertEq(String.fromCodePoint(0x10430).toUpperCase().codePointAt(0), 0x10408); // DESERET SMALL LETTER SHORT A, DESERET CAPITAL LETTER SHORT A
|
||||
assertEq(String.fromCodePoint(0x10431).toUpperCase().codePointAt(0), 0x10409); // DESERET SMALL LETTER SHORT AH, DESERET CAPITAL LETTER SHORT AH
|
||||
assertEq(String.fromCodePoint(0x10432).toUpperCase().codePointAt(0), 0x1040A); // DESERET SMALL LETTER SHORT O, DESERET CAPITAL LETTER SHORT O
|
||||
assertEq(String.fromCodePoint(0x10433).toUpperCase().codePointAt(0), 0x1040B); // DESERET SMALL LETTER SHORT OO, DESERET CAPITAL LETTER SHORT OO
|
||||
assertEq(String.fromCodePoint(0x10434).toUpperCase().codePointAt(0), 0x1040C); // DESERET SMALL LETTER AY, DESERET CAPITAL LETTER AY
|
||||
assertEq(String.fromCodePoint(0x10435).toUpperCase().codePointAt(0), 0x1040D); // DESERET SMALL LETTER OW, DESERET CAPITAL LETTER OW
|
||||
assertEq(String.fromCodePoint(0x10436).toUpperCase().codePointAt(0), 0x1040E); // DESERET SMALL LETTER WU, DESERET CAPITAL LETTER WU
|
||||
assertEq(String.fromCodePoint(0x10437).toUpperCase().codePointAt(0), 0x1040F); // DESERET SMALL LETTER YEE, DESERET CAPITAL LETTER YEE
|
||||
assertEq(String.fromCodePoint(0x10438).toUpperCase().codePointAt(0), 0x10410); // DESERET SMALL LETTER H, DESERET CAPITAL LETTER H
|
||||
assertEq(String.fromCodePoint(0x10439).toUpperCase().codePointAt(0), 0x10411); // DESERET SMALL LETTER PEE, DESERET CAPITAL LETTER PEE
|
||||
assertEq(String.fromCodePoint(0x1043A).toUpperCase().codePointAt(0), 0x10412); // DESERET SMALL LETTER BEE, DESERET CAPITAL LETTER BEE
|
||||
assertEq(String.fromCodePoint(0x1043B).toUpperCase().codePointAt(0), 0x10413); // DESERET SMALL LETTER TEE, DESERET CAPITAL LETTER TEE
|
||||
assertEq(String.fromCodePoint(0x1043C).toUpperCase().codePointAt(0), 0x10414); // DESERET SMALL LETTER DEE, DESERET CAPITAL LETTER DEE
|
||||
assertEq(String.fromCodePoint(0x1043D).toUpperCase().codePointAt(0), 0x10415); // DESERET SMALL LETTER CHEE, DESERET CAPITAL LETTER CHEE
|
||||
assertEq(String.fromCodePoint(0x1043E).toUpperCase().codePointAt(0), 0x10416); // DESERET SMALL LETTER JEE, DESERET CAPITAL LETTER JEE
|
||||
assertEq(String.fromCodePoint(0x1043F).toUpperCase().codePointAt(0), 0x10417); // DESERET SMALL LETTER KAY, DESERET CAPITAL LETTER KAY
|
||||
assertEq(String.fromCodePoint(0x10440).toUpperCase().codePointAt(0), 0x10418); // DESERET SMALL LETTER GAY, DESERET CAPITAL LETTER GAY
|
||||
assertEq(String.fromCodePoint(0x10441).toUpperCase().codePointAt(0), 0x10419); // DESERET SMALL LETTER EF, DESERET CAPITAL LETTER EF
|
||||
assertEq(String.fromCodePoint(0x10442).toUpperCase().codePointAt(0), 0x1041A); // DESERET SMALL LETTER VEE, DESERET CAPITAL LETTER VEE
|
||||
assertEq(String.fromCodePoint(0x10443).toUpperCase().codePointAt(0), 0x1041B); // DESERET SMALL LETTER ETH, DESERET CAPITAL LETTER ETH
|
||||
assertEq(String.fromCodePoint(0x10444).toUpperCase().codePointAt(0), 0x1041C); // DESERET SMALL LETTER THEE, DESERET CAPITAL LETTER THEE
|
||||
assertEq(String.fromCodePoint(0x10445).toUpperCase().codePointAt(0), 0x1041D); // DESERET SMALL LETTER ES, DESERET CAPITAL LETTER ES
|
||||
assertEq(String.fromCodePoint(0x10446).toUpperCase().codePointAt(0), 0x1041E); // DESERET SMALL LETTER ZEE, DESERET CAPITAL LETTER ZEE
|
||||
assertEq(String.fromCodePoint(0x10447).toUpperCase().codePointAt(0), 0x1041F); // DESERET SMALL LETTER ESH, DESERET CAPITAL LETTER ESH
|
||||
assertEq(String.fromCodePoint(0x10448).toUpperCase().codePointAt(0), 0x10420); // DESERET SMALL LETTER ZHEE, DESERET CAPITAL LETTER ZHEE
|
||||
assertEq(String.fromCodePoint(0x10449).toUpperCase().codePointAt(0), 0x10421); // DESERET SMALL LETTER ER, DESERET CAPITAL LETTER ER
|
||||
assertEq(String.fromCodePoint(0x1044A).toUpperCase().codePointAt(0), 0x10422); // DESERET SMALL LETTER EL, DESERET CAPITAL LETTER EL
|
||||
assertEq(String.fromCodePoint(0x1044B).toUpperCase().codePointAt(0), 0x10423); // DESERET SMALL LETTER EM, DESERET CAPITAL LETTER EM
|
||||
assertEq(String.fromCodePoint(0x1044C).toUpperCase().codePointAt(0), 0x10424); // DESERET SMALL LETTER EN, DESERET CAPITAL LETTER EN
|
||||
assertEq(String.fromCodePoint(0x1044D).toUpperCase().codePointAt(0), 0x10425); // DESERET SMALL LETTER ENG, DESERET CAPITAL LETTER ENG
|
||||
assertEq(String.fromCodePoint(0x1044E).toUpperCase().codePointAt(0), 0x10426); // DESERET SMALL LETTER OI, DESERET CAPITAL LETTER OI
|
||||
assertEq(String.fromCodePoint(0x1044F).toUpperCase().codePointAt(0), 0x10427); // DESERET SMALL LETTER EW, DESERET CAPITAL LETTER EW
|
||||
assertEq(String.fromCodePoint(0x104D8).toUpperCase().codePointAt(0), 0x104B0); // OSAGE SMALL LETTER A, OSAGE CAPITAL LETTER A
|
||||
assertEq(String.fromCodePoint(0x104D9).toUpperCase().codePointAt(0), 0x104B1); // OSAGE SMALL LETTER AI, OSAGE CAPITAL LETTER AI
|
||||
assertEq(String.fromCodePoint(0x104DA).toUpperCase().codePointAt(0), 0x104B2); // OSAGE SMALL LETTER AIN, OSAGE CAPITAL LETTER AIN
|
||||
assertEq(String.fromCodePoint(0x104DB).toUpperCase().codePointAt(0), 0x104B3); // OSAGE SMALL LETTER AH, OSAGE CAPITAL LETTER AH
|
||||
assertEq(String.fromCodePoint(0x104DC).toUpperCase().codePointAt(0), 0x104B4); // OSAGE SMALL LETTER BRA, OSAGE CAPITAL LETTER BRA
|
||||
assertEq(String.fromCodePoint(0x104DD).toUpperCase().codePointAt(0), 0x104B5); // OSAGE SMALL LETTER CHA, OSAGE CAPITAL LETTER CHA
|
||||
assertEq(String.fromCodePoint(0x104DE).toUpperCase().codePointAt(0), 0x104B6); // OSAGE SMALL LETTER EHCHA, OSAGE CAPITAL LETTER EHCHA
|
||||
assertEq(String.fromCodePoint(0x104DF).toUpperCase().codePointAt(0), 0x104B7); // OSAGE SMALL LETTER E, OSAGE CAPITAL LETTER E
|
||||
assertEq(String.fromCodePoint(0x104E0).toUpperCase().codePointAt(0), 0x104B8); // OSAGE SMALL LETTER EIN, OSAGE CAPITAL LETTER EIN
|
||||
assertEq(String.fromCodePoint(0x104E1).toUpperCase().codePointAt(0), 0x104B9); // OSAGE SMALL LETTER HA, OSAGE CAPITAL LETTER HA
|
||||
assertEq(String.fromCodePoint(0x104E2).toUpperCase().codePointAt(0), 0x104BA); // OSAGE SMALL LETTER HYA, OSAGE CAPITAL LETTER HYA
|
||||
assertEq(String.fromCodePoint(0x104E3).toUpperCase().codePointAt(0), 0x104BB); // OSAGE SMALL LETTER I, OSAGE CAPITAL LETTER I
|
||||
assertEq(String.fromCodePoint(0x104E4).toUpperCase().codePointAt(0), 0x104BC); // OSAGE SMALL LETTER KA, OSAGE CAPITAL LETTER KA
|
||||
assertEq(String.fromCodePoint(0x104E5).toUpperCase().codePointAt(0), 0x104BD); // OSAGE SMALL LETTER EHKA, OSAGE CAPITAL LETTER EHKA
|
||||
assertEq(String.fromCodePoint(0x104E6).toUpperCase().codePointAt(0), 0x104BE); // OSAGE SMALL LETTER KYA, OSAGE CAPITAL LETTER KYA
|
||||
assertEq(String.fromCodePoint(0x104E7).toUpperCase().codePointAt(0), 0x104BF); // OSAGE SMALL LETTER LA, OSAGE CAPITAL LETTER LA
|
||||
assertEq(String.fromCodePoint(0x104E8).toUpperCase().codePointAt(0), 0x104C0); // OSAGE SMALL LETTER MA, OSAGE CAPITAL LETTER MA
|
||||
assertEq(String.fromCodePoint(0x104E9).toUpperCase().codePointAt(0), 0x104C1); // OSAGE SMALL LETTER NA, OSAGE CAPITAL LETTER NA
|
||||
assertEq(String.fromCodePoint(0x104EA).toUpperCase().codePointAt(0), 0x104C2); // OSAGE SMALL LETTER O, OSAGE CAPITAL LETTER O
|
||||
assertEq(String.fromCodePoint(0x104EB).toUpperCase().codePointAt(0), 0x104C3); // OSAGE SMALL LETTER OIN, OSAGE CAPITAL LETTER OIN
|
||||
assertEq(String.fromCodePoint(0x104EC).toUpperCase().codePointAt(0), 0x104C4); // OSAGE SMALL LETTER PA, OSAGE CAPITAL LETTER PA
|
||||
assertEq(String.fromCodePoint(0x104ED).toUpperCase().codePointAt(0), 0x104C5); // OSAGE SMALL LETTER EHPA, OSAGE CAPITAL LETTER EHPA
|
||||
assertEq(String.fromCodePoint(0x104EE).toUpperCase().codePointAt(0), 0x104C6); // OSAGE SMALL LETTER SA, OSAGE CAPITAL LETTER SA
|
||||
assertEq(String.fromCodePoint(0x104EF).toUpperCase().codePointAt(0), 0x104C7); // OSAGE SMALL LETTER SHA, OSAGE CAPITAL LETTER SHA
|
||||
assertEq(String.fromCodePoint(0x104F0).toUpperCase().codePointAt(0), 0x104C8); // OSAGE SMALL LETTER TA, OSAGE CAPITAL LETTER TA
|
||||
assertEq(String.fromCodePoint(0x104F1).toUpperCase().codePointAt(0), 0x104C9); // OSAGE SMALL LETTER EHTA, OSAGE CAPITAL LETTER EHTA
|
||||
assertEq(String.fromCodePoint(0x104F2).toUpperCase().codePointAt(0), 0x104CA); // OSAGE SMALL LETTER TSA, OSAGE CAPITAL LETTER TSA
|
||||
assertEq(String.fromCodePoint(0x104F3).toUpperCase().codePointAt(0), 0x104CB); // OSAGE SMALL LETTER EHTSA, OSAGE CAPITAL LETTER EHTSA
|
||||
assertEq(String.fromCodePoint(0x104F4).toUpperCase().codePointAt(0), 0x104CC); // OSAGE SMALL LETTER TSHA, OSAGE CAPITAL LETTER TSHA
|
||||
assertEq(String.fromCodePoint(0x104F5).toUpperCase().codePointAt(0), 0x104CD); // OSAGE SMALL LETTER DHA, OSAGE CAPITAL LETTER DHA
|
||||
assertEq(String.fromCodePoint(0x104F6).toUpperCase().codePointAt(0), 0x104CE); // OSAGE SMALL LETTER U, OSAGE CAPITAL LETTER U
|
||||
assertEq(String.fromCodePoint(0x104F7).toUpperCase().codePointAt(0), 0x104CF); // OSAGE SMALL LETTER WA, OSAGE CAPITAL LETTER WA
|
||||
assertEq(String.fromCodePoint(0x104F8).toUpperCase().codePointAt(0), 0x104D0); // OSAGE SMALL LETTER KHA, OSAGE CAPITAL LETTER KHA
|
||||
assertEq(String.fromCodePoint(0x104F9).toUpperCase().codePointAt(0), 0x104D1); // OSAGE SMALL LETTER GHA, OSAGE CAPITAL LETTER GHA
|
||||
assertEq(String.fromCodePoint(0x104FA).toUpperCase().codePointAt(0), 0x104D2); // OSAGE SMALL LETTER ZA, OSAGE CAPITAL LETTER ZA
|
||||
assertEq(String.fromCodePoint(0x104FB).toUpperCase().codePointAt(0), 0x104D3); // OSAGE SMALL LETTER ZHA, OSAGE CAPITAL LETTER ZHA
|
||||
assertEq(String.fromCodePoint(0x10CC0).toUpperCase().codePointAt(0), 0x10C80); // OLD HUNGARIAN SMALL LETTER A, OLD HUNGARIAN CAPITAL LETTER A
|
||||
assertEq(String.fromCodePoint(0x10CC1).toUpperCase().codePointAt(0), 0x10C81); // OLD HUNGARIAN SMALL LETTER AA, OLD HUNGARIAN CAPITAL LETTER AA
|
||||
assertEq(String.fromCodePoint(0x10CC2).toUpperCase().codePointAt(0), 0x10C82); // OLD HUNGARIAN SMALL LETTER EB, OLD HUNGARIAN CAPITAL LETTER EB
|
||||
assertEq(String.fromCodePoint(0x10CC3).toUpperCase().codePointAt(0), 0x10C83); // OLD HUNGARIAN SMALL LETTER AMB, OLD HUNGARIAN CAPITAL LETTER AMB
|
||||
assertEq(String.fromCodePoint(0x10CC4).toUpperCase().codePointAt(0), 0x10C84); // OLD HUNGARIAN SMALL LETTER EC, OLD HUNGARIAN CAPITAL LETTER EC
|
||||
assertEq(String.fromCodePoint(0x10CC5).toUpperCase().codePointAt(0), 0x10C85); // OLD HUNGARIAN SMALL LETTER ENC, OLD HUNGARIAN CAPITAL LETTER ENC
|
||||
assertEq(String.fromCodePoint(0x10CC6).toUpperCase().codePointAt(0), 0x10C86); // OLD HUNGARIAN SMALL LETTER ECS, OLD HUNGARIAN CAPITAL LETTER ECS
|
||||
assertEq(String.fromCodePoint(0x10CC7).toUpperCase().codePointAt(0), 0x10C87); // OLD HUNGARIAN SMALL LETTER ED, OLD HUNGARIAN CAPITAL LETTER ED
|
||||
assertEq(String.fromCodePoint(0x10CC8).toUpperCase().codePointAt(0), 0x10C88); // OLD HUNGARIAN SMALL LETTER AND, OLD HUNGARIAN CAPITAL LETTER AND
|
||||
assertEq(String.fromCodePoint(0x10CC9).toUpperCase().codePointAt(0), 0x10C89); // OLD HUNGARIAN SMALL LETTER E, OLD HUNGARIAN CAPITAL LETTER E
|
||||
assertEq(String.fromCodePoint(0x10CCA).toUpperCase().codePointAt(0), 0x10C8A); // OLD HUNGARIAN SMALL LETTER CLOSE E, OLD HUNGARIAN CAPITAL LETTER CLOSE E
|
||||
assertEq(String.fromCodePoint(0x10CCB).toUpperCase().codePointAt(0), 0x10C8B); // OLD HUNGARIAN SMALL LETTER EE, OLD HUNGARIAN CAPITAL LETTER EE
|
||||
assertEq(String.fromCodePoint(0x10CCC).toUpperCase().codePointAt(0), 0x10C8C); // OLD HUNGARIAN SMALL LETTER EF, OLD HUNGARIAN CAPITAL LETTER EF
|
||||
assertEq(String.fromCodePoint(0x10CCD).toUpperCase().codePointAt(0), 0x10C8D); // OLD HUNGARIAN SMALL LETTER EG, OLD HUNGARIAN CAPITAL LETTER EG
|
||||
assertEq(String.fromCodePoint(0x10CCE).toUpperCase().codePointAt(0), 0x10C8E); // OLD HUNGARIAN SMALL LETTER EGY, OLD HUNGARIAN CAPITAL LETTER EGY
|
||||
assertEq(String.fromCodePoint(0x10CCF).toUpperCase().codePointAt(0), 0x10C8F); // OLD HUNGARIAN SMALL LETTER EH, OLD HUNGARIAN CAPITAL LETTER EH
|
||||
assertEq(String.fromCodePoint(0x10CD0).toUpperCase().codePointAt(0), 0x10C90); // OLD HUNGARIAN SMALL LETTER I, OLD HUNGARIAN CAPITAL LETTER I
|
||||
assertEq(String.fromCodePoint(0x10CD1).toUpperCase().codePointAt(0), 0x10C91); // OLD HUNGARIAN SMALL LETTER II, OLD HUNGARIAN CAPITAL LETTER II
|
||||
assertEq(String.fromCodePoint(0x10CD2).toUpperCase().codePointAt(0), 0x10C92); // OLD HUNGARIAN SMALL LETTER EJ, OLD HUNGARIAN CAPITAL LETTER EJ
|
||||
assertEq(String.fromCodePoint(0x10CD3).toUpperCase().codePointAt(0), 0x10C93); // OLD HUNGARIAN SMALL LETTER EK, OLD HUNGARIAN CAPITAL LETTER EK
|
||||
assertEq(String.fromCodePoint(0x10CD4).toUpperCase().codePointAt(0), 0x10C94); // OLD HUNGARIAN SMALL LETTER AK, OLD HUNGARIAN CAPITAL LETTER AK
|
||||
assertEq(String.fromCodePoint(0x10CD5).toUpperCase().codePointAt(0), 0x10C95); // OLD HUNGARIAN SMALL LETTER UNK, OLD HUNGARIAN CAPITAL LETTER UNK
|
||||
assertEq(String.fromCodePoint(0x10CD6).toUpperCase().codePointAt(0), 0x10C96); // OLD HUNGARIAN SMALL LETTER EL, OLD HUNGARIAN CAPITAL LETTER EL
|
||||
assertEq(String.fromCodePoint(0x10CD7).toUpperCase().codePointAt(0), 0x10C97); // OLD HUNGARIAN SMALL LETTER ELY, OLD HUNGARIAN CAPITAL LETTER ELY
|
||||
assertEq(String.fromCodePoint(0x10CD8).toUpperCase().codePointAt(0), 0x10C98); // OLD HUNGARIAN SMALL LETTER EM, OLD HUNGARIAN CAPITAL LETTER EM
|
||||
assertEq(String.fromCodePoint(0x10CD9).toUpperCase().codePointAt(0), 0x10C99); // OLD HUNGARIAN SMALL LETTER EN, OLD HUNGARIAN CAPITAL LETTER EN
|
||||
assertEq(String.fromCodePoint(0x10CDA).toUpperCase().codePointAt(0), 0x10C9A); // OLD HUNGARIAN SMALL LETTER ENY, OLD HUNGARIAN CAPITAL LETTER ENY
|
||||
assertEq(String.fromCodePoint(0x10CDB).toUpperCase().codePointAt(0), 0x10C9B); // OLD HUNGARIAN SMALL LETTER O, OLD HUNGARIAN CAPITAL LETTER O
|
||||
assertEq(String.fromCodePoint(0x10CDC).toUpperCase().codePointAt(0), 0x10C9C); // OLD HUNGARIAN SMALL LETTER OO, OLD HUNGARIAN CAPITAL LETTER OO
|
||||
assertEq(String.fromCodePoint(0x10CDD).toUpperCase().codePointAt(0), 0x10C9D); // OLD HUNGARIAN SMALL LETTER NIKOLSBURG OE, OLD HUNGARIAN CAPITAL LETTER NIKOLSBURG OE
|
||||
assertEq(String.fromCodePoint(0x10CDE).toUpperCase().codePointAt(0), 0x10C9E); // OLD HUNGARIAN SMALL LETTER RUDIMENTA OE, OLD HUNGARIAN CAPITAL LETTER RUDIMENTA OE
|
||||
assertEq(String.fromCodePoint(0x10CDF).toUpperCase().codePointAt(0), 0x10C9F); // OLD HUNGARIAN SMALL LETTER OEE, OLD HUNGARIAN CAPITAL LETTER OEE
|
||||
assertEq(String.fromCodePoint(0x10CE0).toUpperCase().codePointAt(0), 0x10CA0); // OLD HUNGARIAN SMALL LETTER EP, OLD HUNGARIAN CAPITAL LETTER EP
|
||||
assertEq(String.fromCodePoint(0x10CE1).toUpperCase().codePointAt(0), 0x10CA1); // OLD HUNGARIAN SMALL LETTER EMP, OLD HUNGARIAN CAPITAL LETTER EMP
|
||||
assertEq(String.fromCodePoint(0x10CE2).toUpperCase().codePointAt(0), 0x10CA2); // OLD HUNGARIAN SMALL LETTER ER, OLD HUNGARIAN CAPITAL LETTER ER
|
||||
assertEq(String.fromCodePoint(0x10CE3).toUpperCase().codePointAt(0), 0x10CA3); // OLD HUNGARIAN SMALL LETTER SHORT ER, OLD HUNGARIAN CAPITAL LETTER SHORT ER
|
||||
assertEq(String.fromCodePoint(0x10CE4).toUpperCase().codePointAt(0), 0x10CA4); // OLD HUNGARIAN SMALL LETTER ES, OLD HUNGARIAN CAPITAL LETTER ES
|
||||
assertEq(String.fromCodePoint(0x10CE5).toUpperCase().codePointAt(0), 0x10CA5); // OLD HUNGARIAN SMALL LETTER ESZ, OLD HUNGARIAN CAPITAL LETTER ESZ
|
||||
assertEq(String.fromCodePoint(0x10CE6).toUpperCase().codePointAt(0), 0x10CA6); // OLD HUNGARIAN SMALL LETTER ET, OLD HUNGARIAN CAPITAL LETTER ET
|
||||
assertEq(String.fromCodePoint(0x10CE7).toUpperCase().codePointAt(0), 0x10CA7); // OLD HUNGARIAN SMALL LETTER ENT, OLD HUNGARIAN CAPITAL LETTER ENT
|
||||
assertEq(String.fromCodePoint(0x10CE8).toUpperCase().codePointAt(0), 0x10CA8); // OLD HUNGARIAN SMALL LETTER ETY, OLD HUNGARIAN CAPITAL LETTER ETY
|
||||
assertEq(String.fromCodePoint(0x10CE9).toUpperCase().codePointAt(0), 0x10CA9); // OLD HUNGARIAN SMALL LETTER ECH, OLD HUNGARIAN CAPITAL LETTER ECH
|
||||
assertEq(String.fromCodePoint(0x10CEA).toUpperCase().codePointAt(0), 0x10CAA); // OLD HUNGARIAN SMALL LETTER U, OLD HUNGARIAN CAPITAL LETTER U
|
||||
assertEq(String.fromCodePoint(0x10CEB).toUpperCase().codePointAt(0), 0x10CAB); // OLD HUNGARIAN SMALL LETTER UU, OLD HUNGARIAN CAPITAL LETTER UU
|
||||
assertEq(String.fromCodePoint(0x10CEC).toUpperCase().codePointAt(0), 0x10CAC); // OLD HUNGARIAN SMALL LETTER NIKOLSBURG UE, OLD HUNGARIAN CAPITAL LETTER NIKOLSBURG UE
|
||||
assertEq(String.fromCodePoint(0x10CED).toUpperCase().codePointAt(0), 0x10CAD); // OLD HUNGARIAN SMALL LETTER RUDIMENTA UE, OLD HUNGARIAN CAPITAL LETTER RUDIMENTA UE
|
||||
assertEq(String.fromCodePoint(0x10CEE).toUpperCase().codePointAt(0), 0x10CAE); // OLD HUNGARIAN SMALL LETTER EV, OLD HUNGARIAN CAPITAL LETTER EV
|
||||
assertEq(String.fromCodePoint(0x10CEF).toUpperCase().codePointAt(0), 0x10CAF); // OLD HUNGARIAN SMALL LETTER EZ, OLD HUNGARIAN CAPITAL LETTER EZ
|
||||
assertEq(String.fromCodePoint(0x10CF0).toUpperCase().codePointAt(0), 0x10CB0); // OLD HUNGARIAN SMALL LETTER EZS, OLD HUNGARIAN CAPITAL LETTER EZS
|
||||
assertEq(String.fromCodePoint(0x10CF1).toUpperCase().codePointAt(0), 0x10CB1); // OLD HUNGARIAN SMALL LETTER ENT-SHAPED SIGN, OLD HUNGARIAN CAPITAL LETTER ENT-SHAPED SIGN
|
||||
assertEq(String.fromCodePoint(0x10CF2).toUpperCase().codePointAt(0), 0x10CB2); // OLD HUNGARIAN SMALL LETTER US, OLD HUNGARIAN CAPITAL LETTER US
|
||||
assertEq(String.fromCodePoint(0x118C0).toUpperCase().codePointAt(0), 0x118A0); // WARANG CITI SMALL LETTER NGAA, WARANG CITI CAPITAL LETTER NGAA
|
||||
assertEq(String.fromCodePoint(0x118C1).toUpperCase().codePointAt(0), 0x118A1); // WARANG CITI SMALL LETTER A, WARANG CITI CAPITAL LETTER A
|
||||
assertEq(String.fromCodePoint(0x118C2).toUpperCase().codePointAt(0), 0x118A2); // WARANG CITI SMALL LETTER WI, WARANG CITI CAPITAL LETTER WI
|
||||
assertEq(String.fromCodePoint(0x118C3).toUpperCase().codePointAt(0), 0x118A3); // WARANG CITI SMALL LETTER YU, WARANG CITI CAPITAL LETTER YU
|
||||
assertEq(String.fromCodePoint(0x118C4).toUpperCase().codePointAt(0), 0x118A4); // WARANG CITI SMALL LETTER YA, WARANG CITI CAPITAL LETTER YA
|
||||
assertEq(String.fromCodePoint(0x118C5).toUpperCase().codePointAt(0), 0x118A5); // WARANG CITI SMALL LETTER YO, WARANG CITI CAPITAL LETTER YO
|
||||
assertEq(String.fromCodePoint(0x118C6).toUpperCase().codePointAt(0), 0x118A6); // WARANG CITI SMALL LETTER II, WARANG CITI CAPITAL LETTER II
|
||||
assertEq(String.fromCodePoint(0x118C7).toUpperCase().codePointAt(0), 0x118A7); // WARANG CITI SMALL LETTER UU, WARANG CITI CAPITAL LETTER UU
|
||||
assertEq(String.fromCodePoint(0x118C8).toUpperCase().codePointAt(0), 0x118A8); // WARANG CITI SMALL LETTER E, WARANG CITI CAPITAL LETTER E
|
||||
assertEq(String.fromCodePoint(0x118C9).toUpperCase().codePointAt(0), 0x118A9); // WARANG CITI SMALL LETTER O, WARANG CITI CAPITAL LETTER O
|
||||
assertEq(String.fromCodePoint(0x118CA).toUpperCase().codePointAt(0), 0x118AA); // WARANG CITI SMALL LETTER ANG, WARANG CITI CAPITAL LETTER ANG
|
||||
assertEq(String.fromCodePoint(0x118CB).toUpperCase().codePointAt(0), 0x118AB); // WARANG CITI SMALL LETTER GA, WARANG CITI CAPITAL LETTER GA
|
||||
assertEq(String.fromCodePoint(0x118CC).toUpperCase().codePointAt(0), 0x118AC); // WARANG CITI SMALL LETTER KO, WARANG CITI CAPITAL LETTER KO
|
||||
assertEq(String.fromCodePoint(0x118CD).toUpperCase().codePointAt(0), 0x118AD); // WARANG CITI SMALL LETTER ENY, WARANG CITI CAPITAL LETTER ENY
|
||||
assertEq(String.fromCodePoint(0x118CE).toUpperCase().codePointAt(0), 0x118AE); // WARANG CITI SMALL LETTER YUJ, WARANG CITI CAPITAL LETTER YUJ
|
||||
assertEq(String.fromCodePoint(0x118CF).toUpperCase().codePointAt(0), 0x118AF); // WARANG CITI SMALL LETTER UC, WARANG CITI CAPITAL LETTER UC
|
||||
assertEq(String.fromCodePoint(0x118D0).toUpperCase().codePointAt(0), 0x118B0); // WARANG CITI SMALL LETTER ENN, WARANG CITI CAPITAL LETTER ENN
|
||||
assertEq(String.fromCodePoint(0x118D1).toUpperCase().codePointAt(0), 0x118B1); // WARANG CITI SMALL LETTER ODD, WARANG CITI CAPITAL LETTER ODD
|
||||
assertEq(String.fromCodePoint(0x118D2).toUpperCase().codePointAt(0), 0x118B2); // WARANG CITI SMALL LETTER TTE, WARANG CITI CAPITAL LETTER TTE
|
||||
assertEq(String.fromCodePoint(0x118D3).toUpperCase().codePointAt(0), 0x118B3); // WARANG CITI SMALL LETTER NUNG, WARANG CITI CAPITAL LETTER NUNG
|
||||
assertEq(String.fromCodePoint(0x118D4).toUpperCase().codePointAt(0), 0x118B4); // WARANG CITI SMALL LETTER DA, WARANG CITI CAPITAL LETTER DA
|
||||
assertEq(String.fromCodePoint(0x118D5).toUpperCase().codePointAt(0), 0x118B5); // WARANG CITI SMALL LETTER AT, WARANG CITI CAPITAL LETTER AT
|
||||
assertEq(String.fromCodePoint(0x118D6).toUpperCase().codePointAt(0), 0x118B6); // WARANG CITI SMALL LETTER AM, WARANG CITI CAPITAL LETTER AM
|
||||
assertEq(String.fromCodePoint(0x118D7).toUpperCase().codePointAt(0), 0x118B7); // WARANG CITI SMALL LETTER BU, WARANG CITI CAPITAL LETTER BU
|
||||
assertEq(String.fromCodePoint(0x118D8).toUpperCase().codePointAt(0), 0x118B8); // WARANG CITI SMALL LETTER PU, WARANG CITI CAPITAL LETTER PU
|
||||
assertEq(String.fromCodePoint(0x118D9).toUpperCase().codePointAt(0), 0x118B9); // WARANG CITI SMALL LETTER HIYO, WARANG CITI CAPITAL LETTER HIYO
|
||||
assertEq(String.fromCodePoint(0x118DA).toUpperCase().codePointAt(0), 0x118BA); // WARANG CITI SMALL LETTER HOLO, WARANG CITI CAPITAL LETTER HOLO
|
||||
assertEq(String.fromCodePoint(0x118DB).toUpperCase().codePointAt(0), 0x118BB); // WARANG CITI SMALL LETTER HORR, WARANG CITI CAPITAL LETTER HORR
|
||||
assertEq(String.fromCodePoint(0x118DC).toUpperCase().codePointAt(0), 0x118BC); // WARANG CITI SMALL LETTER HAR, WARANG CITI CAPITAL LETTER HAR
|
||||
assertEq(String.fromCodePoint(0x118DD).toUpperCase().codePointAt(0), 0x118BD); // WARANG CITI SMALL LETTER SSUU, WARANG CITI CAPITAL LETTER SSUU
|
||||
assertEq(String.fromCodePoint(0x118DE).toUpperCase().codePointAt(0), 0x118BE); // WARANG CITI SMALL LETTER SII, WARANG CITI CAPITAL LETTER SII
|
||||
assertEq(String.fromCodePoint(0x118DF).toUpperCase().codePointAt(0), 0x118BF); // WARANG CITI SMALL LETTER VIYO, WARANG CITI CAPITAL LETTER VIYO
|
||||
assertEq(String.fromCodePoint(0x16E60).toUpperCase().codePointAt(0), 0x16E40); // MEDEFAIDRIN SMALL LETTER M, MEDEFAIDRIN CAPITAL LETTER M
|
||||
assertEq(String.fromCodePoint(0x16E61).toUpperCase().codePointAt(0), 0x16E41); // MEDEFAIDRIN SMALL LETTER S, MEDEFAIDRIN CAPITAL LETTER S
|
||||
assertEq(String.fromCodePoint(0x16E62).toUpperCase().codePointAt(0), 0x16E42); // MEDEFAIDRIN SMALL LETTER V, MEDEFAIDRIN CAPITAL LETTER V
|
||||
assertEq(String.fromCodePoint(0x16E63).toUpperCase().codePointAt(0), 0x16E43); // MEDEFAIDRIN SMALL LETTER W, MEDEFAIDRIN CAPITAL LETTER W
|
||||
assertEq(String.fromCodePoint(0x16E64).toUpperCase().codePointAt(0), 0x16E44); // MEDEFAIDRIN SMALL LETTER ATIU, MEDEFAIDRIN CAPITAL LETTER ATIU
|
||||
assertEq(String.fromCodePoint(0x16E65).toUpperCase().codePointAt(0), 0x16E45); // MEDEFAIDRIN SMALL LETTER Z, MEDEFAIDRIN CAPITAL LETTER Z
|
||||
assertEq(String.fromCodePoint(0x16E66).toUpperCase().codePointAt(0), 0x16E46); // MEDEFAIDRIN SMALL LETTER KP, MEDEFAIDRIN CAPITAL LETTER KP
|
||||
assertEq(String.fromCodePoint(0x16E67).toUpperCase().codePointAt(0), 0x16E47); // MEDEFAIDRIN SMALL LETTER P, MEDEFAIDRIN CAPITAL LETTER P
|
||||
assertEq(String.fromCodePoint(0x16E68).toUpperCase().codePointAt(0), 0x16E48); // MEDEFAIDRIN SMALL LETTER T, MEDEFAIDRIN CAPITAL LETTER T
|
||||
assertEq(String.fromCodePoint(0x16E69).toUpperCase().codePointAt(0), 0x16E49); // MEDEFAIDRIN SMALL LETTER G, MEDEFAIDRIN CAPITAL LETTER G
|
||||
assertEq(String.fromCodePoint(0x16E6A).toUpperCase().codePointAt(0), 0x16E4A); // MEDEFAIDRIN SMALL LETTER F, MEDEFAIDRIN CAPITAL LETTER F
|
||||
assertEq(String.fromCodePoint(0x16E6B).toUpperCase().codePointAt(0), 0x16E4B); // MEDEFAIDRIN SMALL LETTER I, MEDEFAIDRIN CAPITAL LETTER I
|
||||
assertEq(String.fromCodePoint(0x16E6C).toUpperCase().codePointAt(0), 0x16E4C); // MEDEFAIDRIN SMALL LETTER K, MEDEFAIDRIN CAPITAL LETTER K
|
||||
assertEq(String.fromCodePoint(0x16E6D).toUpperCase().codePointAt(0), 0x16E4D); // MEDEFAIDRIN SMALL LETTER A, MEDEFAIDRIN CAPITAL LETTER A
|
||||
assertEq(String.fromCodePoint(0x16E6E).toUpperCase().codePointAt(0), 0x16E4E); // MEDEFAIDRIN SMALL LETTER J, MEDEFAIDRIN CAPITAL LETTER J
|
||||
assertEq(String.fromCodePoint(0x16E6F).toUpperCase().codePointAt(0), 0x16E4F); // MEDEFAIDRIN SMALL LETTER E, MEDEFAIDRIN CAPITAL LETTER E
|
||||
assertEq(String.fromCodePoint(0x16E70).toUpperCase().codePointAt(0), 0x16E50); // MEDEFAIDRIN SMALL LETTER B, MEDEFAIDRIN CAPITAL LETTER B
|
||||
assertEq(String.fromCodePoint(0x16E71).toUpperCase().codePointAt(0), 0x16E51); // MEDEFAIDRIN SMALL LETTER C, MEDEFAIDRIN CAPITAL LETTER C
|
||||
assertEq(String.fromCodePoint(0x16E72).toUpperCase().codePointAt(0), 0x16E52); // MEDEFAIDRIN SMALL LETTER U, MEDEFAIDRIN CAPITAL LETTER U
|
||||
assertEq(String.fromCodePoint(0x16E73).toUpperCase().codePointAt(0), 0x16E53); // MEDEFAIDRIN SMALL LETTER YU, MEDEFAIDRIN CAPITAL LETTER YU
|
||||
assertEq(String.fromCodePoint(0x16E74).toUpperCase().codePointAt(0), 0x16E54); // MEDEFAIDRIN SMALL LETTER L, MEDEFAIDRIN CAPITAL LETTER L
|
||||
assertEq(String.fromCodePoint(0x16E75).toUpperCase().codePointAt(0), 0x16E55); // MEDEFAIDRIN SMALL LETTER Q, MEDEFAIDRIN CAPITAL LETTER Q
|
||||
assertEq(String.fromCodePoint(0x16E76).toUpperCase().codePointAt(0), 0x16E56); // MEDEFAIDRIN SMALL LETTER HP, MEDEFAIDRIN CAPITAL LETTER HP
|
||||
assertEq(String.fromCodePoint(0x16E77).toUpperCase().codePointAt(0), 0x16E57); // MEDEFAIDRIN SMALL LETTER NY, MEDEFAIDRIN CAPITAL LETTER NY
|
||||
assertEq(String.fromCodePoint(0x16E78).toUpperCase().codePointAt(0), 0x16E58); // MEDEFAIDRIN SMALL LETTER X, MEDEFAIDRIN CAPITAL LETTER X
|
||||
assertEq(String.fromCodePoint(0x16E79).toUpperCase().codePointAt(0), 0x16E59); // MEDEFAIDRIN SMALL LETTER D, MEDEFAIDRIN CAPITAL LETTER D
|
||||
assertEq(String.fromCodePoint(0x16E7A).toUpperCase().codePointAt(0), 0x16E5A); // MEDEFAIDRIN SMALL LETTER OE, MEDEFAIDRIN CAPITAL LETTER OE
|
||||
assertEq(String.fromCodePoint(0x16E7B).toUpperCase().codePointAt(0), 0x16E5B); // MEDEFAIDRIN SMALL LETTER N, MEDEFAIDRIN CAPITAL LETTER N
|
||||
assertEq(String.fromCodePoint(0x16E7C).toUpperCase().codePointAt(0), 0x16E5C); // MEDEFAIDRIN SMALL LETTER R, MEDEFAIDRIN CAPITAL LETTER R
|
||||
assertEq(String.fromCodePoint(0x16E7D).toUpperCase().codePointAt(0), 0x16E5D); // MEDEFAIDRIN SMALL LETTER O, MEDEFAIDRIN CAPITAL LETTER O
|
||||
assertEq(String.fromCodePoint(0x16E7E).toUpperCase().codePointAt(0), 0x16E5E); // MEDEFAIDRIN SMALL LETTER AI, MEDEFAIDRIN CAPITAL LETTER AI
|
||||
assertEq(String.fromCodePoint(0x16E7F).toUpperCase().codePointAt(0), 0x16E5F); // MEDEFAIDRIN SMALL LETTER Y, MEDEFAIDRIN CAPITAL LETTER Y
|
||||
assertEq(String.fromCodePoint(0x1E922).toUpperCase().codePointAt(0), 0x1E900); // ADLAM SMALL LETTER ALIF, ADLAM CAPITAL LETTER ALIF
|
||||
assertEq(String.fromCodePoint(0x1E923).toUpperCase().codePointAt(0), 0x1E901); // ADLAM SMALL LETTER DAALI, ADLAM CAPITAL LETTER DAALI
|
||||
assertEq(String.fromCodePoint(0x1E924).toUpperCase().codePointAt(0), 0x1E902); // ADLAM SMALL LETTER LAAM, ADLAM CAPITAL LETTER LAAM
|
||||
assertEq(String.fromCodePoint(0x1E925).toUpperCase().codePointAt(0), 0x1E903); // ADLAM SMALL LETTER MIIM, ADLAM CAPITAL LETTER MIIM
|
||||
assertEq(String.fromCodePoint(0x1E926).toUpperCase().codePointAt(0), 0x1E904); // ADLAM SMALL LETTER BA, ADLAM CAPITAL LETTER BA
|
||||
assertEq(String.fromCodePoint(0x1E927).toUpperCase().codePointAt(0), 0x1E905); // ADLAM SMALL LETTER SINNYIIYHE, ADLAM CAPITAL LETTER SINNYIIYHE
|
||||
assertEq(String.fromCodePoint(0x1E928).toUpperCase().codePointAt(0), 0x1E906); // ADLAM SMALL LETTER PE, ADLAM CAPITAL LETTER PE
|
||||
assertEq(String.fromCodePoint(0x1E929).toUpperCase().codePointAt(0), 0x1E907); // ADLAM SMALL LETTER BHE, ADLAM CAPITAL LETTER BHE
|
||||
assertEq(String.fromCodePoint(0x1E92A).toUpperCase().codePointAt(0), 0x1E908); // ADLAM SMALL LETTER RA, ADLAM CAPITAL LETTER RA
|
||||
assertEq(String.fromCodePoint(0x1E92B).toUpperCase().codePointAt(0), 0x1E909); // ADLAM SMALL LETTER E, ADLAM CAPITAL LETTER E
|
||||
assertEq(String.fromCodePoint(0x1E92C).toUpperCase().codePointAt(0), 0x1E90A); // ADLAM SMALL LETTER FA, ADLAM CAPITAL LETTER FA
|
||||
assertEq(String.fromCodePoint(0x1E92D).toUpperCase().codePointAt(0), 0x1E90B); // ADLAM SMALL LETTER I, ADLAM CAPITAL LETTER I
|
||||
assertEq(String.fromCodePoint(0x1E92E).toUpperCase().codePointAt(0), 0x1E90C); // ADLAM SMALL LETTER O, ADLAM CAPITAL LETTER O
|
||||
assertEq(String.fromCodePoint(0x1E92F).toUpperCase().codePointAt(0), 0x1E90D); // ADLAM SMALL LETTER DHA, ADLAM CAPITAL LETTER DHA
|
||||
assertEq(String.fromCodePoint(0x1E930).toUpperCase().codePointAt(0), 0x1E90E); // ADLAM SMALL LETTER YHE, ADLAM CAPITAL LETTER YHE
|
||||
assertEq(String.fromCodePoint(0x1E931).toUpperCase().codePointAt(0), 0x1E90F); // ADLAM SMALL LETTER WAW, ADLAM CAPITAL LETTER WAW
|
||||
assertEq(String.fromCodePoint(0x1E932).toUpperCase().codePointAt(0), 0x1E910); // ADLAM SMALL LETTER NUN, ADLAM CAPITAL LETTER NUN
|
||||
assertEq(String.fromCodePoint(0x1E933).toUpperCase().codePointAt(0), 0x1E911); // ADLAM SMALL LETTER KAF, ADLAM CAPITAL LETTER KAF
|
||||
assertEq(String.fromCodePoint(0x1E934).toUpperCase().codePointAt(0), 0x1E912); // ADLAM SMALL LETTER YA, ADLAM CAPITAL LETTER YA
|
||||
assertEq(String.fromCodePoint(0x1E935).toUpperCase().codePointAt(0), 0x1E913); // ADLAM SMALL LETTER U, ADLAM CAPITAL LETTER U
|
||||
assertEq(String.fromCodePoint(0x1E936).toUpperCase().codePointAt(0), 0x1E914); // ADLAM SMALL LETTER JIIM, ADLAM CAPITAL LETTER JIIM
|
||||
assertEq(String.fromCodePoint(0x1E937).toUpperCase().codePointAt(0), 0x1E915); // ADLAM SMALL LETTER CHI, ADLAM CAPITAL LETTER CHI
|
||||
assertEq(String.fromCodePoint(0x1E938).toUpperCase().codePointAt(0), 0x1E916); // ADLAM SMALL LETTER HA, ADLAM CAPITAL LETTER HA
|
||||
assertEq(String.fromCodePoint(0x1E939).toUpperCase().codePointAt(0), 0x1E917); // ADLAM SMALL LETTER QAAF, ADLAM CAPITAL LETTER QAAF
|
||||
assertEq(String.fromCodePoint(0x1E93A).toUpperCase().codePointAt(0), 0x1E918); // ADLAM SMALL LETTER GA, ADLAM CAPITAL LETTER GA
|
||||
assertEq(String.fromCodePoint(0x1E93B).toUpperCase().codePointAt(0), 0x1E919); // ADLAM SMALL LETTER NYA, ADLAM CAPITAL LETTER NYA
|
||||
assertEq(String.fromCodePoint(0x1E93C).toUpperCase().codePointAt(0), 0x1E91A); // ADLAM SMALL LETTER TU, ADLAM CAPITAL LETTER TU
|
||||
assertEq(String.fromCodePoint(0x1E93D).toUpperCase().codePointAt(0), 0x1E91B); // ADLAM SMALL LETTER NHA, ADLAM CAPITAL LETTER NHA
|
||||
assertEq(String.fromCodePoint(0x1E93E).toUpperCase().codePointAt(0), 0x1E91C); // ADLAM SMALL LETTER VA, ADLAM CAPITAL LETTER VA
|
||||
assertEq(String.fromCodePoint(0x1E93F).toUpperCase().codePointAt(0), 0x1E91D); // ADLAM SMALL LETTER KHA, ADLAM CAPITAL LETTER KHA
|
||||
assertEq(String.fromCodePoint(0x1E940).toUpperCase().codePointAt(0), 0x1E91E); // ADLAM SMALL LETTER GBE, ADLAM CAPITAL LETTER GBE
|
||||
assertEq(String.fromCodePoint(0x1E941).toUpperCase().codePointAt(0), 0x1E91F); // ADLAM SMALL LETTER ZAL, ADLAM CAPITAL LETTER ZAL
|
||||
assertEq(String.fromCodePoint(0x1E942).toUpperCase().codePointAt(0), 0x1E920); // ADLAM SMALL LETTER KPO, ADLAM CAPITAL LETTER KPO
|
||||
assertEq(String.fromCodePoint(0x1E943).toUpperCase().codePointAt(0), 0x1E921); // ADLAM SMALL LETTER SHA, ADLAM CAPITAL LETTER SHA
|
||||
assertEq(String.fromCodePoint(0x10400).toLowerCase().codePointAt(0), 0x10428); // DESERET CAPITAL LETTER LONG I, DESERET SMALL LETTER LONG I
|
||||
assertEq(String.fromCodePoint(0x10401).toLowerCase().codePointAt(0), 0x10429); // DESERET CAPITAL LETTER LONG E, DESERET SMALL LETTER LONG E
|
||||
assertEq(String.fromCodePoint(0x10402).toLowerCase().codePointAt(0), 0x1042A); // DESERET CAPITAL LETTER LONG A, DESERET SMALL LETTER LONG A
|
||||
assertEq(String.fromCodePoint(0x10403).toLowerCase().codePointAt(0), 0x1042B); // DESERET CAPITAL LETTER LONG AH, DESERET SMALL LETTER LONG AH
|
||||
assertEq(String.fromCodePoint(0x10404).toLowerCase().codePointAt(0), 0x1042C); // DESERET CAPITAL LETTER LONG O, DESERET SMALL LETTER LONG O
|
||||
assertEq(String.fromCodePoint(0x10405).toLowerCase().codePointAt(0), 0x1042D); // DESERET CAPITAL LETTER LONG OO, DESERET SMALL LETTER LONG OO
|
||||
assertEq(String.fromCodePoint(0x10406).toLowerCase().codePointAt(0), 0x1042E); // DESERET CAPITAL LETTER SHORT I, DESERET SMALL LETTER SHORT I
|
||||
assertEq(String.fromCodePoint(0x10407).toLowerCase().codePointAt(0), 0x1042F); // DESERET CAPITAL LETTER SHORT E, DESERET SMALL LETTER SHORT E
|
||||
assertEq(String.fromCodePoint(0x10408).toLowerCase().codePointAt(0), 0x10430); // DESERET CAPITAL LETTER SHORT A, DESERET SMALL LETTER SHORT A
|
||||
assertEq(String.fromCodePoint(0x10409).toLowerCase().codePointAt(0), 0x10431); // DESERET CAPITAL LETTER SHORT AH, DESERET SMALL LETTER SHORT AH
|
||||
assertEq(String.fromCodePoint(0x1040A).toLowerCase().codePointAt(0), 0x10432); // DESERET CAPITAL LETTER SHORT O, DESERET SMALL LETTER SHORT O
|
||||
assertEq(String.fromCodePoint(0x1040B).toLowerCase().codePointAt(0), 0x10433); // DESERET CAPITAL LETTER SHORT OO, DESERET SMALL LETTER SHORT OO
|
||||
assertEq(String.fromCodePoint(0x1040C).toLowerCase().codePointAt(0), 0x10434); // DESERET CAPITAL LETTER AY, DESERET SMALL LETTER AY
|
||||
assertEq(String.fromCodePoint(0x1040D).toLowerCase().codePointAt(0), 0x10435); // DESERET CAPITAL LETTER OW, DESERET SMALL LETTER OW
|
||||
assertEq(String.fromCodePoint(0x1040E).toLowerCase().codePointAt(0), 0x10436); // DESERET CAPITAL LETTER WU, DESERET SMALL LETTER WU
|
||||
assertEq(String.fromCodePoint(0x1040F).toLowerCase().codePointAt(0), 0x10437); // DESERET CAPITAL LETTER YEE, DESERET SMALL LETTER YEE
|
||||
assertEq(String.fromCodePoint(0x10410).toLowerCase().codePointAt(0), 0x10438); // DESERET CAPITAL LETTER H, DESERET SMALL LETTER H
|
||||
assertEq(String.fromCodePoint(0x10411).toLowerCase().codePointAt(0), 0x10439); // DESERET CAPITAL LETTER PEE, DESERET SMALL LETTER PEE
|
||||
assertEq(String.fromCodePoint(0x10412).toLowerCase().codePointAt(0), 0x1043A); // DESERET CAPITAL LETTER BEE, DESERET SMALL LETTER BEE
|
||||
assertEq(String.fromCodePoint(0x10413).toLowerCase().codePointAt(0), 0x1043B); // DESERET CAPITAL LETTER TEE, DESERET SMALL LETTER TEE
|
||||
assertEq(String.fromCodePoint(0x10414).toLowerCase().codePointAt(0), 0x1043C); // DESERET CAPITAL LETTER DEE, DESERET SMALL LETTER DEE
|
||||
assertEq(String.fromCodePoint(0x10415).toLowerCase().codePointAt(0), 0x1043D); // DESERET CAPITAL LETTER CHEE, DESERET SMALL LETTER CHEE
|
||||
assertEq(String.fromCodePoint(0x10416).toLowerCase().codePointAt(0), 0x1043E); // DESERET CAPITAL LETTER JEE, DESERET SMALL LETTER JEE
|
||||
assertEq(String.fromCodePoint(0x10417).toLowerCase().codePointAt(0), 0x1043F); // DESERET CAPITAL LETTER KAY, DESERET SMALL LETTER KAY
|
||||
assertEq(String.fromCodePoint(0x10418).toLowerCase().codePointAt(0), 0x10440); // DESERET CAPITAL LETTER GAY, DESERET SMALL LETTER GAY
|
||||
assertEq(String.fromCodePoint(0x10419).toLowerCase().codePointAt(0), 0x10441); // DESERET CAPITAL LETTER EF, DESERET SMALL LETTER EF
|
||||
assertEq(String.fromCodePoint(0x1041A).toLowerCase().codePointAt(0), 0x10442); // DESERET CAPITAL LETTER VEE, DESERET SMALL LETTER VEE
|
||||
assertEq(String.fromCodePoint(0x1041B).toLowerCase().codePointAt(0), 0x10443); // DESERET CAPITAL LETTER ETH, DESERET SMALL LETTER ETH
|
||||
assertEq(String.fromCodePoint(0x1041C).toLowerCase().codePointAt(0), 0x10444); // DESERET CAPITAL LETTER THEE, DESERET SMALL LETTER THEE
|
||||
assertEq(String.fromCodePoint(0x1041D).toLowerCase().codePointAt(0), 0x10445); // DESERET CAPITAL LETTER ES, DESERET SMALL LETTER ES
|
||||
assertEq(String.fromCodePoint(0x1041E).toLowerCase().codePointAt(0), 0x10446); // DESERET CAPITAL LETTER ZEE, DESERET SMALL LETTER ZEE
|
||||
assertEq(String.fromCodePoint(0x1041F).toLowerCase().codePointAt(0), 0x10447); // DESERET CAPITAL LETTER ESH, DESERET SMALL LETTER ESH
|
||||
assertEq(String.fromCodePoint(0x10420).toLowerCase().codePointAt(0), 0x10448); // DESERET CAPITAL LETTER ZHEE, DESERET SMALL LETTER ZHEE
|
||||
assertEq(String.fromCodePoint(0x10421).toLowerCase().codePointAt(0), 0x10449); // DESERET CAPITAL LETTER ER, DESERET SMALL LETTER ER
|
||||
assertEq(String.fromCodePoint(0x10422).toLowerCase().codePointAt(0), 0x1044A); // DESERET CAPITAL LETTER EL, DESERET SMALL LETTER EL
|
||||
assertEq(String.fromCodePoint(0x10423).toLowerCase().codePointAt(0), 0x1044B); // DESERET CAPITAL LETTER EM, DESERET SMALL LETTER EM
|
||||
assertEq(String.fromCodePoint(0x10424).toLowerCase().codePointAt(0), 0x1044C); // DESERET CAPITAL LETTER EN, DESERET SMALL LETTER EN
|
||||
assertEq(String.fromCodePoint(0x10425).toLowerCase().codePointAt(0), 0x1044D); // DESERET CAPITAL LETTER ENG, DESERET SMALL LETTER ENG
|
||||
assertEq(String.fromCodePoint(0x10426).toLowerCase().codePointAt(0), 0x1044E); // DESERET CAPITAL LETTER OI, DESERET SMALL LETTER OI
|
||||
assertEq(String.fromCodePoint(0x10427).toLowerCase().codePointAt(0), 0x1044F); // DESERET CAPITAL LETTER EW, DESERET SMALL LETTER EW
|
||||
assertEq(String.fromCodePoint(0x104B0).toLowerCase().codePointAt(0), 0x104D8); // OSAGE CAPITAL LETTER A, OSAGE SMALL LETTER A
|
||||
assertEq(String.fromCodePoint(0x104B1).toLowerCase().codePointAt(0), 0x104D9); // OSAGE CAPITAL LETTER AI, OSAGE SMALL LETTER AI
|
||||
assertEq(String.fromCodePoint(0x104B2).toLowerCase().codePointAt(0), 0x104DA); // OSAGE CAPITAL LETTER AIN, OSAGE SMALL LETTER AIN
|
||||
assertEq(String.fromCodePoint(0x104B3).toLowerCase().codePointAt(0), 0x104DB); // OSAGE CAPITAL LETTER AH, OSAGE SMALL LETTER AH
|
||||
assertEq(String.fromCodePoint(0x104B4).toLowerCase().codePointAt(0), 0x104DC); // OSAGE CAPITAL LETTER BRA, OSAGE SMALL LETTER BRA
|
||||
assertEq(String.fromCodePoint(0x104B5).toLowerCase().codePointAt(0), 0x104DD); // OSAGE CAPITAL LETTER CHA, OSAGE SMALL LETTER CHA
|
||||
assertEq(String.fromCodePoint(0x104B6).toLowerCase().codePointAt(0), 0x104DE); // OSAGE CAPITAL LETTER EHCHA, OSAGE SMALL LETTER EHCHA
|
||||
assertEq(String.fromCodePoint(0x104B7).toLowerCase().codePointAt(0), 0x104DF); // OSAGE CAPITAL LETTER E, OSAGE SMALL LETTER E
|
||||
assertEq(String.fromCodePoint(0x104B8).toLowerCase().codePointAt(0), 0x104E0); // OSAGE CAPITAL LETTER EIN, OSAGE SMALL LETTER EIN
|
||||
assertEq(String.fromCodePoint(0x104B9).toLowerCase().codePointAt(0), 0x104E1); // OSAGE CAPITAL LETTER HA, OSAGE SMALL LETTER HA
|
||||
assertEq(String.fromCodePoint(0x104BA).toLowerCase().codePointAt(0), 0x104E2); // OSAGE CAPITAL LETTER HYA, OSAGE SMALL LETTER HYA
|
||||
assertEq(String.fromCodePoint(0x104BB).toLowerCase().codePointAt(0), 0x104E3); // OSAGE CAPITAL LETTER I, OSAGE SMALL LETTER I
|
||||
assertEq(String.fromCodePoint(0x104BC).toLowerCase().codePointAt(0), 0x104E4); // OSAGE CAPITAL LETTER KA, OSAGE SMALL LETTER KA
|
||||
assertEq(String.fromCodePoint(0x104BD).toLowerCase().codePointAt(0), 0x104E5); // OSAGE CAPITAL LETTER EHKA, OSAGE SMALL LETTER EHKA
|
||||
assertEq(String.fromCodePoint(0x104BE).toLowerCase().codePointAt(0), 0x104E6); // OSAGE CAPITAL LETTER KYA, OSAGE SMALL LETTER KYA
|
||||
assertEq(String.fromCodePoint(0x104BF).toLowerCase().codePointAt(0), 0x104E7); // OSAGE CAPITAL LETTER LA, OSAGE SMALL LETTER LA
|
||||
assertEq(String.fromCodePoint(0x104C0).toLowerCase().codePointAt(0), 0x104E8); // OSAGE CAPITAL LETTER MA, OSAGE SMALL LETTER MA
|
||||
assertEq(String.fromCodePoint(0x104C1).toLowerCase().codePointAt(0), 0x104E9); // OSAGE CAPITAL LETTER NA, OSAGE SMALL LETTER NA
|
||||
assertEq(String.fromCodePoint(0x104C2).toLowerCase().codePointAt(0), 0x104EA); // OSAGE CAPITAL LETTER O, OSAGE SMALL LETTER O
|
||||
assertEq(String.fromCodePoint(0x104C3).toLowerCase().codePointAt(0), 0x104EB); // OSAGE CAPITAL LETTER OIN, OSAGE SMALL LETTER OIN
|
||||
assertEq(String.fromCodePoint(0x104C4).toLowerCase().codePointAt(0), 0x104EC); // OSAGE CAPITAL LETTER PA, OSAGE SMALL LETTER PA
|
||||
assertEq(String.fromCodePoint(0x104C5).toLowerCase().codePointAt(0), 0x104ED); // OSAGE CAPITAL LETTER EHPA, OSAGE SMALL LETTER EHPA
|
||||
assertEq(String.fromCodePoint(0x104C6).toLowerCase().codePointAt(0), 0x104EE); // OSAGE CAPITAL LETTER SA, OSAGE SMALL LETTER SA
|
||||
assertEq(String.fromCodePoint(0x104C7).toLowerCase().codePointAt(0), 0x104EF); // OSAGE CAPITAL LETTER SHA, OSAGE SMALL LETTER SHA
|
||||
assertEq(String.fromCodePoint(0x104C8).toLowerCase().codePointAt(0), 0x104F0); // OSAGE CAPITAL LETTER TA, OSAGE SMALL LETTER TA
|
||||
assertEq(String.fromCodePoint(0x104C9).toLowerCase().codePointAt(0), 0x104F1); // OSAGE CAPITAL LETTER EHTA, OSAGE SMALL LETTER EHTA
|
||||
assertEq(String.fromCodePoint(0x104CA).toLowerCase().codePointAt(0), 0x104F2); // OSAGE CAPITAL LETTER TSA, OSAGE SMALL LETTER TSA
|
||||
assertEq(String.fromCodePoint(0x104CB).toLowerCase().codePointAt(0), 0x104F3); // OSAGE CAPITAL LETTER EHTSA, OSAGE SMALL LETTER EHTSA
|
||||
assertEq(String.fromCodePoint(0x104CC).toLowerCase().codePointAt(0), 0x104F4); // OSAGE CAPITAL LETTER TSHA, OSAGE SMALL LETTER TSHA
|
||||
assertEq(String.fromCodePoint(0x104CD).toLowerCase().codePointAt(0), 0x104F5); // OSAGE CAPITAL LETTER DHA, OSAGE SMALL LETTER DHA
|
||||
assertEq(String.fromCodePoint(0x104CE).toLowerCase().codePointAt(0), 0x104F6); // OSAGE CAPITAL LETTER U, OSAGE SMALL LETTER U
|
||||
assertEq(String.fromCodePoint(0x104CF).toLowerCase().codePointAt(0), 0x104F7); // OSAGE CAPITAL LETTER WA, OSAGE SMALL LETTER WA
|
||||
assertEq(String.fromCodePoint(0x104D0).toLowerCase().codePointAt(0), 0x104F8); // OSAGE CAPITAL LETTER KHA, OSAGE SMALL LETTER KHA
|
||||
assertEq(String.fromCodePoint(0x104D1).toLowerCase().codePointAt(0), 0x104F9); // OSAGE CAPITAL LETTER GHA, OSAGE SMALL LETTER GHA
|
||||
assertEq(String.fromCodePoint(0x104D2).toLowerCase().codePointAt(0), 0x104FA); // OSAGE CAPITAL LETTER ZA, OSAGE SMALL LETTER ZA
|
||||
assertEq(String.fromCodePoint(0x104D3).toLowerCase().codePointAt(0), 0x104FB); // OSAGE CAPITAL LETTER ZHA, OSAGE SMALL LETTER ZHA
|
||||
assertEq(String.fromCodePoint(0x10C80).toLowerCase().codePointAt(0), 0x10CC0); // OLD HUNGARIAN CAPITAL LETTER A, OLD HUNGARIAN SMALL LETTER A
|
||||
assertEq(String.fromCodePoint(0x10C81).toLowerCase().codePointAt(0), 0x10CC1); // OLD HUNGARIAN CAPITAL LETTER AA, OLD HUNGARIAN SMALL LETTER AA
|
||||
assertEq(String.fromCodePoint(0x10C82).toLowerCase().codePointAt(0), 0x10CC2); // OLD HUNGARIAN CAPITAL LETTER EB, OLD HUNGARIAN SMALL LETTER EB
|
||||
assertEq(String.fromCodePoint(0x10C83).toLowerCase().codePointAt(0), 0x10CC3); // OLD HUNGARIAN CAPITAL LETTER AMB, OLD HUNGARIAN SMALL LETTER AMB
|
||||
assertEq(String.fromCodePoint(0x10C84).toLowerCase().codePointAt(0), 0x10CC4); // OLD HUNGARIAN CAPITAL LETTER EC, OLD HUNGARIAN SMALL LETTER EC
|
||||
assertEq(String.fromCodePoint(0x10C85).toLowerCase().codePointAt(0), 0x10CC5); // OLD HUNGARIAN CAPITAL LETTER ENC, OLD HUNGARIAN SMALL LETTER ENC
|
||||
assertEq(String.fromCodePoint(0x10C86).toLowerCase().codePointAt(0), 0x10CC6); // OLD HUNGARIAN CAPITAL LETTER ECS, OLD HUNGARIAN SMALL LETTER ECS
|
||||
assertEq(String.fromCodePoint(0x10C87).toLowerCase().codePointAt(0), 0x10CC7); // OLD HUNGARIAN CAPITAL LETTER ED, OLD HUNGARIAN SMALL LETTER ED
|
||||
assertEq(String.fromCodePoint(0x10C88).toLowerCase().codePointAt(0), 0x10CC8); // OLD HUNGARIAN CAPITAL LETTER AND, OLD HUNGARIAN SMALL LETTER AND
|
||||
assertEq(String.fromCodePoint(0x10C89).toLowerCase().codePointAt(0), 0x10CC9); // OLD HUNGARIAN CAPITAL LETTER E, OLD HUNGARIAN SMALL LETTER E
|
||||
assertEq(String.fromCodePoint(0x10C8A).toLowerCase().codePointAt(0), 0x10CCA); // OLD HUNGARIAN CAPITAL LETTER CLOSE E, OLD HUNGARIAN SMALL LETTER CLOSE E
|
||||
assertEq(String.fromCodePoint(0x10C8B).toLowerCase().codePointAt(0), 0x10CCB); // OLD HUNGARIAN CAPITAL LETTER EE, OLD HUNGARIAN SMALL LETTER EE
|
||||
assertEq(String.fromCodePoint(0x10C8C).toLowerCase().codePointAt(0), 0x10CCC); // OLD HUNGARIAN CAPITAL LETTER EF, OLD HUNGARIAN SMALL LETTER EF
|
||||
assertEq(String.fromCodePoint(0x10C8D).toLowerCase().codePointAt(0), 0x10CCD); // OLD HUNGARIAN CAPITAL LETTER EG, OLD HUNGARIAN SMALL LETTER EG
|
||||
assertEq(String.fromCodePoint(0x10C8E).toLowerCase().codePointAt(0), 0x10CCE); // OLD HUNGARIAN CAPITAL LETTER EGY, OLD HUNGARIAN SMALL LETTER EGY
|
||||
assertEq(String.fromCodePoint(0x10C8F).toLowerCase().codePointAt(0), 0x10CCF); // OLD HUNGARIAN CAPITAL LETTER EH, OLD HUNGARIAN SMALL LETTER EH
|
||||
assertEq(String.fromCodePoint(0x10C90).toLowerCase().codePointAt(0), 0x10CD0); // OLD HUNGARIAN CAPITAL LETTER I, OLD HUNGARIAN SMALL LETTER I
|
||||
assertEq(String.fromCodePoint(0x10C91).toLowerCase().codePointAt(0), 0x10CD1); // OLD HUNGARIAN CAPITAL LETTER II, OLD HUNGARIAN SMALL LETTER II
|
||||
assertEq(String.fromCodePoint(0x10C92).toLowerCase().codePointAt(0), 0x10CD2); // OLD HUNGARIAN CAPITAL LETTER EJ, OLD HUNGARIAN SMALL LETTER EJ
|
||||
assertEq(String.fromCodePoint(0x10C93).toLowerCase().codePointAt(0), 0x10CD3); // OLD HUNGARIAN CAPITAL LETTER EK, OLD HUNGARIAN SMALL LETTER EK
|
||||
assertEq(String.fromCodePoint(0x10C94).toLowerCase().codePointAt(0), 0x10CD4); // OLD HUNGARIAN CAPITAL LETTER AK, OLD HUNGARIAN SMALL LETTER AK
|
||||
assertEq(String.fromCodePoint(0x10C95).toLowerCase().codePointAt(0), 0x10CD5); // OLD HUNGARIAN CAPITAL LETTER UNK, OLD HUNGARIAN SMALL LETTER UNK
|
||||
assertEq(String.fromCodePoint(0x10C96).toLowerCase().codePointAt(0), 0x10CD6); // OLD HUNGARIAN CAPITAL LETTER EL, OLD HUNGARIAN SMALL LETTER EL
|
||||
assertEq(String.fromCodePoint(0x10C97).toLowerCase().codePointAt(0), 0x10CD7); // OLD HUNGARIAN CAPITAL LETTER ELY, OLD HUNGARIAN SMALL LETTER ELY
|
||||
assertEq(String.fromCodePoint(0x10C98).toLowerCase().codePointAt(0), 0x10CD8); // OLD HUNGARIAN CAPITAL LETTER EM, OLD HUNGARIAN SMALL LETTER EM
|
||||
assertEq(String.fromCodePoint(0x10C99).toLowerCase().codePointAt(0), 0x10CD9); // OLD HUNGARIAN CAPITAL LETTER EN, OLD HUNGARIAN SMALL LETTER EN
|
||||
assertEq(String.fromCodePoint(0x10C9A).toLowerCase().codePointAt(0), 0x10CDA); // OLD HUNGARIAN CAPITAL LETTER ENY, OLD HUNGARIAN SMALL LETTER ENY
|
||||
assertEq(String.fromCodePoint(0x10C9B).toLowerCase().codePointAt(0), 0x10CDB); // OLD HUNGARIAN CAPITAL LETTER O, OLD HUNGARIAN SMALL LETTER O
|
||||
assertEq(String.fromCodePoint(0x10C9C).toLowerCase().codePointAt(0), 0x10CDC); // OLD HUNGARIAN CAPITAL LETTER OO, OLD HUNGARIAN SMALL LETTER OO
|
||||
assertEq(String.fromCodePoint(0x10C9D).toLowerCase().codePointAt(0), 0x10CDD); // OLD HUNGARIAN CAPITAL LETTER NIKOLSBURG OE, OLD HUNGARIAN SMALL LETTER NIKOLSBURG OE
|
||||
assertEq(String.fromCodePoint(0x10C9E).toLowerCase().codePointAt(0), 0x10CDE); // OLD HUNGARIAN CAPITAL LETTER RUDIMENTA OE, OLD HUNGARIAN SMALL LETTER RUDIMENTA OE
|
||||
assertEq(String.fromCodePoint(0x10C9F).toLowerCase().codePointAt(0), 0x10CDF); // OLD HUNGARIAN CAPITAL LETTER OEE, OLD HUNGARIAN SMALL LETTER OEE
|
||||
assertEq(String.fromCodePoint(0x10CA0).toLowerCase().codePointAt(0), 0x10CE0); // OLD HUNGARIAN CAPITAL LETTER EP, OLD HUNGARIAN SMALL LETTER EP
|
||||
assertEq(String.fromCodePoint(0x10CA1).toLowerCase().codePointAt(0), 0x10CE1); // OLD HUNGARIAN CAPITAL LETTER EMP, OLD HUNGARIAN SMALL LETTER EMP
|
||||
assertEq(String.fromCodePoint(0x10CA2).toLowerCase().codePointAt(0), 0x10CE2); // OLD HUNGARIAN CAPITAL LETTER ER, OLD HUNGARIAN SMALL LETTER ER
|
||||
assertEq(String.fromCodePoint(0x10CA3).toLowerCase().codePointAt(0), 0x10CE3); // OLD HUNGARIAN CAPITAL LETTER SHORT ER, OLD HUNGARIAN SMALL LETTER SHORT ER
|
||||
assertEq(String.fromCodePoint(0x10CA4).toLowerCase().codePointAt(0), 0x10CE4); // OLD HUNGARIAN CAPITAL LETTER ES, OLD HUNGARIAN SMALL LETTER ES
|
||||
assertEq(String.fromCodePoint(0x10CA5).toLowerCase().codePointAt(0), 0x10CE5); // OLD HUNGARIAN CAPITAL LETTER ESZ, OLD HUNGARIAN SMALL LETTER ESZ
|
||||
assertEq(String.fromCodePoint(0x10CA6).toLowerCase().codePointAt(0), 0x10CE6); // OLD HUNGARIAN CAPITAL LETTER ET, OLD HUNGARIAN SMALL LETTER ET
|
||||
assertEq(String.fromCodePoint(0x10CA7).toLowerCase().codePointAt(0), 0x10CE7); // OLD HUNGARIAN CAPITAL LETTER ENT, OLD HUNGARIAN SMALL LETTER ENT
|
||||
assertEq(String.fromCodePoint(0x10CA8).toLowerCase().codePointAt(0), 0x10CE8); // OLD HUNGARIAN CAPITAL LETTER ETY, OLD HUNGARIAN SMALL LETTER ETY
|
||||
assertEq(String.fromCodePoint(0x10CA9).toLowerCase().codePointAt(0), 0x10CE9); // OLD HUNGARIAN CAPITAL LETTER ECH, OLD HUNGARIAN SMALL LETTER ECH
|
||||
assertEq(String.fromCodePoint(0x10CAA).toLowerCase().codePointAt(0), 0x10CEA); // OLD HUNGARIAN CAPITAL LETTER U, OLD HUNGARIAN SMALL LETTER U
|
||||
assertEq(String.fromCodePoint(0x10CAB).toLowerCase().codePointAt(0), 0x10CEB); // OLD HUNGARIAN CAPITAL LETTER UU, OLD HUNGARIAN SMALL LETTER UU
|
||||
assertEq(String.fromCodePoint(0x10CAC).toLowerCase().codePointAt(0), 0x10CEC); // OLD HUNGARIAN CAPITAL LETTER NIKOLSBURG UE, OLD HUNGARIAN SMALL LETTER NIKOLSBURG UE
|
||||
assertEq(String.fromCodePoint(0x10CAD).toLowerCase().codePointAt(0), 0x10CED); // OLD HUNGARIAN CAPITAL LETTER RUDIMENTA UE, OLD HUNGARIAN SMALL LETTER RUDIMENTA UE
|
||||
assertEq(String.fromCodePoint(0x10CAE).toLowerCase().codePointAt(0), 0x10CEE); // OLD HUNGARIAN CAPITAL LETTER EV, OLD HUNGARIAN SMALL LETTER EV
|
||||
assertEq(String.fromCodePoint(0x10CAF).toLowerCase().codePointAt(0), 0x10CEF); // OLD HUNGARIAN CAPITAL LETTER EZ, OLD HUNGARIAN SMALL LETTER EZ
|
||||
assertEq(String.fromCodePoint(0x10CB0).toLowerCase().codePointAt(0), 0x10CF0); // OLD HUNGARIAN CAPITAL LETTER EZS, OLD HUNGARIAN SMALL LETTER EZS
|
||||
assertEq(String.fromCodePoint(0x10CB1).toLowerCase().codePointAt(0), 0x10CF1); // OLD HUNGARIAN CAPITAL LETTER ENT-SHAPED SIGN, OLD HUNGARIAN SMALL LETTER ENT-SHAPED SIGN
|
||||
assertEq(String.fromCodePoint(0x10CB2).toLowerCase().codePointAt(0), 0x10CF2); // OLD HUNGARIAN CAPITAL LETTER US, OLD HUNGARIAN SMALL LETTER US
|
||||
assertEq(String.fromCodePoint(0x118A0).toLowerCase().codePointAt(0), 0x118C0); // WARANG CITI CAPITAL LETTER NGAA, WARANG CITI SMALL LETTER NGAA
|
||||
assertEq(String.fromCodePoint(0x118A1).toLowerCase().codePointAt(0), 0x118C1); // WARANG CITI CAPITAL LETTER A, WARANG CITI SMALL LETTER A
|
||||
assertEq(String.fromCodePoint(0x118A2).toLowerCase().codePointAt(0), 0x118C2); // WARANG CITI CAPITAL LETTER WI, WARANG CITI SMALL LETTER WI
|
||||
assertEq(String.fromCodePoint(0x118A3).toLowerCase().codePointAt(0), 0x118C3); // WARANG CITI CAPITAL LETTER YU, WARANG CITI SMALL LETTER YU
|
||||
assertEq(String.fromCodePoint(0x118A4).toLowerCase().codePointAt(0), 0x118C4); // WARANG CITI CAPITAL LETTER YA, WARANG CITI SMALL LETTER YA
|
||||
assertEq(String.fromCodePoint(0x118A5).toLowerCase().codePointAt(0), 0x118C5); // WARANG CITI CAPITAL LETTER YO, WARANG CITI SMALL LETTER YO
|
||||
assertEq(String.fromCodePoint(0x118A6).toLowerCase().codePointAt(0), 0x118C6); // WARANG CITI CAPITAL LETTER II, WARANG CITI SMALL LETTER II
|
||||
assertEq(String.fromCodePoint(0x118A7).toLowerCase().codePointAt(0), 0x118C7); // WARANG CITI CAPITAL LETTER UU, WARANG CITI SMALL LETTER UU
|
||||
assertEq(String.fromCodePoint(0x118A8).toLowerCase().codePointAt(0), 0x118C8); // WARANG CITI CAPITAL LETTER E, WARANG CITI SMALL LETTER E
|
||||
assertEq(String.fromCodePoint(0x118A9).toLowerCase().codePointAt(0), 0x118C9); // WARANG CITI CAPITAL LETTER O, WARANG CITI SMALL LETTER O
|
||||
assertEq(String.fromCodePoint(0x118AA).toLowerCase().codePointAt(0), 0x118CA); // WARANG CITI CAPITAL LETTER ANG, WARANG CITI SMALL LETTER ANG
|
||||
assertEq(String.fromCodePoint(0x118AB).toLowerCase().codePointAt(0), 0x118CB); // WARANG CITI CAPITAL LETTER GA, WARANG CITI SMALL LETTER GA
|
||||
assertEq(String.fromCodePoint(0x118AC).toLowerCase().codePointAt(0), 0x118CC); // WARANG CITI CAPITAL LETTER KO, WARANG CITI SMALL LETTER KO
|
||||
assertEq(String.fromCodePoint(0x118AD).toLowerCase().codePointAt(0), 0x118CD); // WARANG CITI CAPITAL LETTER ENY, WARANG CITI SMALL LETTER ENY
|
||||
assertEq(String.fromCodePoint(0x118AE).toLowerCase().codePointAt(0), 0x118CE); // WARANG CITI CAPITAL LETTER YUJ, WARANG CITI SMALL LETTER YUJ
|
||||
assertEq(String.fromCodePoint(0x118AF).toLowerCase().codePointAt(0), 0x118CF); // WARANG CITI CAPITAL LETTER UC, WARANG CITI SMALL LETTER UC
|
||||
assertEq(String.fromCodePoint(0x118B0).toLowerCase().codePointAt(0), 0x118D0); // WARANG CITI CAPITAL LETTER ENN, WARANG CITI SMALL LETTER ENN
|
||||
assertEq(String.fromCodePoint(0x118B1).toLowerCase().codePointAt(0), 0x118D1); // WARANG CITI CAPITAL LETTER ODD, WARANG CITI SMALL LETTER ODD
|
||||
assertEq(String.fromCodePoint(0x118B2).toLowerCase().codePointAt(0), 0x118D2); // WARANG CITI CAPITAL LETTER TTE, WARANG CITI SMALL LETTER TTE
|
||||
assertEq(String.fromCodePoint(0x118B3).toLowerCase().codePointAt(0), 0x118D3); // WARANG CITI CAPITAL LETTER NUNG, WARANG CITI SMALL LETTER NUNG
|
||||
assertEq(String.fromCodePoint(0x118B4).toLowerCase().codePointAt(0), 0x118D4); // WARANG CITI CAPITAL LETTER DA, WARANG CITI SMALL LETTER DA
|
||||
assertEq(String.fromCodePoint(0x118B5).toLowerCase().codePointAt(0), 0x118D5); // WARANG CITI CAPITAL LETTER AT, WARANG CITI SMALL LETTER AT
|
||||
assertEq(String.fromCodePoint(0x118B6).toLowerCase().codePointAt(0), 0x118D6); // WARANG CITI CAPITAL LETTER AM, WARANG CITI SMALL LETTER AM
|
||||
assertEq(String.fromCodePoint(0x118B7).toLowerCase().codePointAt(0), 0x118D7); // WARANG CITI CAPITAL LETTER BU, WARANG CITI SMALL LETTER BU
|
||||
assertEq(String.fromCodePoint(0x118B8).toLowerCase().codePointAt(0), 0x118D8); // WARANG CITI CAPITAL LETTER PU, WARANG CITI SMALL LETTER PU
|
||||
assertEq(String.fromCodePoint(0x118B9).toLowerCase().codePointAt(0), 0x118D9); // WARANG CITI CAPITAL LETTER HIYO, WARANG CITI SMALL LETTER HIYO
|
||||
assertEq(String.fromCodePoint(0x118BA).toLowerCase().codePointAt(0), 0x118DA); // WARANG CITI CAPITAL LETTER HOLO, WARANG CITI SMALL LETTER HOLO
|
||||
assertEq(String.fromCodePoint(0x118BB).toLowerCase().codePointAt(0), 0x118DB); // WARANG CITI CAPITAL LETTER HORR, WARANG CITI SMALL LETTER HORR
|
||||
assertEq(String.fromCodePoint(0x118BC).toLowerCase().codePointAt(0), 0x118DC); // WARANG CITI CAPITAL LETTER HAR, WARANG CITI SMALL LETTER HAR
|
||||
assertEq(String.fromCodePoint(0x118BD).toLowerCase().codePointAt(0), 0x118DD); // WARANG CITI CAPITAL LETTER SSUU, WARANG CITI SMALL LETTER SSUU
|
||||
assertEq(String.fromCodePoint(0x118BE).toLowerCase().codePointAt(0), 0x118DE); // WARANG CITI CAPITAL LETTER SII, WARANG CITI SMALL LETTER SII
|
||||
assertEq(String.fromCodePoint(0x118BF).toLowerCase().codePointAt(0), 0x118DF); // WARANG CITI CAPITAL LETTER VIYO, WARANG CITI SMALL LETTER VIYO
|
||||
assertEq(String.fromCodePoint(0x16E40).toLowerCase().codePointAt(0), 0x16E60); // MEDEFAIDRIN CAPITAL LETTER M, MEDEFAIDRIN SMALL LETTER M
|
||||
assertEq(String.fromCodePoint(0x16E41).toLowerCase().codePointAt(0), 0x16E61); // MEDEFAIDRIN CAPITAL LETTER S, MEDEFAIDRIN SMALL LETTER S
|
||||
assertEq(String.fromCodePoint(0x16E42).toLowerCase().codePointAt(0), 0x16E62); // MEDEFAIDRIN CAPITAL LETTER V, MEDEFAIDRIN SMALL LETTER V
|
||||
assertEq(String.fromCodePoint(0x16E43).toLowerCase().codePointAt(0), 0x16E63); // MEDEFAIDRIN CAPITAL LETTER W, MEDEFAIDRIN SMALL LETTER W
|
||||
assertEq(String.fromCodePoint(0x16E44).toLowerCase().codePointAt(0), 0x16E64); // MEDEFAIDRIN CAPITAL LETTER ATIU, MEDEFAIDRIN SMALL LETTER ATIU
|
||||
assertEq(String.fromCodePoint(0x16E45).toLowerCase().codePointAt(0), 0x16E65); // MEDEFAIDRIN CAPITAL LETTER Z, MEDEFAIDRIN SMALL LETTER Z
|
||||
assertEq(String.fromCodePoint(0x16E46).toLowerCase().codePointAt(0), 0x16E66); // MEDEFAIDRIN CAPITAL LETTER KP, MEDEFAIDRIN SMALL LETTER KP
|
||||
assertEq(String.fromCodePoint(0x16E47).toLowerCase().codePointAt(0), 0x16E67); // MEDEFAIDRIN CAPITAL LETTER P, MEDEFAIDRIN SMALL LETTER P
|
||||
assertEq(String.fromCodePoint(0x16E48).toLowerCase().codePointAt(0), 0x16E68); // MEDEFAIDRIN CAPITAL LETTER T, MEDEFAIDRIN SMALL LETTER T
|
||||
assertEq(String.fromCodePoint(0x16E49).toLowerCase().codePointAt(0), 0x16E69); // MEDEFAIDRIN CAPITAL LETTER G, MEDEFAIDRIN SMALL LETTER G
|
||||
assertEq(String.fromCodePoint(0x16E4A).toLowerCase().codePointAt(0), 0x16E6A); // MEDEFAIDRIN CAPITAL LETTER F, MEDEFAIDRIN SMALL LETTER F
|
||||
assertEq(String.fromCodePoint(0x16E4B).toLowerCase().codePointAt(0), 0x16E6B); // MEDEFAIDRIN CAPITAL LETTER I, MEDEFAIDRIN SMALL LETTER I
|
||||
assertEq(String.fromCodePoint(0x16E4C).toLowerCase().codePointAt(0), 0x16E6C); // MEDEFAIDRIN CAPITAL LETTER K, MEDEFAIDRIN SMALL LETTER K
|
||||
assertEq(String.fromCodePoint(0x16E4D).toLowerCase().codePointAt(0), 0x16E6D); // MEDEFAIDRIN CAPITAL LETTER A, MEDEFAIDRIN SMALL LETTER A
|
||||
assertEq(String.fromCodePoint(0x16E4E).toLowerCase().codePointAt(0), 0x16E6E); // MEDEFAIDRIN CAPITAL LETTER J, MEDEFAIDRIN SMALL LETTER J
|
||||
assertEq(String.fromCodePoint(0x16E4F).toLowerCase().codePointAt(0), 0x16E6F); // MEDEFAIDRIN CAPITAL LETTER E, MEDEFAIDRIN SMALL LETTER E
|
||||
assertEq(String.fromCodePoint(0x16E50).toLowerCase().codePointAt(0), 0x16E70); // MEDEFAIDRIN CAPITAL LETTER B, MEDEFAIDRIN SMALL LETTER B
|
||||
assertEq(String.fromCodePoint(0x16E51).toLowerCase().codePointAt(0), 0x16E71); // MEDEFAIDRIN CAPITAL LETTER C, MEDEFAIDRIN SMALL LETTER C
|
||||
assertEq(String.fromCodePoint(0x16E52).toLowerCase().codePointAt(0), 0x16E72); // MEDEFAIDRIN CAPITAL LETTER U, MEDEFAIDRIN SMALL LETTER U
|
||||
assertEq(String.fromCodePoint(0x16E53).toLowerCase().codePointAt(0), 0x16E73); // MEDEFAIDRIN CAPITAL LETTER YU, MEDEFAIDRIN SMALL LETTER YU
|
||||
assertEq(String.fromCodePoint(0x16E54).toLowerCase().codePointAt(0), 0x16E74); // MEDEFAIDRIN CAPITAL LETTER L, MEDEFAIDRIN SMALL LETTER L
|
||||
assertEq(String.fromCodePoint(0x16E55).toLowerCase().codePointAt(0), 0x16E75); // MEDEFAIDRIN CAPITAL LETTER Q, MEDEFAIDRIN SMALL LETTER Q
|
||||
assertEq(String.fromCodePoint(0x16E56).toLowerCase().codePointAt(0), 0x16E76); // MEDEFAIDRIN CAPITAL LETTER HP, MEDEFAIDRIN SMALL LETTER HP
|
||||
assertEq(String.fromCodePoint(0x16E57).toLowerCase().codePointAt(0), 0x16E77); // MEDEFAIDRIN CAPITAL LETTER NY, MEDEFAIDRIN SMALL LETTER NY
|
||||
assertEq(String.fromCodePoint(0x16E58).toLowerCase().codePointAt(0), 0x16E78); // MEDEFAIDRIN CAPITAL LETTER X, MEDEFAIDRIN SMALL LETTER X
|
||||
assertEq(String.fromCodePoint(0x16E59).toLowerCase().codePointAt(0), 0x16E79); // MEDEFAIDRIN CAPITAL LETTER D, MEDEFAIDRIN SMALL LETTER D
|
||||
assertEq(String.fromCodePoint(0x16E5A).toLowerCase().codePointAt(0), 0x16E7A); // MEDEFAIDRIN CAPITAL LETTER OE, MEDEFAIDRIN SMALL LETTER OE
|
||||
assertEq(String.fromCodePoint(0x16E5B).toLowerCase().codePointAt(0), 0x16E7B); // MEDEFAIDRIN CAPITAL LETTER N, MEDEFAIDRIN SMALL LETTER N
|
||||
assertEq(String.fromCodePoint(0x16E5C).toLowerCase().codePointAt(0), 0x16E7C); // MEDEFAIDRIN CAPITAL LETTER R, MEDEFAIDRIN SMALL LETTER R
|
||||
assertEq(String.fromCodePoint(0x16E5D).toLowerCase().codePointAt(0), 0x16E7D); // MEDEFAIDRIN CAPITAL LETTER O, MEDEFAIDRIN SMALL LETTER O
|
||||
assertEq(String.fromCodePoint(0x16E5E).toLowerCase().codePointAt(0), 0x16E7E); // MEDEFAIDRIN CAPITAL LETTER AI, MEDEFAIDRIN SMALL LETTER AI
|
||||
assertEq(String.fromCodePoint(0x16E5F).toLowerCase().codePointAt(0), 0x16E7F); // MEDEFAIDRIN CAPITAL LETTER Y, MEDEFAIDRIN SMALL LETTER Y
|
||||
assertEq(String.fromCodePoint(0x1E900).toLowerCase().codePointAt(0), 0x1E922); // ADLAM CAPITAL LETTER ALIF, ADLAM SMALL LETTER ALIF
|
||||
assertEq(String.fromCodePoint(0x1E901).toLowerCase().codePointAt(0), 0x1E923); // ADLAM CAPITAL LETTER DAALI, ADLAM SMALL LETTER DAALI
|
||||
assertEq(String.fromCodePoint(0x1E902).toLowerCase().codePointAt(0), 0x1E924); // ADLAM CAPITAL LETTER LAAM, ADLAM SMALL LETTER LAAM
|
||||
assertEq(String.fromCodePoint(0x1E903).toLowerCase().codePointAt(0), 0x1E925); // ADLAM CAPITAL LETTER MIIM, ADLAM SMALL LETTER MIIM
|
||||
assertEq(String.fromCodePoint(0x1E904).toLowerCase().codePointAt(0), 0x1E926); // ADLAM CAPITAL LETTER BA, ADLAM SMALL LETTER BA
|
||||
assertEq(String.fromCodePoint(0x1E905).toLowerCase().codePointAt(0), 0x1E927); // ADLAM CAPITAL LETTER SINNYIIYHE, ADLAM SMALL LETTER SINNYIIYHE
|
||||
assertEq(String.fromCodePoint(0x1E906).toLowerCase().codePointAt(0), 0x1E928); // ADLAM CAPITAL LETTER PE, ADLAM SMALL LETTER PE
|
||||
assertEq(String.fromCodePoint(0x1E907).toLowerCase().codePointAt(0), 0x1E929); // ADLAM CAPITAL LETTER BHE, ADLAM SMALL LETTER BHE
|
||||
assertEq(String.fromCodePoint(0x1E908).toLowerCase().codePointAt(0), 0x1E92A); // ADLAM CAPITAL LETTER RA, ADLAM SMALL LETTER RA
|
||||
assertEq(String.fromCodePoint(0x1E909).toLowerCase().codePointAt(0), 0x1E92B); // ADLAM CAPITAL LETTER E, ADLAM SMALL LETTER E
|
||||
assertEq(String.fromCodePoint(0x1E90A).toLowerCase().codePointAt(0), 0x1E92C); // ADLAM CAPITAL LETTER FA, ADLAM SMALL LETTER FA
|
||||
assertEq(String.fromCodePoint(0x1E90B).toLowerCase().codePointAt(0), 0x1E92D); // ADLAM CAPITAL LETTER I, ADLAM SMALL LETTER I
|
||||
assertEq(String.fromCodePoint(0x1E90C).toLowerCase().codePointAt(0), 0x1E92E); // ADLAM CAPITAL LETTER O, ADLAM SMALL LETTER O
|
||||
assertEq(String.fromCodePoint(0x1E90D).toLowerCase().codePointAt(0), 0x1E92F); // ADLAM CAPITAL LETTER DHA, ADLAM SMALL LETTER DHA
|
||||
assertEq(String.fromCodePoint(0x1E90E).toLowerCase().codePointAt(0), 0x1E930); // ADLAM CAPITAL LETTER YHE, ADLAM SMALL LETTER YHE
|
||||
assertEq(String.fromCodePoint(0x1E90F).toLowerCase().codePointAt(0), 0x1E931); // ADLAM CAPITAL LETTER WAW, ADLAM SMALL LETTER WAW
|
||||
assertEq(String.fromCodePoint(0x1E910).toLowerCase().codePointAt(0), 0x1E932); // ADLAM CAPITAL LETTER NUN, ADLAM SMALL LETTER NUN
|
||||
assertEq(String.fromCodePoint(0x1E911).toLowerCase().codePointAt(0), 0x1E933); // ADLAM CAPITAL LETTER KAF, ADLAM SMALL LETTER KAF
|
||||
assertEq(String.fromCodePoint(0x1E912).toLowerCase().codePointAt(0), 0x1E934); // ADLAM CAPITAL LETTER YA, ADLAM SMALL LETTER YA
|
||||
assertEq(String.fromCodePoint(0x1E913).toLowerCase().codePointAt(0), 0x1E935); // ADLAM CAPITAL LETTER U, ADLAM SMALL LETTER U
|
||||
assertEq(String.fromCodePoint(0x1E914).toLowerCase().codePointAt(0), 0x1E936); // ADLAM CAPITAL LETTER JIIM, ADLAM SMALL LETTER JIIM
|
||||
assertEq(String.fromCodePoint(0x1E915).toLowerCase().codePointAt(0), 0x1E937); // ADLAM CAPITAL LETTER CHI, ADLAM SMALL LETTER CHI
|
||||
assertEq(String.fromCodePoint(0x1E916).toLowerCase().codePointAt(0), 0x1E938); // ADLAM CAPITAL LETTER HA, ADLAM SMALL LETTER HA
|
||||
assertEq(String.fromCodePoint(0x1E917).toLowerCase().codePointAt(0), 0x1E939); // ADLAM CAPITAL LETTER QAAF, ADLAM SMALL LETTER QAAF
|
||||
assertEq(String.fromCodePoint(0x1E918).toLowerCase().codePointAt(0), 0x1E93A); // ADLAM CAPITAL LETTER GA, ADLAM SMALL LETTER GA
|
||||
assertEq(String.fromCodePoint(0x1E919).toLowerCase().codePointAt(0), 0x1E93B); // ADLAM CAPITAL LETTER NYA, ADLAM SMALL LETTER NYA
|
||||
assertEq(String.fromCodePoint(0x1E91A).toLowerCase().codePointAt(0), 0x1E93C); // ADLAM CAPITAL LETTER TU, ADLAM SMALL LETTER TU
|
||||
assertEq(String.fromCodePoint(0x1E91B).toLowerCase().codePointAt(0), 0x1E93D); // ADLAM CAPITAL LETTER NHA, ADLAM SMALL LETTER NHA
|
||||
assertEq(String.fromCodePoint(0x1E91C).toLowerCase().codePointAt(0), 0x1E93E); // ADLAM CAPITAL LETTER VA, ADLAM SMALL LETTER VA
|
||||
assertEq(String.fromCodePoint(0x1E91D).toLowerCase().codePointAt(0), 0x1E93F); // ADLAM CAPITAL LETTER KHA, ADLAM SMALL LETTER KHA
|
||||
assertEq(String.fromCodePoint(0x1E91E).toLowerCase().codePointAt(0), 0x1E940); // ADLAM CAPITAL LETTER GBE, ADLAM SMALL LETTER GBE
|
||||
assertEq(String.fromCodePoint(0x1E91F).toLowerCase().codePointAt(0), 0x1E941); // ADLAM CAPITAL LETTER ZAL, ADLAM SMALL LETTER ZAL
|
||||
assertEq(String.fromCodePoint(0x1E920).toLowerCase().codePointAt(0), 0x1E942); // ADLAM CAPITAL LETTER KPO, ADLAM SMALL LETTER KPO
|
||||
assertEq(String.fromCodePoint(0x1E921).toLowerCase().codePointAt(0), 0x1E943); // ADLAM CAPITAL LETTER SHA, ADLAM SMALL LETTER SHA
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
|
|||
|
|
@ -56,6 +56,29 @@ skip script test262/ch10/10.6/10.6-14-b-4-s.js
|
|||
skip script test262/ch10/10.6/10.6-13-b-1-s.js
|
||||
skip script test262/ch10/10.6/10.6-13-b-2-s.js
|
||||
|
||||
# ES2017 Intl legacy constructor semantics changes made these tests invalid
|
||||
# (bug 1328386).
|
||||
skip script test262/intl402/ch10/10.1/10.1.1_1.js
|
||||
skip script test262/intl402/ch10/10.1/10.1.2_a.js
|
||||
skip script test262/intl402/ch11/11.1/11.1.1_1.js
|
||||
skip script test262/intl402/ch12/12.1/12.1.1_1.js
|
||||
|
||||
# Intl.{Collator,DateTimeFormat,NumberFormat}.prototype are now plain objects.
|
||||
skip script test262/intl402/ch10/10.3/10.3_a.js
|
||||
skip script test262/intl402/ch11/11.3/11.3_a.js
|
||||
skip script test262/intl402/ch12/12.3/12.3_a.js
|
||||
|
||||
# Tests already removed upstream (https://github.com/tc39/test262/pull/807/files),
|
||||
# but apparently for the wrong reason.
|
||||
skip script test262/intl402/ch10/10.1/10.1.2.1_4.js
|
||||
skip script test262/intl402/ch11/11.1/11.1.2.1_4.js
|
||||
skip script test262/intl402/ch12/12.1/12.1.2.1_4.js
|
||||
|
||||
# Tests not updated to follow new language tag canonicalisation.
|
||||
skip script test262/intl402/Locale/constructor-non-iana-canon.js
|
||||
skip script test262/intl402/Intl/getCanonicalLocales/preferred-variant.js
|
||||
skip script test262/intl402/Intl/getCanonicalLocales/non-iana-canon.js
|
||||
|
||||
#######################################################################
|
||||
# Tests disabled due to jstest limitations wrt imported test262 tests #
|
||||
#######################################################################
|
||||
|
|
|
|||
69
js/src/tests/non262/Intl/DateTimeFormat/calendar-option.js
Normal file
69
js/src/tests/non262/Intl/DateTimeFormat/calendar-option.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// |reftest| skip-if(release_or_beta)
|
||||
|
||||
const defaultLocale = "en";
|
||||
const defaultCalendar = new Intl.DateTimeFormat(defaultLocale).resolvedOptions().calendar;
|
||||
|
||||
function createWithLocale(locale, calendar) {
|
||||
return new Intl.DateTimeFormat(locale, {calendar});
|
||||
}
|
||||
|
||||
function create(calendar) {
|
||||
return createWithLocale(defaultLocale, calendar);
|
||||
}
|
||||
|
||||
// Empty string should throw.
|
||||
assertThrowsInstanceOf(() => create(""), RangeError);
|
||||
|
||||
// Trailing \0 should throw.
|
||||
assertThrowsInstanceOf(() => create("gregory\0"), RangeError);
|
||||
|
||||
// Too short or too long strings should throw.
|
||||
assertThrowsInstanceOf(() => create("a"), RangeError);
|
||||
assertThrowsInstanceOf(() => create("toolongstring"), RangeError);
|
||||
|
||||
// Throw even when prefix is valid.
|
||||
assertThrowsInstanceOf(() => create("gregory-toolongstring"), RangeError);
|
||||
|
||||
// |calendar| can be set to |undefined|.
|
||||
let dtf = create(undefined);
|
||||
assertEq(dtf.resolvedOptions().calendar, defaultCalendar);
|
||||
|
||||
// Unsupported calendars are ignored.
|
||||
dtf = create("xxxxxxxx");
|
||||
assertEq(dtf.resolvedOptions().calendar, defaultCalendar);
|
||||
|
||||
// Calendars in options overwrite Unicode extension keyword.
|
||||
dtf = createWithLocale(`${defaultLocale}-u-ca-iso8601`, "japanese");
|
||||
assertEq(dtf.resolvedOptions().locale, defaultLocale);
|
||||
assertEq(dtf.resolvedOptions().calendar, "japanese");
|
||||
|
||||
// |calendar| option ignores case.
|
||||
dtf = create("CHINESE");
|
||||
assertEq(dtf.resolvedOptions().locale, defaultLocale);
|
||||
assertEq(dtf.resolvedOptions().calendar, "chinese");
|
||||
|
||||
const calendars = [
|
||||
"buddhist", "chinese", "coptic", "dangi", "ethioaa", "ethiopic-amete-alem",
|
||||
"ethiopic", "gregory", "hebrew", "indian", "islamic", "islamic-umalqura",
|
||||
"islamic-tbla", "islamic-civil", "islamic-rgsa", "iso8601", "japanese",
|
||||
"persian", "roc", "islamicc",
|
||||
];
|
||||
|
||||
// https://github.com/tc39/proposal-intl-locale/issues/96
|
||||
const canonical = {
|
||||
"islamicc": "islamic-civil",
|
||||
"ethiopic-amete-alem": "ethioaa",
|
||||
};
|
||||
|
||||
for (let calendar of calendars) {
|
||||
let dtf1 = new Intl.DateTimeFormat(`${defaultLocale}-u-ca-${calendar}`);
|
||||
let dtf2 = new Intl.DateTimeFormat(defaultLocale, {calendar});
|
||||
|
||||
assertEq(dtf1.resolvedOptions().calendar, canonical[calendar] ?? calendar);
|
||||
assertEq(dtf2.resolvedOptions().calendar, canonical[calendar] ?? calendar);
|
||||
|
||||
assertEq(dtf2.format(0), dtf1.format(0));
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
// |reftest| skip-if(release_or_beta)
|
||||
|
||||
const defaultLocale = "en";
|
||||
const defaultNumberingSystem = new Intl.DateTimeFormat(defaultLocale).resolvedOptions().numberingSystem;
|
||||
|
||||
function createWithLocale(locale, numberingSystem) {
|
||||
return new Intl.DateTimeFormat(locale, {numberingSystem});
|
||||
}
|
||||
|
||||
function create(numberingSystem) {
|
||||
return createWithLocale(defaultLocale, numberingSystem);
|
||||
}
|
||||
|
||||
// Empty string should throw.
|
||||
assertThrowsInstanceOf(() => create(""), RangeError);
|
||||
|
||||
// Trailing \0 should throw.
|
||||
assertThrowsInstanceOf(() => create("latn\0"), RangeError);
|
||||
|
||||
// Too short or too long strings should throw.
|
||||
assertThrowsInstanceOf(() => create("a"), RangeError);
|
||||
assertThrowsInstanceOf(() => create("toolongstring"), RangeError);
|
||||
|
||||
// Throw even when prefix is valid.
|
||||
assertThrowsInstanceOf(() => create("latn-toolongstring"), RangeError);
|
||||
|
||||
// |numberingSystem| can be set to |undefined|.
|
||||
let dtf = create(undefined);
|
||||
assertEq(dtf.resolvedOptions().numberingSystem, defaultNumberingSystem);
|
||||
|
||||
// Unsupported numbering systems are ignored.
|
||||
dtf = create("xxxxxxxx");
|
||||
assertEq(dtf.resolvedOptions().numberingSystem, defaultNumberingSystem);
|
||||
|
||||
// Numbering system in options overwrite Unicode extension keyword.
|
||||
dtf = createWithLocale(`${defaultLocale}-u-nu-thai`, "arab");
|
||||
assertEq(dtf.resolvedOptions().locale, defaultLocale);
|
||||
assertEq(dtf.resolvedOptions().numberingSystem, "arab");
|
||||
|
||||
// |numberingSystem| option ignores case.
|
||||
dtf = create("ARAB");
|
||||
assertEq(dtf.resolvedOptions().locale, defaultLocale);
|
||||
assertEq(dtf.resolvedOptions().numberingSystem, "arab");
|
||||
|
||||
const numberingSystems = [
|
||||
"arab", "arabext", "bali", "beng", "deva",
|
||||
"fullwide", "gujr", "guru", "hanidec", "khmr",
|
||||
"knda", "laoo", "latn", "limb", "mlym",
|
||||
"mong", "mymr", "orya", "tamldec", "telu",
|
||||
"thai", "tibt",
|
||||
];
|
||||
|
||||
for (let numberingSystem of numberingSystems) {
|
||||
let dtf1 = new Intl.DateTimeFormat(`${defaultLocale}-u-nu-${numberingSystem}`);
|
||||
let dtf2 = new Intl.DateTimeFormat(defaultLocale, {numberingSystem});
|
||||
|
||||
assertEq(dtf1.resolvedOptions().numberingSystem, numberingSystem);
|
||||
assertEq(dtf2.resolvedOptions().numberingSystem, numberingSystem);
|
||||
|
||||
assertEq(dtf2.format(0), dtf1.format(0));
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty('Intl'))
|
||||
|
||||
// ApplyOptionsToTag canonicalises the locale identifier before applying the
|
||||
// options. That means "und-Armn-SU" is first canonicalised to "und-Armn-AM",
|
||||
// then the language is changed to "ru". If "ru" were applied first, the result
|
||||
// would be "ru-Armn-RU" instead.
|
||||
assertEq(new Intl.Locale("und-Armn-SU", {language:"ru"}).toString(),
|
||||
"ru-Armn-AM");
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0);
|
||||
0
js/src/tests/non262/Intl/Locale/browser.js
Normal file
0
js/src/tests/non262/Intl/Locale/browser.js
Normal file
28
js/src/tests/non262/Intl/Locale/cross-compartment.js
Normal file
28
js/src/tests/non262/Intl/Locale/cross-compartment.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty('Intl'))
|
||||
|
||||
var g = newGlobal();
|
||||
|
||||
var tag = "de-Latn-AT-u-ca-gregory-nu-latn-co-phonebk-kf-false-kn-hc-h23";
|
||||
var locale = new Intl.Locale(tag);
|
||||
var ccwLocale = new g.Intl.Locale(tag);
|
||||
|
||||
for (var [key, {get, value = get}] of Object.entries(Object.getOwnPropertyDescriptors(Intl.Locale.prototype))) {
|
||||
if (typeof value === "function") {
|
||||
if (key !== "constructor") {
|
||||
var expectedValue = value.call(locale);
|
||||
|
||||
if (typeof expectedValue === "string" || typeof expectedValue === "boolean") {
|
||||
assertEq(value.call(ccwLocale), expectedValue, key);
|
||||
} else if (expectedValue instanceof Intl.Locale) {
|
||||
assertEq(value.call(ccwLocale).toString(), expectedValue.toString(), key);
|
||||
} else {
|
||||
throw new Error("unexpected result value");
|
||||
}
|
||||
} else {
|
||||
assertEq(new value(ccwLocale).toString(), new value(locale).toString(), key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0);
|
||||
75
js/src/tests/non262/Intl/Locale/grandfathered.js
Normal file
75
js/src/tests/non262/Intl/Locale/grandfathered.js
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty('Intl'))
|
||||
|
||||
var testData = [
|
||||
{
|
||||
tag: "cel-gaulish",
|
||||
options: {
|
||||
numberingSystem: "latn",
|
||||
},
|
||||
canonical: "xtg-u-nu-latn-x-cel-gaulish",
|
||||
extensions: {
|
||||
numberingSystem: "latn",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
tag: "cel-gaulish",
|
||||
options: {
|
||||
region: "FR",
|
||||
numberingSystem: "latn",
|
||||
},
|
||||
canonical: "xtg-FR-u-nu-latn-x-cel-gaulish",
|
||||
extensions: {
|
||||
numberingSystem: "latn",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
tag: "art-lojban",
|
||||
options: {
|
||||
numberingSystem: "latn",
|
||||
},
|
||||
canonical: "jbo-u-nu-latn",
|
||||
extensions: {
|
||||
numberingSystem: "latn",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
tag: "art-lojban",
|
||||
options: {
|
||||
region: "ZZ",
|
||||
numberingSystem: "latn",
|
||||
},
|
||||
canonical: "jbo-ZZ-u-nu-latn",
|
||||
extensions: {
|
||||
numberingSystem: "latn",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (var {tag, options, canonical, extensions} of testData) {
|
||||
var loc = new Intl.Locale(tag, options);
|
||||
assertEq(loc.toString(), canonical);
|
||||
|
||||
for (var [name, value] of Object.entries(extensions)) {
|
||||
assertEq(loc[name], value);
|
||||
}
|
||||
}
|
||||
|
||||
var errorTestData = [
|
||||
"en-gb-oed",
|
||||
"i-default",
|
||||
"sgn-ch-de",
|
||||
"zh-min",
|
||||
"zh-min-nan",
|
||||
"zh-hakka-hakka",
|
||||
];
|
||||
|
||||
for (var tag of errorTestData) {
|
||||
assertThrowsInstanceOf(() => new Intl.Locale(tag), RangeError);
|
||||
assertThrowsInstanceOf(() => new Intl.Locale(tag, {}), RangeError);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0);
|
||||
3433
js/src/tests/non262/Intl/Locale/likely-subtags-generated.js
Normal file
3433
js/src/tests/non262/Intl/Locale/likely-subtags-generated.js
Normal file
File diff suppressed because it is too large
Load diff
61
js/src/tests/non262/Intl/Locale/likely-subtags.js
Normal file
61
js/src/tests/non262/Intl/Locale/likely-subtags.js
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty('Intl'))
|
||||
|
||||
var testDataMaximal = {
|
||||
// Keeps "und" primary language.
|
||||
"und-AQ": "und-Latn-AQ",
|
||||
|
||||
// Modifies primary language.
|
||||
"und-Cyrl-RO": "bg-Cyrl-RO",
|
||||
}
|
||||
|
||||
var testDataMinimal = {
|
||||
// Undefined primary language.
|
||||
"und": "en",
|
||||
"und-Thai": "th",
|
||||
"und-419": "es-419",
|
||||
"und-150": "ru",
|
||||
"und-AT": "de-AT",
|
||||
|
||||
// https://ssl.icu-project.org/trac/ticket/13786
|
||||
"aae-Latn-IT": "aae-Latn-IT",
|
||||
"aae-Thai-CO": "aae-Thai-CO",
|
||||
|
||||
// https://ssl.icu-project.org/trac/ticket/10220
|
||||
// https://ssl.icu-project.org/trac/ticket/12345
|
||||
"und-CW": "pap-CW",
|
||||
"und-US": "en",
|
||||
"zh-Hant": "zh-TW",
|
||||
"zh-Hani": "zh-Hani",
|
||||
};
|
||||
|
||||
// Add variants, extensions, and privateuse subtags and ensure they don't
|
||||
// modify the result of the likely subtags algorithms.
|
||||
var extras = [
|
||||
"fonipa",
|
||||
"a-not-assigned",
|
||||
"u-attr",
|
||||
"u-co",
|
||||
"u-co-phonebk",
|
||||
"x-private",
|
||||
];
|
||||
|
||||
for (var [tag, maximal] of Object.entries(testDataMaximal)) {
|
||||
assertEq(new Intl.Locale(tag).maximize().toString(), maximal);
|
||||
assertEq(new Intl.Locale(maximal).maximize().toString(), maximal);
|
||||
|
||||
for (var extra of extras) {
|
||||
assertEq(new Intl.Locale(tag + "-" + extra).maximize().toString(), maximal + "-" + extra);
|
||||
}
|
||||
}
|
||||
|
||||
for (var [tag, minimal] of Object.entries(testDataMinimal)) {
|
||||
assertEq(new Intl.Locale(tag).minimize().toString(), minimal);
|
||||
assertEq(new Intl.Locale(minimal).minimize().toString(), minimal);
|
||||
|
||||
for (var extra of extras) {
|
||||
assertEq(new Intl.Locale(tag + "-" + extra).minimize().toString(), minimal + "-" + extra);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0);
|
||||
26
js/src/tests/non262/Intl/Locale/same-compartment.js
Normal file
26
js/src/tests/non262/Intl/Locale/same-compartment.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.wrapWithProto)
|
||||
|
||||
var tag = "de-Latn-AT-u-ca-gregory-nu-latn-co-phonebk-kf-false-kn-hc-h23";
|
||||
var locale = new Intl.Locale(tag);
|
||||
var scwLocale = wrapWithProto(locale, Intl.Locale.prototype);
|
||||
|
||||
for (var [key, {get, value = get}] of Object.entries(Object.getOwnPropertyDescriptors(Intl.Locale.prototype))) {
|
||||
if (typeof value === "function") {
|
||||
if (key !== "constructor") {
|
||||
var expectedValue = value.call(locale);
|
||||
|
||||
if (typeof expectedValue === "string" || typeof expectedValue === "boolean") {
|
||||
assertEq(value.call(scwLocale), expectedValue, key);
|
||||
} else if (expectedValue instanceof Intl.Locale) {
|
||||
assertEq(value.call(scwLocale).toString(), expectedValue.toString(), key);
|
||||
} else {
|
||||
throw new Error("unexpected result value");
|
||||
}
|
||||
} else {
|
||||
assertEq(new value(scwLocale).toString(), new value(locale).toString(), key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0);
|
||||
0
js/src/tests/non262/Intl/Locale/shell.js
Normal file
0
js/src/tests/non262/Intl/Locale/shell.js
Normal file
98
js/src/tests/non262/Intl/Locale/surface.js
Normal file
98
js/src/tests/non262/Intl/Locale/surface.js
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty('Intl'))
|
||||
|
||||
function assertProperty(object, name, desc) {
|
||||
assertEq(desc === undefined || (typeof desc === "object" && desc !== null), true,
|
||||
"desc is a property descriptor");
|
||||
|
||||
var actual = Object.getOwnPropertyDescriptor(object, name);
|
||||
if (desc === undefined) {
|
||||
assertEq(actual, desc, `property ${String(name)} is absent`);
|
||||
return;
|
||||
}
|
||||
assertEq(actual !== undefined, true, `property ${String(name)} is present`);
|
||||
|
||||
var fields = ["value", "writable", "enumerable", "configurable", "get", "set"];
|
||||
for (var field of fields) {
|
||||
if (Object.prototype.hasOwnProperty.call(desc, field)) {
|
||||
assertEq(actual[field], desc[field], `unexpected value for ${field}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function assertBuiltinFunction(fn, length, name) {
|
||||
assertProperty(fn, "length", {
|
||||
value: length, writable: false, enumerable: false, configurable: true,
|
||||
});
|
||||
}
|
||||
|
||||
function assertBuiltinMethod(object, propName, length, name) {
|
||||
var desc = Object.getOwnPropertyDescriptor(object, propName);
|
||||
assertProperty(object, propName, {
|
||||
value: desc.value, writable: true, enumerable: false, configurable: true
|
||||
});
|
||||
assertBuiltinFunction(desc.value, length, name);
|
||||
}
|
||||
|
||||
function assertBuiltinGetter(object, propName, length, name) {
|
||||
var desc = Object.getOwnPropertyDescriptor(object, propName);
|
||||
|
||||
assertBuiltinFunction(desc.get, length, name);
|
||||
}
|
||||
|
||||
// Intl.Locale( tag[, options] )
|
||||
assertBuiltinFunction(Intl.Locale, 1, "Locale");
|
||||
|
||||
// Properties of the Intl.Locale Constructor
|
||||
|
||||
// Intl.Locale.prototype
|
||||
assertProperty(Intl.Locale, "prototype", {
|
||||
value: Intl.Locale.prototype, writable: false, enumerable: false, configurable: false,
|
||||
});
|
||||
|
||||
// Properties of the Intl.Locale Prototype Object
|
||||
|
||||
// Intl.Locale.prototype.constructor
|
||||
assertProperty(Intl.Locale.prototype, "constructor", {
|
||||
value: Intl.Locale, writable: true, enumerable: false, configurable: true,
|
||||
});
|
||||
|
||||
// Intl.Locale.prototype[ @@toStringTag ]
|
||||
assertProperty(Intl.Locale.prototype, Symbol.toStringTag, {
|
||||
value: "Intl.Locale", writable: false, enumerable: false, configurable: true,
|
||||
});
|
||||
|
||||
// Intl.Locale.prototype.toString ()
|
||||
assertBuiltinMethod(Intl.Locale.prototype, "toString", 0, "toString");
|
||||
|
||||
// get Intl.Locale.prototype.baseName
|
||||
assertBuiltinGetter(Intl.Locale.prototype, "baseName", 0, "get baseName");
|
||||
|
||||
// get Intl.Locale.prototype.calendar
|
||||
assertBuiltinGetter(Intl.Locale.prototype, "calendar", 0, "get calendar");
|
||||
|
||||
// get Intl.Locale.prototype.collation
|
||||
assertBuiltinGetter(Intl.Locale.prototype, "collation", 0, "get collation");
|
||||
|
||||
// get Intl.Locale.prototype.hourCycle
|
||||
assertBuiltinGetter(Intl.Locale.prototype, "hourCycle", 0, "get hourCycle");
|
||||
|
||||
// get Intl.Locale.prototype.caseFirst
|
||||
assertBuiltinGetter(Intl.Locale.prototype, "caseFirst", 0, "get caseFirst");
|
||||
|
||||
// get Intl.Locale.prototype.numeric
|
||||
assertBuiltinGetter(Intl.Locale.prototype, "numeric", 0, "get numeric");
|
||||
|
||||
// get Intl.Locale.prototype.numberingSystem
|
||||
assertBuiltinGetter(Intl.Locale.prototype, "numberingSystem", 0, "get numberingSystem");
|
||||
|
||||
// get Intl.Locale.prototype.language
|
||||
assertBuiltinGetter(Intl.Locale.prototype, "language", 0, "get language");
|
||||
|
||||
// get Intl.Locale.prototype.script
|
||||
assertBuiltinGetter(Intl.Locale.prototype, "script", 0, "get script");
|
||||
|
||||
// get Intl.Locale.prototype.region
|
||||
assertBuiltinGetter(Intl.Locale.prototype, "region", 0, "get region");
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0);
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue