mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-15 14:25:52 +00:00
Bug 1431957 - Move String-based Intl-dependent functionality out of Intl.cpp, into jsstr.cpp. It may *depend* on Intl, but it *lives* on String, so it should be defined in String code for easiest searching. And with newly-slimmed builtin/intl/*.h headers, it's no real compile overhead to define this outside of Intl code. r=anba
--HG-- extra : rebase_source : 6471c2d3028dba1f6ac9348d8b40c20f07e50cbd
This commit is contained in:
parent
d0f152b2e0
commit
9999e83d84
@ -63,123 +63,6 @@ using js::intl::IcuLocale;
|
|||||||
using js::intl::INITIAL_CHAR_BUFFER_SIZE;
|
using js::intl::INITIAL_CHAR_BUFFER_SIZE;
|
||||||
using js::intl::StringsAreEqual;
|
using js::intl::StringsAreEqual;
|
||||||
|
|
||||||
/******************** String ********************/
|
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
RootedString string(cx, args[0].toString());
|
|
||||||
|
|
||||||
const char* locale = CaseMappingLocale(cx, args[1].toString());
|
|
||||||
if (!locale)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Call String.prototype.toLowerCase() for language independent casing.
|
|
||||||
if (StringsAreEqual(locale, "")) {
|
|
||||||
JSString* str = js::StringToLowerCase(cx, string);
|
|
||||||
if (!str)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
args.rval().setString(str);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoStableStringChars inputChars(cx);
|
|
||||||
if (!inputChars.initTwoByte(cx, string))
|
|
||||||
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 = CallICU(cx, [&input, locale](UChar* chars, int32_t size, UErrorCode* status) {
|
|
||||||
return u_strToLower(chars, size, input.begin().get(), input.length(), locale, status);
|
|
||||||
});
|
|
||||||
if (!str)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
args.rval().setString(str);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
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());
|
|
||||||
|
|
||||||
RootedString string(cx, args[0].toString());
|
|
||||||
|
|
||||||
const char* locale = CaseMappingLocale(cx, args[1].toString());
|
|
||||||
if (!locale)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Call String.prototype.toUpperCase() for language independent casing.
|
|
||||||
if (StringsAreEqual(locale, "")) {
|
|
||||||
JSString* str = js::StringToUpperCase(cx, string);
|
|
||||||
if (!str)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
args.rval().setString(str);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoStableStringChars inputChars(cx);
|
|
||||||
if (!inputChars.initTwoByte(cx, string))
|
|
||||||
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 = CallICU(cx, [&input, locale](UChar* chars, int32_t size, UErrorCode* status) {
|
|
||||||
return u_strToUpper(chars, size, input.begin().get(), input.length(), locale, status);
|
|
||||||
});
|
|
||||||
if (!str)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
args.rval().setString(str);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/******************** Intl ********************/
|
/******************** Intl ********************/
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -125,28 +125,6 @@ intl_GetLocaleInfo(JSContext* cx, unsigned argc, Value* vp);
|
|||||||
extern MOZ_MUST_USE bool
|
extern MOZ_MUST_USE bool
|
||||||
intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp);
|
intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
|
|
||||||
/******************** String ********************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
#endif /* builtin_Intl_h */
|
#endif /* builtin_Intl_h */
|
||||||
|
154
js/src/jsstr.cpp
154
js/src/jsstr.cpp
@ -31,6 +31,8 @@
|
|||||||
#include "jstypes.h"
|
#include "jstypes.h"
|
||||||
#include "jsutil.h"
|
#include "jsutil.h"
|
||||||
|
|
||||||
|
#include "builtin/intl/CommonFunctions.h"
|
||||||
|
#include "builtin/intl/ICUStubs.h"
|
||||||
#include "builtin/RegExp.h"
|
#include "builtin/RegExp.h"
|
||||||
#include "jit/InlinableNatives.h"
|
#include "jit/InlinableNatives.h"
|
||||||
#include "js/Conversions.h"
|
#include "js/Conversions.h"
|
||||||
@ -1011,7 +1013,85 @@ js::str_toLowerCase(JSContext* cx, unsigned argc, Value* vp)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !EXPOSE_INTL_API
|
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());
|
||||||
|
|
||||||
|
RootedString string(cx, args[0].toString());
|
||||||
|
|
||||||
|
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, string);
|
||||||
|
if (!str)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
args.rval().setString(str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoStableStringChars inputChars(cx);
|
||||||
|
if (!inputChars.initTwoByte(cx, string))
|
||||||
|
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, input.begin().get(), input.length(), locale, status);
|
||||||
|
});
|
||||||
|
if (!str)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
args.rval().setString(str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if EXPOSE_INTL_API
|
||||||
|
|
||||||
|
// String.prototype.toLocaleLowerCase is self-hosted when Intl is exposed,
|
||||||
|
// with core functionality performed by the intrinsic above.
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::str_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp)
|
js::str_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
@ -1045,7 +1125,8 @@ js::str_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp)
|
|||||||
args.rval().setString(result);
|
args.rval().setString(result);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif /* !EXPOSE_INTL_API */
|
|
||||||
|
#endif // EXPOSE_INTL_API
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
CanUpperCaseSpecialCasing(Latin1Char charCode)
|
CanUpperCaseSpecialCasing(Latin1Char charCode)
|
||||||
@ -1348,7 +1429,57 @@ js::str_toUpperCase(JSContext* cx, unsigned argc, Value* vp)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !EXPOSE_INTL_API
|
bool
|
||||||
|
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());
|
||||||
|
|
||||||
|
RootedString string(cx, args[0].toString());
|
||||||
|
|
||||||
|
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 = js::StringToUpperCase(cx, string);
|
||||||
|
if (!str)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
args.rval().setString(str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoStableStringChars inputChars(cx);
|
||||||
|
if (!inputChars.initTwoByte(cx, string))
|
||||||
|
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, input.begin().get(), input.length(), locale, status);
|
||||||
|
});
|
||||||
|
if (!str)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
args.rval().setString(str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if EXPOSE_INTL_API
|
||||||
|
|
||||||
|
// String.prototype.toLocaleLowerCase is self-hosted when Intl is exposed,
|
||||||
|
// with core functionality performed by the intrinsic above.
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::str_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp)
|
js::str_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
@ -1382,9 +1513,15 @@ js::str_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp)
|
|||||||
args.rval().setString(result);
|
args.rval().setString(result);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif /* !EXPOSE_INTL_API */
|
|
||||||
|
|
||||||
#if !EXPOSE_INTL_API
|
#endif // EXPOSE_INTL_API
|
||||||
|
|
||||||
|
#if EXPOSE_INTL_API
|
||||||
|
|
||||||
|
// String.prototype.localeCompare is self-hosted when Intl is exposed.
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::str_localeCompare(JSContext* cx, unsigned argc, Value* vp)
|
js::str_localeCompare(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
@ -1413,9 +1550,11 @@ js::str_localeCompare(JSContext* cx, unsigned argc, Value* vp)
|
|||||||
args.rval().setInt32(result);
|
args.rval().setInt32(result);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
#endif // EXPOSE_INTL_API
|
||||||
|
|
||||||
#if EXPOSE_INTL_API
|
#if EXPOSE_INTL_API
|
||||||
|
|
||||||
// ES2017 draft rev 45e890512fd77add72cc0ee742785f9f6f6482de
|
// ES2017 draft rev 45e890512fd77add72cc0ee742785f9f6f6482de
|
||||||
// 21.1.3.12 String.prototype.normalize ( [ form ] )
|
// 21.1.3.12 String.prototype.normalize ( [ form ] )
|
||||||
bool
|
bool
|
||||||
@ -1549,7 +1688,8 @@ js::str_normalize(JSContext* cx, unsigned argc, Value* vp)
|
|||||||
args.rval().setString(ns);
|
args.rval().setString(ns);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
#endif // EXPOSE_INTL_API
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::str_charAt(JSContext* cx, unsigned argc, Value* vp)
|
js::str_charAt(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
@ -344,19 +344,72 @@ str_trimLeft(JSContext* cx, unsigned argc, Value* vp);
|
|||||||
extern bool
|
extern bool
|
||||||
str_trimRight(JSContext* cx, unsigned argc, Value* vp);
|
str_trimRight(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
#if !EXPOSE_INTL_API
|
/**
|
||||||
|
* Returns the input string converted to lower case based on the language
|
||||||
|
* specific case mappings for the input locale.
|
||||||
|
*
|
||||||
|
* This function only works #if EXPOSE_INTL_API; if not, it will *crash*.
|
||||||
|
* Govern yourself accordingly.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This function only works #if EXPOSE_INTL_API; if not, it will *crash*.
|
||||||
|
* Govern yourself accordingly.
|
||||||
|
*
|
||||||
|
* Usage: upperCase = intl_toLocaleUpperCase(string, locale)
|
||||||
|
*/
|
||||||
|
extern MOZ_MUST_USE bool
|
||||||
|
intl_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
|
#if EXPOSE_INTL_API
|
||||||
|
|
||||||
|
// When the Intl API is exposed, String.prototype.to{Lower,Upper}Case is
|
||||||
|
// self-hosted. The core functionality is provided by the intrinsics above.
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// When the Intl API is not exposed, String.prototype.to{Lower,Upper}Case are
|
||||||
|
// implemented in C++.
|
||||||
|
|
||||||
extern bool
|
extern bool
|
||||||
str_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp);
|
str_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
extern bool
|
extern bool
|
||||||
str_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp);
|
str_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
extern bool
|
#endif // EXPOSE_INTL_API
|
||||||
str_localeCompare(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
#else
|
#if EXPOSE_INTL_API
|
||||||
|
|
||||||
|
// String.prototype.normalize is only implementable if ICU's normalization
|
||||||
|
// functionality is available.
|
||||||
extern bool
|
extern bool
|
||||||
str_normalize(JSContext* cx, unsigned argc, Value* vp);
|
str_normalize(JSContext* cx, unsigned argc, Value* vp);
|
||||||
#endif
|
|
||||||
|
#endif // EXPOSE_INTL_API
|
||||||
|
|
||||||
|
#if EXPOSE_INTL_API
|
||||||
|
|
||||||
|
// String.prototype.localeCompare is self-hosted when Intl functionality is
|
||||||
|
// exposed, and the only intrinsics it requires are provided in the
|
||||||
|
// implementation of Intl.Collator.
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// String.prototype.localeCompare is implemented in C++ (delegating to
|
||||||
|
// JSLocaleCallbacks) when Intl functionality is not exposed.
|
||||||
|
|
||||||
|
extern bool
|
||||||
|
str_localeCompare(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
|
#endif // EXPOSE_INTL_API
|
||||||
|
|
||||||
extern bool
|
extern bool
|
||||||
str_concat(JSContext* cx, unsigned argc, Value* vp);
|
str_concat(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
Loading…
Reference in New Issue
Block a user