Bug 1490609: Add JS_EncodeStringToASCII to CharacterEncoding.h. r=Waldo

This commit is contained in:
André Bargull 2018-09-13 01:34:06 -07:00
Родитель f3074bfafc
Коммит c76bfd0158
9 изменённых файлов: 123 добавлений и 47 удалений

Просмотреть файл

@ -368,4 +368,23 @@ JS_EncodeStringToLatin1(JSContext* cx, JSString* str);
extern JS_PUBLIC_API(JS::UniqueChars) extern JS_PUBLIC_API(JS::UniqueChars)
JS_EncodeStringToUTF8(JSContext* cx, JS::Handle<JSString*> str); JS_EncodeStringToUTF8(JSContext* cx, JS::Handle<JSString*> str);
/**
* DEPRECATED
*
* Same behavior as JS_EncodeStringToLatin1(), but encode into an ASCII string.
*
* This function asserts in debug mode that the input string contains only
* ASCII characters.
*
* The returned string is also subject to misinterpretation if |str| contains
* any nulls (which are faithfully transcribed into the returned string, but
* which will implicitly truncate the string if it's passed to functions that
* expect null-terminated strings).
*
* Avoid using this function if possible, because we'll remove it once we can
* devise a better API for the task.
*/
extern JS_PUBLIC_API(JS::UniqueChars)
JS_EncodeStringToASCII(JSContext* cx, JSString* str);
#endif /* js_CharacterEncoding_h */ #endif /* js_CharacterEncoding_h */

Просмотреть файл

@ -5016,7 +5016,17 @@ SetTimeZone(JSContext* cx, unsigned argc, Value* vp)
}; };
if (args[0].isString() && !args[0].toString()->empty()) { if (args[0].isString() && !args[0].toString()->empty()) {
UniqueChars timeZone = JS_EncodeStringToLatin1(cx, args[0].toString()); RootedLinearString str(cx, args[0].toString()->ensureLinear(cx));
if (!str) {
return false;
}
if (!StringIsAscii(str)) {
ReportUsageErrorASCII(cx, callee, "First argument contains non-ASCII characters");
return false;
}
UniqueChars timeZone = JS_EncodeStringToASCII(cx, str);
if (!timeZone) { if (!timeZone) {
return false; return false;
} }
@ -5081,38 +5091,31 @@ SetDefaultLocale(JSContext* cx, unsigned argc, Value* vp)
} }
if (args[0].isString() && !args[0].toString()->empty()) { if (args[0].isString() && !args[0].toString()->empty()) {
auto containsOnlyValidBCP47Characters = [](auto* chars, size_t length) {
return mozilla::IsAsciiAlpha(chars[0]) &&
std::all_of(chars, chars + length, [](auto c) {
return mozilla::IsAsciiAlphanumeric(c) || c == '-';
});
};
RootedLinearString str(cx, args[0].toString()->ensureLinear(cx)); RootedLinearString str(cx, args[0].toString()->ensureLinear(cx));
if (!str) { if (!str) {
return false; return false;
} }
bool hasValidChars; if (!StringIsAscii(str)) {
{ ReportUsageErrorASCII(cx, callee, "First argument contains non-ASCII characters");
JS::AutoCheckCannotGC nogc;
size_t length = str->length();
hasValidChars = str->hasLatin1Chars()
? containsOnlyValidBCP47Characters(str->latin1Chars(nogc), length)
: containsOnlyValidBCP47Characters(str->twoByteChars(nogc), length);
}
if (!hasValidChars) {
ReportUsageErrorASCII(cx, callee, "First argument should be BCP47 language tag");
return false; return false;
} }
UniqueChars locale = JS_EncodeStringToLatin1(cx, str); UniqueChars locale = JS_EncodeStringToASCII(cx, str);
if (!locale) { if (!locale) {
return false; return false;
} }
bool containsOnlyValidBCP47Characters = mozilla::IsAsciiAlpha(locale[0]) &&
std::all_of(locale.get(), locale.get() + str->length(), [](auto c) {
return mozilla::IsAsciiAlphanumeric(c) || c == '-';
});
if (!containsOnlyValidBCP47Characters) {
ReportUsageErrorASCII(cx, callee, "First argument should be a BCP47 language tag");
return false;
}
if (!JS_SetDefaultLocale(cx->runtime(), locale.get())) { if (!JS_SetDefaultLocale(cx->runtime(), locale.get())) {
ReportOutOfMemory(cx); ReportOutOfMemory(cx);
return false; return false;

Просмотреть файл

@ -91,27 +91,22 @@ js::intl::ReportInternalError(JSContext* cx)
js::UniqueChars js::UniqueChars
js::intl::EncodeLocale(JSContext* cx, JSString* locale) js::intl::EncodeLocale(JSContext* cx, JSString* locale)
{ {
#ifdef DEBUG MOZ_ASSERT(locale->length() > 0);
auto containsOnlyValidBCP47Chars = [](auto* chars, size_t length) {
return length > 0 &&
mozilla::IsAsciiAlpha(chars[0]) &&
std::all_of(chars, chars + length, [](auto c) {
return mozilla::IsAsciiAlphanumeric(c) || c == '-';
});
};
if (JSLinearString* linear = locale->ensureLinear(cx)) { js::UniqueChars chars = EncodeAscii(cx, locale);
JS::AutoCheckCannotGC nogc;
MOZ_ASSERT(linear->hasLatin1Chars() #ifdef DEBUG
? containsOnlyValidBCP47Chars(linear->latin1Chars(nogc), linear->length()) // Ensure the returned value contains only valid BCP 47 characters.
: containsOnlyValidBCP47Chars(linear->twoByteChars(nogc), linear->length())); // (Lambdas can't be placed inside MOZ_ASSERT, so move the checks in an
} else { // #ifdef block.)
// Ignore OOM when only performing a debug assertion. if (chars) {
cx->recoverFromOutOfMemory(); auto alnumOrDash = [](char c) { return mozilla::IsAsciiAlphanumeric(c) || c == '-'; };
MOZ_ASSERT(mozilla::IsAsciiAlpha(chars[0]));
MOZ_ASSERT(std::all_of(chars.get(), chars.get() + locale->length(), alnumOrDash));
} }
#endif #endif
return EncodeLatin1(cx, locale); return chars;
} }
bool bool

Просмотреть файл

@ -6267,6 +6267,15 @@ JS_DecodeBytes(JSContext* cx, const char* src, size_t srclen, char16_t* dst, siz
return true; return true;
} }
JS_PUBLIC_API(JS::UniqueChars)
JS_EncodeStringToASCII(JSContext* cx, JSString* str)
{
AssertHeapIsIdle();
CHECK_THREAD(cx);
return js::EncodeAscii(cx, str);
}
JS_PUBLIC_API(JS::UniqueChars) JS_PUBLIC_API(JS::UniqueChars)
JS_EncodeStringToLatin1(JSContext* cx, JSString* str) JS_EncodeStringToLatin1(JSContext* cx, JSString* str)
{ {

Просмотреть файл

@ -842,7 +842,7 @@ num_toLocaleString_impl(JSContext* cx, const CallArgs& args)
* Create the string, move back to bytes to make string twiddling * Create the string, move back to bytes to make string twiddling
* a bit easier and so we can insert platform charset seperators. * a bit easier and so we can insert platform charset seperators.
*/ */
UniqueChars numBytes = JS_EncodeStringToLatin1(cx, str); UniqueChars numBytes = EncodeAscii(cx, str);
if (!numBytes) { if (!numBytes) {
return false; return false;
} }

Просмотреть файл

@ -187,12 +187,12 @@ GetProxyTrap(JSContext* cx, HandleObject handler, HandlePropertyName name, Mutab
// Step 4. // Step 4.
if (!IsCallable(func)) { if (!IsCallable(func)) {
UniqueChars bytes = EncodeLatin1(cx, name); UniqueChars bytes = EncodeAscii(cx, name);
if (!bytes) { if (!bytes) {
return false; return false;
} }
JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_BAD_TRAP, bytes.get()); JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_TRAP, bytes.get());
return false; return false;
} }

Просмотреть файл

@ -5069,6 +5069,16 @@ class MOZ_STACK_CLASS Debugger::ObjectQuery
"neither undefined nor a string"); "neither undefined nor a string");
return false; return false;
} }
JSLinearString* str = cls.toString()->ensureLinear(cx);
if (!str) {
return false;
}
if (!StringIsAscii(str)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
"query object's 'class' property",
"not a string containing only ASCII characters");
return false;
}
className = cls; className = cls;
} }
return true; return true;
@ -5197,7 +5207,7 @@ class MOZ_STACK_CLASS Debugger::ObjectQuery
*/ */
bool prepareQuery() { bool prepareQuery() {
if (className.isString()) { if (className.isString()) {
classNameCString = JS_EncodeStringToLatin1(cx, className.toString()); classNameCString = JS_EncodeStringToASCII(cx, className.toString());
if (!classNameCString) { if (!classNameCString) {
return false; return false;
} }

Просмотреть файл

@ -17,6 +17,8 @@
#include "mozilla/TypeTraits.h" #include "mozilla/TypeTraits.h"
#include "mozilla/Unused.h" #include "mozilla/Unused.h"
#include <algorithm>
#include "jsfriendapi.h" #include "jsfriendapi.h"
#include "frontend/BytecodeCompiler.h" #include "frontend/BytecodeCompiler.h"
@ -1038,15 +1040,27 @@ js::CompareAtoms(JSAtom* atom1, JSAtom* atom2)
return CompareStringsImpl(atom1, atom2); return CompareStringsImpl(atom1, atom2);
} }
bool
js::StringIsAscii(JSLinearString* str)
{
auto containsOnlyAsciiCharacters = [](const auto* chars, size_t length) {
return std::all_of(chars, chars + length, [](auto c) {
return mozilla::IsAscii(c);
});
};
JS::AutoCheckCannotGC nogc;
return str->hasLatin1Chars()
? containsOnlyAsciiCharacters(str->latin1Chars(nogc), str->length())
: containsOnlyAsciiCharacters(str->twoByteChars(nogc), str->length());
}
bool bool
js::StringEqualsAscii(JSLinearString* str, const char* asciiBytes) js::StringEqualsAscii(JSLinearString* str, const char* asciiBytes)
{ {
MOZ_ASSERT(JS::StringIsASCII(asciiBytes));
size_t length = strlen(asciiBytes); size_t length = strlen(asciiBytes);
#ifdef DEBUG
for (size_t i = 0; i != length; ++i) {
MOZ_ASSERT(unsigned(asciiBytes[i]) <= 127);
}
#endif
if (length != str->length()) { if (length != str->length()) {
return false; return false;
} }
@ -2194,6 +2208,18 @@ js::EncodeLatin1(JSContext* cx, JSString* str)
return UniqueChars(reinterpret_cast<char*>(buf)); return UniqueChars(reinterpret_cast<char*>(buf));
} }
UniqueChars
js::EncodeAscii(JSContext* cx, JSString* str)
{
JSLinearString* linear = str->ensureLinear(cx);
if (!linear) {
return nullptr;
}
MOZ_ASSERT(StringIsAscii(linear));
return EncodeLatin1(cx, linear);
}
UniqueChars UniqueChars
js::IdToPrintableUTF8(JSContext* cx, HandleId id, IdToPrintableBehavior behavior) js::IdToPrintableUTF8(JSContext* cx, HandleId id, IdToPrintableBehavior behavior)
{ {

Просмотреть файл

@ -1659,6 +1659,12 @@ CompareStrings(JSContext* cx, JSString* str1, JSString* str2, int32_t* result);
extern int32_t extern int32_t
CompareAtoms(JSAtom* atom1, JSAtom* atom2); CompareAtoms(JSAtom* atom1, JSAtom* atom2);
/**
* Return true if the string contains only ASCII characters.
*/
extern bool
StringIsAscii(JSLinearString* str);
/* /*
* Return true if the string matches the given sequence of ASCII bytes. * Return true if the string matches the given sequence of ASCII bytes.
*/ */
@ -1688,6 +1694,14 @@ SubstringKernel(JSContext* cx, HandleString str, int32_t beginInt, int32_t lengt
/*** Conversions *********************************************************************************/ /*** Conversions *********************************************************************************/
/*
* Convert a string to a printable C string.
*
* Asserts if the input contains any non-ASCII characters.
*/
UniqueChars
EncodeAscii(JSContext* cx, JSString* str);
/* /*
* Convert a string to a printable C string. * Convert a string to a printable C string.
*/ */