Bug 1533636 - Don't bother explicitly aligning when XDRing char16_t data: the operations we use to perform the little/native-endian conversion will correctly translate into, or translate out of, unaligned memory. r=tcampbell

Differential Revision: https://phabricator.services.mozilla.com/D22654

--HG--
extra : rebase_source : 2013431c35fff24bb003671dc257005903ff3c07
This commit is contained in:
Jeff Walden 2019-03-06 13:36:47 -08:00
parent 6004ce1971
commit b299df2f1a
9 changed files with 300 additions and 150 deletions

View File

@ -11,6 +11,8 @@
#ifndef vm_AtomsTable_h
#define vm_AtomsTable_h
#include <type_traits> // std::{enable_if,is_const}
#include "js/GCHashTable.h"
#include "js/TypeDecls.h"
#include "vm/JSAtom.h"
@ -162,12 +164,22 @@ class AtomsTable {
~AtomsTable();
bool init();
template <typename CharT>
template <typename Chars>
MOZ_ALWAYS_INLINE JSAtom* atomizeAndCopyChars(
JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
JSContext* cx, Chars chars, size_t length, PinningBehavior pin,
const mozilla::Maybe<uint32_t>& indexValue,
const AtomHasher::Lookup& lookup);
template <typename CharT, typename = typename std::enable_if<
!std::is_const<CharT>::value>::type>
MOZ_ALWAYS_INLINE JSAtom* atomizeAndCopyChars(
JSContext* cx, CharT* chars, size_t length, PinningBehavior pin,
const mozilla::Maybe<uint32_t>& indexValue,
const AtomHasher::Lookup& lookup) {
return atomizeAndCopyChars(cx, const_cast<const CharT*>(chars), length, pin,
indexValue, lookup);
}
void pinExistingAtom(JSContext* cx, JSAtom* atom);
void tracePinnedAtoms(JSTracer* trc, const AutoAccessAtomsZone& access);

View File

@ -60,9 +60,10 @@ struct js::AtomHasher::Lookup {
union {
const JS::Latin1Char* latin1Chars;
const char16_t* twoByteChars;
LittleEndianChars littleEndianChars;
const char* utf8Bytes;
};
enum { TwoByteChar, Latin1, UTF8, WTF8 } type;
enum { TwoByteChar, LittleEndianTwoByte, Latin1, UTF8, WTF8 } type;
size_t length;
size_t byteLength;
const JSAtom* atom; /* Optional. */
@ -107,6 +108,13 @@ struct js::AtomHasher::Lookup {
MOZ_ASSERT(mozilla::HashString(twoByteChars, length) == hash);
}
}
MOZ_ALWAYS_INLINE Lookup(LittleEndianChars chars, size_t length)
: littleEndianChars(chars),
type(LittleEndianTwoByte),
length(length),
atom(nullptr),
hash(mozilla::HashStringKnownLength(chars, length)) {}
};
inline HashNumber js::AtomHasher::hash(const Lookup& l) { return l.hash; }
@ -121,6 +129,15 @@ MOZ_ALWAYS_INLINE bool js::AtomHasher::match(const AtomStateEntry& entry,
return false;
}
auto EqualsLittleEndianChars = [&lookup](auto keyChars) {
for (size_t i = 0, len = lookup.length; i < len; i++) {
if (keyChars[i] != lookup.littleEndianChars[i]) {
return false;
}
}
return true;
};
if (key->hasLatin1Chars()) {
const Latin1Char* keyChars = key->latin1Chars(lookup.nogc);
switch (lookup.type) {
@ -128,6 +145,8 @@ MOZ_ALWAYS_INLINE bool js::AtomHasher::match(const AtomStateEntry& entry,
return mozilla::ArrayEqual(keyChars, lookup.latin1Chars, lookup.length);
case Lookup::TwoByteChar:
return EqualChars(keyChars, lookup.twoByteChars, lookup.length);
case Lookup::LittleEndianTwoByte:
return EqualsLittleEndianChars(keyChars);
case Lookup::UTF8: {
JS::UTF8Chars utf8(lookup.utf8Bytes, lookup.byteLength);
return UTF8OrWTF8EqualsChars(utf8, keyChars);
@ -145,6 +164,8 @@ MOZ_ALWAYS_INLINE bool js::AtomHasher::match(const AtomStateEntry& entry,
return EqualChars(lookup.latin1Chars, keyChars, lookup.length);
case Lookup::TwoByteChar:
return mozilla::ArrayEqual(keyChars, lookup.twoByteChars, lookup.length);
case Lookup::LittleEndianTwoByte:
return EqualsLittleEndianChars(keyChars);
case Lookup::UTF8: {
JS::UTF8Chars utf8(lookup.utf8Bytes, lookup.byteLength);
return UTF8OrWTF8EqualsChars(utf8, keyChars);
@ -607,42 +628,39 @@ bool JSRuntime::initMainAtomsTables(JSContext* cx) {
return atoms_ && atoms_->init();
}
template <typename CharT>
MOZ_NEVER_INLINE static JSAtom* PermanentlyAtomizeAndCopyChars(
JSContext* cx, Maybe<AtomSet::AddPtr>& zonePtr, const CharT* tbchars,
size_t length, const Maybe<uint32_t>& indexValue,
const AtomHasher::Lookup& lookup);
template <typename Chars>
static MOZ_ALWAYS_INLINE JSAtom* AtomizeAndCopyCharsFromLookup(
JSContext* cx, Chars chars, size_t length, const AtomHasher::Lookup& lookup,
PinningBehavior pin, const Maybe<uint32_t>& indexValue);
template <typename CharT>
MOZ_ALWAYS_INLINE static JSAtom* AllocateNewAtom(
JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup);
template <typename CharT>
MOZ_ALWAYS_INLINE static JSAtom* AtomizeAndCopyCharsFromLookup(
JSContext* cx, const CharT* tbchars, size_t length,
template <typename CharT, typename = typename std::enable_if<
!std::is_const<CharT>::value>::type>
static MOZ_ALWAYS_INLINE JSAtom* AtomizeAndCopyCharsFromLookup(
JSContext* cx, CharT* chars, size_t length,
const AtomHasher::Lookup& lookup, PinningBehavior pin,
const Maybe<uint32_t>& indexValue);
/* |tbchars| must not point into an inline or short string. */
template <typename CharT>
MOZ_ALWAYS_INLINE static JSAtom* AtomizeAndCopyChars(
JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
const Maybe<uint32_t>& indexValue) {
if (JSAtom* s = cx->staticStrings().lookup(tbchars, length)) {
return s;
}
AtomHasher::Lookup lookup(tbchars, length);
return AtomizeAndCopyCharsFromLookup(cx, tbchars, length, lookup, pin,
indexValue);
return AtomizeAndCopyCharsFromLookup(cx, const_cast<const CharT*>(chars),
length, lookup, pin, indexValue);
}
template <typename CharT>
MOZ_ALWAYS_INLINE static JSAtom* AtomizeAndCopyCharsFromLookup(
JSContext* cx, const CharT* tbchars, size_t length,
const AtomHasher::Lookup& lookup, PinningBehavior pin,
const Maybe<uint32_t>& indexValue) {
template <typename Chars>
static MOZ_NEVER_INLINE JSAtom* PermanentlyAtomizeAndCopyChars(
JSContext* cx, Maybe<AtomSet::AddPtr>& zonePtr, Chars chars, size_t length,
const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup);
template <typename CharT, typename = typename std::enable_if<
!std::is_const<CharT>::value>::type>
static JSAtom* PermanentlyAtomizeAndCopyChars(
JSContext* cx, Maybe<AtomSet::AddPtr>& zonePtr, CharT* chars, size_t length,
const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup) {
return PermanentlyAtomizeAndCopyChars(
cx, zonePtr, const_cast<const CharT*>(chars), length, indexValue, lookup);
}
template <typename Chars>
static MOZ_ALWAYS_INLINE JSAtom* AtomizeAndCopyCharsFromLookup(
JSContext* cx, Chars chars, size_t length, const AtomHasher::Lookup& lookup,
PinningBehavior pin, const Maybe<uint32_t>& indexValue) {
// Try the per-Zone cache first. If we find the atom there we can avoid the
// atoms lock, the markAtom call, and the multiple HashSet lookups below.
// We don't use the per-Zone cache if we want a pinned atom: handling that
@ -665,7 +683,7 @@ MOZ_ALWAYS_INLINE static JSAtom* AtomizeAndCopyCharsFromLookup(
// atoms table is being created. In this case all atoms created are added to
// the permanent atoms table.
if (!cx->permanentAtomsPopulated()) {
return PermanentlyAtomizeAndCopyChars(cx, zonePtr, tbchars, length,
return PermanentlyAtomizeAndCopyChars(cx, zonePtr, chars, length,
indexValue, lookup);
}
@ -687,7 +705,7 @@ MOZ_ALWAYS_INLINE static JSAtom* AtomizeAndCopyCharsFromLookup(
return nullptr;
}
JSAtom* atom = cx->atoms().atomizeAndCopyChars(cx, tbchars, length, pin,
JSAtom* atom = cx->atoms().atomizeAndCopyChars(cx, chars, length, pin,
indexValue, lookup);
if (!atom) {
return nullptr;
@ -707,9 +725,23 @@ MOZ_ALWAYS_INLINE static JSAtom* AtomizeAndCopyCharsFromLookup(
return atom;
}
template <typename CharT>
template <typename Chars>
static MOZ_ALWAYS_INLINE JSAtom* AllocateNewAtom(
JSContext* cx, Chars chars, size_t length, PinningBehavior pin,
const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup);
template <typename CharT, typename = typename std::enable_if<
!std::is_const<CharT>::value>::type>
static MOZ_ALWAYS_INLINE JSAtom* AllocateNewAtom(
JSContext* cx, CharT* chars, size_t length, PinningBehavior pin,
const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup) {
return AllocateNewAtom(cx, const_cast<const CharT*>(chars), length, pin,
indexValue, lookup);
}
template <typename Chars>
MOZ_ALWAYS_INLINE JSAtom* AtomsTable::atomizeAndCopyChars(
JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
JSContext* cx, Chars chars, size_t length, PinningBehavior pin,
const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup) {
Partition& part = *partitions[getPartitionIndex(lookup)];
AutoLock lock(cx->runtime(), part.lock);
@ -746,7 +778,7 @@ MOZ_ALWAYS_INLINE JSAtom* AtomsTable::atomizeAndCopyChars(
return atom;
}
JSAtom* atom = AllocateNewAtom(cx, tbchars, length, pin, indexValue, lookup);
JSAtom* atom = AllocateNewAtom(cx, chars, length, pin, indexValue, lookup);
if (!atom) {
return nullptr;
}
@ -764,19 +796,24 @@ MOZ_ALWAYS_INLINE JSAtom* AtomsTable::atomizeAndCopyChars(
return atom;
}
template JSAtom* AtomizeAndCopyChars(JSContext* cx, const char16_t* tbchars,
size_t length, PinningBehavior pin,
const Maybe<uint32_t>& indexValue);
template JSAtom* AtomizeAndCopyChars(JSContext* cx, const Latin1Char* tbchars,
size_t length, PinningBehavior pin,
const Maybe<uint32_t>& indexValue);
/* |chars| must not point into an inline or short string. */
template <typename CharT>
MOZ_NEVER_INLINE static JSAtom* PermanentlyAtomizeAndCopyChars(
JSContext* cx, Maybe<AtomSet::AddPtr>& zonePtr, const CharT* tbchars,
size_t length, const Maybe<uint32_t>& indexValue,
const AtomHasher::Lookup& lookup) {
static MOZ_ALWAYS_INLINE JSAtom* AtomizeAndCopyChars(
JSContext* cx, const CharT* chars, size_t length, PinningBehavior pin,
const Maybe<uint32_t>& indexValue) {
if (JSAtom* s = cx->staticStrings().lookup(chars, length)) {
return s;
}
AtomHasher::Lookup lookup(chars, length);
return AtomizeAndCopyCharsFromLookup(cx, chars, length, lookup, pin,
indexValue);
}
template <typename Chars>
static MOZ_NEVER_INLINE JSAtom* PermanentlyAtomizeAndCopyChars(
JSContext* cx, Maybe<AtomSet::AddPtr>& zonePtr, Chars chars, size_t length,
const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup) {
MOZ_ASSERT(!cx->permanentAtomsPopulated());
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
@ -788,7 +825,7 @@ MOZ_NEVER_INLINE static JSAtom* PermanentlyAtomizeAndCopyChars(
}
JSAtom* atom =
AllocateNewAtom(cx, tbchars, length, DoNotPinAtom, indexValue, lookup);
AllocateNewAtom(cx, chars, length, DoNotPinAtom, indexValue, lookup);
if (!atom) {
return nullptr;
}
@ -825,13 +862,20 @@ struct AtomizeUTF8OrWTF8CharsWrapper {
// MakeFlatStringForAtomization has 4 variants.
// This is used by Latin1Char and char16_t.
template <typename CharT>
MOZ_ALWAYS_INLINE static JSFlatString* MakeFlatStringForAtomization(
JSContext* cx, const CharT* tbchars, size_t length) {
return NewStringCopyN<NoGC>(cx, tbchars, length);
static MOZ_ALWAYS_INLINE JSFlatString* MakeFlatStringForAtomization(
JSContext* cx, const CharT* chars, size_t length) {
return NewStringCopyN<NoGC>(cx, chars, length);
}
// MakeFlatStringForAtomization has one further variant -- a non-template
// overload accepting LittleEndianChars.
static MOZ_ALWAYS_INLINE JSFlatString* MakeFlatStringForAtomization(
JSContext* cx, LittleEndianChars chars, size_t length) {
return NewStringFromLittleEndianNoGC(cx, chars, length);
}
template <typename CharT, typename WrapperT>
MOZ_ALWAYS_INLINE static JSFlatString* MakeUTF8AtomHelper(JSContext* cx,
static MOZ_ALWAYS_INLINE JSFlatString* MakeUTF8AtomHelper(JSContext* cx,
const WrapperT* chars,
size_t length) {
if (JSInlineString::lengthFits<CharT>(length)) {
@ -870,11 +914,9 @@ MOZ_ALWAYS_INLINE static JSFlatString* MakeUTF8AtomHelper(JSContext* cx,
// Another 2 variants of MakeFlatStringForAtomization.
// This is used by AtomizeUTF8OrWTF8CharsWrapper with UTF8Chars or WTF8Chars.
template <typename InputCharsT>
MOZ_ALWAYS_INLINE
/* static */ JSFlatString*
MakeFlatStringForAtomization(
JSContext* cx, const AtomizeUTF8OrWTF8CharsWrapper<InputCharsT>* chars,
size_t length) {
/* static */ MOZ_ALWAYS_INLINE JSFlatString* MakeFlatStringForAtomization(
JSContext* cx, const AtomizeUTF8OrWTF8CharsWrapper<InputCharsT>* chars,
size_t length) {
if (length == 0) {
return cx->emptyString();
}
@ -885,13 +927,13 @@ MOZ_ALWAYS_INLINE
return MakeUTF8AtomHelper<JS::Latin1Char>(cx, chars, length);
}
template <typename CharT>
MOZ_ALWAYS_INLINE static JSAtom* AllocateNewAtom(
JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
template <typename Chars>
static MOZ_ALWAYS_INLINE JSAtom* AllocateNewAtom(
JSContext* cx, Chars chars, size_t length, PinningBehavior pin,
const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup) {
AutoAllocInAtomsZone ac(cx);
JSFlatString* flat = MakeFlatStringForAtomization(cx, tbchars, length);
JSFlatString* flat = MakeFlatStringForAtomization(cx, chars, length);
if (!flat) {
// Grudgingly forgo last-ditch GC. The alternative would be to release
// the lock, manually GC here, and retry from the top. If you fix this,
@ -1126,6 +1168,22 @@ template JSAtom* js::ToAtom<CanGC>(JSContext* cx, HandleValue v);
template JSAtom* js::ToAtom<NoGC>(JSContext* cx, const Value& v);
static JSAtom* AtomizeLittleEndianTwoByteChars(JSContext* cx,
const uint8_t* leTwoByte,
size_t length) {
CHECK_THREAD(cx);
LittleEndianChars chars(leTwoByte);
if (JSAtom* s = cx->staticStrings().lookup(chars, length)) {
return s;
}
AtomHasher::Lookup lookup(chars, length);
return AtomizeAndCopyCharsFromLookup(cx, chars, length, lookup, DoNotPinAtom,
Nothing());
}
template <XDRMode mode>
XDRResult js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp) {
bool latin1 = false;
@ -1170,51 +1228,13 @@ XDRResult js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp) {
}
atom = AtomizeChars(cx, chars, length);
} else {
#if MOZ_LITTLE_ENDIAN
/* Directly access the little endian chars in the XDR buffer. */
const char16_t* chars = nullptr;
const uint8_t* twoByteCharsLE = nullptr;
if (length) {
// In the |mode == XDR_ENCODE| case above, when |nchars > 0|,
// |XDRState::codeChars(char16_t*, size_t nchars)| will align the
// buffer. This code never calls that function, but it must act
// *as if* it had, so we must align manually here.
MOZ_TRY(xdr->codeAlign(sizeof(char16_t)));
const uint8_t* ptr;
size_t nbyte = length * sizeof(char16_t);
MOZ_TRY(xdr->peekData(&ptr, nbyte));
MOZ_ASSERT(reinterpret_cast<uintptr_t>(ptr) % sizeof(char16_t) == 0,
"non-aligned buffer during JSAtom decoding");
chars = reinterpret_cast<const char16_t*>(ptr);
}
atom = AtomizeChars(cx, chars, length);
#else
/*
* We must copy chars to a temporary buffer to convert between little and
* big endian data.
*/
char16_t* chars;
char16_t stackChars[256];
UniqueTwoByteChars heapChars;
if (length <= ArrayLength(stackChars)) {
chars = stackChars;
} else {
/*
* This is very uncommon. Don't use the tempLifoAlloc arena for this as
* most allocations here will be bigger than tempLifoAlloc's default
* chunk size.
*/
heapChars.reset(cx->pod_malloc<char16_t>(length));
if (!heapChars) {
return xdr->fail(JS::TranscodeResult_Throw);
}
chars = heapChars.get();
MOZ_TRY(xdr->peekData(&twoByteCharsLE, nbyte));
}
MOZ_TRY(xdr->codeChars(chars, length));
atom = AtomizeChars(cx, chars, length);
#endif /* !MOZ_LITTLE_ENDIAN */
atom = AtomizeLittleEndianTwoByteChars(cx, twoByteCharsLE, length);
}
if (!atom) {

View File

@ -86,9 +86,10 @@ static MOZ_ALWAYS_INLINE JSInlineString* NewInlineString(
return s;
}
template <typename CharT>
static MOZ_ALWAYS_INLINE JSFlatString* TryEmptyOrStaticString(
JSContext* cx, const CharT* chars, size_t n) {
template <typename Chars>
static MOZ_ALWAYS_INLINE JSFlatString* TryEmptyOrStaticString(JSContext* cx,
Chars chars,
size_t n) {
// Measurements on popular websites indicate empty strings are pretty common
// and most strings with length 1 or 2 are in the StaticStrings table. For
// length 3 strings that's only about 1%, so we check n <= 2.
@ -105,6 +106,14 @@ static MOZ_ALWAYS_INLINE JSFlatString* TryEmptyOrStaticString(
return nullptr;
}
template <typename CharT, typename = typename std::enable_if<
!std::is_const<CharT>::value>::type>
static MOZ_ALWAYS_INLINE JSFlatString* TryEmptyOrStaticString(JSContext* cx,
CharT* chars,
size_t n) {
return TryEmptyOrStaticString(cx, const_cast<const CharT*>(chars), n);
}
} /* namespace js */
MOZ_ALWAYS_INLINE bool JSString::validateLength(JSContext* maybecx,

View File

@ -1539,6 +1539,16 @@ static bool CanStoreCharsAsLatin1(const Latin1Char* s, size_t length) {
MOZ_CRASH("Shouldn't be called for Latin1 chars");
}
static bool CanStoreCharsAsLatin1(LittleEndianChars chars, size_t length) {
for (size_t i = 0; i < length; i++) {
if (chars[i] > JSString::MAX_LATIN1_CHAR) {
return false;
}
}
return true;
}
template <AllowGC allowGC>
static MOZ_ALWAYS_INLINE JSInlineString* NewInlineStringDeflated(
JSContext* cx, mozilla::Range<const char16_t> chars) {
@ -1589,6 +1599,38 @@ static JSFlatString* NewStringDeflated(JSContext* cx, const Latin1Char* s,
MOZ_CRASH("Shouldn't be called for Latin1 chars");
}
static JSFlatString* NewStringDeflatedFromLittleEndianNoGC(
JSContext* cx, LittleEndianChars chars, size_t length) {
MOZ_ASSERT(CanStoreCharsAsLatin1(chars, length));
if (JSInlineString::lengthFits<Latin1Char>(length)) {
Latin1Char* storage;
JSInlineString* str = AllocateInlineString<NoGC>(cx, length, &storage);
if (!str) {
return nullptr;
}
FillFromCompatibleAndTerminate(storage, chars, length);
return str;
}
auto news = cx->make_pod_array<Latin1Char>(length + 1);
if (!news) {
cx->recoverFromOutOfMemory();
return nullptr;
}
FillFromCompatibleAndTerminate(news.get(), chars, length);
JSFlatString* str = JSFlatString::new_<NoGC>(cx, news.get(), length);
if (!str) {
return nullptr;
}
mozilla::Unused << news.release();
return str;
}
template <typename CharT>
JSFlatString* js::NewStringDontDeflate(JSContext* cx, CharT* chars,
size_t length) {
@ -1756,6 +1798,36 @@ template JSFlatString* NewStringCopyNDontDeflate<NoGC>(JSContext* cx,
const Latin1Char* s,
size_t n);
static JSFlatString* NewUndeflatedStringFromLittleEndianNoGC(
JSContext* cx, LittleEndianChars chars, size_t length) {
if (JSInlineString::lengthFits<char16_t>(length)) {
char16_t* storage;
JSInlineString* str = AllocateInlineString<NoGC>(cx, length, &storage);
if (!str) {
return nullptr;
}
FillAndTerminate(storage, chars, length);
return str;
}
auto news = cx->make_pod_array<char16_t>(length + 1);
if (!news) {
cx->recoverFromOutOfMemory();
return nullptr;
}
FillAndTerminate(news.get(), chars, length);
if (JSFlatString* str = JSFlatString::new_<NoGC>(cx, news.get(), length)) {
// Successful JSFlatString::new_ took ownership of |news.release()|.
mozilla::Unused << news.release();
return str;
}
return nullptr;
}
JSFlatString* NewLatin1StringZ(JSContext* cx, UniqueChars chars) {
size_t length = strlen(chars.get());
UniqueLatin1Chars latin1(reinterpret_cast<Latin1Char*>(chars.release()));
@ -1783,6 +1855,20 @@ template JSFlatString* NewStringCopyN<CanGC>(JSContext* cx, const Latin1Char* s,
template JSFlatString* NewStringCopyN<NoGC>(JSContext* cx, const Latin1Char* s,
size_t n);
JSFlatString* NewStringFromLittleEndianNoGC(JSContext* cx,
LittleEndianChars chars,
size_t length) {
if (JSFlatString* str = TryEmptyOrStaticString(cx, chars, length)) {
return str;
}
if (CanStoreCharsAsLatin1(chars, length)) {
return NewStringDeflatedFromLittleEndianNoGC(cx, chars, length);
}
return NewUndeflatedStringFromLittleEndianNoGC(cx, chars, length);
}
template <js::AllowGC allowGC>
JSFlatString* NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars utf8) {
JS::SmallestEncoding encoding = JS::FindSmallestEncoding(utf8);

View File

@ -11,6 +11,8 @@
#include "mozilla/Range.h"
#include "mozilla/TextUtils.h"
#include <type_traits> // std::is_same
#include "jsapi.h"
#include "jsfriendapi.h"
@ -1331,6 +1333,24 @@ MOZ_ALWAYS_INLINE JSAtom* JSFlatString::morphAtomizedStringIntoPermanentAtom(
namespace js {
/**
* An indexable characters class exposing unaligned, little-endian encoded
* char16_t data.
*/
class LittleEndianChars {
public:
explicit constexpr LittleEndianChars(const uint8_t* leTwoByte)
: current(leTwoByte) {}
constexpr char16_t operator[](size_t index) const {
size_t offset = index * sizeof(char16_t);
return (current[offset + 1] << 8) | current[offset];
}
private:
const uint8_t* current;
};
class StaticStrings {
private:
/* Bigger chars cannot be in a length-2 string. */
@ -1382,8 +1402,14 @@ class StaticStrings {
static bool isStatic(JSAtom* atom);
/* Return null if no static atom exists for the given (chars, length). */
template <typename CharT>
MOZ_ALWAYS_INLINE JSAtom* lookup(const CharT* chars, size_t length) {
template <typename Chars>
MOZ_ALWAYS_INLINE JSAtom* lookup(Chars chars, size_t length) {
static_assert(std::is_same<Chars, const Latin1Char*>::value ||
std::is_same<Chars, const char16_t*>::value ||
std::is_same<Chars, LittleEndianChars>::value,
"for understandability, |chars| must be one of a few "
"identified types");
switch (length) {
case 1: {
char16_t c = chars[0];
@ -1423,6 +1449,19 @@ class StaticStrings {
return nullptr;
}
template <typename CharT, typename = typename std::enable_if<
std::is_same<CharT, char>::value ||
std::is_same<CharT, const char>::value ||
!std::is_const<CharT>::value>::type>
MOZ_ALWAYS_INLINE JSAtom* lookup(CharT* chars, size_t length) {
// Collapse calls for |char*| or |const char*| into |const unsigned char*|
// to avoid excess instantiations. Collapse the remaining |CharT*| to
// |const CharT*| for the same reason.
using UnsignedCharT = typename std::make_unsigned<CharT>::type;
UnsignedCharT* unsignedChars = reinterpret_cast<UnsignedCharT*>(chars);
return lookup(const_cast<const UnsignedCharT*>(unsignedChars), length);
}
private:
typedef uint8_t SmallChar;
static const SmallChar INVALID_SMALL_CHAR = -1;
@ -1560,6 +1599,13 @@ JSString* NewMaybeExternalString(JSContext* cx, const char16_t* s, size_t n,
const JSStringFinalizer* fin,
bool* allocatedExternal);
/**
* Allocate a new string consisting of |chars[0..length]| characters.
*/
extern JSFlatString* NewStringFromLittleEndianNoGC(JSContext* cx,
LittleEndianChars chars,
size_t length);
JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
template <AllowGC allowGC>

View File

@ -86,21 +86,22 @@ XDRResult XDRState<mode>::codeChars(char16_t* chars, size_t nchars) {
return Ok();
}
// Align the buffer to avoid unaligned loads.
MOZ_TRY(codeAlign(sizeof(char16_t)));
size_t nbytes = nchars * sizeof(char16_t);
if (mode == XDR_ENCODE) {
uint8_t* ptr = buf.write(nbytes);
if (!ptr) {
return fail(JS::TranscodeResult_Throw);
}
// |mozilla::NativeEndian| correctly handles writing into unaligned |ptr|.
mozilla::NativeEndian::copyAndSwapToLittleEndian(ptr, chars, nchars);
} else {
const uint8_t* ptr = buf.read(nbytes);
if (!ptr) {
return fail(JS::TranscodeResult_Failure_BadDecode);
}
// |mozilla::NativeEndian| correctly handles reading from unaligned |ptr|.
mozilla::NativeEndian::copyAndSwapFromLittleEndian(chars, ptr, nchars);
}
return Ok();

View File

@ -484,8 +484,6 @@ class XDRState : public XDRCoderBase {
XDRResult codeChars(JS::Latin1Char* chars, size_t nchars);
XDRResult codeChars(mozilla::Utf8Unit* units, size_t nchars);
// If |nchars > 0|, this calls |codeAlign(sizeof(char16_t))| so callers
// don't have to.
XDRResult codeChars(char16_t* chars, size_t nchars);
XDRResult codeFunction(JS::MutableHandleFunction objp,

View File

@ -60,15 +60,6 @@ using namespace mozilla::loader;
ProcessType ScriptPreloader::sProcessType;
// This type correspond to js::vm::XDRAlignment type, which is used as a size
// reference for alignment of XDR buffers.
using XDRAlign = uint16_t;
static const uint8_t sAlignPadding[sizeof(XDRAlign)] = {0, 0};
static inline size_t ComputeByteAlignment(size_t bytes, size_t align) {
return (align - (bytes % align)) % align;
}
nsresult ScriptPreloader::CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) {
MOZ_COLLECT_REPORT(
@ -540,8 +531,6 @@ Result<Ok, nsresult> ScriptPreloader::InitCacheInternal(
}
auto data = mCacheData.get<uint8_t>();
uint8_t* start = data.get();
MOZ_ASSERT(reinterpret_cast<uintptr_t>(start) % sizeof(XDRAlign) == 0);
auto end = data + size;
if (memcmp(MAGIC, data.get(), sizeof(MAGIC))) {
@ -566,10 +555,6 @@ Result<Ok, nsresult> ScriptPreloader::InitCacheInternal(
InputBuffer buf(header);
size_t len = data.get() - start;
size_t alignLen = ComputeByteAlignment(len, sizeof(XDRAlign));
data += alignLen;
size_t offset = 0;
while (!buf.finished()) {
auto script = MakeUnique<CachedScript>(*this, buf);
@ -587,9 +572,6 @@ Result<Ok, nsresult> ScriptPreloader::InitCacheInternal(
}
offset += script->mSize;
MOZ_ASSERT(reinterpret_cast<uintptr_t>(scriptData.get()) %
sizeof(XDRAlign) ==
0);
script->mXDRRange.emplace(scriptData, scriptData + script->mSize);
// Don't pre-decode the script unless it was used in this process type
@ -739,7 +721,6 @@ Result<Ok, nsresult> ScriptPreloader::WriteCache() {
OutputBuffer buf;
size_t offset = 0;
for (auto script : scripts) {
MOZ_ASSERT(offset % sizeof(XDRAlign) == 0);
script->mOffset = offset;
script->Code(buf);
@ -749,22 +730,11 @@ Result<Ok, nsresult> ScriptPreloader::WriteCache() {
uint8_t headerSize[4];
LittleEndian::writeUint32(headerSize, buf.cursor());
size_t len = 0;
MOZ_TRY(Write(fd, MAGIC, sizeof(MAGIC)));
len += sizeof(MAGIC);
MOZ_TRY(Write(fd, headerSize, sizeof(headerSize)));
len += sizeof(headerSize);
MOZ_TRY(Write(fd, buf.Get(), buf.cursor()));
len += buf.cursor();
size_t alignLen = ComputeByteAlignment(len, sizeof(XDRAlign));
if (alignLen) {
MOZ_TRY(Write(fd, sAlignPadding, alignLen));
len += alignLen;
}
for (auto script : scripts) {
MOZ_ASSERT(script->mSize % sizeof(XDRAlign) == 0);
MOZ_TRY(Write(fd, script->Range().begin().get(), script->mSize));
len += script->mSize;
if (script->mScript) {
script->FreeData();

View File

@ -404,7 +404,9 @@ class Endian : private EndianUtils {
/*
* Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting
* them to little-endian format if ThisEndian is Big.
* them to little-endian format if ThisEndian is Big. |aSrc| as a typed
* pointer must be aligned; |aDest| need not be.
*
* As with memcpy, |aDest| and |aSrc| must not overlap.
*/
template <typename T>
@ -431,7 +433,9 @@ class Endian : private EndianUtils {
/*
* Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting
* them to big-endian format if ThisEndian is Little.
* them to big-endian format if ThisEndian is Little. |aSrc| as a typed
* pointer must be aligned; |aDest| need not be.
*
* As with memcpy, |aDest| and |aSrc| must not overlap.
*/
template <typename T>
@ -479,7 +483,9 @@ class Endian : private EndianUtils {
/*
* Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting
* them to little-endian format if ThisEndian is Big.
* them to little-endian format if ThisEndian is Big. |aDest| as a typed
* pointer must be aligned; |aSrc| need not be.
*
* As with memcpy, |aDest| and |aSrc| must not overlap.
*/
template <typename T>
@ -506,7 +512,9 @@ class Endian : private EndianUtils {
/*
* Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting
* them to big-endian format if ThisEndian is Little.
* them to big-endian format if ThisEndian is Little. |aDest| as a typed
* pointer must be aligned; |aSrc| need not be.
*
* As with memcpy, |aDest| and |aSrc| must not overlap.
*/
template <typename T>